Compare commits

..

1 commit

Author SHA1 Message Date
Krille
c8aa8041e8
refactor: Fetch FLUTTER_VERSION from pubspec.yaml file 2024-12-30 14:46:33 +01:00
546 changed files with 126874 additions and 133505 deletions

3
.github/CODEOWNERS vendored
View file

@ -1,3 +1,2 @@
* @krille-chan
pubspec.* @dependabot
lib/l10n/*.arb @weblate
assets/l10n/*.arb @weblate

View file

@ -34,28 +34,6 @@ body:
placeholder: "e.g. 1.12.0"
validations:
required: true
- type: dropdown
id: platform
attributes:
label: "Platform"
description: "Select the platform where the bug occurs."
options:
- Android (PlayStore)
- Android (F-Droid)
- Android (Other)
- iOS (iPhone)
- iOS (iPad)
- Web (Chrome(ium))
- Web (Firefox)
- Web (Safari)
- Linux (Snap)
- Linux (Flatpak)
- Linux (Other)
- macOS (iOS/iPadOS version)
- macOS (Self-compiled)
- Windows (Self-compiled)
validations:
required: true
- type: input
id: platform-info
attributes:

View file

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

View file

@ -1,27 +0,0 @@
name: Close Inactive Issues And PRs
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 365
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 365 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
stale-pr-message: "This pull request is stale because it has been open for 365 days with no activity."
close-pr-message: "This pull request was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: 365
days-before-pr-close: 14
exempt-milestones: true
exempt-assignees: krille-chan
operations-per-run: 500
repo-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -39,6 +39,3 @@ labels: test
10. Drag&Drop to send a file into a chat still works:
- [ ] Web
- [ ] Linux
11. Deeplinks are still working? https://matrix.to/#/@krille:janian.de
- [ ] Android
- [ ] iOS

View file

@ -1,50 +0,0 @@
name: "Free up space"
inputs:
target:
required: true
runs:
using: "composite"
steps:
- name: Free up space
shell: bash
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/share/boost
sudo rm -rf /usr/local/share/chromium
sudo rm -rf /usr/local/share/powershell
sudo rm -rf /usr/local/share/vcpkg
sudo rm -rf /usr/local/share/miniconda
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /opt/hostedtoolcache/go
sudo rm -rf /opt/hostedtoolcache/Python
sudo rm -rf /opt/hostedtoolcache/node
sudo rm -rf /opt/hostedtoolcache/R
sudo rm -rf /opt/hostedtoolcache/Java
sudo rm -rf /opt/hostedtoolcache/LLVM
sudo rm -rf /opt/hostedtoolcache/Swift
sudo rm -rf /opt/hostedtoolcache/Php
sudo rm -rf /opt/hostedtoolcache/Perl
sudo rm -rf /opt/hostedtoolcache/Scala
sudo rm -rf /opt/hostedtoolcache/Julia
sudo rm -rf /opt/hostedtoolcache/Mono
sudo rm -rf /opt/hostedtoolcache/PowerShell
sudo rm -rf /opt/hostedtoolcache/Crystal
sudo rm -rf /opt/hostedtoolcache/Elixir
sudo rm -rf /opt/hostedtoolcache/Erlang
sudo rm -rf /opt/hostedtoolcache/FSharp
sudo rm -rf /opt/hostedtoolcache/Haskell
sudo rm -rf /opt/hostedtoolcache/OCaml
sudo rm -rf /opt/hostedtoolcache/Sbt
sudo rm -rf /opt/hostedtoolcache/Solidity
sudo rm -rf /opt/hostedtoolcache/VisualStudio
sudo rm -rf /opt/hostedtoolcache/WinAppDriver
sudo rm -rf /opt/hostedtoolcache/Xamarin
sudo rm -rf /opt/hostedtoolcache/Yarn
sudo rm -rf /opt/hostedtoolcache/Zephyr
sudo rm -rf /opt/hostedtoolcache/zig
sudo rm -rf /opt/hostedtoolcache/zulu
sudo rm -rf /opt/hostedtoolcache/azcopy
echo "export CARGO_BUILD_JOBS=1" >> $GITHUB_ENV

View file

@ -9,9 +9,6 @@ updates:
commit-message:
prefix: "build: "
include: "scope"
assignees:
- "krille-chan"
open-pull-requests-limit: 5
- package-ecosystem: "github-actions"
directory: "/"
schedule:

View file

@ -1,6 +1,13 @@
*Thank you so much for your contribution to FluffyChat ❤️❤️❤️*
- [ ] I have read and understood the [contributing guidelines](https://github.com/krille-chan/fluffychat/blob/main/CONTRIBUTING.md).
Please make sure that your Pull Request meet the following **acceptance criteria**:
- [ ] Code formatting and import sorting has been done with `dart format lib/ test/` and `dart run import_sorter:main --no-comments`
- [ ] The commit message uses the format of [Conventional Commits](https://www.conventionalcommits.org)
- [ ] The commit message describes what has been changed, why it has been changed and how it has been changed
- [ ] Every new feature or change of the design/GUI is linked to an approved design proposal in an issue
- [ ] Every new feature in the app or the build system has a strategy how this will be tested and maintained from now on for every release, e.g. a volunteer who takes over maintainership
### Pull Request has been tested on:

View file

@ -8,19 +8,13 @@ jobs:
code_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: webiny/action-conventional-commits@v1.3.1
- run: ./scripts/generate-locale-config.sh
- run: git diff --exit-code
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- name: Check for unused translations
run: flutter pub run translations_cleaner list-unused-terms -a
- run: flutter pub get
- name: Check if pubspec.lock is up to date
run: git diff --exit-code pubspec.lock
- run: flutter gen-l10n
- name: Check formatting
run: dart format lib/ test/ --set-exit-if-changed
- name: Check import formatting
@ -28,222 +22,69 @@ jobs:
- name: Check license compliance
run: dart run license_checker check-licenses -c licenses.yaml --problematic
- run: flutter analyze
- name: Search unused dependencies
run: |
dart pub global activate dependency_validator
dart pub global run dependency_validator
- run: dart run dart_code_linter:metrics analyze lib --reporter=github
- run: dart run dart_code_linter:metrics check-unused-code lib
- run: dart run dart_code_linter:metrics check-unused-files lib
- run: dart run dart_code_linter:metrics check-unused-l10n lib
- name: Check for commented-out Dart code with semicolons
run: |
if grep -R --include="*.dart" -nE '^[[:space:]]*//[^/<].*;[[:space:]]*$' lib/; then
echo ""
echo "❌ Found commented-out Dart code ending with semicolon."
exit 1
fi
- name: Add Firebase Messaging
run: ./scripts/add-firebase-messaging.sh
- name: Apply google services patch
run: git apply ./scripts/enable-android-google-services.patch
- run: flutter analyze
- run: flutter test
build_debug_apk:
needs: [ code_tests ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/free_up_space
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: "zulu"
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- uses: moonrepo/setup-rust@v1
with:
cache: true
- name: Cache Gradle
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: gradle-${{ runner.os }}-
- run: ./scripts/add-firebase-messaging.sh
- run: flutter build apk --debug --target-platform android-x64
- name: Upload Debug APK
uses: actions/upload-artifact@v7
with:
name: debug-apk-x64
path: build/app/outputs/flutter-apk/app-debug.apk
- run: flutter pub get
- run: flutter build apk --debug
build_debug_web:
needs: [ code_tests ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- uses: moonrepo/setup-rust@v1
- run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
- run: flutter pub get
- name: Prepare web
run: ./scripts/prepare-web.sh
- run: flutter build web --dart-define=WITH_SEMANTICS=true
- name: Upload Web Build
uses: actions/upload-artifact@v7
with:
name: Web Build
path: build/web
- run: flutter build web
build_debug_linux:
needs: [ code_tests ]
strategy:
matrix:
arch: [ x64, arm64 ]
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest'}}
runs-on: ${{ matrix.arch == 'arm64' && 'self-hosted' || 'ubuntu-latest'}}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install git wget 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 libwebkit2gtk-4.1-dev -y
- run: echo "FLUTTER_VERSION=$(yq '.environment.flutter' < .tool_versions.yaml)" >> $GITHUB_ENV
run: sudo apt-get update && sudo apt-get install git wget 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 libssl-dev libwebkit2gtk-4.1-dev -y
- uses: chrisdickinson/setup-yq@latest
- run: FLUTTER_VERSION=$(yq e '.environment.flutter' pubspec.yaml)
- name: Install Flutter
run: |
git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git
./flutter/bin/flutter doctor
- uses: moonrepo/setup-rust@v1
- run: ./flutter/bin/flutter pub get
- run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }}
build_debug_ios:
needs: [ code_tests ]
runs-on: macos-15
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- name: Use Xcode 16.4
run: sudo xcode-select --switch /Applications/Xcode_16.4.app
- name: Setup Xcode version
uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: latest
- run: brew install sqlcipher
- uses: moonrepo/setup-rust@v1
- name: Add Firebase Messaging
run: |
flutter pub add fcm_shared_isolate:0.1.0
sed -i '' 's,//<GOOGLE_SERVICES>,,g' lib/utils/background_push.dart
- run: flutter pub get
- run: flutter build ios --no-codesign
integration_test:
runs-on: ubuntu-latest
timeout-minutes: 60
needs: [ build_debug_apk ]
strategy:
matrix:
api-level: [34]
env:
ANDROID_USER_HOME: /home/runner/.android
ANDROID_EMULATOR_HOME: /home/runner/.android
ANDROID_AVD_HOME: /home/runner/.android/avd
AVD_CONFIG_PATH: "~/.android/avd/test.avd/config.ini"
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v8
with:
name: debug-apk-x64
path: .
- uses: ./.github/actions/free_up_space
# https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: AVD cache
uses: actions/cache@v5
id: avd-cache
with:
path: ~/.android/*
key: avd-${{ matrix.api-level }}-integration_docker
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b
with:
api-level: ${{ matrix.api-level }}
target: google_apis
arch: x86_64
cores: 16
ndk: 28.2.13676358
force-avd-creation: false
disk-size: 4096M
ram-size: 4096M
sdcard-path-or-size: 4096M
emulator-options: -no-window -wipe-data -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: |
cat ${{ env.AVD_CONFIG_PATH }}
sed -i.bak 's/hw.lcd.density = .*/hw.lcd.density=420/' ${{ env.AVD_CONFIG_PATH }}
sed -i.bak 's/hw.lcd.height = .*/hw.lcd.height=1920/' ${{ env.AVD_CONFIG_PATH }}
sed -i.bak 's/hw.lcd.width = .*/hw.lcd.width=1080/' ${{ env.AVD_CONFIG_PATH }}
if ! grep -q "hw.lcd.density" ${{ env.AVD_CONFIG_PATH }} && echo "hw.lcd.density = 420" >> ${{ env.AVD_CONFIG_PATH }}; then :; fi
if ! grep -q "hw.lcd.height" ${{ env.AVD_CONFIG_PATH }} && echo "hw.lcd.height = 1920" >> ${{ env.AVD_CONFIG_PATH }}; then :; fi
if ! grep -q "hw.lcd.width" ${{ env.AVD_CONFIG_PATH }} && echo "hw.lcd.width = 1080" >> ${{ env.AVD_CONFIG_PATH }}; then :; fi
echo "Emulator settings (${{ env.AVD_CONFIG_PATH }})"
cat ${{ env.AVD_CONFIG_PATH }}
echo "Generated AVD snapshot for caching."
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
cache: true
- uses: remarkablemark/setup-maestro-cli@v1
- name: Load integration test env
run: cat integration_test/data/integration_users.env >> $GITHUB_ENV
- name: Prepare Homeserver
run: |
docker run -d --name synapse --tmpfs /data \
--volume="$(pwd)/integration_test/synapse/data/homeserver.yaml":/data/homeserver.yaml:rw \
--volume="$(pwd)/integration_test/synapse/data/localhost.log.config":/data/localhost.log.config:rw \
-p 80:80 matrixdotorg/synapse:latest
while ! curl -XGET "http://$HOMESERVER/_matrix/client/v3/login" >/dev/null 2>/dev/null; do
echo "Waiting for homeserver to be available... (GET http://$HOMESERVER/_matrix/client/v3/login)"
sleep 2
done
echo "Homeserver is online!"
# create users
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER1_NAME\", \"password\":\"$USER1_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER2_NAME\", \"password\":\"$USER2_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
- name: Integration tests
id: integration_tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: google_apis
arch: x86_64
cores: 16
ndk: 28.2.13676358
force-avd-creation: false
disk-size: 4096M
ram-size: 4096M
sdcard-path-or-size: 4096M
emulator-options: -no-snapshot-save -no-window -wipe-data -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: |
flutter run --use-application-binary=$PWD/app-debug.apk > flutter_logs.txt 2>&1 &
FLUTTER_PID=$!
maestro test integration_test/login.yaml --env HOMESERVER=10.0.2.2 --env USER1_NAME=${USER1_NAME} --env USER1_PW=${USER1_PW}
kill $FLUTTER_PID 2>/dev/null || true
cp flutter_logs.txt ~/.maestro/tests/
- name: Upload Flutter and Maestro logs
if: failure()
uses: actions/upload-artifact@v7
with:
name: maestro-logs
path: ~/.maestro/tests
if-no-files-found: ignore

View file

@ -14,16 +14,17 @@ jobs:
deploy_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
- uses: moonrepo/setup-rust@v1
- run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
flutter-version-file: pubspec.yaml
- name: Remove Emoji Font
run: |
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare web
run: ./scripts/prepare-web.sh
- run: rm ./assets/vodozemac/.gitignore
- run: flutter pub get
- name: Build Release Web
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/nightly/"
- run: mv build/web/ public
@ -38,23 +39,27 @@ jobs:
deploy_playstore_internal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'zulu'
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- uses: ./.github/actions/free_up_space
- uses: moonrepo/setup-rust@v1
- name: Install Fastlane
run: gem install fastlane -NV
- name: Add Firebase Messaging
- name: Apply Google Services Patch
run: git apply ./scripts/enable-android-google-services.patch
- name: Remove Emoji Font
run: |
echo '${{secrets.GOOGLE_SERVICES_JSON}}' | base64 -d > android/app/google-services.json
./scripts/add-firebase-messaging.sh
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare Android Release Build
env:
@ -63,10 +68,7 @@ jobs:
PLAYSTORE_DEPLOY_KEY: ${{ secrets.PLAYSTORE_DEPLOY_KEY }}
run: ./scripts/prepare-android-release.sh
- name: Build Android Release
run: flutter build appbundle --target-platform android-arm,android-arm64
- name: Set changelog
working-directory: android/fastlane/metadata/android/en-US/changelogs
run: git log --no-merges -1 --pretty=%B > default.txt
run: flutter build appbundle --target-platform android-arm,android-arm64,android-x64
- name: Deploy Android Release
run: |
mkdir -p build/android

