Fluffychat merge (#2055)
* Translated using Weblate (Tamil)
Currently translated at 100.0% (696 of 696 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ta/
* Translated using Weblate (Italian)
Currently translated at 100.0% (696 of 696 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/it/
* Translated using Weblate (Czech)
Currently translated at 77.5% (540 of 696 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/cs/
* Translated using Weblate (Tamil)
Currently translated at 100.0% (696 of 696 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ta/
* build: Bump version to v1.24.0
* fix: Only try again load mxc image on io exception
* fix: swipe_to_action upgrade to 0.3.0
Fixes #1415
* Update Mastodon Link in README.md
* feat: Swipe to next or previous image in image viewer
* chore: Follow up image viewer swipe
* chore: Follow up image viewer
* chore: Follow up imageviewer
* chore: Follow up image viewer
* chore: Try out new matrix dart sdk
* build: Update matrix dart sdk
* fix: Do not leave old room if join new room failed
* feat: Display file description on all file events
* chore: Show close icon for device verification warning
* feat: Prevent sending messages if other party has no encryption keys
* chore: Follow up other party has no keys
* build: Update flutter to 3.27.2
* chore: Update matrix dart sdk
* chore: Update lastEvent after redaction
* chore: Follow up reply color
* chore: Follow up colors reply
* chore: Follow up snackbar theme
* build: Update Flutter Version
* chore: Design adjustments
* chore: Adjust emoji picker design
* chore: Follow up emoji picker design
* chore: Follow up design
* chore: adjust design
* chore: Adjust colors
* build: Update matrix dart sdk
* fix: Textfields in dialogs on iOS
* chore: UX Feedback when selecting files needs some time
* chore: Do only show fileDescription if filename is not null
* chore: Follow up format
* refactor: Improve sso login UX on web
* refactor: New html rendering
* refactor: Switch to ubuntu font
* refactor: Remove unused class
* chore: Design adjustments
* chore: Follow up html rendering
* chore: Follow up html rendering
* chore: Adjust share scaffold dialog design
* chore: Better connection status indicator
* chore: Follow up connection status
* chore: Follow up sync status
* chore: Slightly adjust welcome screen
* chore: Adjust button icon colors
* chore: Follow up connection status
* chore: Add start to ordered list
* chore: Follow up html lists
* chore: Follow up html rendering
* chore: Add tooltip to links in html
* chore: Add explanation for PlayStore Safety Standards
* chore: Use UbuntuMono
* feat: Pick share keys with
* chore: Adjust design
* chore: Add medium font
* chore: Follow up title spacing
* chore: Follow up colors
* chore: Adjust design of adaptive dialogs
* chore: Follow up colors
* chore: Follow up colors
* chore: Design follow up
* chore: Follow up colors
* feat: Display all push rules and allow to enable disable them
* feat: Inspect and delete push rules
* Translated using Weblate (Italian)
Currently translated at 99.8% (695 of 696 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/it/
* Translated using Weblate (Arabic)
Currently translated at 100.0% (698 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ar/
* Translated using Weblate (Slovak)
Currently translated at 29.3% (205 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/sk/
* Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (698 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Dutch)
Currently translated at 86.5% (604 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/
* Translated using Weblate (Galician)
Currently translated at 100.0% (698 of 698 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% (698 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Dutch)
Currently translated at 88.1% (615 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/
* Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 91.8% (641 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/
* Translated using Weblate (Estonian)
Currently translated at 99.4% (694 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/
* Translated using Weblate (Basque)
Currently translated at 100.0% (698 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/
* Translated using Weblate (Ukrainian)
Currently translated at 100.0% (698 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/
* Translated using Weblate (Korean)
Currently translated at 98.8% (690 of 698 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ko/
* Translated using Weblate (Estonian)
Currently translated at 99.2% (696 of 701 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/
* Translated using Weblate (Basque)
Currently translated at 100.0% (701 of 701 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/
* Translated using Weblate (Galician)
Currently translated at 99.8% (700 of 701 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% (701 of 701 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Korean)
Currently translated at 100.0% (701 of 701 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ko/
* Translated using Weblate (Estonian)
Currently translated at 100.0% (704 of 704 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% (704 of 704 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Irish)
Currently translated at 100.0% (704 of 704 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/
* Translated using Weblate (Latvian)
Currently translated at 100.0% (704 of 704 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/
* chore: Adjust navrail design
* chore: Follow up push rules
* chore: Follow up push rule settings
* feat: Select share keys with property in security settings
* feat: Use dynamic gradient for chat bubbles
* chore: follow up chat bubble colors
* chore: Follow up message bubble colors
* fix: Image search rendering problem
* chore: Follow up bubble color
* chore: Message bubble color follow up
* chore: Follow up bubble color
* chore: Follow up message bubble color
* chore: Follow up linebreak formatting
* chore: Follow up code blocks
* build: Update to flutter 3.27.4
* docs: Fix snap store icon
* refactor: Display navigationrail in settings page
* build: Add locale config for android
* build: Fix ios debug build
* Translated using Weblate (German)
Currently translated at 99.5% (702 of 705 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/
* Translated using Weblate (Estonian)
Currently translated at 100.0% (705 of 705 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% (705 of 705 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (German)
Currently translated at 94.6% (713 of 753 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/
* Translated using Weblate (Estonian)
Currently translated at 100.0% (753 of 753 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/
* Translated using Weblate (Basque)
Currently translated at 95.0% (716 of 753 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/
* Translated using Weblate (Ukrainian)
Currently translated at 92.8% (699 of 753 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% (753 of 753 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Latvian)
Currently translated at 100.0% (753 of 753 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/
* Translated using Weblate (Estonian)
Currently translated at 100.0% (759 of 759 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% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/
* Translated using Weblate (Catalan)
Currently translated at 99.8% (758 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ca/
* 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 (Korean)
Currently translated at 92.8% (705 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ko/
* Translated using Weblate (Irish)
Currently translated at 100.0% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/
* chore: Follow up linebreaks in html rendering
* chore: Follow up html rendering br tag
* Translated using Weblate (Ukrainian)
Currently translated at 92.0% (699 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/
* Translated using Weblate (Indonesian)
Currently translated at 100.0% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/
* Translated using Weblate (Galician)
Currently translated at 100.0% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/
* Translated using Weblate (Spanish)
Currently translated at 100.0% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/
* Translated using Weblate (Croatian)
Currently translated at 83.2% (632 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/hr/
* Translated using Weblate (Ukrainian)
Currently translated at 93.5% (710 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/
* refactor: Update arb file types
* build: Upgrade gradle
* refactor: Follow up fix types in localization files
* build: Automerge weblate PRs
* build: Follow up auto merge weblate
* build: Update PAT
* build: Update weblate auto merge
* build: Add missing permissions
* build: Update weblate auto merge
* build: remove weblate auto merge
* Translated using Weblate (German)
Currently translated at 94.0% (714 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/
* Translated using Weblate (Spanish)
Currently translated at 100.0% (759 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/
* Translated using Weblate (Ukrainian)
Currently translated at 93.8% (712 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/
* build: Update flutter web uild
* chore: Follow up connection status header
* build: Switch to flutter_shortcuts_new
* refactor: Remove broken callkeep implementation
* refactor: Migrate uni_links to app_links
* refactor: Migrate to maintained badge package
* build: Update flutter_olm to 2.0.0
* build: Update native-imaging
* chore: Remove gradle workaround
* build: Update dependencies for flutter
* build: Update dependencies to remove more flutter android v1 references
* build: Update gradle version
* refactor: Switch to maintained qr code package
* chore: Make login with matrix id more prominent again
* build: Update fcm_shared_isolate
* build: Update native_imaging
* build: Add changelog for v1.25.0
* Translated using Weblate (French)
Currently translated at 84.4% (641 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fr/
* Translated using Weblate (French)
Currently translated at 84.5% (642 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fr/
* Translated using Weblate (French)
Currently translated at 86.6% (658 of 759 strings)
Translation: FluffyChat/Translations
Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fr/
* build: Use correct flutter version in snapcraft
* build: Detect flutter path better
* 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 commit 2625e89a33.
* chore: Combine mimetype types in send file dialog logic
* build: Update flutter to 3.29.0
* Revert "build: Update flutter to 3.29.0"
* fix: Crash in settings when using MAS
* build: Fix build tailwindcss for website
* fix pubspec
* update index.html
* generated
* fluffychat merge
* update matrix SDK
* generated
* fix message bubble background color
* generated
---------
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Co-authored-by: Angelo Schirinzi <Odi-3@users.noreply.hosted.weblate.org>
Co-authored-by: Erin <erin@erindesu.cz>
Co-authored-by: Christian <christian-pauly@posteo.de>
Co-authored-by: Krille-chan <christian-kussowski@posteo.de>
Co-authored-by: Krille <c.kussowski@famedly.com>
Co-authored-by: EpicKiwi <me@epickiwi.fr>
Co-authored-by: Christian Tietze <me@christiantietze.de>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Jelv <post@jelv.nl>
Co-authored-by: josé m <correoxm@disroot.org>
Co-authored-by: 玖然 <noctiro@gmail.com>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: xabirequejo <xabi.rn@gmail.com>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
Co-authored-by: kdh8219 <kdh8219@monamo.dev>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv>
Co-authored-by: Jana <j.kussowski@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: fadelkon <fadelkon@posteo.net>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: Alfredo Sola <alfredo@sola.es>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Antonin Del Fabbro <message@antonin.one>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
888cc7e13d
commit
36a4654b2d
184 changed files with 6018 additions and 2885 deletions
4
.github/workflows/integrate.yaml
vendored
4
.github/workflows/integrate.yaml
vendored
|
|
@ -9,6 +9,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./scripts/generate-locale-config.sh
|
||||
- run: git diff --exit-code
|
||||
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
|
|
@ -88,7 +90,7 @@ jobs:
|
|||
# Pangea#
|
||||
|
||||
build_debug_ios:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||
|
|
|
|||
2
.github/workflows/versions.env
vendored
2
.github/workflows/versions.env
vendored
|
|
@ -1,2 +1,2 @@
|
|||
FLUTTER_VERSION=3.27.3
|
||||
FLUTTER_VERSION=3.27.4
|
||||
JAVA_VERSION=17
|
||||
|
|
|
|||
305
.gitignore
vendored
305
.gitignore
vendored
|
|
@ -75,4 +75,307 @@ scripts/.credentials
|
|||
olm
|
||||
|
||||
needed-translations.txt
|
||||
.venv
|
||||
.venv
|
||||
|
||||
docs/node_modules/.package-lock.json
|
||||
docs/node_modules/.bin/detect-libc
|
||||
docs/node_modules/.bin/jiti
|
||||
docs/node_modules/.bin/tailwindcss
|
||||
docs/node_modules/@parcel/watcher/binding.gyp
|
||||
docs/node_modules/@parcel/watcher/index.d.ts
|
||||
docs/node_modules/@parcel/watcher/index.js
|
||||
docs/node_modules/@parcel/watcher/index.js.flow
|
||||
docs/node_modules/@parcel/watcher/LICENSE
|
||||
docs/node_modules/@parcel/watcher/package.json
|
||||
docs/node_modules/@parcel/watcher/README.md
|
||||
docs/node_modules/@parcel/watcher/wrapper.js
|
||||
docs/node_modules/@parcel/watcher/scripts/build-from-source.js
|
||||
docs/node_modules/@parcel/watcher/src/Backend.cc
|
||||
docs/node_modules/@parcel/watcher/src/Backend.hh
|
||||
docs/node_modules/@parcel/watcher/src/binding.cc
|
||||
docs/node_modules/@parcel/watcher/src/Debounce.cc
|
||||
docs/node_modules/@parcel/watcher/src/Debounce.hh
|
||||
docs/node_modules/@parcel/watcher/src/DirTree.cc
|
||||
docs/node_modules/@parcel/watcher/src/DirTree.hh
|
||||
docs/node_modules/@parcel/watcher/src/Event.hh
|
||||
docs/node_modules/@parcel/watcher/src/Glob.cc
|
||||
docs/node_modules/@parcel/watcher/src/Glob.hh
|
||||
docs/node_modules/@parcel/watcher/src/PromiseRunner.hh
|
||||
docs/node_modules/@parcel/watcher/src/Signal.hh
|
||||
docs/node_modules/@parcel/watcher/src/Watcher.cc
|
||||
docs/node_modules/@parcel/watcher/src/Watcher.hh
|
||||
docs/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/linux/InotifyBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/linux/InotifyBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/unix/fts.cc
|
||||
docs/node_modules/@parcel/watcher/src/unix/legacy.cc
|
||||
docs/node_modules/@parcel/watcher/src/wasm/include.h
|
||||
docs/node_modules/@parcel/watcher/src/wasm/WasmBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/wasm/WasmBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/watchman/BSER.cc
|
||||
docs/node_modules/@parcel/watcher/src/watchman/BSER.hh
|
||||
docs/node_modules/@parcel/watcher/src/watchman/IPC.hh
|
||||
docs/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh
|
||||
docs/node_modules/@parcel/watcher/src/windows/win_utils.cc
|
||||
docs/node_modules/@parcel/watcher/src/windows/win_utils.hh
|
||||
docs/node_modules/@parcel/watcher/src/windows/WindowsBackend.cc
|
||||
docs/node_modules/@parcel/watcher/src/windows/WindowsBackend.hh
|
||||
docs/node_modules/@parcel/watcher-linux-x64-glibc/LICENSE
|
||||
docs/node_modules/@parcel/watcher-linux-x64-glibc/package.json
|
||||
docs/node_modules/@parcel/watcher-linux-x64-glibc/README.md
|
||||
docs/node_modules/@parcel/watcher-linux-x64-glibc/watcher.node
|
||||
docs/node_modules/@tailwindcss/cli/LICENSE
|
||||
docs/node_modules/@tailwindcss/cli/package.json
|
||||
docs/node_modules/@tailwindcss/cli/README.md
|
||||
docs/node_modules/@tailwindcss/cli/dist/index.mjs
|
||||
docs/node_modules/@tailwindcss/node/LICENSE
|
||||
docs/node_modules/@tailwindcss/node/package.json
|
||||
docs/node_modules/@tailwindcss/node/README.md
|
||||
docs/node_modules/@tailwindcss/node/dist/esm-cache.loader.d.mts
|
||||
docs/node_modules/@tailwindcss/node/dist/esm-cache.loader.mjs
|
||||
docs/node_modules/@tailwindcss/node/dist/index.d.mts
|
||||
docs/node_modules/@tailwindcss/node/dist/index.d.ts
|
||||
docs/node_modules/@tailwindcss/node/dist/index.js
|
||||
docs/node_modules/@tailwindcss/node/dist/index.mjs
|
||||
docs/node_modules/@tailwindcss/node/dist/require-cache.d.ts
|
||||
docs/node_modules/@tailwindcss/node/dist/require-cache.js
|
||||
docs/node_modules/@tailwindcss/oxide/index.d.ts
|
||||
docs/node_modules/@tailwindcss/oxide/index.js
|
||||
docs/node_modules/@tailwindcss/oxide/LICENSE
|
||||
docs/node_modules/@tailwindcss/oxide/package.json
|
||||
docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/LICENSE
|
||||
docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/package.json
|
||||
docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/README.md
|
||||
docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/tailwindcss-oxide.linux-x64-gnu.node
|
||||
docs/node_modules/braces/index.js
|
||||
docs/node_modules/braces/LICENSE
|
||||
docs/node_modules/braces/package.json
|
||||
docs/node_modules/braces/README.md
|
||||
docs/node_modules/braces/lib/compile.js
|
||||
docs/node_modules/braces/lib/constants.js
|
||||
docs/node_modules/braces/lib/expand.js
|
||||
docs/node_modules/braces/lib/parse.js
|
||||
docs/node_modules/braces/lib/stringify.js
|
||||
docs/node_modules/braces/lib/utils.js
|
||||
docs/node_modules/detect-libc/.npmignore
|
||||
docs/node_modules/detect-libc/LICENSE
|
||||
docs/node_modules/detect-libc/package.json
|
||||
docs/node_modules/detect-libc/README.md
|
||||
docs/node_modules/detect-libc/bin/detect-libc.js
|
||||
docs/node_modules/detect-libc/lib/detect-libc.js
|
||||
docs/node_modules/enhanced-resolve/LICENSE
|
||||
docs/node_modules/enhanced-resolve/package.json
|
||||
docs/node_modules/enhanced-resolve/README.md
|
||||
docs/node_modules/enhanced-resolve/types.d.ts
|
||||
docs/node_modules/enhanced-resolve/lib/AliasFieldPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/AliasPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/AppendPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js
|
||||
docs/node_modules/enhanced-resolve/lib/CloneBasenamePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ConditionalPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/createInnerContext.js
|
||||
docs/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/DescriptionFileUtils.js
|
||||
docs/node_modules/enhanced-resolve/lib/DirectoryExistsPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ExportsFieldPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ExtensionAliasPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/FileExistsPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/forEachBail.js
|
||||
docs/node_modules/enhanced-resolve/lib/getInnerRequest.js
|
||||
docs/node_modules/enhanced-resolve/lib/getPaths.js
|
||||
docs/node_modules/enhanced-resolve/lib/ImportsFieldPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/index.js
|
||||
docs/node_modules/enhanced-resolve/lib/JoinRequestPartPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/JoinRequestPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/LogInfoPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/MainFieldPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ModulesInHierachicDirectoriesPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ModulesInHierarchicalDirectoriesPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ModulesInRootPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/NextPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ParsePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/PnpPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/Resolver.js
|
||||
docs/node_modules/enhanced-resolve/lib/ResolverFactory.js
|
||||
docs/node_modules/enhanced-resolve/lib/RestrictionsPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/ResultPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/RootsPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/SelfReferencePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/SymlinkPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/SyncAsyncFileSystemDecorator.js
|
||||
docs/node_modules/enhanced-resolve/lib/TryNextPlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/UseFilePlugin.js
|
||||
docs/node_modules/enhanced-resolve/lib/util/entrypoints.js
|
||||
docs/node_modules/enhanced-resolve/lib/util/identifier.js
|
||||
docs/node_modules/enhanced-resolve/lib/util/module-browser.js
|
||||
docs/node_modules/enhanced-resolve/lib/util/path.js
|
||||
docs/node_modules/enhanced-resolve/lib/util/process-browser.js
|
||||
docs/node_modules/fill-range/index.js
|
||||
docs/node_modules/fill-range/LICENSE
|
||||
docs/node_modules/fill-range/package.json
|
||||
docs/node_modules/fill-range/README.md
|
||||
docs/node_modules/graceful-fs/clone.js
|
||||
docs/node_modules/graceful-fs/graceful-fs.js
|
||||
docs/node_modules/graceful-fs/legacy-streams.js
|
||||
docs/node_modules/graceful-fs/LICENSE
|
||||
docs/node_modules/graceful-fs/package.json
|
||||
docs/node_modules/graceful-fs/polyfills.js
|
||||
docs/node_modules/graceful-fs/README.md
|
||||
docs/node_modules/is-extglob/index.js
|
||||
docs/node_modules/is-extglob/LICENSE
|
||||
docs/node_modules/is-extglob/package.json
|
||||
docs/node_modules/is-extglob/README.md
|
||||
docs/node_modules/is-glob/index.js
|
||||
docs/node_modules/is-glob/LICENSE
|
||||
docs/node_modules/is-glob/package.json
|
||||
docs/node_modules/is-glob/README.md
|
||||
docs/node_modules/is-number/index.js
|
||||
docs/node_modules/is-number/LICENSE
|
||||
docs/node_modules/is-number/package.json
|
||||
docs/node_modules/is-number/README.md
|
||||
docs/node_modules/jiti/LICENSE
|
||||
docs/node_modules/jiti/package.json
|
||||
docs/node_modules/jiti/README.md
|
||||
docs/node_modules/jiti/dist/babel.cjs
|
||||
docs/node_modules/jiti/dist/jiti.cjs
|
||||
docs/node_modules/jiti/lib/jiti-cli.mjs
|
||||
docs/node_modules/jiti/lib/jiti-hooks.mjs
|
||||
docs/node_modules/jiti/lib/jiti-native.mjs
|
||||
docs/node_modules/jiti/lib/jiti-register.d.mts
|
||||
docs/node_modules/jiti/lib/jiti-register.mjs
|
||||
docs/node_modules/jiti/lib/jiti.cjs
|
||||
docs/node_modules/jiti/lib/jiti.d.cts
|
||||
docs/node_modules/jiti/lib/jiti.d.mts
|
||||
docs/node_modules/jiti/lib/jiti.mjs
|
||||
docs/node_modules/jiti/lib/types.d.ts
|
||||
docs/node_modules/lightningcss/LICENSE
|
||||
docs/node_modules/lightningcss/package.json
|
||||
docs/node_modules/lightningcss/README.md
|
||||
docs/node_modules/lightningcss/node/ast.d.ts
|
||||
docs/node_modules/lightningcss/node/ast.js.flow
|
||||
docs/node_modules/lightningcss/node/browserslistToTargets.js
|
||||
docs/node_modules/lightningcss/node/composeVisitors.js
|
||||
docs/node_modules/lightningcss/node/flags.js
|
||||
docs/node_modules/lightningcss/node/index.d.ts
|
||||
docs/node_modules/lightningcss/node/index.js
|
||||
docs/node_modules/lightningcss/node/index.js.flow
|
||||
docs/node_modules/lightningcss/node/index.mjs
|
||||
docs/node_modules/lightningcss/node/targets.d.ts
|
||||
docs/node_modules/lightningcss/node/targets.js.flow
|
||||
docs/node_modules/lightningcss-linux-x64-gnu/LICENSE
|
||||
docs/node_modules/lightningcss-linux-x64-gnu/lightningcss.linux-x64-gnu.node
|
||||
docs/node_modules/lightningcss-linux-x64-gnu/package.json
|
||||
docs/node_modules/lightningcss-linux-x64-gnu/README.md
|
||||
docs/node_modules/micromatch/index.js
|
||||
docs/node_modules/micromatch/LICENSE
|
||||
docs/node_modules/micromatch/package.json
|
||||
docs/node_modules/micromatch/README.md
|
||||
docs/node_modules/mri/index.d.ts
|
||||
docs/node_modules/mri/license.md
|
||||
docs/node_modules/mri/package.json
|
||||
docs/node_modules/mri/readme.md
|
||||
docs/node_modules/mri/lib/index.js
|
||||
docs/node_modules/mri/lib/index.mjs
|
||||
docs/node_modules/node-addon-api/common.gypi
|
||||
docs/node_modules/node-addon-api/except.gypi
|
||||
docs/node_modules/node-addon-api/index.js
|
||||
docs/node_modules/node-addon-api/LICENSE.md
|
||||
docs/node_modules/node-addon-api/napi-inl.deprecated.h
|
||||
docs/node_modules/node-addon-api/napi-inl.h
|
||||
docs/node_modules/node-addon-api/napi.h
|
||||
docs/node_modules/node-addon-api/node_addon_api.gyp
|
||||
docs/node_modules/node-addon-api/node_api.gyp
|
||||
docs/node_modules/node-addon-api/noexcept.gypi
|
||||
docs/node_modules/node-addon-api/nothing.c
|
||||
docs/node_modules/node-addon-api/package-support.json
|
||||
docs/node_modules/node-addon-api/package.json
|
||||
docs/node_modules/node-addon-api/README.md
|
||||
docs/node_modules/node-addon-api/tools/check-napi.js
|
||||
docs/node_modules/node-addon-api/tools/clang-format.js
|
||||
docs/node_modules/node-addon-api/tools/conversion.js
|
||||
docs/node_modules/node-addon-api/tools/eslint-format.js
|
||||
docs/node_modules/node-addon-api/tools/README.md
|
||||
docs/node_modules/picocolors/LICENSE
|
||||
docs/node_modules/picocolors/package.json
|
||||
docs/node_modules/picocolors/picocolors.browser.js
|
||||
docs/node_modules/picocolors/picocolors.d.ts
|
||||
docs/node_modules/picocolors/picocolors.js
|
||||
docs/node_modules/picocolors/README.md
|
||||
docs/node_modules/picocolors/types.d.ts
|
||||
docs/node_modules/picomatch/CHANGELOG.md
|
||||
docs/node_modules/picomatch/index.js
|
||||
docs/node_modules/picomatch/LICENSE
|
||||
docs/node_modules/picomatch/package.json
|
||||
docs/node_modules/picomatch/README.md
|
||||
docs/node_modules/picomatch/lib/constants.js
|
||||
docs/node_modules/picomatch/lib/parse.js
|
||||
docs/node_modules/picomatch/lib/picomatch.js
|
||||
docs/node_modules/picomatch/lib/scan.js
|
||||
docs/node_modules/picomatch/lib/utils.js
|
||||
docs/node_modules/tailwindcss/index.css
|
||||
docs/node_modules/tailwindcss/LICENSE
|
||||
docs/node_modules/tailwindcss/package.json
|
||||
docs/node_modules/tailwindcss/preflight.css
|
||||
docs/node_modules/tailwindcss/README.md
|
||||
docs/node_modules/tailwindcss/theme.css
|
||||
docs/node_modules/tailwindcss/utilities.css
|
||||
docs/node_modules/tailwindcss/dist/chunk-AZANAYY2.mjs
|
||||
docs/node_modules/tailwindcss/dist/chunk-CH45MXZF.mjs
|
||||
docs/node_modules/tailwindcss/dist/chunk-V2K3XTS4.mjs
|
||||
docs/node_modules/tailwindcss/dist/colors-b_6i0Oi7.d.ts
|
||||
docs/node_modules/tailwindcss/dist/colors.d.mts
|
||||
docs/node_modules/tailwindcss/dist/colors.d.ts
|
||||
docs/node_modules/tailwindcss/dist/colors.js
|
||||
docs/node_modules/tailwindcss/dist/colors.mjs
|
||||
docs/node_modules/tailwindcss/dist/default-theme.d.mts
|
||||
docs/node_modules/tailwindcss/dist/default-theme.d.ts
|
||||
docs/node_modules/tailwindcss/dist/default-theme.js
|
||||
docs/node_modules/tailwindcss/dist/default-theme.mjs
|
||||
docs/node_modules/tailwindcss/dist/flatten-color-palette.d.mts
|
||||
docs/node_modules/tailwindcss/dist/flatten-color-palette.d.ts
|
||||
docs/node_modules/tailwindcss/dist/flatten-color-palette.js
|
||||
docs/node_modules/tailwindcss/dist/flatten-color-palette.mjs
|
||||
docs/node_modules/tailwindcss/dist/lib.d.mts
|
||||
docs/node_modules/tailwindcss/dist/lib.d.ts
|
||||
docs/node_modules/tailwindcss/dist/lib.js
|
||||
docs/node_modules/tailwindcss/dist/lib.mjs
|
||||
docs/node_modules/tailwindcss/dist/plugin.d.mts
|
||||
docs/node_modules/tailwindcss/dist/plugin.d.ts
|
||||
docs/node_modules/tailwindcss/dist/plugin.js
|
||||
docs/node_modules/tailwindcss/dist/plugin.mjs
|
||||
docs/node_modules/tailwindcss/dist/resolve-config-BIFUA2FY.d.ts
|
||||
docs/node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts
|
||||
docs/node_modules/tailwindcss/dist/types-B254mqw1.d.mts
|
||||
docs/node_modules/tapable/LICENSE
|
||||
docs/node_modules/tapable/package.json
|
||||
docs/node_modules/tapable/README.md
|
||||
docs/node_modules/tapable/tapable.d.ts
|
||||
docs/node_modules/tapable/lib/AsyncParallelBailHook.js
|
||||
docs/node_modules/tapable/lib/AsyncParallelHook.js
|
||||
docs/node_modules/tapable/lib/AsyncSeriesBailHook.js
|
||||
docs/node_modules/tapable/lib/AsyncSeriesHook.js
|
||||
docs/node_modules/tapable/lib/AsyncSeriesLoopHook.js
|
||||
docs/node_modules/tapable/lib/AsyncSeriesWaterfallHook.js
|
||||
docs/node_modules/tapable/lib/Hook.js
|
||||
docs/node_modules/tapable/lib/HookCodeFactory.js
|
||||
docs/node_modules/tapable/lib/HookMap.js
|
||||
docs/node_modules/tapable/lib/index.js
|
||||
docs/node_modules/tapable/lib/MultiHook.js
|
||||
docs/node_modules/tapable/lib/SyncBailHook.js
|
||||
docs/node_modules/tapable/lib/SyncHook.js
|
||||
docs/node_modules/tapable/lib/SyncLoopHook.js
|
||||
docs/node_modules/tapable/lib/SyncWaterfallHook.js
|
||||
docs/node_modules/tapable/lib/util-browser.js
|
||||
docs/node_modules/to-regex-range/index.js
|
||||
docs/node_modules/to-regex-range/LICENSE
|
||||
docs/node_modules/to-regex-range/package.json
|
||||
docs/node_modules/to-regex-range/README.md
|
||||
docs/package.json
|
||||
docs/package-lock.json
|
||||
|
|
|
|||
75
CHANGELOG.md
75
CHANGELOG.md
|
|
@ -1,3 +1,78 @@
|
|||
## v1.25.0
|
||||
- feat: Display all push rules and allow to enable disable them (Krille)
|
||||
- feat: Inspect and delete push rules (Krille)
|
||||
- feat: Pick share keys with (Krille)
|
||||
- feat: Select share keys with property in security settings (Krille)
|
||||
- feat: Use dynamic gradient for chat bubbles (Krille)
|
||||
- fix: Image search rendering problem (krille-chan)
|
||||
- build: Add locale config for android (krille-chan)
|
||||
- build: Add missing permissions (Krille)
|
||||
- build: Automerge weblate PRs (Krille)
|
||||
- build: Fix ios debug build (krille-chan)
|
||||
- build: Follow up auto merge weblate (Krille)
|
||||
- build: remove weblate auto merge (Krille)
|
||||
- build: Switch to flutter_shortcuts_new (Krille)
|
||||
- build: Update dependencies for flutter (Krille)
|
||||
- build: Update dependencies to remove more flutter android v1 references (Krille)
|
||||
- build: Update fcm_shared_isolate (Krille)
|
||||
- build: Update flutter web uild (Krille)
|
||||
- build: Update flutter_olm to 2.0.0 (Krille)
|
||||
- build: Update gradle version (Krille)
|
||||
- build: Update native_imaging (Krille)
|
||||
- build: Update PAT (Krille)
|
||||
- build: Update to flutter 3.27.4 (Krille)
|
||||
- build: Update weblate auto merge (Krille)
|
||||
- build: Upgrade gradle (Krille)
|
||||
- chore: Add explanation for PlayStore Safety Standards (Krille)
|
||||
- chore: Add medium font (Krille)
|
||||
- chore: Add start to ordered list (Krille)
|
||||
- chore: Add tooltip to links in html (Krille)
|
||||
- chore: Adjust button icon colors (Krille)
|
||||
- chore: Adjust design of adaptive dialogs (krille-chan)
|
||||
- chore: Adjust navrail design (Krille)
|
||||
- chore: Adjust share scaffold dialog design (Krille)
|
||||
- chore: Better connection status indicator (Krille)
|
||||
- chore: Design adjustments (krille-chan)
|
||||
- chore: Make login with matrix id more prominent again (krille-chan)
|
||||
- chore: Message bubble color follow up (krille-chan)
|
||||
- chore: Remove gradle workaround (Krille)
|
||||
- chore: Slightly adjust welcome screen (Krille)
|
||||
- chore: Use UbuntuMono (Krille)
|
||||
- docs: Fix snap store icon (krille-chan)
|
||||
- refactor: Display navigationrail in settings page (krille-chan)
|
||||
- refactor: Follow up fix types in localization files (Krille)
|
||||
- refactor: Improve sso login UX on web (krille-chan)
|
||||
- refactor: Migrate to maintained badge package (Krille)
|
||||
- refactor: Migrate uni_links to app_links (Krille)
|
||||
- refactor: New html rendering (Krille)
|
||||
- refactor: Remove broken callkeep implementation (Krille)
|
||||
- refactor: Remove unused class (krille-chan)
|
||||
- refactor: Switch to maintained qr code package (Krille)
|
||||
- refactor: Switch to ubuntu font (krille-chan)
|
||||
- refactor: Update arb file types (Krille)
|
||||
- Translated using Weblate (Arabic) (Rex_sa)
|
||||
- Translated using Weblate (Basque) (xabirequejo)
|
||||
- Translated using Weblate (Catalan) (fadelkon)
|
||||
- Translated using Weblate (Chinese (Simplified Han script)) (玖然)
|
||||
- Translated using Weblate (Chinese (Simplified Han script)) (大王叫我来巡山)
|
||||
- Translated using Weblate (Chinese (Traditional Han script)) (玖然)
|
||||
- Translated using Weblate (Croatian) (Milo Ivir)
|
||||
- Translated using Weblate (Dutch) (Jelv)
|
||||
- Translated using Weblate (Estonian) (Priit Jõerüüt)
|
||||
- Translated using Weblate (Galician) (josé m)
|
||||
- Translated using Weblate (German) (Christian)
|
||||
- Translated using Weblate (German) (Ettore Atalan)
|
||||
- Translated using Weblate (German) (Jana)
|
||||
- Translated using Weblate (Indonesian) (Linerly)
|
||||
- Translated using Weblate (Irish) (Aindriú Mac Giolla Eoin)
|
||||
- Translated using Weblate (Italian) (Angelo Schirinzi)
|
||||
- Translated using Weblate (Korean) (kdh8219)
|
||||
- Translated using Weblate (Latvian) (Edgars Andersons)
|
||||
- Translated using Weblate (Slovak) (Anonymous)
|
||||
- Translated using Weblate (Spanish) (Alfredo Sola)
|
||||
- Translated using Weblate (Ukrainian) (Bezruchenko Simon)
|
||||
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
|
||||
|
||||
## v1.24.0
|
||||
- build: Add missing libssl library (krille-chan)
|
||||
- build: Update dart_webrtc package (Krille)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
android:requestLegacyExternalStorage="true"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:localeConfig="@xml/locale_config"
|
||||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
|
|
|||
54
android/app/src/main/res/xml/locale_config.xml
Normal file
54
android/app/src/main/res/xml/locale_config.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="ar"/>
|
||||
<locale android:name="be"/>
|
||||
<locale android:name="bn"/>
|
||||
<locale android:name="bo"/>
|
||||
<locale android:name="ca"/>
|
||||
<locale android:name="cs"/>
|
||||
<locale android:name="de"/>
|
||||
<locale android:name="el"/>
|
||||
<locale android:name="en"/>
|
||||
<locale android:name="eo"/>
|
||||
<locale android:name="es"/>
|
||||
<locale android:name="et"/>
|
||||
<locale android:name="eu"/>
|
||||
<locale android:name="fa"/>
|
||||
<locale android:name="fi"/>
|
||||
<locale android:name="fil"/>
|
||||
<locale android:name="fr"/>
|
||||
<locale android:name="ga"/>
|
||||
<locale android:name="gl"/>
|
||||
<locale android:name="he"/>
|
||||
<locale android:name="hi"/>
|
||||
<locale android:name="hr"/>
|
||||
<locale android:name="hu"/>
|
||||
<locale android:name="ia"/>
|
||||
<locale android:name="id"/>
|
||||
<locale android:name="ie"/>
|
||||
<locale android:name="it"/>
|
||||
<locale android:name="ja"/>
|
||||
<locale android:name="ka"/>
|
||||
<locale android:name="ko"/>
|
||||
<locale android:name="lt"/>
|
||||
<locale android:name="lv"/>
|
||||
<locale android:name="nb"/>
|
||||
<locale android:name="nl"/>
|
||||
<locale android:name="pl"/>
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="ro"/>
|
||||
<locale android:name="ru"/>
|
||||
<locale android:name="sk"/>
|
||||
<locale android:name="sl"/>
|
||||
<locale android:name="sr"/>
|
||||
<locale android:name="sv"/>
|
||||
<locale android:name="ta"/>
|
||||
<locale android:name="th"/>
|
||||
<locale android:name="tr"/>
|
||||
<locale android:name="uk"/>
|
||||
<locale android:name="vi"/>
|
||||
<locale android:name="zh"/>
|
||||
<locale android:name="zh"/>
|
||||
</locale-config>
|
||||
|
|
@ -7,30 +7,14 @@ allprojects {
|
|||
|
||||
rootProject.buildDir = '../build'
|
||||
|
||||
// Workaround for building with Flutter 3.24
|
||||
// See: https://github.com/flutter/flutter/issues/153281#issuecomment-2292201697
|
||||
subprojects {
|
||||
afterEvaluate { project ->
|
||||
if (project.extensions.findByName("android") != null) {
|
||||
Integer pluginCompileSdk = project.android.compileSdk
|
||||
if (pluginCompileSdk != null && pluginCompileSdk < 31) {
|
||||
project.logger.error(
|
||||
"Warning: Overriding compileSdk version in Flutter plugin: "
|
||||
+ project.name
|
||||
+ " from "
|
||||
+ pluginCompileSdk
|
||||
+ " to 31 (to work around https://issuetracker.google.com/issues/199180389)."
|
||||
+ "\nIf there is not a new version of " + project.name + ", consider filing an issue against "
|
||||
+ project.name
|
||||
+ " to increase their compileSdk to the latest (otherwise try updating to the latest version)."
|
||||
)
|
||||
project.android {
|
||||
compileSdk 31
|
||||
}
|
||||
}
|
||||
beforeEvaluate { project ->
|
||||
if (project.name == "text_to_speech") {
|
||||
project.buildscript.dependencies.classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
project.evaluationDependsOn(":app")
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ buildscript {
|
|||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.1.3" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
||||
id "com.android.application" version "7.3.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
|
||||
// id "com.google.gms.google-services" version "4.3.8" apply false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3202,6 +3202,10 @@
|
|||
"crossVerifiedDevicesIfEnabled": "Cross verified devices if enabled",
|
||||
"crossVerifiedDevices": "Cross verified devices",
|
||||
"verifiedDevicesOnly": "Verified devices only",
|
||||
"takeAPhoto": "Take a photo",
|
||||
"recordAVideo": "Record a video",
|
||||
"optionalMessage": "(Optional) message...",
|
||||
"notSupportedOnThisDevice": "Not supported on this device",
|
||||
"accountInformation": "Account information",
|
||||
"addGroupDescription": "Add a chat description",
|
||||
"addNewFriend": "Add new friend",
|
||||
|
|
@ -4858,4 +4862,4 @@
|
|||
"downloadGboard": "Download Gboard",
|
||||
"autocorrectNotAvailable": "Unfortunately your platform is not currently supported for this feature. Stay tuned for further development!",
|
||||
"pleaseUpdateApp": "Please update the app to continue."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5457,4 +5457,4 @@
|
|||
},
|
||||
"downloadGboard": "Descargar Gboard",
|
||||
"autocorrectNotAvailable": "Desafortunadamente, tu plataforma no es compatible actualmente con esta función. ¡Mantente atento a futuros desarrollos!"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1306,7 +1306,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"openCamera": "Pildista",
|
||||
"openCamera": "Ava kaamera",
|
||||
"@openCamera": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -3333,5 +3333,13 @@
|
|||
"crossVerifiedDevices": "Risttunnustatud seadmeid",
|
||||
"@crossVerifiedDevices": {},
|
||||
"verifiedDevicesOnly": "Vaid verifitseeritud seadmeid",
|
||||
"@verifiedDevicesOnly": {}
|
||||
"@verifiedDevicesOnly": {},
|
||||
"recordAVideo": "Salvesta video",
|
||||
"@recordAVideo": {},
|
||||
"takeAPhoto": "Pildista",
|
||||
"@takeAPhoto": {},
|
||||
"optionalMessage": "Sõnum (kui soovid lisada)...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "See pole antud seadmes toetatud",
|
||||
"@notSupportedOnThisDevice": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3338,5 +3338,13 @@
|
|||
"shareKeysWithDescription": "Cad iad na gléasanna ar cheart muinín a chur iontu ionas gur féidir leo do chuid teachtaireachtaí a léamh i gcomhráite criptithe?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"verifiedDevicesOnly": "Gléasanna fíoraithe amháin",
|
||||
"@verifiedDevicesOnly": {}
|
||||
"@verifiedDevicesOnly": {},
|
||||
"takeAPhoto": "Glac grianghraf",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Taifead físeán",
|
||||
"@recordAVideo": {},
|
||||
"notSupportedOnThisDevice": "Ní thacaítear leis ar an ngléas seo",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"optionalMessage": "Teachtaireacht (Roghnach)…",
|
||||
"@optionalMessage": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3333,5 +3333,13 @@
|
|||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionalMessage": "(Optativo) mensaxe…",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "Non compatible co dispositivo",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"takeAPhoto": "Facer foto",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Gravar vídeo",
|
||||
"@recordAVideo": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1376,7 +1376,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"synchronizingPleaseWait": "Synchronizacja… Proszę czekać.",
|
||||
"synchronizingPleaseWait": "Synchronizowanie… Proszę czekać.",
|
||||
"@synchronizingPleaseWait": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -3182,5 +3182,160 @@
|
|||
}
|
||||
},
|
||||
"kickUserDescription": "Użytkownik jest wyrzucony z czatu, ale nie zbanowany. Do czatu publicznego może dołączyć ponownie.",
|
||||
"@kickUserDescription": {}
|
||||
"@kickUserDescription": {},
|
||||
"appWantsToUseForLogin": "Użyj serwera '{server}' do zalogowania się",
|
||||
"@appWantsToUseForLogin": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appWantsToUseForLoginDescription": "Niniejszym zezwalasz aplikacji i witrynie na udostępnianie informacji o sobie.",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"open": "Otwórz",
|
||||
"@open": {},
|
||||
"contentNotificationSettings": "Ustawienia powiadomień o treści",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "Ogólne ustawienia powiadomień",
|
||||
"@generalNotificationSettings": {},
|
||||
"roomNotificationSettings": "Ustawienia powiadomień w pokoju",
|
||||
"@roomNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "Ustawienia powiadomień dla użytkownika",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "Inne ustawienia powiadomień",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "Zawiera nazwę użytkownika",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleContainsUserNameDescription": "Powiadamia użytkownika kiedy wiadomość zawiera jego nazwę.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMaster": "Wycisz wszystkie powiadomienia",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleMasterDescription": "Zastępuje wszystkie inne reguły i wyłącza wszystkie powiadomienia.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleSuppressNotices": "Stłum automatyczne wiadomości",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleSuppressNoticesDescription": "Tłumi powiadomienia z automatycznych klientów, takich jak boty.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMe": "Zaproszenia",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleInviteForMeDescription": "Powiadamia o zaproszeniach do pokoju.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleMemberEvent": "Zdarzenia członków pokoju",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleMemberEventDescription": "Tłumi powiadomienia o zmianach członkostwa w pokoju.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsUserMention": "Wzmianki",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleIsUserMentionDescription": "Powiadamia o byciu wzmiankowanym w wiadomości.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayName": "Zawiera nazwę wyświetlaną",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "Powiadamia osobę o wiadomości zawierającej jej nazwę wyświetlaną.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "Wzmianki pokoju",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleIsRoomMentionDescription": "Powiadamia o wzmiankowaniu całego pokoju.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleRoomnotif": "Powiadomienia w pokoju",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "Powiadamia o wiadomości zawierającej „@room”.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleTombstone": "Nagrobki",
|
||||
"@notificationRuleTombstone": {},
|
||||
"notificationRuleTombstoneDescription": "Powiadamia o komunikatach dezaktywacji pokojów.",
|
||||
"@notificationRuleTombstoneDescription": {},
|
||||
"notificationRuleReaction": "Reakcje",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleReactionDescription": "Tłumi powiadomienia o reakcjach.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleSuppressEdits": "Stłum edycje",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleSuppressEditsDescription": "Tłumi powiadomienia o edycjach wiadomości.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleCall": "Połączenia",
|
||||
"@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": {},
|
||||
"notificationRuleEncryptedRoomOneToOne": "Szyfrowane pokoje jeden na jeden",
|
||||
"@notificationRuleEncryptedRoomOneToOne": {},
|
||||
"notificationRuleCallDescription": "Powiadamia o przychodzących połączeniach.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"notificationRuleRoomOneToOne": "Pokoje jeden na jeden",
|
||||
"@notificationRuleRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOneDescription": "Powiadamia o wiadomościach w pokojach jeden na jeden (one-to-one).",
|
||||
"@notificationRuleRoomOneToOneDescription": {},
|
||||
"notificationRuleMessage": "Wiadomości",
|
||||
"@notificationRuleMessage": {},
|
||||
"unknownPushRule": "Nieznana reguła: '{rule}'",
|
||||
"@unknownPushRule": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"rule": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Powiadamia o wiadomościach w szyfrowanych pokojach jeden na jeden (one-to-one).",
|
||||
"@notificationRuleEncryptedRoomOneToOneDescription": {},
|
||||
"notificationRuleEncrypted": "Zaszyfrowane pokoje",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"notificationRuleJitsi": "Jitsi",
|
||||
"@notificationRuleJitsi": {},
|
||||
"notificationRuleServerAcl": "Stłum komunikaty o listach kontroli dostępu serwerów pokojów",
|
||||
"@notificationRuleServerAcl": {},
|
||||
"notificationRuleJitsiDescription": "Powiadamia o komunikatach widżetów Jitsi.",
|
||||
"@notificationRuleJitsiDescription": {},
|
||||
"notificationRuleMessageDescription": "Powiadamia o ogólnych wiadomościach.",
|
||||
"@notificationRuleMessageDescription": {},
|
||||
"notificationRuleEncryptedDescription": "Powiadamia o wiadomościach w zaszyfrowanych pokojach.",
|
||||
"@notificationRuleEncryptedDescription": {},
|
||||
"notificationRuleServerAclDescription": "Tłumi powiadomienia o komunikatach o listach kontroli dostępu (ACL) serwerów pokojów.",
|
||||
"@notificationRuleServerAclDescription": {},
|
||||
"newChatRequest": "📩 Nowa prośba o czat",
|
||||
"@newChatRequest": {},
|
||||
"synchronizingPleaseWaitCounter": " Synchronizowanie… ({percentage}%)",
|
||||
"@synchronizingPleaseWaitCounter": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"percentage": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"waitingForServer": "Oczekiwanie na serwer...",
|
||||
"@waitingForServer": {},
|
||||
"appIntroduction": "FluffyChat umożliwia czatowanie ze znajomymi za pośrednictwem różnych komunikatorów. Dowiedz się więcej na stronie https://matrix.org lub kliknij na *Kontynuuj*.",
|
||||
"@appIntroduction": {},
|
||||
"previous": "Poprzedni",
|
||||
"@previous": {},
|
||||
"otherPartyNotLoggedIn": "Druga strona nie jest obecnie zalogowana i dlatego nie może odbierać wiadomości!",
|
||||
"@otherPartyNotLoggedIn": {},
|
||||
"deletePushRuleCanNotBeUndone": "Jeśli skasujesz to ustawienie powiadomień, nie będzie się dało tego cofnąć.",
|
||||
"@deletePushRuleCanNotBeUndone": {},
|
||||
"more": "Więcej",
|
||||
"@more": {},
|
||||
"shareKeysWith": "Udostępnij klucze...",
|
||||
"@shareKeysWith": {},
|
||||
"crossVerifiedDevicesIfEnabled": "Urządzenia zweryfikowane krzyżowo, jeśli włączone",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"crossVerifiedDevices": "Urządzenia zweryfikowane krzyżowo",
|
||||
"@crossVerifiedDevices": {},
|
||||
"takeAPhoto": "Zrób zdjęcie",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Nagraj film",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "(Opcjonalna) wiadomość...",
|
||||
"@optionalMessage": {},
|
||||
"verifiedDevicesOnly": "Tylko zweryfikowane urządzenia",
|
||||
"@verifiedDevicesOnly": {},
|
||||
"shareKeysWithDescription": "Które urządzenia powinny być zaufane, aby mogły odczytywać Twoje wiadomości w zaszyfrowanych czatach?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"allDevices": "Wszystkie urządzenia",
|
||||
"@allDevices": {},
|
||||
"notSupportedOnThisDevice": "Niewspierane na tym urządzeniu",
|
||||
"@notSupportedOnThisDevice": {}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3740,4 +3740,4 @@
|
|||
},
|
||||
"downloadGboard": "Tải Gboard",
|
||||
"autocorrectNotAvailable": "Rất tiếc, nền tảng của bạn hiện không được hỗ trợ cho tính năng này. Hãy theo dõi để biết thêm thông tin phát triển!"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
</a>
|
||||
<a href="https://fluffychat.im/web">
|
||||
<img src="browser-badge.png" class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"></a>
|
||||
<a href="https://snapcraft.io/fluffychat"><img src="snap-store-black.svg"
|
||||
<a href="https://snapcraft.io/fluffychat"><img src="snap-store-badge.svg"
|
||||
class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"></a>
|
||||
<a href="https://flathub.org/apps/details/im.fluffychat.Fluffychat"><img src="flathub-badge-en.png"
|
||||
class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"></a>
|
||||
|
|
|
|||
BIN
fonts/Ubuntu/Ubuntu-Medium.ttf
Normal file
BIN
fonts/Ubuntu/Ubuntu-Medium.ttf
Normal file
Binary file not shown.
|
|
@ -169,8 +169,6 @@ abstract class AppRoutes {
|
|||
FluffyThemes.isColumnMode(context) &&
|
||||
state.fullPath?.startsWith('/rooms/settings') == false
|
||||
? TwoColumnLayout(
|
||||
displayNavigationRail:
|
||||
state.path?.startsWith('/rooms/settings') != true,
|
||||
mainView: ChatList(
|
||||
activeChat: state.pathParameters['roomid'],
|
||||
displayNavigationRail:
|
||||
|
|
@ -319,9 +317,8 @@ abstract class AppRoutes {
|
|||
state,
|
||||
FluffyThemes.isColumnMode(context)
|
||||
? TwoColumnLayout(
|
||||
mainView: const Settings(),
|
||||
mainView: Settings(key: state.pageKey),
|
||||
sideView: child,
|
||||
displayNavigationRail: false,
|
||||
)
|
||||
: child,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -34,4 +34,5 @@ abstract class SettingKeys {
|
|||
'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';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import 'app_config.dart';
|
|||
abstract class FluffyThemes {
|
||||
static const double columnWidth = 380.0;
|
||||
|
||||
static const double navRailWidth = 64.0;
|
||||
static const double navRailWidth = 80.0;
|
||||
|
||||
static bool isColumnModeByWidth(double width) =>
|
||||
width > columnWidth * 2 + navRailWidth;
|
||||
|
|
@ -77,7 +77,9 @@ abstract class FluffyThemes {
|
|||
// causes memory leak on iOS
|
||||
// textTheme: fallbackTextTheme,
|
||||
// Pangea#
|
||||
dividerColor: colorScheme.surfaceContainer,
|
||||
dividerColor: brightness == Brightness.dark
|
||||
? colorScheme.surfaceContainerHighest
|
||||
: colorScheme.surfaceContainer,
|
||||
popupMenuTheme: PopupMenuThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
|
|
@ -154,3 +156,18 @@ extension on Brightness {
|
|||
Brightness get reversed =>
|
||||
this == Brightness.dark ? Brightness.light : Brightness.dark;
|
||||
}
|
||||
|
||||
extension BubbleColorTheme on ThemeData {
|
||||
Color get bubbleColor => brightness == Brightness.light
|
||||
? colorScheme.primary
|
||||
: colorScheme.primaryContainer;
|
||||
Color get onBubbleColor => brightness == Brightness.light
|
||||
? colorScheme.onPrimary
|
||||
: colorScheme.onPrimaryContainer;
|
||||
|
||||
Color get secondaryBubbleColor => HSLColor.fromColor(
|
||||
brightness == Brightness.light
|
||||
? colorScheme.tertiary
|
||||
: colorScheme.tertiaryContainer,
|
||||
).withSaturation(0.5).toColor();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final room = controller.room;
|
||||
if (controller.selectedEvents.isNotEmpty) {
|
||||
return Text(controller.selectedEvents.length.toString());
|
||||
return Text(
|
||||
controller.selectedEvents.length.toString(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
hoverColor: Colors.transparent,
|
||||
|
|
@ -98,12 +103,17 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||
)
|
||||
: Row(
|
||||
children: [
|
||||
Icon(
|
||||
status.icon,
|
||||
size: 12,
|
||||
color: status.error != null
|
||||
? Theme.of(context).colorScheme.error
|
||||
: null,
|
||||
SizedBox.square(
|
||||
dimension: 10,
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 1,
|
||||
value: status.progress,
|
||||
valueColor: status.error != null
|
||||
? AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.error,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ class ChatEventList extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final colors = [
|
||||
theme.secondaryBubbleColor,
|
||||
theme.bubbleColor,
|
||||
];
|
||||
|
||||
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
|
||||
|
||||
final events = timeline.events.filterByVisibleInGui();
|
||||
|
|
@ -131,8 +138,6 @@ class ChatEventList extends StatelessWidget {
|
|||
// #Pangea
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => controller.requestHistory);
|
||||
// WidgetsBinding.instance
|
||||
// .addPostFrameCallback(controller.requestHistory);
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: AppConfig.toolbarMaxHeight),
|
||||
|
|
@ -144,6 +149,8 @@ class ChatEventList extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
);
|
||||
// WidgetsBinding.instance
|
||||
// .addPostFrameCallback(controller.requestHistory);
|
||||
// return Center(
|
||||
// child: IconButton(
|
||||
// onPressed: controller.requestHistory,
|
||||
|
|
@ -161,6 +168,7 @@ class ChatEventList extends StatelessWidget {
|
|||
}
|
||||
i--;
|
||||
|
||||
// The message at this index:
|
||||
final event = events[i];
|
||||
final animateIn = animateInEventIndex != null &&
|
||||
timeline.events.length > animateInEventIndex &&
|
||||
|
|
@ -227,6 +235,8 @@ class ChatEventList extends StatelessWidget {
|
|||
i + 1 < events.length ? events[i + 1] : null,
|
||||
previousEvent: i > 0 ? events[i - 1] : null,
|
||||
wallpaperMode: hasWallpaper,
|
||||
scrollController: controller.scrollController,
|
||||
colors: colors,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ class ChatInputRow extends StatelessWidget {
|
|||
controller.emojiPickerType == EmojiPickerType.reaction) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
// #Pangea
|
||||
// const height = 48.0;
|
||||
const height = AppConfig.defaultFooterHeight;
|
||||
// Pangea#
|
||||
|
||||
if (!controller.room.otherPartyCanReceiveMessages) {
|
||||
return Center(
|
||||
|
|
@ -53,9 +57,6 @@ class ChatInputRow extends StatelessWidget {
|
|||
}
|
||||
|
||||
// #Pangea
|
||||
// const height = 48.0;
|
||||
const height = AppConfig.defaultFooterHeight;
|
||||
|
||||
final activel1 =
|
||||
controller.pangeaController.languageController.activeL1Model();
|
||||
final activel2 =
|
||||
|
|
@ -109,18 +110,18 @@ class ChatInputRow extends StatelessWidget {
|
|||
),
|
||||
// #Pangea
|
||||
// else
|
||||
// SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// onPressed: controller.forwardEventsAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// const Icon(Icons.keyboard_arrow_left_outlined),
|
||||
// Text(L10n.of(context).forward),
|
||||
// ],
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: height,
|
||||
// child: TextButton(
|
||||
// onPressed: controller.forwardEventsAction,
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// const Icon(Icons.keyboard_arrow_left_outlined),
|
||||
// Text(L10n.of(context).forward),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// controller.selectedEvents.length == 1
|
||||
// ? controller.selectedEvents.first
|
||||
// .getDisplayEvent(controller.timeline!)
|
||||
|
|
@ -374,8 +375,8 @@ class ChatInputRow extends StatelessWidget {
|
|||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(height),
|
||||
),
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
backgroundColor: theme.bubbleColor,
|
||||
foregroundColor: theme.onBubbleColor,
|
||||
child: const Icon(Icons.mic_none_outlined),
|
||||
)
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ class ChatView extends StatelessWidget {
|
|||
}
|
||||
// } else if (!controller.room.isArchived) {
|
||||
// return [
|
||||
// if (Matrix.of(context).voipPlugin != null &&
|
||||
// if (AppConfig.experimentalVoip &&
|
||||
// Matrix.of(context).voipPlugin != null &&
|
||||
// controller.room.isDirectChat)
|
||||
// IconButton(
|
||||
// onPressed: controller.onPhoneButtonTap,
|
||||
|
|
@ -199,28 +200,31 @@ class ChatView extends StatelessWidget {
|
|||
actionsIconTheme: IconThemeData(
|
||||
color: controller.selectedEvents.isEmpty
|
||||
? null
|
||||
: theme.colorScheme.primary,
|
||||
: theme.colorScheme.tertiary,
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
leading: controller.selectMode
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: controller.clearSelectedEvents,
|
||||
tooltip: L10n.of(context).close,
|
||||
color: theme.colorScheme.primary,
|
||||
color: theme.colorScheme.tertiary,
|
||||
)
|
||||
: StreamBuilder<Object>(
|
||||
stream: Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where((syncUpdate) => syncUpdate.hasRoomUpdate),
|
||||
builder: (context, _) => UnreadRoomsBadge(
|
||||
filter: (r) => r.id != controller.roomId,
|
||||
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
||||
child: const Center(child: BackButton()),
|
||||
),
|
||||
),
|
||||
titleSpacing: 0,
|
||||
: FluffyThemes.isColumnMode(context)
|
||||
? null
|
||||
: StreamBuilder<Object>(
|
||||
stream:
|
||||
Matrix.of(context).client.onSync.stream.where(
|
||||
(syncUpdate) => syncUpdate.hasRoomUpdate,
|
||||
),
|
||||
builder: (context, _) => UnreadRoomsBadge(
|
||||
filter: (r) => r.id != controller.roomId,
|
||||
badgePosition:
|
||||
BadgePosition.topEnd(end: 8, top: 4),
|
||||
child: const Center(child: BackButton()),
|
||||
),
|
||||
),
|
||||
titleSpacing: FluffyThemes.isColumnMode(context) ? 24 : 0,
|
||||
title: ChatAppBarTitle(controller),
|
||||
actions: _appBarActions(context),
|
||||
bottom: PreferredSize(
|
||||
|
|
|
|||
|
|
@ -113,21 +113,24 @@ class HtmlMessage extends StatelessWidget {
|
|||
/// We add line breaks before these tags:
|
||||
static const Set<String> blockHtmlTags = {
|
||||
'p',
|
||||
'ul',
|
||||
'ol',
|
||||
'pre',
|
||||
'div',
|
||||
'table',
|
||||
'details',
|
||||
'blockquote',
|
||||
};
|
||||
|
||||
/// We add line breaks before these tags:
|
||||
static const Set<String> fullLineHtmlTag = {
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'ul',
|
||||
'ol',
|
||||
'li',
|
||||
'pre',
|
||||
'br',
|
||||
'div',
|
||||
'table',
|
||||
'blockquote',
|
||||
'details',
|
||||
};
|
||||
|
||||
// #Pangea
|
||||
|
|
@ -202,17 +205,24 @@ class HtmlMessage extends StatelessWidget {
|
|||
dom.NodeList nodes,
|
||||
BuildContext context, {
|
||||
int depth = 1,
|
||||
}) =>
|
||||
[
|
||||
for (var i = 0; i < nodes.length; i++) ...[
|
||||
if (i > 0 &&
|
||||
nodes[i] is dom.Element &&
|
||||
blockHtmlTags.contains((nodes[i] as dom.Element).localName))
|
||||
const TextSpan(text: '\n'), // Add linebreak
|
||||
// Actually render the node child:
|
||||
_renderHtml(nodes[i], context, depth: depth + 1),
|
||||
}) {
|
||||
final onlyElements = nodes.whereType<dom.Element>().toList();
|
||||
return [
|
||||
for (var i = 0; i < nodes.length; i++) ...[
|
||||
// Actually render the node child:
|
||||
_renderHtml(nodes[i], context, depth: depth + 1),
|
||||
// Add linebreaks between blocks:
|
||||
if (nodes[i] is dom.Element &&
|
||||
onlyElements.indexOf(nodes[i] as dom.Element) <
|
||||
onlyElements.length - 1) ...[
|
||||
if (blockHtmlTags.contains((nodes[i] as dom.Element).localName))
|
||||
const TextSpan(text: '\n\n'),
|
||||
if (fullLineHtmlTag.contains((nodes[i] as dom.Element).localName))
|
||||
const TextSpan(text: '\n'),
|
||||
],
|
||||
];
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/// Transforms a Node to an InlineSpan.
|
||||
InlineSpan _renderHtml(
|
||||
|
|
@ -287,6 +297,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
).merge(TextStyle(backgroundColor: backgroundColor)),
|
||||
);
|
||||
// Pangea#
|
||||
case 'br':
|
||||
return const TextSpan(text: '\n');
|
||||
case 'a':
|
||||
final href = node.attributes['href'];
|
||||
if (href == null) continue block;
|
||||
|
|
@ -423,7 +435,10 @@ class HtmlMessage extends StatelessWidget {
|
|||
horizontal: 8,
|
||||
vertical: isInline ? 0 : 8,
|
||||
),
|
||||
textStyle: TextStyle(fontSize: fontSize),
|
||||
textStyle: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontFamily: 'UbuntuMono',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ class ImageBubble extends StatelessWidget {
|
|||
final Event event;
|
||||
final bool tapToView;
|
||||
final BoxFit fit;
|
||||
final bool maxSize;
|
||||
final Color? backgroundColor;
|
||||
final Color? textColor;
|
||||
final Color? linkColor;
|
||||
|
|
@ -29,7 +28,6 @@ class ImageBubble extends StatelessWidget {
|
|||
const ImageBubble(
|
||||
this.event, {
|
||||
this.tapToView = true,
|
||||
this.maxSize = true,
|
||||
this.backgroundColor,
|
||||
this.fit = BoxFit.contain,
|
||||
this.thumbnailOnly = true,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
|
@ -42,12 +44,14 @@ class Message extends StatelessWidget {
|
|||
final bool animateIn;
|
||||
final void Function()? resetAnimateIn;
|
||||
final bool wallpaperMode;
|
||||
final ScrollController scrollController;
|
||||
// #Pangea
|
||||
final bool immersionMode;
|
||||
final ChatController controller;
|
||||
final MessageOverlayController? overlayController;
|
||||
final bool isButton;
|
||||
// Pangea#
|
||||
final List<Color> colors;
|
||||
|
||||
const Message(
|
||||
this.event, {
|
||||
|
|
@ -66,12 +70,14 @@ class Message extends StatelessWidget {
|
|||
this.animateIn = false,
|
||||
this.resetAnimateIn,
|
||||
this.wallpaperMode = false,
|
||||
required this.scrollController,
|
||||
// #Pangea
|
||||
required this.immersionMode,
|
||||
required this.controller,
|
||||
this.overlayController,
|
||||
this.isButton = false,
|
||||
// Pangea#
|
||||
required this.colors,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -155,17 +161,13 @@ class Message extends StatelessWidget {
|
|||
previousEvent!.senderId == event.senderId &&
|
||||
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
|
||||
// #Pangea
|
||||
// final textColor =
|
||||
// ownMessage ? theme.onBubbleColor : theme.colorScheme.onSurface;
|
||||
final textColor = ownMessage
|
||||
?
|
||||
// #Pangea
|
||||
// theme.brightness == Brightness.light
|
||||
// ? theme.colorScheme.onPrimary
|
||||
// : theme.colorScheme.onPrimaryContainer
|
||||
ThemeData.dark().colorScheme.onPrimary
|
||||
// Pangea#
|
||||
? ThemeData.dark().colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
|
||||
// #Pangea
|
||||
// final linkColor = ownMessage
|
||||
// ? theme.brightness == Brightness.light
|
||||
// ? theme.colorScheme.primaryFixed
|
||||
|
|
@ -210,12 +212,11 @@ class Message extends StatelessWidget {
|
|||
}.contains(event.messageType);
|
||||
|
||||
if (ownMessage) {
|
||||
// #Pangea
|
||||
// color =
|
||||
// displayEvent.status.isError ? Colors.redAccent : theme.bubbleColor;
|
||||
color = displayEvent.status.isError
|
||||
? Colors.redAccent
|
||||
// #Pangea
|
||||
// : theme.brightness == Brightness.light
|
||||
// ? theme.colorScheme.primary
|
||||
// : theme.colorScheme.primaryContainer;
|
||||
: Color.alphaBlend(
|
||||
Colors.white.withAlpha(180),
|
||||
ThemeData.dark().colorScheme.primary,
|
||||
|
|
@ -439,10 +440,17 @@ class Message extends StatelessWidget {
|
|||
event.eventId,
|
||||
)
|
||||
.link,
|
||||
// child: BubbleBackground(
|
||||
// colors: colors,
|
||||
// ignore: noBubble || !ownMessage,
|
||||
// scrollController: scrollController,
|
||||
// Pangea#
|
||||
child: Container(
|
||||
// #Pangea
|
||||
key: overlayController != null
|
||||
? LayerLinkAndKey('overlay_msg')
|
||||
.key
|
||||
? LayerLinkAndKey(
|
||||
'overlay_msg',
|
||||
).key
|
||||
: MatrixState.pAnyState
|
||||
.layerLinkAndKey(
|
||||
event.eventId,
|
||||
|
|
@ -580,8 +588,9 @@ class Message extends StatelessWidget {
|
|||
.msgUseType
|
||||
.iconView(
|
||||
context,
|
||||
textColor
|
||||
.withAlpha(164),
|
||||
textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
|
|
@ -597,7 +606,9 @@ class Message extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor
|
||||
.withAlpha(164),
|
||||
.withAlpha(
|
||||
164,
|
||||
),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
|
|
@ -798,3 +809,72 @@ class Message extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BubbleBackground extends StatelessWidget {
|
||||
const BubbleBackground({
|
||||
super.key,
|
||||
required this.scrollController,
|
||||
required this.colors,
|
||||
required this.ignore,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final ScrollController scrollController;
|
||||
final List<Color> colors;
|
||||
final bool ignore;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (ignore) return child;
|
||||
return CustomPaint(
|
||||
painter: BubblePainter(
|
||||
repaint: scrollController,
|
||||
colors: colors,
|
||||
context: context,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BubblePainter extends CustomPainter {
|
||||
BubblePainter({
|
||||
required this.context,
|
||||
required this.colors,
|
||||
required super.repaint,
|
||||
});
|
||||
|
||||
final BuildContext context;
|
||||
final List<Color> colors;
|
||||
ScrollableState? _scrollable;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final scrollable = _scrollable ??= Scrollable.of(context);
|
||||
final scrollableBox = scrollable.context.findRenderObject() as RenderBox;
|
||||
final scrollableRect = Offset.zero & scrollableBox.size;
|
||||
final bubbleBox = context.findRenderObject() as RenderBox;
|
||||
|
||||
final origin =
|
||||
bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox);
|
||||
final paint = Paint()
|
||||
..shader = ui.Gradient.linear(
|
||||
scrollableRect.topCenter,
|
||||
scrollableRect.bottomCenter,
|
||||
colors,
|
||||
[0.0, 1.0],
|
||||
TileMode.clamp,
|
||||
Matrix4.translationValues(-origin.dx, -origin.dy, 0.0).storage,
|
||||
);
|
||||
canvas.drawRect(Offset.zero & size, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(BubblePainter oldDelegate) {
|
||||
final scrollable = Scrollable.of(context);
|
||||
final oldScrollable = _scrollable;
|
||||
_scrollable = scrollable;
|
||||
return scrollable.position != oldScrollable?.position;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ class InputBar extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
commandExample(command),
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
),
|
||||
Text(
|
||||
hint,
|
||||
|
|
@ -265,7 +265,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: 'monospace')),
|
||||
child: Text(label, style: const TextStyle(fontFamily: 'UbuntuMono')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,8 +161,9 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
var sendStr = L10n.of(context).sendFile;
|
||||
final uniqueMimeType = widget.files
|
||||
final uniqueFileType = widget.files
|
||||
.map((file) => file.mimeType ?? lookupMimeType(file.name))
|
||||
.map((mimeType) => mimeType?.split('/').first)
|
||||
.toSet()
|
||||
.singleOrNull;
|
||||
|
||||
|
|
@ -175,15 +176,15 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
.join(', ')
|
||||
.toUpperCase();
|
||||
|
||||
if (uniqueMimeType?.startsWith('image') ?? false) {
|
||||
if (uniqueFileType == 'image') {
|
||||
if (widget.files.length == 1) {
|
||||
sendStr = L10n.of(context).sendImage;
|
||||
} else {
|
||||
sendStr = L10n.of(context).sendImages(widget.files.length);
|
||||
}
|
||||
} else if (uniqueMimeType?.startsWith('audio') ?? false) {
|
||||
} else if (uniqueFileType == 'audio') {
|
||||
sendStr = L10n.of(context).sendAudio;
|
||||
} else if (uniqueMimeType?.startsWith('video') ?? false) {
|
||||
} else if (uniqueFileType == 'video') {
|
||||
sendStr = L10n.of(context).sendVideo;
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +202,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
if (uniqueMimeType?.startsWith('image') ?? false)
|
||||
if (uniqueFileType == 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: SizedBox(
|
||||
|
|
@ -233,17 +234,17 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (uniqueMimeType?.startsWith('image') != true)
|
||||
if (uniqueFileType != 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
uniqueMimeType == null
|
||||
uniqueFileType == null
|
||||
? Icons.description_outlined
|
||||
: uniqueMimeType.startsWith('video')
|
||||
: uniqueFileType == 'video'
|
||||
? Icons.video_file_outlined
|
||||
: uniqueMimeType.startsWith('audio')
|
||||
: uniqueFileType == 'audio'
|
||||
? Icons.audio_file_outlined
|
||||
: Icons.description_outlined,
|
||||
size: 32,
|
||||
|
|
@ -272,9 +273,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
),
|
||||
),
|
||||
// Workaround for SwitchListTile.adaptive crashes in CupertinoDialog
|
||||
if (uniqueMimeType != null &&
|
||||
(uniqueMimeType.startsWith('image') ||
|
||||
uniqueMimeType.startsWith('video')))
|
||||
if ({'image', 'video'}.contains(uniqueFileType))
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
|
@ -282,7 +281,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
.contains(theme.platform))
|
||||
CupertinoSwitch(
|
||||
value: compress,
|
||||
onChanged: uniqueMimeType.startsWith('video') &&
|
||||
onChanged: uniqueFileType == 'video' &&
|
||||
!PlatformInfos.isMobile
|
||||
? null
|
||||
: (v) => setState(() => compress = v),
|
||||
|
|
@ -290,7 +289,7 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
else
|
||||
Switch.adaptive(
|
||||
value: compress,
|
||||
onChanged: uniqueMimeType.startsWith('video') &&
|
||||
onChanged: uniqueFileType == 'video' &&
|
||||
!PlatformInfos.isMobile
|
||||
? null
|
||||
: (v) => setState(() => compress = v),
|
||||
|
|
|
|||
|
|
@ -94,7 +94,11 @@ class ParticipantListItem extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text(user.id),
|
||||
subtitle: Text(
|
||||
user.id,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
leading: Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
void onChatTap(Room room) async {
|
||||
if (room.membership == Membership.invite) {
|
||||
// #Pangea
|
||||
await showInviteDialog(room);
|
||||
return;
|
||||
// final joinResult = await showFutureLoadingDialog(
|
||||
|
|
@ -184,6 +185,7 @@ class ChatListController extends State<ChatList>
|
|||
// exceptionContext: ExceptionContext.joinRoom,
|
||||
// );
|
||||
// if (joinResult.error != null) return;
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
if (room.membership == Membership.ban) {
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ class ChatListViewBody extends StatelessWidget {
|
|||
style: TextStyle(
|
||||
fontWeight: filter ==
|
||||
controller.activeFilter
|
||||
? FontWeight.bold
|
||||
? FontWeight.w500
|
||||
: FontWeight.normal,
|
||||
color: filter ==
|
||||
controller.activeFilter
|
||||
|
|
@ -320,15 +320,6 @@ class ChatListViewBody extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Icon(
|
||||
// CupertinoIcons.chat_bubble_2,
|
||||
// size: 128,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
||||
import 'package:fluffychat/utils/sync_status_localization.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
||||
class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
final ChatListController controller;
|
||||
|
|
@ -79,12 +79,21 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
color: theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
status.icon,
|
||||
color: status.error != null
|
||||
? theme.colorScheme.error
|
||||
: theme.colorScheme.onPrimaryContainer,
|
||||
size: 18,
|
||||
: Container(
|
||||
margin: const EdgeInsets.all(12),
|
||||
width: 8,
|
||||
height: 8,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
value: status.progress,
|
||||
valueColor: status.error != null
|
||||
? AlwaysStoppedAnimation<Color>(
|
||||
theme.colorScheme.error,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
suffixIcon: controller.isSearchMode && globalSearch
|
||||
? controller.isSearching
|
||||
|
|
@ -115,14 +124,16 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
maxLines: 2,
|
||||
),
|
||||
)
|
||||
// #Pangea
|
||||
// : SizedBox(
|
||||
// width: 0,
|
||||
// child: ClientChooserButton(controller),
|
||||
// ),
|
||||
: const SizedBox(
|
||||
width: 0,
|
||||
child: ClientChooserButton(
|
||||
// #Pangea
|
||||
// controller,
|
||||
// Pangea#
|
||||
),
|
||||
child: ClientChooserButton(),
|
||||
),
|
||||
// Pangea#
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class ChatListItem extends StatelessWidget {
|
|||
return forgetResult.isValue;
|
||||
}
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).leave,
|
||||
|
|
@ -236,9 +235,11 @@ class ChatListItem extends StatelessWidget {
|
|||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: unread || room.hasNewMessages
|
||||
? const TextStyle(fontWeight: FontWeight.bold)
|
||||
: null,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.w500
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isMuted)
|
||||
|
|
@ -249,8 +250,7 @@ class ChatListItem extends StatelessWidget {
|
|||
size: 16,
|
||||
),
|
||||
),
|
||||
if (room.isFavourite ||
|
||||
room.membership == Membership.invite)
|
||||
if (room.isFavourite)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: hasNotifications ? 4.0 : 0.0,
|
||||
|
|
@ -269,16 +269,15 @@ class ChatListItem extends StatelessWidget {
|
|||
child: Text(
|
||||
lastEvent.originServerTs.localizedTimeShort(context),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: unread
|
||||
? theme.colorScheme.secondary
|
||||
: theme.textTheme.bodyMedium!.color,
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if (typingText.isEmpty &&
|
||||
|
|
@ -315,6 +314,8 @@ class ChatListItem extends StatelessWidget {
|
|||
// Pangea#
|
||||
(room.summary.mJoinedMemberCount ?? 1),
|
||||
),
|
||||
style:
|
||||
TextStyle(color: theme.colorScheme.outline),
|
||||
)
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
|
|
@ -370,12 +371,21 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? isDirectChat
|
||||
? L10n.of(context)
|
||||
.invitePrivateChat
|
||||
// #Pangea
|
||||
// : L10n.of(context).inviteGroupChat
|
||||
: L10n.of(context).inviteChat
|
||||
? 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,
|
||||
|
|
@ -384,12 +394,9 @@ class ChatListItem extends StatelessWidget {
|
|||
room.notificationCount >= 1 ? 2 : 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme
|
||||
.colorScheme.onSurfaceVariant,
|
||||
color: unread || room.hasNewMessages
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.outline,
|
||||
decoration:
|
||||
room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
|
|
@ -402,6 +409,7 @@ class ChatListItem extends StatelessWidget {
|
|||
AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||
height: unreadBubbleSize,
|
||||
width:
|
||||
|
|
@ -413,29 +421,28 @@ class ChatListItem extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
color: room.highlightCount > 0 ||
|
||||
room.membership == Membership.invite
|
||||
? Colors.red
|
||||
? theme.colorScheme.onError
|
||||
: hasNotifications || room.markedUnread
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
child: Center(
|
||||
child: hasNotifications
|
||||
? Text(
|
||||
room.notificationCount.toString(),
|
||||
style: TextStyle(
|
||||
color: room.highlightCount > 0
|
||||
? Colors.white
|
||||
: hasNotifications
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme
|
||||
.colorScheme.onPrimaryContainer,
|
||||
fontSize: 13,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,17 +2,11 @@ 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/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/navi_rail_item.dart';
|
||||
import 'package:fluffychat/pangea/chat_list/widgets/chat_list_view_body_wrapper.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'package:fluffychat/widgets/navigation_rail.dart';
|
||||
|
||||
class ChatListView extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
|
|
@ -21,7 +15,6 @@ class ChatListView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
return PopScope(
|
||||
canPop: !controller.isSearchMode && controller.activeSpaceId == null,
|
||||
onPopInvokedWithResult: (pop, _) {
|
||||
|
|
@ -39,81 +32,10 @@ class ChatListView extends StatelessWidget {
|
|||
children: [
|
||||
if (FluffyThemes.isColumnMode(context) &&
|
||||
controller.widget.displayNavigationRail) ...[
|
||||
StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
final allSpaces = Matrix.of(context)
|
||||
.client
|
||||
.rooms
|
||||
.where((room) => room.isSpace);
|
||||
final rootSpaces = allSpaces
|
||||
.where(
|
||||
(space) => !allSpaces.any(
|
||||
(parentSpace) => parentSpace.spaceChildren
|
||||
.any((child) => child.roomId == space.id),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return SizedBox(
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: rootSpaces.length + 2,
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
return NaviRailItem(
|
||||
isSelected: controller.activeSpaceId == null,
|
||||
onTap: controller.clearActiveSpace,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
selectedIcon: const Icon(Icons.forum),
|
||||
toolTip: L10n.of(context).chats,
|
||||
unreadBadgeFilter: (room) => true,
|
||||
);
|
||||
}
|
||||
i--;
|
||||
if (i == rootSpaces.length) {
|
||||
return NaviRailItem(
|
||||
isSelected: false,
|
||||
onTap: () => context.go('/rooms/newspace'),
|
||||
icon: const Icon(Icons.add),
|
||||
toolTip: L10n.of(context).createNewSpace,
|
||||
);
|
||||
}
|
||||
final space = rootSpaces[i];
|
||||
final displayname = rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
);
|
||||
final spaceChildrenIds =
|
||||
space.spaceChildren.map((c) => c.roomId).toSet();
|
||||
return NaviRailItem(
|
||||
toolTip: displayname,
|
||||
isSelected: controller.activeSpaceId == space.id,
|
||||
onTap: () =>
|
||||
controller.setActiveSpace(rootSpaces[i].id),
|
||||
unreadBadgeFilter: (room) =>
|
||||
spaceChildrenIds.contains(room.id),
|
||||
icon: Avatar(
|
||||
mxContent: rootSpaces[i].avatar,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
presenceUserId: space.directChatMatrixID,
|
||||
// Pangea#
|
||||
size: 32,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
SpacesNavigationRail(
|
||||
activeSpaceId: controller.activeSpaceId,
|
||||
onGoToChats: controller.clearActiveSpace,
|
||||
onGoToSpaceId: controller.setActiveSpace,
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).dividerColor,
|
||||
|
|
|
|||
|
|
@ -35,16 +35,16 @@ class NaviRailItem extends StatelessWidget {
|
|||
return HoverBuilder(
|
||||
builder: (context, hovered) {
|
||||
return SizedBox(
|
||||
height: FluffyThemes.navRailWidth,
|
||||
height: 72,
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 0,
|
||||
child: AnimatedContainer(
|
||||
width: isSelected ? 4 : 0,
|
||||
width: isSelected ? 8 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -58,35 +58,29 @@ class NaviRailItem extends StatelessWidget {
|
|||
),
|
||||
Center(
|
||||
child: AnimatedScale(
|
||||
scale: hovered ? 1.2 : 1.0,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
borderRadius: borderRadius,
|
||||
color: isSelected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: theme.colorScheme.surface,
|
||||
: theme.colorScheme.surfaceContainerHigh,
|
||||
child: Tooltip(
|
||||
message: toolTip,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: unreadBadgeFilter == null
|
||||
? icon
|
||||
: UnreadRoomsBadge(
|
||||
filter: unreadBadgeFilter,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
top: -12,
|
||||
end: -8,
|
||||
),
|
||||
child: icon,
|
||||
child: unreadBadgeFilter == null
|
||||
? icon
|
||||
: UnreadRoomsBadge(
|
||||
filter: unreadBadgeFilter,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
top: -12,
|
||||
end: -8,
|
||||
),
|
||||
),
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:matrix/matrix.dart' as sdk;
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
|
|
@ -295,15 +296,14 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
break;
|
||||
case SpaceActions.leave:
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
// #Pangea
|
||||
// message: L10n.of(context).archiveRoomDescription,
|
||||
message: L10n.of(context).leaveSpaceDescription,
|
||||
// Pangea#
|
||||
okLabel: L10n.of(context).leave,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
|
@ -510,12 +510,15 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Center(
|
||||
child: CloseButton(
|
||||
onPressed: widget.onBack,
|
||||
),
|
||||
),
|
||||
titleSpacing: 0,
|
||||
leading: FluffyThemes.isColumnMode(context)
|
||||
? null
|
||||
: Center(
|
||||
child: CloseButton(
|
||||
onPressed: widget.onBack,
|
||||
),
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
titleSpacing: FluffyThemes.isColumnMode(context) ? null : 0,
|
||||
title: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: Avatar(
|
||||
|
|
@ -524,6 +527,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
// #Pangea
|
||||
presenceUserId: room?.directChatMatrixID,
|
||||
// Pangea#
|
||||
border: BorderSide(width: 1, color: theme.dividerColor),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
title: Text(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat/events/image_bubble.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat/events/video_player.dart';
|
||||
import 'package:fluffychat/pages/image_viewer/image_viewer.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
|
||||
class ChatSearchImagesTab extends StatelessWidget {
|
||||
final Room room;
|
||||
|
|
@ -25,6 +27,7 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final borderRadius = BorderRadius.circular(AppConfig.borderRadius / 2);
|
||||
return StreamBuilder(
|
||||
stream: searchStream,
|
||||
builder: (context, snapshot) {
|
||||
|
|
@ -144,16 +147,39 @@ class ChatSearchImagesTab extends StatelessWidget {
|
|||
shrinkWrap: true,
|
||||
mainAxisSpacing: padding,
|
||||
crossAxisSpacing: padding,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
padding: const EdgeInsets.all(padding),
|
||||
crossAxisCount: 3,
|
||||
children: monthEvents.map(
|
||||
(event) {
|
||||
if (event.messageType == MessageTypes.Video) {
|
||||
return EventVideoPlayer(event);
|
||||
return Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: EventVideoPlayer(event),
|
||||
);
|
||||
}
|
||||
return ImageBubble(
|
||||
event,
|
||||
fit: BoxFit.cover,
|
||||
return InkWell(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => ImageViewer(
|
||||
event,
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
borderRadius: borderRadius,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: borderRadius,
|
||||
child: MxcImage(
|
||||
event: event,
|
||||
width: 128,
|
||||
height: 128,
|
||||
fit: BoxFit.cover,
|
||||
animated: true,
|
||||
isThumbnail: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class ChatSearchView extends StatelessWidget {
|
|||
enabled: controller.tabController.index == 0,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context).search,
|
||||
suffixIcon: const Icon(Icons.search_outlined),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/device_settings/device_settings.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'user_device_list_item.dart';
|
||||
|
|
@ -15,7 +16,8 @@ class DevicesSettingsView extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
title: Text(L10n.of(context).devices),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
|
|
|
|||
|
|
@ -218,8 +218,8 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||
|
||||
void onMoreAction(MoreLoginActions action) {
|
||||
switch (action) {
|
||||
case MoreLoginActions.passwordLogin:
|
||||
checkHomeserverAction(legacyPasswordLogin: true);
|
||||
case MoreLoginActions.importBackup:
|
||||
restoreBackup();
|
||||
case MoreLoginActions.privacy:
|
||||
launchUrlString(AppConfig.privacyUrl);
|
||||
case MoreLoginActions.about:
|
||||
|
|
@ -228,7 +228,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||
}
|
||||
}
|
||||
|
||||
enum MoreLoginActions { passwordLogin, privacy, about }
|
||||
enum MoreLoginActions { importBackup, privacy, about }
|
||||
|
||||
class IdentityProvider {
|
||||
final String? id;
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
onSelected: controller.onMoreAction,
|
||||
itemBuilder: (_) => [
|
||||
PopupMenuItem(
|
||||
value: MoreLoginActions.passwordLogin,
|
||||
value: MoreLoginActions.importBackup,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.login_outlined),
|
||||
const Icon(Icons.import_export_outlined),
|
||||
const SizedBox(width: 12),
|
||||
Text(L10n.of(context).loginWithMatrixId),
|
||||
Text(L10n.of(context).hydrate),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -122,10 +122,6 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: SelectableLinkify(
|
||||
text: L10n.of(context).appIntroduction,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
|
|
@ -216,8 +212,10 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
),
|
||||
onPressed: controller.isLoading
|
||||
? null
|
||||
: controller.restoreBackup,
|
||||
child: Text(L10n.of(context).hydrate),
|
||||
: () => controller.checkHomeserverAction(
|
||||
legacyPasswordLogin: true,
|
||||
),
|
||||
child: Text(L10n.of(context).loginWithMatrixId),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
|
|
@ -26,6 +27,8 @@ class ImageViewer extends StatefulWidget {
|
|||
}
|
||||
|
||||
class ImageViewerController extends State<ImageViewer> {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -45,6 +48,17 @@ class ImageViewerController extends State<ImageViewer> {
|
|||
|
||||
late final List<Event> allEvents;
|
||||
|
||||
void onKeyEvent(KeyEvent event) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.arrowLeft:
|
||||
if (canGoBack) prevImage();
|
||||
break;
|
||||
case LogicalKeyboardKey.arrowRight:
|
||||
if (canGoNext) nextImage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prevImage() async {
|
||||
await pageController.previousPage(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
|
|
|
|||
|
|
@ -185,20 +185,20 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
],
|
||||
);
|
||||
buttons.add(
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.close),
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
label: Text(L10n.of(context).reject),
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request
|
||||
.rejectVerification()
|
||||
.then((_) => Navigator.of(context, rootNavigator: false).pop()),
|
||||
child: Text(
|
||||
L10n.of(context).reject,
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
);
|
||||
buttons.add(
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.check),
|
||||
label: Text(L10n.of(context).accept),
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request.acceptVerification(),
|
||||
child: Text(L10n.of(context).accept),
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
|
@ -207,6 +207,7 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
body = Center(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 16),
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
|
|
@ -230,10 +231,9 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
),
|
||||
);
|
||||
buttons.add(
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.close),
|
||||
label: Text(L10n.of(context).cancel),
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request.cancel(),
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
@ -271,20 +271,18 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
],
|
||||
);
|
||||
buttons.add(
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.close),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
),
|
||||
label: Text(L10n.of(context).theyDontMatch),
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request.rejectSas(),
|
||||
child: Text(
|
||||
L10n.of(context).theyDontMatch,
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
);
|
||||
buttons.add(
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.check_outlined),
|
||||
label: Text(L10n.of(context).theyMatch),
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () => widget.request.acceptSas(),
|
||||
child: Text(L10n.of(context).theyMatch),
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
|
@ -295,8 +293,9 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 16),
|
||||
const CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
acceptText,
|
||||
textAlign: TextAlign.center,
|
||||
|
|
@ -305,20 +304,14 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
);
|
||||
break;
|
||||
case KeyVerificationState.done:
|
||||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
Icons.check_circle_outlined,
|
||||
color: Colors.green,
|
||||
size: 128.0,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
L10n.of(context).verifySuccess,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
title = Text(L10n.of(context).verifySuccess);
|
||||
body = const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Icon(
|
||||
Icons.verified_outlined,
|
||||
color: Colors.green,
|
||||
size: 128.0,
|
||||
),
|
||||
);
|
||||
buttons.add(
|
||||
AdaptiveDialogAction(
|
||||
|
|
@ -334,7 +327,8 @@ class KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||
body = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(Icons.cancel, color: Colors.red, size: 128.0),
|
||||
const SizedBox(height: 16),
|
||||
Icon(Icons.cancel, color: theme.colorScheme.error, size: 64.0),
|
||||
const SizedBox(height: 16),
|
||||
// TODO: Add better error UI to user
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -207,14 +207,6 @@ class SettingsController extends State<Settings> {
|
|||
// Pangea#
|
||||
}
|
||||
|
||||
Future<String?> getOidcAccountManageUrl() async {
|
||||
final client = Matrix.of(context).client;
|
||||
final wellKnown = client.wellKnown ?? await client.getWellknown();
|
||||
return wellKnown.additionalProperties
|
||||
.tryGetMap<String, Object?>('org.matrix.msc2965.authentication')
|
||||
?.tryGet<String>('account');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/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';
|
||||
import 'package:fluffychat/widgets/navigation_rail.dart';
|
||||
import 'settings.dart';
|
||||
|
||||
class SettingsView extends StatelessWidget {
|
||||
|
|
@ -26,279 +28,330 @@ class SettingsView extends StatelessWidget {
|
|||
// #Pangea
|
||||
// final showChatBackupBanner = controller.showChatBackupBanner;
|
||||
// Pangea#
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Center(
|
||||
child: CloseButton(
|
||||
onPressed: () => context.go('/rooms'),
|
||||
final activeRoute =
|
||||
GoRouter.of(context).routeInformationProvider.value.uri.path;
|
||||
// #Pangea
|
||||
// final accountManageUrl = Matrix.of(context)
|
||||
// .client
|
||||
// .wellKnown
|
||||
// ?.additionalProperties
|
||||
// .tryGetMap<String, Object?>('org.matrix.msc2965.authentication')
|
||||
// ?.tryGet<String>('account');
|
||||
// Pangea#
|
||||
return Row(
|
||||
children: [
|
||||
if (FluffyThemes.isColumnMode(context)) ...[
|
||||
SpacesNavigationRail(
|
||||
activeSpaceId: null,
|
||||
onGoToChats: () => context.go('/rooms'),
|
||||
onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Scaffold(
|
||||
appBar: FluffyThemes.isColumnMode(context)
|
||||
? null
|
||||
: AppBar(
|
||||
title: Text(L10n.of(context).settings),
|
||||
leading: Center(
|
||||
child: BackButton(
|
||||
onPressed: () => context.go('/rooms'),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: ListTileTheme(
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
child: ListView(
|
||||
key: const Key('SettingsListViewContent'),
|
||||
children: <Widget>[
|
||||
FutureBuilder<Profile>(
|
||||
future: controller.profileFuture,
|
||||
builder: (context, snapshot) {
|
||||
final profile = snapshot.data;
|
||||
final mxid = Matrix.of(context).client.userID ??
|
||||
L10n.of(context).user;
|
||||
final displayname =
|
||||
profile?.displayName ?? mxid.localpart ?? mxid;
|
||||
return Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: profile?.avatarUrl,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
presenceUserId: profile?.userId,
|
||||
// Pangea#
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
),
|
||||
if (profile != null)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: FloatingActionButton.small(
|
||||
elevation: 2,
|
||||
onPressed: controller.setAvatarAction,
|
||||
heroTag: null,
|
||||
child: const Icon(
|
||||
Icons.camera_alt_outlined,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: controller.setDisplaynameAction,
|
||||
icon: const Icon(
|
||||
Icons.edit_outlined,
|
||||
size: 16,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor:
|
||||
theme.colorScheme.onSurface,
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
label: Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () =>
|
||||
FluffyShare.share(mxid, context),
|
||||
icon: const Icon(
|
||||
Icons.copy_outlined,
|
||||
size: 14,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor:
|
||||
theme.colorScheme.secondary,
|
||||
iconColor: theme.colorScheme.secondary,
|
||||
),
|
||||
label: Text(
|
||||
mxid,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
// #Pangea
|
||||
// if (accountManageUrl != null)
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.account_circle_outlined),
|
||||
// title: Text(L10n.of(context).manageAccount),
|
||||
// trailing: const Icon(Icons.open_in_new_outlined),
|
||||
// onTap: () => launchUrlString(
|
||||
// accountManageUrl,
|
||||
// mode: LaunchMode.inAppBrowserView,
|
||||
// ),
|
||||
// ),
|
||||
// Divider(color: theme.dividerColor),
|
||||
// if (showChatBackupBanner == null)
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.backup_outlined),
|
||||
// title: Text(L10n.of(context).chatBackup),
|
||||
// trailing: const CircularProgressIndicator.adaptive(),
|
||||
// )
|
||||
// else
|
||||
// SwitchListTile.adaptive(
|
||||
// controlAffinity: ListTileControlAffinity.trailing,
|
||||
// value: controller.showChatBackupBanner == false,
|
||||
// secondary: const Icon(Icons.backup_outlined),
|
||||
// title: Text(L10n.of(context).chatBackup),
|
||||
// onChanged: controller.firstRunBootstrapAction,
|
||||
// ),
|
||||
// Divider(
|
||||
// color: theme.dividerColor,
|
||||
// ),
|
||||
// Pangea#
|
||||
ListTile(
|
||||
leading: const Icon(Icons.format_paint_outlined),
|
||||
title: Text(L10n.of(context).changeTheme),
|
||||
tileColor: activeRoute.startsWith('/rooms/settings/style')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
onTap: () => context.go('/rooms/settings/style'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.notifications_outlined),
|
||||
title: Text(L10n.of(context).notifications),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/notifications')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
onTap: () => context.go('/rooms/settings/notifications'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.devices_outlined),
|
||||
title: Text(L10n.of(context).devices),
|
||||
onTap: () => context.go('/rooms/settings/devices'),
|
||||
tileColor: activeRoute.startsWith('/rooms/settings/devices')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.forum_outlined),
|
||||
title: Text(L10n.of(context).chat),
|
||||
onTap: () => context.go('/rooms/settings/chat'),
|
||||
tileColor: activeRoute.startsWith('/rooms/settings/chat')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
// #Pangea
|
||||
ListTile(
|
||||
leading: const Icon(Icons.account_circle_outlined),
|
||||
title: Text(L10n.of(context).subscriptionManagement),
|
||||
onTap: () => context.go('/rooms/settings/subscription'),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/subscription')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
// Pangea#
|
||||
ListTile(
|
||||
leading: const Icon(Icons.shield_outlined),
|
||||
title: Text(L10n.of(context).security),
|
||||
onTap: () => context.go('/rooms/settings/security'),
|
||||
tileColor:
|
||||
activeRoute.startsWith('/rooms/settings/security')
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: null,
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
// #Pangea
|
||||
ListTile(
|
||||
leading: const Icon(Icons.help_outline_outlined),
|
||||
title: Text(L10n.of(context).help),
|
||||
onTap: () async {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId =
|
||||
await Matrix.of(context).client.startDirectChat(
|
||||
Environment.supportUserId,
|
||||
enableEncryption: false,
|
||||
);
|
||||
context.go('/rooms/$roomId');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.shield_outlined),
|
||||
title: Text(L10n.of(context).termsAndConditions),
|
||||
onTap: () => launchUrlString(AppConfig.termsOfServiceUrl),
|
||||
trailing: const Icon(Icons.open_in_new_outlined),
|
||||
),
|
||||
FutureBuilder<PackageInfo>(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
trailing: const Icon(Icons.copy_outlined),
|
||||
onTap: () async {
|
||||
if (snapshot.data == null) return;
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text:
|
||||
"${snapshot.data!.version}+${snapshot.data!.buildNumber}",
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text(L10n.of(context).copiedToClipboard),
|
||||
),
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
snapshot.data != null
|
||||
? L10n.of(context).versionText(
|
||||
snapshot.data!.version,
|
||||
snapshot.data!.buildNumber,
|
||||
)
|
||||
: L10n.of(context).versionNotFound,
|
||||
),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.error_outline),
|
||||
title: Text(L10n.of(context).versionFetchError),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
leading: const CircularProgressIndicator(),
|
||||
title: Text(L10n.of(context).fetchingVersion),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
// Conditional ListTile based on the environment (staging or not)
|
||||
if (Environment.isStaging)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.bug_report_outlined),
|
||||
title: Text(L10n.of(context).connectedToStaging),
|
||||
),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.dns_outlined),
|
||||
// title: Text(
|
||||
// L10n.of(context).aboutHomeserver(
|
||||
// Matrix.of(context).client.userID?.domain ??
|
||||
// 'homeserver',
|
||||
// ),
|
||||
// ),
|
||||
// onTap: () => context.go('/rooms/settings/homeserver'),
|
||||
// tileColor:
|
||||
// activeRoute.startsWith('/rooms/settings/homeserver')
|
||||
// ? theme.colorScheme.surfaceContainerHigh
|
||||
// : null,
|
||||
// ),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.privacy_tip_outlined),
|
||||
// title: Text(L10n.of(context).privacy),
|
||||
// onTap: () => launchUrlString(AppConfig.privacyUrl),
|
||||
// ),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.info_outline_rounded),
|
||||
// title: Text(L10n.of(context).about),
|
||||
// onTap: () => PlatformInfos.showDialog(context),
|
||||
// ),
|
||||
// Pangea#
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout_outlined),
|
||||
title: Text(L10n.of(context).logout),
|
||||
onTap: controller.logoutAction,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(L10n.of(context).settings),
|
||||
),
|
||||
body: ListTileTheme(
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
child: FutureBuilder(
|
||||
future: controller.getOidcAccountManageUrl(),
|
||||
builder: (context, snapshot) {
|
||||
// #Pangea
|
||||
// final accountManageUrl = snapshot.data;
|
||||
// Pangea#
|
||||
return ListView(
|
||||
key: const Key('SettingsListViewContent'),
|
||||
children: <Widget>[
|
||||
FutureBuilder<Profile>(
|
||||
future: controller.profileFuture,
|
||||
builder: (context, snapshot) {
|
||||
final profile = snapshot.data;
|
||||
final mxid = Matrix.of(context).client.userID ??
|
||||
L10n.of(context).user;
|
||||
final displayname =
|
||||
profile?.displayName ?? mxid.localpart ?? mxid;
|
||||
return Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: profile?.avatarUrl,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
presenceUserId: profile?.userId,
|
||||
// Pangea#
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
),
|
||||
if (profile != null)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: FloatingActionButton.small(
|
||||
elevation: 2,
|
||||
onPressed: controller.setAvatarAction,
|
||||
heroTag: null,
|
||||
child:
|
||||
const Icon(Icons.camera_alt_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: controller.setDisplaynameAction,
|
||||
icon: const Icon(
|
||||
Icons.edit_outlined,
|
||||
size: 16,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
label: Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () =>
|
||||
FluffyShare.share(mxid, context),
|
||||
icon: const Icon(
|
||||
Icons.copy_outlined,
|
||||
size: 14,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: theme.colorScheme.secondary,
|
||||
iconColor: theme.colorScheme.secondary,
|
||||
),
|
||||
label: Text(
|
||||
mxid,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
// #Pangea
|
||||
// if (accountManageUrl != null)
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.account_circle_outlined),
|
||||
// title: Text(L10n.of(context).manageAccount),
|
||||
// trailing: const Icon(Icons.open_in_new_outlined),
|
||||
// onTap: () => launchUrlString(
|
||||
// accountManageUrl,
|
||||
// mode: LaunchMode.inAppBrowserView,
|
||||
// ),
|
||||
// ),
|
||||
// Divider(color: theme.dividerColor),
|
||||
// if (showChatBackupBanner == null)
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.backup_outlined),
|
||||
// title: Text(L10n.of(context).chatBackup),
|
||||
// trailing: const CircularProgressIndicator.adaptive(),
|
||||
// )
|
||||
// else
|
||||
// SwitchListTile.adaptive(
|
||||
// controlAffinity: ListTileControlAffinity.trailing,
|
||||
// value: controller.showChatBackupBanner == false,
|
||||
// secondary: const Icon(Icons.backup_outlined),
|
||||
// title: Text(L10n.of(context).chatBackup),
|
||||
// onChanged: controller.firstRunBootstrapAction,
|
||||
// ),
|
||||
// Divider(
|
||||
// color: theme.dividerColor,
|
||||
// ),
|
||||
// Pangea#
|
||||
ListTile(
|
||||
leading: const Icon(Icons.format_paint_outlined),
|
||||
title: Text(L10n.of(context).changeTheme),
|
||||
onTap: () => context.go('/rooms/settings/style'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.notifications_outlined),
|
||||
title: Text(L10n.of(context).notifications),
|
||||
onTap: () => context.go('/rooms/settings/notifications'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.devices_outlined),
|
||||
title: Text(L10n.of(context).devices),
|
||||
onTap: () => context.go('/rooms/settings/devices'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.forum_outlined),
|
||||
title: Text(L10n.of(context).chat),
|
||||
onTap: () => context.go('/rooms/settings/chat'),
|
||||
),
|
||||
// #Pangea
|
||||
ListTile(
|
||||
leading: const Icon(Icons.account_circle_outlined),
|
||||
title: Text(L10n.of(context).subscriptionManagement),
|
||||
onTap: () => context.go('/rooms/settings/subscription'),
|
||||
),
|
||||
// Pangea#
|
||||
ListTile(
|
||||
leading: const Icon(Icons.shield_outlined),
|
||||
title: Text(L10n.of(context).security),
|
||||
onTap: () => context.go('/rooms/settings/security'),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
// #Pangea
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.dns_outlined),
|
||||
// title: Text(
|
||||
// L10n.of(context).aboutHomeserver(
|
||||
// Matrix.of(context).client.userID?.domain ?? 'homeserver',
|
||||
// ),
|
||||
// ),
|
||||
// onTap: () => context.go('/rooms/settings/homeserver'),
|
||||
// ),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.help_outline_outlined),
|
||||
title: Text(L10n.of(context).help),
|
||||
onTap: () async {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId =
|
||||
await Matrix.of(context).client.startDirectChat(
|
||||
Environment.supportUserId,
|
||||
enableEncryption: false,
|
||||
);
|
||||
context.go('/rooms/$roomId');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.shield_sharp),
|
||||
// title: Text(L10n.of(context).privacy),
|
||||
// onTap: () => launchUrlString(AppConfig.privacyUrl),
|
||||
// ),
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.info_outline_rounded),
|
||||
// title: Text(L10n.of(context).about),
|
||||
// onTap: () => PlatformInfos.showDialog(context),
|
||||
// ),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.shield_outlined),
|
||||
title: Text(L10n.of(context).termsAndConditions),
|
||||
onTap: () => launchUrlString(AppConfig.termsOfServiceUrl),
|
||||
trailing: const Icon(Icons.open_in_new_outlined),
|
||||
),
|
||||
FutureBuilder<PackageInfo>(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
trailing: const Icon(Icons.copy_outlined),
|
||||
onTap: () async {
|
||||
if (snapshot.data == null) return;
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text:
|
||||
"${snapshot.data!.version}+${snapshot.data!.buildNumber}",
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).copiedToClipboard),
|
||||
),
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
snapshot.data != null
|
||||
? L10n.of(context).versionText(
|
||||
snapshot.data!.version,
|
||||
snapshot.data!.buildNumber,
|
||||
)
|
||||
: L10n.of(context).versionNotFound,
|
||||
),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.error_outline),
|
||||
title: Text(L10n.of(context).versionFetchError),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
leading: const CircularProgressIndicator(),
|
||||
title: Text(L10n.of(context).fetchingVersion),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
// Conditional ListTile based on the environment (staging or not)
|
||||
if (Environment.isStaging)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.bug_report_outlined),
|
||||
title: Text(L10n.of(context).connectedToStaging),
|
||||
),
|
||||
// Pangea#
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout_outlined),
|
||||
title: Text(L10n.of(context).logout),
|
||||
onTap: controller.logoutAction,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:fluffychat/widgets/settings_switch_list_tile.dart';
|
||||
|
|
@ -18,7 +19,11 @@ class SettingsChatView extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(L10n.of(context).chat)),
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).chat),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
),
|
||||
body: ListTileTheme(
|
||||
iconColor: theme.textTheme.bodyLarge!.color,
|
||||
child: MaxWidthBody(
|
||||
|
|
@ -114,16 +119,6 @@ class SettingsChatView extends StatelessWidget {
|
|||
// storeKey: SettingKeys.experimentalVoip,
|
||||
// defaultValue: AppConfig.experimentalVoip,
|
||||
// ),
|
||||
// if (PlatformInfos.isMobile)
|
||||
// ListTile(
|
||||
// title: Text(L10n.of(context).callingPermissions),
|
||||
// onTap: () =>
|
||||
// CallKeepManager().checkoutPhoneAccountSetting(context),
|
||||
// trailing: const Padding(
|
||||
// padding: EdgeInsets.all(16.0),
|
||||
// child: Icon(Icons.call),
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
|
@ -26,14 +27,15 @@ class SettingsHomeserverView extends StatelessWidget {
|
|||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
title: Text(
|
||||
L10n.of(context)
|
||||
.aboutHomeserver(client.userID?.domain ?? 'Homeserver'),
|
||||
),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
withScrolling: false,
|
||||
withScrolling: true,
|
||||
child: SelectionArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
|||
121
lib/pages/settings_notifications/push_rule_extensions.dart
Normal file
121
lib/pages/settings_notifications/push_rule_extensions.dart
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
extension PushRuleExtension on PushRule {
|
||||
String getPushRuleName(L10n l10n) {
|
||||
switch (ruleId) {
|
||||
case '.m.rule.contains_user_name':
|
||||
return l10n.notificationRuleContainsUserName;
|
||||
case '.m.rule.master':
|
||||
return l10n.notificationRuleMaster;
|
||||
case '.m.rule.suppress_notices':
|
||||
return l10n.notificationRuleSuppressNotices;
|
||||
case '.m.rule.invite_for_me':
|
||||
return l10n.notificationRuleInviteForMe;
|
||||
case '.m.rule.member_event':
|
||||
return l10n.notificationRuleMemberEvent;
|
||||
case '.m.rule.is_user_mention':
|
||||
return l10n.notificationRuleIsUserMention;
|
||||
case '.m.rule.contains_display_name':
|
||||
return l10n.notificationRuleContainsDisplayName;
|
||||
case '.m.rule.is_room_mention':
|
||||
return l10n.notificationRuleIsRoomMention;
|
||||
case '.m.rule.roomnotif':
|
||||
return l10n.notificationRuleRoomnotif;
|
||||
case '.m.rule.tombstone':
|
||||
return l10n.notificationRuleTombstone;
|
||||
case '.m.rule.reaction':
|
||||
return l10n.notificationRuleReaction;
|
||||
case '.m.rule.room_server_acl':
|
||||
return l10n.notificationRuleRoomServerAcl;
|
||||
case '.m.rule.suppress_edits':
|
||||
return l10n.notificationRuleSuppressEdits;
|
||||
case '.m.rule.call':
|
||||
return l10n.notificationRuleCall;
|
||||
case '.m.rule.encrypted_room_one_to_one':
|
||||
return l10n.notificationRuleEncryptedRoomOneToOne;
|
||||
case '.m.rule.room_one_to_one':
|
||||
return l10n.notificationRuleRoomOneToOne;
|
||||
case '.m.rule.message':
|
||||
return l10n.notificationRuleMessage;
|
||||
case '.m.rule.encrypted':
|
||||
return l10n.notificationRuleEncrypted;
|
||||
case '.m.rule.room.server_acl':
|
||||
return l10n.notificationRuleServerAcl;
|
||||
case '.im.vector.jitsi':
|
||||
return l10n.notificationRuleJitsi;
|
||||
default:
|
||||
return ruleId.split('.').last.replaceAll('_', ' ').capitalize();
|
||||
}
|
||||
}
|
||||
|
||||
String getPushRuleDescription(L10n l10n) {
|
||||
switch (ruleId) {
|
||||
case '.m.rule.contains_user_name':
|
||||
return l10n.notificationRuleContainsUserNameDescription;
|
||||
case '.m.rule.master':
|
||||
return l10n.notificationRuleMasterDescription;
|
||||
case '.m.rule.suppress_notices':
|
||||
return l10n.notificationRuleSuppressNoticesDescription;
|
||||
case '.m.rule.invite_for_me':
|
||||
return l10n.notificationRuleInviteForMeDescription;
|
||||
case '.m.rule.member_event':
|
||||
return l10n.notificationRuleMemberEventDescription;
|
||||
case '.m.rule.is_user_mention':
|
||||
return l10n.notificationRuleIsUserMentionDescription;
|
||||
case '.m.rule.contains_display_name':
|
||||
return l10n.notificationRuleContainsDisplayNameDescription;
|
||||
case '.m.rule.is_room_mention':
|
||||
return l10n.notificationRuleIsRoomMentionDescription;
|
||||
case '.m.rule.roomnotif':
|
||||
return l10n.notificationRuleRoomnotifDescription;
|
||||
case '.m.rule.tombstone':
|
||||
return l10n.notificationRuleTombstoneDescription;
|
||||
case '.m.rule.reaction':
|
||||
return l10n.notificationRuleReactionDescription;
|
||||
case '.m.rule.room_server_acl':
|
||||
return l10n.notificationRuleRoomServerAclDescription;
|
||||
case '.m.rule.suppress_edits':
|
||||
return l10n.notificationRuleSuppressEditsDescription;
|
||||
case '.m.rule.call':
|
||||
return l10n.notificationRuleCallDescription;
|
||||
case '.m.rule.encrypted_room_one_to_one':
|
||||
return l10n.notificationRuleEncryptedRoomOneToOneDescription;
|
||||
case '.m.rule.room_one_to_one':
|
||||
return l10n.notificationRuleRoomOneToOneDescription;
|
||||
case '.m.rule.message':
|
||||
return l10n.notificationRuleMessageDescription;
|
||||
case '.m.rule.encrypted':
|
||||
return l10n.notificationRuleEncryptedDescription;
|
||||
case '.m.rule.room.server_acl':
|
||||
return l10n.notificationRuleServerAclDescription;
|
||||
case '.im.vector.jitsi':
|
||||
return l10n.notificationRuleJitsiDescription;
|
||||
default:
|
||||
return l10n.unknownPushRule(ruleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PushRuleKindLocal on PushRuleKind {
|
||||
String localized(L10n l10n) {
|
||||
switch (this) {
|
||||
case PushRuleKind.content:
|
||||
return l10n.contentNotificationSettings;
|
||||
case PushRuleKind.override:
|
||||
return l10n.generalNotificationSettings;
|
||||
case PushRuleKind.room:
|
||||
return l10n.roomNotificationSettings;
|
||||
case PushRuleKind.sender:
|
||||
return l10n.userSpecificNotificationSettings;
|
||||
case PushRuleKind.underride:
|
||||
return l10n.otherNotificationSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on String {
|
||||
String capitalize() {
|
||||
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +1,20 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'settings_notifications_view.dart';
|
||||
|
||||
class NotificationSettingsItem {
|
||||
final PushRuleKind type;
|
||||
final String key;
|
||||
final String Function(BuildContext) title;
|
||||
const NotificationSettingsItem(this.type, this.key, this.title);
|
||||
static List<NotificationSettingsItem> items = [
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.underride,
|
||||
'.m.rule.message',
|
||||
(c) => L10n.of(c).allRooms,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.underride,
|
||||
'.m.rule.room_one_to_one',
|
||||
(c) => L10n.of(c).directChats,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.override,
|
||||
'.m.rule.contains_display_name',
|
||||
(c) => L10n.of(c).containsDisplayName,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.content,
|
||||
'.m.rule.contains_user_name',
|
||||
(c) => L10n.of(c).containsUserName,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.override,
|
||||
'.m.rule.invite_for_me',
|
||||
(c) => L10n.of(c).inviteForMe,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.override,
|
||||
'.m.rule.member_event',
|
||||
(c) => L10n.of(c).memberChanges,
|
||||
),
|
||||
NotificationSettingsItem(
|
||||
PushRuleKind.override,
|
||||
'.m.rule.suppress_notices',
|
||||
(c) => L10n.of(c).botMessages,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class SettingsNotifications extends StatefulWidget {
|
||||
const SettingsNotifications({super.key});
|
||||
|
||||
|
|
@ -63,80 +24,8 @@ class SettingsNotifications extends StatefulWidget {
|
|||
}
|
||||
|
||||
class SettingsNotificationsController extends State<SettingsNotifications> {
|
||||
bool? getNotificationSetting(NotificationSettingsItem item) {
|
||||
final pushRules = Matrix.of(context).client.globalPushRules;
|
||||
if (pushRules == null) return null;
|
||||
switch (item.type) {
|
||||
case PushRuleKind.content:
|
||||
return pushRules.content
|
||||
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||
?.enabled;
|
||||
case PushRuleKind.override:
|
||||
return pushRules.override
|
||||
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||
?.enabled;
|
||||
case PushRuleKind.room:
|
||||
return pushRules.room
|
||||
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||
?.enabled;
|
||||
case PushRuleKind.sender:
|
||||
return pushRules.sender
|
||||
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||
?.enabled;
|
||||
case PushRuleKind.underride:
|
||||
return pushRules.underride
|
||||
?.singleWhereOrNull((r) => r.ruleId == item.key)
|
||||
?.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
void setNotificationSetting(
|
||||
NotificationSettingsItem item,
|
||||
bool enabled,
|
||||
) async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
await Matrix.of(context).client.setPushRuleEnabled(
|
||||
item.type,
|
||||
item.key,
|
||||
enabled,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change notification settings', e, s);
|
||||
scaffoldMessenger
|
||||
.showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void onToggleMuteAllNotifications() async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
await Matrix.of(context).client.setMuteAllPushNotifications(
|
||||
!Matrix.of(context).client.allPushNotificationsMuted,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change notification settings', e, s);
|
||||
scaffoldMessenger
|
||||
.showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void onPusherTap(Pusher pusher) async {
|
||||
final delete = await showModalActionPopup<bool>(
|
||||
context: context,
|
||||
|
|
@ -172,6 +61,146 @@ class SettingsNotificationsController extends State<SettingsNotifications> {
|
|||
|
||||
Future<List<Pusher>?>? pusherFuture;
|
||||
|
||||
void togglePushRule(PushRuleKind kind, PushRule pushRule) async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
final updateFromSync = Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.accountData?.any(
|
||||
(accountData) => accountData.type == 'm.push_rules',
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.first;
|
||||
await Matrix.of(context).client.setPushRuleEnabled(
|
||||
kind,
|
||||
pushRule.ruleId,
|
||||
!pushRule.enabled,
|
||||
);
|
||||
await updateFromSync;
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to toggle push rule', e, s);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void editPushRule(PushRule rule, PushRuleKind kind) async {
|
||||
final theme = Theme.of(context);
|
||||
final action = await showAdaptiveDialog<PushRuleDialogAction>(
|
||||
context: context,
|
||||
builder: (context) => ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: AlertDialog.adaptive(
|
||||
title: Text(rule.getPushRuleName(L10n.of(context))),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SelectableText(
|
||||
prettyJson(rule.toJson()),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
if (!rule.ruleId.startsWith('.m.'))
|
||||
AdaptiveDialogAction(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(PushRuleDialogAction.delete),
|
||||
child: Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (action == null) return;
|
||||
if (!mounted) return;
|
||||
switch (action) {
|
||||
case PushRuleDialogAction.delete:
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
message: L10n.of(context).deletePushRuleCanNotBeUndone,
|
||||
okLabel: L10n.of(context).delete,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (consent != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
final updateFromSync = Matrix.of(context)
|
||||
.client
|
||||
.onSync
|
||||
.stream
|
||||
.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.accountData?.any(
|
||||
(accountData) => accountData.type == 'm.push_rules',
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.first;
|
||||
await Matrix.of(context).client.deletePushRule(
|
||||
kind,
|
||||
rule.ruleId,
|
||||
);
|
||||
await updateFromSync;
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to delete push rule', e, s);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(e.toLocalizedString(context))),
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SettingsNotificationsView(this);
|
||||
}
|
||||
|
||||
enum PushRuleDialogAction { delete }
|
||||
|
||||
String prettyJson(Map<String, Object?> json) {
|
||||
const decoder = JsonDecoder();
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
final object = decoder.convert(jsonEncode(json));
|
||||
return encoder.convert(object);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import '../../utils/localized_exception_extension.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
|
@ -15,9 +17,21 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pushRules = Matrix.of(context).client.globalPushRules;
|
||||
final pushCategories = [
|
||||
if (pushRules?.override?.isNotEmpty ?? false)
|
||||
(rules: pushRules?.override ?? [], kind: PushRuleKind.override),
|
||||
if (pushRules?.content?.isNotEmpty ?? false)
|
||||
(rules: pushRules?.content ?? [], kind: PushRuleKind.content),
|
||||
if (pushRules?.sender?.isNotEmpty ?? false)
|
||||
(rules: pushRules?.sender ?? [], kind: PushRuleKind.sender),
|
||||
if (pushRules?.underride?.isNotEmpty ?? false)
|
||||
(rules: pushRules?.underride ?? [], kind: PushRuleKind.underride),
|
||||
];
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
title: Text(L10n.of(context).notifications),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
|
|
@ -31,92 +45,121 @@ class SettingsNotificationsView extends StatelessWidget {
|
|||
),
|
||||
builder: (BuildContext context, _) {
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
value: !Matrix.of(context).client.allPushNotificationsMuted,
|
||||
title: Text(
|
||||
L10n.of(context).notificationsEnabledForThisAccount,
|
||||
),
|
||||
onChanged: controller.isLoading
|
||||
? null
|
||||
: (_) => controller.onToggleMuteAllNotifications(),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).notifyMeFor,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final item in NotificationSettingsItem.items)
|
||||
SwitchListTile.adaptive(
|
||||
value: Matrix.of(context).client.allPushNotificationsMuted
|
||||
? false
|
||||
: controller.getNotificationSetting(item) ?? true,
|
||||
title: Text(item.title(context)),
|
||||
onChanged: controller.isLoading
|
||||
? null
|
||||
: Matrix.of(context).client.allPushNotificationsMuted
|
||||
? null
|
||||
: (bool enabled) => controller
|
||||
.setNotificationSetting(item, enabled),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).devices,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
FutureBuilder<List<Pusher>?>(
|
||||
future: controller.pusherFuture ??=
|
||||
Matrix.of(context).client.getPushers(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
Center(
|
||||
child: Text(
|
||||
snapshot.error!.toLocalizedString(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
const Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
final pushers = snapshot.data ?? [];
|
||||
if (pushers.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Text(L10n.of(context).noOtherDevicesFound),
|
||||
),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: pushers.length,
|
||||
itemBuilder: (_, i) => ListTile(
|
||||
return SelectionArea(
|
||||
child: Column(
|
||||
children: [
|
||||
if (pushRules != null)
|
||||
for (final category in pushCategories) ...[
|
||||
ListTile(
|
||||
title: Text(
|
||||
'${pushers[i].appDisplayName} - ${pushers[i].appId}',
|
||||
category.kind.localized(L10n.of(context)),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(pushers[i].data.url.toString()),
|
||||
onTap: () => controller.onPusherTap(pushers[i]),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
for (final rule in category.rules)
|
||||
ListTile(
|
||||
title: Text(rule.getPushRuleName(L10n.of(context))),
|
||||
subtitle: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: rule.getPushRuleDescription(
|
||||
L10n.of(context),
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
WidgetSpan(
|
||||
child: InkWell(
|
||||
onTap: () => controller.editPushRule(
|
||||
rule,
|
||||
category.kind,
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context).more,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor:
|
||||
theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: Switch.adaptive(
|
||||
value: rule.enabled,
|
||||
onChanged: controller.isLoading
|
||||
? null
|
||||
: rule.ruleId != '.m.rule.master' &&
|
||||
Matrix.of(context)
|
||||
.client
|
||||
.allPushNotificationsMuted
|
||||
? null
|
||||
: (_) => controller.togglePushRule(
|
||||
category.kind,
|
||||
rule,
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
],
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).devices,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
FutureBuilder<List<Pusher>?>(
|
||||
future: controller.pusherFuture ??=
|
||||
Matrix.of(context).client.getPushers(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
Center(
|
||||
child: Text(
|
||||
snapshot.error!.toLocalizedString(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
const Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
final pushers = snapshot.data ?? [];
|
||||
if (pushers.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Text(L10n.of(context).noOtherDevicesFound),
|
||||
),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: pushers.length,
|
||||
itemBuilder: (_, i) => ListTile(
|
||||
title: Text(
|
||||
'${pushers[i].appDisplayName} - ${pushers[i].appId}',
|
||||
),
|
||||
subtitle: Text(pushers[i].data.url.toString()),
|
||||
onTap: () => controller.onPusherTap(pushers[i]),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:matrix/matrix.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||
import 'package:fluffychat/widgets/app_lock.dart';
|
||||
|
|
@ -135,6 +136,16 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||
|
||||
Future<void> dehydrateAction() => Matrix.of(context).dehydrateAction(context);
|
||||
|
||||
void changeShareKeysWith(ShareKeysWith? shareKeysWith) async {
|
||||
if (shareKeysWith == null) return;
|
||||
Matrix.of(context).store.setString(
|
||||
SettingKeys.shareKeysWith,
|
||||
shareKeysWith.name,
|
||||
);
|
||||
Matrix.of(context).client.shareKeysWith = shareKeysWith;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SettingsSecurityView(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ 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/app_config.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/utils/beautify_string_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
|
|
@ -21,7 +23,11 @@ class SettingsSecurityView extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(L10n.of(context).security)),
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).security),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
),
|
||||
body: ListTileTheme(
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
child: MaxWidthBody(
|
||||
|
|
@ -88,6 +94,41 @@ class SettingsSecurityView extends StatelessWidget {
|
|||
),
|
||||
},
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).shareKeysWith,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(L10n.of(context).shareKeysWithDescription),
|
||||
),
|
||||
ListTile(
|
||||
title: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
child: DropdownButton<ShareKeysWith>(
|
||||
isExpanded: true,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
underline: const SizedBox.shrink(),
|
||||
value: Matrix.of(context).client.shareKeysWith,
|
||||
items: ShareKeysWith.values
|
||||
.map(
|
||||
(share) => DropdownMenuItem(
|
||||
value: share,
|
||||
child: Text(share.localized(L10n.of(context))),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: controller.changeShareKeysWith,
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).account,
|
||||
|
|
@ -102,7 +143,7 @@ class SettingsSecurityView extends StatelessWidget {
|
|||
leading: const Icon(Icons.vpn_key_outlined),
|
||||
subtitle: SelectableText(
|
||||
Matrix.of(context).client.fingerprintKey.beautified,
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
),
|
||||
),
|
||||
if (capabilities?.mChangePassword?.enabled != false ||
|
||||
|
|
@ -142,3 +183,18 @@ class SettingsSecurityView extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on ShareKeysWith {
|
||||
String localized(L10n l10n) {
|
||||
switch (this) {
|
||||
case ShareKeysWith.all:
|
||||
return l10n.allDevices;
|
||||
case ShareKeysWith.crossVerifiedIfEnabled:
|
||||
return l10n.crossVerifiedDevicesIfEnabled;
|
||||
case ShareKeysWith.crossVerified:
|
||||
return l10n.crossVerifiedDevices;
|
||||
case ShareKeysWith.directlyVerifiedOnly:
|
||||
return l10n.verifiedDevicesOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ class SettingsStyleView extends StatelessWidget {
|
|||
final client = Matrix.of(context).client;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
automaticallyImplyLeading: !FluffyThemes.isColumnMode(context),
|
||||
centerTitle: FluffyThemes.isColumnMode(context),
|
||||
title: Text(L10n.of(context).changeTheme),
|
||||
),
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
|
|
@ -214,9 +215,7 @@ class SettingsStyleView extends StatelessWidget {
|
|||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.brightness == Brightness.light
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
color: theme.bubbleColor,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
|
|
@ -229,11 +228,7 @@ class SettingsStyleView extends StatelessWidget {
|
|||
child: Text(
|
||||
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor',
|
||||
style: TextStyle(
|
||||
color:
|
||||
theme.brightness == Brightness.light
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme
|
||||
.onPrimaryContainer,
|
||||
color: theme.onBubbleColor,
|
||||
fontSize: AppConfig.messageFontSize *
|
||||
AppConfig.fontSizeFactor,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:desktop_notifications/desktop_notifications.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
|
|
@ -12,6 +13,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/utils/custom_http_client.dart';
|
||||
|
|
@ -43,7 +45,8 @@ abstract class ClientManager {
|
|||
clientNames.add(PlatformInfos.clientName);
|
||||
await store.setStringList(clientNamespace, clientNames.toList());
|
||||
}
|
||||
final clients = clientNames.map(createClient).toList();
|
||||
final clients =
|
||||
clientNames.map((name) => createClient(name, store)).toList();
|
||||
if (initialize) {
|
||||
await Future.wait(
|
||||
clients.map(
|
||||
|
|
@ -99,7 +102,9 @@ abstract class ClientManager {
|
|||
? const NativeImplementationsDummy()
|
||||
: NativeImplementationsIsolate(compute);
|
||||
|
||||
static Client createClient(String clientName) {
|
||||
static Client createClient(String clientName, SharedPreferences store) {
|
||||
final shareKeysWith = store.getString(SettingKeys.shareKeysWith) ?? 'all';
|
||||
|
||||
return Client(
|
||||
clientName,
|
||||
httpClient:
|
||||
|
|
@ -136,6 +141,10 @@ abstract class ClientManager {
|
|||
customImageResizer: PlatformInfos.isMobile ? customImageResizer : null,
|
||||
defaultNetworkRequestTimeout: const Duration(minutes: 30),
|
||||
enableDehydratedDevices: true,
|
||||
shareKeysWith: ShareKeysWith.values
|
||||
.singleWhereOrNull((share) => share.name == shareKeysWith) ??
|
||||
ShareKeysWith.all,
|
||||
convertLinebreaksInFormatting: false,
|
||||
// #Pangea
|
||||
syncFilter: Filter(
|
||||
room: RoomFilter(
|
||||
|
|
|
|||
|
|
@ -28,11 +28,10 @@ extension RoomStatusExtension on Room {
|
|||
typingUsers[1].calcDisplayname(),
|
||||
);
|
||||
} else if (typingUsers.length > 2) {
|
||||
typingText = L10n.of(context).isTyping;
|
||||
if (typingUsers.first.id != directChatMatrixID) {
|
||||
typingText =
|
||||
L10n.of(context).userIsTyping(typingUsers.first.calcDisplayname());
|
||||
}
|
||||
typingText = L10n.of(context).userAndOthersAreTyping(
|
||||
typingUsers.first.calcDisplayname(),
|
||||
(typingUsers.length - 1),
|
||||
);
|
||||
}
|
||||
return typingText;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,21 +6,6 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
|
||||
extension SyncStatusLocalization on SyncStatusUpdate {
|
||||
IconData get icon {
|
||||
switch (status) {
|
||||
case SyncStatus.waitingForResponse:
|
||||
return Icons.hourglass_empty_outlined;
|
||||
case SyncStatus.error:
|
||||
return Icons.cloud_off_outlined;
|
||||
case SyncStatus.processing:
|
||||
return Icons.hourglass_top_outlined;
|
||||
case SyncStatus.cleaningUp:
|
||||
return Icons.hourglass_bottom_outlined;
|
||||
case SyncStatus.finished:
|
||||
return Icons.hourglass_full_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
String calcLocalizedString(BuildContext context) {
|
||||
final progress = this.progress;
|
||||
switch (status) {
|
||||
|
|
|
|||
107
lib/widgets/adaptive_dialogs/dialog_text_field.dart
Normal file
107
lib/widgets/adaptive_dialogs/dialog_text_field.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DialogTextField extends StatelessWidget {
|
||||
final TextEditingController? controller;
|
||||
final String? hintText;
|
||||
final String? labelText;
|
||||
final String? initialText;
|
||||
final String? counterText;
|
||||
final String? prefixText;
|
||||
final String? suffixText;
|
||||
final String? errorText;
|
||||
final bool obscureText = false;
|
||||
final bool isDestructive = false;
|
||||
final int? minLines;
|
||||
final int? maxLines;
|
||||
final TextInputType? keyboardType;
|
||||
final int? maxLength;
|
||||
final bool autocorrect = true;
|
||||
// #Pangea
|
||||
final void Function(String)? onSubmitted;
|
||||
// Pangea#
|
||||
|
||||
const DialogTextField({
|
||||
super.key,
|
||||
this.hintText,
|
||||
this.labelText,
|
||||
this.initialText,
|
||||
this.prefixText,
|
||||
this.suffixText,
|
||||
this.minLines,
|
||||
this.maxLines,
|
||||
this.keyboardType,
|
||||
this.maxLength,
|
||||
this.controller,
|
||||
this.counterText,
|
||||
this.errorText,
|
||||
// #Pangea
|
||||
this.onSubmitted,
|
||||
// Pangea#
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final prefixText = this.prefixText;
|
||||
final suffixText = this.suffixText;
|
||||
final errorText = this.errorText;
|
||||
final theme = Theme.of(context);
|
||||
switch (theme.platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
return TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
decoration: InputDecoration(
|
||||
errorText: errorText,
|
||||
hintText: hintText,
|
||||
labelText: labelText,
|
||||
prefixText: prefixText,
|
||||
suffixText: suffixText,
|
||||
counterText: counterText,
|
||||
),
|
||||
// #Pangea
|
||||
onSubmitted: onSubmitted,
|
||||
// Pangea#
|
||||
);
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CupertinoTextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
prefix: prefixText != null ? Text(prefixText) : null,
|
||||
suffix: suffixText != null ? Text(suffixText) : null,
|
||||
placeholder: labelText ?? hintText,
|
||||
// #Pangea
|
||||
onSubmitted: onSubmitted,
|
||||
// Pangea#
|
||||
),
|
||||
if (errorText != null)
|
||||
Text(
|
||||
errorText,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
|
||||
enum OkCancelResult { ok, cancel }
|
||||
|
|
@ -25,7 +27,17 @@ Future<OkCancelResult?> showOkCancelAlertDialog({
|
|||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: message == null ? null : Text(message),
|
||||
child: message == null
|
||||
? null
|
||||
: SelectableLinkify(
|
||||
text: message,
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
|
|
@ -65,7 +77,17 @@ Future<OkCancelResult?> showOkAlertDialog({
|
|||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: message == null ? null : Text(message),
|
||||
child: message == null
|
||||
? null
|
||||
: SelectableLinkify(
|
||||
text: message,
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
|
||||
Future<String?> showTextInputDialog({
|
||||
|
|
@ -43,103 +45,111 @@ Future<String?> showTextInputDialog({
|
|||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Text(title),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (message != null)
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Text(message),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ValueListenableBuilder<String?>(
|
||||
valueListenable: error,
|
||||
builder: (context, error, _) {
|
||||
switch (theme.platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
return TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
// #Pangea
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
minLines: autoSubmit ? 1 : minLines,
|
||||
maxLines: autoSubmit ? 1 : maxLines,
|
||||
onSubmitted: autoSubmit
|
||||
? (_) {
|
||||
final input = controller.text;
|
||||
final errorText = validator?.call(input);
|
||||
if (errorText != null) {
|
||||
error = errorText;
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop<String>(input);
|
||||
}
|
||||
: null,
|
||||
// Pangea#
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
decoration: InputDecoration(
|
||||
errorText: error,
|
||||
hintText: hintText,
|
||||
labelText: labelText,
|
||||
prefixText: prefixText,
|
||||
suffixText: suffixText,
|
||||
),
|
||||
);
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CupertinoTextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
// #Pangea
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
minLines: autoSubmit ? 1 : minLines,
|
||||
maxLines: autoSubmit ? 1 : maxLines,
|
||||
onSubmitted: autoSubmit
|
||||
? (_) {
|
||||
final input = controller.text;
|
||||
final errorText = validator?.call(input);
|
||||
if (errorText != null) {
|
||||
error = errorText;
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop<String>(input);
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (message != null)
|
||||
SelectableLinkify(
|
||||
text: message,
|
||||
linkStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ValueListenableBuilder<String?>(
|
||||
valueListenable: error,
|
||||
builder: (context, error, _) {
|
||||
switch (theme.platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
return TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
// #Pangea
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
minLines: autoSubmit ? 1 : minLines,
|
||||
maxLines: autoSubmit ? 1 : maxLines,
|
||||
onSubmitted: autoSubmit
|
||||
? (_) {
|
||||
final input = controller.text;
|
||||
final errorText = validator?.call(input);
|
||||
if (errorText != null) {
|
||||
error = errorText;
|
||||
return;
|
||||
}
|
||||
: null,
|
||||
// Pangea#
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
prefix:
|
||||
prefixText != null ? Text(prefixText) : null,
|
||||
suffix:
|
||||
suffixText != null ? Text(suffixText) : null,
|
||||
placeholder: labelText ?? hintText,
|
||||
Navigator.of(context).pop<String>(input);
|
||||
}
|
||||
: null,
|
||||
// Pangea#
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
decoration: InputDecoration(
|
||||
errorText: error,
|
||||
hintText: hintText,
|
||||
labelText: labelText,
|
||||
prefixText: prefixText,
|
||||
suffixText: suffixText,
|
||||
),
|
||||
if (error != null)
|
||||
Text(
|
||||
error!,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CupertinoTextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
// #Pangea
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
minLines: autoSubmit ? 1 : minLines,
|
||||
maxLines: autoSubmit ? 1 : maxLines,
|
||||
onSubmitted: autoSubmit
|
||||
? (_) {
|
||||
final input = controller.text;
|
||||
final errorText = validator?.call(input);
|
||||
if (errorText != null) {
|
||||
error = errorText;
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop<String>(input);
|
||||
}
|
||||
: null,
|
||||
// Pangea#
|
||||
maxLength: maxLength,
|
||||
keyboardType: keyboardType,
|
||||
autocorrect: autocorrect,
|
||||
prefix:
|
||||
prefixText != null ? Text(prefixText) : null,
|
||||
suffix:
|
||||
suffixText != null ? Text(suffixText) : null,
|
||||
placeholder: labelText ?? hintText,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
if (error != null)
|
||||
Text(
|
||||
error!,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
|
|
|
|||
|
|
@ -56,15 +56,14 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
|||
switch (choice) {
|
||||
case ChatPopupMenuActions.leave:
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
// #Pangea
|
||||
// message: L10n.of(context).archiveRoomDescription,
|
||||
message: L10n.of(context).leaveRoomDescription,
|
||||
// Pangea#
|
||||
okLabel: L10n.of(context).leave,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (confirmed == OkCancelResult.ok) {
|
||||
|
|
|
|||
|
|
@ -34,12 +34,18 @@ class MaxWidthBody extends StatelessWidget {
|
|||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Material(
|
||||
elevation: theme.appBarTheme.scrolledUnderElevation ?? 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
side: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
shadowColor: theme.appBarTheme.shadowColor,
|
||||
child: child,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@ import 'package:fluffychat/config/themes.dart';
|
|||
class TwoColumnLayout extends StatelessWidget {
|
||||
final Widget mainView;
|
||||
final Widget sideView;
|
||||
final bool displayNavigationRail;
|
||||
|
||||
const TwoColumnLayout({
|
||||
super.key,
|
||||
required this.mainView,
|
||||
required this.sideView,
|
||||
required this.displayNavigationRail,
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -24,8 +22,7 @@ class TwoColumnLayout extends StatelessWidget {
|
|||
Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(),
|
||||
width: FluffyThemes.columnWidth +
|
||||
(displayNavigationRail ? FluffyThemes.navRailWidth : 0),
|
||||
width: FluffyThemes.columnWidth + FluffyThemes.navRailWidth,
|
||||
child: mainView,
|
||||
),
|
||||
Container(
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
}
|
||||
final candidate = _loginClientCandidate ??= ClientManager.createClient(
|
||||
'${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}',
|
||||
store,
|
||||
)..onLoginStateChanged
|
||||
.stream
|
||||
.where((l) => l == LoginState.loggedIn)
|
||||
|
|
|
|||
137
lib/widgets/navigation_rail.dart
Normal file
137
lib/widgets/navigation_rail.dart
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
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/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/navi_rail_item.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class SpacesNavigationRail extends StatelessWidget {
|
||||
final String? activeSpaceId;
|
||||
final void Function() onGoToChats;
|
||||
final void Function(String) onGoToSpaceId;
|
||||
|
||||
const SpacesNavigationRail({
|
||||
required this.activeSpaceId,
|
||||
required this.onGoToChats,
|
||||
required this.onGoToSpaceId,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
final isSettings = GoRouter.of(context)
|
||||
.routeInformationProvider
|
||||
.value
|
||||
.uri
|
||||
.path
|
||||
.startsWith('/rooms/settings');
|
||||
return StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
final allSpaces = client.rooms.where((room) => room.isSpace);
|
||||
final rootSpaces = allSpaces
|
||||
.where(
|
||||
(space) => !allSpaces.any(
|
||||
(parentSpace) => parentSpace.spaceChildren
|
||||
.any((child) => child.roomId == space.id),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return SizedBox(
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: rootSpaces.length + 2,
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
return NaviRailItem(
|
||||
isSelected: activeSpaceId == null && !isSettings,
|
||||
onTap: onGoToChats,
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.forum_outlined),
|
||||
),
|
||||
selectedIcon: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.forum),
|
||||
),
|
||||
toolTip: L10n.of(context).chats,
|
||||
unreadBadgeFilter: (room) => true,
|
||||
);
|
||||
}
|
||||
i--;
|
||||
if (i == rootSpaces.length) {
|
||||
return NaviRailItem(
|
||||
isSelected: false,
|
||||
onTap: () => context.go('/rooms/newspace'),
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
toolTip: L10n.of(context).createNewSpace,
|
||||
);
|
||||
}
|
||||
final space = rootSpaces[i];
|
||||
final displayname = rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
);
|
||||
final spaceChildrenIds =
|
||||
space.spaceChildren.map((c) => c.roomId).toSet();
|
||||
return NaviRailItem(
|
||||
toolTip: displayname,
|
||||
isSelected: activeSpaceId == space.id,
|
||||
onTap: () => onGoToSpaceId(rootSpaces[i].id),
|
||||
unreadBadgeFilter: (room) =>
|
||||
spaceChildrenIds.contains(room.id),
|
||||
icon: Avatar(
|
||||
mxContent: rootSpaces[i].avatar,
|
||||
name: displayname,
|
||||
border: BorderSide(
|
||||
width: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
NaviRailItem(
|
||||
isSelected: isSettings,
|
||||
onTap: () => context.go('/rooms/settings'),
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.settings_outlined),
|
||||
),
|
||||
selectedIcon: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.settings),
|
||||
),
|
||||
toolTip: L10n.of(context).settings,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -345,9 +345,9 @@ class PublicRoomBottomSheetState extends State<PublicRoomBottomSheet> {
|
|||
ListTile(
|
||||
subtitle: SelectableLinkify(
|
||||
text: profile!.topic!,
|
||||
linkStyle: const TextStyle(
|
||||
color: Colors.blueAccent,
|
||||
decorationColor: Colors.blueAccent,
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decorationColor: theme.colorScheme.primary,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ packageLicenseOverride:
|
|||
latlong2: Apache-2.0
|
||||
platform_detect: Apache-2.0
|
||||
rxdart: Apache-2.0
|
||||
flutter_new_badger: MIT
|
||||
|
||||
# flutter's internal packages
|
||||
flutter_driver: BSD-3-Clause
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <emoji_picker_flutter/emoji_picker_flutter_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
|
|
@ -26,9 +25,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
||||
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
desktop_webview_window
|
||||
dynamic_color
|
||||
emoji_picker_flutter
|
||||
file_selector_linux
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import Foundation
|
|||
import app_links
|
||||
import audio_session
|
||||
import audioplayers_darwin
|
||||
import desktop_webview_window
|
||||
import device_info_plus
|
||||
import dynamic_color
|
||||
import emoji_picker_flutter
|
||||
|
|
@ -47,7 +46,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
|
||||
|
|
|
|||
1
pangea_packages/fcm_shared_isolate/.github/CODEOWNERS
vendored
Normal file
1
pangea_packages/fcm_shared_isolate/.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* @famedly/instant-messaging
|
||||
10
pangea_packages/fcm_shared_isolate/.gitignore
vendored
Normal file
10
pangea_packages/fcm_shared_isolate/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.DS_Store
|
||||
.dart_tool/
|
||||
.idea/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
pubspec.lock
|
||||
.flutter-plugins*
|
||||
|
||||
build/
|
||||
13
pangea_packages/fcm_shared_isolate/.gitlab-ci.yml
Normal file
13
pangea_packages/fcm_shared_isolate/.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter/stable
|
||||
|
||||
stages:
|
||||
- test
|
||||
|
||||
code_analyze:
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- flutter pub get
|
||||
- flutter format --set-exit-if-changed lib/ test/
|
||||
- flutter analyze
|
||||
10
pangea_packages/fcm_shared_isolate/.metadata
Normal file
10
pangea_packages/fcm_shared_isolate/.metadata
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 84f3d28555368a70270e9ac8390a9441df95e752
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
## 0.2.0
|
||||
|
||||
- Upgrade kotlin version and dependencies
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Initial release
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- camel_case_types
|
||||
|
|
@ -7,3 +5,11 @@ linter:
|
|||
- constant_identifier_names
|
||||
- prefer_final_locals
|
||||
- prefer_final_in_for_each
|
||||
- prefer_relative_imports
|
||||
- unawaited_futures
|
||||
- require_trailing_commas
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
todo: ignore
|
||||
fixme: ignore
|
||||
|
|
|
|||
8
pangea_packages/fcm_shared_isolate/android/.gitignore
vendored
Normal file
8
pangea_packages/fcm_shared_isolate/android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
|||
#Tue Jan 17 18:47:16 PKT 2023
|
||||
gradle.version=6.7.1
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -2,14 +2,14 @@ group 'com.famedly.fcm_shared_isolate'
|
|||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.32'
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.1'
|
||||
classpath 'com.android.tools.build:gradle:8.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
|
@ -26,10 +26,9 @@ apply plugin: 'kotlin-android'
|
|||
|
||||
def firebaseCoreProject = findProject(':firebase_core')
|
||||
if (firebaseCoreProject == null) {
|
||||
|
||||
throw GradleException('Could not find the firebase_core FlutterFire plugin, have you added it as a dependency in your pubspec?')
|
||||
throw new GradleException('Could not find the firebase_core FlutterFire plugin, have you added it as a dependency in your pubspec?')
|
||||
} else if (!firebaseCoreProject.properties['FirebaseSDKVersion']) {
|
||||
throw GradleException('A newer version of the firebase_core FlutterFire plugin is required, please update your firebase_core pubspec dependency.')
|
||||
throw new GradleException('A newer version of the firebase_core FlutterFire plugin is required, please update your firebase_core pubspec dependency.')
|
||||
}
|
||||
|
||||
def getRootProjectExtOrCoreProperty(name, firebaseCoreProject) {
|
||||
|
|
@ -39,13 +38,14 @@ def getRootProjectExtOrCoreProperty(name, firebaseCoreProject) {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 34
|
||||
compileSdk 33
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdkVersion 19
|
||||
namespace "com.famedly.fcm_shared_isolate"
|
||||
}
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
|
|
@ -53,10 +53,14 @@ android {
|
|||
dependencies {
|
||||
api firebaseCoreProject
|
||||
implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}")
|
||||
api 'com.google.firebase:firebase-messaging'
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'com.google.firebase:firebase-messaging-ktx:23.0.2'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
## This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Tue Jan 17 02:51:21 PKT 2023
|
||||
sdk.dir=/Users/lalaiqbalkhokhar/Library/Android/sdk
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package com.famedly.fcm_shared_isolate
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
|
|
@ -12,7 +11,7 @@ import io.flutter.plugin.common.MethodChannel.Result
|
|||
class FcmSharedIsolatePlugin : FlutterPlugin, MethodCallHandler {
|
||||
private lateinit var channel: MethodChannel
|
||||
|
||||
private val fcm = FirebaseMessaging.getInstance()
|
||||
private val fcm = try { FirebaseMessaging.getInstance() } catch (e: Exception) { null }
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "fcm_shared_isolate")
|
||||
|
|
@ -26,7 +25,7 @@ class FcmSharedIsolatePlugin : FlutterPlugin, MethodCallHandler {
|
|||
}
|
||||
|
||||
if (call.method == "getToken") {
|
||||
val getToken = FirebaseMessaging.getInstance().getToken()
|
||||
val getToken = fcm.getToken()
|
||||
getToken.addOnSuccessListener { result.success(it) }
|
||||
getToken.addOnFailureListener { result.error("unknown", null, null) }
|
||||
} else {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue