Compare commits

..

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

307 changed files with 74686 additions and 62720 deletions

3
.gitignore vendored
View File

@ -10,6 +10,7 @@
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
lib/generated_plugin_registrant.dart
prime prime
# libolm package # libolm package
@ -37,6 +38,7 @@ prime
/build/ /build/
# Web related # Web related
lib/generated_plugin_registrant.dart
docs/build/ docs/build/
docs/.jekyll-cache/ docs/.jekyll-cache/
docs/_site/ docs/_site/
@ -60,4 +62,3 @@ ios/Podfile.lock
/linux/out /linux/out
/macos/out /macos/out
.vs .vs
olm

View File

@ -1,7 +1,7 @@
variables: variables:
FLUTTER_VERSION: 3.10.0 FLUTTER_VERSION: 3.3.9
image: ghcr.io/cirruslabs/flutter:${FLUTTER_VERSION} image: cirrusci/flutter:${FLUTTER_VERSION}
.shared_windows_runners: .shared_windows_runners:
tags: tags:
@ -16,22 +16,20 @@ stages:
code_analyze: code_analyze:
stage: test stage: test
script: script: [./scripts/code_analyze.sh]
- flutter pub get
- dart run import_sorter:main --no-comments --exit-if-changed
- dart format lib/ test/ --set-exit-if-changed
- flutter analyze
- git apply ./scripts/enable-android-google-services.patch
- flutter pub get
- flutter analyze
- flutter pub run dart_code_metrics:metrics lib -r gitlab > code-quality-report.json || true
artifacts: artifacts:
reports: reports:
codequality: code-quality-report.json codequality: code-quality-report.json
tags:
- docker
- famedly
widget_test: widget_test:
stage: test stage: test
script: [flutter test] script: [flutter test]
tags:
- docker
- famedly
# the basic integration test configuration testing FLOSS builds on Synapse # the basic integration test configuration testing FLOSS builds on Synapse
.integration_test: .integration_test:
@ -51,13 +49,15 @@ widget_test:
FF_NETWORK_PER_BUILD: "true" FF_NETWORK_PER_BUILD: "true"
# Tell docker CLI how to talk to Docker daemon. # Tell docker CLI how to talk to Docker daemon.
DOCKER_HOST: tcp://docker:2375/ DOCKER_HOST: tcp://docker:2375/
# Use the btrfs driver for improved performance. # Use the overlayfs driver for improved performance.
DOCKER_DRIVER: btrfs DOCKER_DRIVER: overlay2
# Disable TLS since we're running inside local network. # Disable TLS since we're running inside local network.
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
HOMESERVER: docker HOMESERVER: "docker"
before_script: before_script:
- scripts/integration-prepare-host.sh # start AVD and keep running in background
- scripts/integration-start-avd.sh &
- scripts/integration-prepare-alpine.sh
# create test user environment variables # create test user environment variables
- source scripts/integration-create-environment-variables.sh - source scripts/integration-create-environment-variables.sh
# create Synapse instance # create Synapse instance
@ -65,57 +65,31 @@ widget_test:
# properly set the homeserver IP and create test users # properly set the homeserver IP and create test users
- scripts/integration-prepare-homeserver.sh - scripts/integration-prepare-homeserver.sh
script: script:
# start AVD and keep running in background
- scripts/integration-start-avd.sh &
- flutter pub get - flutter pub get
- scrcpy --no-display --record video.mkv & - flutter test integration_test
- flutter test integration_test --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 ) timeout: 20m
after_script:
- ffmpeg -i video.mkv -vf scale=iw/2:-2 -crf 40 -b:v 2000k -preset fast video.mp4 || true
timeout: 30m
retry: 2
only:
- tags
artifacts:
when: always
paths:
- video.mp4
tags: tags:
- docker - docker
- famedly - famedly
# integration tests for Linux builds # integration tests for Linux builds
### disabled because of Linux headless issues
.integration_test_linux: .integration_test_linux:
extends: .integration_test extends: .integration_test
parallel:
matrix:
- HOMESERVER_IMPLEMENTATION:
- conduit
script: script:
- apt-get update - apk add cmake ninja gtk+3.0-dev clang pkgconf xz-dev libsecret-dev jsoncpp-dev
- apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libsecret-1-dev libjsoncpp-dev
- flutter pub get - flutter pub get
- flutter test integration_test -d linux --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 ) - flutter test integration_test -d linux
after_script: []
artifacts:
# extending the default tests to test the Google-flavored builds # extending the default tests to test the Google-flavored builds
.integration_test_proprietary: .integration_test_proprietary:
extends: .integration_test extends: .integration_test
parallel:
matrix:
- HOMESERVER_IMPLEMENTATION:
- conduit
script: script:
# start AVD and keep running in background
- scripts/integration-start-avd.sh &
- git apply ./scripts/enable-android-google-services.patch - git apply ./scripts/enable-android-google-services.patch
- flutter pub get - flutter pub get
- scrcpy --no-display --record video.mkv & - flutter test integration_test
- flutter test integration_test --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 )
release_mode_launches: .release_mode_launches:
parallel: parallel:
matrix: matrix:
- FLAVOR: - FLAVOR:
@ -125,17 +99,15 @@ release_mode_launches:
stage: test stage: test
before_script: before_script:
- | - |
if [ "$FLAVOR" == "proprietary" ]; then if [ "$FLAVOR" == "proprietary" ]; then
git apply ./scripts/enable-android-google-services.patch git apply ./scripts/enable-android-google-services.patch
fi fi
script: script:
# start AVD and keep running in background # start AVD and keep running in background
- scripts/integration-start-avd.sh & - scripts/integration-start-avd.sh &
# generate temporary release build configuration and ensure app launches # generate temporary release build configuration and ensure app launches
- scripts/integration-check-release-build.sh - scripts/integration-check-release-build.sh
timeout: 20m timeout: 20m
only:
- tags
tags: tags:
- docker - docker
- famedly - famedly
@ -144,46 +116,20 @@ build_web:
stage: build stage: build
before_script: before_script:
[sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh] [sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh]
script: script: [./scripts/build-web.sh]
- flutter build web --release --verbose --source-maps
artifacts: artifacts:
paths: paths:
- build/web/ - build/web/
tags:
# yes, we *do* build a Windows DLL on Linux. More reliable. - docker
build_olm_windows: - famedly
image: archlinux:latest
stage: test
before_script:
- pacman-key --init
- pacman --noconfirm -Sy mingw-w64 cmake git base-devel
script:
- ./scripts/build-olm-windows.sh
- mv olm/build/libolm.dll .
artifacts:
paths:
- libolm.dll
allow_failure: true
only:
- main
- tags
build_windows: build_windows:
extends: extends:
- .shared_windows_runners - .shared_windows_runners
stage: test stage: build
before_script: before_script: [./scripts/prepare-windows.ps1]
- ./scripts/prepare-windows.ps1 script: [./scripts/build-windows.ps1]
# workarounding artifacts download being broken
- $response = Invoke-WebRequest -Uri "$CI_API_V4_URL/projects/$CI_PROJECT_ID/pipelines/$CI_PIPELINE_ID/jobs" -UseBasicParsing
- $jobs = $response | ConvertFrom-Json
- $job = $jobs | where { $_.name -eq "build_olm_windows" }
- $jobId = $job.id
- Invoke-WebRequest -Uri "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$jobId/artifacts/libolm.dll" -UseBasicParsing -OutFile libolm.dll
script:
- ./scripts/build-windows.ps1
- Copy-Item -Path "libolm.dll" -Destination "build/windows/runner/Release"
- ./scripts/package-windows.ps1
artifacts: artifacts:
paths: paths:
- build/windows/runner/Release - build/windows/runner/Release
@ -194,28 +140,28 @@ build_windows:
build_android_debug: build_android_debug:
stage: build stage: build
script: [flutter build apk --debug] script: [./scripts/build-android-debug.sh]
artifacts: artifacts:
when: on_success when: on_success
paths: paths:
- build/app/outputs/apk/debug/app-debug.apk - build/app/outputs/apk/debug/app-debug.apk
tags:
- docker
- famedly
except: except:
- main - main
- tags - tags
tags:
- docker
- famedly
build_android_apk: build_android_apk:
stage: build stage: build
before_script: before_script:
- git apply ./scripts/enable-android-google-services.patch - git apply ./scripts/enable-android-google-services.patch
- ./scripts/prepare-android-release.sh - ./scripts/prepare-android-release.sh
script: [flutter build apk --release] script: [./scripts/build-android-apk.sh]
artifacts: artifacts:
when: on_success when: on_success
paths: paths:
- build/app/outputs/apk/release/app-release.apk - build/android/app-release.apk
tags: tags:
- docker - docker
- famedly - famedly
@ -255,6 +201,9 @@ fdroid_repo:
needs: needs:
- "build_android_apk" - "build_android_apk"
resource_group: playstore_release resource_group: playstore_release
tags:
- docker
- famedly
allow_failure: true allow_failure: true
only: only:
- main - main
@ -274,7 +223,12 @@ pages:
- cd .. - cd ..
- mv docs public - mv docs public
- mv repo public || true - mv repo public || true
- mv build/web/ public/web - mv build/web/ public/nightly
# ensure the nightly deployment knows its location
- sed -i "s/href=\"\/web\/\"/href=\"\/nightly\/\"/g" public/nightly/index.html
- rm -rf build
- ./scripts/download-web-stable.sh
- mv stable public/web
artifacts: artifacts:
paths: paths:
- public - public
@ -283,11 +237,11 @@ pages:
build_linux_x86: build_linux_x86:
stage: build stage: build
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter-linux/stable:${FLUTTER_VERSION}
before_script: before_script:
- sudo apt-get update [
- sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y sudo apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install keyboard-configuration -y && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y,
script: [flutter build linux --release] ]
script: [./scripts/build-linux.sh]
tags: tags:
- docker - docker
- famedly - famedly
@ -298,9 +252,8 @@ build_linux_x86:
build_linux_arm64: build_linux_arm64:
stage: build stage: build
before_script: before_script: [flutter upgrade]
- flutter upgrade $FLUTTER_VERSION --force script: [./scripts/build-linux.sh]
script: [flutter build linux --release]
tags: [docker_arm64] tags: [docker_arm64]
only: only:
- main - main
@ -314,6 +267,8 @@ build_linux_arm64:
update_dependencies: update_dependencies:
stage: build stage: build
needs: [] needs: []
tags:
- docker
only: only:
- schedules - schedules
variables: variables:
@ -338,6 +293,9 @@ update_dependencies:
.release: .release:
stage: deploy stage: deploy
image: curlimages/curl:latest image: curlimages/curl:latest
tags:
- docker
- famedly
rules: rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/' - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
- if: '$CI_COMMIT_TAG =~ /^rc\d+\.\d+\.\d+-\d+$/' - if: '$CI_COMMIT_TAG =~ /^rc\d+\.\d+\.\d+-\d+$/'
@ -350,29 +308,28 @@ upload_android:
extends: .release extends: .release
script: script:
- | - |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file build/app/outputs/apk/release/app-release.apk ${PACKAGE_REGISTRY_URL}/fluffychat.apk curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file build/android/app-release.apk ${PACKAGE_REGISTRY_URL}/fluffychat.apk
upload_web: upload_web:
extends: .release extends: .release
script: script:
- tar czf package.tar.gz -C build/web/ . - tar czf package.tar.gz -C build/web/ .
- | - |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-web.tar.gz curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-web.tar.gz
upload_linux_x86: upload_linux_x86:
extends: .release extends: .release
script: script:
- tar czf package.tar.gz -C build/linux/x64/release/bundle/ . - tar czf package.tar.gz -C build/linux/x64/release/bundle/ .
- | - |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-x86.tar.gz curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-x86.tar.gz
upload_linux_arm64: upload_linux_arm64:
extends: .release extends: .release
script: script:
- tar czf package.tar.gz -C build/linux/arm64/release/bundle/ . - tar czf package.tar.gz -C build/linux/arm64/release/bundle/ .
- | - |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-arm64.tar.gz curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-arm64.tar.gz
allow_failure: true
upload_windows: upload_windows:
extends: .release extends: .release
@ -382,9 +339,8 @@ upload_windows:
- mv build/windows/runner/Release/fluffychat.msix fluffychat.msix - mv build/windows/runner/Release/fluffychat.msix fluffychat.msix
- cd build/windows/runner/Release; zip -r ../../../../package.zip . ; cd - - cd build/windows/runner/Release; zip -r ../../../../package.zip . ; cd -
- | - |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.zip ${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.zip ${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file fluffychat.msix ${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file fluffychat.msix ${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix
allow_failure: true
deploy_playstore: deploy_playstore:
stage: deploy stage: deploy

View File

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

View File

@ -1,229 +1,3 @@
## v1.12.0
- Added translation using Weblate (Toki Pona) (Mæve Rey)
- Translated using Weblate (Arabic) (Rex_sa)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Polish) (lauren n. liberda)
- Translated using Weblate (Romanian) (Riley)
- Translated using Weblate (Russian) (DarkCoder15)
- Translated using Weblate (Spanish) (José Muñoz)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- build: Remove dependency overwrite for ffi (Krille)
- build: Update dependencies (Krille)
- builds: Change minsdkversion of Android from 16 to 19 (Krille)
- builds: Do not allow failure for linux x86 (Krille)
- builds: Do not use verbose mode on building linux (Krille)
- builds: Linux with flutter 3.10 (Krille)
- builds: Remove workaround for building linux arm64 (Krille)
- builds: Update file_picker to 5.3.0 (Krille)
- builds: Update flutter table html (Krille)
- builds: Update flutter_html (Krille)
- builds: migrate to dart 3.0/flutter 3.10 (lauren n. liberda)
- chore: Add missing blockquote style (Krille)
- chore: Allow failure in build linux for now (Krille)
- chore: Ask for storage persistence (Krille)
- chore: Clean unused translations (Malin Errenst)
- chore: Enhance room pills (Krille)
- chore: Minor code clean up (Krille)
- chore: Update flutter webrtc (Krille)
- chore: Upgrade to Flutter 3.10.1 (Malin Errenst)
- chore: change release curl calls to use --fail-with-body (Tim Flink)
- chore: update macOS icons and add build script (TheOneWithTheBraid)
- design: Replace anime images with neutral cupertino icons (Krille)
- feat: Add toggle to mute notifications from chat groups (fbievan)
- feat: Allow ruby tags in html (Krille)
- feat: Display progress value for initial sync (Krille)
- feat: Implement new error reporting tool when critical features break like playing audio or video messages or opening a chat (Krille)
- feat: clean up macOS build metadata (TheOneWithTheBraid)
- feat: set display information correctly (TheOneWithTheBraid)
- feat: update macOS build files (TheOneWithTheBraid)
- feat: update macOS build information for macOS Ventura (TheOneWithTheBraid)
- fix "Unhandled Exception: VRouter.of(context) was called with a context which does not contain a VRouter." (Lauren N. Liberda)
- fix: Broken arb file (Krille)
- fix: Do not unnecessary request all members in public rooms (Krille)
- fix: Remove wrong rendered linebreak in html (Krille)
- fix: Scroll down button (Krille)
- fix: Scroll up and scroll down buttons in chat list (Krille)
- fix: Scrolldown button (Krille)
- fix: Too long file name cause a render overflow (Skying)
- fix: Try to reload timeline on IOException (Krille)
- fix: User pills (Krille)
- fix: broken CI artifact uploads (TheOneWithTheBraid)
- fix: custom emote placeholder (TheOneWithTheBraid)
- fix: path of libolm (TheOneWithTheBraid)
- fix: Quick account switching (JHansen)
- fix: read reciepts (JHansen)
- perf: Use valuenotifier to not rebuild chatlist (Krille)
- refactor: Reimplement flutter matrix html locally (Krille)
- refactor: Update Roboto and Noto Emoji (The one with the Braid)
- refactor: Use AnimatedSize for FAB (Krille)
- refactor: Use DateTime for weekday localization (Malin Errenst)
## v1.11.2
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Polish) (Eryk Michalak)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- feat: Permission dialog before open link in browser (Krille)
- fix: Chats do not load (Krille)
## v1.11.1 - 2023-04-20
- fix: Download files on web and iOS with correct mimetype
## v1.11.0 - 2023-04-14
- feat: Add visual read marker (Krille)
- feat: Jump to last read event (Krille)
- feat: Use fragmented timeline to jump to event (Krille)
- feat: change to flutterwebauth2 (ShootingStarDragons)
- fix: Join public room (Krille)
- fix: Set fcm priority to max on android (Krille)
- refactor: CI scripts and old workarounds for build scripts (Krille)
- refactor: Client in ChatPage (Krille)
- refactor: Not nullable room in ChatPage (Krille)
- refactor: Switch to file_picker package and get rid of some dependency overrides (Krille)
- refactor: Use correct Matrix instance (Krille)
- style: Make emptypage logo bigger (Krille)
- style: Minor adjustments for modal bottom sheets (Krille)
- style: Move chats to top (Krille)
- style: Use SliverList for chatlist (Krille)
- refactor: Container -> SizedBox.shrink() (noob_tea)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (French) (Anne Onyme 017)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Persian) (Parsa)
- Translated using Weblate (Persian) (Siavash)
- Translated using Weblate (Polish) (Luna)
- Translated using Weblate (Swedish) (Kristoffer Grundström)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
## v1.10.0 - 2023-02-25
- Added translation using Weblate (Thai) (Wphaoka)
- Added translation using Weblate (Tibetan) (Nathan Freitas)
- Default hardcoded message when l10n is not available (fabienli)
- Fix: The stable repo fingerprint (TODO the qr-code should be updated) (machiav3lli)
- Translated using Weblate (Basque) (xabirequejo)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (French) (Anne Onyme 017)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Japanese) (Suguru Hirahara)
- Translated using Weblate (Persian) (Farooq Karimi Zadeh)
- Translated using Weblate (Swedish) (Joaquim Homrighausen)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- chore: Disable stable for web until script is fixed (Krille)
- chore: Display warning when logout without backup (Krille)
- chore: Downgrade flutter CI version (Krille)
- chore: Follow up audioplayer on linux (Krille)
- chore: Follow up chat encryption desgin (Krille)
- chore: Follow up fix audioplayer on android (Christian Pauly)
- chore: Follow up formatting (Christian Pauly)
- chore: Follow up formatting (Krille)
- chore: Follow up remove hero animation (Krille)
- chore: Follow up secrity settings design (Krille)
- chore: Follow up settings page (Krille)
- chore: Follow up settings page design (Christian Pauly)
- chore: Follow up style adjustments (Krille)
- chore: Lookup l10n in pushhelper if null (Krille)
- chore: Update matrix package to 0.17.0 (Krille)
- chore: Update to Flutter 3.7.1 (Krille)
- docs/qr-stable.svg: update the QR code (Aminda Suomalainen)
- feat: Enable audioplayer for web and linux (Christian Pauly)
- fix: Display error when user tries to send too large file (Christian Pauly)
- refactor: Do only instantiate AudioPlayer() object when in use (Christian Pauly)
- refactor: Remove syncstatus verbose logs (Christian Pauly)
- refactor: Store cached files in tmp directory so OS will clear file cache from time to time (Krille)
- style: Adjust key verification dialog (Christian Pauly)
- style: Bootstrap design adjustments (Christian Pauly)
- style: Encryption page adjustments (Christian Pauly)
- style: Enhance user device settings design (Krille)
- style: Enhanced chat details design (Krille)
- style: Give chat list list tiles rounded corners (Krille)
- style: Link underline color (Christian Pauly)
- style: Make adaptive bottom sheets scrollable by default (Krille)
- style: Make invite page more pretty (Krille)
- style: New settings design (Krille)
- style: Nicer chips in encryption settings and icons showing device status (Krille)
- style: Use emojis on web as well (Christian Pauly)
- style: Use robotomono to display device keys (Christian Pauly)
- utils/url_launcher: force opening http(s) links in external browser (Marcus Hoffmann)
## v1.9.0 - 2023-01-29
- Translated using Weblate (Czech) (Michal Bedáň)
- Translated using Weblate (Czech) (grreby)
- 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) (Vri 🌈)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Korean) (Youngbin Han)
- Translated using Weblate (Polish) (Wiktor)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- chore: Change invite link textfield label (Krille)
- chore: Remove unused dependency (Krille)
- chore: Remove unused translations (Krille)
- chore: Update Matrix SDK and refactor (Krille)
- chore: Update dependencies (Krille)
- chore: Update flutter_map (Krille)
- chore: add integration tests (TheOneWithTheBraid)
- chore: add integration tests for spaces (TheOneWithTheBraid)
- design: More clear chat background and rounded popup menu (Krille)
- design: Nicer navigationrail (Krille)
- design: Upgrade to Flutter 3.7
- feat: Bring back disabling the header bar on Linux desktop (q234rty)
- feat: Nicer design for abandonded DM rooms (Christian Pauly)
- fix: Archive (Krille)
- fix: Shared preferences package for flutter 3.7 (Christian Pauly)
- fix: permission of web builds (TheOneWithTheBraid)
- fix: Notification Settings (Krille)
- refactor: Migrate to Flutter 3.7.0 (Christian Pauly)
- refactor: Same animations everywhere in app (Krille)
- refactor: Stories header with futurebuilder (Krille)
- refactor: disable some redundant tests (TheOneWithTheBraid)
- style: Animate in out search results (Krille)
- style: New modal bottom sheets (Krille)
- style: Redesign public room bottomsheets (Krille)
## v1.8.0 2022-12-30
- Added translation using Weblate (Yue (yue_HK)) (Raatty)
- Translated using Weblate (Chinese (Simplified)) (Mike Evans)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (French) (Anne Onyme 017)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- design: New encryption page (Krille Fear)
- feat: Add audio message support to linux (Krille Fear)
- feat: Use Android system accent color (Krille Fear)
- feat: include olm to Windows builds (TheOneWithTheBraid)
- feat: Store drafts (Krille)
- fix: Android push notification follow-up (TheOneWithTheBraid)
- fix: Content banner (Krille Fear)
- fix: Correct redacted by username (Krille Fear)
- fix: Do not setup push on every app resume (Krille Fear)
- fix: Encryption button is orange in public rooms (Krille Fear)
- fix: File event design (Krille Fear)
- fix: Hide google services warning after marked (Krille Fear)
- fix: Improve story page appearance (Reinhart Previano Koentjoro)
- fix: Libhandy windows (Krille Fear)
- fix: Monochromatic icon rendering for Android 13+ (Reinhart Previano Koentjoro)
- fix: homeserver error text not visible in app bar (TheOneWithTheBraid)
- fix: minor issues in room list (TheOneWithTheBraid)
## v1.7.2 2022-12-19 ## v1.7.2 2022-12-19
Update dependencies and translations. Update dependencies and translations.