View file

@ -1,60 +0,0 @@
name: Matrix Notification
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
jobs:
notify:
if: ${{ (github.event_name == 'issues' && github.event.issue.user.login != 'krille-chan') || (github.event_name == 'pull_request_target' && github.event.pull_request.user.login != 'krille-chan') }}
runs-on: ubuntu-latest
steps:
- name: Send notification to Matrix room
env:
HOMESERVER: ${{ secrets.MATRIX_HOMESERVER }}
ACCESS_TOKEN: ${{ secrets.MATRIX_ACCESS_TOKEN }}
ROOM_ID: ${{ secrets.MATRIX_ROOM_ID }}
EVENT_NAME: ${{ github.event_name }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_URL: ${{ github.event.issue.html_url }}
ISSUE_LABELS: ${{ join(github.event.issue.labels.*.name, ', ') }}
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ', ') }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
if [ "${EVENT_NAME}" = "issues" ]; then
EMOJI="📝"
PREFIX="New Issue"
TITLE="${ISSUE_TITLE}"
URL="${ISSUE_URL}"
LABELS="${ISSUE_LABELS}"
AUTHOR="${ISSUE_AUTHOR}"
else
EMOJI="🔀"
PREFIX="New Pull Request"
TITLE="${PR_TITLE}"
URL="${PR_URL}"
LABELS="${PR_LABELS}"
AUTHOR="${PR_AUTHOR}"
fi
AUTHOR_URL="https://github.com/${AUTHOR}"
PLAIN="${EMOJI} ${PREFIX} by ${AUTHOR}: ${TITLE} - ${URL}"
HTML="<h3>${EMOJI} ${PREFIX}</h3>"
HTML="${HTML}<b><a href='${URL}'>${TITLE}</a></b><br/>"
HTML="${HTML}👤 <a href='${AUTHOR_URL}'>${AUTHOR}</a>"
if [ -n "${LABELS}" ]; then
HTML="${HTML}<br/>🏷️ ${LABELS}"
fi
TXN_ID=$(date +%s%N)
curl -s -o /dev/null -w "%{http_code}" -X PUT \
"${HOMESERVER}/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/${TXN_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg body "$PLAIN" --arg html "$HTML" \
'{"msgtype":"m.notice","body":$body,"format":"org.matrix.custom.html","formatted_body":$html}')"

View file

