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:
ggurdin 2025-03-06 11:29:03 -05:00 committed by GitHub
parent 888cc7e13d
commit 36a4654b2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
184 changed files with 6018 additions and 2885 deletions

View file

@ -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

View file

@ -1,2 +1,2 @@
FLUTTER_VERSION=3.27.3
FLUTTER_VERSION=3.27.4
JAVA_VERSION=17

305
.gitignore vendored
View file

@ -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

View file

@ -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)

View file

@ -38,6 +38,7 @@
android:requestLegacyExternalStorage="true"
android:allowBackup="false"
android:fullBackupContent="false"
android:localeConfig="@xml/locale_config"
>
<activity
android:name=".MainActivity"

View 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>

View file

@ -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")

View file

@ -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

View file

@ -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
}

View file

@ -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."
}
}

View file

@ -5457,4 +5457,4 @@
},
"downloadGboard": "Descargar Gboard",
"autocorrectNotAvailable": "Desafortunadamente, tu plataforma no es compatible actualmente con esta función. ¡Mantente atento a futuros desarrollos!"
}
}

View file

@ -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": {}
}

View file

@ -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": {}
}

View file

@ -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": {}
}

View file

@ -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

View file

@ -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!"
}
}

View file

@ -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>

Binary file not shown.

View file

@ -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,
),

View file

@ -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';
}

View file

@ -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();
}

View file

@ -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(

View file

@ -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,
),
);
},

View file

@ -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

View file

@ -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(

View file

@ -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',
),
),
),
),

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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')),
),
);
}

View file

@ -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),

View file

@ -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(),

View file

@ -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) {

View file

@ -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#
],
],
),

View file

@ -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#
),
);
},

View file

@ -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(),
),
],
),

View file

@ -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,

View file

@ -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,
),
),
),
),

View file

@ -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(

View file

@ -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(),

View file

@ -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(

View file

@ -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(

View file

@ -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;

View file

@ -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),
),
],
),

View file

@ -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,

View file

@ -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(

View file

@ -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;

View file

@ -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,
),
],
);
},
),
),
],
);
}
}

View file

@ -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#
],
),

View file

@ -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,

View 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()}";
}
}

View file

@ -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);
}

View file

@ -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]),
),
);
},
),
],
),
);
},
),

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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,
),

View file

@ -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(

View file

@ -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;
}

View file

@ -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) {

View 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,
),
],
);
}
}
}

View file

@ -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(

View file

@ -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(

View file

@ -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) {

View file

@ -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,
),
),
),
);

View file

@ -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(

View file

@ -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)

View 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,
),
],
),
);
},
);
}
}

View file

@ -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,

View file

@ -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

View file

@ -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);

View file

@ -4,7 +4,6 @@
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
desktop_webview_window
dynamic_color
emoji_picker_flutter
file_selector_linux

View file

@ -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"))

View file

@ -0,0 +1 @@
* @famedly/instant-messaging

View file

@ -0,0 +1,10 @@
.DS_Store
.dart_tool/
.idea/
.packages
.pub/
pubspec.lock
.flutter-plugins*
build/

View 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

View 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

View file

@ -1,3 +1,7 @@
## 0.2.0
- Upgrade kotlin version and dependencies
## 0.1.0
- Initial release

View file

@ -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

View file

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View file

@ -1,2 +0,0 @@
#Tue Jan 17 18:47:16 PKT 2023
gradle.version=6.7.1

View file

@ -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'
}

View file

@ -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

View file

@ -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