View File

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

View File

@ -8,7 +8,6 @@ linter:
- prefer_final_locals - prefer_final_locals
- prefer_final_in_for_each - prefer_final_in_for_each
- sort_pub_dependencies - sort_pub_dependencies
- require_trailing_commas
analyzer: analyzer:
errors: errors:

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -2,5 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/> <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@ -1,5 +1,5 @@
buildscript { buildscript {
ext.kotlin_version = '1.8.0' ext.kotlin_version = '1.6.10'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
@ -27,6 +27,6 @@ subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }
tasks.register("clean", Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

View File

@ -5,17 +5,12 @@
<testcase classname="fastlane.lanes" name="0: update_fastlane" time="1.455419"> <testcase classname="fastlane.lanes" name="0: update_fastlane" time="0.000202">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: default_platform" time="0.000127"> <testcase classname="fastlane.lanes" name="1: default_platform" time="7.9e-05">
</testcase>
<testcase classname="fastlane.lanes" name="2: google_play_track_version_codes" time="2.638619">
</testcase> </testcase>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,30 @@
{ {
"@@last_modified": "2021-08-14 12:41:10.154280", "@@last_modified": "2021-08-14 12:41:10.154280",
"about": "সম্পর্কে", "about": "সম্পর্কে",
"@about": { "@about": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"accept": "স্বীকার করি", "accept": "স্বীকার করি",
"@accept": { "@accept": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"acceptedTheInvitation": "{username} আমন্ত্রণ গ্রহণ করেছে", "acceptedTheInvitation": "{username} আমন্ত্রণ গ্রহণ করেছে",
"@acceptedTheInvitation": { "@acceptedTheInvitation": {
"type": "text", "type": "text",
"placeholders": { "placeholders": {
"username": {} "username": {}
}
},
"account": "অ্যাকাউন্ট",
"@account": {
"type": "text",
"placeholders": {}
} }
},
"account": "অ্যাকাউন্ট",
"@account": {
"type": "text",
"placeholders": {}
},
"accountInformation": "অ্যাকাউন্ট তথ্য",
"@accountInformation": {
"type": "text",
"placeholders": {}
}
} }

View File

@ -1 +0,0 @@
{}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
{}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
{} {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1551
assets/l10n/intl_hy.arb Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,120 +1,155 @@
{ {
"@@last_modified": "2021-08-14 12:41:09.940318", "@@last_modified": "2021-08-14 12:41:09.940318",
"copiedToClipboard": "Copiada para a área de transferência", "copiedToClipboard": "Copiada para a área de transferência",
"@copiedToClipboard": { "@copiedToClipboard": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"login": "Iniciar sessão", "login": "Iniciar sessão",
"@login": { "@login": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"about": "Sobre", "monday": "segunda-feira",
"@about": { "@monday": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"admin": "Admin", "saturday": "sábado",
"@admin": { "@saturday": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"areYouSure": "Tens a certeza?", "wednesday": "quarta-feira",
"@areYouSure": { "@wednesday": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"notifications": "Notificações", "about": "Sobre",
"@notifications": { "@about": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"account": "Conta", "admin": "Admin",
"@account": { "@admin": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"cancel": "Cancelar", "areYouSure": "Tens a certeza?",
"@cancel": { "@areYouSure": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"delete": "Eliminar", "notifications": "Notificações",
"@delete": { "@notifications": {
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"dateAndTimeOfDay": "{date}, {timeOfDay}", "account": "Conta",
"@dateAndTimeOfDay": { "@account": {
"type": "text", "type": "text",
"placeholders": { "placeholders": {}
"date": {}, },
"timeOfDay": {} "cancel": "Cancelar",
} "@cancel": {
}, "type": "text",
"dateWithYear": "{day}-{month}-{year}", "placeholders": {}
"@dateWithYear": { },
"type": "text", "delete": "Eliminar",
"placeholders": { "@delete": {
"year": {}, "type": "text",
"month": {}, "placeholders": {}
"day": {} },
} "dateAndTimeOfDay": "{date}, {timeOfDay}",
}, "@dateAndTimeOfDay": {
"help": "Ajuda", "type": "text",
"@help": { "placeholders": {
"type": "text", "date": {},
"placeholders": {} "timeOfDay": {}
},
"messages": "Mensagens",
"@messages": {
"type": "text",
"placeholders": {}
},
"reason": "Razão",
"@reason": {
"type": "text",
"placeholders": {}
},
"privacy": "Privacidade",
"@privacy": {
"type": "text",
"placeholders": {}
},
"openCamera": "Abrir câmara",
"@openCamera": {
"type": "text",
"placeholders": {}
},
"settings": "Configurações",
"@settings": {
"type": "text",
"placeholders": {}
},
"logout": "Terminar sessão",
"@logout": {
"type": "text",
"placeholders": {}
},
"search": "Pesquisar",
"@search": {
"type": "text",
"placeholders": {}
},
"users": "Utilizadores",
"@users": {},
"close": "Fechar",
"@close": {
"type": "text",
"placeholders": {}
},
"dateWithoutYear": "{day}-{month}",
"@dateWithoutYear": {
"type": "text",
"placeholders": {
"month": {},
"day": {}
}
} }
} },
"dateWithYear": "{day}-{month}-{year}",
"@dateWithYear": {
"type": "text",
"placeholders": {
"year": {},
"month": {},
"day": {}
}
},
"help": "Ajuda",
"@help": {
"type": "text",
"placeholders": {}
},
"messages": "Mensagens",
"@messages": {
"type": "text",
"placeholders": {}
},
"reason": "Razão",
"@reason": {
"type": "text",
"placeholders": {}
},
"privacy": "Privacidade",
"@privacy": {
"type": "text",
"placeholders": {}
},
"openCamera": "Abrir câmara",
"@openCamera": {
"type": "text",
"placeholders": {}
},
"settings": "Configurações",
"@settings": {
"type": "text",
"placeholders": {}
},
"tuesday": "terça-feira",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"logout": "Terminar sessão",
"@logout": {
"type": "text",
"placeholders": {}
},
"search": "Pesquisar",
"@search": {
"type": "text",
"placeholders": {}
},
"sunday": "domingo",
"@sunday": {
"type": "text",
"placeholders": {}
},
"users": "Utilizadores",
"@users": {},
"close": "Fechar",
"@close": {
"type": "text",
"placeholders": {}
},
"dateWithoutYear": "{day}-{month}",
"@dateWithoutYear": {
"type": "text",
"placeholders": {
"month": {},
"day": {}
}
},
"friday": "sexta-feira",
"@friday": {
"type": "text",
"placeholders": {}
},
"thursday": "quinta-feira",
"@thursday": {
"type": "text",
"placeholders": {}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

374
assets/l10n/intl_si.arb Normal file
View File

@ -0,0 +1,374 @@
{
"@@last_modified": "2021-08-14 12:41:09.895217",
"about": "පිළිබඳව",
"@about": {
"type": "text",
"placeholders": {}
},
"accept": "පිළිගන්න",
"@accept": {
"type": "text",
"placeholders": {}
},
"account": "ගිණුම",
"@account": {
"type": "text",
"placeholders": {}
},
"accountInformation": "ගිණුමේ තොරතුරු",
"@accountInformation": {
"type": "text",
"placeholders": {}
},
"addEmail": "වි-තැපෑල එකතු කරන්න",
"@addEmail": {
"type": "text",
"placeholders": {}
},
"admin": "පරිපාලක",
"@admin": {
"type": "text",
"placeholders": {}
},
"allChats": "සියලුම සංවාද",
"@allChats": {
"type": "text",
"placeholders": {}
},
"alreadyHaveAnAccount": "දැනටමත් ගිණුමක් තිබේද?",
"@alreadyHaveAnAccount": {
"type": "text",
"placeholders": {}
},
"anyoneCanJoin": "ඕනෑම කෙනෙකුට එක්විය හැකිය",
"@anyoneCanJoin": {
"type": "text",
"placeholders": {}
},
"archive": "සංරක්ෂිතය",
"@archive": {
"type": "text",
"placeholders": {}
},
"archivedRoom": "සංරක්ෂිත කාමරය",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "ආගන්තුක පරිශීලකයින්ට එක්වීමට අවසර තිබේද",
"@areGuestsAllowedToJoin": {
"type": "text",
"placeholders": {}
},
"areYouSure": "ඔබට විශ්වාසද?",
"@areYouSure": {
"type": "text",
"placeholders": {}
},
"areYouSureYouWantToLogout": "ඔබට නික්මීමට අවශ්‍ය බව විශ්වාසද?",
"@areYouSureYouWantToLogout": {
"type": "text",
"placeholders": {}
},
"audioPlayerPlay": "ධාවනය",
"@audioPlayerPlay": {
"type": "text",
"placeholders": {}
},
"blockDevice": "උපාංගය අවහිර කරන්න",
"@blockDevice": {
"type": "text",
"placeholders": {}
},
"cachedKeys": "යතුරු නිහිතගතයි",
"@cachedKeys": {
"type": "text",
"placeholders": {}
},
"cancel": "අවලංගු කරන්න",
"@cancel": {
"type": "text",
"placeholders": {}
},
"changeDeviceName": "උපාංගයේ නම වෙනස් කරන්න",
"@changeDeviceName": {
"type": "text",
"placeholders": {}
},
"changePassword": "මුරපදය වෙනස් කරන්න",
"@changePassword": {
"type": "text",
"placeholders": {}
},
"chat": "සංවාදය",
"@chat": {
"type": "text",
"placeholders": {}
},
"chatBackup": "සංවාද උපස්ථය",
"@chatBackup": {
"type": "text",
"placeholders": {}
},
"chatDetails": "සංවාදයේ විස්තර",
"@chatDetails": {
"type": "text",
"placeholders": {}
},
"chats": "සංවාද",
"@chats": {
"type": "text",
"placeholders": {}
},
"chooseAStrongPassword": "ශක්තිමත් මුරපදයක් තෝරන්න",
"@chooseAStrongPassword": {
"type": "text",
"placeholders": {}
},
"chooseAUsername": "පරිශීලක නාමයක් තෝරන්න",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "සංරක්ෂිතය හිස් කරන්න",
"@clearArchive": {},
"close": "වසන්න",
"@close": {
"type": "text",
"placeholders": {}
},
"commandHint_join": "දී ඇති කාමරයට එක්වන්න",
"@commandHint_join": {
"type": "text",
"description": "Usage hint for the command /join"
},
"commandHint_leave": "මෙම කාමරය හැරයන්න",
"@commandHint_leave": {
"type": "text",
"description": "Usage hint for the command /leave"
},
"commandInvalid": "විධානය වලංගු නොවේ",
"@commandInvalid": {
"type": "text"
},
"commandMissing": "{{command} විධානයක් නොවේ.",
"@commandMissing": {
"type": "text",
"placeholders": {
"command": {}
},
"description": "State that {command} is not a valid /command."
},
"compareEmojiMatch": "සසඳා බලා පහත දැක්වෙන ඉමොජි අනෙක් උපාංගයට නිසැකවම ගැලපෙන බවට වග බලා ගන්න:",
"@compareEmojiMatch": {
"type": "text",
"placeholders": {}
},
"compareNumbersMatch": "සංසන්දනය කර පහත දැක්වෙන අංක අනෙක් උපාංගට නිසැකව ගැලපෙන බවට වග බලා ගන්න:",
"@compareNumbersMatch": {
"type": "text",
"placeholders": {}
},
"confirm": "තහවුරු කරන්න",
"@confirm": {
"type": "text",
"placeholders": {}
},
"connect": "සබඳින්න",
"@connect": {
"type": "text",
"placeholders": {}
},
"connectionAttemptFailed": "සබැඳීමේ උත්සාහය අසාර්ථකයි",
"@connectionAttemptFailed": {
"type": "text",
"placeholders": {}
},
"contactHasBeenInvitedToTheGroup": "සමූහය වෙත සබඳතාවයකට ආරාධනා කර ඇත",
"@contactHasBeenInvitedToTheGroup": {
"type": "text",
"placeholders": {}
},
"copy": "පිටපත්",
"@copy": {
"type": "text",
"placeholders": {}
},
"create": "සාදන්න",
"@create": {
"type": "text",
"placeholders": {}
},
"createAccountNow": "දැන් ගිණුමක් සාදන්න",
"@createAccountNow": {
"type": "text",
"placeholders": {}
},
"createNewGroup": "නව සමූහයක් සාදන්න",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"donate": "පරිත්‍යාග",
"@donate": {
"type": "text",
"placeholders": {}
},
"encryption": "සංකේතාංකනය",
"@encryption": {
"type": "text",
"placeholders": {}
},
"everythingReady": "සියල්ල සූදානම්!",
"@everythingReady": {
"type": "text",
"placeholders": {}
},
"fontSize": "මුද්‍රණඅකුරේ ප්‍රමාණය",
"@fontSize": {
"type": "text",
"placeholders": {}
},
"goToTheNewRoom": "නව කාමරයට යන්න",
"@goToTheNewRoom": {
"type": "text",
"placeholders": {}
},
"joinRoom": "කාමරයට එක්වන්න",
"@joinRoom": {
"type": "text",
"placeholders": {}
},
"keysCached": "යතුරු නිහිතගත යි",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"next": "ඊලඟ",
"@next": {
"type": "text",
"placeholders": {}
},
"noPublicRoomsFound": "ප්‍රසිද්ධ කාමර හමු නොවිණි…",
"@noPublicRoomsFound": {
"type": "text",
"placeholders": {}
},
"people": "මිනිසුන්",
"@people": {
"type": "text",
"placeholders": {}
},
"publicGroups": "ප්‍රසිද්ධ සමූහ",
"@publicGroups": {
"type": "text",
"placeholders": {}
},
"removeDevice": "උපාංගය ඉවත්කරන්න",
"@removeDevice": {
"type": "text",
"placeholders": {}
},
"roomVersion": "කාමරයේ අනුවාදය",
"@roomVersion": {
"type": "text",
"placeholders": {}
},
"savedFileAs": "ලෙස ගොනුව සුරකින්න {filename}",
"@savedFileAs": {
"type": "text",
"placeholders": {
"filename": {}
}
},
"saveFile": "ගොනුව සුරකින්න",
"@saveFile": {
"type": "text",
"placeholders": {}
},
"saveFileToFolder": "ගොනුව මෙම බහාලුමට සුරකින්න",
"@saveFileToFolder": {
"type": "text",
"placeholders": {}
},
"securityKey": "ආරක්ෂක යතුර",
"@securityKey": {
"type": "text",
"placeholders": {}
},
"securityKeyLost": "ආරක්ෂක යතුර නැතිවුනාද?",
"@securityKeyLost": {
"type": "text",
"placeholders": {}
},
"send": "යවන්න",
"@send": {
"type": "text",
"placeholders": {}
},
"showPassword": "මුරපදය පෙන්වන්න",
"@showPassword": {
"type": "text",
"placeholders": {}
},
"sunday": "ඉරිදා",
"@sunday": {
"type": "text",
"placeholders": {}
},
"username": "පරිශීලක නාමය",
"@username": {
"type": "text",
"placeholders": {}
},
"videoCall": "දෘශ්‍ය ඇමතුම",
"@videoCall": {
"type": "text",
"placeholders": {}
},
"wallpaper": "බිතුපත",
"@wallpaper": {
"type": "text",
"placeholders": {}
},
"warning": "අවවාදයයි!",
"@warning": {
"type": "text",
"placeholders": {}
},
"wednesday": "බදාදා",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"writeAMessage": "පණිවිඩයක් ලියන්න…",
"@writeAMessage": {
"type": "text",
"placeholders": {}
},
"yes": "ඔව්",
"@yes": {
"type": "text",
"placeholders": {}
},
"you": "ඔබ",
"@you": {
"type": "text",
"placeholders": {}
},
"yourOwnUsername": "ඔබට හිමි පරිශීලකනාමය",
"@yourOwnUsername": {
"type": "text",
"placeholders": {}
},
"zoomIn": "විශාලනය",
"@zoomIn": {
"type": "text",
"placeholders": {}
},
"zoomOut": "කුඩාලනය",
"@zoomOut": {
"type": "text",
"placeholders": {}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,20 @@
{ {
"@@last_modified": "2021-08-14 12:41:09.826673", "@@last_modified": "2021-08-14 12:41:09.826673",
"acceptedTheInvitation": "{username} அழைப்பை ஏற்றுக்கொண்டார்", "acceptedTheInvitation": "{username} அழைப்பை ஏற்றுக்கொண்டார்",
"@acceptedTheInvitation": { "@acceptedTheInvitation": {
"type": "text", "type": "text",
"placeholders": { "placeholders": {
"username": {} "username": {}
}
},
"accept": "ஏற்றுக்கொள்",
"@accept": {
"type": "text",
"placeholders": {}
},
"about": "பற்றி",
"@about": {
"type": "text",
"placeholders": {}
} }
} },
"accept": "ஏற்றுக்கொள்",
"@accept": {
"type": "text",
"placeholders": {}
},
"about": "பற்றி",
"@about": {
"type": "text",
"placeholders": {}
}
}

View File

@ -1 +0,0 @@
{}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/Kofi_pixel_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -7,9 +7,11 @@ are also published on it.
Easiest way to add the Repository is to either **scan the QR-Code** or if you are on your phone **directly click it**. Easiest way to add the Repository is to either **scan the QR-Code** or if you are on your phone **directly click it**.
<a href="fdroidrepos://fluffychat.im/repo/stable/repo/?fingerprint=5EDB5C4395B2F2D9BA682F6A1D275170CCE5365A6FA27D2220EA8D52A6D95F07" > {::nomarkdown}
<a href="fdroidrepos://fluffychat.im/repo/stable/repo/?fingerprint=8E2637AEF6697CC6DD486AF044A6EE45B1A742AE3EF56566E748CDE8BC65C1FB" >
<img src="qr-stable.svg" width="300" height="300"/> <img src="qr-stable.svg" width="300" height="300"/>
</a> </a>
{:/}
### If the QR-Code doesn't work: ### If the QR-Code doesn't work:
@ -22,11 +24,11 @@ If this still isn't working follow the next steps:
2. Go to the `Settings` Tab in the Bottom bar 2. Go to the `Settings` Tab in the Bottom bar
3. Click the `Repositories` Action 3. Click the `Repositories` Action
4. Click on the plus sign at the top. 4. Click on the plus sign at the top.
5. Fill in `https://fluffychat.im/repo/stable/repo/` into the top field and `5EDB5C4395B2F2D9BA682F6A1D275170CCE5365A6FA27D2220EA8D52A6D95F07` in the bottom field. 5. Fill in `https://fluffychat.im/repo/stable/repo/` into the top field and `8E2637AEF6697CC6DD486AF044A6EE45B1A742AE3EF56566E748CDE8BC65C1FB` in the bottom field.
## What is the fingerprint? ## What is the fingerprint?
The fingerprint of the Repository is: `5EDB5C4395B2F2D9BA682F6A1D275170CCE5365A6FA27D2220EA8D52A6D95F07` The fingerprint of the Repository is: `8E2637AEF6697CC6DD486AF044A6EE45B1A742AE3EF56566E748CDE8BC65C1FB`
# Nightly Repository # Nightly Repository
@ -34,9 +36,11 @@ The fingerprint of the Repository is: `5EDB5C4395B2F2D9BA682F6A1D275170CCE5365A6
Easiest way to add the Repository is to either **scan the QR-Code** or if you are on your phone **directly click** it. Easiest way to add the Repository is to either **scan the QR-Code** or if you are on your phone **directly click** it.
{::nomarkdown}
<a href="fdroidrepos://fluffychat.im/repo/nightly/repo/?fingerprint=21A469657300576478B623DF99D8EB889A80BCD939ACA60A4074741BEAEC397D" > <a href="fdroidrepos://fluffychat.im/repo/nightly/repo/?fingerprint=21A469657300576478B623DF99D8EB889A80BCD939ACA60A4074741BEAEC397D" >
<img src="qr-nightly.svg" width="300" height="300"/> <img src="qr-nightly.svg" width="300" height="300"/>
</a> </a>
{:/}
### If the QR-Code doesn't work: ### If the QR-Code doesn't work:

View File

@ -1,119 +1,565 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<html>
<head> <head>
<meta charset="utf-8"> <meta charset="UTF-8">
<title>FluffyChat Official Website</title> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="identifier-url" content="https://fluffychat.im" /> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="title" content="FluffyChat Official Website" /> <title>FluffyChat - Official Website</title>
<meta name="description" content="The cutest messenger in the Matrix network" /> <meta name="description" content="A cute and secure chatclient for the matrix protocol">
<meta name="abstract" content="FluffyChat is the cutest messenger in the Matrix network" /> <meta name="keywords"
<meta name="keywords" content="FluffyChat, Matrix, Flutter, App" /> content="Fluffychat, Matrix, Web, Android, iOS, Desktop, Chat, Client, Chatclient, Matrix.org, Secure, E2EE, End to End, Encryption, End to End Encryption, F-Droid, Foss, FOSS, OpenSource, Free, Community, Open">
<meta name="author" content="Krille Fear" /> <script type="application/ld+json">
<meta name="revisit-after" content="15" /> {
<meta name="language" content="EN" /> "@context": "https://schema.org",
<meta name="robots" content="All" /> "@type": "MobileApplication",
<meta name="viewport" content="width=device-width, initial-scale=1"> "name": "Fluffychat",
<link rel="icon" type="image/x-icon" href="favicon.png"> "applicationCategory": "CommunicationApplication",
<link href="tailwind.css" rel="stylesheet"> "countriesNotSupported": "fr",
"operatingSystem": "ANDROID",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"ratingCount": "133"
},
"installUrl": "https://play.google.com/store/apps/details?id=chat.fluffy.fluffychat"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MobileApplication",
"name": "Fluffychat",
"applicationCategory": "CommunicationApplication",
"countriesNotSupported": "fr",
"operatingSystem": "ANDROID",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"ratingCount": "133"
},
"installUrl": "https://f-droid.org/de/packages/chat.fluffy.fluffychat/"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MobileApplication",
"name": "Fluffychat",
"applicationCategory": "CommunicationApplication",
"countriesNotSupported": "fr",
"operatingSystem": "IOS",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.4",
"ratingCount": "28"
},
"installUrl": "https://apps.apple.com/app/fluffychat/id1551469600"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Fluffychat",
"applicationCategory": "CommunicationApplication",
"countriesNotSupported": "fr",
"operatingSystem": "WEB",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"ratingCount": "133"
},
"url": "https://fluffychat.im/web",
"downloadUrl": "https://fluffychat.im/web",
"installUrl": "https://fluffychat.im/web"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Fluffychat",
"applicationCategory": "CommunicationApplication",
"countriesNotSupported": "fr",
"operatingSystem": "LINUX",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"ratingCount": "133"
},
"downloadUrl": "https://snapcraft.io/fluffychat",
"installUrl": "https://snapcraft.io/fluffychat"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Fluffychat",
"applicationCategory": "CommunicationApplication",
"countriesNotSupported": "fr",
"operatingSystem": "LINUX",
"releaseNotes": "https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md",
"screenshot": "https://gitlab.com/famedly/fluffychat/-/raw/main/docs/screenshots/mobile.png",
"softwareHelp": "https://gitlab.com/famedly/fluffychat/-/wikis/FAQ",
"author": {
"@type": "Person",
"callSign": "KrilleFear"
},
"license": "https://gitlab.com/famedly/fluffychat/-/blob/main/LICENSE",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"ratingCount": "133"
},
"downloadUrl": "https://flathub.org/apps/details/im.fluffychat.Fluffychat",
"installUrl": "https://flathub.org/apps/details/im.fluffychat.Fluffychat"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "FluffyChat - Official Website",
"url": "https://fluffychat.im",
"description": "A cute and secure chatclient for the matrix protocol",
"thumbnailUrl": "https://fluffychat.im/favicon.png",
"inLanguage": "de-de"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"description": "Breadcrumbs list",
"name": "Breadcrumbs",
"itemListElement": [
{
"@type": "ListItem",
"item": {
"@id": "https://fluffychat.im",
"name": "Homepage"
},
"position": 1
}
]
}
</script>
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" href="tailwind.css">
<!-- Animation CSS-->
<style> <style>
@font-face { @font-face {
font-family: Zen Kurenaido; font-family: Zen Kurenaido;
src: url(ZenKurenaido-Regular.ttf); src: url(ZenKurenaido-Regular.ttf);
} }
/* ----------------------------------------------
* Generated by Animista
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */
.slide-in-bottom {
-webkit-animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) both;
animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) both
}
.slide-in-bottom-h1 {
-webkit-animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) .5s both;
animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) .5s both
}
.slide-in-bottom-subtitle {
-webkit-animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) .75s both;
animation: slide-in-bottom .5s cubic-bezier(.25, .46, .45, .94) .75s both
}
.fade-in {
-webkit-animation: fade-in 1.2s cubic-bezier(.39, .575, .565, 1.000) 1s both;
animation: fade-in 1.2s cubic-bezier(.39, .575, .565, 1.000) 1s both
}
.bounce-top-icons {
-webkit-animation: bounce-top .9s 1s both;
animation: bounce-top .9s 1s both
}
@-webkit-keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(1000px);
transform: translateY(1000px);
opacity: 0
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1
}
}
@keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(1000px);
transform: translateY(1000px);
opacity: 0
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1
}
}
@-webkit-keyframes bounce-top {
0% {
-webkit-transform: translateY(-45px);
transform: translateY(-45px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
opacity: 1
}
24% {
opacity: 1
}
40% {
-webkit-transform: translateY(-24px);
transform: translateY(-24px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
65% {
-webkit-transform: translateY(-12px);
transform: translateY(-12px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
82% {
-webkit-transform: translateY(-6px);
transform: translateY(-6px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
93% {
-webkit-transform: translateY(-4px);
transform: translateY(-4px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
25%,
55%,
75%,
87% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
opacity: 1
}
}
@keyframes bounce-top {
0% {
-webkit-transform: translateY(-45px);
transform: translateY(-45px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
opacity: 1
}
24% {
opacity: 1
}
40% {
-webkit-transform: translateY(-24px);
transform: translateY(-24px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
65% {
-webkit-transform: translateY(-12px);
transform: translateY(-12px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
82% {
-webkit-transform: translateY(-6px);
transform: translateY(-6px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
93% {
-webkit-transform: translateY(-4px);
transform: translateY(-4px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in
}
25%,
55%,
75%,
87% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
opacity: 1
}
}
@-webkit-keyframes fade-in {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@keyframes fade-in {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
</style> </style>
</head> </head>
<body
class="flex flex-col items-center justify-center min-h-screen w-screen bg-gradient-to-t from-purple-200 to-blue-50 dark:from-gray-800 dark:to-slate-900 p-4"
style="font-family: 'Zen Kurenaido', sans-serif;">
<img src="favicon.png" class="h-10" />
<h1 class="flex text-4xl items-center mb-4">
<span style="color: #5625BA">Fluffy</span>
<span style="color: #41a2bc">Chat</span>
</h1>
<img src="screenshots/screenshots.png" class="sm:max-w-lg max-w-screen mb-8" />
<div class="max-w-lg mb-8 flex justify-center flex-wrap"> <body class="leading-normal tracking-normal text-gray-900" style="font-family: 'Zen Kurenaido', sans-serif;">
<a href="https://apps.apple.com/app/fluffychat/id1551469600"><img src="appstore-badge.png"
class="w-36 pr-2 mb-2 inline hover:scale-105 transition-transform"></a>
<a href="https://play.google.com/store/apps/details?id=chat.fluffy.fluffychat"><img src="google-play-badge.png"
class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"> <div class="h-screen pb-14 bg-right bg-cover" style="background-image:url('bg.svg');">
</a><a href="https://f-droid.org/packages/chat.fluffy.fluffychat/"><img src="fdroid_button.png" <!--Nav-->
class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"> <div class="w-full container mx-auto p-6">
</a>
<a href="https://fluffychat.im/web"> <div class="w-full flex items-center justify-between">
<img src="browser-badge.png" class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"></a> <a class="flex items-center no-underline hover:no-underline font-bold text-2xl lg:text-4xl" href="#">
<a href="https://snapcraft.io/fluffychat"><img <img src="favicon.png" class="h-8 fill-current text-indigo-600 pr-2" /> <span
src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" style="color: #5625BA">Fluffy</span><span style="color: #41a2bc">Chat</span>
class="w-36 pr-2 mb-2 hover:scale-105 transition-transform inline"></a> </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> <div class="flex w-1/2 justify-end content-center">
</div> <a class="inline-block text-blue-300 no-underline hover:text-indigo-800 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4"
href="https://matrix.to/#/#fluffychat:matrix.org">
<svg class="fill-current h-6" enable-background="new -91 49.217 56.693 56.693" id="Layer_1"
version="1.1" viewBox="-91 49.217 56.693 56.693" xml:space="preserve"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M-38.3289,79.8244c-0.7526-2.2362-3.1756-3.4388-5.4117-2.6861l-4.5351,1.5264l-3.0737-9.1321l4.4169-1.4866 c2.2362-0.7526,3.4388-3.1756,2.6861-5.4117c-0.7526-2.2362-3.1756-3.4388-5.4117-2.6861l-4.4168,1.4866l-1.4877-4.4201 c-0.7527-2.2362-3.1756-3.4388-5.4117-2.6861v0c-2.2362,0.7526-3.4388,3.1756-2.6861,5.4117l1.4877,4.4201l-9.3246,3.1385 l-1.4697-4.3666c-0.7527-2.2362-3.1756-3.4388-5.4117-2.6861c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117l1.4697,4.3666 l-4.445,1.4961c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117v0c0.7526,2.2362,3.1756,3.4388,5.4117,2.6861l4.445-1.4961 l3.0737,9.1321l-4.3268,1.4563c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117c0.7526,2.2362,3.1756,3.4388,5.4117,2.6861 l4.3268-1.4563l1.5778,4.6877c0.7527,2.2362,3.1756,3.4388,5.4117,2.6861c2.2362-0.7527,3.4388-3.1756,2.6861-5.4117l-1.5778-4.6877 l9.3246-3.1385l1.5598,4.6342c0.7527,2.2362,3.1756,3.4388,5.4117,2.6861c2.2362-0.7527,3.4388-3.1756,2.6861-5.4117l-1.5598-4.6342 l4.5351-1.5264C-38.7789,84.4835-37.5762,82.0606-38.3289,79.8244z M-65.6982,84.5288l-3.0737-9.1321l9.3246-3.1385l3.0737,9.1321 L-65.6982,84.5288z" />
</svg>
</a>
<a class="inline-block text-blue-300 no-underline hover:text-indigo-800 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4"
href="https://twitter.com/KrilleFear">
<svg class="fill-current h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path
d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z">
</path>
</svg>
</a>
<a class="inline-block text-blue-300 no-underline hover:text-indigo-800 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4"
href="https://metalhead.club/@krille">
<svg class="fill-current h-6" viewBox="0 0 1000 1000" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<clipPath id="_clip1">
<rect x="33.6" y="-0.035" width="932.844" height="1000" />
</clipPath>
<g clip-path="url(#_clip1)">
<path
d="M946.586,599.455c-13.713,70.541 -122.816,147.742 -248.121,162.703c-65.341,7.796 -129.674,14.962 -198.275,11.815c-112.191,-5.139 -200.716,-26.776 -200.716,-26.776c0,10.92 0.673,21.319 2.02,31.044c14.586,110.711 109.787,117.344 199.967,120.436c91.021,3.114 172.068,-22.44 172.068,-22.44l3.74,82.281c0,0 -63.666,34.185 -177.079,40.473c-62.539,3.437 -140.192,-1.573 -230.636,-25.511c-196.158,-51.916 -229.893,-260.996 -235.055,-473.143c-1.573,-62.987 -0.603,-122.381 -0.603,-172.056c0,-216.931 142.142,-280.516 142.142,-280.516c71.672,-32.914 194.655,-46.755 322.508,-47.8l3.142,0c127.853,1.045 250.917,14.886 322.583,47.8c0,0 142.138,63.585 142.138,280.516c0,0 1.783,160.053 -19.823,271.174"
style="fill-rule:nonzero;" />
<path
d="M798.748,345.11l0,262.667l-104.07,0l0,-254.946c0,-53.743 -22.614,-81.021 -67.847,-81.021c-50.012,0 -75.077,32.359 -75.077,96.343l0,139.547l-103.457,0l0,-139.547c0,-63.984 -25.07,-96.343 -75.082,-96.343c-45.233,0 -67.847,27.278 -67.847,81.021l0,254.946l-104.07,0l0,-262.667c0,-53.683 13.669,-96.343 41.127,-127.904c28.314,-31.561 65.395,-47.741 111.425,-47.741c53.256,0 93.585,20.468 120.251,61.41l25.922,43.451l25.927,-43.451c26.66,-40.942 66.99,-61.41 120.251,-61.41c46.025,0 83.106,16.18 111.425,47.741c27.453,31.561 41.122,74.221 41.122,127.904"
style="fill:#fff;fill-rule:nonzero;" />
</g>
</svg>
</a>
<a class="inline-block text-blue-300 no-underline hover:text-indigo-800 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4"
href="https://ko-fi.com/krille">
<img class="w-10 hover:animate-bounce" src="Kofi_pixel_logo.png"/>
</a>
</div>
</div>
</div>
<!--Main-->
<div class="container pt-8 px-6 mx-auto flex flex-wrap flex-col md:flex-row items-center">
<!--Left Col-->
<div class="flex flex-col w-full xl:w-2/5 justify-center lg:items-start overflow-y-hidden">
<h1
class="my-4 text-3xl md:text-5xl text-purple-800 font-bold leading-tight text-center md:text-left slide-in-bottom-h1">
Open. Nonprofit. Cute.</h1>
<p class="leading-normal text-base md:text-2xl mb-8 text-center md:text-left slide-in-bottom-subtitle">
Easy to use (<a class="underline hover:text-blue-700 transition-all"
href="https://matrix.org">matrix</a>) messenger. Secure and decentralized.</p>
<p class="text-blue-700 font-bold pb-4 text-center md:text-left fade-in">Mobile app:</p>
<div class="w-full flex justify-center md:justify-start pb-24 lg:pb-0 fade-in">
<a href="https://apps.apple.com/app/fluffychat/id1551469600"><img src="appstore-badge.png"
class="max-h-12 pr-2 mb-2 bounce-top-icons inline"></a>
<a href="https://play.google.com/store/apps/details?id=chat.fluffy.fluffychat"><img
src="google-play-badge.png" class="max-h-12 pr-2 mb-2 bounce-top-icons inline">
</a><a href="https://f-droid.org/de/packages/chat.fluffy.fluffychat/"><img src="fdroid_button.png"
class="max-h-12 pr-2 mb-2 bounce-top-icons inline">
</a>
</div>
<p class="text-blue-700 font-bold py-4 text-center md:text-left fade-in">Desktop app:</p>
<div class="w-full flex justify-center md:justify-start pb-24 lg:pb-0 fade-in">
<a href="https://fluffychat.im/web">
<img src="browser-badge.png" class="max-h-12 pr-2 mb-2 bounce-top-icons inline"></a>
<a href="https://snapcraft.io/fluffychat"><img
src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg"
class="max-h-12 pr-2 mb-2 bounce-top-icons inline"></a>
<a href="https://flathub.org/apps/details/im.fluffychat.Fluffychat"><img src="flathub-badge-en.png"
class="max-h-12 pr-2 mb-2 bounce-top-icons inline"></a>
</div>
</div>
<!--Right Col-->
<div class="w-full xl:w-3/5 py-6 relative">
<img class="w-full mx-auto slide-in-bottom" src="screenshots/screenshots.png">
</div>
<!--Footer-->
<div class="w-full pt-16 pb-6 text-sm text-center md:text-left fade-in">
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat">Source code</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/PRIVACY.md">Privacy</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md">Changelog</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://hosted.weblate.org/projects/fluffychat/">Translations</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/docs/fdroid_repo.md">FluffyChat F-Droid repository</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://liberapay.com/KrilleChritzelius/donate">Donate</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800"
href="https://keys.mailvelope.com/pks/lookup?op=get&search=christian-pauly%40posteo.de">Contact</a>
-
<a class="text-gray-500 no-underline hover:text-purple-800" href="https://krillefear.gitlab.io">Created
by Krille Fear</a>
</div>
</div>
<div class="flex mb-8 justify-center content-center">
<a rel="me"
class="inline-block text-indigo-500 no-underline hover:text-indigo-900 hover:scale-105 transition-all text-center h-auto p-4"
rel="me" href="https://mastodon.art/@krille">
<svg class="fill-current h-6" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<clipPath id="_clip1">
<rect x="33.6" y="-0.035" width="932.844" height="1000" />
</clipPath>
<g clip-path="url(#_clip1)">
<path
d="M946.586,599.455c-13.713,70.541 -122.816,147.742 -248.121,162.703c-65.341,7.796 -129.674,14.962 -198.275,11.815c-112.191,-5.139 -200.716,-26.776 -200.716,-26.776c0,10.92 0.673,21.319 2.02,31.044c14.586,110.711 109.787,117.344 199.967,120.436c91.021,3.114 172.068,-22.44 172.068,-22.44l3.74,82.281c0,0 -63.666,34.185 -177.079,40.473c-62.539,3.437 -140.192,-1.573 -230.636,-25.511c-196.158,-51.916 -229.893,-260.996 -235.055,-473.143c-1.573,-62.987 -0.603,-122.381 -0.603,-172.056c0,-216.931 142.142,-280.516 142.142,-280.516c71.672,-32.914 194.655,-46.755 322.508,-47.8l3.142,0c127.853,1.045 250.917,14.886 322.583,47.8c0,0 142.138,63.585 142.138,280.516c0,0 1.783,160.053 -19.823,271.174"
style="fill-rule:nonzero;" />
<path
d="M798.748,345.11l0,262.667l-104.07,0l0,-254.946c0,-53.743 -22.614,-81.021 -67.847,-81.021c-50.012,0 -75.077,32.359 -75.077,96.343l0,139.547l-103.457,0l0,-139.547c0,-63.984 -25.07,-96.343 -75.082,-96.343c-45.233,0 -67.847,27.278 -67.847,81.021l0,254.946l-104.07,0l0,-262.667c0,-53.683 13.669,-96.343 41.127,-127.904c28.314,-31.561 65.395,-47.741 111.425,-47.741c53.256,0 93.585,20.468 120.251,61.41l25.922,43.451l25.927,-43.451c26.66,-40.942 66.99,-61.41 120.251,-61.41c46.025,0 83.106,16.18 111.425,47.741c27.453,31.561 41.122,74.221 41.122,127.904"
style="fill:#fff;fill-rule:nonzero;" />
</g>
</svg>
</a>
<a class="inline-block text-indigo-500 no-underline hover:text-indigo-900 hover:scale-105 transition-all text-center h-auto p-4"
href="https://matrix.to/#/#fluffychat:matrix.org">
<svg class="fill-current h-6" enable-background="new -91 49.217 56.693 56.693" id="Layer_1" version="1.1"
viewBox="-91 49.217 56.693 56.693" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M-38.3289,79.8244c-0.7526-2.2362-3.1756-3.4388-5.4117-2.6861l-4.5351,1.5264l-3.0737-9.1321l4.4169-1.4866 c2.2362-0.7526,3.4388-3.1756,2.6861-5.4117c-0.7526-2.2362-3.1756-3.4388-5.4117-2.6861l-4.4168,1.4866l-1.4877-4.4201 c-0.7527-2.2362-3.1756-3.4388-5.4117-2.6861v0c-2.2362,0.7526-3.4388,3.1756-2.6861,5.4117l1.4877,4.4201l-9.3246,3.1385 l-1.4697-4.3666c-0.7527-2.2362-3.1756-3.4388-5.4117-2.6861c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117l1.4697,4.3666 l-4.445,1.4961c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117v0c0.7526,2.2362,3.1756,3.4388,5.4117,2.6861l4.445-1.4961 l3.0737,9.1321l-4.3268,1.4563c-2.2362,0.7527-3.4388,3.1756-2.6861,5.4117c0.7526,2.2362,3.1756,3.4388,5.4117,2.6861 l4.3268-1.4563l1.5778,4.6877c0.7527,2.2362,3.1756,3.4388,5.4117,2.6861c2.2362-0.7527,3.4388-3.1756,2.6861-5.4117l-1.5778-4.6877 l9.3246-3.1385l1.5598,4.6342c0.7527,2.2362,3.1756,3.4388,5.4117,2.6861c2.2362-0.7527,3.4388-3.1756,2.6861-5.4117l-1.5598-4.6342 l4.5351-1.5264C-38.7789,84.4835-37.5762,82.0606-38.3289,79.8244z M-65.6982,84.5288l-3.0737-9.1321l9.3246-3.1385l3.0737,9.1321 L-65.6982,84.5288z" />
</svg>
</a>
<a class="inline-block no-underline hover:scale-105 transition-all text-center h-auto p-4"
href="https://ko-fi.com/krille">
<img src="kofi_button_dark.png" class="h-6 fill-current" />
</a>
</div>
<!--Footer-->
<div class="w-full text-sm text-center max-w-lg">
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat">Source
code</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/PRIVACY.md">Privacy</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/CHANGELOG.md">Changelog</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://hosted.weblate.org/projects/fluffychat/">Translations</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://gitlab.com/famedly/fluffychat/-/blob/main/docs/fdroid_repo.md">FluffyChat F-Droid
repository</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://keys.mailvelope.com/pks/lookup?op=get&search=christian-pauly%40posteo.de">Contact</a>
-
<a class="text-slate-700 dark:text-slate-200 no-underline hover:text-purple-800"
href="https://krillefear.gitlab.io">Created
by Krille Fear</a>
</div> </div>
</body> </body>
</html> </html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,193 +1,49 @@
import 'package:fluffychat/config/setting_keys.dart'; import 'dart:developer';
import 'package:fluffychat/pages/chat/chat_view.dart';
import 'package:fluffychat/pages/chat_list/chat_list_body.dart';
import 'package:fluffychat/pages/chat_list/search_title.dart';
import 'package:fluffychat/pages/invitation_selection/invitation_selection_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:fluffychat/main.dart' as app; import 'package:fluffychat/main.dart' as app;
import 'package:shared_preferences/shared_preferences.dart';
import 'extensions/default_flows.dart';
import 'extensions/wait_for.dart';
import 'users.dart'; import 'users.dart';
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group( group('Integration Test', () {
'Integration Test', testWidgets('Test if the app starts', (WidgetTester tester) async {
() { app.main();
setUpAll( await tester.pumpAndSettle();
() async {
// this random dialog popping up is super hard to cover in tests
SharedPreferences.setMockInitialValues({
SettingKeys.showNoGoogle: false,
});
try {
Hive.deleteFromDisk();
Hive.initFlutter();
} catch (_) {}
},
);
testWidgets( await Future.delayed(const Duration(seconds: 10));
'Start app, login and logout',
(WidgetTester tester) async {
app.main();
await tester.ensureAppStartedHomescreen();
await tester.ensureLoggedOut();
},
);
testWidgets( await tester.pumpAndSettle();
'Login again',
(WidgetTester tester) async {
app.main();
await tester.ensureAppStartedHomescreen();
},
);
testWidgets( expect(find.text('Connect'), findsOneWidget);
'Start chat and send message',
(WidgetTester tester) async {
app.main();
await tester.ensureAppStartedHomescreen();
await tester.waitFor(find.byType(TextField));
await tester.enterText(find.byType(TextField), Users.user2.name);
await tester.pumpAndSettle();
await tester.scrollUntilVisible( final input = find.byType(TextField);
find.text('Chats').first,
500,
scrollable: find
.descendant(
of: find.byType(ChatListViewBody),
matching: find.byType(Scrollable),
)
.first,
);
await tester.pumpAndSettle();
await tester.tap(find.text('Chats'));
await tester.pumpAndSettle();
await tester.waitFor(find.byType(SearchTitle));
await tester.pumpAndSettle();
await tester.scrollUntilVisible( expect(input, findsOneWidget);
find.text(Users.user2.name).first,
500,
scrollable: find
.descendant(
of: find.byType(ChatListViewBody),
matching: find.byType(Scrollable),
)
.first,
);
await tester.pumpAndSettle();
await tester.tap(find.text(Users.user2.name).first);
try { await tester.enterText(input, homeserver);
await tester.waitFor( await tester.testTextInput.receiveAction(TextInputAction.done);
find.byType(ChatView), await tester.pumpAndSettle();
timeout: const Duration(seconds: 5),
);
} catch (_) {
// in case the homeserver sends the username as search result
if (find.byIcon(Icons.send_outlined).evaluate().isNotEmpty) {
await tester.tap(find.byIcon(Icons.send_outlined));
await tester.pumpAndSettle();
}
}
await tester.waitFor(find.byType(ChatView)); // in case registration is allowed
await tester.enterText(find.byType(TextField).last, 'Test'); try {
await tester.pumpAndSettle(); await tester.tap(find.text('Login'));
try {
await tester.waitFor(find.byIcon(Icons.send_outlined));
await tester.tap(find.byIcon(Icons.send_outlined));
} catch (_) {
await tester.testTextInput.receiveAction(TextInputAction.done);
}
await tester.pumpAndSettle();
await tester.waitFor(find.text('Test'));
await tester.pumpAndSettle();
},
);
testWidgets('Spaces', (tester) async {
app.main();
await tester.ensureAppStartedHomescreen();
await tester.waitFor(find.byTooltip('Show menu'));
await tester.tap(find.byTooltip('Show menu'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
} catch (e) {
log('Registration is not allowed. Proceeding with login...');
}
await tester.pumpAndSettle();
await tester.waitFor(find.byIcon(Icons.workspaces_outlined)); final inputs = find.byType(TextField);
await tester.tap(find.byIcon(Icons.workspaces_outlined));
await tester.pumpAndSettle();
await tester.waitFor(find.byType(TextField)); await tester.enterText(inputs.first, Users.user1.name);
await tester.enterText(find.byType(TextField).last, 'Test Space'); await tester.enterText(inputs.last, Users.user1.password);
await tester.pumpAndSettle(); await tester.testTextInput.receiveAction(TextInputAction.done);
});
await tester.testTextInput.receiveAction(TextInputAction.done); });
await tester.pumpAndSettle();
await tester.waitFor(find.text('Invite contact'));
await tester.tap(find.text('Invite contact'));
await tester.pumpAndSettle();
await tester.waitFor(
find.descendant(
of: find.byType(InvitationSelectionView),
matching: find.byType(TextField),
),
);
await tester.enterText(
find.descendant(
of: find.byType(InvitationSelectionView),
matching: find.byType(TextField),
),
Users.user2.name,
);
await Future.delayed(const Duration(milliseconds: 250));
await tester.testTextInput.receiveAction(TextInputAction.done);
await Future.delayed(const Duration(milliseconds: 1000));
await tester.pumpAndSettle();
await tester.tap(
find
.descendant(
of: find.descendant(
of: find.byType(InvitationSelectionView),
matching: find.byType(ListTile),
),
matching: find.text(Users.user2.name),
)
.last,
);
await tester.pumpAndSettle();
await tester.waitFor(find.maybeUppercaseText('Yes'));
await tester.tap(find.maybeUppercaseText('Yes'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
await tester.waitFor(find.text('Load 2 more participants'));
await tester.tap(find.text('Load 2 more participants'));
await tester.pumpAndSettle();
expect(find.text(Users.user2.name), findsOneWidget);
});
},
);
} }

View File

@ -1,171 +0,0 @@
import 'dart:developer';
import 'package:fluffychat/pages/chat_list/chat_list_body.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../users.dart';
import 'wait_for.dart';
extension DefaultFlowExtensions on WidgetTester {
Future<void> login() async {
final tester = this;
await tester.pumpAndSettle();
await tester.waitFor(find.text('Let\'s start'));
expect(find.text('Let\'s start'), findsOneWidget);
final input = find.byType(TextField);
expect(input, findsOneWidget);
// getting the placeholder in place
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
await tester.enterText(input, homeserver);
await tester.pumpAndSettle();
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
// in case registration is allowed
// try {
await Future.delayed(const Duration(milliseconds: 50));
await tester.scrollUntilVisible(
find.text('Login'),
500,
scrollable: find.descendant(
of: find.byKey(const Key('ConnectPageListView')),
matching: find.byType(Scrollable).first,
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
/*} catch (e) {
log('Registration is not allowed. Proceeding with login...');
}*/
await tester.pumpAndSettle();
await Future.delayed(const Duration(milliseconds: 50));
final inputs = find.byType(TextField);
await tester.enterText(inputs.first, Users.user1.name);
await tester.enterText(inputs.last, Users.user1.password);
await tester.pumpAndSettle();
await tester.testTextInput.receiveAction(TextInputAction.done);
try {
// pumpAndSettle does not work in here as setState is called
// asynchronously
await tester.waitFor(
find.byType(LinearProgressIndicator),
timeout: const Duration(milliseconds: 1500),
skipPumpAndSettle: true,
);
} catch (_) {
// in case the input action does not work on the desired platform
if (find.text('Login').evaluate().isNotEmpty) {
await tester.tap(find.text('Login'));
}
}
try {
await tester.pumpAndSettle();
} catch (_) {
// may fail because of ongoing animation below dialog
}
await tester.waitFor(
find.byType(ChatListViewBody),
skipPumpAndSettle: true,
);
}
/// ensure PushProvider check passes
Future<void> acceptPushWarning() async {
final tester = this;
final matcher = find.maybeUppercaseText('Do not show again');
try {
await tester.waitFor(matcher, timeout: const Duration(seconds: 5));
// the FCM push error dialog to be handled...
await tester.tap(matcher);
await tester.pumpAndSettle();
} catch (_) {}
}
Future<void> ensureLoggedOut() async {
final tester = this;
await tester.pumpAndSettle();
if (find.byType(ChatListViewBody).evaluate().isNotEmpty) {
await tester.tap(find.byTooltip('Show menu'));
await tester.pumpAndSettle();
await tester.tap(find.text('Settings'));
await tester.pumpAndSettle();
await tester.scrollUntilVisible(
find.text('Account'),
500,
scrollable: find.descendant(
of: find.byKey(const Key('SettingsListViewContent')),
matching: find.byType(Scrollable),
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Logout'));
await tester.pumpAndSettle();
await tester.tap(find.maybeUppercaseText('Yes'));
await tester.pumpAndSettle();
}
}
Future<void> ensureAppStartedHomescreen({
Duration timeout = const Duration(seconds: 20),
}) async {
final tester = this;
await tester.pumpAndSettle();
final homeserverPickerFinder = find.byType(HomeserverPicker);
final chatListFinder = find.byType(ChatListViewBody);
final end = DateTime.now().add(timeout);
log(
'Waiting for HomeserverPicker or ChatListViewBody...',
name: 'Test Runner',
);
do {
if (DateTime.now().isAfter(end)) {
throw Exception(
'Timed out waiting for HomeserverPicker or ChatListViewBody',
);
}
await pumpAndSettle();
await Future.delayed(const Duration(milliseconds: 100));
} while (homeserverPickerFinder.evaluate().isEmpty &&
chatListFinder.evaluate().isEmpty);
if (homeserverPickerFinder.evaluate().isNotEmpty) {
log(
'Found HomeserverPicker, performing login.',
name: 'Test Runner',
);
await tester.login();
} else {
log(
'Found ChatListViewBody, skipping login.',
name: 'Test Runner',
);
}
await tester.acceptPushWarning();
}
}

View File

@ -1,49 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
/// Workaround for https://github.com/flutter/flutter/issues/88765
extension WaitForExtension on WidgetTester {
Future<void> waitFor(
Finder finder, {
Duration timeout = const Duration(seconds: 20),
bool skipPumpAndSettle = false,
}) async {
final end = DateTime.now().add(timeout);
do {
if (DateTime.now().isAfter(end)) {
throw Exception('Timed out waiting for $finder');
}
if (!skipPumpAndSettle) {
await pumpAndSettle();
}
await Future.delayed(const Duration(milliseconds: 100));
} while (finder.evaluate().isEmpty);
}
}
extension MaybeUppercaseFinder on CommonFinders {
/// On Android some button labels are in uppercase while on iOS they
/// are not. This method tries both.
Finder maybeUppercaseText(
String text, {
bool findRichText = false,
bool skipOffstage = true,
}) {
try {
final finder = find.text(
text.toUpperCase(),
findRichText: findRichText,
skipOffstage: skipOffstage,
);
expect(finder, findsOneWidget);
return finder;
} catch (_) {
return find.text(
text,
findRichText: findRichText,
skipOffstage: skipOffstage,
);
}
}
}

View File

@ -1,25 +1,15 @@
import 'dart:io';
abstract class Users { abstract class Users {
const Users._(); const Users._();
static const user1 = User( static final user1 = User(
String.fromEnvironment( Platform.environment['USER1_NAME'] ?? 'alice',
'USER1_NAME', Platform.environment['USER1_PW'] ?? 'AliceInWonderland',
defaultValue: 'alice',
),
String.fromEnvironment(
'USER1_PW',
defaultValue: 'AliceInWonderland',
),
); );
static const user2 = User( static final user2 = User(
String.fromEnvironment( Platform.environment['USER2_NAME'] ?? 'bob',
'USER2_NAME', Platform.environment['USER2_PW'] ?? 'JoWirSchaffenDas',
defaultValue: 'bob',
),
String.fromEnvironment(
'USER2_PW',
defaultValue: 'JoWirSchaffenDas',
),
); );
} }
@ -30,7 +20,5 @@ class User {
const User(this.name, this.password); const User(this.name, this.password);
} }
const homeserver = 'http://${const String.fromEnvironment( final homeserver =
'HOMESERVER', 'http://${Platform.environment['HOMESERVER'] ?? 'localhost'}';
defaultValue: 'localhost',
)}';

View File

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 54; objectVersion = 51;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -286,12 +286,10 @@
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
); );
name = "Thin Binary"; name = "Thin Binary";
outputPaths = ( outputPaths = (
@ -324,7 +322,6 @@
}; };
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );

View File

@ -110,7 +110,5 @@
</dict> </dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

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

View File

@ -16,7 +16,7 @@ abstract class AppConfig {
static const double messageFontSize = 15.75; static const double messageFontSize = 15.75;
static const bool allowOtherHomeservers = true; static const bool allowOtherHomeservers = true;
static const bool enableRegistration = true; static const bool enableRegistration = true;
static const Color primaryColor = Color(0xFF5625BA); static const Color primaryColor = Color.fromARGB(255, 135, 103, 172);
static const Color primaryColorLight = Color(0xFFCCBDEA); static const Color primaryColorLight = Color(0xFFCCBDEA);
static const Color secondaryColor = Color(0xFF41a2bc); static const Color secondaryColor = Color(0xFF41a2bc);
static String _privacyUrl = static String _privacyUrl =
@ -24,8 +24,6 @@ abstract class AppConfig {
static String get privacyUrl => _privacyUrl; static String get privacyUrl => _privacyUrl;
static const String enablePushTutorial = static const String enablePushTutorial =
'https://gitlab.com/famedly/fluffychat/-/wikis/Push-Notifications-without-Google-Services'; 'https://gitlab.com/famedly/fluffychat/-/wikis/Push-Notifications-without-Google-Services';
static const String encryptionTutorial =
'https://gitlab.com/famedly/fluffychat/-/wikis/How-to-use-end-to-end-encryption-in-FluffyChat';
static const String appId = 'im.fluffychat.FluffyChat'; static const String appId = 'im.fluffychat.FluffyChat';
static const String appOpenUrlScheme = 'im.fluffychat'; static const String appOpenUrlScheme = 'im.fluffychat';
static String _webBaseUrl = 'https://fluffychat.im/web'; static String _webBaseUrl = 'https://fluffychat.im/web';
@ -33,11 +31,6 @@ abstract class AppConfig {
static const String sourceCodeUrl = 'https://gitlab.com/famedly/fluffychat'; static const String sourceCodeUrl = 'https://gitlab.com/famedly/fluffychat';
static const String supportUrl = static const String supportUrl =
'https://gitlab.com/famedly/fluffychat/issues'; 'https://gitlab.com/famedly/fluffychat/issues';
static final Uri newIssueUrl = Uri(
scheme: 'https',
host: 'gitlab.com',
path: '/famedly/fluffychat/-/issues/new',
);
static const bool enableSentry = true; static const bool enableSentry = true;
static const String sentryDns = static const String sentryDns =
'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143';
@ -75,9 +68,8 @@ abstract class AppConfig {
colorSchemeSeed = Color(json['chat_color']); colorSchemeSeed = Color(json['chat_color']);
} catch (e) { } catch (e) {
Logs().w( Logs().w(
'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"', 'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"',
e, e);
);
} }
} }
if (json['application_name'] is String) { if (json['application_name'] is String) {

View File

@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat_details/chat_details.dart';
import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart'; import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_permissions_settings/chat_permissions_settings.dart'; import 'package:fluffychat/pages/chat_permissions_settings/chat_permissions_settings.dart';
import 'package:fluffychat/pages/connect/connect_page.dart';
import 'package:fluffychat/pages/device_settings/device_settings.dart'; import 'package:fluffychat/pages/device_settings/device_settings.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart';
import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart'; import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart';
@ -18,6 +19,7 @@ import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart';
import 'package:fluffychat/pages/new_space/new_space.dart'; import 'package:fluffychat/pages/new_space/new_space.dart';
import 'package:fluffychat/pages/settings/settings.dart'; import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart'; import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart';
import 'package:fluffychat/pages/settings_account/settings_account.dart';
import 'package:fluffychat/pages/settings_chat/settings_chat.dart'; import 'package:fluffychat/pages/settings_chat/settings_chat.dart';
import 'package:fluffychat/pages/settings_emotes/settings_emotes.dart'; import 'package:fluffychat/pages/settings_emotes/settings_emotes.dart';
import 'package:fluffychat/pages/settings_ignore_list/settings_ignore_list.dart'; import 'package:fluffychat/pages/settings_ignore_list/settings_ignore_list.dart';
@ -26,6 +28,7 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d
import 'package:fluffychat/pages/settings_security/settings_security.dart'; import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_stories/settings_stories.dart'; import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pages/sign_up/signup.dart';
import 'package:fluffychat/pages/story/story_page.dart'; import 'package:fluffychat/pages/story/story_page.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/loading_view.dart'; import 'package:fluffychat/widgets/layouts/loading_view.dart';
@ -68,25 +71,21 @@ class AppRoutes {
widget: const ChatDetails(), widget: const ChatDetails(),
stackedRoutes: _chatDetailsRoutes, stackedRoutes: _chatDetailsRoutes,
), ),
VWidget( VWidget(path: ':roomid', widget: const Chat(), stackedRoutes: [
path: ':roomid', VWidget(
widget: const ChatPage(), path: 'encryption',
stackedRoutes: [ widget: const ChatEncryptionSettings(),
VWidget( ),
path: 'encryption', VWidget(
widget: const ChatEncryptionSettings(), path: 'invite',
), widget: const InvitationSelection(),
VWidget( ),
path: 'invite', VWidget(
widget: const InvitationSelection(), path: 'details',
), widget: const ChatDetails(),
VWidget( stackedRoutes: _chatDetailsRoutes,
path: 'details', ),
widget: const ChatDetails(), ]),
stackedRoutes: _chatDetailsRoutes,
),
],
),
VWidget( VWidget(
path: '/settings', path: '/settings',
widget: const Settings(), widget: const Settings(),
@ -95,13 +94,6 @@ class AppRoutes {
VWidget( VWidget(
path: '/archive', path: '/archive',
widget: const Archive(), widget: const Archive(),
stackedRoutes: [
VWidget(
path: ':roomid',
widget: const ChatPage(),
buildTransition: _dynamicTransition,
),
],
), ),
VWidget( VWidget(
path: '/newprivatechat', path: '/newprivatechat',
@ -172,14 +164,14 @@ class AppRoutes {
VNester( VNester(
path: ':roomid', path: ':roomid',
widgetBuilder: (child) => SideViewLayout( widgetBuilder: (child) => SideViewLayout(
mainView: const ChatPage(), mainView: const Chat(),
sideView: child, sideView: child,
), ),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
nestedRoutes: [ nestedRoutes: [
VWidget( VWidget(
path: '', path: '',
widget: const ChatPage(), widget: const Chat(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
), ),
VWidget( VWidget(
@ -228,25 +220,13 @@ class AppRoutes {
), ),
], ],
), ),
VNester( VWidget(
path: '/archive', path: '/archive',
widgetBuilder: (child) => TwoColumnLayout( widget: const TwoColumnLayout(
mainView: const Archive(), mainView: Archive(),
sideView: child, sideView: EmptyPage(),
), ),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const EmptyPage(),
buildTransition: _dynamicTransition,
),
VWidget(
path: ':roomid',
widget: const ChatPage(),
buildTransition: _dynamicTransition,
),
],
), ),
], ],
), ),
@ -264,6 +244,22 @@ class AppRoutes {
widget: const Login(), widget: const Login(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
), ),
VWidget(
path: 'connect',
widget: const ConnectPage(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'signup',
widget: const SignupPage(),
buildTransition: _fadeTransition,
),
]),
VWidget( VWidget(
path: 'logs', path: 'logs',
widget: const LogViewer(), widget: const LogViewer(),
@ -330,14 +326,37 @@ class AppRoutes {
], ],
), ),
VWidget( VWidget(
path: 'addaccount', path: 'account',
widget: const HomeserverPicker(), widget: const SettingsAccount(),
buildTransition: _fadeTransition, buildTransition: _dynamicTransition,
stackedRoutes: [ stackedRoutes: [
VWidget( VWidget(
path: 'login', path: 'add',
widget: const Login(), widget: const HomeserverPicker(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'connect',
widget: const ConnectPage(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'signup',
widget: const SignupPage(),
buildTransition: _fadeTransition,
),
]),
],
), ),
], ],
), ),

View File

@ -8,6 +8,7 @@ abstract class SettingKeys {
static const String showDirectChatsInSpaces = static const String showDirectChatsInSpaces =
'chat.fluffy.showDirectChatsInSpaces'; 'chat.fluffy.showDirectChatsInSpaces';
static const String separateChatTypes = 'chat.fluffy.separateChatTypes'; static const String separateChatTypes = 'chat.fluffy.separateChatTypes';
static const String chatColor = 'chat.fluffy.chat_color';
static const String sentry = 'sentry'; static const String sentry = 'sentry';
static const String theme = 'theme'; static const String theme = 'theme';
static const String amoledEnabled = 'amoled_enabled'; static const String amoledEnabled = 'amoled_enabled';

View File

@ -9,10 +9,7 @@ import 'app_config.dart';
abstract class FluffyThemes { abstract class FluffyThemes {
static const double columnWidth = 360.0; static const double columnWidth = 360.0;
static const double navRailWidth = 64.0; static bool isColumnModeByWidth(double width) => width > columnWidth * 2 + 64;
static bool isColumnModeByWidth(double width) =>
width > columnWidth * 2 + navRailWidth;
static bool isColumnMode(BuildContext context) => static bool isColumnMode(BuildContext context) =>
isColumnModeByWidth(MediaQuery.of(context).size.width); isColumnModeByWidth(MediaQuery.of(context).size.width);
@ -26,47 +23,31 @@ abstract class FluffyThemes {
); );
static var fallbackTextTheme = const TextTheme( static var fallbackTextTheme = const TextTheme(
bodyLarge: fallbackTextStyle, bodyText1: fallbackTextStyle,
bodyMedium: fallbackTextStyle, bodyText2: fallbackTextStyle,
labelLarge: fallbackTextStyle, button: fallbackTextStyle,
bodySmall: fallbackTextStyle, caption: fallbackTextStyle,
labelSmall: fallbackTextStyle, overline: fallbackTextStyle,
displayLarge: fallbackTextStyle, headline1: fallbackTextStyle,
displayMedium: fallbackTextStyle, headline2: fallbackTextStyle,
displaySmall: fallbackTextStyle, headline3: fallbackTextStyle,
headlineMedium: fallbackTextStyle, headline4: fallbackTextStyle,
headlineSmall: fallbackTextStyle, headline5: fallbackTextStyle,
titleLarge: fallbackTextStyle, headline6: fallbackTextStyle,
titleMedium: fallbackTextStyle, subtitle1: fallbackTextStyle,
titleSmall: fallbackTextStyle, subtitle2: fallbackTextStyle,
); );
static LinearGradient backgroundGradient( static ThemeData buildTheme(Brightness brightness,
BuildContext context, [ColorScheme? colorScheme]) =>
int alpha,
) {
final colorScheme = Theme.of(context).colorScheme;
return LinearGradient(
begin: Alignment.topCenter,
colors: [
colorScheme.primaryContainer.withAlpha(alpha),
colorScheme.secondaryContainer.withAlpha(alpha),
colorScheme.tertiaryContainer.withAlpha(alpha),
colorScheme.primaryContainer.withAlpha(alpha),
],
);
}
static const Duration animationDuration = Duration(milliseconds: 250);
static const Curve animationCurve = Curves.easeInOut;
static ThemeData buildTheme(Brightness brightness, [Color? seed]) =>
ThemeData( ThemeData(
visualDensity: VisualDensity.standard, visualDensity: VisualDensity.standard,
useMaterial3: true, useMaterial3: true,
brightness: brightness, brightness: brightness,
colorSchemeSeed: seed ?? AppConfig.colorSchemeSeed, colorSchemeSeed: AppConfig.colorSchemeSeed ??
textTheme: PlatformInfos.isDesktop || PlatformInfos.isWeb colorScheme?.primary ??
AppConfig.chatColor,
textTheme: PlatformInfos.isDesktop
? brightness == Brightness.light ? brightness == Brightness.light
? Typography.material2018().black.merge(fallbackTextTheme) ? Typography.material2018().black.merge(fallbackTextTheme)
: Typography.material2018().white.merge(fallbackTextTheme) : Typography.material2018().white.merge(fallbackTextTheme)
@ -77,16 +58,8 @@ abstract class FluffyThemes {
dividerColor: brightness == Brightness.light dividerColor: brightness == Brightness.light
? Colors.blueGrey.shade50 ? Colors.blueGrey.shade50
: Colors.blueGrey.shade900, : Colors.blueGrey.shade900,
popupMenuTheme: PopupMenuThemeData( inputDecorationTheme: const InputDecorationTheme(
shape: RoundedRectangleBorder( border: InputBorder.none,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
),
inputDecorationTheme: InputDecorationTheme(
border: UnderlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
filled: true, filled: true,
), ),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
@ -99,32 +72,10 @@ abstract class FluffyThemes {
statusBarBrightness: brightness, statusBarBrightness: brightness,
), ),
), ),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
),
),
dialogTheme: DialogTheme(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
),
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
textStyle: const TextStyle(fontSize: 16), textStyle: const TextStyle(fontSize: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
), ),
), ),
); );

View File

@ -3,8 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:file_picker/file_picker.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
@ -13,12 +12,12 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/add_story/add_story_view.dart'; import 'package:fluffychat/pages/add_story/add_story_view.dart';
import 'package:fluffychat/pages/add_story/invite_story_page.dart'; import 'package:fluffychat/pages/add_story/invite_story_page.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
import 'package:fluffychat/utils/resize_image.dart'; import 'package:fluffychat/utils/resize_image.dart';
import 'package:fluffychat/utils/story_theme_data.dart'; import 'package:fluffychat/utils/story_theme_data.dart';
import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/matrix_sdk_extensions/client_stories_extension.dart'; import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
class AddStoryPage extends StatefulWidget { class AddStoryPage extends StatefulWidget {
const AddStoryPage({Key? key}) : super(key: key); const AddStoryPage({Key? key}) : super(key: key);
@ -69,15 +68,14 @@ class AddStoryController extends State<AddStoryPage> {
} }
void importMedia() async { void importMedia() async {
final picked = await FilePicker.platform.pickFiles( final picked = await FilePickerCross.importFromStorage(
type: FileType.image, type: FileTypeCross.image,
withData: true,
); );
final file = picked?.files.firstOrNull; final fileName = picked.fileName;
if (file == null) return; if (fileName == null) return;
final matrixFile = MatrixImageFile( final matrixFile = MatrixImageFile(
bytes: file.bytes!, bytes: picked.toUint8List(),
name: file.name, name: fileName,
); );
setState(() { setState(() {
image = matrixFile; image = matrixFile;
@ -90,15 +88,14 @@ class AddStoryController extends State<AddStoryPage> {
); );
if (picked == null) return; if (picked == null) return;
final matrixFile = await showFutureLoadingDialog( final matrixFile = await showFutureLoadingDialog(
context: context, context: context,
future: () async { future: () async {
final bytes = await picked.readAsBytes(); final bytes = await picked.readAsBytes();
return MatrixImageFile( return MatrixImageFile(
bytes: bytes, bytes: bytes,
name: picked.name, name: picked.name,
); );
}, });
);
setState(() { setState(() {
image = matrixFile.result; image = matrixFile.result;

View File

@ -6,7 +6,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
@ -92,39 +92,34 @@ class InviteStoryPageState extends State<InviteStoryPage> {
const Divider(height: 1), const Divider(height: 1),
Expanded( Expanded(
child: FutureBuilder<List<User>>( child: FutureBuilder<List<User>>(
future: loadContacts, future: loadContacts,
builder: (context, snapshot) { builder: (context, snapshot) {
final contacts = snapshot.data; final contacts = snapshot.data;
if (contacts == null) { if (contacts == null) {
final error = snapshot.error; final error = snapshot.error;
if (error != null) { if (error != null) {
return Center( return Center(
child: Text(error.toLocalizedString(context)), child: Text(error.toLocalizedString(context)));
); }
return const Center(
child: CircularProgressIndicator.adaptive());
} }
return const Center( _undecided = contacts.map((u) => u.id).toSet();
child: CircularProgressIndicator.adaptive(), return ListView.builder(
); itemCount: contacts.length,
} itemBuilder: (context, i) => SwitchListTile.adaptive(
_undecided = contacts.map((u) => u.id).toSet(); value: _invite.contains(contacts[i].id),
return ListView.builder( onChanged: (b) => setState(() => b
itemCount: contacts.length,
itemBuilder: (context, i) => SwitchListTile.adaptive(
value: _invite.contains(contacts[i].id),
onChanged: (b) => setState(
() => b
? _invite.add(contacts[i].id) ? _invite.add(contacts[i].id)
: _invite.remove(contacts[i].id), : _invite.remove(contacts[i].id)),
secondary: Avatar(
mxContent: contacts[i].avatarUrl,
name: contacts[i].calcDisplayname(),
),
title: Text(contacts[i].calcDisplayname()),
), ),
secondary: Avatar( );
mxContent: contacts[i].avatarUrl, }),
name: contacts[i].calcDisplayname(),
),
title: Text(contacts[i].calcDisplayname()),
),
);
},
),
), ),
], ],
), ),

View File

@ -21,9 +21,11 @@ class ArchiveController extends State<Archive> {
Future<List<Room>> getArchive(BuildContext context) async { Future<List<Room>> getArchive(BuildContext context) async {
final archive = this.archive; final archive = this.archive;
if (archive != null) return archive; if (archive != null) return archive;
return this.archive = await Matrix.of(context).client.loadArchive(); return await Matrix.of(context).client.loadArchive();
} }
void forgetAction(int i) => setState(() => archive?.removeAt(i));
void forgetAllAction() async { void forgetAllAction() async {
final archive = this.archive; final archive = this.archive;
if (archive == null) return; if (archive == null) return;
@ -42,7 +44,7 @@ class ArchiveController extends State<Archive> {
context: context, context: context,
future: () async { future: () async {
while (archive.isNotEmpty) { while (archive.isNotEmpty) {
Logs().v('Forget room ${archive.last.getLocalizedDisplayname()}'); Logs().v('Forget room ${archive.last.displayname}');
await archive.last.forget(); await archive.last.forget();
archive.removeLast(); archive.removeLast();
} }

View File

@ -21,14 +21,10 @@ class ArchiveView extends StatelessWidget {
leading: const BackButton(), leading: const BackButton(),
title: Text(L10n.of(context)!.archive), title: Text(L10n.of(context)!.archive),
actions: [ actions: [
if (snapshot.data?.isNotEmpty ?? false) if (snapshot.hasData && archive != null && archive!.isNotEmpty)
Padding( TextButton(
padding: const EdgeInsets.all(8.0), onPressed: controller.forgetAllAction,
child: TextButton.icon( child: Text(L10n.of(context)!.clearArchive),
onPressed: controller.forgetAllAction,
label: Text(L10n.of(context)!.clearArchive),
icon: const Icon(Icons.cleaning_services_outlined),
),
) )
], ],
), ),
@ -36,27 +32,25 @@ class ArchiveView extends StatelessWidget {
builder: (BuildContext context) { builder: (BuildContext context) {
if (snapshot.hasError) { if (snapshot.hasError) {
return Center( return Center(
child: Text( child: Text(
L10n.of(context)!.oopsSomethingWentWrong, L10n.of(context)!.oopsSomethingWentWrong,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ));
);
} }
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const Center( return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2));
);
} else { } else {
archive = snapshot.data; archive = snapshot.data;
if (archive == null || archive!.isEmpty) { if (archive == null || archive!.isEmpty) {
return const Center( return const Center(
child: Icon(Icons.archive_outlined, size: 80), child: Icon(Icons.archive_outlined, size: 80));
);
} }
return ListView.builder( return ListView.builder(
itemCount: archive!.length, itemCount: archive!.length,
itemBuilder: (BuildContext context, int i) => ChatListItem( itemBuilder: (BuildContext context, int i) => ChatListItem(
archive![i], archive![i],
onForget: controller.forgetAction,
), ),
); );
} }

View File

@ -125,12 +125,9 @@ class BootstrapDialogState extends State<BootstrapDialog> {
children: [ children: [
ListTile( ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0), contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
trailing: CircleAvatar( trailing: Icon(
backgroundColor: Colors.transparent, Icons.info_outlined,
child: Icon( color: Theme.of(context).colorScheme.primary,
Icons.info_outlined,
color: Theme.of(context).colorScheme.primary,
),
), ),
subtitle: Text(L10n.of(context)!.chatBackupDescription), subtitle: Text(L10n.of(context)!.chatBackupDescription),
), ),
@ -139,15 +136,10 @@ class BootstrapDialogState extends State<BootstrapDialog> {
thickness: 1, thickness: 1,
), ),
TextField( TextField(
minLines: 2, minLines: 4,
maxLines: 4, maxLines: 4,
readOnly: true, readOnly: true,
style: const TextStyle(fontFamily: 'RobotoMono'),
controller: TextEditingController(text: key), controller: TextEditingController(text: key),
decoration: const InputDecoration(
contentPadding: EdgeInsets.all(16),
suffixIcon: Icon(Icons.key_outlined),
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
if (_supportsSecureStorage) if (_supportsSecureStorage)
@ -241,13 +233,12 @@ class BootstrapDialogState extends State<BootstrapDialog> {
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: Navigator.of(context).pop, onPressed: Navigator.of(context).pop,
), ),
title: Text(L10n.of(context)!.chatBackup), title: Text(L10n.of(context)!.unlockOldMessages),
), ),
body: Center( body: Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5, maxWidth: FluffyThemes.columnWidth * 1.5),
),
child: ListView( child: ListView(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
children: [ children: [
@ -259,85 +250,74 @@ class BootstrapDialogState extends State<BootstrapDialog> {
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
subtitle: Text( subtitle: Text(
L10n.of(context)!.pleaseEnterRecoveryKeyDescription, L10n.of(context)!.pleaseEnterRecoveryKeyDescription),
),
), ),
const Divider(height: 32), const Divider(height: 32),
TextField( TextField(
minLines: 1, minLines: 1,
maxLines: 2, maxLines: 1,
autocorrect: false, autocorrect: false,
readOnly: _recoveryKeyInputLoading, readOnly: _recoveryKeyInputLoading,
autofillHints: _recoveryKeyInputLoading autofillHints: _recoveryKeyInputLoading
? null ? null
: [AutofillHints.password], : [AutofillHints.password],
controller: _recoveryKeyTextEditingController, controller: _recoveryKeyTextEditingController,
style: const TextStyle(fontFamily: 'RobotoMono'),
decoration: InputDecoration( decoration: InputDecoration(
contentPadding: const EdgeInsets.all(16), hintText: 'Abc123 Def456',
hintStyle: TextStyle( labelText: L10n.of(context)!.recoveryKey,
fontFamily:
Theme.of(context).textTheme.bodyLarge?.fontFamily,
),
hintText: L10n.of(context)!.recoveryKey,
errorText: _recoveryKeyInputError, errorText: _recoveryKeyInputError,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
ElevatedButton.icon( ElevatedButton.icon(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
foregroundColor: foregroundColor:
Theme.of(context).colorScheme.onPrimary, Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
),
icon: _recoveryKeyInputLoading
? const CircularProgressIndicator.adaptive()
: const Icon(Icons.lock_open_outlined),
label: Text(L10n.of(context)!.unlockOldMessages),
onPressed: _recoveryKeyInputLoading
? null
: () async {
setState(() {
_recoveryKeyInputError = null;
_recoveryKeyInputLoading = true;
});
try {
final key =
_recoveryKeyTextEditingController.text;
await bootstrap.newSsssKey!.unlock(
keyOrPassphrase: key,
);
Logs().d('SSSS unlocked');
await bootstrap.client.encryption!.crossSigning
.selfSign(
keyOrPassphrase: key,
);
Logs().d('Successful elfsigned');
await bootstrap.openExistingSsss();
} catch (e, s) {
Logs().w('Unable to unlock SSSS', e, s);
setState(
() => _recoveryKeyInputError =
L10n.of(context)!.oopsSomethingWentWrong,
);
} finally {
setState(
() => _recoveryKeyInputLoading = false,
);
}
},
),
const SizedBox(height: 16),
Row(
children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(L10n.of(context)!.or),
), ),
const Expanded(child: Divider()), icon: _recoveryKeyInputLoading
], ? const CircularProgressIndicator.adaptive()
), : const Icon(Icons.lock_open_outlined),
label: Text(L10n.of(context)!.unlockOldMessages),
onPressed: _recoveryKeyInputLoading
? null
: () async {
setState(() {
_recoveryKeyInputError = null;
_recoveryKeyInputLoading = true;
});
try {
final key =
_recoveryKeyTextEditingController.text;
await bootstrap.newSsssKey!.unlock(
keyOrPassphrase: key,
);
Logs().d('SSSS unlocked');
await bootstrap
.client.encryption!.crossSigning
.selfSign(
keyOrPassphrase: key,
);
Logs().d('Successful elfsigned');
await bootstrap.openExistingSsss();
} catch (e, s) {
Logs().w('Unable to unlock SSSS', e, s);
setState(() => _recoveryKeyInputError =
L10n.of(context)!.oopsSomethingWentWrong);
} finally {
setState(
() => _recoveryKeyInputLoading = false);
}
}),
const SizedBox(height: 16),
Row(children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(L10n.of(context)!.or),
),
const Expanded(child: Divider()),
]),
const SizedBox(height: 16), const SizedBox(height: 16),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.cast_connected_outlined), icon: const Icon(Icons.cast_connected_outlined),
@ -414,13 +394,11 @@ class BootstrapDialogState extends State<BootstrapDialog> {
case BootstrapState.error: case BootstrapState.error:
titleText = L10n.of(context)!.oopsSomethingWentWrong; titleText = L10n.of(context)!.oopsSomethingWentWrong;
body = const Icon(Icons.error_outline, color: Colors.red, size: 40); body = const Icon(Icons.error_outline, color: Colors.red, size: 40);
buttons.add( buttons.add(AdaptiveFlatButton(
AdaptiveFlatButton( label: L10n.of(context)!.close,
label: L10n.of(context)!.close, onPressed: () =>
onPressed: () => Navigator.of(context, rootNavigator: false).pop<bool>(false),
Navigator.of(context, rootNavigator: false).pop<bool>(false), ));
),
);
break; break;
case BootstrapState.done: case BootstrapState.done:
titleText = L10n.of(context)!.everythingReady; titleText = L10n.of(context)!.everythingReady;
@ -431,13 +409,11 @@ class BootstrapDialogState extends State<BootstrapDialog> {
Text(L10n.of(context)!.yourChatBackupHasBeenSetUp), Text(L10n.of(context)!.yourChatBackupHasBeenSetUp),
], ],
); );
buttons.add( buttons.add(AdaptiveFlatButton(
AdaptiveFlatButton( label: L10n.of(context)!.close,
label: L10n.of(context)!.close, onPressed: () =>
onPressed: () => Navigator.of(context, rootNavigator: false).pop<bool>(false),
Navigator.of(context, rootNavigator: false).pop<bool>(false), ));
),
);
break; break;
} }
} }

View File

@ -75,8 +75,7 @@ class AddWidgetTileState extends State<AddWidgetTile> {
Navigator.of(context).pop(); Navigator.of(context).pop();
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context)!.errorAddingWidget)), SnackBar(content: Text(L10n.of(context)!.errorAddingWidget)));
);
} }
} }

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