@ -17,23 +17,26 @@ jobs:
build_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- uses: moonrepo/setup-rust@v1
- run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install nodejs -y
- name: Remove Emoji Font
run: |
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare web
run: ./scripts/prepare-web.sh
- run: rm ./assets/vodozemac/.gitignore
- name: Build Release Web
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/web/"
- name: Create archive
run: tar -czf fluffychat-web.tar.gz build/web/
- name: Upload Web Build
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Web Build
path: fluffychat-web.tar.gz
@ -46,25 +49,13 @@ jobs:
asset_path: fluffychat-web.tar.gz
asset_name: fluffychat-web.tar.gz
asset_content_type: application/gzip
- name: Clone fluffychat website
- name: Build Website
run: |
git clone https://github.com/krille-chan/fluffychat-website.git
cp CHANGELOG.md fluffychat-website/
cp PRIVACY.md fluffychat-website/
- name: Build website
working-directory: fluffychat-website
run: |
npm install tailwindcss @tailwindcss/cli
npx tailwindcss -i ./src/styles.css -o ./src/assets/tailwind.css --minify
npx @11ty/eleventy
mv public ../
- name: Copy FluffyChat web into it
run: |
mkdir public/web
mkdir public/nightly
cp -r build/web/* public/web/
cp -r build/web/* public/nightly/
echo "fluffychat.im" >> public/CNAME
cd docs && npx tailwindcss -o ./tailwind.css --minify && cd ..
mv docs public
mv repo public || true
mv build/web/ public/web
cp public/web -r public/nightly
- name: Deploy to GitHub Pages
if: startsWith(github.ref, 'refs/tags/v')
uses: peaceiris/actions-gh-pages@v4
@ -77,17 +68,21 @@ jobs:
build_apk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'zulu'
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- name: Add Firebase Messaging
- name: Apply Google Services Patch
run: git apply ./scripts/enable-android-google-services.patch
- name: Remove Emoji Font
run: |
echo '${{secrets.GOOGLE_SERVICES_JSON}}' | base64 -d > android/app/google-services.json
./scripts/add-firebase-messaging.sh
- uses: ./.github/actions/free_up_space
- uses: moonrepo/setup-rust@v1
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare Android Release Build
env:
@ -95,7 +90,7 @@ jobs:
FDROID_KEY_PASS: ${{ secrets.FDROID_KEY_PASS }}
PLAYSTORE_DEPLOY_KEY: ${{ secrets.PLAYSTORE_DEPLOY_KEY }}
run: ./scripts/prepare-android-release.sh
- run: flutter build apk --release --target-platform android-arm,android-arm64
- run: flutter build apk --release
- name: Upload to release
uses: actions/upload-release-asset@v1
env:
@ -110,17 +105,17 @@ jobs:
strategy:
matrix:
arch: [ x64, arm64 ]
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest'}}
runs-on: ${{ matrix.arch == 'arm64' && 'self-hosted' || 'ubuntu-latest'}}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install dependencies
run: 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 libwebkit2gtk-4.1-dev -y
- run: echo "FLUTTER_VERSION=$(yq '.environment.flutter' < .tool_versions.yaml)" >> $GITHUB_ENV
run: 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 libssl-dev libwebkit2gtk-4.1-dev -y
- uses: chrisdickinson/setup-yq@latest
- run: FLUTTER_VERSION=$(yq e '.environment.flutter' pubspec.yaml)
- name: Install Flutter
run: |
git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git
./flutter/bin/flutter doctor
- uses: moonrepo/setup-rust@v1
- run: ./flutter/bin/flutter pub get
- run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }}
- name: Create archive
@ -138,24 +133,27 @@ jobs:
deploy_playstore:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'zulu'
- uses: subosito/flutter-action@v2
with:
flutter-version-file: .tool_versions.yaml
flutter-version-file: pubspec.yaml
cache: true
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- uses: ./.github/actions/free_up_space
- uses: moonrepo/setup-rust@v1
- name: Install Fastlane
working-directory: android
run: gem install fastlane -NV
- name: Add Firebase Messaging
- name: Apply Google Services Patch
run: git apply ./scripts/enable-android-google-services.patch
- name: Remove Emoji Font
run: |
echo '${{secrets.GOOGLE_SERVICES_JSON}}' | base64 -d > android/app/google-services.json
./scripts/add-firebase-messaging.sh
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare Android Release Build
env:
@ -164,7 +162,7 @@ jobs:
PLAYSTORE_DEPLOY_KEY: ${{ secrets.PLAYSTORE_DEPLOY_KEY }}
run: ./scripts/prepare-android-release.sh
- name: Build Android Release
run: flutter build appbundle --target-platform android-arm,android-arm64
run: flutter build appbundle --target-platform android-arm,android-arm64,android-x64
- name: Get Tag Name
id: tag_name
run: echo "::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})"
@ -183,6 +181,28 @@ jobs:
fi
cd ..
promote_snapcraft:
runs-on: ubuntu-latest
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v3
- name: Get Tag Name
id: tag_name
run: echo "::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})"
- name: Promote Snap
env: # Workaround for https://github.com/snapcore/snapcraft/issues/4439
SNAPCRAFT_HAS_TTY: "true"
run: |
if [[ $GITHUB_REF_NAME == rc* ]]; then
yes | snapcraft promote fluffychat --from-channel edge --to-channel candidate
else
yes | snapcraft promote fluffychat --from-channel edge --to-channel stable
fi
deploy_docker:
runs-on: ubuntu-latest
permissions:
@ -190,20 +210,20 @@ jobs:
packages: write
steps:
- name: Check out Git repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
with:
context: .
push: true

View file

@ -1,21 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
with:
stale-issue-message: 'This issue is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
stale-pr-message: 'This PR is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
close-issue-message: 'This issue was closed because it has been stalled for 30 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 30 days with no activity.'
days-before-issue-stale: 365
days-before-pr-stale: 365
days-before-issue-close: 30
days-before-pr-close: 30
exempt-all-assignees: true
operations-per-run: 1000

16
.gitignore vendored
View file

@ -13,8 +13,7 @@
prime
# libolm package
web/Imaging.js
web/Imaging.wasm
/assets/js/package
# IntelliJ related
*.iml
@ -50,11 +49,10 @@ docs/tailwind.css
android/key.jks
android/keys.json
android/Gemfile.lock
lib/l10n/*.dart
lib/l10n_old
ios/Flutter/.last_build_id
ios/Podfile.lock
ios/Runner.ipa
ios/Runner/GoogleServices-Info.plist
/windows/out
/winuwp/out
@ -62,13 +60,3 @@ ios/Runner/GoogleServices-Info.plist
/macos/out
.vs
olm
docs/node_modules
rust
libcrypto.3.dylib
android/app/src/main/jniLibs/**
android/app/google-services.json
web/pkg/package.json
web/pkg/vodozemac_bindings_dart_bg.wasm
web/pkg/vodozemac_bindings_dart.js
web/native_executor.js*

View file

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "fcf2c11572af6f390246c056bc905eca609533a0"
revision: "abb292a07e20d696c4568099f918f6c5f330e6b0"
channel: "stable"
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: android
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
- platform: linux
create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
# User provided section

View file

@ -1,2 +0,0 @@
environment:
flutter: 3.41.4

File diff suppressed because it is too large Load diff

View file

@ -1,172 +0,0 @@
# Contributing to FluffyChat
Contributions are always welcome. Yet we might lack manpower to review all of them in time.
To improve the process please make sure that you read the following guidelines carefully:
## Contributing Guidelines
1. Always create a Pull Request for any changes.
2. Whenever possible please make sure that your Pull Request only contains **one** commit. Cases where multiple commits make sense are very rare.
3. Do not add merge commits. Use rebases.
4. Every Pull Request should change only one thing. For bigger changes it is often better to split them up in multiple Pull Requests.
5. [Sign your commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits).
6. Format the commit message as [Conventional Commits](https://www.conventionalcommits.org).
7. Format (`flutter format lib`) and sort impots (`dart run import_sorter:main --no-comments`) in all code files.
8. For bigger or complex changes (more than a couple of code lines) write an issue or refer to an existing issue and ask for approval from the maintainers (@krille-chan) **before** starting to implement it. This way you reduce the risk that your Pull Request get's declined.
9. Prefer simple and easy to maintain solutions over complexity and fancy ones.
# Code Style
FluffyChat tries to be as minimal as possible even in the code style. We try to keep the code clean, simple and easy to read. The source code of the app is under `/lib` with the main entry point `/lib/main.dart`.
<!-- editorconfig-checker-disable -->
<!-- prettier-ignore-start -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
- [Directory Structure:](#directory-structure)
- [Separation of Controllers and Views](#separation-of-controllers-and-views)
- [Formatting](#formatting)
- [Code Analyzis](#code-analyzis)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- prettier-ignore-end -->
<!-- editorconfig-checker-enable -->
### Directory Structure:
- /lib
- /config
- app_config.dart
- ...Constants, styles and other configurations
- /utils
- handy_function.dart
- ...Helper functions and extensions
- /pages
- /chat
- chat.dart
- chat_view.dart
- /chat_list
- chat_list.dart
- chat_list_view.dart
- ...The pages of the app separated in Controllers and Views
- /widgets
- /layouts
- ...Custom widgets created for this project
- main.dart
Most of the business model is in the Famedly Matrix Dart SDK. We try to not keep a model inside of the source code but extend it under `/utils`.
### Separation of Controllers and Views
We split views and controller logic with stateful widgets as controller where the build method just builds a stateless widget which receives the state as the only parameter. A common controller would look like this:
```dart
// /lib/controller/enter_name_controller.dart
import 'package:flutter/material.dart';
class EnterName extends StatefulWidget {
@override
EnterNameController createState() => EnterNameController();
}
class EnterNameController extends State<EnterName> {
final TextEditingController textEditingController = TextEditingController();
String name = 'Unknown';
/// Changes the name with the content in the textfield. If the textfield is
/// empty, this breaks up and displays a SnackBar.
void setNameAction() {
if (textEditingController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('You have not entered your name'),
),
);
return;
}
setState(() => name = textEditingController.text);
}
@override
Widget build(BuildContext context) => EnterNameView(this);
}
```
So we have a controller for a `EnterName` view which as a `TextEditingController`, a state `name` and an action `void setNameAction()`. Actions must always be methods of a type, that we dont need to pass parameters in the corresponding view class and must have dartdoc comments.
The view class could look like this:
```dart
// /lib/views/enter_name_view.dart
import 'package:flutter/material.dart';
class EnterNameView extends StatelessWidget {
final EnterNameController controller;
const EnterNameView(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your name: ${controller.name}'),
),
body: Center(
child: TextField(
controller: controller.textEditingController,
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.setNameAction,
child: Icon(Icons.save),
),
);
}
}
```
Views should just contain code which describes the view. All other parameters or logic should be in the controller. The job of the view class is just to take the current state and build the widget tree and pipe the callbacks back. If there is any calulation necessary which is not solveable as a simple if-else or switch statement, it should be done in an external helper function unter `/lib/utils/`.
All file names must be lower_snake_case. All views must have a `View` suffix and all controller must have a `Controller` suffix. Widgets may have a controller too but they should pass the callbacks back to the view where possible. Calling one line methods directly in the view is only recommended if there is no need to pass a parameter.
To perform an action on state initialization we use the initState method:
```dart
@override
void initState() {
// TODO: implement initState
super.initState();
}
```
And the dispose method to perform an action on disposing:
```dart
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
```
To run code after the widget was created first we use the WidgetBindings in the initState:
```dart
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
// Do something when build is finished
});
super.initState();
}
```
### Formatting
We do not allow code with wrong formatting. Please run `flutter format lib` if your IDE doesn't do this automatically.
### Code Analyzis
We do not allow codes with dart errors or warnings. We use the [flutter_lints](https://pub.dev/packages/flutter_lints) package for static code analysis with additional rules under `analysis_options.yaml`.

View file

@ -1,5 +1,5 @@
FROM ghcr.io/cirruslabs/flutter as builder
RUN sudo apt update && sudo apt install curl wget jq build-essential -y
RUN sudo apt update && sudo apt install curl wget jq -y
WORKDIR /tmp
RUN wget https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64.tar.gz
@ -8,9 +8,6 @@ RUN mv yq_linux_amd64 /usr/bin/yq
COPY . /app
WORKDIR /app
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
RUN ./scripts/prepare-web.sh
COPY config.* /app/
RUN flutter pub get

View file

@ -1,15 +1,14 @@
# Privacy
FluffyChat is available on Android, iOS, Linux and as a web version. Desktop versions for Windows and macOS may follow.
FluffyChat is available on Android, iOS and as a web version. Desktop versions for Windows, Linux and macOS may follow.
* [Matrix](#matrix)
* [Database](#database)
* [Encryption](#encryption)
* [App Permissions](#app-permissions)
* [Push Notifications](#push-notifications)
* [PlayStore Safety Standards](#playstore-safety)
## <a id="matrix" href="#matrix">#</a> Matrix
## Matrix<a id="matrix"/>
FluffyChat uses the Matrix protocol. This means that FluffyChat is just a client that can be connected to any compatible matrix server. The respective data protection agreement of the server selected by the user then applies.
For convenience, one or more servers are set as default that the FluffyChat developers consider trustworthy. The developers of FluffyChat do not guarantee their trustworthiness. Before the first communication, users are informed which server they are connecting to.
@ -18,17 +17,17 @@ FluffyChat only communicates with the selected server and with [OpenStreetMap](h
More information is available at: [https://matrix.org](https://matrix.org)
## <a id="database" href="#database">#</a> Database
## Database<a id="database"/>
FluffyChat caches some data received from the server in a local sqflite database on the device of the user. On web indexedDB is used. FluffyChat always tries to encrypt the database by using SQLCipher and stores the encryption key in the [Secure Storage](https://pub.dev/packages/flutter_secure_storage) of the device.
More information is available at: [https://pub.dev/packages/sqflite](https://pub.dev/packages/sqflite) and [https://pub.dev/packages/sqlcipher_flutter_libs](https://pub.dev/packages/sqlcipher_flutter_libs)
## <a id="encryption" href="#encryption">#</a> Encryption
## Encryption<a id="encryption"/>
All communication of substantive content between Fluffychat and any server is done in secure way, using transport encryption to protect it.
FluffyChat also uses End-To-End-Encryption by using [Vodozemac](https://github.com/matrix-org/vodozemac) and enables it by default for private chats.
FluffyChat also uses End-To-End-Encryption by using [libolm](https://gitlab.matrix.org/matrix-org/olm) and enables it by default for private chats.
## <a id="app-permissions" href="#app-permissions">#</a> App Permissions
## App Permissions<a id="app-permissions"/>
The permissions are the same on Android and iOS but may differ in the name. This are the Android Permissions:
@ -51,7 +50,7 @@ The user is able to send files from the device's file system.
#### Location
FluffyChat makes it possible to share the current location via the chat. When the user shares their location, FluffyChat uses the device location service and sends the geo-data via Matrix.
## <a id="push-notifications" href="#push-notifications">#</a> Push Notifications
## Push Notifications<a id="push-notifications"/>
FluffyChat uses the Firebase Cloud Messaging service for push notifications on Android and iOS. This takes place in the following steps:
1. The matrix server sends the push notification to the FluffyChat Push Gateway
2. The FluffyChat Push Gateway forwards the message in a different format to Firebase Cloud Messaging
@ -59,7 +58,7 @@ FluffyChat uses the Firebase Cloud Messaging service for push notifications on A
4. The device receives the push notification from Firebase Cloud Messaging and displays it as a notification
The source code of the push gateway can be viewed here:
[https://github.com/krille-chan/fluffygate](https://github.com/krille-chan/fluffygate)
[https://gitlab.com/famedly/services/famedly-push-gateway](https://gitlab.com/famedly/services/famedly-push-gateway)
`event_id_only` is used as the format for the push notification. A typical push notification therefore only contains:
- Event ID
@ -93,21 +92,3 @@ A typical push notification could look like this:
```
FluffyChat sets the `event_id_only` flag at the Matrix Server. This server is then responsible to send the correct data.
# <a id="playstore-safety" href="#playstore-safety">#</a> Explanation of FluffyChat's Compliance with Google Play Store's Safety Standards
FluffyChat is committed to promoting a safe and respectful environment for all users. As a Matrix client, FluffyChat connects users to various Matrix servers. Please note that FluffyChat does not host or manage any servers directly, and as such, we do not have the capability to enforce content moderation or deletion within the app itself.
To enhance user safety and help protect against the sexual abuse and exploitation of children, FluffyChat enables users to report inappropriate content directly to server administrators.
#### Reporting Content or Users:
1. Mark a message in the chat: Tap and hold the message you wish to report.
2. Report the message: Select the "Report" option.
3. Provide a reason and score: Enter the reason for reporting and assign a score from 1-100 to indicate how offensive the content is.
4. Notification to admin: The server administrator will be notified of the reported content.
In addition to reporting messages, users can also report other users following a similar process.
We encourage server administrators to adhere to strict safety standards and provide mechanisms for addressing and moderating inappropriate content. For more information on the Matrix protocol and its safety standards, please refer to the following link: https://matrix.org/docs/older/moderation/

View file

@ -1,12 +1,12 @@
![Screenshot](https://github.com/krille-chan/fluffychat/blob/main/assets/banner_transparent.png?raw=true)
[FluffyChat](https://fluffy.chat) is an open source, nonprofit and cute [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). The goal of the app is to create an easy to use instant messenger which is open source and accessible for everyone.
[FluffyChat](https://fluffychat.im) is an open source, nonprofit and cute [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). The goal of the app is to create an easy to use instant messenger which is open source and accessible for everyone.
### Links:
- 🌐 [[Weblate] Translate FluffyChat into your language](https://hosted.weblate.org/projects/fluffychat/)
- 🌍 [[m] Join the community](https://matrix.to/#/#fluffy-space:matrix.org)
- 📰 [[Mastodon] Get updates on social media](https://troet.cafe/@krille)
- 🌍 [[m] Join the community](https://matrix.to/#/#fluffychat:matrix.org)
- 📰 [[Mastodon] Get updates on social media](https://mastodon.art/@krille)
- 🖥️ [[Famedly] Server hosting and professional support](https://famedly.com/kontakt)
- 💝 [[Liberapay] Support FluffyChat development](https://de.liberapay.com/KrilleChritzelius)
@ -14,8 +14,7 @@
### Screenshots:
<img src="https://github.com/krille-chan/fluffychat-website/blob/main/src/assets/screenshots/mobile.png?raw=true" height="300">
<img src="https://github.com/krille-chan/fluffychat-website/blob/main/src/assets/screenshots/desktop.png?raw=true" height="300">
![Screenshot](https://github.com/krille-chan/fluffychat/blob/main/docs/screenshots/product.jpeg?raw=true)
# Features
@ -44,67 +43,13 @@
Please visit the website for installation instructions:
- https://fluffy.chat
- https://fluffychat.im
# How to build
1. To build FluffyChat you need [Flutter](https://flutter.dev) and [Rust](https://www.rust-lang.org/tools/install)
Please visit the [Wiki](https://github.com/krille-chan/fluffychat/wiki) for build instructions:
2. Clone the repo:
```
git clone https://github.com/krille-chan/fluffychat.git
cd fluffychat
```
3. Choose your target platform below and enable support for it.
3.1 If you want, enable Googles Firebase Cloud Messaging:
`./scripts/add-firebase-messaging.sh`
4. Debug with: `flutter run`
### Android
* Build with: `flutter build apk`
### iOS / iPadOS
* Have a Mac with Xcode installed, and set up for Xcode-managed app signing
* If you want automatic app installation to connected devices, make sure you have Apple Configurator installed, with the Automation Tools (`cfgutil`) enabled
* Set a few environment variables
* FLUFFYCHAT_NEW_TEAM: the Apple Developer team that your certificates should live under
* FLUFFYCHAT_NEW_GROUP: the group you want App IDs and such to live under (ie: com.example.fluffychat)
* FLUFFYCHAT_INSTALL_IPA: set to `1` if you want the IPA to be deployed to connected devices after building, otherwise unset
* Run `./scripts/build-ios.sh`
### Web
* Build with:
```bash
./scripts/prepare-web.sh # To install Vodozemac
flutter build web --release
```
* Optionally configure by serving a `config.json` at the same path as fluffychat.
An example can be found at `config.sample.json`. All values there are optional.
**Please only the values, you really need**. If you e.g. only want
to change the default homeserver, then only modify the `defaultHomeserver` key.
### Desktop (Linux, Windows, macOS)
* Enable Desktop support in Flutter: https://flutter.dev/desktop
#### Install custom dependencies (Linux)
```bash
sudo apt install libjsoncpp1 libsecret-1-dev libsecret-1-0 librhash0 libwebkit2gtk-4.0-dev lld
```
* Build with one of these:
```bash
flutter build linux --release
flutter build windows --release
flutter build macos --release
```
- https://github.com/krille-chan/fluffychat/wiki/How-To-Build
# Special thanks
@ -117,6 +62,8 @@ flutter build macos --release
* Also thanks to all translators and testers! With your help, fluffychat is now available in more than 12 languages.
* <a href="https://github.com/googlefonts/noto-emoji/">Noto Emoji Font</a> for the awesome emojis.
* <a href="https://github.com/madsrh/WoodenBeaver">WoodenBeaver</a> sound theme for the notification sound.
* The Matrix Foundation for making and maintaining the [emoji translations](https://github.com/matrix-org/matrix-spec/blob/main/data-definitions/sas-emoji.json) used for emoji verification, licensed Apache 2.0

View file

@ -1,15 +0,0 @@
# Security Policy
If you believe you have discovered a security vulnerability in this project, **do not open a public issue**.
To report a security issue responsibly, please go to the Security tab or visit:
https://github.com/krille-chan/fluffychat/security/advisories/new
The following information can help us address the issue:
- Clear steps to reproduce the vulnerability
- The software version you are using
- The affected platforms
We appreciate your report and will respond as quickly as possible. Please note that this project is maintained by volunteers and is provided without guarantees.

View file

@ -2,6 +2,7 @@ include: package:flutter_lints/flutter.yaml
linter:
rules:
- camel_case_types
- avoid_print
- constant_identifier_names
- prefer_final_locals
@ -9,68 +10,45 @@ linter:
- sort_pub_dependencies
- require_trailing_commas
- omit_local_variable_types
- cancel_subscriptions
- always_declare_return_types
- avoid_void_async
- no_adjacent_strings_in_list
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- avoid_bool_literals_in_conditional_expressions
- prefer_single_quotes
- prefer_const_declarations
- unnecessary_lambdas
- combinators_ordering
- noop_primitive_operations
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_to_list_in_spreads
- use_is_even_rather_than_modulo
- use_super_parameters
# Flutter specific:
- use_colored_box
analyzer:
plugins:
- dart_code_linter
errors:
todo: ignore
use_build_context_synchronously: ignore
exclude:
- lib/generated_plugin_registrant.dart
- lib/l10n/*.dart
dart_code_linter:
dart_code_metrics:
metrics:
cyclomatic-complexity: 20
number-of-arguments: 4
maximum-nesting-level: 5
source-lines-of-code: 50
maintainability-index: 40
rules:
# Dart specific:
- avoid-dynamic
- avoid-redundant-async
- avoid-unnecessary-type-assertions
- avoid-unnecessary-type-casts
- avoid-unrelated-type-assertions
- no-boolean-literal-compare
- no-empty-block
- prefer-conditional-expressions
- no-equal-then-else
- prefer-first
- prefer-last
- prefer-immediate-return
- prefer-enums-by-name
- avoid-unnecessary-conditionals
# TODO:
# - member-ordering
# - avoid-global-state
# - prefer-match-file-name
# - avoid-banned-imports:
# entries:
# - paths: ['some/folder/.*\.dart', 'another/folder/.*\.dart']
# deny: ['package:flutter/material.dart']
# message: 'Do not import Flutter Material Design library, we should not depend on it!'
# - no-magic-number:
# allowed: [-1, 0, 1,2,4,6,8,12,16,32,40,56,64]
# allow-only-once: true
# Flutter specific:
- prefer-media-query-direct-access
- no-magic-number:
severity: style
- avoid-late-keyword:
severity: style
- avoid-non-null-assertion:
severity: style
- avoid-unused-parameters
- binary-expression-operand-order
- avoid-unnecessary-setstate
- avoid-wrapping-in-padding
- prefer-correct-edge-insets-constructor
- avoid-returning-widgets
# TODO:
# - prefer-single-widget-per-file:
# ignore-private-widgets: true
# - prefer-extracting-callbacks
- prefer-const-border-radius
- prefer-single-widget-per-file:
ignore-private-widgets: true
- prefer-extracting-callbacks
metrics-exclude:
- test/**
rules-exclude:
- test/**
anti-patterns:
- long-method
- long-parameter-list

92
android/app/build.gradle Normal file
View file

@ -0,0 +1,92 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
//id "com.google.gms.google-services"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
compileSdkVersion 34
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "chat.fluffy.fluffychat"
minSdkVersion 21
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
signingConfigs {
release {
if (keystorePropertiesFile.exists()) {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
versionNameSuffix "-debug"
}
release {
signingConfig signingConfigs.release
}
}
// https://stackoverflow.com/a/77494454/8222484
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
}
}
flutter {
source '../..'
}
dependencies {
//implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698
implementation 'androidx.multidex:multidex:2.0.1'
}
configurations.all {
exclude group: 'com.google.android.gms'
}

View file

@ -1,95 +0,0 @@
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
if (file("google-services.json").exists()) {
apply(plugin = "com.google.gms.google-services")
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") // For flutter_local_notifications // Workaround for: https://github.com/MaikuB/flutter_local_notifications/issues/2286
implementation("androidx.core:core-ktx:1.17.0") // For Android Auto
}
// Workaround for https://pub.dev/packages/unifiedpush#the-build-fails-because-of-duplicate-classes
configurations.all {
// Use the latest version published: https://central.sonatype.com/artifact/com.google.crypto.tink/tink-android
val tink = "com.google.crypto.tink:tink-android:1.17.0"
// You can also use the library declaration catalog
// val tink = libs.google.tink
resolutionStrategy {
force(tink)
dependencySubstitution {
substitute(module("com.google.crypto.tink:tink")).using(module(tink))
}
}
}
android {
namespace = "chat.fluffy.fluffychat"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
signingConfigs {
create("release") {
keyAlias = "dummyAlias"
keyPassword = "dummyPassword"
storeFile = file("dummy.keystore")
storePassword = "dummyStorePassword"
}
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
signingConfigs.getByName("release").apply {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
defaultConfig {
applicationId = "chat.fluffy.fluffychat"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
ndk { // Workaround for https://github.com/flutter/flutter/issues/162153#issuecomment-2612443642
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64", "x86")
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
}
flutter {
source = "../.."
}

View file

@ -0,0 +1,46 @@
{
"project_info": {
"project_number": "865731724731",
"project_id": "fluffychat-ef3e8",
"storage_bucket": "fluffychat-ef3e8.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:865731724731:android:ec427b3b1dcd4a1e64309e",
"android_client_info": {
"package_name": "chat.fluffy.fluffychat"
}
},
"oauth_client": [
{
"client_id": "865731724731-od6969v178ul9970elgacpt936v5t7qg.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBLdZpGSPjcinikB4lAU6awW_h88NG17Sg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "865731724731-od6969v178ul9970elgacpt936v5t7qg.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "865731724731-ofdr7e6m04murgb1bvchlj9oaos0q5i3.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "im.fluffychat.app"
}
}
]
}
}
}
],
"configuration_version": "1"
}

View file

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
package="chat.fluffy.fluffychat" android:installLocation="auto">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
@ -17,10 +17,14 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"/>
<!-- To make app compatible with tablets -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-sdk
tools:overrideLibrary="io.wazo.callkeep, net.touchcapture.qr.flutterqr, com.cloudwebrtc.webrtc, org.webrtc, com.it_nomads.fluttersecurestorage, com.pichillilorenzo.flutter_inappwebview, com.example.video_compress, com.otaliastudios.transcoder, com.otaliastudios.opengl, com.kineapps.flutter_file_dialog, com.llfbandit.record, com.pravera.flutter_foreground_task"/>
@ -30,7 +34,6 @@
android:requestLegacyExternalStorage="true"
android:allowBackup="false"
android:fullBackupContent="false"
android:localeConfig="@xml/locale_config"
>
<activity
android:name=".MainActivity"
@ -106,7 +109,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="im.fluffychat" android:path="/login"/>
<data android:scheme="im.fluffychat" android:host="login"/>
</intent-filter>
</activity>
@ -121,13 +124,29 @@
android:foregroundServiceType="camera|microphone|mediaProjection">
</service>
<!-- From flutter_local_notifications package for notification actions -->
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />
<service android:name="io.wazo.callkeep.VoiceConnectionService"
android:label="Wazo"
android:foregroundServiceType="camera|microphone|mediaProjection"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
<!-- To make notifications available in Android Auto -->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<receiver android:name="org.unifiedpush.flutter.connector.UnifiedPushReceiver"
tools:replace="android:enabled"
android:enabled="false">
</receiver>
<receiver android:exported="false" android:enabled="true" android:name=".UnifiedPushReceiver">
<intent-filter>
<action android:name="org.unifiedpush.flutter.connector.MESSAGE"/>
<action android:name="org.unifiedpush.flutter.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.flutter.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.flutter.connector.REGISTRATION_FAILED" />
</intent-filter>
</receiver>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

View file

@ -2,9 +2,17 @@
import com.famedly.fcm_shared_isolate.FcmSharedIsolateService
import chat.fluffy.fluffychat.MainActivity
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.view.FlutterMain
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
class FcmPushService : FcmSharedIsolateService() {
override fun getEngine(): FlutterEngine {

View file

@ -4,11 +4,13 @@ import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import android.content.Context
import androidx.multidex.MultiDex
class MainActivity : FlutterActivity() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}

View file

@ -0,0 +1,23 @@
package chat.fluffy.fluffychat
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import org.unifiedpush.flutter.connector.UnifiedPushReceiver
import android.content.Context
class UnifiedPushReceiver : UnifiedPushReceiver() {
override fun getEngine(context: Context): FlutterEngine {
var engine = MainActivity.engine
if (engine == null) {
engine = MainActivity.provideEngine(context)
engine.localizationPlugin.sendLocalesToFlutter(
context.resources.configuration
)
engine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
}
return engine
}
}

View file

@ -1,3 +0,0 @@
<automotiveApp>
<uses name="notification" />
</automotiveApp>

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="ar"/>
<locale android:name="be"/>
<locale android:name="bn"/>
<locale android:name="bo"/>
<locale android:name="ca"/>
<locale android:name="cs"/>
<locale android:name="da"/>
<locale android:name="de"/>
<locale android:name="el"/>
<locale android:name="en"/>
<locale android:name="eo"/>
<locale android:name="es"/>
<locale android:name="et"/>
<locale android:name="eu"/>
<locale android:name="fa"/>
<locale android:name="fi"/>
<locale android:name="fil"/>
<locale android:name="fr"/>
<locale android:name="ga"/>
<locale android:name="gl"/>
<locale android:name="he"/>
<locale android:name="hi"/>
<locale android:name="hr"/>
<locale android:name="hu"/>
<locale android:name="ia"/>
<locale android:name="id"/>
<locale android:name="ie"/>
<locale android:name="it"/>
<locale android:name="ja"/>
<locale android:name="ka"/>
<locale android:name="kab"/>
<locale android:name="ko"/>
<locale android:name="lt"/>
<locale android:name="lv"/>
<locale android:name="nb"/>
<locale android:name="nl"/>
<locale android:name="pl"/>
<locale android:name="pt"/>
<locale android:name="pt"/>
<locale android:name="pt"/>
<locale android:name="ro"/>
<locale android:name="ru"/>
<locale android:name="sk"/>
<locale android:name="sl"/>
<locale android:name="sq"/>
<locale android:name="sr"/>
<locale android:name="sv"/>
<locale android:name="ta"/>
<locale android:name="te"/>
<locale android:name="th"/>
<locale android:name="tr"/>
<locale android:name="uk"/>
<locale android:name="uz"/>
<locale android:name="vi"/>
<locale android:name="yue"/>
<locale android:name="zh"/>
<locale android:name="zh"/>
</locale-config>

41
android/build.gradle Normal file
View file

@ -0,0 +1,41 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
// Workaround for building with Flutter 3.24
// See: https://github.com/flutter/flutter/issues/153281#issuecomment-2292201697
subprojects {
afterEvaluate { project ->
if (project.extensions.findByName("android") != null) {
Integer pluginCompileSdk = project.android.compileSdk
if (pluginCompileSdk != null && pluginCompileSdk < 31) {
project.logger.error(
"Warning: Overriding compileSdk version in Flutter plugin: "
+ project.name
+ " from "
+ pluginCompileSdk
+ " to 31 (to work around https://issuetracker.google.com/issues/199180389)."
+ "\nIf there is not a new version of " + project.name + ", consider filing an issue against "
+ project.name
+ " to increase their compileSdk to the latest (otherwise try updating to the latest version)."
)
project.android {
compileSdk 31
}
}
}
}
}
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View file

@ -1,21 +0,0 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View file

@ -0,0 +1,68 @@
FluffyChat 1.6.0 features a lot of bug fixes and improvements. The code base has been
simplified and the drawer on the chat list page got a come-back. Some new features like
the space hierarchy and session dump have been implemented.
- feat: Added monochrome entry for themed icon support in Android 13 (James Reilly)
- feat: Display timeline of messages in android notification (Christian Pauly)
- feat: Emoji related fixes (TheOneWithTheBraid)
- feat: Implement deleting pushers in app (Christian Pauly)
- feat: New material 3 design (Christian Pauly)
- feat: Redesign bootsstrap and offer secure storage support (Christian Pauly)
- feat: Send multiple images at once (Christian Pauly)
- feat: implement session dump (TheOneWithTheBraid)
- feat: implement space hierarchy (TheOneWithTheBraid)
- feat: introduce extended integration tests (TheOneWithTheBraid)
- feat: libhandy integration (TheOneWithTheBraid)
- fix: Clearing push triggered when only one room got seen (Christian Pauly)
- fix: Dont display loading dialog when adding reaction (Christian Pauly)
- fix: Follow up for spaces hierarchy (TheOneWithTheBraid)
- fix: Missing null checks in chat details view (Christian Pauly)
- fix: Non FCM Android builds crash on start (Christian Pauly)
- fix: Permission chooser dialog on iOS (Christian Pauly)
- fix: Set avatar on only single action available (Christian Pauly)
- fix: Sharing on iOS and iPad (Christian Pauly)
- fix: Unread bubble is invisible in dark mode (Christian Pauly)
- fix: appimage builds (TheOneWithTheBraid)
- fix: only use custom http client on android (Jayesh Nirve)
- fix: pass isrg cert to http client (Jayesh Nirve)
- refactor: Chat view (Christian Pauly)
- refactor: Encryption button (Christian Pauly)
- refactor: Remove duplicated imports (Christian Pauly)
- refactor: Remove legacy store (Christian Pauly)
- refactor: Remove presence status feature (Christian Pauly)
- refactor: Simplify MxcImage and replace CachedNetworkImage (Christian Pauly)
- refactor: Switch to Hive Collections DB (Christian Pauly)
- refactor: move start chat FAB to implementation file (TheOneWithTheBraid)
- Translated using Weblate (Catalan) (Alfonso Montero López)
- Translated using Weblate (Catalan) (Auri B.P)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (English) (Raatty)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Finnish) (Aminda Suomalainen)
- Translated using Weblate (Galician) (Xosé M)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Persian) (Amir Hossein Maher)
- Translated using Weblate (Polish) (Przemysław Romanik)
- Translated using Weblate (Russian) (Nikita Epifanov)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- chore: Add border to avatars (Christian Pauly)
- chore: Add fancy hero animations (Christian Pauly)
- chore: Adjust appbar design (Christian Pauly)
- chore: Adjust design (Christian Pauly)
- chore: Adjust search bar design (Christian Pauly)
- chore: Always display header elevation in chat (Christian Pauly)
- chore: Design follow up fixes (Christian Pauly)
- chore: Design follow up fixes (Christian Pauly)
- chore: Disable integration tests without runners (Krille Fear)
- chore: Enhance invitiation UX (Christian Pauly)
- chore: Make push helper more fail safe (Christian Pauly)
- chore: Make push helper more stable (Christian Pauly)
- chore: Minor design improvements (Christian Pauly)
- chore: Pinned events design (Christian Pauly)
- chore: Remove permission handler dependency and increase compileSdkVersion (Christian Pauly)
- chore: Switch to flutter 3.0.5 (Krille Fear)
- chore: Update SDK (Christian Pauly)
- chore: remove snapping sheet (TheOneWithTheBraid)

View file

@ -1,3 +0,0 @@
FluffyChat 2.5.0 introduces a new homeserver picker for onboarding,
better image compression performance and several smaller new features,
design adjustments and bug fixes.

View file

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx4608m
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
kotlin.jvm.target.validation.mode=IGNORE

View file

@ -1,6 +1,6 @@
#Mon Mar 17 08:36:03 CET 2025
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

26
android/settings.gradle Normal file
View file

@ -0,0 +1,26 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.1.2" apply false
id "org.jetbrains.kotlin.android" version "1.8.0" apply false
// id "com.google.gms.google-services" version "4.3.8" apply false
}
include ":app"

View file

@ -1,28 +0,0 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
if (file("app/google-services.json").exists()) {
id("com.google.gms.google-services") version "4.3.8" apply false
}
}
include(":app")

3
appimage/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
FluffyChat.AppDir
*.AppImage
*.AppImage.zsync

4
appimage/AppRun Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
cd "$(dirname "$0")"
exec ./fluffychat

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=FluffyChat
Comment=Matrix Client. Chat with your friends
Exec=AppRun
Icon=fluffychat
Terminal=false
Categories=Network;Chat;InstantMessaging;X-Matrix;

24
appimage/README.md Normal file
View file

@ -0,0 +1,24 @@
# FluffyChat AppImage
FluffyChat is provided as AppImage too. To Download, visit fluffychat.im.
## Building
- Ensure you install `appimagetool`
```shell
flutter build linux
# copy binaries to appimage dir
cp -r build/linux/{x64,arm64}/release/bundle appimage/FluffyChat.AppDir
cd appimage
# prepare AppImage files
cp FluffyChat.desktop FluffyChat.AppDir/
mkdir -p FluffyChat.AppDir/usr/share/icons
cp ../assets/logo.svg FluffyChat.AppDir/fluffychat.svg
cp AppRun FluffyChat.AppDir
# build the AppImage
appimagetool FluffyChat.AppDir
```

View file

2889
assets/l10n/intl_ar.arb Normal file

File diff suppressed because it is too large Load diff

1
assets/l10n/intl_be.arb Normal file
View file

@ -0,0 +1 @@
{}

1885
assets/l10n/intl_bn.arb Normal file

File diff suppressed because it is too large Load diff

1880
assets/l10n/intl_bo.arb Normal file

File diff suppressed because it is too large Load diff

2893
assets/l10n/intl_ca.arb Normal file

File diff suppressed because it is too large Load diff

2441
assets/l10n/intl_cs.arb Normal file

File diff suppressed because it is too large Load diff

2901
assets/l10n/intl_de.arb Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2824
assets/l10n/intl_en.arb Normal file

File diff suppressed because it is too large Load diff

1906
assets/l10n/intl_eo.arb Normal file

File diff suppressed because it is too large Load diff

2380
assets/l10n/intl_es.arb Normal file

File diff suppressed because it is too large Load diff

2898
assets/l10n/intl_et.arb Normal file

File diff suppressed because it is too large Load diff

2898
assets/l10n/intl_eu.arb Normal file

File diff suppressed because it is too large Load diff

2305
assets/l10n/intl_fa.arb Normal file

File diff suppressed because it is too large Load diff

2414
assets/l10n/intl_fi.arb Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2741
assets/l10n/intl_fr.arb Normal file

File diff suppressed because it is too large Load diff

2901
assets/l10n/intl_ga.arb Normal file

File diff suppressed because it is too large Load diff

2898
assets/l10n/intl_gl.arb Normal file

File diff suppressed because it is too large Load diff

1301
assets/l10n/intl_he.arb Normal file

File diff suppressed because it is too large Load diff

1887
assets/l10n/intl_hi.arb Normal file

File diff suppressed because it is too large Load diff

2716
assets/l10n/intl_hr.arb Normal file

File diff suppressed because it is too large Load diff

2807
assets/l10n/intl_hu.arb Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
"@notAnImage": {},
"remove": "Remover",
"@remove": {
"type": "String",
"type": "text",
"placeholders": {}
},
"importEmojis": "Importar emojis",
@ -20,41 +20,37 @@
"@replace": {},
"about": "A proposito de",
"@about": {
"type": "String",
"type": "text",
"placeholders": {}
},
"accept": "Acceptar",
"@accept": {
"type": "String",
"type": "text",
"placeholders": {}
},
"acceptedTheInvitation": "👍 {username} acceptava tu invitation",
"@acceptedTheInvitation": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"account": "Conto",
"@account": {
"type": "String",
"type": "text",
"placeholders": {}
},
"addEmail": "Adder email",
"@addEmail": {
"type": "String",
"type": "text",
"placeholders": {}
},
"supposedMxid": "Isto deberea esser {mxid}",
"@supposedMxid": {
"type": "String",
"type": "text",
"placeholders": {
"mxid": {
"type": "String"
}
"mxid": {}
}
},
"@custom": {}
}
}

2897
assets/l10n/intl_id.arb Normal file

File diff suppressed because it is too large Load diff

2124
assets/l10n/intl_ie.arb Normal file

File diff suppressed because it is too large Load diff

2897
assets/l10n/intl_it.arb Normal file

File diff suppressed because it is too large Load diff

2207
assets/l10n/intl_ja.arb Normal file

File diff suppressed because it is too large Load diff

744
assets/l10n/intl_ka.arb Normal file
View file

@ -0,0 +1,744 @@
{
"alias": "მეტსახელი",
"@alias": {
"type": "text",
"placeholders": {}
},
"appLockDescription": "პინკოდის გამოყენების გარეშე აპლიკაციის ბლოკირება",
"@appLockDescription": {},
"commandHint_hug": "მეგობრული ჩახუტვის გაგზავნა",
"@commandHint_hug": {},
"areYouSure": "დარწმუნებული ხართ?",
"@areYouSure": {
"type": "text",
"placeholders": {}
},
"areYouSureYouWantToLogout": "დარწმუნებული ხართ, რომ გამოსვლა გსურთ?",
"@areYouSureYouWantToLogout": {
"type": "text",
"placeholders": {}
},
"hugContent": "{senderName} მეგობრულად გეხუტება",
"@hugContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"askSSSSSign": "სხვა მომხმარებლის დადასტურებლად, გთხოვთ, ჩაწეროთ თქვენი ან საიდუმლო ფრაზა, ან აღდგენის გასაღები.",
"@askSSSSSign": {
"type": "text",
"placeholders": {}
},
"autoplayImages": "ანიმირებული სტიკერებისა და ემოჯების ავტომატური ჩართვა",
"@autoplayImages": {
"type": "text",
"placeholder": {}
},
"banFromChat": "ჩატიდან გაგდება და ბლოკირება",
"@banFromChat": {
"type": "text",
"placeholders": {}
},
"banned": "დაბლოკილია",
"@banned": {
"type": "text",
"placeholders": {}
},
"badServerLoginTypesException": "ამ სერვერს აქვს შესვლის მეთოდების მხარდაჭერა:\n{serverVersions}\nმაგრამ ამ აპლიკაციას აქვს მხარდაჭერა მხოლოდ:\n{supportedVersions}",
"@badServerLoginTypesException": {
"type": "text",
"placeholders": {
"serverVersions": {},
"supportedVersions": {}
}
},
"sendOnEnter": "გაგზავნა enter-ის დაჭერისას",
"@sendOnEnter": {},
"bannedUser": "{username} დაბლოკა {targetName}",
"@bannedUser": {
"type": "text",
"placeholders": {
"username": {},
"targetName": {}
}
},
"blockDevice": "მოწყობილების ბლოკირება",
"@blockDevice": {
"type": "text",
"placeholders": {}
},
"blocked": "დაბლოკილია",
"@blocked": {
"type": "text",
"placeholders": {}
},
"botMessages": "ბოტის შეტყობინებები",
"@botMessages": {
"type": "text",
"placeholders": {}
},
"cancel": "გაუქმება",
"@cancel": {
"type": "text",
"placeholders": {}
},
"changedTheHistoryVisibilityTo": "{username} შეცვალა ისტორიის ხილვადობა: {rules}",
"@changedTheHistoryVisibilityTo": {
"type": "text",
"placeholders": {
"username": {},
"rules": {}
}
},
"changedTheJoinRules": "{username} გაწევრიანების წესები შეცვალა",
"@changedTheJoinRules": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheProfileAvatar": "{username} შეცვალა პროფილის ფოტო",
"@changedTheProfileAvatar": {
"type": "text",
"placeholders": {
"username": {}
}
},
"chat": "ჩატი",
"@chat": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "პროფილის ფოტოს შეცვლა",
"@changeYourAvatar": {
"type": "text",
"placeholders": {}
},
"yourChatBackupHasBeenSetUp": "თქვენი ჩატის სარეზერვო საშუალება კონფიგურირებული იქნა.",
"@yourChatBackupHasBeenSetUp": {},
"channelCorruptedDecryptError": "დაშიფვრა დაზიანდა",
"@channelCorruptedDecryptError": {
"type": "text",
"placeholders": {}
},
"chatBackupDescription": "თქვენი ძველი შეტყობინებები დაცულია აღდგების გასაღებით. არ დაკარგოთ ის.",
"@chatBackupDescription": {
"type": "text",
"placeholders": {}
},
"commandHint_discardsession": "სესიის გაუქმება",
"@commandHint_discardsession": {
"type": "text",
"description": "Usage hint for the command /discardsession"
},
"commandHint_invite": "მოცემული მომხმარებლის მოწვევა ამ ოთახში",
"@commandHint_invite": {
"type": "text",
"description": "Usage hint for the command /invite"
},
"commandHint_plain": "არაფორმატირებული ტექსტის გაგზავნა",
"@commandHint_plain": {
"type": "text",
"description": "Usage hint for the command /plain"
},
"commandHint_send": "ტექსტის გაგზავნა",
"@commandHint_send": {
"type": "text",
"description": "Usage hint for the command /send"
},
"commandMissing": "{command} არაა ბრძანება.",
"@commandMissing": {
"type": "text",
"placeholders": {
"command": {}
},
"description": "State that {command} is not a valid /command."
},
"confirm": "დადასტურება",
"@confirm": {
"type": "text",
"placeholders": {}
},
"connect": "დაკავშირება",
"@connect": {
"type": "text",
"placeholders": {}
},
"countParticipants": "{count} მონაწილე",
"@countParticipants": {
"type": "text",
"placeholders": {
"count": {}
}
},
"createGroup": "ჯგუფის შექმნა",
"@createGroup": {},
"deactivateAccountWarning": "ეს გააუქმებს თქვენს ანგარიშს. ამის გაუქმება შეუძლებელია. დარწმუნებული ხართ?",
"@deactivateAccountWarning": {
"type": "text",
"placeholders": {}
},
"devices": "მოწყობილებები",
"@devices": {
"type": "text",
"placeholders": {}
},
"darkTheme": "ბნელი",
"@darkTheme": {
"type": "text",
"placeholders": {}
},
"chatPermissions": "ჩატის უფლებები",
"@chatPermissions": {},
"dateAndTimeOfDay": "{date}, {timeOfDay}",
"@dateAndTimeOfDay": {
"type": "text",
"placeholders": {
"date": {},
"timeOfDay": {}
}
},
"editRoomAliases": "ოთახის მეტსახელების შეცვლა",
"@editRoomAliases": {
"type": "text",
"placeholders": {}
},
"emoteExists": "ეს ემოცია უკვე არსებობს!",
"@emoteExists": {
"type": "text",
"placeholders": {}
},
"emoteInvalid": "ემოციის არასწორი მოკლე კოდი!",
"@emoteInvalid": {
"type": "text",
"placeholders": {}
},
"importNow": "იმპორტი",
"@importNow": {},
"importEmojis": "ემოჯის იმპორტი",
"@importEmojis": {},
"importFromZipFile": "იმპორტი .zip ფაილიდან",
"@importFromZipFile": {},
"exportEmotePack": "ემოციების .zip ფაილში ექსპორტი",
"@exportEmotePack": {},
"replace": "ჩანაცვლება",
"@replace": {},
"accept": "თანხმობა",
"@accept": {
"type": "text",
"placeholders": {}
},
"acceptedTheInvitation": "👍 {username} მიიღო მოწვევა",
"@acceptedTheInvitation": {
"type": "text",
"placeholders": {
"username": {}
}
},
"account": "ანგარიში",
"@account": {
"type": "text",
"placeholders": {}
},
"addEmail": "ელ.ფოსტის დამატება",
"@addEmail": {
"type": "text",
"placeholders": {}
},
"confirmMatrixId": "გთხოვთ, დაადასტუროთ თქვენი Matrix ID ანგარიშის წაშლისათვის.",
"@confirmMatrixId": {},
"addChatDescription": "ჩატის აღწერილობის დამატება...",
"@addChatDescription": {},
"addToSpace": "სივრცეში დამატება",
"@addToSpace": {},
"admin": "ადმინი",
"@admin": {
"type": "text",
"placeholders": {}
},
"all": "ყველა",
"@all": {
"type": "text",
"placeholders": {}
},
"allChats": "ყველა ჩატი",
"@allChats": {
"type": "text",
"placeholders": {}
},
"commandHint_cuddle": "ჩახუტების გაგზავნა",
"@commandHint_cuddle": {},
"answeredTheCall": "{senderName} უპასუხა ზარს",
"@answeredTheCall": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"anyoneCanJoin": "ყველას შეუძლია გაწევრიანება",
"@anyoneCanJoin": {
"type": "text",
"placeholders": {}
},
"appLock": "აპლიკაციის ბლოკირება",
"@appLock": {
"type": "text",
"placeholders": {}
},
"archive": "არქივი",
"@archive": {
"type": "text",
"placeholders": {}
},
"commandHint_googly": "გამოშტერილი თვალების გაგზავნა",
"@commandHint_googly": {},
"googlyEyesContent": "{senderName} გამოშტერილ თვალებს გიგზავნის",
"@googlyEyesContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"cuddleContent": "{senderName} გეხუტება",
"@cuddleContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"areGuestsAllowedToJoin": "შეუძლიათ თუ არა სტუმარ მომხმარებლებს გაწევრიანება",
"@areGuestsAllowedToJoin": {
"type": "text",
"placeholders": {}
},
"askVerificationRequest": "მიიღებთ {username} დადასტურების მოთხოვნას?",
"@askVerificationRequest": {
"type": "text",
"placeholders": {
"username": {}
}
},
"sendTypingNotifications": "წერის შეტყობინების გაგზავნა",
"@sendTypingNotifications": {},
"cantOpenUri": "ვერ იხსნება ბმული {uri}",
"@cantOpenUri": {
"type": "text",
"placeholders": {
"uri": {}
}
},
"changeDeviceName": "მოწყობილების გადარქმევა",
"@changeDeviceName": {
"type": "text",
"placeholders": {}
},
"changedTheChatAvatar": "{username} ჩატის ფოტო შეცვალა",
"@changedTheChatAvatar": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheChatDescriptionTo": "{username} ჩატის ახალი აღწერილობა დააყენა: '{description}'",
"@changedTheChatDescriptionTo": {
"type": "text",
"placeholders": {
"username": {},
"description": {}
}
},
"changedTheChatNameTo": "{username} ჩატი გადაარქვა: '{chatname}'",
"@changedTheChatNameTo": {
"type": "text",
"placeholders": {
"username": {},
"chatname": {}
}
},
"changedTheChatPermissions": "{username} ჩატის უფლებები შეცვალა",
"@changedTheChatPermissions": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheGuestAccessRules": "{username} შეცვალა სტუმრების წვდომის წესები",
"@changedTheGuestAccessRules": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheGuestAccessRulesTo": "{username} შეცვალა სტუმრების წვდომის წესები: {rules}",
"@changedTheGuestAccessRulesTo": {
"type": "text",
"placeholders": {
"username": {},
"rules": {}
}
},
"changedTheHistoryVisibility": "{username} შეცვალა ისტორიის ხილვადობა",
"@changedTheHistoryVisibility": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheJoinRulesTo": "{username} გაწევრიანების წესები შეცვალა: {joinRules}",
"@changedTheJoinRulesTo": {
"type": "text",
"placeholders": {
"username": {},
"joinRules": {}
}
},
"changedTheRoomAliases": "{username} ოთახის მეტსახელები შეცვალა",
"@changedTheRoomAliases": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheRoomInvitationLink": "{username} მოწვევის ბმული შეცვალა",
"@changedTheRoomInvitationLink": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changePassword": "პაროლის შეცვლა",
"@changePassword": {
"type": "text",
"placeholders": {}
},
"changeTheHomeserver": "სახლის სერვერის შეცვლა",
"@changeTheHomeserver": {
"type": "text",
"placeholders": {}
},
"changeTheme": "სტილის შეცვლა",
"@changeTheme": {
"type": "text",
"placeholders": {}
},
"changeTheNameOfTheGroup": "ჯგუფის გადარქმევა",
"@changeTheNameOfTheGroup": {
"type": "text",
"placeholders": {}
},
"chatBackup": "ჩატის სარეზერვო საშუალება",
"@chatBackup": {
"type": "text",
"placeholders": {}
},
"chatDetails": "ჩატის დეტალები",
"@chatDetails": {
"type": "text",
"placeholders": {}
},
"chatHasBeenAddedToThisSpace": "ჩატი დაემატა ამ სივრცეს",
"@chatHasBeenAddedToThisSpace": {},
"chats": "ჩატები",
"@chats": {
"type": "text",
"placeholders": {}
},
"chooseAStrongPassword": "ძლიერი პაროლი აარჩიეთ",
"@chooseAStrongPassword": {
"type": "text",
"placeholders": {}
},
"clearArchive": "არქივის გაწმენდა",
"@clearArchive": {},
"close": "დახურვა",
"@close": {
"type": "text",
"placeholders": {}
},
"commandHint_markasgroup": "აღნიშვნა, როგორც ჯგუფის",
"@commandHint_markasgroup": {},
"commandHint_ban": "მოცემული მომხმარებლის ბლოკირება ამ ოთახში",
"@commandHint_ban": {
"type": "text",
"description": "Usage hint for the command /ban"
},
"commandHint_clearcache": "­ქეშის გაწმენდა",
"@commandHint_clearcache": {
"type": "text",
"description": "Usage hint for the command /clearcache"
},
"commandHint_join": "მოცემულ ოთახში გაწევრიანება",
"@commandHint_join": {
"type": "text",
"description": "Usage hint for the command /join"
},
"commandHint_kick": "მოცემული მომხმარებლის წაშლა ამ ოთახიდან",
"@commandHint_kick": {
"type": "text",
"description": "Usage hint for the command /kick"
},
"commandHint_leave": "ამ ოთახიდან გასვლა",
"@commandHint_leave": {
"type": "text",
"description": "Usage hint for the command /leave"
},
"commandHint_me": "აღწერეთ თქვენი თავი",
"@commandHint_me": {
"type": "text",
"description": "Usage hint for the command /me"
},
"commandHint_unban": "ამ ოთახში მომხმარებლისგან ბლოკის მოხსნა",
"@commandHint_unban": {
"type": "text",
"description": "Usage hint for the command /unban"
},
"commandInvalid": "არასწორი ბრძანება",
"@commandInvalid": {
"type": "text"
},
"compareEmojiMatch": "გთხოვთ, შეადაროთ ეს ემოჯი",
"@compareEmojiMatch": {
"type": "text",
"placeholders": {}
},
"compareNumbersMatch": "გთხოვთ, შეადაროთ ეს რიცხვები",
"@compareNumbersMatch": {
"type": "text",
"placeholders": {}
},
"configureChat": "ჩატის კონფიგურაცია",
"@configureChat": {
"type": "text",
"placeholders": {}
},
"contactHasBeenInvitedToTheGroup": "კონტაქტი მოწვეული იქნა ჯგუფში",
"@contactHasBeenInvitedToTheGroup": {
"type": "text",
"placeholders": {}
},
"containsUserName": "შეიცავს სახელს",
"@containsUserName": {
"type": "text",
"placeholders": {}
},
"copiedToClipboard": "კოპირებულია ბუფერში",
"@copiedToClipboard": {
"type": "text",
"placeholders": {}
},
"copy": "კოპირება",
"@copy": {
"type": "text",
"placeholders": {}
},
"copyToClipboard": "კოპირება ბუფერში",
"@copyToClipboard": {
"type": "text",
"placeholders": {}
},
"couldNotDecryptMessage": "შეტყობინების გაშიფვრის შეცდომა: {error}",
"@couldNotDecryptMessage": {
"type": "text",
"placeholders": {
"error": {}
}
},
"create": "შექმნა",
"@create": {
"type": "text",
"placeholders": {}
},
"createdTheChat": "💬 {username} შექმნა ჩატი",
"@createdTheChat": {
"type": "text",
"placeholders": {
"username": {}
}
},
"createNewSpace": "ახალი სივრცე",
"@createNewSpace": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "ახლა აქტიურია",
"@currentlyActive": {
"type": "text",
"placeholders": {}
},
"dateWithoutYear": "{day}-{month}",
"@dateWithoutYear": {
"type": "text",
"placeholders": {
"month": {},
"day": {}
}
},
"dateWithYear": "{day}-{month}-{year}",
"@dateWithYear": {
"type": "text",
"placeholders": {
"year": {},
"month": {},
"day": {}
}
},
"delete": "წაშლა",
"@delete": {
"type": "text",
"placeholders": {}
},
"deleteAccount": "ანგარიშის წაშლა",
"@deleteAccount": {
"type": "text",
"placeholders": {}
},
"deleteMessage": "შეტყობინების წაშლა",
"@deleteMessage": {
"type": "text",
"placeholders": {}
},
"device": "მოწყობილება",
"@device": {
"type": "text",
"placeholders": {}
},
"deviceId": "მოწყობილების ID",
"@deviceId": {
"type": "text",
"placeholders": {}
},
"directChats": "პირდაპირი ჩატები",
"@directChats": {
"type": "text",
"placeholders": {}
},
"allRooms": "ყველა ჯგუფური ჩატები",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"downloadFile": "ფაილის ჩატვირთვა",
"@downloadFile": {
"type": "text",
"placeholders": {}
},
"edit": "რედაქტირება",
"@edit": {
"type": "text",
"placeholders": {}
},
"editBlockedServers": "ბლოკირებული სერვერების რედაქტირება",
"@editBlockedServers": {
"type": "text",
"placeholders": {}
},
"editRoomAvatar": "ოთახის ფოტოს შეცვლა",
"@editRoomAvatar": {
"type": "text",
"placeholders": {}
},
"emoteSettings": "ემოციების პარამეტრები",
"@emoteSettings": {
"type": "text",
"placeholders": {}
},
"globalChatId": "გლობალური ჩატის ID",
"@globalChatId": {},
"repeatPassword": "გაიმეორეთ პაროლი",
"@repeatPassword": {},
"notAnImage": "ფაილი არაა სურათი.",
"@notAnImage": {},
"remove": "წაშლა",
"@remove": {
"type": "text",
"placeholders": {}
},
"activatedEndToEndEncryption": "🔐 {username} გააქტიურა end to end დაშიფვრა",
"@activatedEndToEndEncryption": {
"type": "text",
"placeholders": {
"username": {}
}
},
"supposedMxid": "ეს უნდა იყოს {mxid}",
"@supposedMxid": {
"type": "text",
"placeholders": {
"mxid": {}
}
},
"about": "შესახებ",
"@about": {
"type": "text",
"placeholders": {}
},
"changedTheDisplaynameTo": "{username} შეცვალა ნაჩვენები სახელი: '{displayname}'",
"@changedTheDisplaynameTo": {
"type": "text",
"placeholders": {
"username": {},
"displayname": {}
}
},
"commandHint_create": "ცარიელი ჯგუფური ჩატის შექმნა\nგამოიენეთ --no-encryption გაშიფვრის გასათიშად",
"@commandHint_create": {
"type": "text",
"description": "Usage hint for the command /create"
},
"commandHint_dm": "პირდაპირი ჩატის დაწყება\nგამოიენეთ --no-encryption გაშიფვრის გასათიშად",
"@commandHint_dm": {
"type": "text",
"description": "Usage hint for the command /dm"
},
"commandHint_html": "HTML ფორმატირებული ტექსტის გაგზავნა",
"@commandHint_html": {
"type": "text",
"description": "Usage hint for the command /html"
},
"commandHint_myroomavatar": "თქვენი ფოტოს დაყენება ამ ოთახისათვის(mxc-uri-ს დახმარებით)",
"@commandHint_myroomavatar": {
"type": "text",
"description": "Usage hint for the command /myroomavatar"
},
"commandHint_myroomnick": "ამ ოთახისათვის ნაჩვენები სახელის დაყენება",
"@commandHint_myroomnick": {
"type": "text",
"description": "Usage hint for the command /myroomnick"
},
"commandHint_op": "მოცემული მომხმარებლისათვის უფლებების დონის დაყენება (ჩვეულებრივ: 50)",
"@commandHint_op": {
"type": "text",
"description": "Usage hint for the command /op"
},
"commandHint_react": "რეაქციის სახით პასუხის გაგზავნა",
"@commandHint_react": {
"type": "text",
"description": "Usage hint for the command /react"
},
"containsDisplayName": "ნაჩვენებ სახელს შეიცავს",
"@containsDisplayName": {
"type": "text",
"placeholders": {}
},
"contentHasBeenReported": "ეს კონტენტი გაგზავნილ იქნა სერვერის ადმინისტრატორებთან",
"@contentHasBeenReported": {
"type": "text",
"placeholders": {}
},
"defaultPermissionLevel": "ნაგულისხმევი უფლების დონე",
"@defaultPermissionLevel": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "ნაჩვენები სახელი შეიცვალა",
"@displaynameHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "ნაჩვენები სახელის შეცვლა",
"@editDisplayname": {
"type": "text",
"placeholders": {}
}
}

2870
assets/l10n/intl_ko.arb Normal file

File diff suppressed because it is too large Load diff

2218
assets/l10n/intl_lt.arb Normal file

File diff suppressed because it is too large Load diff

2876
assets/l10n/intl_lv.arb Normal file

File diff suppressed because it is too large Load diff

1653
assets/l10n/intl_nb.arb Normal file

File diff suppressed because it is too large Load diff

2595
assets/l10n/intl_nl.arb Normal file

File diff suppressed because it is too large Load diff

2894
assets/l10n/intl_pl.arb Normal file

File diff suppressed because it is too large Load diff

1903
assets/l10n/intl_pt.arb Normal file

File diff suppressed because it is too large Load diff

2709
assets/l10n/intl_pt_BR.arb Normal file

File diff suppressed because it is too large Load diff

1569
assets/l10n/intl_pt_PT.arb Normal file

File diff suppressed because it is too large Load diff

2303
assets/l10n/intl_ro.arb Normal file

File diff suppressed because it is too large Load diff

2896
assets/l10n/intl_ru.arb Normal file

File diff suppressed because it is too large Load diff

2117
assets/l10n/intl_sk.arb Normal file

File diff suppressed because it is too large Load diff

View file

@ -3,591 +3,573 @@
"@repeatPassword": {},
"about": "O aplikaciji",
"@about": {
"type": "String",
"type": "text",
"placeholders": {}
},
"accept": "Sprejmi",
"@accept": {
"type": "String",
"type": "text",
"placeholders": {}
},
"account": "Račun",
"@account": {
"type": "String",
"type": "text",
"placeholders": {}
},
"activatedEndToEndEncryption": "Uporabnik {username} je aktiviral šifriranje od konca do konca",
"@activatedEndToEndEncryption": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"addEmail": "Dodajte e-pošto",
"@addEmail": {
"type": "String",
"type": "text",
"placeholders": {}
},
"addToSpace": "Dodajte v prostor",
"@addToSpace": {},
"alias": "vzdevek",
"@alias": {
"type": "String",
"type": "text",
"placeholders": {}
},
"all": "Vse",
"@all": {
"type": "String",
"type": "text",
"placeholders": {}
},
"allChats": "Vsi klepeti",
"@allChats": {
"type": "String",
"type": "text",
"placeholders": {}
},
"answeredTheCall": "Oseba {senderName} je odgovorila na klic",
"@answeredTheCall": {
"type": "String",
"type": "text",
"placeholders": {
"senderName": {
"type": "String"
}
"senderName": {}
}
},
"anyoneCanJoin": "Pridruži se lahko vsak",
"@anyoneCanJoin": {
"type": "String",
"type": "text",
"placeholders": {}
},
"appLock": "Zaklepanje aplikacije",
"@appLock": {
"type": "String",
"type": "text",
"placeholders": {}
},
"askSSSSSign": "Če želite podpisati drugo osebo, vnesite geslo za varno trgovino ali obnovitveni ključ.",
"@askSSSSSign": {
"type": "String",
"type": "text",
"placeholders": {}
},
"askVerificationRequest": "Ali želite sprejeti to zahtevo za preverjanje od {username}?",
"@askVerificationRequest": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"autoplayImages": "Samodejno predvajajte animirane nalepke in čustva",
"@autoplayImages": {
"type": "String",
"type": "text",
"placeholder": {}
},
"badServerLoginTypesException": "Domači strežnik podpira vrste prijave:\n{serverVersions}\nToda ta aplikacija podpira samo:\n{supportedVersions}",
"@badServerLoginTypesException": {
"type": "String",
"type": "text",
"placeholders": {
"serverVersions": {
"type": "String"
},
"supportedVersions": {
"type": "String"
}
"serverVersions": {},
"supportedVersions": {}
}
},
"sendOnEnter": "Pošlji ob vstopu",
"@sendOnEnter": {},
"banFromChat": "Prepoved klepeta",
"@banFromChat": {
"type": "String",
"type": "text",
"placeholders": {}
},
"banned": "Prepovedano",
"@banned": {
"type": "String",
"type": "text",
"placeholders": {}
},
"bannedUser": "{username} je prepovedan v {targetName}",
"@bannedUser": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"targetName": {
"type": "String"
}
"username": {},
"targetName": {}
}
},
"blockDevice": "Blokirana naprava",
"@blockDevice": {
"type": "String",
"type": "text",
"placeholders": {}
},
"blocked": "Blokirano",
"@blocked": {
"type": "String",
"type": "text",
"placeholders": {}
},
"botMessages": "Botova sporočila",
"@botMessages": {
"type": "text",
"placeholders": {}
},
"cancel": "Prekliči",
"@cancel": {
"type": "String",
"type": "text",
"placeholders": {}
},
"cantOpenUri": "URI-ja {uri} ni mogoče odpreti",
"@cantOpenUri": {
"type": "String",
"type": "text",
"placeholders": {
"uri": {
"type": "String"
}
"uri": {}
}
},
"changedTheChatAvatar": "{username} je spremenil avatar za klepet",
"@changedTheChatAvatar": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheChatPermissions": "{username} je spremenila dovoljenja za klepet",
"@changedTheChatPermissions": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheDisplaynameTo": "{username} je spremenil svoje prikazno ime v: '{displayname}'",
"@changedTheDisplaynameTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"displayname": {
"type": "String"
}
"username": {},
"displayname": {}
}
},
"changedTheGuestAccessRules": "{username} je spremenila pravila dostopa za goste",
"@changedTheGuestAccessRules": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheGuestAccessRulesTo": "{username} je spremenila pravila dostopa za goste v: {rules}",
"@changedTheGuestAccessRulesTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"rules": {
"type": "String"
}
"username": {},
"rules": {}
}
},
"changedTheHistoryVisibilityTo": "{username} je spremenil vidnost zgodovine v: {rules}",
"@changedTheHistoryVisibilityTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"rules": {
"type": "String"
}
"username": {},
"rules": {}
}
},
"changedTheJoinRules": "{username} je spremenil pravila za pridružitev",
"@changedTheJoinRules": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheJoinRulesTo": "{username} je spremenila pravila pridružitve v: {joinRules}",
"@changedTheJoinRulesTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"joinRules": {
"type": "String"
}
"username": {},
"joinRules": {}
}
},
"changedTheProfileAvatar": "{username} je spremenil avatar",
"@changedTheProfileAvatar": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheRoomAliases": "{username} je spremenil vzdevke sobe",
"@changedTheRoomAliases": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changedTheRoomInvitationLink": "{username} je spremenil povezavo za povabilo",
"@changedTheRoomInvitationLink": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"changePassword": "Spremeni geslo",
"@changePassword": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changeTheHomeserver": "Spremenite domači strežnik",
"@changeTheHomeserver": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changeTheme": "Spremenite svoj slog",
"@changeTheme": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changeTheNameOfTheGroup": "Spremenite ime skupine",
"@changeTheNameOfTheGroup": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Spremenite svoj avatar",
"@changeYourAvatar": {
"type": "String",
"type": "text",
"placeholders": {}
},
"chat": "Klepet",
"@chat": {
"type": "String",
"type": "text",
"placeholders": {}
},
"yourChatBackupHasBeenSetUp": "Varnostna kopija klepeta je nastavljena.",
"@yourChatBackupHasBeenSetUp": {},
"chatBackup": "Varnostno kopiranje klepeta",
"@chatBackup": {
"type": "String",
"type": "text",
"placeholders": {}
},
"chatDetails": "Podrobnosti klepeta",
"@chatDetails": {
"type": "String",
"type": "text",
"placeholders": {}
},
"chatHasBeenAddedToThisSpace": "Klepet je bil dodan v ta prostor",
"@chatHasBeenAddedToThisSpace": {},
"chats": "Klepeti",
"@chats": {
"type": "String",
"type": "text",
"placeholders": {}
},
"chooseAStrongPassword": "Izberite močno geslo",
"@chooseAStrongPassword": {
"type": "String",
"type": "text",
"placeholders": {}
},
"clearArchive": "Počisti arhiv",
"@clearArchive": {},
"close": "Zapri",
"@close": {
"type": "String",
"type": "text",
"placeholders": {}
},
"commandHint_ban": "Izključi določenega uporabnika iz te sobe",
"@commandHint_ban": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /ban"
},
"commandHint_html": "Pošljite besedilo v obliki HTML",
"@commandHint_html": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /html"
},
"commandHint_invite": "Povabi danega uporabnika v to sobo",
"@commandHint_invite": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /invite"
},
"commandHint_join": "Pridružite se dani sobi",
"@commandHint_join": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /join"
},
"commandHint_kick": "Odstranite danega uporabnika iz te sobe",
"@commandHint_kick": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /kick"
},
"commandHint_me": "Opisi sebe",
"@commandHint_me": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /me"
},
"commandHint_myroomavatar": "Nastavite svojo sliko za to sobo",
"@commandHint_myroomavatar": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /myroomavatar"
},
"commandHint_op": "Nastavite raven moči danega uporabnika (privzeto: 50)",
"@commandHint_op": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /op"
},
"commandHint_react": "Pošljite odgovor kot reakcijo",
"@commandHint_react": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /react"
},
"commandHint_send": "Pošlji besedilo",
"@commandHint_send": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /send"
},
"commandHint_unban": "Prekliči izključitev določenega uporabnika iz te sobe",
"@commandHint_unban": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /unban"
},
"commandInvalid": "Ukaz ni veljaven",
"@commandInvalid": {
"type": "String"
"type": "text"
},
"commandMissing": "{command} is not a command.",
"@commandMissing": {
"type": "String",
"type": "text",
"placeholders": {
"command": {
"type": "String"
}
"command": {}
},
"description": "State that {command} is not a valid /command."
},
"compareEmojiMatch": "Primerjajte in se prepričajte, da se naslednji emoji ujemajo s tistimi iz druge naprave:",
"@compareEmojiMatch": {
"type": "String",
"type": "text",
"placeholders": {}
},
"compareNumbersMatch": "Primerjajte in se prepričajte, da se naslednje številke ujemajo s številkami druge naprave:",
"@compareNumbersMatch": {
"type": "String",
"type": "text",
"placeholders": {}
},
"configureChat": "Konfigurirajte klepet",
"@configureChat": {
"type": "String",
"type": "text",
"placeholders": {}
},
"confirm": "Potrdi",
"@confirm": {
"type": "text",
"placeholders": {}
},
"containsDisplayName": "Vsebuje prikazno ime",
"@containsDisplayName": {
"type": "text",
"placeholders": {}
},
"containsUserName": "Vsebuje uporabniško ime",
"@containsUserName": {
"type": "text",
"placeholders": {}
},
"archive": "Arhiv",
"@archive": {
"type": "String",
"type": "text",
"placeholders": {}
},
"areYouSure": "Ali si prepričan?",
"@areYouSure": {
"type": "String",
"type": "text",
"placeholders": {}
},
"acceptedTheInvitation": "{username} je sprejel povabilo",
"@acceptedTheInvitation": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"areYouSureYouWantToLogout": "Ali ste prepričani, da se želite odjaviti?",
"@areYouSureYouWantToLogout": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changedTheChatDescriptionTo": "{username} je spremenil opis klepeta v: '{description}'",
"@changedTheChatDescriptionTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"description": {
"type": "String"
}
"username": {},
"description": {}
}
},
"areGuestsAllowedToJoin": "Ali se lahko gostujoči uporabniki pridružijo",
"@areGuestsAllowedToJoin": {
"type": "String",
"type": "text",
"placeholders": {}
},
"admin": "Admin",
"@admin": {
"type": "String",
"type": "text",
"placeholders": {}
},
"badServerVersionsException": "Domači strežnik podpira različice Spec:\n{serverVersions}\nToda ta aplikacija podpira samo {supportedVersions}",
"@badServerVersionsException": {
"type": "text",
"placeholders": {
"serverVersions": {},
"supportedVersions": {}
}
},
"changedTheChatNameTo": "{username} je spremenil ime klepeta v: '{chatname}'",
"@changedTheChatNameTo": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
},
"chatname": {
"type": "String"
}
"username": {},
"chatname": {}
}
},
"changeDeviceName": "Spremenite ime naprave",
"@changeDeviceName": {
"type": "String",
"type": "text",
"placeholders": {}
},
"changedTheHistoryVisibility": "{username} je spremenila vidnost zgodovine",
"@changedTheHistoryVisibility": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"channelCorruptedDecryptError": "Šifriranje je poškodovano",
"@channelCorruptedDecryptError": {
"type": "String",
"type": "text",
"placeholders": {}
},
"contentHasBeenReported": "Vsebina je bila prijavljena skrbnikom strežnika",
"@contentHasBeenReported": {
"type": "String",
"type": "text",
"placeholders": {}
},
"chatBackupDescription": "Varnostna kopija klepeta je zavarovana z varnostnim ključem. Prosimo, pazite, da ga ne izgubite.",
"@chatBackupDescription": {
"type": "String",
"type": "text",
"placeholders": {}
},
"commandHint_myroomnick": "Nastavite prikazno ime za to sobo",
"@commandHint_myroomnick": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /myroomnick"
},
"connect": "Povežite se",
"@connect": {
"type": "text",
"placeholders": {}
},
"contactHasBeenInvitedToTheGroup": "Kontakt je bil povabljen v skupino",
"@contactHasBeenInvitedToTheGroup": {
"type": "String",
"type": "text",
"placeholders": {}
},
"commandHint_leave": "Zapusti to sobo",
"@commandHint_leave": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /leave"
},
"commandHint_plain": "Pošlji neformatirano besedilo",
"@commandHint_plain": {
"type": "String",
"type": "text",
"description": "Usage hint for the command /plain"
},
"copiedToClipboard": "Kopirano v odložišče",
"@copiedToClipboard": {
"type": "String",
"type": "text",
"placeholders": {}
},
"copy": "Kopiraj",
"@copy": {
"type": "String",
"type": "text",
"placeholders": {}
},
"copyToClipboard": "Kopiraj v odložišče",
"@copyToClipboard": {
"type": "String",
"type": "text",
"placeholders": {}
},
"couldNotDecryptMessage": "Sporočila ni bilo mogoče dešifrirati: {error}",
"@couldNotDecryptMessage": {
"type": "String",
"type": "text",
"placeholders": {
"error": {
"type": "String"
}
"error": {}
}
},
"countParticipants": "{count} udeležencev",
"@countParticipants": {
"type": "String",
"type": "text",
"placeholders": {
"count": {
"type": "int"
}
"count": {}
}
},
"create": "Ustvari",
"@create": {
"type": "String",
"type": "text",
"placeholders": {}
},
"createNewSpace": "Nov prostor",
"@createNewSpace": {
"type": "String",
"type": "text",
"placeholders": {}
},
"currentlyActive": "Trenutno aktiven",
"@currentlyActive": {
"type": "String",
"type": "text",
"placeholders": {}
},
"darkTheme": "Temno",
"@darkTheme": {
"type": "String",
"type": "text",
"placeholders": {}
},
"defaultPermissionLevel": "Privzeta raven dovoljenja",
"@defaultPermissionLevel": {
"type": "String",
"type": "text",
"placeholders": {}
},
"dateWithYear": "{day}-{month}-{year}",
"@dateWithYear": {
"type": "text",
"placeholders": {
"year": {},
"month": {},
"day": {}
}
},
"dateWithoutYear": "{month}-{day}",
"@dateWithoutYear": {
"type": "text",
"placeholders": {
"month": {},
"day": {}
}
},
"createdTheChat": "{username} je ustvaril klepet",
"@createdTheChat": {
"type": "String",
"type": "text",
"placeholders": {
"username": {
"type": "String"
}
"username": {}
}
},
"dateAndTimeOfDay": "{date}, {timeOfDay}",
"@dateAndTimeOfDay": {
"type": "String",
"type": "text",
"placeholders": {
"date": {
"type": "String"
},
"timeOfDay": {
"type": "String"
}
"date": {},
"timeOfDay": {}
}
},
"deactivateAccountWarning": "S tem boste deaktivirali vaš uporabniški račun. Tega ni mogoče razveljaviti! Ali si prepričan?",
"@deactivateAccountWarning": {
"type": "String",
"type": "text",
"placeholders": {}
}
}
}

1805
assets/l10n/intl_sr.arb Normal file

File diff suppressed because it is too large Load diff

2607
assets/l10n/intl_sv.arb Normal file

File diff suppressed because it is too large Load diff

1884
assets/l10n/intl_ta.arb Normal file

File diff suppressed because it is too large Load diff

1930
assets/l10n/intl_th.arb Normal file

File diff suppressed because it is too large Load diff

2836
assets/l10n/intl_tr.arb Normal file

File diff suppressed because it is too large Load diff

2898
assets/l10n/intl_uk.arb Normal file

File diff suppressed because it is too large Load diff

633
assets/l10n/intl_vi.arb Normal file
View file

@ -0,0 +1,633 @@
{
"@@last_modified": "2021-08-14 12:41:09.781172",
"about": "Giới thiệu",
"@about": {
"type": "text",
"placeholders": {}
},
"accept": "Đồng ý",
"@accept": {
"type": "text",
"placeholders": {}
},
"acceptedTheInvitation": "{username} đã đồng ý lời mời",
"@acceptedTheInvitation": {
"type": "text",
"placeholders": {
"username": {}
}
},
"account": "Tài khoản",
"@account": {
"type": "text",
"placeholders": {}
},
"activatedEndToEndEncryption": "{username} đã kích hoạt mã hóa đầu cuối 2 chiều",
"@activatedEndToEndEncryption": {
"type": "text",
"placeholders": {
"username": {}
}
},
"admin": "Quản trị viên",
"@admin": {
"type": "text",
"placeholders": {}
},
"alias": "bí danh",
"@alias": {
"type": "text",
"placeholders": {}
},
"answeredTheCall": "{senderName} đã trả lời cuộc gọi",
"@answeredTheCall": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"anyoneCanJoin": "Mọi người đều có thể gia nhập",
"@anyoneCanJoin": {
"type": "text",
"placeholders": {}
},
"archive": "Lưu trữ",
"@archive": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Khách vãng lai có được tham gia không",
"@areGuestsAllowedToJoin": {
"type": "text",
"placeholders": {}
},
"areYouSure": "Bạn chắc chứ?",
"@areYouSure": {
"type": "text",
"placeholders": {}
},
"blockDevice": "Thiết bị bị chặn",
"@blockDevice": {
"type": "text",
"placeholders": {}
},
"verified": "Đã xác thực",
"@verified": {
"type": "text",
"placeholders": {}
},
"transferFromAnotherDevice": "Chuyển từ thiết bị khác",
"@transferFromAnotherDevice": {
"type": "text",
"placeholders": {}
},
"showPassword": "Hiển thị mật khẩu",
"@showPassword": {
"type": "text",
"placeholders": {}
},
"pleaseFollowInstructionsOnWeb": "Vui lòng làm theo hướng dẫn trên trang web và bấm tiếp",
"@pleaseFollowInstructionsOnWeb": {
"type": "text",
"placeholders": {}
},
"noEncryptionForPublicRooms": "Bạn chỉ có thể kích hoạt mã hoá khi phòng này không mở",
"@noEncryptionForPublicRooms": {
"type": "text",
"placeholders": {}
},
"next": "Tiếp",
"@next": {
"type": "text",
"placeholders": {}
},
"everythingReady": "Mọi thứ đã sẵn sàng!",
"@everythingReady": {
"type": "text",
"placeholders": {}
},
"emoteSettings": "Cài đặt biểu tượng cảm xúc",
"@emoteSettings": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "Sửa tên hiển thị",
"@editDisplayname": {
"type": "text",
"placeholders": {}
},
"downloadFile": "Tải ảnh xuống",
"@downloadFile": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Tên hiển thị đã được thay đổi",
"@displaynameHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"devices": "Các thiết bị",
"@devices": {
"type": "text",
"placeholders": {}
},
"deviceId": "Mã xác định thiết bị",
"@deviceId": {
"type": "text",
"placeholders": {}
},
"device": "Thiết bị",
"@device": {
"type": "text",
"placeholders": {}
},
"deleteMessage": "Xoá tin nhắn",
"@deleteMessage": {
"type": "text",
"placeholders": {}
},
"deleteAccount": "Xoá tài khoản",
"@deleteAccount": {
"type": "text",
"placeholders": {}
},
"delete": "Xoá",
"@delete": {
"type": "text",
"placeholders": {}
},
"deactivateAccountWarning": "Việc này sẽ vô hiệu hoá tài khoản của bạn. Điều này không thể đảo ngược được! Bạn chắc là vẫn muốn tiếp tục chứ?",
"@deactivateAccountWarning": {
"type": "text",
"placeholders": {}
},
"dateWithYear": "{day}/{month}/{year}",
"@dateWithYear": {
"type": "text",
"placeholders": {
"year": {},
"month": {},
"day": {}
}
},
"dateWithoutYear": "{day}/{month}",
"@dateWithoutYear": {
"type": "text",
"placeholders": {
"month": {},
"day": {}
}
},
"dateAndTimeOfDay": "{date}, {timeOfDay}",
"@dateAndTimeOfDay": {
"type": "text",
"placeholders": {
"date": {},
"timeOfDay": {}
}
},
"currentlyActive": "Đang hoạt động",
"@currentlyActive": {
"type": "text",
"placeholders": {}
},
"createdTheChat": "{username} đã tạo cuộc trò chuyện",
"@createdTheChat": {
"type": "text",
"placeholders": {
"username": {}
}
},
"create": "Tạo",
"@create": {
"type": "text",
"placeholders": {}
},
"countParticipants": "{count} thành viên",
"@countParticipants": {
"type": "text",
"placeholders": {
"count": {}
}
},
"couldNotDecryptMessage": "Không thể giải mã tin nhắn: {error}",
"@couldNotDecryptMessage": {
"type": "text",
"placeholders": {
"error": {}
}
},
"copy": "Sao chép",
"@copy": {
"type": "text",
"placeholders": {}
},
"contactHasBeenInvitedToTheGroup": "Liên hệ đã được mời vào nhóm",
"@contactHasBeenInvitedToTheGroup": {
"type": "text",
"placeholders": {}
},
"connect": "Kết nối",
"@connect": {
"type": "text",
"placeholders": {}
},
"confirm": "Xác nhận",
"@confirm": {
"type": "text",
"placeholders": {}
},
"compareNumbersMatch": "So sánh và đảm bảo các số sau đây giống trên máy còn lại",
"@compareNumbersMatch": {
"type": "text",
"placeholders": {}
},
"compareEmojiMatch": "So sánh và đảm bảo các biểu tượng cảm xúc sau đây giống với các biểu tượng trên máy còn lại",
"@compareEmojiMatch": {
"type": "text",
"placeholders": {}
},
"close": "Đóng",
"@close": {
"type": "text",
"placeholders": {}
},
"chooseAStrongPassword": "Chọn một mật khẩu mạnh",
"@chooseAStrongPassword": {
"type": "text",
"placeholders": {}
},
"chatDetails": "Chi tiết cuộc trò chuyện",
"@chatDetails": {
"type": "text",
"placeholders": {}
},
"chatBackupDescription": "Bản sao lưu cuộc trò chuyện của bạn được bảo mật bằng một khoá bảo mật. Bạn đừng làm mất nó.",
"@chatBackupDescription": {
"type": "text",
"placeholders": {}
},
"chatBackup": "Sao lưu cuộc trò chuyện",
"@chatBackup": {
"type": "text",
"placeholders": {}
},
"chat": "Chat",
"@chat": {
"type": "text",
"placeholders": {}
},
"changeTheNameOfTheGroup": "Thay đổi tên nhóm",
"@changeTheNameOfTheGroup": {
"type": "text",
"placeholders": {}
},
"changeTheHomeserver": "Thay đổi máy chủ nhà",
"@changeTheHomeserver": {
"type": "text",
"placeholders": {}
},
"changePassword": "Thay đổi mật khẩu",
"@changePassword": {
"type": "text",
"placeholders": {}
},
"changedTheRoomInvitationLink": "{username} đã thay đổi đường dẫn mời",
"@changedTheRoomInvitationLink": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheRoomAliases": "{username} đã đổi địa chỉ phòng chat",
"@changedTheRoomAliases": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheProfileAvatar": "{username} đã thay đổi ảnh đại diện của mình",
"@changedTheProfileAvatar": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheGuestAccessRulesTo": "{username} đã thay đổi quy tắc truy cập đối với khách thành: {rules}",
"@changedTheGuestAccessRulesTo": {
"type": "text",
"placeholders": {
"username": {},
"rules": {}
}
},
"changedTheGuestAccessRules": "{username} đã thay đổi quy tắc truy cập đối với khách",
"@changedTheGuestAccessRules": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheChatPermissions": "{username} đã thay đổi quyền trong phòng chat",
"@changedTheChatPermissions": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changedTheChatNameTo": "{username} đã thay đổi tên phòng chat thành: '{chatname}'",
"@changedTheChatNameTo": {
"type": "text",
"placeholders": {
"username": {},
"chatname": {}
}
},
"changedTheChatDescriptionTo": "{username} đã thay đổi mô tả phòng chat thành: '{description}'",
"@changedTheChatDescriptionTo": {
"type": "text",
"placeholders": {
"username": {},
"description": {}
}
},
"changedTheChatAvatar": "{username} đã thay đổi ảnh phòng chat",
"@changedTheChatAvatar": {
"type": "text",
"placeholders": {
"username": {}
}
},
"changeDeviceName": "Thay đổi tên thiết bị",
"@changeDeviceName": {
"type": "text",
"placeholders": {}
},
"cancel": "Hủy",
"@cancel": {
"type": "text",
"placeholders": {}
},
"blocked": "Đã chặn",
"@blocked": {
"type": "text",
"placeholders": {}
},
"bannedUser": "{username} đã cấm {targetName}",
"@bannedUser": {
"type": "text",
"placeholders": {
"username": {},
"targetName": {}
}
},
"banned": "Đã bị cấm",
"@banned": {
"type": "text",
"placeholders": {}
},
"banFromChat": "Cấm khỏi cuộc trò chuyện",
"@banFromChat": {
"type": "text",
"placeholders": {}
},
"badServerVersionsException": "Máy chủ nhà hỗ trợ Spec phiên bản:\n{serverVerions}\nNhưng ứng dụng này chỉ hỗ trợ {supportedVersions}",
"@badServerVersionsException": {
"type": "text",
"placeholders": {
"serverVersions": {},
"supportedVersions": {}
}
},
"badServerLoginTypesException": "Máy chủ nhà hỗ trợ kiểu đăng nhập:\n{serverVersions}\nNhưng ứng dụng này chỉ hỗ trợ:\n{supportedVersions}",
"@badServerLoginTypesException": {
"type": "text",
"placeholders": {
"serverVersions": {},
"supportedVersions": {}
}
},
"askVerificationRequest": "Bạn có đồng ý yêu cầu chứng thực từ {username} không?",
"@askVerificationRequest": {
"type": "text",
"placeholders": {
"username": {}
}
},
"areYouSureYouWantToLogout": "Bạn có chắc bạn muốn đăng xuất không?",
"@areYouSureYouWantToLogout": {
"type": "text",
"placeholders": {}
},
"addEmail": "Thêm email",
"@addEmail": {
"type": "text",
"placeholders": {}
},
"notifications": "Thông báo",
"@notifications": {
"type": "text",
"placeholders": {}
},
"newGroup": "Nhóm mới",
"@newGroup": {},
"pleaseEnterANumber": "Hãy nhập số lớn hơn 0",
"@pleaseEnterANumber": {},
"newSpaceDescription": "Không gian cho phép bạn hợp nhất các cuộc trò chuyện của mình và xây dựng cộng đồng riêng tư hoặc công khai.",
"@newSpaceDescription": {},
"disableEncryptionWarning": "Vì lý do bảo mật, bạn không thể tắt tính năng mã hóa trong cuộc trò chuyện đã được bật tính năng này trước đó.",
"@disableEncryptionWarning": {},
"makeAdminDescription": "Khi bạn đặt người dùng này làm quản trị viên, bạn không thể hoàn tác việc này vì khi đó họ sẽ có quyền ngang bạn.",
"@makeAdminDescription": {},
"setColorTheme": "Chọn màu giao diện:",
"@setColorTheme": {},
"callingAccount": "Gọi tài khoản",
"@callingAccount": {},
"openLinkInBrowser": "Mở đường dẫn trong trình duyệt",
"@openLinkInBrowser": {},
"setTheme": "Chọn giao diện:",
"@setTheme": {},
"invitePrivateChat": "📨 Mời trò chuyện riêng tư",
"@invitePrivateChat": {},
"inviteGroupChat": "📨 Mời nhóm trò chuyện",
"@inviteGroupChat": {},
"addToSpace": "Thêm vào không gian",
"@addToSpace": {},
"importEmojis": "Nhập Biểu cảm",
"@importEmojis": {},
"importFromZipFile": "Nhập vào từ tệp .zip",
"@importFromZipFile": {},
"exportEmotePack": "Xuất gói biểu cảm bằng tệp .zip",
"@exportEmotePack": {},
"hideUnimportantStateEvents": "Ẩn các sự kiện không quan trọng",
"@hideUnimportantStateEvents": {},
"replace": "Thay thế",
"@replace": {},
"addChatDescription": "Thêm mô tả hội thoại...",
"@addChatDescription": {},
"report": "báo cáo",
"@report": {},
"remove": "Loại bỏ",
"@remove": {
"type": "text",
"placeholders": {}
},
"restricted": "Bị hạn chế",
"@restricted": {},
"newSpace": "Không gian mới",
"@newSpace": {},
"enterRoom": "Nhập phòng",
"@enterRoom": {},
"signInWithPassword": "Đăng nhập với mật khẩu",
"@signInWithPassword": {},
"all": "Tất cả",
"@all": {
"type": "text",
"placeholders": {}
},
"appLock": "Khoá ứng dụng",
"@appLock": {
"type": "text",
"placeholders": {}
},
"allChats": "Tất cả hội thoại",
"@allChats": {
"type": "text",
"placeholders": {}
},
"repeatPassword": "Nhập lại mật khẩu",
"@repeatPassword": {},
"confirmMatrixId": "Hãy xác nhận Matrix ID để xoá tài khoản.",
"@confirmMatrixId": {},
"supposedMxid": "Đây nên là {mxid}",
"@supposedMxid": {
"type": "text",
"placeholders": {
"mxid": {}
}
},
"noBackupWarning": "Cẩn thận! Nếu không bật sao lưu trò chuyện, bạn sẽ mất quyền truy cập vào tin nhắn được mã hóa của mình. Chúng tôi khuyên bạn nên bật sao lưu trò chuyện trước khi đăng xuất.",
"@noBackupWarning": {},
"doNotShowAgain": "Không hiện lại nữa",
"@doNotShowAgain": {},
"wasDirectChatDisplayName": "Hội thoại trống (từng là {oldDisplayName})",
"@wasDirectChatDisplayName": {
"type": "text",
"placeholders": {
"oldDisplayName": {}
}
},
"reportErrorDescription": "😭 Ôi. Có lỗi xảy ra. Bạn có thể báo cáo lỗi tới nhà phát triển nếu muốn.",
"@reportErrorDescription": {},
"profileNotFound": "Không tìm thấy người dùng này tại máy chủ. Có thể do lỗi kết nối hoặc người dùng không tồn tại.",
"@profileNotFound": {},
"banUserDescription": "Người dùng sẽ bị cấm khỏi cuộc trò chuyện và không thể tham gia lại cho tới khi được gỡ cấm.",
"@banUserDescription": {},
"learnMore": "Tìm hiểu thêm",
"@learnMore": {},
"incomingMessages": "Tin nhắn đến",
"@incomingMessages": {},
"encryptThisChat": "Mã hóa cuộc trò chuyện này",
"@encryptThisChat": {},
"noOtherDevicesFound": "Không tìm thấy thiết bị khác",
"@noOtherDevicesFound": {},
"fileIsTooBigForServer": "Máy chủ báo cáo rằng tệp tin quá lớn để gửi.",
"@fileIsTooBigForServer": {},
"signInWith": "Đăng nhập với {provider}",
"@signInWith": {
"type": "text",
"placeholders": {
"provider": {}
}
},
"notAnImage": "Không phải tệp ảnh.",
"@notAnImage": {},
"importNow": "Nhập vào",
"@importNow": {},
"allSpaces": "Tất cả không gian",
"@allSpaces": {},
"enterSpace": "Nhập không gian",
"@enterSpace": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Hãy thử lại sau hoặc chọn 1 máy chủ khác.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"jumpToLastReadMessage": "Đi tới tin nhắn đã đọc mới nhất",
"@jumpToLastReadMessage": {},
"commandHint_ignore": "Phớt lờ matrix ID này",
"@commandHint_ignore": {},
"appLockDescription": "Khoá ứng dụng khi không dùng bằng mã pin",
"@appLockDescription": {},
"notifyMeFor": "Bật thông báo cho",
"@notifyMeFor": {},
"settings": "Cài đặt",
"@settings": {
"type": "text",
"placeholders": {}
},
"sendSticker": "Gửi nhãn dán",
"@sendSticker": {
"type": "text",
"placeholders": {}
},
"fileHasBeenSavedAt": "Tệp đã được lưu tại {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"readUpToHere": "Đọc tới đây",
"@readUpToHere": {},
"jump": "Đi tới",
"@jump": {},
"callingPermissions": "Quyền gọi điện",
"@callingPermissions": {},
"numChats": "{number} cuộc hội thoại",
"@numChats": {
"type": "number",
"placeholders": {
"number": {}
}
},
"hidePresences": "Ẩn danh sách trạng thái?",
"@hidePresences": {},
"sorryThatsNotPossible": "Xin lỗi... không khả dụng",
"@sorryThatsNotPossible": {},
"reopenChat": "Mở lại cuộc trò chuyện",
"@reopenChat": {},
"wrongPinEntered": "Nhập sai mã pin! Thử lại sau {seconds} giây...",
"@wrongPinEntered": {
"type": "text",
"placeholders": {
"seconds": {}
}
},
"archiveRoomDescription": "Cuộc trò chuyện sẽ được chuyển tới mục lưu trữ. Người dùng khác sẽ thấy được bạn đã rời khỏi cuộc trò chuyện.",
"@archiveRoomDescription": {},
"kickUserDescription": "Người dùng bị đuổi khỏi cuộc trò chuyện nhưng không bị cấm. Trong các cuộc trò chuyện công khai, người dùng có thể vào lại bất cứ lúc nào.",
"@kickUserDescription": {},
"unbanUserDescription": "Người dùng sẽ có thể vào lại cuộc trò chuyện nếu họ thử.",
"@unbanUserDescription": {},
"pushNotificationsNotAvailable": "Thông báo đẩy không khả dụng",
"@pushNotificationsNotAvailable": {},
"invite": "Mời",
"@invite": {},
"invalidInput": "Dữ liệu nhập không hợp lệ!",
"@invalidInput": {},
"removeDevicesDescription": "Bạn sẽ đăng xuất khỏi thiết bị này và không nhận được tin nhắn nữa.",
"@removeDevicesDescription": {},
"noUsersFoundWithQuery": "Không tìm thấy người dùng nào với \"{query}\". Hãy kiểm tra xem bạn có nhập nhầm không.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"commandHint_unignore": "Bỏ phớt lờ matrix ID này",
"@commandHint_unignore": {},
"discover": "Khám phá",
"@discover": {},
"stickers": "Nhãn dán",
"@stickers": {},
"roomUpgradeDescription": "Cuộc trò chuyện sẽ được tạo lại với phiên bản phòng mới. Tất cả những người tham gia sẽ được thông báo rằng họ cần chuyển sang cuộc trò chuyện mới. Bạn có thể tìm hiểu thêm về các phiên bản phòng tại https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {}
}

2898
assets/l10n/intl_zh.arb Normal file

File diff suppressed because it is too large Load diff

2767
assets/l10n/intl_zh_Hant.arb Normal file

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
vodozemac_bindings_dart*

View file

@ -1,31 +1,10 @@
{
"applicationName": "FluffyChat",
"defaultHomeserver": "matrix.org",
"presetHomeserver": "",
"welcomeText": "",
"privacyUrl": "https://github.com/krille-chan/fluffychat/blob/main/PRIVACY.md",
"audioRecordingNumChannels": 1,
"audioRecordingAutoGain": true,
"audioRecordingEchoCancel": false,
"audioRecordingNoiseSuppress": true,
"audioRecordingBitRate": 64000,
"audioRecordingSamplingRate": 44100,
"renderHtml": true,
"fontSizeFactor": 1,
"hideRedactedEvents": false,
"hideUnknownEvents": true,
"separateChatTypes": false,
"autoplayImages": true,
"sendTypingNotifications": true,
"sendPublicReadReceipts": true,
"swipeRightToLeftToReply": true,
"sendOnEnter": false,
"showPresences": true,
"displayNavigationRail": false,
"experimentalVoip": false,
"shareKeysWith": "all",
"noEncryptionWarningShown": false,
"displayChatDetailsColumn": false,
"colorSchemeSeedInt": 4283835834,
"enableSoftLogout": false
"application_name": "FluffyChat",
"application_welcome_message": null,
"default_homeserver": "matrix.org",
"web_base_url": "https://fluffychat.im/web",
"privacy_url": "https://fluffychat.im/en/privacy.html",
"render_html": false,
"hide_redacted_events": false,
"hide_unknown_events": false
}

View file

@ -1,3 +0,0 @@
ignore:
- handy_window
- sqlcipher_flutter_libs

View file

@ -0,0 +1,2 @@
# im.fluffychat.Fluffychat
8b25b37b-f160-4350-b4f6-9a04554e8f9e

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