From 635f92f0c1c145a314b3dc41b1ef6c549620d567 Mon Sep 17 00:00:00 2001 From: MoonlightWave-12 <123384363+MoonlightWave-12@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:02:01 +0200 Subject: [PATCH 001/157] fix: Show WebP-images with a file-extension written in lower-case when choosing an image to send. --- lib/utils/file_selector.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index 8cc5e7552..9dfc5bdfa 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -59,8 +59,8 @@ enum FileSelectorType { extensions: ['png', 'PNG'], ), XTypeGroup( - label: 'WEBP', - extensions: ['WebP', 'WEBP'], + label: 'WebP', + extensions: ['webp', 'WebP', 'WEBP'], ), XTypeGroup( label: 'GIF', From 0c277571546cde05707b2f97c13f565b7f890ceb Mon Sep 17 00:00:00 2001 From: MoonlightWave-12 <123384363+MoonlightWave-12@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:30:42 +0200 Subject: [PATCH 002/157] feat: Show WebM-videos when choosing video-files for sending --- lib/utils/file_selector.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index 9dfc5bdfa..1b6dca6d7 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -92,6 +92,10 @@ enum FileSelectorType { label: 'MP4', extensions: ['mp4', 'MP4'], ), + XTypeGroup( + label: 'WebM', + extensions: ['webm', 'WebM', 'WEBM'], + ), XTypeGroup( label: 'AVI', extensions: ['avi', 'AVI'], From 380639496da72e267760837ad6a777448434d29f Mon Sep 17 00:00:00 2001 From: MoonlightWave-12 <123384363+MoonlightWave-12@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:31:34 +0200 Subject: [PATCH 003/157] feat: Show all supported image-/video-files when sending images or videos --- lib/utils/file_selector.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index 1b6dca6d7..13910cabb 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -50,6 +50,10 @@ enum FileSelectorType { any([], FileType.any, null), images( [ + XTypeGroup( + label: 'Images', + extensions: ['jpg', 'JPG', 'jpeg', 'JPEG', 'png', 'PNG', 'webp', 'WebP', 'WEBP', 'gif', 'GIF', 'bmp', 'BMP', 'tiff', 'TIFF', 'tif', 'TIF', 'heic', 'HEIC', 'svg', 'SVG'], + ), XTypeGroup( label: 'JPG', extensions: ['jpg', 'JPG', 'jpeg', 'JPEG'], @@ -88,6 +92,10 @@ enum FileSelectorType { ), videos( [ + XTypeGroup( + label: 'Videos', + extensions: ['mp4', 'MP4', 'avi', 'AVI', 'webm', 'WebM', 'WEBM', 'mov', 'MOV', 'mkv', 'MKV', 'wmv', 'WMV', 'flv', 'FLV', 'mpeg', 'MPEG', '3gp', '3GP', 'ogg', 'OGG'], + ), XTypeGroup( label: 'MP4', extensions: ['mp4', 'MP4'], From c4226f3fcf63fa0c5edb784f9cf5be844bdb5fec Mon Sep 17 00:00:00 2001 From: MoonlightWave-12 <123384363+MoonlightWave-12@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:47:06 +0200 Subject: [PATCH 004/157] fix: Change `PNGs` to `PNG` for consistency in file-selector for consistency. --- lib/utils/file_selector.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index 13910cabb..8cc9fbeac 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -59,7 +59,7 @@ enum FileSelectorType { extensions: ['jpg', 'JPG', 'jpeg', 'JPEG'], ), XTypeGroup( - label: 'PNGs', + label: 'PNG', extensions: ['png', 'PNG'], ), XTypeGroup( From 87572be6f78ee256fde715e0f5d22ca0c9f2b26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 10 May 2025 07:52:12 +0200 Subject: [PATCH 005/157] build: Changelog for 1.26.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- CHANGELOG.md | 16 ++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b7d34be..369c19f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## v1.26.1 + +Updates the Matrix Dart SDK to fix multiple issues where messages can be missing in the timeline. + +- feat: Reenable archive feature after various fixes landed in sdk (krille-chan) +- build: Update matrix dart sdk to 0.40.0 (Christian Kußowski) +- chore: Adjust new chat page design (krille-chan) +- chore: Display count of invited in members page (krille-chan) +- chore: Do not load timeline while sync processing (Krille) +- chore: Do not store event from push in database (Krille) +- chore: Only show joined members in members list by default (Krille) +- chore: Remove matrix notification workflow (Krille) +- fix(docs): escape tag (Sophie L) +- fix: Crash when opening settings in column mode from popup menu (krille-chan) +- refactor: Display thread messages like replies as a thread fallback (Krille) + ## v1.26.0 - feat: Add advanced configuration page (Krille) diff --git a/pubspec.yaml b/pubspec.yaml index 737a34b4a..270a2f1fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: fluffychat description: Chat with your friends. publish_to: none # On version bump also increase the build number for F-Droid -version: 1.26.0+3538 +version: 1.26.1+3539 environment: sdk: ">=3.0.0 <4.0.0" From 6c332c1d83bd8a3293905144521e42641f79d927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 10 May 2025 07:54:22 +0200 Subject: [PATCH 006/157] build: Remove arch from release ios shell script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- scripts/release-ios-testflight.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/release-ios-testflight.sh b/scripts/release-ios-testflight.sh index b175266e0..f3ca8b4fb 100755 --- a/scripts/release-ios-testflight.sh +++ b/scripts/release-ios-testflight.sh @@ -6,8 +6,8 @@ flutter pub get cd ios rm -rf Pods rm -f Podfile.lock -arch -x86_64 pod install -arch -x86_64 pod update +pod install +pod update cd .. flutter build ios --release cd ios From dc263094898f51a486611529c87b4a3287ba1047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 10 May 2025 10:17:59 +0200 Subject: [PATCH 007/157] chore: Display loading dialog when preparing voice message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- lib/pages/chat/chat.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 94c1ed431..1103c47fa 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -626,10 +626,19 @@ class ChatController extends State ); if (result == null) return; final audioFile = XFile(result.path); + + final bytesResult = await showFutureLoadingDialog( + context: context, + future: audioFile.readAsBytes, + ); + final bytes = bytesResult.result; + if (bytes == null) return; + final file = MatrixAudioFile( - bytes: await audioFile.readAsBytes(), + bytes: bytes, name: result.fileName ?? audioFile.path, ); + await room.sendFileEvent( file, inReplyTo: replyEvent, From 276003aeddb94e341a9dd58c94a872f1194acf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 10 May 2025 10:19:52 +0200 Subject: [PATCH 008/157] build: Update record package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- pubspec.lock | 40 +++++++++++-------- pubspec.yaml | 2 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 279cea329..1cf0fdfca 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -22,7 +22,7 @@ import just_audio import package_info_plus import pasteboard import path_provider_foundation -import record_darwin +import record_macos import share_plus import shared_preferences_foundation import sqflite @@ -51,7 +51,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 3515a3642..f1c892e49 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1551,58 +1551,66 @@ packages: dependency: "direct main" description: name: record - sha256: "4a5cf4d083d1ee49e0878823c4397d073f8eb0a775f31215d388e2bc47a9e867" + sha256: daeb3f9b3fea9797094433fe6e49a879d8e4ca4207740bc6dc7e4a58764f0817 url: "https://pub.dev" source: hosted - version: "5.1.2" + version: "6.0.0" record_android: dependency: transitive description: name: record_android - sha256: d7af0b3119725a0f561817c72b5f5eca4d7a76d441deef519ae04e4824c0734c + sha256: "36e009c3b83e034321a44a7683d95dd055162a231f95600f7da579dcc79701f9" url: "https://pub.dev" source: hosted - version: "1.2.6" - record_darwin: + version: "1.3.1" + record_ios: dependency: transitive description: - name: record_darwin - sha256: fe90d302acb1f3cee1ade5df9c150ca5cee33b48d8cdf1cf433bf577d7f00134 + name: record_ios + sha256: "73706ebbece6150654c9d6f57897cf9b622c581148304132ba85dba15df0fdfb" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.0.0" record_linux: dependency: transitive description: name: record_linux - sha256: "74d41a9ebb1eb498a38e9a813dd524e8f0b4fdd627270bda9756f437b110a3e3" + sha256: fcb5964a84292813de70d52253663c1caca00a15f849fb5d0fdf9b929b28a7b9 url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "1.0.0" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: "02240833fde16c33fcf2c589f3e08d4394b704761b4a3bb609d872ff3043fbbd" + url: "https://pub.dev" + source: hosted + version: "1.0.0" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "11f8b03ea8a0e279b0e306571dbe0db0202c0b8e866495c9fa1ad2281d5e4c15" + sha256: "8a575828733d4c3cb5983c914696f40db8667eab3538d4c41c50cbb79e722ef4" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" record_web: dependency: transitive description: name: record_web - sha256: "656b7a865f90651fab997c2a563364f5fd60a0b527d5dadbb915d62d84fc3867" + sha256: "654c08113961051dcb5427e63f56315ba47c0752781ba990dac9313d0ec23c70" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.6" record_windows: dependency: transitive description: name: record_windows - sha256: e653555aa3fda168aded7c34e11bd82baf0c6ac84e7624553def3c77ffefd36f + sha256: "26bfebc8899f4fa5b6b044089887dc42115820cd6a907bdf40c16e909e87de0a" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.5" retry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 270a2f1fe..a3fc68d61 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,7 +76,7 @@ dependencies: qr_code_scanner_plus: ^2.0.10+1 qr_image: ^1.0.0 receive_sharing_intent: ^1.8.1 - record: ^5.1.2 + record: ^6.0.0 scroll_to_index: ^3.0.1 share_plus: ^10.0.2 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 From 7cc341ac917c00a07d2db00e56b3cd11876cd132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 10 May 2025 13:49:01 +0200 Subject: [PATCH 009/157] feat: Background audio player MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- lib/pages/chat/events/audio_player.dart | 631 ++++++++++++++---------- lib/widgets/matrix.dart | 4 + 2 files changed, 362 insertions(+), 273 deletions(-) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 10d13362d..3d8aa0d11 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:async/async.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:just_audio/just_audio.dart'; import 'package:matrix/matrix.dart'; @@ -17,6 +18,7 @@ import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import '../../../utils/matrix_sdk_extensions/event_extension.dart'; +import '../../../widgets/matrix.dart'; class AudioPlayerWidget extends StatefulWidget { final Color color; @@ -24,8 +26,6 @@ class AudioPlayerWidget extends StatefulWidget { final double fontSize; final Event event; - static String? currentId; - static const int wavesCount = 40; const AudioPlayerWidget( @@ -43,48 +43,115 @@ class AudioPlayerWidget extends StatefulWidget { enum AudioPlayerStatus { notDownloaded, downloading, downloaded } class AudioPlayerState extends State { + static const double buttonSize = 36; + AudioPlayerStatus status = AudioPlayerStatus.notDownloaded; - AudioPlayer? audioPlayer; - StreamSubscription? onAudioPositionChanged; - StreamSubscription? onDurationChanged; - StreamSubscription? onPlayerStateChanged; - StreamSubscription? onPlayerError; - - String? statusText; - double currentPosition = 0; - double maxPosition = 1; - - MatrixFile? matrixFile; - File? audioFile; + late final MatrixState matrix; + late final List? _waveform; + late final String? _durationString; @override void dispose() { - if (audioPlayer?.playerState.playing == true) { - audioPlayer?.stop(); - } - onAudioPositionChanged?.cancel(); - onDurationChanged?.cancel(); - onPlayerStateChanged?.cancel(); - onPlayerError?.cancel(); - super.dispose(); - } + final audioPlayer = matrix.voiceMessageEventId.value != widget.event.eventId + ? null + : matrix.audioPlayer; + if (audioPlayer != null) { + if (audioPlayer.playing && !audioPlayer.isAtEndPosition) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(matrix.context).showMaterialBanner( + MaterialBanner( + padding: EdgeInsets.zero, + content: Row( + children: [ + StreamBuilder( + stream: audioPlayer.playerStateStream.asBroadcastStream(), + builder: (context, _) => IconButton( + onPressed: () { + if (audioPlayer.isAtEndPosition) { + audioPlayer.seek(Duration.zero); + } else if (audioPlayer.playing) { + audioPlayer.pause(); + } else { + audioPlayer.play(); + } + }, + icon: audioPlayer.playing && !audioPlayer.isAtEndPosition + ? const Icon(Icons.pause_outlined) + : const Icon(Icons.play_arrow_outlined), + ), + ), + Expanded( + child: StreamBuilder( + stream: audioPlayer.positionStream.asBroadcastStream(), + builder: (context, _) { + return Text( + '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + }, + ), + ), + ], + ), + actions: [ + IconButton( + onPressed: () { + audioPlayer.pause(); + audioPlayer.dispose(); + matrix.voiceMessageEventId.value = + matrix.audioPlayer = null; - void _startAction() { - if (status == AudioPlayerStatus.downloaded) { - _playAction(); - } else { - _downloadAction(); + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(matrix.context) + .clearMaterialBanners(); + }); + }, + icon: const Icon(Icons.close_outlined), + ), + ], + ), + ); + }); + return; + } + audioPlayer.pause(); + audioPlayer.dispose(); + matrix.voiceMessageEventId.value = matrix.audioPlayer = null; } } - Future _downloadAction() async { - if (status != AudioPlayerStatus.notDownloaded) return; + void _onButtonTap() async { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(matrix.context).clearMaterialBanners(); + }); + final currentPlayer = + matrix.voiceMessageEventId.value != widget.event.eventId + ? null + : matrix.audioPlayer; + if (currentPlayer != null) { + if (currentPlayer.isAtEndPosition) { + currentPlayer.seek(Duration.zero); + } else if (currentPlayer.playing) { + currentPlayer.pause(); + } else { + currentPlayer.play(); + } + return; + } + + matrix.voiceMessageEventId.value = widget.event.eventId; + matrix.audioPlayer + ?..stop() + ..dispose(); + File? file; + MatrixFile? matrixFile; + setState(() => status = AudioPlayerStatus.downloading); try { - final matrixFile = await widget.event.downloadAndDecryptAttachment(); - File? file; + matrixFile = await widget.event.downloadAndDecryptAttachment(); if (!kIsWeb) { final tempDir = await getTemporaryDirectory(); @@ -107,11 +174,8 @@ class AudioPlayerState extends State { } setState(() { - audioFile = file; - this.matrixFile = matrixFile; status = AudioPlayerStatus.downloaded; }); - _playAction(); } catch (e, s) { Logs().v('Could not download audio file', e, s); ScaffoldMessenger.of(context).showSnackBar( @@ -119,67 +183,47 @@ class AudioPlayerState extends State { content: Text(e.toLocalizedString(context)), ), ); + rethrow; } - } + if (!context.mounted) return; + if (matrix.voiceMessageEventId.value != widget.event.eventId) return; - void _playAction() async { - final audioPlayer = this.audioPlayer ??= AudioPlayer(); - if (AudioPlayerWidget.currentId != widget.event.eventId) { - if (AudioPlayerWidget.currentId != null) { - if (audioPlayer.playerState.playing) { - await audioPlayer.stop(); - setState(() {}); - } - } - AudioPlayerWidget.currentId = widget.event.eventId; - } - if (audioPlayer.playerState.playing) { - await audioPlayer.pause(); - return; - } else if (audioPlayer.position != Duration.zero) { - await audioPlayer.play(); - return; - } + final audioPlayer = matrix.audioPlayer = AudioPlayer(); - onAudioPositionChanged ??= audioPlayer.positionStream.listen((state) { - if (maxPosition <= 0) return; - setState(() { - statusText = - '${state.inMinutes.toString().padLeft(2, '0')}:${(state.inSeconds % 60).toString().padLeft(2, '0')}'; - currentPosition = state.inMilliseconds.toDouble(); - }); - if (state.inMilliseconds.toDouble() == maxPosition) { - audioPlayer.stop(); - audioPlayer.seek(null); - } - }); - onDurationChanged ??= audioPlayer.durationStream.listen((max) { - if (max == null || max == Duration.zero) return; - setState(() => maxPosition = max.inMilliseconds.toDouble()); - }); - onPlayerStateChanged ??= - audioPlayer.playingStream.listen((_) => setState(() {})); - final audioFile = this.audioFile; - if (audioFile != null) { - audioPlayer.setFilePath(audioFile.path); + if (file != null) { + audioPlayer.setFilePath(file.path); } else { - await audioPlayer.setAudioSource(MatrixFileAudioSource(matrixFile!)); + await audioPlayer.setAudioSource(MatrixFileAudioSource(matrixFile)); } + audioPlayer.play().onError( ErrorReporter(context, 'Unable to play audio message') .onErrorCallback, ); } - static const double buttonSize = 36; - - String? get _durationString { - final durationInt = widget.event.content - .tryGetMap('info') - ?.tryGet('duration'); - if (durationInt == null) return null; - final duration = Duration(milliseconds: durationInt); - return '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}'; + void _toggleSpeed() async { + final audioPlayer = matrix.audioPlayer; + if (audioPlayer == null) return; + switch (audioPlayer.speed) { + case 1.0: + await audioPlayer.setSpeed(1.25); + break; + case 1.25: + await audioPlayer.setSpeed(1.5); + break; + case 1.5: + await audioPlayer.setSpeed(2.0); + break; + case 2.0: + await audioPlayer.setSpeed(0.5); + break; + case 0.5: + default: + await audioPlayer.setSpeed(1.0); + break; + } + setState(() {}); } List? _getWaveform() { @@ -203,36 +247,26 @@ class AudioPlayerState extends State { return eventWaveForm.map((i) => i > 1024 ? 1024 : i).toList(); } - late final List? _waveform; - - void _toggleSpeed() async { - final audioPlayer = this.audioPlayer; - if (audioPlayer == null) return; - switch (audioPlayer.speed) { - case 1.0: - await audioPlayer.setSpeed(1.25); - break; - case 1.25: - await audioPlayer.setSpeed(1.5); - break; - case 1.5: - await audioPlayer.setSpeed(2.0); - break; - case 2.0: - await audioPlayer.setSpeed(0.5); - break; - case 0.5: - default: - await audioPlayer.setSpeed(1.0); - break; - } - setState(() {}); - } - @override void initState() { super.initState(); + matrix = Matrix.of(context); _waveform = _getWaveform(); + + if (matrix.voiceMessageEventId.value == widget.event.eventId && + matrix.audioPlayer != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(matrix.context).clearMaterialBanners(); + }); + } + + final durationInt = widget.event.content + .tryGetMap('info') + ?.tryGet('duration'); + if (durationInt != null) { + final duration = Duration(milliseconds: durationInt); + _durationString = duration.minuteSecondString; + } } @override @@ -240,186 +274,224 @@ class AudioPlayerState extends State { final theme = Theme.of(context); final waveform = _waveform; - final statusText = this.statusText ??= _durationString ?? '00:00'; - final audioPlayer = this.audioPlayer; + return ValueListenableBuilder( + valueListenable: matrix.voiceMessageEventId, + builder: (context, eventId, _) { + final audioPlayer = + eventId != widget.event.eventId ? null : matrix.audioPlayer; - final fileDescription = widget.event.fileDescription; + final fileDescription = widget.event.fileDescription; - final wavePosition = - (currentPosition / maxPosition) * AudioPlayerWidget.wavesCount; + return StreamBuilder( + stream: audioPlayer == null + ? null + : StreamGroup.merge([ + audioPlayer.positionStream.asBroadcastStream(), + audioPlayer.playerStateStream.asBroadcastStream(), + ]), + builder: (context, _) { + final maxPosition = + audioPlayer?.duration?.inMilliseconds.toDouble() ?? 1.0; + var currentPosition = + audioPlayer?.position.inMilliseconds.toDouble() ?? 0.0; + if (currentPosition > maxPosition) currentPosition = maxPosition; - return Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ConstrainedBox( - constraints: - const BoxConstraints(maxWidth: FluffyThemes.columnWidth), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: buttonSize, - height: buttonSize, - child: status == AudioPlayerStatus.downloading - ? CircularProgressIndicator( - strokeWidth: 2, - color: widget.color, - ) - : InkWell( - borderRadius: BorderRadius.circular(64), - onLongPress: () => widget.event.saveFile(context), - onTap: _startAction, - child: Material( - color: widget.color.withAlpha(64), - borderRadius: BorderRadius.circular(64), - child: Icon( - audioPlayer?.playerState.playing == true - ? Icons.pause_outlined - : Icons.play_arrow_outlined, - color: widget.color, - ), - ), - ), - ), - const SizedBox(width: 8), - Expanded( - child: Stack( - children: [ - if (waveform != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row( - children: [ - for (var i = 0; - i < AudioPlayerWidget.wavesCount; - i++) - Expanded( - child: Container( - height: 32, - alignment: Alignment.center, - child: Container( - margin: const EdgeInsets.symmetric( - horizontal: 1, - ), - decoration: BoxDecoration( - color: i < wavePosition - ? widget.color - : widget.color.withAlpha(128), - borderRadius: BorderRadius.circular(64), - ), - height: 32 * (waveform[i] / 1024), + final wavePosition = + (currentPosition / maxPosition) * AudioPlayerWidget.wavesCount; + + final statusText = audioPlayer == null + ? _durationString ?? '00:00' + : audioPlayer.position.minuteSecondString; + return Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: buttonSize, + height: buttonSize, + child: status == AudioPlayerStatus.downloading + ? CircularProgressIndicator( + strokeWidth: 2, + color: widget.color, + ) + : InkWell( + borderRadius: BorderRadius.circular(64), + onLongPress: () => + widget.event.saveFile(context), + onTap: _onButtonTap, + child: Material( + color: widget.color.withAlpha(64), + borderRadius: BorderRadius.circular(64), + child: Icon( + audioPlayer?.playing == true && + audioPlayer?.isAtEndPosition == + false + ? Icons.pause_outlined + : Icons.play_arrow_outlined, + color: widget.color, ), ), ), + ), + const SizedBox(width: 8), + Expanded( + child: Stack( + children: [ + if (waveform != null) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Row( + children: [ + for (var i = 0; + i < AudioPlayerWidget.wavesCount; + i++) + Expanded( + child: Container( + height: 32, + alignment: Alignment.center, + child: Container( + margin: + const EdgeInsets.symmetric( + horizontal: 1, + ), + decoration: BoxDecoration( + color: i < wavePosition + ? widget.color + : widget.color + .withAlpha(128), + borderRadius: + BorderRadius.circular(64), + ), + height: 32 * (waveform[i] / 1024), + ), + ), + ), + ], + ), + ), + SizedBox( + height: 32, + child: Slider( + thumbColor: widget.event.senderId == + widget.event.room.client.userID + ? theme.colorScheme.onPrimary + : theme.colorScheme.primary, + activeColor: waveform == null + ? widget.color + : Colors.transparent, + inactiveColor: waveform == null + ? widget.color.withAlpha(128) + : Colors.transparent, + max: maxPosition, + value: currentPosition, + onChanged: (position) => audioPlayer == null + ? _onButtonTap() + : audioPlayer.seek( + Duration( + milliseconds: position.round(), + ), + ), + ), + ), ], ), ), - SizedBox( - height: 32, - child: Slider( - thumbColor: widget.event.senderId == - widget.event.room.client.userID - ? theme.colorScheme.onPrimary - : theme.colorScheme.primary, - activeColor: waveform == null - ? widget.color - : Colors.transparent, - inactiveColor: waveform == null - ? widget.color.withAlpha(128) - : Colors.transparent, - max: maxPosition, - value: currentPosition, - onChanged: (position) => audioPlayer == null - ? _startAction() - : audioPlayer.seek( - Duration(milliseconds: position.round()), - ), - ), - ), - ], - ), - ), - const SizedBox(width: 8), - SizedBox( - width: 36, - child: Text( - statusText, - style: TextStyle( - color: widget.color, - fontSize: 12, - ), - ), - ), - const SizedBox(width: 8), - AnimatedCrossFade( - firstChild: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Icon( - Icons.mic_none_outlined, - color: widget.color, - ), - ), - secondChild: Material( - color: widget.color.withAlpha(64), - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - child: InkWell( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - onTap: _toggleSpeed, - child: SizedBox( - width: 32, - height: 20, - child: Center( + const SizedBox(width: 8), + SizedBox( + width: 36, child: Text( - '${audioPlayer?.speed.toString()}x', + statusText, style: TextStyle( color: widget.color, - fontSize: 9, + fontSize: 12, ), ), ), - ), + const SizedBox(width: 8), + AnimatedCrossFade( + firstChild: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon( + Icons.mic_none_outlined, + color: widget.color, + ), + ), + secondChild: Material( + color: widget.color.withAlpha(64), + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + child: InkWell( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + onTap: _toggleSpeed, + child: SizedBox( + width: 32, + height: 20, + child: Center( + child: Text( + '${audioPlayer?.speed.toString()}x', + style: TextStyle( + color: widget.color, + fontSize: 9, + ), + ), + ), + ), + ), + ), + alignment: Alignment.center, + crossFadeState: audioPlayer == null + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + duration: FluffyThemes.animationDuration, + ), + ], ), ), - alignment: Alignment.center, - crossFadeState: audioPlayer == null - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - duration: FluffyThemes.animationDuration, - ), - ], - ), - ), - if (fileDescription != null) ...[ - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, + if (fileDescription != null) ...[ + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + child: Linkify( + text: fileDescription, + textScaleFactor: + MediaQuery.textScalerOf(context).scale(1), + style: TextStyle( + color: widget.color, + fontSize: widget.fontSize, + ), + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: widget.linkColor, + fontSize: widget.fontSize, + decoration: TextDecoration.underline, + decorationColor: widget.linkColor, + ), + onOpen: (url) => + UrlLauncher(context, url.url).launchUrl(), + ), + ), + ], + ], ), - child: Linkify( - text: fileDescription, - textScaleFactor: MediaQuery.textScalerOf(context).scale(1), - style: TextStyle( - color: widget.color, - fontSize: widget.fontSize, - ), - options: const LinkifyOptions(humanize: false), - linkStyle: TextStyle( - color: widget.linkColor, - fontSize: widget.fontSize, - decoration: TextDecoration.underline, - decorationColor: widget.linkColor, - ), - onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), - ), - ), - ], - ], - ), + ); + }, + ); + }, ); } } @@ -443,3 +515,16 @@ class MatrixFileAudioSource extends StreamAudioSource { ); } } + +extension on AudioPlayer { + bool get isAtEndPosition { + final duration = this.duration; + if (duration == null) return true; + return position >= duration; + } +} + +extension on Duration { + String get minuteSecondString => + '${inMinutes.toString().padLeft(2, '0')}:${(inSeconds % 60).toString().padLeft(2, '0')}'; +} diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 125ec0b49..4b8f42979 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -10,6 +10,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; +import 'package:just_audio/just_audio.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; @@ -148,6 +149,9 @@ class MatrixState extends State with WidgetsBindingObserver { Client? _loginClientCandidate; + AudioPlayer? audioPlayer; + final ValueNotifier voiceMessageEventId = ValueNotifier(null); + Client getLoginClient() { if (widget.clients.isNotEmpty && !client.isLogged()) { return client; From a2e5a940bdd64877b63ce467aba1a7b35fe4365c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 10 May 2025 16:27:58 +0200 Subject: [PATCH 010/157] feat: Check markdown checkboxes in messages --- lib/pages/chat/events/html_message.dart | 70 ++++++++++++++++++++-- lib/pages/chat/events/message_content.dart | 6 ++ lib/utils/event_checkbox_extension.dart | 27 +++++++++ pubspec.lock | 9 +-- pubspec.yaml | 7 ++- 5 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 lib/utils/event_checkbox_extension.dart diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 7aca3154b..5e57b128c 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -8,7 +8,9 @@ import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as parser; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/utils/event_checkbox_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import '../../../utils/url_launcher.dart'; @@ -19,6 +21,8 @@ class HtmlMessage extends StatelessWidget { final double fontSize; final TextStyle linkStyle; final void Function(LinkableElement) onOpen; + final String? eventId; + final Set? checkboxCheckedEvents; const HtmlMessage({ super.key, @@ -28,6 +32,8 @@ class HtmlMessage extends StatelessWidget { required this.linkStyle, this.textColor = Colors.black, required this.onOpen, + this.eventId, + this.checkboxCheckedEvents, }); /// Keep in sync with: https://spec.matrix.org/latest/client-server-api/#mroommessage-msgtypes @@ -218,6 +224,24 @@ class HtmlMessage extends StatelessWidget { if (!{'ol', 'ul'}.contains(node.parent?.localName)) { continue block; } + final eventId = this.eventId; + + final isCheckbox = node.className == 'task-list-item'; + final checkboxIndex = isCheckbox + ? node.rootElement + .getElementsByClassName('task-list-item') + .indexOf(node) + + 1 + : null; + final checkedByReaction = !isCheckbox + ? null + : checkboxCheckedEvents?.firstWhereOrNull( + (event) => event.checkedCheckboxId == checkboxIndex, + ); + final staticallyChecked = !isCheckbox + ? false + : node.children.first.attributes['checked'] == 'true'; + return WidgetSpan( child: Padding( padding: EdgeInsets.only(left: fontSize), @@ -231,6 +255,42 @@ class HtmlMessage extends StatelessWidget { text: '${(node.parent?.nodes.whereType().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ', ), + if (node.className == 'task-list-item') + WidgetSpan( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: SizedBox.square( + dimension: fontSize, + child: Checkbox.adaptive( + checkColor: textColor, + side: BorderSide(color: textColor), + activeColor: textColor.withAlpha(64), + visualDensity: VisualDensity.compact, + value: + staticallyChecked || checkedByReaction != null, + onChanged: eventId == null || + checkboxIndex == null || + staticallyChecked || + !room.canSendDefaultMessages || + (checkedByReaction != null && + checkedByReaction.senderId != + room.client.userID) + ? null + : (_) => showFutureLoadingDialog( + context: context, + future: () => checkedByReaction != null + ? room.redactEvent( + checkedByReaction.eventId, + ) + : room.checkCheckbox( + eventId, + checkboxIndex, + ), + ), + ), + ), + ), + ), ..._renderWithLineBreaks( node.nodes, context, @@ -446,11 +506,9 @@ class HtmlMessage extends StatelessWidget { @override Widget build(BuildContext context) { + final element = parser.parse(html).body ?? dom.Element.html(''); return Text.rich( - _renderHtml( - parser.parse(html).body ?? dom.Element.html(''), - context, - ), + _renderHtml(element, context), style: TextStyle( fontSize: fontSize, color: textColor, @@ -516,3 +574,7 @@ extension on String { return colorValue == null ? null : Color(colorValue); } } + +extension on dom.Element { + dom.Element get rootElement => parent?.rootElement ?? this; +} diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 116aaf4ef..8c72cae3b 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../../config/app_config.dart'; +import '../../../utils/event_checkbox_extension.dart'; import '../../../utils/platform_infos.dart'; import '../../../utils/url_launcher.dart'; import '../../bootstrap/bootstrap_dialog.dart'; @@ -204,6 +205,11 @@ class MessageContent extends StatelessWidget { decorationColor: linkColor, ), onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), + eventId: event.eventId, + checkboxCheckedEvents: event.aggregatedEvents( + timeline, + EventCheckboxRoomExtension.relationshipType, + ), ), ); } diff --git a/lib/utils/event_checkbox_extension.dart b/lib/utils/event_checkbox_extension.dart new file mode 100644 index 000000000..cf3832ba6 --- /dev/null +++ b/lib/utils/event_checkbox_extension.dart @@ -0,0 +1,27 @@ +import 'package:matrix/matrix.dart'; + +extension EventCheckboxRoomExtension on Room { + static const String relationshipType = 'im.fluffychat.checkboxes'; + Future checkCheckbox( + String eventId, + int checkboxId, { + String? txid, + }) => + sendEvent( + { + 'm.relates_to': { + 'rel_type': relationshipType, + 'event_id': eventId, + 'checkbox_id': checkboxId, + }, + }, + type: EventTypes.Reaction, + txid: txid, + ); +} + +extension EventCheckboxExtension on Event { + int? get checkedCheckboxId => content + .tryGetMap('m.relates_to') + ?.tryGet('checkbox_id'); +} diff --git a/pubspec.lock b/pubspec.lock index f1c892e49..084b31477 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1150,10 +1150,11 @@ packages: matrix: dependency: "direct main" description: - name: matrix - sha256: "7d15fdbc760be7e40c58bb65e03baa8241b1e31db2bc67dab61883aabc083a85" - url: "https://pub.dev" - source: hosted + path: "." + ref: "krille/add-markdown-checkboxes" + resolved-ref: f3bb654ac2cda19bdd8a35fb46846018acd01a89 + url: "https://github.com/famedly/matrix-dart-sdk.git" + source: git version: "0.40.0" meta: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index a3fc68d61..61efd69b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,7 +61,10 @@ dependencies: just_audio: ^0.9.39 latlong2: ^0.9.1 linkify: ^5.0.0 - matrix: ^0.40.0 + matrix: + git: + url: https://github.com/famedly/matrix-dart-sdk.git + ref: krille/add-markdown-checkboxes mime: ^1.0.6 native_imaging: ^0.2.0 opus_caf_converter_dart: ^1.0.1 @@ -140,4 +143,4 @@ dependency_overrides: url: https://github.com/ThexXTURBOXx/flutter_web_auth_2.git ref: 3.x-without-v1 path: flutter_web_auth_2 - win32: 5.5.3 + win32: 5.5.3 \ No newline at end of file From 9da7a5704e17392f4ac33f60cd3c5feac0b0bb77 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 10 May 2025 16:54:29 +0200 Subject: [PATCH 011/157] feat: Create lists with checkboxes via + menu --- assets/l10n/intl_en.arb | 1 + lib/pages/chat/chat.dart | 49 +++++++++++++++++++++++++----- lib/pages/chat/chat_input_row.dart | 12 ++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index c1ffb8627..40a1c3f7e 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -696,6 +696,7 @@ } } }, + "checkList": "Check list", "countParticipants": "{count} participants", "@countParticipants": { "type": "String", diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 1103c47fa..dd1d16c91 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -275,13 +275,44 @@ class ChatController extends State ); } - KeyEventResult _shiftEnterKeyHandling(FocusNode node, KeyEvent evt) { + KeyEventResult _customEnterKeyHandling(FocusNode node, KeyEvent evt) { if (!HardwareKeyboard.instance.isShiftPressed && - evt.logicalKey.keyLabel == 'Enter') { + evt.logicalKey.keyLabel == 'Enter' && + (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)) { if (evt is KeyDownEvent) { send(); } return KeyEventResult.handled; + } else if (evt.logicalKey.keyLabel == 'Enter' && evt is KeyDownEvent) { + final currentLineNum = sendController.text + .substring( + 0, + sendController.selection.baseOffset, + ) + .split('\n') + .length - + 1; + final currentLine = sendController.text.split('\n')[currentLineNum]; + + for (final pattern in [ + '- [ ] ', + '- [x] ', + '* [ ] ', + '* [x] ', + '- ', + '* ', + '+ ', + ]) { + if (currentLine.startsWith(pattern)) { + if (currentLine == pattern) { + return KeyEventResult.ignored; + } + sendController.text += '\n$pattern'; + return KeyEventResult.handled; + } + } + + return KeyEventResult.ignored; } else { return KeyEventResult.ignored; } @@ -289,11 +320,7 @@ class ChatController extends State @override void initState() { - inputFocus = FocusNode( - onKeyEvent: (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile) - ? _shiftEnterKeyHandling - : null, - ); + inputFocus = FocusNode(onKeyEvent: _customEnterKeyHandling); scrollController.addListener(_updateScrollController); inputFocus.addListener(_inputFocusListener); @@ -1154,6 +1181,14 @@ class ChatController extends State if (choice == 'location') { sendLocationAction(); } + if (choice == 'checklist') { + if (sendController.text.isEmpty) { + sendController.text = '- [ ] '; + } else { + sendController.text += '\n- [ ] '; + } + inputFocus.requestFocus(); + } } unpinEvent(String eventId) async { diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 88860a77e..b07d2ae38 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -123,6 +123,18 @@ class ChatInputRow extends StatelessWidget { onSelected: controller.onAddPopupMenuButtonSelected, itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: 'checklist', + child: ListTile( + leading: CircleAvatar( + backgroundColor: theme.colorScheme.onPrimaryContainer, + foregroundColor: theme.colorScheme.primaryContainer, + child: const Icon(Icons.check_circle_outlined), + ), + title: Text(L10n.of(context).checkList), + contentPadding: const EdgeInsets.all(0), + ), + ), if (PlatformInfos.isMobile) PopupMenuItem( value: 'location', From b0227413100a20e15b5580160cafd59ca293db39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 11 May 2025 10:46:39 +0200 Subject: [PATCH 012/157] chore: Follow up background audio player MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- lib/pages/chat/events/audio_player.dart | 75 ++++++++++++------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 3d8aa0d11..764f437b1 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -1,23 +1,23 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - import 'package:async/async.dart'; -import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:just_audio/just_audio.dart'; -import 'package:matrix/matrix.dart'; -import 'package:opus_caf_converter_dart/opus_caf_converter_dart.dart'; -import 'package:path_provider/path_provider.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/error_reporter.dart'; import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:matrix/matrix.dart'; +import 'package:opus_caf_converter_dart/opus_caf_converter_dart.dart'; +import 'package:path_provider/path_provider.dart'; + import '../../../utils/matrix_sdk_extensions/event_extension.dart'; +import '../../../widgets/fluffy_chat_app.dart'; import '../../../widgets/matrix.dart'; class AudioPlayerWidget extends StatefulWidget { @@ -63,38 +63,35 @@ class AudioPlayerState extends State { ScaffoldMessenger.of(matrix.context).showMaterialBanner( MaterialBanner( padding: EdgeInsets.zero, - content: Row( - children: [ - StreamBuilder( - stream: audioPlayer.playerStateStream.asBroadcastStream(), - builder: (context, _) => IconButton( - onPressed: () { - if (audioPlayer.isAtEndPosition) { - audioPlayer.seek(Duration.zero); - } else if (audioPlayer.playing) { - audioPlayer.pause(); - } else { - audioPlayer.play(); - } - }, - icon: audioPlayer.playing && !audioPlayer.isAtEndPosition - ? const Icon(Icons.pause_outlined) - : const Icon(Icons.play_arrow_outlined), - ), + leading: StreamBuilder( + stream: audioPlayer.playerStateStream.asBroadcastStream(), + builder: (context, _) => IconButton( + onPressed: () { + if (audioPlayer.isAtEndPosition) { + audioPlayer.seek(Duration.zero); + } else if (audioPlayer.playing) { + audioPlayer.pause(); + } else { + audioPlayer.play(); + } + }, + icon: audioPlayer.playing && !audioPlayer.isAtEndPosition + ? const Icon(Icons.pause_outlined) + : const Icon(Icons.play_arrow_outlined), + ), + ), + content: StreamBuilder( + stream: audioPlayer.positionStream.asBroadcastStream(), + builder: (context, _) => GestureDetector( + onTap: () => FluffyChatApp.router.go( + '/rooms/${widget.event.room.id}?event=${widget.event.eventId}', ), - Expanded( - child: StreamBuilder( - stream: audioPlayer.positionStream.asBroadcastStream(), - builder: (context, _) { - return Text( - '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - }, - ), + child: Text( + '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}', + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - ], + ), ), actions: [ IconButton( From 55705d761db13a9c80f6afd79c429ebe4b2f33ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 11 May 2025 10:59:03 +0200 Subject: [PATCH 013/157] feat: Move videoplayer into multi image viewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fixes video thumbnails Signed-off-by: Christian Kußowski --- lib/pages/chat/events/audio_player.dart | 15 +- lib/pages/chat/events/message_content.dart | 7 +- lib/pages/chat/events/video_player.dart | 219 ++++++------------ lib/pages/image_viewer/image_viewer.dart | 8 +- lib/pages/image_viewer/image_viewer_view.dart | 61 +++-- lib/pages/image_viewer/video_player.dart | 152 ++++++++++++ lib/utils/platform_infos.dart | 3 + lib/widgets/mxc_image.dart | 2 +- 8 files changed, 292 insertions(+), 175 deletions(-) create mode 100644 lib/pages/image_viewer/video_player.dart diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 764f437b1..9dff85028 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -1,21 +1,22 @@ import 'dart:async'; import 'dart:io'; -import 'package:async/async.dart'; -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/utils/error_reporter.dart'; -import 'package:fluffychat/utils/file_description.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; + +import 'package:async/async.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:just_audio/just_audio.dart'; import 'package:matrix/matrix.dart'; import 'package:opus_caf_converter_dart/opus_caf_converter_dart.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/utils/error_reporter.dart'; +import 'package:fluffychat/utils/file_description.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; import '../../../utils/matrix_sdk_extensions/event_extension.dart'; import '../../../widgets/fluffy_chat_app.dart'; import '../../../widgets/matrix.dart'; diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 8c72cae3b..0ea0b1ba1 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -168,7 +168,12 @@ class MessageContent extends StatelessWidget { linkColor: linkColor, ); case MessageTypes.Video: - return EventVideoPlayer(event, textColor: textColor); + return EventVideoPlayer( + event, + textColor: textColor, + linkColor: linkColor, + timeline: timeline, + ); case MessageTypes.File: return MessageDownloadContent( event, diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index e47979e80..b71b8b29d 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -1,136 +1,51 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:chewie/chewie.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:universal_html/html.dart' as html; -import 'package:video_player/video_player.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pages/chat/events/image_bubble.dart'; import 'package:fluffychat/utils/file_description.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/blur_hash.dart'; -import '../../../utils/error_reporter.dart'; +import 'package:fluffychat/widgets/mxc_image.dart'; +import '../../image_viewer/image_viewer.dart'; -class EventVideoPlayer extends StatefulWidget { +class EventVideoPlayer extends StatelessWidget { final Event event; + final Timeline? timeline; final Color? textColor; final Color? linkColor; const EventVideoPlayer( this.event, { + this.timeline, this.textColor, this.linkColor, super.key, }); - @override - EventVideoPlayerState createState() => EventVideoPlayerState(); -} - -class EventVideoPlayerState extends State { - ChewieController? _chewieController; - VideoPlayerController? _videoPlayerController; - bool _isDownloading = false; - - // The video_player package only doesn't support Windows and Linux. - final _supportsVideoPlayer = - !PlatformInfos.isWindows && !PlatformInfos.isLinux; - - void _downloadAction() async { - if (!_supportsVideoPlayer) { - widget.event.saveFile(context); - return; - } - - setState(() => _isDownloading = true); - - try { - final videoFile = await widget.event.downloadAndDecryptAttachment(); - - // Dispose the controllers if we already have them. - _disposeControllers(); - late VideoPlayerController videoPlayerController; - - // Create the VideoPlayerController from the contents of videoFile. - if (kIsWeb) { - final blob = html.Blob([videoFile.bytes]); - final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob)); - videoPlayerController = VideoPlayerController.networkUrl(networkUri); - } else { - final tempDir = await getTemporaryDirectory(); - final fileName = Uri.encodeComponent( - widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, - ); - final file = File('${tempDir.path}/${fileName}_${videoFile.name}'); - if (await file.exists() == false) { - await file.writeAsBytes(videoFile.bytes); - } - videoPlayerController = VideoPlayerController.file(file); - } - _videoPlayerController = videoPlayerController; - - await videoPlayerController.initialize(); - - // Create a ChewieController on top. - _chewieController = ChewieController( - videoPlayerController: videoPlayerController, - useRootNavigator: !kIsWeb, - autoPlay: true, - autoInitialize: true, - ); - } on IOException catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(e.toLocalizedString(context)), - ), - ); - } catch (e, s) { - ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s); - } finally { - setState(() => _isDownloading = false); - } - } - - void _disposeControllers() { - _chewieController?.dispose(); - _videoPlayerController?.dispose(); - _chewieController = null; - _videoPlayerController = null; - } - - @override - void dispose() { - _disposeControllers(); - super.dispose(); - } - static const String fallbackBlurHash = 'L5H2EC=PM+yV0g-mq.wG9c010J}I'; @override Widget build(BuildContext context) { - final theme = Theme.of(context); + final supportsVideoPlayer = PlatformInfos.supportsVideoPlayer; - final hasThumbnail = widget.event.hasThumbnail; - final blurHash = (widget.event.infoMap as Map) + final blurHash = (event.infoMap as Map) .tryGet('xyz.amorgan.blurhash') ?? fallbackBlurHash; - final fileDescription = widget.event.fileDescription; - final textColor = widget.textColor; - final linkColor = widget.linkColor; + final fileDescription = event.fileDescription; + final infoMap = event.content.tryGetMap('info'); + final videoWidth = infoMap?.tryGet('w') ?? 400; + final videoHeight = infoMap?.tryGet('h') ?? 300; + const height = 300.0; + final width = videoWidth * (height / videoHeight); - const width = 300.0; + final durationInt = infoMap?.tryGet('duration'); + final duration = + durationInt == null ? null : Duration(milliseconds: durationInt); - final chewieController = _chewieController; return Column( mainAxisSize: MainAxisSize.min, spacing: 8, @@ -138,52 +53,66 @@ class EventVideoPlayerState extends State { Material( color: Colors.black, borderRadius: BorderRadius.circular(AppConfig.borderRadius), - child: SizedBox( - height: width, - child: chewieController != null - ? Center(child: Chewie(controller: chewieController)) - : Stack( - children: [ - if (hasThumbnail) - Center( - child: ImageBubble( - widget.event, - tapToView: false, - textColor: widget.textColor, - ), - ) - else - BlurHash( - blurhash: blurHash, - width: width, - height: width, - ), - Center( - child: IconButton( - style: IconButton.styleFrom( - backgroundColor: theme.colorScheme.surface, - ), - icon: _isDownloading - ? const SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ) - : _supportsVideoPlayer - ? const Icon(Icons.play_circle_outlined) - : const Icon(Icons.file_download_outlined), - tooltip: _isDownloading - ? L10n.of(context).loadingPleaseWait - : L10n.of(context).videoWithSize( - widget.event.sizeString ?? '?MB', - ), - onPressed: _isDownloading ? null : _downloadAction, + child: InkWell( + onTap: () => supportsVideoPlayer + ? showDialog( + context: context, + builder: (_) => ImageViewer( + event, + timeline: timeline, + outerContext: context, + ), + ) + : event.saveFile(context), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + child: SizedBox( + width: width, + height: height, + child: Stack( + children: [ + if (event.hasThumbnail) + MxcImage( + event: event, + isThumbnail: true, + width: width, + height: height, + fit: BoxFit.cover, + placeholder: (context) => BlurHash( + blurhash: blurHash, + width: width, + height: height, + fit: BoxFit.cover, + ), + ) + else + BlurHash( + blurhash: blurHash, + width: width, + height: height, + fit: BoxFit.cover, + ), + Center( + child: CircleAvatar( + child: supportsVideoPlayer + ? const Icon(Icons.play_arrow_outlined) + : const Icon(Icons.file_download_outlined), + ), + ), + if (duration != null) + Positioned( + bottom: 8, + left: 16, + child: Text( + '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}', + style: TextStyle( + color: Colors.white, + backgroundColor: Colors.black.withAlpha(32), ), ), - ], - ), + ), + ], + ), + ), ), ), if (fileDescription != null && textColor != null && linkColor != null) diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index 80f9b371d..d42a845a7 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -33,7 +33,13 @@ class ImageViewerController extends State { void initState() { super.initState(); allEvents = widget.timeline?.events - .where((event) => event.messageType == MessageTypes.Image) + .where( + (event) => { + MessageTypes.Image, + MessageTypes.Sticker, + if (PlatformInfos.supportsVideoPlayer) MessageTypes.Video, + }.contains(event.messageType), + ) .toList() .reversed .toList() ?? diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 997a9e6f9..983171be2 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; +import 'package:fluffychat/pages/image_viewer/video_player.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; @@ -75,27 +77,46 @@ class ImageViewerView extends StatelessWidget { child: PageView.builder( controller: controller.pageController, itemCount: controller.allEvents.length, - itemBuilder: (context, i) => InteractiveViewer( - minScale: 1.0, - maxScale: 10.0, - onInteractionEnd: controller.onInteractionEnds, - child: Center( - child: Hero( - tag: controller.allEvents[i].eventId, - child: GestureDetector( - // Ignore taps to not go back here: - onTap: () {}, - child: MxcImage( - key: ValueKey(controller.allEvents[i].eventId), - event: controller.allEvents[i], - fit: BoxFit.contain, - isThumbnail: false, - animated: true, + itemBuilder: (context, i) { + final event = controller.allEvents[i]; + switch (event.messageType) { + case MessageTypes.Video: + return Padding( + padding: const EdgeInsets.only(top: 52.0), + child: Center( + child: GestureDetector( + // Ignore taps to not go back here: + onTap: () {}, + child: EventVideoPlayer(event), + ), ), - ), - ), - ), - ), + ); + case MessageTypes.Image: + case MessageTypes.Sticker: + default: + return InteractiveViewer( + minScale: 1.0, + maxScale: 10.0, + onInteractionEnd: controller.onInteractionEnds, + child: Center( + child: Hero( + tag: event.eventId, + child: GestureDetector( + // Ignore taps to not go back here: + onTap: () {}, + child: MxcImage( + key: ValueKey(event.eventId), + event: event, + fit: BoxFit.contain, + isThumbnail: false, + animated: true, + ), + ), + ), + ), + ); + } + }, ), ), if (hovered && controller.canGoBack) diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart new file mode 100644 index 000000000..f355433ae --- /dev/null +++ b/lib/pages/image_viewer/video_player.dart @@ -0,0 +1,152 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:chewie/chewie.dart'; +import 'package:matrix/matrix.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:universal_html/html.dart' as html; +import 'package:video_player/video_player.dart'; + +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/widgets/blur_hash.dart'; +import '../../../utils/error_reporter.dart'; +import '../../widgets/mxc_image.dart'; + +class EventVideoPlayer extends StatefulWidget { + final Event event; + + const EventVideoPlayer( + this.event, { + super.key, + }); + + @override + EventVideoPlayerState createState() => EventVideoPlayerState(); +} + +class EventVideoPlayerState extends State { + ChewieController? _chewieController; + VideoPlayerController? _videoPlayerController; + + // The video_player package only doesn't support Windows and Linux. + final _supportsVideoPlayer = + !PlatformInfos.isWindows && !PlatformInfos.isLinux; + + void _downloadAction() async { + if (!_supportsVideoPlayer) { + widget.event.saveFile(context); + return; + } + + try { + final videoFile = await widget.event.downloadAndDecryptAttachment(); + + // Dispose the controllers if we already have them. + _disposeControllers(); + late VideoPlayerController videoPlayerController; + + // Create the VideoPlayerController from the contents of videoFile. + if (kIsWeb) { + final blob = html.Blob([videoFile.bytes]); + final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob)); + videoPlayerController = VideoPlayerController.networkUrl(networkUri); + } else { + final tempDir = await getTemporaryDirectory(); + final fileName = Uri.encodeComponent( + widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + ); + final file = File('${tempDir.path}/${fileName}_${videoFile.name}'); + if (await file.exists() == false) { + await file.writeAsBytes(videoFile.bytes); + } + videoPlayerController = VideoPlayerController.file(file); + } + _videoPlayerController = videoPlayerController; + + await videoPlayerController.initialize(); + + // Create a ChewieController on top. + _chewieController = ChewieController( + videoPlayerController: videoPlayerController, + useRootNavigator: !kIsWeb, + autoPlay: true, + autoInitialize: true, + looping: true, + ); + } on IOException catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toLocalizedString(context)), + ), + ); + } catch (e, s) { + ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s); + } + } + + void _disposeControllers() { + _chewieController?.dispose(); + _videoPlayerController?.dispose(); + _chewieController = null; + _videoPlayerController = null; + } + + @override + void dispose() { + _disposeControllers(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _downloadAction(); + }); + } + + static const String fallbackBlurHash = 'L5H2EC=PM+yV0g-mq.wG9c010J}I'; + + @override + Widget build(BuildContext context) { + final hasThumbnail = widget.event.hasThumbnail; + final blurHash = (widget.event.infoMap as Map) + .tryGet('xyz.amorgan.blurhash') ?? + fallbackBlurHash; + + const width = 300.0; + + final chewieController = _chewieController; + return chewieController != null + ? Center(child: Chewie(controller: chewieController)) + : Stack( + children: [ + Center( + child: hasThumbnail + ? MxcImage( + event: widget.event, + isThumbnail: true, + width: width, + fit: BoxFit.cover, + placeholder: (context) => BlurHash( + blurhash: blurHash, + width: width, + height: width, + fit: BoxFit.cover, + ), + ) + : BlurHash( + blurhash: blurHash, + width: width, + height: width, + ), + ), + const Center(child: CircularProgressIndicator.adaptive()), + ], + ); + } +} diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index c23548397..7e3364b87 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -29,6 +29,9 @@ abstract class PlatformInfos { static bool get usesTouchscreen => !isMobile; + static bool get supportsVideoPlayer => + !PlatformInfos.isWindows && !PlatformInfos.isLinux; + /// Web could also record in theory but currently only wav which is too large static bool get platformCanRecord => (isMobile || isMacOS); diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index bdc938e76..605d5078b 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -96,7 +96,7 @@ class _MxcImageState extends State { final data = await event.downloadAndDecryptAttachment( getThumbnail: widget.isThumbnail, ); - if (data.detectFileType is MatrixImageFile) { + if (data.detectFileType is MatrixImageFile || widget.isThumbnail) { if (!mounted) return; setState(() { _imageData = data.bytes; From f3c36e0b09365252ae097cfac4accdd0210bbffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 11 May 2025 11:31:18 +0200 Subject: [PATCH 014/157] chore: Follow up videoplayer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian Kußowski --- lib/pages/chat/events/video_player.dart | 75 ++++++++++++------------ lib/pages/image_viewer/video_player.dart | 65 ++++++++++++-------- 2 files changed, 79 insertions(+), 61 deletions(-) diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index b71b8b29d..856bda50c 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -68,49 +68,52 @@ class EventVideoPlayer extends StatelessWidget { child: SizedBox( width: width, height: height, - child: Stack( - children: [ - if (event.hasThumbnail) - MxcImage( - event: event, - isThumbnail: true, - width: width, - height: height, - fit: BoxFit.cover, - placeholder: (context) => BlurHash( + child: Hero( + tag: event.eventId, + child: Stack( + children: [ + if (event.hasThumbnail) + MxcImage( + event: event, + isThumbnail: true, + width: width, + height: height, + fit: BoxFit.cover, + placeholder: (context) => BlurHash( + blurhash: blurHash, + width: width, + height: height, + fit: BoxFit.cover, + ), + ) + else + BlurHash( blurhash: blurHash, width: width, height: height, fit: BoxFit.cover, ), - ) - else - BlurHash( - blurhash: blurHash, - width: width, - height: height, - fit: BoxFit.cover, - ), - Center( - child: CircleAvatar( - child: supportsVideoPlayer - ? const Icon(Icons.play_arrow_outlined) - : const Icon(Icons.file_download_outlined), - ), - ), - if (duration != null) - Positioned( - bottom: 8, - left: 16, - child: Text( - '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}', - style: TextStyle( - color: Colors.white, - backgroundColor: Colors.black.withAlpha(32), - ), + Center( + child: CircleAvatar( + child: supportsVideoPlayer + ? const Icon(Icons.play_arrow_outlined) + : const Icon(Icons.file_download_outlined), ), ), - ], + if (duration != null) + Positioned( + bottom: 8, + left: 16, + child: Text( + '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}', + style: TextStyle( + color: Colors.white, + backgroundColor: Colors.black.withAlpha(32), + ), + ), + ), + ], + ), ), ), ), diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index f355433ae..0962306e5 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -70,13 +70,15 @@ class EventVideoPlayerState extends State { await videoPlayerController.initialize(); // Create a ChewieController on top. - _chewieController = ChewieController( - videoPlayerController: videoPlayerController, - useRootNavigator: !kIsWeb, - autoPlay: true, - autoInitialize: true, - looping: true, - ); + setState(() { + _chewieController = ChewieController( + videoPlayerController: videoPlayerController, + useRootNavigator: !kIsWeb, + autoPlay: true, + autoInitialize: true, + looping: true, + ); + }); } on IOException catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -117,33 +119,46 @@ class EventVideoPlayerState extends State { final blurHash = (widget.event.infoMap as Map) .tryGet('xyz.amorgan.blurhash') ?? fallbackBlurHash; - - const width = 300.0; + final infoMap = widget.event.content.tryGetMap('info'); + final videoWidth = infoMap?.tryGet('w') ?? 400; + final videoHeight = infoMap?.tryGet('h') ?? 300; + final height = MediaQuery.of(context).size.height - 52; + final width = videoWidth * (height / videoHeight); final chewieController = _chewieController; return chewieController != null - ? Center(child: Chewie(controller: chewieController)) + ? Center( + child: SizedBox( + width: width, + height: height, + child: Chewie(controller: chewieController), + ), + ) : Stack( children: [ Center( - child: hasThumbnail - ? MxcImage( - event: widget.event, - isThumbnail: true, - width: width, - fit: BoxFit.cover, - placeholder: (context) => BlurHash( + child: Hero( + tag: widget.event.eventId, + child: hasThumbnail + ? MxcImage( + event: widget.event, + isThumbnail: true, + width: width, + height: height, + fit: BoxFit.cover, + placeholder: (context) => BlurHash( + blurhash: blurHash, + width: width, + height: height, + fit: BoxFit.cover, + ), + ) + : BlurHash( blurhash: blurHash, width: width, - height: width, - fit: BoxFit.cover, + height: height, ), - ) - : BlurHash( - blurhash: blurHash, - width: width, - height: width, - ), + ), ), const Center(child: CircularProgressIndicator.adaptive()), ], From 2fb8156718d7f26abc9c9a57e96336fb3ff10e7e Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 11 May 2025 14:25:45 +0200 Subject: [PATCH 015/157] chore: Follow up videoplayer --- lib/pages/image_viewer/video_player.dart | 2 +- pubspec.lock | 4 ++-- pubspec.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index 0962306e5..b85d6799d 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -73,7 +73,7 @@ class EventVideoPlayerState extends State { setState(() { _chewieController = ChewieController( videoPlayerController: videoPlayerController, - useRootNavigator: !kIsWeb, + showControlsOnInitialize: false, autoPlay: true, autoInitialize: true, looping: true, diff --git a/pubspec.lock b/pubspec.lock index 084b31477..1590cc319 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -178,10 +178,10 @@ packages: dependency: "direct main" description: name: chewie - sha256: "0bf6f7692cb65f7b8f59a2a17025b9cbe8f75ab4251e66161a4fc86162475fb6" + sha256: "4d9554a8f87cc2dc6575dfd5ad20a4375015a29edd567fd6733febe6365e2566" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.3" cli_util: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 61efd69b9..5e5bae322 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: async: ^2.11.0 badges: ^3.1.2 blurhash_dart: ^1.2.1 - chewie: ^1.11.0 + chewie: ^1.11.3 collection: ^1.18.0 cross_file: ^0.3.4+2 cupertino_icons: any @@ -93,7 +93,7 @@ dependencies: universal_html: ^2.2.4 url_launcher: ^6.2.5 video_compress: ^3.1.4 - video_player: ^2.9.2 + video_player: ^2.9.5 wakelock_plus: ^1.2.2 webrtc_interface: ^1.0.13 From 51449fc7244ce20a24de19814a3883e52bb9cb87 Mon Sep 17 00:00:00 2001 From: MoonlightWave-12 <123384363+MoonlightWave-12@users.noreply.github.com> Date: Mon, 5 May 2025 21:05:23 +0200 Subject: [PATCH 016/157] chore: Format lib/utils/file_selector.dart --- lib/utils/file_selector.dart | 143 ++++++++++++++++------------------- 1 file changed, 65 insertions(+), 78 deletions(-) diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index 8cc9fbeac..fd35891af 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -30,17 +30,11 @@ Future> selectFiles( if (allowMultiple) { return await AppLock.of(context).pauseWhile( - openFiles( - confirmButtonText: title, - acceptedTypeGroups: type.groups, - ), + openFiles(confirmButtonText: title, acceptedTypeGroups: type.groups), ); } final file = await AppLock.of(context).pauseWhile( - openFile( - confirmButtonText: title, - acceptedTypeGroups: type.groups, - ), + openFile(confirmButtonText: title, acceptedTypeGroups: type.groups), ); if (file == null) return []; return [file]; @@ -52,40 +46,44 @@ enum FileSelectorType { [ XTypeGroup( label: 'Images', - extensions: ['jpg', 'JPG', 'jpeg', 'JPEG', 'png', 'PNG', 'webp', 'WebP', 'WEBP', 'gif', 'GIF', 'bmp', 'BMP', 'tiff', 'TIFF', 'tif', 'TIF', 'heic', 'HEIC', 'svg', 'SVG'], + extensions: [ + 'jpg', + 'JPG', + 'jpeg', + 'JPEG', + 'png', + 'PNG', + 'webp', + 'WebP', + 'WEBP', + 'gif', + 'GIF', + 'bmp', + 'BMP', + 'tiff', + 'TIFF', + 'tif', + 'TIF', + 'heic', + 'HEIC', + 'svg', + 'SVG', + ], ), XTypeGroup( label: 'JPG', extensions: ['jpg', 'JPG', 'jpeg', 'JPEG'], ), - XTypeGroup( - label: 'PNG', - extensions: ['png', 'PNG'], - ), - XTypeGroup( - label: 'WebP', - extensions: ['webp', 'WebP', 'WEBP'], - ), - XTypeGroup( - label: 'GIF', - extensions: ['gif', 'GIF'], - ), - XTypeGroup( - label: 'BMP', - extensions: ['bmp', 'BMP'], - ), + XTypeGroup(label: 'PNG', extensions: ['png', 'PNG']), + XTypeGroup(label: 'WebP', extensions: ['webp', 'WebP', 'WEBP']), + XTypeGroup(label: 'GIF', extensions: ['gif', 'GIF']), + XTypeGroup(label: 'BMP', extensions: ['bmp', 'BMP']), XTypeGroup( label: 'TIFF', extensions: ['tiff', 'TIFF', 'tif', 'TIF'], ), - XTypeGroup( - label: 'HEIC', - extensions: ['heic', 'HEIC'], - ), - XTypeGroup( - label: 'SVG', - extensions: ['svg', 'SVG'], - ), + XTypeGroup(label: 'HEIC', extensions: ['heic', 'HEIC']), + XTypeGroup(label: 'SVG', extensions: ['svg', 'SVG']), ], FileType.image, null, @@ -94,58 +92,47 @@ enum FileSelectorType { [ XTypeGroup( label: 'Videos', - extensions: ['mp4', 'MP4', 'avi', 'AVI', 'webm', 'WebM', 'WEBM', 'mov', 'MOV', 'mkv', 'MKV', 'wmv', 'WMV', 'flv', 'FLV', 'mpeg', 'MPEG', '3gp', '3GP', 'ogg', 'OGG'], - ), - XTypeGroup( - label: 'MP4', - extensions: ['mp4', 'MP4'], - ), - XTypeGroup( - label: 'WebM', - extensions: ['webm', 'WebM', 'WEBM'], - ), - XTypeGroup( - label: 'AVI', - extensions: ['avi', 'AVI'], - ), - XTypeGroup( - label: 'MOV', - extensions: ['mov', 'MOV'], - ), - XTypeGroup( - label: 'MKV', - extensions: ['mkv', 'MKV'], - ), - XTypeGroup( - label: 'WMV', - extensions: ['wmv', 'WMV'], - ), - XTypeGroup( - label: 'FLV', - extensions: ['flv', 'FLV'], - ), - XTypeGroup( - label: 'MPEG', - extensions: ['mpeg', 'MPEG'], - ), - XTypeGroup( - label: '3GP', - extensions: ['3gp', '3GP'], - ), - XTypeGroup( - label: 'OGG', - extensions: ['ogg', 'OGG'], + extensions: [ + 'mp4', + 'MP4', + 'avi', + 'AVI', + 'webm', + 'WebM', + 'WEBM', + 'mov', + 'MOV', + 'mkv', + 'MKV', + 'wmv', + 'WMV', + 'flv', + 'FLV', + 'mpeg', + 'MPEG', + '3gp', + '3GP', + 'ogg', + 'OGG', + ], ), + XTypeGroup(label: 'MP4', extensions: ['mp4', 'MP4']), + XTypeGroup(label: 'WebM', extensions: ['webm', 'WebM', 'WEBM']), + XTypeGroup(label: 'AVI', extensions: ['avi', 'AVI']), + XTypeGroup(label: 'MOV', extensions: ['mov', 'MOV']), + XTypeGroup(label: 'MKV', extensions: ['mkv', 'MKV']), + XTypeGroup(label: 'WMV', extensions: ['wmv', 'WMV']), + XTypeGroup(label: 'FLV', extensions: ['flv', 'FLV']), + XTypeGroup(label: 'MPEG', extensions: ['mpeg', 'MPEG']), + XTypeGroup(label: '3GP', extensions: ['3gp', '3GP']), + XTypeGroup(label: 'OGG', extensions: ['ogg', 'OGG']), ], FileType.video, null, ), zip( [ - XTypeGroup( - label: 'ZIP', - extensions: ['zip', 'ZIP'], - ), + XTypeGroup(label: 'ZIP', extensions: ['zip', 'ZIP']), ], FileType.custom, ['zip', 'ZIP'], From 13051712199e75f480bb45dfa1bc8c4990c48954 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 12 May 2025 18:28:48 +0200 Subject: [PATCH 017/157] chore: Crop shortcut file on android and cache it --- .../chat_list/client_chooser_button.dart | 20 +++++------ lib/utils/push_helper.dart | 8 +++-- lib/utils/shortcut_memory_icon.dart | 36 +++++++++++++++++++ 3 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 lib/utils/shortcut_memory_icon.dart diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 68acc13cb..717837320 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -4,7 +4,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -67,17 +66,16 @@ class ClientChooserButton extends StatelessWidget { ], ), ), - if (!FluffyThemes.isColumnMode(context)) - PopupMenuItem( - value: SettingsAction.settings, - child: Row( - children: [ - const Icon(Icons.settings_outlined), - const SizedBox(width: 18), - Text(L10n.of(context).settings), - ], - ), + PopupMenuItem( + value: SettingsAction.settings, + child: Row( + children: [ + const Icon(Icons.settings_outlined), + const SizedBox(width: 18), + Text(L10n.of(context).settings), + ], ), + ), const PopupMenuDivider(), for (final bundle in bundles) ...[ if (matrix.accountBundles[bundle]!.length != 1 || diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 6e242eedb..41684a3cc 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -15,6 +15,7 @@ import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/shortcut_memory_icon.dart'; Future pushHelper( PushNotification notification, { @@ -312,9 +313,10 @@ Future _setShortcut( action: AppConfig.inviteLinkPrefix + event.room.id, shortLabel: title, conversationShortcut: true, - icon: avatarFile == null - ? null - : ShortcutMemoryIcon(jpegImage: avatarFile).toString(), + icon: await avatarFile?.toShortcutMemoryIcon( + event.room.id, + event.room.client.database, + ), shortcutIconAsset: avatarFile == null ? ShortcutIconAsset.androidAsset : ShortcutIconAsset.memoryAsset, diff --git a/lib/utils/shortcut_memory_icon.dart b/lib/utils/shortcut_memory_icon.dart new file mode 100644 index 000000000..c1557e44a --- /dev/null +++ b/lib/utils/shortcut_memory_icon.dart @@ -0,0 +1,36 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:image/image.dart'; +import 'package:matrix/matrix.dart'; + +extension ShortcutMemoryIcon on Uint8List { + Future toShortcutMemoryIcon( + String roomId, + DatabaseApi? database, + ) async { + final cacheKey = Uri.parse('im.fluffychat://shortcuts/$roomId'); + final cachedFile = await database?.getFile(cacheKey); + if (cachedFile != null) return base64Encode(cachedFile); + + final image = decodeImage(this); + if (image == null) return null; + + final size = image.width < image.height ? image.width : image.height; + final x = (image.width - size) ~/ 2; + final y = (image.height - size) ~/ 2; + + final croppedImage = copyCrop( + image, + x: x, + y: y, + width: size, + height: size, + ); + + final bytes = croppedImage.toUint8List(); + await database?.storeFile(cacheKey, bytes, 0); + + return base64Encode(croppedImage.toUint8List()); + } +} From 71751feb698261d18468df416447185ae104a168 Mon Sep 17 00:00:00 2001 From: Marc Pina Artigas Date: Thu, 10 Apr 2025 04:31:48 +0200 Subject: [PATCH 018/157] Translated using Weblate (Catalan) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ca/ --- assets/l10n/intl_ca.arb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_ca.arb b/assets/l10n/intl_ca.arb index 57a406154..0f6e4fbf0 100644 --- a/assets/l10n/intl_ca.arb +++ b/assets/l10n/intl_ca.arb @@ -2150,7 +2150,7 @@ "@widgetUrlError": {}, "emailOrUsername": "Email o nom d'usuàrïi", "@emailOrUsername": {}, - "newSpaceDescription": "Els espais et permeten consolidar ls por", + "newSpaceDescription": "Els espais et permeten consolidar els teus xats i construir comunitats públiques o privades.", "@newSpaceDescription": {}, "chatDescription": "Descripció del xat", "@chatDescription": {}, @@ -3328,5 +3328,17 @@ "shareKeysWith": "Comparteix les claus amb...", "@shareKeysWith": {}, "crossVerifiedDevicesIfEnabled": "Els dispositius verificats mútuament, si està activat", - "@crossVerifiedDevicesIfEnabled": {} + "@crossVerifiedDevicesIfEnabled": {}, + "commandHint_roomupgrade": "Actualitza aquesta sala a la versió indicada", + "@commandHint_roomupgrade": {}, + "takeAPhoto": "Fes una foto", + "@takeAPhoto": {}, + "recordAVideo": "Grava un vídeo", + "@recordAVideo": {}, + "optionalMessage": "(Opcional) missatge...", + "@optionalMessage": {}, + "enterNewChat": "Entra al nou xat", + "@enterNewChat": {}, + "notSupportedOnThisDevice": "No suportat en aquest dispositiu", + "@notSupportedOnThisDevice": {} } From b08e59cdd6f7ffd318cfa83ea4d32621e96399fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Fri, 11 Apr 2025 08:03:16 +0200 Subject: [PATCH 019/157] Translated using Weblate (Galician) Currently translated at 100.0% (769 of 769 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ --- assets/l10n/intl_gl.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index d81422e84..5f4a33b05 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -3345,5 +3345,13 @@ "enterNewChat": "Entrar na nova conversa", "@enterNewChat": {}, "commandHint_roomupgrade": "Actualizar esta sala á versión de sala indicada", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "Establecer nivel personalizado dos permisos", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Elixe un rol predefinido dos indicados ou escribe un nivel personalizado entre 0 e 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Ignorar usuaria", + "@ignoreUser": {}, + "normalUser": "Usuaria corrente", + "@normalUser": {} } From bac6e7108c85d7254c32e088053bb53365a93a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Fri, 11 Apr 2025 13:29:22 +0200 Subject: [PATCH 020/157] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (769 of 769 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index 377750632..e0840c27a 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -3345,5 +3345,13 @@ "enterNewChat": "进入新聊天", "@enterNewChat": {}, "commandHint_roomupgrade": "将此聊天室升级到给定的聊天室版本", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "设置自定义权限等级", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "请在下方选择预定义的角色或输入 0 到 100 间的自定义权限等级。", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "忽略用户", + "@ignoreUser": {}, + "normalUser": "正常用户", + "@normalUser": {} } From 9ce0c2ccd3aeac1e7e5deab18635141d52fdac74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 12 Apr 2025 17:14:59 +0200 Subject: [PATCH 021/157] Translated using Weblate (Estonian) Currently translated at 100.0% (769 of 769 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index 5cefa835e..561fc5a97 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -3345,5 +3345,13 @@ "enterNewChat": "Liitu uue vestlusega", "@enterNewChat": {}, "commandHint_roomupgrade": "Uuenda see jututuba antud jututoa versioonini", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "Seadista kohandatud õiguste tase", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Palun vali eelvalitud rollide seast või lisa õiguste tase vahemikus 0 kuni 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Eira kasutajat", + "@ignoreUser": {}, + "normalUser": "Tavakasutaja", + "@normalUser": {} } From 4d2de3b131b97caf26cf85e7e49f781e9be7004f Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Sat, 12 Apr 2025 11:55:57 +0200 Subject: [PATCH 022/157] Translated using Weblate (Latvian) Currently translated at 100.0% (769 of 769 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 9ec64399b..ff67fcc78 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -3323,5 +3323,13 @@ "enterNewChat": "Ieiet jaunajā tērzēšanā", "@enterNewChat": {}, "commandHint_roomupgrade": "Uzlabot šo istabu uz norādīto istabas versiju", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "Iestatīt pielāgotu atļauju līmeni", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Lūgums zemāk izvēlēties iepriekšizveidotu lomu vai ievadīt pielāgotu atļauju līmeni starp 0 un 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Neņemt vērā lietotāju", + "@ignoreUser": {}, + "normalUser": "Parasts lietotājs", + "@normalUser": {} } From 1934653005ea1faf2b1254755a1127bbef213389 Mon Sep 17 00:00:00 2001 From: Jelv Date: Mon, 14 Apr 2025 13:51:30 +0200 Subject: [PATCH 023/157] Translated using Weblate (Dutch) Currently translated at 100.0% (769 of 769 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ --- assets/l10n/intl_nl.arb | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/assets/l10n/intl_nl.arb b/assets/l10n/intl_nl.arb index 0706f6d94..f98d98087 100644 --- a/assets/l10n/intl_nl.arb +++ b/assets/l10n/intl_nl.arb @@ -646,7 +646,7 @@ "type": "String", "placeholders": {} }, - "defaultPermissionLevel": "Standaard machtigingsniveau voor nieuwe personen", + "defaultPermissionLevel": "Standaard rechten-niveau voor nieuwe personen", "@defaultPermissionLevel": { "type": "String", "placeholders": {} @@ -1114,7 +1114,7 @@ "type": "String", "placeholders": {} }, - "locationPermissionDeniedNotice": "Locatievoorzieningen is geweigerd. Zet hem aan om locatie delen te gebruiken.", + "locationPermissionDeniedNotice": "Locatie toegang is geweigerd. Geef toegang om de locatie delen-functie te gebruiken.", "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1158,7 +1158,7 @@ "type": "String", "placeholders": {} }, - "muteChat": "Meldingen uitschakelen", + "muteChat": "Chat dempen", "@muteChat": { "type": "String", "placeholders": {} @@ -1692,7 +1692,7 @@ "type": "String", "placeholders": {} }, - "setPermissionsLevel": "Machtigingsniveau instellen", + "setPermissionsLevel": "Rechten-niveau instellen", "@setPermissionsLevel": { "type": "String", "placeholders": {} @@ -1811,7 +1811,7 @@ "type": "String", "placeholders": {} }, - "toggleMuted": "Meldingen in- of uitschakelen", + "toggleMuted": "Dempen in- of uitschakelen", "@toggleMuted": { "type": "String", "placeholders": {} @@ -1877,7 +1877,7 @@ } } }, - "unmuteChat": "Meldingen inschakelen", + "unmuteChat": "Dempen uitschakelen", "@unmuteChat": { "type": "String", "placeholders": {} @@ -2537,7 +2537,7 @@ "@pleaseTryAgainLaterOrChooseDifferentServer": {}, "signInWithPassword": "Aanmelden met wachtwoord", "@signInWithPassword": {}, - "chatPermissions": "Chat toestemmingen", + "chatPermissions": "Chat rechten", "@chatPermissions": {}, "chatDescription": "Chatomschrijving", "@chatDescription": {}, @@ -2982,7 +2982,7 @@ "@notificationRuleContainsUserName": {}, "notificationRuleContainsUserNameDescription": "Stuurt een melding als een bericht de persoon vermeld.", "@notificationRuleContainsUserNameDescription": {}, - "notificationRuleMaster": "Alle meldingen uitschakelen", + "notificationRuleMaster": "Alle meldingen dempen", "@notificationRuleMaster": {}, "notificationRuleMasterDescription": "Overschrijf alle andere regels en meldingen uitschakelen.", "@notificationRuleMasterDescription": {}, @@ -3340,5 +3340,13 @@ "notSupportedOnThisDevice": "Niet ondersteund op dit apparaat", "@notSupportedOnThisDevice": {}, "commandHint_roomupgrade": "Upgradeer deze kamer naar de aangegeven kamerversie", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "Aangepast rechten-niveau instellen", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Kies een standaard rol hieronder of voer een aangepast rechten-niveau in tussen 0 en 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Persoon negeren", + "@ignoreUser": {}, + "normalUser": "Normaal persoon", + "@normalUser": {} } From 17e8a023fde2586aadbbde84b66ce03de20ee76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Tue, 15 Apr 2025 04:49:02 +0200 Subject: [PATCH 024/157] Translated using Weblate (Galician) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ --- assets/l10n/intl_gl.arb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 5f4a33b05..9bffb662c 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -3353,5 +3353,11 @@ "ignoreUser": "Ignorar usuaria", "@ignoreUser": {}, "normalUser": "Usuaria corrente", - "@normalUser": {} + "@normalUser": {}, + "approve": "Aprobar", + "@approve": {}, + "pleaseWaitUntilInvited": "Agora agarda a que alguén da sala te convide a entrar.", + "@pleaseWaitUntilInvited": {}, + "youHaveKnocked": "Petaches á porta", + "@youHaveKnocked": {} } From 17135ffef8ec9fee56dfaeeafbb7ae5925a5a661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Tue, 15 Apr 2025 05:30:50 +0200 Subject: [PATCH 025/157] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index e0840c27a..dc087aca7 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -3353,5 +3353,11 @@ "ignoreUser": "忽略用户", "@ignoreUser": {}, "normalUser": "正常用户", - "@normalUser": {} + "@normalUser": {}, + "approve": "批准", + "@approve": {}, + "youHaveKnocked": "你已请求加入", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "在来自该聊天室的某人邀请你之前请等待。", + "@pleaseWaitUntilInvited": {} } From 448f9da473f400a7062a502e742f5017d6516372 Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Tue, 15 Apr 2025 08:50:59 +0200 Subject: [PATCH 026/157] Translated using Weblate (Latvian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index ff67fcc78..0d53cadf8 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -3331,5 +3331,11 @@ "ignoreUser": "Neņemt vērā lietotāju", "@ignoreUser": {}, "normalUser": "Parasts lietotājs", - "@normalUser": {} + "@normalUser": {}, + "youHaveKnocked": "Tu pieklauvēji", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Lūgums tagad uzgaidīt, līdz kāds no istabas uzaicinās Tevi.", + "@pleaseWaitUntilInvited": {}, + "approve": "Apstiprināt", + "@approve": {} } From 06ac929caac234295813ef7bffd753780e1fdc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 15 Apr 2025 22:52:44 +0200 Subject: [PATCH 027/157] Translated using Weblate (Estonian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index 561fc5a97..95b7ce823 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -3353,5 +3353,11 @@ "ignoreUser": "Eira kasutajat", "@ignoreUser": {}, "normalUser": "Tavakasutaja", - "@normalUser": {} + "@normalUser": {}, + "approve": "Kiida heaks", + "@approve": {}, + "pleaseWaitUntilInvited": "Palun oota seni, kuni keegi jututoast saadab sulle kutse.", + "@pleaseWaitUntilInvited": {}, + "youHaveKnocked": "Sa oled koputanud", + "@youHaveKnocked": {} } From 7a6c699605e9ef5d1729a619e09bdeabd6364f02 Mon Sep 17 00:00:00 2001 From: Angelo Schirinzi Date: Thu, 17 Apr 2025 10:34:39 +0200 Subject: [PATCH 028/157] Translated using Weblate (Italian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/it/ --- assets/l10n/intl_it.arb | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/assets/l10n/intl_it.arb b/assets/l10n/intl_it.arb index 7bffff886..acd41035a 100644 --- a/assets/l10n/intl_it.arb +++ b/assets/l10n/intl_it.arb @@ -942,7 +942,7 @@ "type": "String", "placeholders": {} }, - "leftTheChat": "Ha abbandonato la chat", + "leftTheChat": "Ha lasciato la chat", "@leftTheChat": { "type": "String", "placeholders": {} @@ -3208,7 +3208,7 @@ "@notificationRuleSuppressNotices": {}, "notificationRuleSuppressNoticesDescription": "Silenzia le notifiche da client automatizzati come i bot.", "@notificationRuleSuppressNoticesDescription": {}, - "notificationRuleInviteForMeDescription": "Notifica l'utente quando è invitato in una stanza.", + "notificationRuleInviteForMeDescription": "Notifica l'utente quando viene invitato a una stanza.", "@notificationRuleInviteForMeDescription": {}, "notificationRuleMemberEvent": "Eventi per i membri", "@notificationRuleMemberEvent": {}, @@ -3332,5 +3332,31 @@ "previous": "Precedente", "@previous": {}, "otherPartyNotLoggedIn": "L'altra parte non è attualmente connessa e quindi non può ricevere messaggi!", - "@otherPartyNotLoggedIn": {} + "@otherPartyNotLoggedIn": {}, + "takeAPhoto": "Scatta una foto", + "@takeAPhoto": {}, + "recordAVideo": "Registra un video", + "@recordAVideo": {}, + "notSupportedOnThisDevice": "Non supportato su questo dispositivo", + "@notSupportedOnThisDevice": {}, + "enterNewChat": "Inizia nuova chat", + "@enterNewChat": {}, + "setCustomPermissionLevel": "Imposta livello di permesso personalizzato", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Scegli un ruolo predefinito qui sotto o inserisci un livello di permesso personalizzato tra 0 e 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Ignora utente", + "@ignoreUser": {}, + "normalUser": "Utente normale", + "@normalUser": {}, + "commandHint_roomupgrade": "Aggiorna questa stanza alla versione specificata", + "@commandHint_roomupgrade": {}, + "optionalMessage": "Messaggio (opzionale)...", + "@optionalMessage": {}, + "approve": "Approva", + "@approve": {}, + "youHaveKnocked": "Hai bussato", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Ora attendi, finché qualcuno dalla stanza non ti invita.", + "@pleaseWaitUntilInvited": {} } From 5349004a7f1d4d79ffcbc547282ba27c6c9f5c08 Mon Sep 17 00:00:00 2001 From: Jelv Date: Wed, 16 Apr 2025 22:50:56 +0200 Subject: [PATCH 029/157] Translated using Weblate (Dutch) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ --- assets/l10n/intl_nl.arb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_nl.arb b/assets/l10n/intl_nl.arb index f98d98087..399822c16 100644 --- a/assets/l10n/intl_nl.arb +++ b/assets/l10n/intl_nl.arb @@ -3343,10 +3343,16 @@ "@commandHint_roomupgrade": {}, "setCustomPermissionLevel": "Aangepast rechten-niveau instellen", "@setCustomPermissionLevel": {}, - "setPermissionsLevelDescription": "Kies een standaard rol hieronder of voer een aangepast rechten-niveau in tussen 0 en 100.", + "setPermissionsLevelDescription": "Kies hieronder een standaard rol of voer een aangepast rechten-niveau in tussen 0 en 100.", "@setPermissionsLevelDescription": {}, "ignoreUser": "Persoon negeren", "@ignoreUser": {}, "normalUser": "Normaal persoon", - "@normalUser": {} + "@normalUser": {}, + "pleaseWaitUntilInvited": "Wacht even alsjeblieft tot iemand van de kamer je uitnodigt.", + "@pleaseWaitUntilInvited": {}, + "approve": "Goedkeuren", + "@approve": {}, + "youHaveKnocked": "Je hebt geklopt", + "@youHaveKnocked": {} } From 1677271597fa5c58d9943aad8bda2426fba9d82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98jvind=20Fritjof=20Arnfred?= Date: Fri, 18 Apr 2025 22:34:37 +0200 Subject: [PATCH 030/157] Added translation using Weblate (Danish) --- assets/l10n/intl_da.arb | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/l10n/intl_da.arb diff --git a/assets/l10n/intl_da.arb b/assets/l10n/intl_da.arb new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/assets/l10n/intl_da.arb @@ -0,0 +1 @@ +{} From 1c62c493b72835c6a7f0a0203f781c4327bc4cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98jvind=20Fritjof=20Arnfred?= Date: Fri, 18 Apr 2025 22:36:40 +0200 Subject: [PATCH 031/157] Translated using Weblate (Danish) Currently translated at 1.2% (10 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/da/ --- assets/l10n/intl_da.arb | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_da.arb b/assets/l10n/intl_da.arb index 0967ef424..40c3ad7fe 100644 --- a/assets/l10n/intl_da.arb +++ b/assets/l10n/intl_da.arb @@ -1 +1,25 @@ -{} +{ + "repeatPassword": "Gentag password", + "@repeatPassword": {}, + "notAnImage": "Ikke en billedfil.", + "@notAnImage": {}, + "setCustomPermissionLevel": "Indstil særligt tilladelsesniveau", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Vælg en prædefineret rolle herunder eller indtaste et særligt tilladelsesniveau mellem 0 og 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Ignorér bruger", + "@ignoreUser": {}, + "remove": "Fjern", + "@remove": { + "type": "String", + "placeholders": {} + }, + "importNow": "Importer nu", + "@importNow": {}, + "importEmojis": "Importer emojis", + "@importEmojis": {}, + "normalUser": "Normal bruger", + "@normalUser": {}, + "importFromZipFile": "Importer fra .zip fil", + "@importFromZipFile": {} +} From 1038da6c09def73d6473109d5eb390e6963a1243 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 23 Apr 2025 11:16:59 +0200 Subject: [PATCH 032/157] Translated using Weblate (Indonesian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ --- assets/l10n/intl_id.arb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_id.arb b/assets/l10n/intl_id.arb index 6e072cdd9..25b91cdd0 100644 --- a/assets/l10n/intl_id.arb +++ b/assets/l10n/intl_id.arb @@ -3344,5 +3344,19 @@ "enterNewChat": "Masuk ke obrolan baru", "@enterNewChat": {}, "commandHint_roomupgrade": "Tingkatkan ruangan ini ke versi ruangan yang ditentukan", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "pleaseWaitUntilInvited": "Silakan menunggu sampai seseorang dari ruangan mengundang Anda.", + "@pleaseWaitUntilInvited": {}, + "setCustomPermissionLevel": "Atur tingkat perizinan kustom", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Silakan pilih peran yang sudah ditentukan di bawah atau masukkan tingkat perizinan kustom antara 0 dan 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Abaikan pengguna", + "@ignoreUser": {}, + "normalUser": "Pengguna biasa", + "@normalUser": {}, + "approve": "Terima", + "@approve": {}, + "youHaveKnocked": "Anda telah mengetuk", + "@youHaveKnocked": {} } From 702423005b9deb72d8789362eed45beb049fdfcc Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 24 Apr 2025 16:10:25 +0200 Subject: [PATCH 033/157] Translated using Weblate (Portuguese (Brazil)) Currently translated at 81.8% (632 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pt_BR/ --- assets/l10n/intl_pt_BR.arb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_pt_BR.arb b/assets/l10n/intl_pt_BR.arb index 0b2e503f4..84716fb3b 100644 --- a/assets/l10n/intl_pt_BR.arb +++ b/assets/l10n/intl_pt_BR.arb @@ -2973,5 +2973,21 @@ "gallery": "Galeria", "@gallery": {}, "files": "Arquivos", - "@files": {} + "@files": {}, + "more": "Mais", + "@more": {}, + "deletePushRuleCanNotBeUndone": "Se você excluir esta configuração de notificação, isso não pode ser desfeito.", + "@deletePushRuleCanNotBeUndone": {}, + "alwaysUse24HourFormat": "Falso", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "setPermissionsLevelDescription": "Por favor, escolha uma função predefinida abaixo ou insira um nível de permissão personalizado entre 0 e 100.", + "@setPermissionsLevelDescription": {}, + "shareKeysWith": "Compartilhar chaves com...", + "@shareKeysWith": {}, + "setCustomPermissionLevel": "Definir nível de permissão personalizado", + "@setCustomPermissionLevel": {}, + "ignoreUser": "Ignorar usuário", + "@ignoreUser": {} } From 58c284a1ad7d2e378b361dbb29c21807ff61efc5 Mon Sep 17 00:00:00 2001 From: sevonj <100710152+sevonj@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:47:53 +0200 Subject: [PATCH 034/157] Translated using Weblate (Finnish) Currently translated at 69.0% (533 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fi/ --- assets/l10n/intl_fi.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_fi.arb b/assets/l10n/intl_fi.arb index cdd3a327b..b52e52166 100644 --- a/assets/l10n/intl_fi.arb +++ b/assets/l10n/intl_fi.arb @@ -2592,7 +2592,7 @@ } } }, - "sendTypingNotifications": "Lähetä kirjoitusilmoituksia", + "sendTypingNotifications": "acLähetä kirjoitusilmoituksia", "@sendTypingNotifications": {}, "inviteGroupChat": "Kutsu ryhmäkeskusteluun", "@inviteGroupChat": {}, From 5e3b2264c04d3b06c5d278014071bd7a0b8f5958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98jvind=20Fritjof=20Arnfred?= Date: Thu, 24 Apr 2025 22:18:30 +0200 Subject: [PATCH 035/157] Translated using Weblate (Danish) Currently translated at 1.4% (11 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/da/ --- assets/l10n/intl_da.arb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_da.arb b/assets/l10n/intl_da.arb index 40c3ad7fe..77532f842 100644 --- a/assets/l10n/intl_da.arb +++ b/assets/l10n/intl_da.arb @@ -21,5 +21,9 @@ "normalUser": "Normal bruger", "@normalUser": {}, "importFromZipFile": "Importer fra .zip fil", - "@importFromZipFile": {} + "@importFromZipFile": {}, + "alwaysUse24HourFormat": "true", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + } } From a383ea9f4662dbafc2570b0909f0d156cab50a2d Mon Sep 17 00:00:00 2001 From: Bezruchenko Simon Date: Sat, 26 Apr 2025 21:02:40 +0200 Subject: [PATCH 036/157] Translated using Weblate (Ukrainian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ --- assets/l10n/intl_uk.arb | 87 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_uk.arb b/assets/l10n/intl_uk.arb index 6240a8b28..b80185279 100644 --- a/assets/l10n/intl_uk.arb +++ b/assets/l10n/intl_uk.arb @@ -1372,7 +1372,7 @@ "type": "String", "placeholders": {} }, - "containsDisplayName": "Містить показуване ім’я", + "containsDisplayName": "Містить відображуване ім'я", "@containsDisplayName": { "type": "String", "placeholders": {} @@ -3276,5 +3276,88 @@ "notificationRuleContainsDisplayNameDescription": "Сповіщає користувача, коли повідомлення містить показуване ім'я.", "@notificationRuleContainsDisplayNameDescription": {}, "notificationRuleIsRoomMention": "Згадки кімнати", - "@notificationRuleIsRoomMention": {} + "@notificationRuleIsRoomMention": {}, + "notificationRuleTombstone": "Могильний камінь", + "@notificationRuleTombstone": {}, + "notificationRuleTombstoneDescription": "Повідомляє користувача про повідомлення деактивації кімнати.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReactionDescription": "Приховує сповіщення про реакції.", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomOneToOneDescription": "Повідомляє користувача про повідомлення в кімнатах \"Один на один\".", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleSuppressEdits": "Приховує редагування", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "Приховує сповіщення про відредаговані повідомлення.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "Дзвінок", + "@notificationRuleCall": {}, + "notificationRuleRoomServerAcl": "ACL серверу кімнати", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleMessageDescription": "Повідомляє користувача про загальні повідомлення.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "Зашифровано", + "@notificationRuleEncrypted": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "Повідомляє користувача про події віджетів Jitsi.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Приховує події ACL серверу", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "Приховує сповіщення про події Server ACL.", + "@notificationRuleServerAclDescription": {}, + "more": "Більше", + "@more": {}, + "takeAPhoto": "Зробити фото", + "@takeAPhoto": {}, + "recordAVideo": "Записати відео", + "@recordAVideo": {}, + "optionalMessage": "(Необов'язково) повідомлення...", + "@optionalMessage": {}, + "enterNewChat": "Створити новий чат", + "@enterNewChat": {}, + "shareKeysWith": "Поділитися ключами з...", + "@shareKeysWith": {}, + "notSupportedOnThisDevice": "Не підтримується на цьому пристрої", + "@notSupportedOnThisDevice": {}, + "notificationRuleMessage": "Повідомлення", + "@notificationRuleMessage": {}, + "unknownPushRule": "Невідоме правило сповіщення '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "pleaseWaitUntilInvited": "Будь ласка, зачекайте, поки хтось з кімнати запросить вас.", + "@pleaseWaitUntilInvited": {}, + "notificationRuleEncryptedRoomOneToOne": "Зашифрована кімната \"Один на один\"", + "@notificationRuleEncryptedRoomOneToOne": {}, + "deletePushRuleCanNotBeUndone": "Якщо ви видалите це налаштування сповіщень, відновити його буде неможливо.", + "@deletePushRuleCanNotBeUndone": {}, + "ignoreUser": "Ігнорувати користувача", + "@ignoreUser": {}, + "setCustomPermissionLevel": "Встановити рівень користувацьких прав", + "@setCustomPermissionLevel": {}, + "normalUser": "Звичайний користувач", + "@normalUser": {}, + "notificationRuleEncryptedDescription": "Повідомляє користувача про повідомлення в зашифрованих кімнатах.", + "@notificationRuleEncryptedDescription": {}, + "setPermissionsLevelDescription": "Будь ласка, виберіть заздалегідь визначену роль нижче або введіть користувацький рівень прав від 0 до 100.", + "@setPermissionsLevelDescription": {}, + "notificationRuleRoomServerAclDescription": "Приховує сповіщення про списки контролю доступу (ACL) серверу кімнати.", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleReaction": "Реакція", + "@notificationRuleReaction": {}, + "notificationRuleCallDescription": "Повідомляє користувача про дзвінки.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Повідомляє користувача про повідомлення в зашифрованих кімнатах \"Один на один\".", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Кімната \"Один на один\"", + "@notificationRuleRoomOneToOne": {}, + "approve": "Схвалити", + "@approve": {}, + "youHaveKnocked": "Ви постукали", + "@youHaveKnocked": {} } From 6965602246a2a122e90bf7305bc55abb8d4ebf8d Mon Sep 17 00:00:00 2001 From: xabirequejo Date: Sun, 27 Apr 2025 22:21:21 +0200 Subject: [PATCH 037/157] Translated using Weblate (Basque) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ --- assets/l10n/intl_eu.arb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index 381c78a35..4f1665095 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -3339,5 +3339,25 @@ "optionalMessage": "Mezua (aukerakoa)…", "@optionalMessage": {}, "notSupportedOnThisDevice": "Ez da gailu honekin bateragarria", - "@notSupportedOnThisDevice": {} + "@notSupportedOnThisDevice": {}, + "commandHint_roomupgrade": "Bertsio-berritu gela adierazitako gela-bertsiora", + "@commandHint_roomupgrade": {}, + "enterNewChat": "Sartu txat berrira", + "@enterNewChat": {}, + "normalUser": "Erabiltzaile arrunta", + "@normalUser": {}, + "notificationRuleRoomServerAclDescription": "Gela-zerbitzarirako sarbide-kontroleko zerrenden (ACL) jakinarazpenak ezkutatzen ditu.", + "@notificationRuleRoomServerAclDescription": {}, + "ignoreUser": "Egin muzin erabiltzaileari", + "@ignoreUser": {}, + "setCustomPermissionLevel": "Ezarri baimen-maila propioak", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Aukeratu defektuzko rola edo sartu baimen-maila pertsonalizatu bat 0 eta 100 artean.", + "@setPermissionsLevelDescription": {}, + "approve": "Onetsi", + "@approve": {}, + "youHaveKnocked": "Sartzeko baimena eskatu duzu", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Orain itxaron gelako norbaitek gonbidatzen zaituen arte.", + "@pleaseWaitUntilInvited": {} } From 581108964118d8ddba0e353a90cbdd69462c245c Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Sun, 27 Apr 2025 23:26:12 +0200 Subject: [PATCH 038/157] Translated using Weblate (Ukrainian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ --- assets/l10n/intl_uk.arb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/l10n/intl_uk.arb b/assets/l10n/intl_uk.arb index b80185279..31595fcd4 100644 --- a/assets/l10n/intl_uk.arb +++ b/assets/l10n/intl_uk.arb @@ -1372,7 +1372,7 @@ "type": "String", "placeholders": {} }, - "containsDisplayName": "Містить відображуване ім'я", + "containsDisplayName": "Містить показуване ім’я", "@containsDisplayName": { "type": "String", "placeholders": {} @@ -3289,9 +3289,9 @@ "@notificationRuleSuppressEdits": {}, "notificationRuleSuppressEditsDescription": "Приховує сповіщення про відредаговані повідомлення.", "@notificationRuleSuppressEditsDescription": {}, - "notificationRuleCall": "Дзвінок", + "notificationRuleCall": "Виклик", "@notificationRuleCall": {}, - "notificationRuleRoomServerAcl": "ACL серверу кімнати", + "notificationRuleRoomServerAcl": "ACL сервера кімнати", "@notificationRuleRoomServerAcl": {}, "notificationRuleMessageDescription": "Повідомляє користувача про загальні повідомлення.", "@notificationRuleMessageDescription": {}, @@ -3301,7 +3301,7 @@ "@notificationRuleJitsi": {}, "notificationRuleJitsiDescription": "Повідомляє користувача про події віджетів Jitsi.", "@notificationRuleJitsiDescription": {}, - "notificationRuleServerAcl": "Приховує події ACL серверу", + "notificationRuleServerAcl": "Приховує події сервера ACL", "@notificationRuleServerAcl": {}, "notificationRuleServerAclDescription": "Приховує сповіщення про події Server ACL.", "@notificationRuleServerAclDescription": {}, @@ -3346,11 +3346,11 @@ "@notificationRuleEncryptedDescription": {}, "setPermissionsLevelDescription": "Будь ласка, виберіть заздалегідь визначену роль нижче або введіть користувацький рівень прав від 0 до 100.", "@setPermissionsLevelDescription": {}, - "notificationRuleRoomServerAclDescription": "Приховує сповіщення про списки контролю доступу (ACL) серверу кімнати.", + "notificationRuleRoomServerAclDescription": "Приховує сповіщення про списки контролю доступу (ACL) сервера кімнати.", "@notificationRuleRoomServerAclDescription": {}, "notificationRuleReaction": "Реакція", "@notificationRuleReaction": {}, - "notificationRuleCallDescription": "Повідомляє користувача про дзвінки.", + "notificationRuleCallDescription": "Повідомляє користувача про виклики.", "@notificationRuleCallDescription": {}, "notificationRuleEncryptedRoomOneToOneDescription": "Повідомляє користувача про повідомлення в зашифрованих кімнатах \"Один на один\".", "@notificationRuleEncryptedRoomOneToOneDescription": {}, From 4a048576e90dc00d80fb737b8fdced49b874ff84 Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Tue, 29 Apr 2025 16:13:59 +0200 Subject: [PATCH 039/157] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index dc087aca7..6e81765c9 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -3243,7 +3243,7 @@ "@notificationRuleContainsDisplayName": {}, "notificationRuleIsRoomMention": "聊天室提及", "@notificationRuleIsRoomMention": {}, - "notificationRuleRoomnotifDescription": "消息包含 \"@room\" 时提醒用户。", + "notificationRuleRoomnotifDescription": "消息包含 「@room」 时提醒用户。", "@notificationRuleRoomnotifDescription": {}, "notificationRuleTombstone": "墓碑", "@notificationRuleTombstone": {}, From f6718e32e86195f489b03d4f4d236b6706122a72 Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Tue, 29 Apr 2025 16:12:43 +0200 Subject: [PATCH 040/157] Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 99.2% (766 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/ --- assets/l10n/intl_zh_Hant.arb | 107 ++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_zh_Hant.arb b/assets/l10n/intl_zh_Hant.arb index c4f77b844..93e057678 100644 --- a/assets/l10n/intl_zh_Hant.arb +++ b/assets/l10n/intl_zh_Hant.arb @@ -186,7 +186,7 @@ } } }, - "changedTheChatDescriptionTo": "{username} 把聊天室介紹變更為:「{description}」", + "changedTheChatDescriptionTo": "{username} 變更了聊天室介紹為:「{description}」", "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -3249,5 +3249,108 @@ "supportPage": "幫助頁面", "@supportPage": {}, "addLink": "插入連結", - "@addLink": {} + "@addLink": {}, + "notificationRuleContainsDisplayName": "包含顯示名稱", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleRoomnotif": "聊天室通知", + "@notificationRuleRoomnotif": {}, + "notificationRuleIsRoomMentionDescription": "當有聊天室提及時通知用户。", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomOneToOneDescription": "在一對一聊天室中通知用户收到訊息。", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleServerAcl": "隱藏伺服器 ACL 事件", + "@notificationRuleServerAcl": {}, + "notificationRuleContainsDisplayNameDescription": "當訊息包含用户的顯示名稱時通知用户。", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "聊天室提及", + "@notificationRuleIsRoomMention": {}, + "notificationRuleRoomnotifDescription": "當訊息包含 \"@room\" 時通知用户。", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "墓碑", + "@notificationRuleTombstone": {}, + "notificationRuleCallDescription": "通知用户有來電。", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "一對一加密聊天室", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "通知用户一對一加密聊天室的訊息。", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "一對一聊天室", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleMessage": "訊息", + "@notificationRuleMessage": {}, + "notificationRuleServerAclDescription": "隱藏伺服器 ACL 事件的通知。", + "@notificationRuleServerAclDescription": {}, + "notificationRuleMessageDescription": "通知用户一般訊息。", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "已加密", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "在已加密房間內通知用户訊息。", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "通知用户 Jitsi 小部件事件。", + "@notificationRuleJitsiDescription": {}, + "unknownPushRule": "未知推送規則 '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "more": "更多", + "@more": {}, + "deletePushRuleCanNotBeUndone": "刪除此通知設定的操作無法復原。", + "@deletePushRuleCanNotBeUndone": {}, + "shareKeysWith": "與哪些設備共享金鑰…", + "@shareKeysWith": {}, + "shareKeysWithDescription": "選擇應該信任的裝置,並允許它們在加密聊天中讀取您的訊息?", + "@shareKeysWithDescription": {}, + "allDevices": "所有裝置", + "@allDevices": {}, + "crossVerifiedDevices": "經交叉驗證的裝置", + "@crossVerifiedDevices": {}, + "crossVerifiedDevicesIfEnabled": "交叉驗證裝置(如啟用)", + "@crossVerifiedDevicesIfEnabled": {}, + "setCustomPermissionLevel": "設置自定義權限等級", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "請在下方選擇預先定義的角色,或輸入在 0 到 100 之間的自訂權限等級。", + "@setPermissionsLevelDescription": {}, + "recordAVideo": "錄製影像", + "@recordAVideo": {}, + "takeAPhoto": "拍攝照片", + "@takeAPhoto": {}, + "optionalMessage": "(可選)訊息...", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "此裝置不受支援", + "@notSupportedOnThisDevice": {}, + "ignoreUser": "忽略用户", + "@ignoreUser": {}, + "normalUser": "正常用户", + "@normalUser": {}, + "notificationRuleTombstoneDescription": "通知用户有關房間解散的訊息。", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReaction": "心情回應", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "關閉心情回應通知。", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomServerAcl": "聊天室伺服器 ACL", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "關閉聊天室伺服器存取控制清單 (ACL) 的通知。", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "隱藏編輯", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "隱藏已編輯訊息通知。", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "來電", + "@notificationRuleCall": {}, + "verifiedDevicesOnly": "僅限已驗證的裝置", + "@verifiedDevicesOnly": {}, + "approve": "核准", + "@approve": {}, + "youHaveKnocked": "您已請求加入", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "直到聊天室裡有人邀請您前,請等候。", + "@pleaseWaitUntilInvited": {} } From 35eddc2793ac0d2e62b4c6699f1c1c5c2ab05c42 Mon Sep 17 00:00:00 2001 From: miullu Date: Tue, 29 Apr 2025 15:54:25 +0200 Subject: [PATCH 041/157] Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 99.2% (766 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/ --- assets/l10n/intl_zh_Hant.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh_Hant.arb b/assets/l10n/intl_zh_Hant.arb index 93e057678..8f9e879ff 100644 --- a/assets/l10n/intl_zh_Hant.arb +++ b/assets/l10n/intl_zh_Hant.arb @@ -3352,5 +3352,7 @@ "youHaveKnocked": "您已請求加入", "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "直到聊天室裡有人邀請您前,請等候。", - "@pleaseWaitUntilInvited": {} + "@pleaseWaitUntilInvited": {}, + "notificationRuleIsUserMentionDescription": "被@時通知他們。", + "@notificationRuleIsUserMentionDescription": {} } From 6d9ef9d24e94550f75cc463a5d49ffed93c24ce8 Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Tue, 29 Apr 2025 16:14:16 +0200 Subject: [PATCH 042/157] Added translation using Weblate (Yue (yue_HK)) --- assets/l10n/intl_yue_HK.arb | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/l10n/intl_yue_HK.arb diff --git a/assets/l10n/intl_yue_HK.arb b/assets/l10n/intl_yue_HK.arb new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/assets/l10n/intl_yue_HK.arb @@ -0,0 +1 @@ +{} From 525374ed4e2573c0f44cdc090c6ddc12e9f37de7 Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Tue, 29 Apr 2025 16:17:48 +0200 Subject: [PATCH 043/157] Added translation using Weblate (Cantonese (Traditional Han script)) --- assets/l10n/intl_yue_Hant.arb | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/l10n/intl_yue_Hant.arb diff --git a/assets/l10n/intl_yue_Hant.arb b/assets/l10n/intl_yue_Hant.arb new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/assets/l10n/intl_yue_Hant.arb @@ -0,0 +1 @@ +{} From 0122a93c50274f775da3a4b20a3eea9fd196ff1b Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Tue, 29 Apr 2025 16:30:55 +0200 Subject: [PATCH 044/157] Translated using Weblate (Cantonese (Traditional Han script)) Currently translated at 8.6% (67 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/yue_Hant/ --- assets/l10n/intl_yue_Hant.arb | 337 +++++++++++++++++++++++++++++++++- 1 file changed, 336 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_yue_Hant.arb b/assets/l10n/intl_yue_Hant.arb index 0967ef424..3f76be695 100644 --- a/assets/l10n/intl_yue_Hant.arb +++ b/assets/l10n/intl_yue_Hant.arb @@ -1 +1,336 @@ -{} +{ + "normalUser": "正常用家", + "@normalUser": {}, + "areYouSureYouWantToLogout": "係咪確定要 log out?", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "畀唔畀陌生人 Join", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "要向其他人簽名,請輸入你 Keep 好咗嘅密碼或者恢復密鑰。", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "badServerLoginTypesException": "呢個 Homeserver 支持嘅登錄類型有:\n{serverVersions}\n但係呢個 App 淨係支援:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "cantOpenUri": "打唔開嘅 URI {uri}", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "badServerVersionsException": "呢個 Homeserver 支持以下 Spec 版本:\n{serverVersions}\n但係個 App 淨係支持 {supoortedVersions} 版本", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "banFromChat": "喺傾偈入面 Ban 咗佢", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "noChatsFoundHere": "暫時未有偈傾。撳下面粒掣同人開始傾偈 ⤵️", + "@noChatsFoundHere": {}, + "bannedUser": "{username} Ban 咗 {targetName}", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} 改咗呢個偈嘅介紹: 「'{description}'」", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "addEmail": "加 Email", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "alwaysUse24HourFormat": "false", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "importEmojis": "導入 Emoji", + "@importEmojis": {}, + "hugContent": "{senderName} 抱咗你", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "commandHint_hug": "Send 個 Hug", + "@commandHint_hug": {}, + "anyoneCanJoin": "任何人都可以 Join", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "repeatPassword": "輸入多一次密碼", + "@repeatPassword": {}, + "notAnImage": "唔係圖檔。", + "@notAnImage": {}, + "remove": "刪走", + "@remove": { + "type": "String", + "placeholders": {} + }, + "importNow": "即刻導入", + "@importNow": {}, + "exportEmotePack": "將表情符號導出成 .zip 檔案", + "@exportEmotePack": {}, + "replace": "換走", + "@replace": {}, + "about": "關於", + "@about": {}, + "aboutHomeserver": "關於{homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "accept": "同意", + "@accept": { + "type": "String", + "placeholders": {} + }, + "acceptedTheInvitation": "👍 {username} 同意咗邀请", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "account": "Account", + "@account": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} 開咗點對點加密", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "supposedMxid": "呢度應該係 {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "addChatDescription": "講下關於呢個偈係傾嘅乜嘢……", + "@addChatDescription": {}, + "addToSpace": "加落去空間嗰度", + "@addToSpace": {}, + "admin": "Admin", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "花名", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "全部", + "@all": { + "type": "String", + "placeholders": {} + }, + "allChats": "所有傾嘅偈仔", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "commandHint_roomupgrade": "將呢間房升級到指定版本", + "@commandHint_roomupgrade": {}, + "commandHint_googly": "送啲古靈精怪表情過去", + "@commandHint_googly": {}, + "commandHint_cuddle": "Send 個攬攬", + "@commandHint_cuddle": {}, + "googlyEyesContent": "{senderName} Send 咗你一個咕嚕眼", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "cuddleContent": "{senderName} 攬咗你", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "answeredTheCall": "{senderName} 聽咗你嘅電話", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "appLock": "App 鎖", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "appLockDescription": "無人用嘅時候用密碼鎖住個App", + "@appLockDescription": {}, + "archive": "存檔", + "@archive": { + "type": "String", + "placeholders": {} + }, + "areYouSure": "咪住先?", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "askVerificationRequest": "係咪要 Accept 來自 {username} 嘅驗證申請?", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "autoplayImages": "自動播放動畫貼紙同表情", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "sendTypingNotifications": "通知人地打緊字", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "向左滑嚟回覆", + "@swipeRightToLeftToReply": {}, + "sendOnEnter": "撳 Enter 即 Send", + "@sendOnEnter": {}, + "countChatsAndCountParticipants": "{chats} 間房同 {participants} 條友", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "搵唔到更多偈傾啦…", + "@noMoreChatsFound": {}, + "joinedChats": "入咗嘅房間", + "@joinedChats": {}, + "unread": "未讀", + "@unread": {}, + "space": "空間", + "@space": {}, + "spaces": "空間", + "@spaces": {}, + "banned": "Block 咗", + "@banned": { + "type": "String", + "placeholders": {} + }, + "blockDevice": "Block 咗嘅裝置", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "blocked": "Block 咗", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "botMessages": "機械人訊息", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "cancel": "取消", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "changeDeviceName": "改裝置名", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username}轉咗個大頭貼", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "confirmMatrixId": "Confirm 你嘅 Matrix ID ,我哋先至可以刪除你嘅 Account。", + "@confirmMatrixId": {}, + "setCustomPermissionLevel": "自訂權限級別", + "@setCustomPermissionLevel": {}, + "importFromZipFile": "喺 .zip 檔案導入", + "@importFromZipFile": {}, + "setPermissionsLevelDescription": "請喺下面選擇一個預定義嘅角色,或輸入介乎0同100之間嘅自定義權限級別。", + "@setPermissionsLevelDescription": {} +} From 22200a7529718bfaeeeb562a9f79a7020d4066b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aindri=C3=BA=20Mac=20Giolla=20Eoin?= Date: Wed, 30 Apr 2025 13:25:42 +0200 Subject: [PATCH 045/157] Translated using Weblate (Irish) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/ --- assets/l10n/intl_ga.arb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_ga.arb b/assets/l10n/intl_ga.arb index bc8642a98..9d54b67a2 100644 --- a/assets/l10n/intl_ga.arb +++ b/assets/l10n/intl_ga.arb @@ -3350,5 +3350,19 @@ "enterNewChat": "Cuir isteach comhrá nua", "@enterNewChat": {}, "commandHint_roomupgrade": "Uasghrádaigh an seomra seo go dtí an leagan seomra a thugtar", - "@commandHint_roomupgrade": {} + "@commandHint_roomupgrade": {}, + "setCustomPermissionLevel": "Socraigh leibhéal ceadanna saincheaptha", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Roghnaigh ról réamhshainithe thíos nó cuir isteach leibhéal ceadanna saincheaptha idir 0 agus 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Déan neamhaird den úsáideoir", + "@ignoreUser": {}, + "normalUser": "Gnáthúsáideoir", + "@normalUser": {}, + "approve": "Ceadaigh", + "@approve": {}, + "youHaveKnocked": "Bhuail tú", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Fan anois, le do thoil, go dtí go dtabharfaidh duine éigin ón seomra cuireadh duit.", + "@pleaseWaitUntilInvited": {} } From cacf942adcd949f80a7a78a6c18595f891265767 Mon Sep 17 00:00:00 2001 From: Yurt Page Date: Sat, 3 May 2025 17:01:08 +0200 Subject: [PATCH 046/157] Translated using Weblate (Russian) Currently translated at 95.4% (737 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ru/ --- assets/l10n/intl_ru.arb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index d986499a9..49c29b03c 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -3270,5 +3270,15 @@ "appIntroduction": "FluffyChat позволяет вам общаться с друзьями с разными мессенджерами. Узнайте больше на https://matrix.org или просто нажмите *Продолжить*.", "@appIntroduction": {}, "previous": "Предыдущий", - "@previous": {} + "@previous": {}, + "notificationRuleCallDescription": "Уведомляет пользователя про звонки.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Шифрованная комната «Один на один»", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Уведомляет пользователя про сообщение в зашифрованных комнатах «Один на один».", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Комната «Один на один»", + "@notificationRuleRoomOneToOne": {}, + "setCustomPermissionLevel": "Установить уровень пользовательских разрешений", + "@setCustomPermissionLevel": {} } From bc4916d948d1af3b54ebfa987265c16b00919bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=84=82=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=9D?= =?UTF-8?q?=20=28=F0=9D=95=98=F0=9D=95=9A=F0=9D=95=A5=F0=9D=95=99?= =?UTF-8?q?=F0=9D=95=A6=F0=9D=95=93=2E=F0=9D=95=94=F0=9D=95=A0=F0=9D=95=9E?= =?UTF-8?q?/=E2=84=82=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=9D=29?= Date: Sun, 4 May 2025 12:28:09 +0200 Subject: [PATCH 047/157] Translated using Weblate (Latvian) Currently translated at 99.6% (769 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 0d53cadf8..722fb2268 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -1835,7 +1835,7 @@ "type": "String", "placeholders": {} }, - "addChatDescription": "Pievienot tērzēšanas aprakstu...", + "addChatDescription": "Pievienot tērzēšanas aprakstu…", "@addChatDescription": {}, "sentAnAudio": "🎤 {username} nosūtīja skaņu", "@sentAnAudio": { @@ -3326,7 +3326,7 @@ "@commandHint_roomupgrade": {}, "setCustomPermissionLevel": "Iestatīt pielāgotu atļauju līmeni", "@setCustomPermissionLevel": {}, - "setPermissionsLevelDescription": "Lūgums zemāk izvēlēties iepriekšizveidotu lomu vai ievadīt pielāgotu atļauju līmeni starp 0 un 100.", + "setPermissionsLevelDescription": "Lūgums zemāk atlasīt iepriekšizveidotu lomu vai ievadīt pielāgotu atļauju līmeni starp 0 un 100.", "@setPermissionsLevelDescription": {}, "ignoreUser": "Neņemt vērā lietotāju", "@ignoreUser": {}, From 5e4e40af427bcabbbdab8cbe0ea589719ad49f24 Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Tue, 6 May 2025 10:18:56 +0200 Subject: [PATCH 048/157] Translated using Weblate (Latvian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 722fb2268..6c3186f62 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -1740,7 +1740,7 @@ } } }, - "report": "ziņot", + "report": "Ziņot", "@report": {}, "status": "Stāvoklis", "@status": { From 38de39e0460be745504a13cd520fb29b10fc290a Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Wed, 7 May 2025 11:58:49 +0200 Subject: [PATCH 049/157] Translated using Weblate (Latvian) Currently translated at 100.0% (772 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 6c3186f62..1a87e4d43 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -3101,7 +3101,7 @@ "@noticeChatBackupDeviceVerification": {}, "continueText": "Turpināt", "@continueText": {}, - "welcomeText": "Sveicieni! 👋 Šis ir FluffyChat. Tu vari pieteikties jebkurā mājasserverī, kas ir saderīgs ar https://matrix.org. Tad vari tērzēt ar ikvienu. Tas ir milzīgs decentralizētās saziņas tīkls!", + "welcomeText": "Sveicieni! 👋 Šis ir FluffyChat. Tu vari pieteikties jebkurā mājasserverī, kas ir saderīgs ar https://matrix.org. Tad vari tērzēt ar ikvienu. Tas ir milzīgs decentralizētās saziņas tīkls.", "@welcomeText": {}, "blur": "Aizmiglojums:", "@blur": {}, From 8c7563ef885918255efb70594a01159481491db8 Mon Sep 17 00:00:00 2001 From: abdelbasset jabrane Date: Sat, 10 May 2025 04:59:56 +0200 Subject: [PATCH 050/157] Translated using Weblate (Arabic) Currently translated at 90.4% (698 of 772 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ar/ --- assets/l10n/intl_ar.arb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_ar.arb b/assets/l10n/intl_ar.arb index 238a6d7a6..45583ff4e 100644 --- a/assets/l10n/intl_ar.arb +++ b/assets/l10n/intl_ar.arb @@ -2472,7 +2472,7 @@ }, "jump": "قفز", "@jump": {}, - "report": "تقرير", + "report": "الإبلاغ", "@report": {}, "noKeyForThisMessage": "يمكن أن يحدث هذا إذا تم إرسال الرسالة قبل تسجيل الدخول إلى حسابك على هذا الجهاز.\n\nمن الممكن أيضا أن يكون المرسل قد حظر جهازك أو حدث خطأ ما في الاتصال بالإنترنت.\n\nهل يمكنك قراءة الرسالة في جلسة أخرى؟ ثم يمكنك نقل الرسالة منه! انتقل إلى الإعدادات > الأجهزة وتأكد من أن أجهزتك قد تحققت من بعضها البعض. عندما تفتح الغرفة في المرة التالية وتكون كلتا الجلستين في المقدمة ، سيتم إرسال المفاتيح تلقائيا.\n\nألا تريد أن تفقد المفاتيح عند تسجيل الخروج أو تبديل الأجهزة؟ تأكد من تمكين النسخ الاحتياطي للدردشة في الإعدادات.", "@noKeyForThisMessage": {}, @@ -2977,7 +2977,7 @@ "@gallery": {}, "swipeRightToLeftToReply": "اسحب من اليمين إلى اليسار للرد", "@swipeRightToLeftToReply": {}, - "alwaysUse24HourFormat": "false", + "alwaysUse24HourFormat": "خطأ", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, From c54e16726809a0de1674fcf2ed912eb5786d346d Mon Sep 17 00:00:00 2001 From: JSchmerling Date: Mon, 12 May 2025 13:59:36 +0200 Subject: [PATCH 051/157] Translated using Weblate (German) Currently translated at 95.3% (739 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index bff044f66..e65dc2135 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -3289,5 +3289,7 @@ "optionalMessage": "(Optionale) Nachricht...", "@optionalMessage": {}, "notSupportedOnThisDevice": "Nicht unterstützt auf diesem Gerät", - "@notSupportedOnThisDevice": {} + "@notSupportedOnThisDevice": {}, + "ignoreUser": "Nutzer ignorieren", + "@ignoreUser": {} } From 1e938c8e3748865ca10f04ec9bb068003cc51ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 12 May 2025 13:18:57 +0200 Subject: [PATCH 052/157] Translated using Weblate (Estonian) Currently translated at 99.8% (774 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index 95b7ce823..e0e3da5ae 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -3359,5 +3359,26 @@ "pleaseWaitUntilInvited": "Palun oota seni, kuni keegi jututoast saadab sulle kutse.", "@pleaseWaitUntilInvited": {}, "youHaveKnocked": "Sa oled koputanud", - "@youHaveKnocked": {} + "@youHaveKnocked": {}, + "countInvited": "{count} kutsutut", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + } } From 32e31834d18adf2c44151502e716be2232ce75e3 Mon Sep 17 00:00:00 2001 From: Piotr Orzechowski Date: Mon, 12 May 2025 07:07:09 +0200 Subject: [PATCH 053/157] Translated using Weblate (Polish) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ --- assets/l10n/intl_pl.arb | 123 ++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 41 deletions(-) diff --git a/assets/l10n/intl_pl.arb b/assets/l10n/intl_pl.arb index c86f57c02..ae2db7235 100644 --- a/assets/l10n/intl_pl.arb +++ b/assets/l10n/intl_pl.arb @@ -3206,69 +3206,69 @@ "@userSpecificNotificationSettings": {}, "otherNotificationSettings": "Inne ustawienia powiadomień", "@otherNotificationSettings": {}, - "notificationRuleContainsUserName": "Zawiera nazwę użytkownika", + "notificationRuleContainsUserName": "Włącz dla wiadomości z nazwą użytkownika", "@notificationRuleContainsUserName": {}, - "notificationRuleContainsUserNameDescription": "Powiadamia użytkownika kiedy wiadomość zawiera jego nazwę.", + "notificationRuleContainsUserNameDescription": "Włącza powiadomienia kiedy wiadomość zawiera Twoją nazwę użytkownika.", "@notificationRuleContainsUserNameDescription": {}, - "notificationRuleMaster": "Wycisz wszystkie powiadomienia", + "notificationRuleMaster": "Wyłącz wszystkie powiadomienia", "@notificationRuleMaster": {}, - "notificationRuleMasterDescription": "Zastępuje wszystkie inne reguły i wyłącza wszystkie powiadomienia.", + "notificationRuleMasterDescription": "Zastępuje inne reguły i wyłącza wszystkie powiadomienia.", "@notificationRuleMasterDescription": {}, - "notificationRuleSuppressNotices": "Stłum automatyczne wiadomości", + "notificationRuleSuppressNotices": "Wyłącz dla automatycznych wiadomości", "@notificationRuleSuppressNotices": {}, - "notificationRuleSuppressNoticesDescription": "Tłumi powiadomienia z automatycznych klientów, takich jak boty.", + "notificationRuleSuppressNoticesDescription": "Wyłącza powiadomienia z automatycznych klientów, takich jak boty.", "@notificationRuleSuppressNoticesDescription": {}, - "notificationRuleInviteForMe": "Zaproszenia", + "notificationRuleInviteForMe": "Włącz dla zaproszeń", "@notificationRuleInviteForMe": {}, - "notificationRuleInviteForMeDescription": "Powiadamia o zaproszeniach do pokoju.", + "notificationRuleInviteForMeDescription": "Włącza powiadamia o zaproszeniach do pokoju.", "@notificationRuleInviteForMeDescription": {}, - "notificationRuleMemberEvent": "Zdarzenia członków pokoju", + "notificationRuleMemberEvent": "Wyłącz dla zmian członkostwa", "@notificationRuleMemberEvent": {}, - "notificationRuleMemberEventDescription": "Tłumi powiadomienia o zmianach członkostwa w pokoju.", + "notificationRuleMemberEventDescription": "Wyłącza powiadomienia o zmianach członkostwa w pokoju.", "@notificationRuleMemberEventDescription": {}, - "notificationRuleIsUserMention": "Wzmianki", + "notificationRuleIsUserMention": "Włącz dla wzmianek", "@notificationRuleIsUserMention": {}, - "notificationRuleIsUserMentionDescription": "Powiadamia o byciu wzmiankowanym w wiadomości.", + "notificationRuleIsUserMentionDescription": "Włącza powiadomienia o byciu wzmiankowanym w wiadomości.", "@notificationRuleIsUserMentionDescription": {}, - "notificationRuleContainsDisplayName": "Zawiera nazwę wyświetlaną", + "notificationRuleContainsDisplayName": "Włącz dla wiadomości z nazwą wyświetlaną", "@notificationRuleContainsDisplayName": {}, - "notificationRuleContainsDisplayNameDescription": "Powiadamia osobę o wiadomości zawierającej jej nazwę wyświetlaną.", + "notificationRuleContainsDisplayNameDescription": "Włącza powiadamia o wiadomościach zawierających Twoją nazwę wyświetlaną.", "@notificationRuleContainsDisplayNameDescription": {}, - "notificationRuleIsRoomMention": "Wzmianki pokoju", + "notificationRuleIsRoomMention": "Włącz dla wzmianek pokoju", "@notificationRuleIsRoomMention": {}, - "notificationRuleIsRoomMentionDescription": "Powiadamia o wzmiankowaniu całego pokoju.", + "notificationRuleIsRoomMentionDescription": "Włącza powiadomienia o wzmiankowaniu całego pokoju.", "@notificationRuleIsRoomMentionDescription": {}, - "notificationRuleRoomnotif": "Powiadomienia w pokoju", + "notificationRuleRoomnotif": "Włącz dla powiadomień w pokoju", "@notificationRuleRoomnotif": {}, - "notificationRuleRoomnotifDescription": "Powiadamia o wiadomości zawierającej „@room”.", + "notificationRuleRoomnotifDescription": "Włącza powiadamia o wiadomościach zawierających „@room”.", "@notificationRuleRoomnotifDescription": {}, - "notificationRuleTombstone": "Nagrobki", + "notificationRuleTombstone": "Włącz dla „nagrobków”", "@notificationRuleTombstone": {}, - "notificationRuleTombstoneDescription": "Powiadamia o komunikatach dezaktywacji pokojów.", + "notificationRuleTombstoneDescription": "Włącza powiadamia o komunikatach dezaktywacji pokojów.", "@notificationRuleTombstoneDescription": {}, - "notificationRuleReaction": "Reakcje", + "notificationRuleReaction": "Wyłącz dla reakcji", "@notificationRuleReaction": {}, - "notificationRuleReactionDescription": "Tłumi powiadomienia o reakcjach.", + "notificationRuleReactionDescription": "Wyłącza powiadomienia o reakcjach.", "@notificationRuleReactionDescription": {}, - "notificationRuleSuppressEdits": "Stłum edycje", + "notificationRuleSuppressEdits": "Wyłącz dla edycji", "@notificationRuleSuppressEdits": {}, - "notificationRuleSuppressEditsDescription": "Tłumi powiadomienia o edycjach wiadomości.", + "notificationRuleSuppressEditsDescription": "Wyłącza powiadomienia o edycjach wiadomości.", "@notificationRuleSuppressEditsDescription": {}, - "notificationRuleCall": "Połączenia", + "notificationRuleCall": "Włącz dla połączeń", "@notificationRuleCall": {}, - "notificationRuleRoomServerAclDescription": "Wyłącza powiadomienia dla list kontroli dostępu (ACL) serwerów pokojów.", + "notificationRuleRoomServerAclDescription": "Wyłącza powiadomienia dla list kontroli dostępu (ACL) serwera.", "@notificationRuleRoomServerAclDescription": {}, - "notificationRuleRoomServerAcl": "Lista kontroli dostępu serwerów pokojów", + "notificationRuleRoomServerAcl": "Wyłącz dla list kontroli dostępu serwera", "@notificationRuleRoomServerAcl": {}, - "notificationRuleEncryptedRoomOneToOne": "Szyfrowane pokoje „jeden na jeden”", + "notificationRuleEncryptedRoomOneToOne": "Włącz dla szyfrowanych pokojów „jeden na jeden”", "@notificationRuleEncryptedRoomOneToOne": {}, - "notificationRuleCallDescription": "Powiadamia o przychodzących połączeniach.", + "notificationRuleCallDescription": "Włącza powiadomienia o przychodzących połączeniach.", "@notificationRuleCallDescription": {}, - "notificationRuleRoomOneToOne": "Pokoje „jeden na jeden”", + "notificationRuleRoomOneToOne": "Włącz dla pokojów „jeden na jeden”", "@notificationRuleRoomOneToOne": {}, - "notificationRuleRoomOneToOneDescription": "Powiadamia o wiadomościach w pokojach „jeden na jeden” (one-to-one).", + "notificationRuleRoomOneToOneDescription": "Włącza powiadomienia o wiadomościach w pokojach „jeden na jeden” (one-to-one).", "@notificationRuleRoomOneToOneDescription": {}, - "notificationRuleMessage": "Wiadomości", + "notificationRuleMessage": "Włącz dla wiadomości", "@notificationRuleMessage": {}, "unknownPushRule": "Nieznana reguła: '{rule}'", "@unknownPushRule": { @@ -3279,21 +3279,21 @@ } } }, - "notificationRuleEncryptedRoomOneToOneDescription": "Powiadamia o wiadomościach w szyfrowanych pokojach „jeden na jeden” (one-to-one).", + "notificationRuleEncryptedRoomOneToOneDescription": "Włącza powiadomienia o wiadomościach w szyfrowanych pokojach „jeden na jeden” (one-to-one).", "@notificationRuleEncryptedRoomOneToOneDescription": {}, - "notificationRuleEncrypted": "Zaszyfrowane pokoje", + "notificationRuleEncrypted": "Włącz dla zaszyfrowanych pokojów", "@notificationRuleEncrypted": {}, - "notificationRuleJitsi": "Jitsi", + "notificationRuleJitsi": "Włącz dla Jitsi", "@notificationRuleJitsi": {}, - "notificationRuleServerAcl": "Stłum komunikaty o listach kontroli dostępu serwerów pokojów", + "notificationRuleServerAcl": "Wyłącz dla komunikatów o listach kontroli dostępu serwera", "@notificationRuleServerAcl": {}, - "notificationRuleJitsiDescription": "Powiadamia o komunikatach widżetów Jitsi.", + "notificationRuleJitsiDescription": "Włącza powiadomienia o komunikatach widżetów Jitsi.", "@notificationRuleJitsiDescription": {}, - "notificationRuleMessageDescription": "Powiadamia o ogólnych wiadomościach.", + "notificationRuleMessageDescription": "Włącza powiadomienia o ogólnych wiadomościach.", "@notificationRuleMessageDescription": {}, - "notificationRuleEncryptedDescription": "Powiadamia o wiadomościach w zaszyfrowanych pokojach.", + "notificationRuleEncryptedDescription": "Włącza powiadomienia o wiadomościach w zaszyfrowanych pokojach.", "@notificationRuleEncryptedDescription": {}, - "notificationRuleServerAclDescription": "Tłumi powiadomienia o komunikatach o listach kontroli dostępu (ACL) serwerów pokojów.", + "notificationRuleServerAclDescription": "Wyłącza powiadomienia o komunikatach o listach kontroli dostępu (ACL) serwera.", "@notificationRuleServerAclDescription": {}, "newChatRequest": "📩 Nowa prośba o czat", "@newChatRequest": {}, @@ -3337,5 +3337,46 @@ "allDevices": "Wszystkie urządzenia", "@allDevices": {}, "notSupportedOnThisDevice": "Niewspierane na tym urządzeniu", - "@notSupportedOnThisDevice": {} + "@notSupportedOnThisDevice": {}, + "commandHint_roomupgrade": "Zaktualizuj ten pokój do podanej wersji", + "@commandHint_roomupgrade": {}, + "enterNewChat": "Dołącz do nowego czatu", + "@enterNewChat": {}, + "countInvited": "{count} zaproszonych", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "checkList": "Lista kontrolna", + "@checkList": {}, + "setCustomPermissionLevel": "Ustaw własny poziom uprawnień", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Proszę wybrać predefiniowaną rolę poniżej, lub wprowadzić własny poziom uprawnień pomiędzy 0 a 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Ignoruj użytkownika", + "@ignoreUser": {}, + "normalUser": "Zwykły użytkownik", + "@normalUser": {}, + "approve": "Zaakceptuj", + "@approve": {}, + "youHaveKnocked": "Zapukałeś/-aś", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Proszę zaczekać na zaproszenie przez kogoś z pokoju.", + "@pleaseWaitUntilInvited": {} } From f267dc2a56771827b4286e64463953718f2485ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Mon, 12 May 2025 05:40:02 +0200 Subject: [PATCH 054/157] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index 6e81765c9..db2e0cdfc 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -3359,5 +3359,28 @@ "youHaveKnocked": "你已请求加入", "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "在来自该聊天室的某人邀请你之前请等待。", - "@pleaseWaitUntilInvited": {} + "@pleaseWaitUntilInvited": {}, + "countInvited": "邀请了 {count}", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "checkList": "清单", + "@checkList": {}, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + } } From aed7680b7a86deb4cae829de000fdd655ecb5630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Tue, 13 May 2025 14:40:29 +0200 Subject: [PATCH 055/157] Translated using Weblate (Galician) Currently translated at 99.8% (774 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ --- assets/l10n/intl_gl.arb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 9bffb662c..99ffe36a2 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -3359,5 +3359,26 @@ "pleaseWaitUntilInvited": "Agora agarda a que alguén da sala te convide a entrar.", "@pleaseWaitUntilInvited": {}, "youHaveKnocked": "Petaches á porta", - "@youHaveKnocked": {} + "@youHaveKnocked": {}, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "countInvited": "{count} convidadas", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + } } From ddf304fdf91c9146b511402d11dae11744057011 Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Tue, 13 May 2025 12:27:56 +0200 Subject: [PATCH 056/157] Translated using Weblate (Latvian) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 1a87e4d43..6acd42492 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -40,7 +40,7 @@ }, "jumpToLastReadMessage": "Pārlēkt uz pēdējo izlasīto ziņu", "@jumpToLastReadMessage": {}, - "allRooms": "Visas kopu tērzēšanas", + "allRooms": "Visām kopu tērzēšanām", "@allRooms": { "type": "String", "placeholders": {} @@ -83,7 +83,7 @@ }, "reportErrorDescription": "😭 Ak nē! Kaut kas nogāja greizi. Ja ir vēlēšanas, par šo nepilnību var ziņot izstrādātājiem.", "@reportErrorDescription": {}, - "directChats": "Tiešās tērzēšanas", + "directChats": "Tiešajām tērzēšanām", "@directChats": { "type": "String", "placeholders": {} @@ -1323,7 +1323,7 @@ "@sendAsText": { "type": "String" }, - "inviteForMe": "Uzaicinājums man", + "inviteForMe": "Uzaicinājumu man", "@inviteForMe": { "type": "String", "placeholders": {} @@ -1762,7 +1762,7 @@ "type": "String", "placeholders": {} }, - "memberChanges": "Dalībnieku izmaiņas", + "memberChanges": "Dalībnieku izmaiņām", "@memberChanges": { "type": "String", "placeholders": {} @@ -2532,7 +2532,7 @@ "type": "String", "placeholders": {} }, - "botMessages": "Robotprogrammatūras ziņas", + "botMessages": "Robotprogrammatūras ziņām", "@botMessages": { "type": "String", "placeholders": {} @@ -3209,7 +3209,7 @@ "@otherNotificationSettings": {}, "notificationRuleContainsUserName": "Saturs lietotāja vārdu", "@notificationRuleContainsUserName": {}, - "notificationRuleMasterDescription": "Pārspēj visas pārējās kārtulas un atspējo visus paziņojumus.", + "notificationRuleMasterDescription": "Aizvieto visas pārējās kārtulas un atspējo visus paziņojumus.", "@notificationRuleMasterDescription": {}, "notificationRuleInviteForMe": "Uzaicinājums man", "@notificationRuleInviteForMe": {}, @@ -3337,5 +3337,28 @@ "pleaseWaitUntilInvited": "Lūgums tagad uzgaidīt, līdz kāds no istabas uzaicinās Tevi.", "@pleaseWaitUntilInvited": {}, "approve": "Apstiprināt", - "@approve": {} + "@approve": {}, + "checkList": "Pārbaužu saraksts", + "@checkList": {}, + "countInvited": "{count} uzaicināti", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + } } From 174762c6e619a87b6f366898f0a52c948ef9c3cc Mon Sep 17 00:00:00 2001 From: xabirequejo Date: Wed, 14 May 2025 13:46:14 +0200 Subject: [PATCH 057/157] Translated using Weblate (Basque) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ --- assets/l10n/intl_eu.arb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index 4f1665095..4c26d05b6 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -3359,5 +3359,28 @@ "youHaveKnocked": "Sartzeko baimena eskatu duzu", "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "Orain itxaron gelako norbaitek gonbidatzen zaituen arte.", - "@pleaseWaitUntilInvited": {} + "@pleaseWaitUntilInvited": {}, + "checkList": "Kontrol-zerrenda", + "@checkList": {}, + "countInvited": "{count} gonbidatu", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + } } From e7dfa00337426c2faf471ae5ee44c282b9f45a18 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 15 May 2025 04:08:46 +0200 Subject: [PATCH 058/157] Translated using Weblate (Indonesian) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ --- assets/l10n/intl_id.arb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_id.arb b/assets/l10n/intl_id.arb index 25b91cdd0..39342c81c 100644 --- a/assets/l10n/intl_id.arb +++ b/assets/l10n/intl_id.arb @@ -3358,5 +3358,28 @@ "approve": "Terima", "@approve": {}, "youHaveKnocked": "Anda telah mengetuk", - "@youHaveKnocked": {} + "@youHaveKnocked": {}, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "checkList": "Ceklis", + "@checkList": {}, + "countInvited": "{count} diundang", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + } } From 200f2c34be4e50bf0395b1e7cd752e813087a5bb Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 15 May 2025 14:54:08 +0200 Subject: [PATCH 059/157] fix: Join new room after room upgrade --- lib/pages/chat/chat.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index dd1d16c91..9e6483f79 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -1103,13 +1103,18 @@ class ChatController extends State } void goToNewRoomAction() async { + final newRoomId = room + .getState(EventTypes.RoomTombstone)! + .parsedTombstoneContent + .replacementRoom; final result = await showFutureLoadingDialog( context: context, - future: () => room.client.joinRoomById( + future: () => room.client.joinRoom( room .getState(EventTypes.RoomTombstone)! .parsedTombstoneContent .replacementRoom, + via: [newRoomId.domain!], ), ); if (result.error != null) return; From 093ee160672ca62d7d77cb8137ef42d21f9e6e0b Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 15 May 2025 15:27:16 +0200 Subject: [PATCH 060/157] fix: Localizations --- android/app/src/main/res/xml/locale_config.xml | 2 ++ assets/l10n/intl_yue_HK.arb | 1 - assets/l10n/intl_yue_Hant.arb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 assets/l10n/intl_yue_HK.arb diff --git a/android/app/src/main/res/xml/locale_config.xml b/android/app/src/main/res/xml/locale_config.xml index b3bb1ff7a..a37191e79 100644 --- a/android/app/src/main/res/xml/locale_config.xml +++ b/android/app/src/main/res/xml/locale_config.xml @@ -6,6 +6,7 @@ + @@ -50,6 +51,7 @@ + diff --git a/assets/l10n/intl_yue_HK.arb b/assets/l10n/intl_yue_HK.arb deleted file mode 100644 index 0967ef424..000000000 --- a/assets/l10n/intl_yue_HK.arb +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/assets/l10n/intl_yue_Hant.arb b/assets/l10n/intl_yue_Hant.arb index 3f76be695..cce50f372 100644 --- a/assets/l10n/intl_yue_Hant.arb +++ b/assets/l10n/intl_yue_Hant.arb @@ -1,4 +1,5 @@ { + "@@locale": "yue", "normalUser": "正常用家", "@normalUser": {}, "areYouSureYouWantToLogout": "係咪確定要 log out?", From e7034184d61465f7d40057bb866bfa380f90cea1 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 15 May 2025 15:47:35 +0200 Subject: [PATCH 061/157] chore: Follow up crop circle notification avatars --- .../client_download_content_extension.dart | 15 ++++++-- lib/utils/push_helper.dart | 13 ++++--- lib/utils/shortcut_memory_icon.dart | 36 ------------------- 3 files changed, 18 insertions(+), 46 deletions(-) delete mode 100644 lib/utils/shortcut_memory_icon.dart diff --git a/lib/utils/client_download_content_extension.dart b/lib/utils/client_download_content_extension.dart index 9fff1eccd..f93d522f0 100644 --- a/lib/utils/client_download_content_extension.dart +++ b/lib/utils/client_download_content_extension.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:image/image.dart'; import 'package:matrix/matrix.dart'; extension ClientDownloadContentExtension on Client { @@ -10,6 +11,7 @@ extension ClientDownloadContentExtension on Client { bool isThumbnail = false, bool? animated, ThumbnailMethod? thumbnailMethod, + bool rounded = false, }) async { // To stay compatible with previous storeKeys: final cacheKey = isThumbnail @@ -44,10 +46,17 @@ extension ClientDownloadContentExtension on Client { if (response.statusCode != 200) { throw Exception(); } - final remoteData = response.bodyBytes; + var imageData = response.bodyBytes; - await database?.storeFile(cacheKey, remoteData, 0); + if (rounded) { + final image = decodeImage(imageData); + if (image != null) { + imageData = copyCropCircle(image).toUint8List(); + } + } - return remoteData; + await database?.storeFile(cacheKey, imageData, 0); + + return imageData; } } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 41684a3cc..3035b50fa 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -15,7 +16,6 @@ import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; -import 'package:fluffychat/utils/shortcut_memory_icon.dart'; Future pushHelper( PushNotification notification, { @@ -164,11 +164,12 @@ Future _tryPushHelper( : await client .downloadMxcCached( avatar, - thumbnailMethod: ThumbnailMethod.scale, + thumbnailMethod: ThumbnailMethod.crop, width: 256, height: 256, animated: false, isThumbnail: true, + rounded: true, ) .timeout(const Duration(seconds: 3)); } catch (e, s) { @@ -182,11 +183,12 @@ Future _tryPushHelper( : await client .downloadMxcCached( senderAvatar, - thumbnailMethod: ThumbnailMethod.scale, + thumbnailMethod: ThumbnailMethod.crop, width: 256, height: 256, animated: false, isThumbnail: true, + rounded: true, ) .timeout(const Duration(seconds: 3)); } catch (e, s) { @@ -313,10 +315,7 @@ Future _setShortcut( action: AppConfig.inviteLinkPrefix + event.room.id, shortLabel: title, conversationShortcut: true, - icon: await avatarFile?.toShortcutMemoryIcon( - event.room.id, - event.room.client.database, - ), + icon: avatarFile == null ? null : base64Encode(avatarFile), shortcutIconAsset: avatarFile == null ? ShortcutIconAsset.androidAsset : ShortcutIconAsset.memoryAsset, diff --git a/lib/utils/shortcut_memory_icon.dart b/lib/utils/shortcut_memory_icon.dart deleted file mode 100644 index c1557e44a..000000000 --- a/lib/utils/shortcut_memory_icon.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:image/image.dart'; -import 'package:matrix/matrix.dart'; - -extension ShortcutMemoryIcon on Uint8List { - Future toShortcutMemoryIcon( - String roomId, - DatabaseApi? database, - ) async { - final cacheKey = Uri.parse('im.fluffychat://shortcuts/$roomId'); - final cachedFile = await database?.getFile(cacheKey); - if (cachedFile != null) return base64Encode(cachedFile); - - final image = decodeImage(this); - if (image == null) return null; - - final size = image.width < image.height ? image.width : image.height; - final x = (image.width - size) ~/ 2; - final y = (image.height - size) ~/ 2; - - final croppedImage = copyCrop( - image, - x: x, - y: y, - width: size, - height: size, - ); - - final bytes = croppedImage.toUint8List(); - await database?.storeFile(cacheKey, bytes, 0); - - return base64Encode(croppedImage.toUint8List()); - } -} From 7b5a9c10c6842b880990c80679a9d8f9cbeccbcf Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 15 May 2025 15:53:25 +0200 Subject: [PATCH 062/157] refactor: Reduce notification avatar size to 128 --- lib/utils/push_helper.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 3035b50fa..33d0cce30 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -165,8 +165,8 @@ Future _tryPushHelper( .downloadMxcCached( avatar, thumbnailMethod: ThumbnailMethod.crop, - width: 256, - height: 256, + width: 128, + height: 128, animated: false, isThumbnail: true, rounded: true, @@ -184,8 +184,8 @@ Future _tryPushHelper( .downloadMxcCached( senderAvatar, thumbnailMethod: ThumbnailMethod.crop, - width: 256, - height: 256, + width: 128, + height: 128, animated: false, isThumbnail: true, rounded: true, From 08a6442068391e9086fcf88b6112f346456c1e66 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 15 May 2025 16:04:23 +0200 Subject: [PATCH 063/157] Translated using Weblate (German) Currently translated at 95.3% (739 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index e65dc2135..77735f142 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -3274,7 +3274,7 @@ "@notificationRuleJitsi": {}, "allDevices": "Alle Geräte", "@allDevices": {}, - "enterNewChat": "Neuen Chat starten", + "enterNewChat": "Neuen Chat betreten", "@enterNewChat": {}, "shareKeysWith": "Schlüssel teilen mit...", "@shareKeysWith": {}, From befc90a4b39414328981433844ac68fa14397e9c Mon Sep 17 00:00:00 2001 From: Jelv Date: Thu, 15 May 2025 11:48:19 +0200 Subject: [PATCH 064/157] Translated using Weblate (Dutch) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ --- assets/l10n/intl_nl.arb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_nl.arb b/assets/l10n/intl_nl.arb index 399822c16..c41f9ba75 100644 --- a/assets/l10n/intl_nl.arb +++ b/assets/l10n/intl_nl.arb @@ -3354,5 +3354,28 @@ "approve": "Goedkeuren", "@approve": {}, "youHaveKnocked": "Je hebt geklopt", - "@youHaveKnocked": {} + "@youHaveKnocked": {}, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "countInvited": "{count} uitgenodigd", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "checkList": "Checklist", + "@checkList": {} } From e8ee3722dbe3ab693b2381487ce4c22a9c30ed14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 17 May 2025 16:29:32 +0200 Subject: [PATCH 065/157] chore: Follow up new video player --- lib/pages/chat/events/audio_player.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 9dff85028..5609c62d0 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -49,8 +49,8 @@ class AudioPlayerState extends State { AudioPlayerStatus status = AudioPlayerStatus.notDownloaded; late final MatrixState matrix; - late final List? _waveform; - late final String? _durationString; + List? _waveform; + String? _durationString; @override void dispose() { From 15543275c477484ddb388b6e4d4c21e49de9793f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 17 May 2025 16:40:09 +0200 Subject: [PATCH 066/157] chore: Follow up push avatars --- lib/utils/client_download_content_extension.dart | 2 +- lib/utils/push_helper.dart | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/utils/client_download_content_extension.dart b/lib/utils/client_download_content_extension.dart index f93d522f0..e159b7b8d 100644 --- a/lib/utils/client_download_content_extension.dart +++ b/lib/utils/client_download_content_extension.dart @@ -51,7 +51,7 @@ extension ClientDownloadContentExtension on Client { if (rounded) { final image = decodeImage(imageData); if (image != null) { - imageData = copyCropCircle(image).toUint8List(); + imageData = encodePng(copyCropCircle(image)); } } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 33d0cce30..f0ee658bf 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -17,6 +17,8 @@ import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +const notificationAvatarDimension = 128; + Future pushHelper( PushNotification notification, { Client? client, @@ -165,8 +167,8 @@ Future _tryPushHelper( .downloadMxcCached( avatar, thumbnailMethod: ThumbnailMethod.crop, - width: 128, - height: 128, + width: notificationAvatarDimension, + height: notificationAvatarDimension, animated: false, isThumbnail: true, rounded: true, @@ -184,8 +186,8 @@ Future _tryPushHelper( .downloadMxcCached( senderAvatar, thumbnailMethod: ThumbnailMethod.crop, - width: 128, - height: 128, + width: notificationAvatarDimension, + height: notificationAvatarDimension, animated: false, isThumbnail: true, rounded: true, From 87f301902216326aa0743bfb535c1665e27dfe02 Mon Sep 17 00:00:00 2001 From: Piotr Orzechowski Date: Fri, 16 May 2025 08:33:17 +0200 Subject: [PATCH 067/157] Translated using Weblate (Polish) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ --- assets/l10n/intl_pl.arb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/l10n/intl_pl.arb b/assets/l10n/intl_pl.arb index ae2db7235..59451fd54 100644 --- a/assets/l10n/intl_pl.arb +++ b/assets/l10n/intl_pl.arb @@ -3220,7 +3220,7 @@ "@notificationRuleSuppressNoticesDescription": {}, "notificationRuleInviteForMe": "Włącz dla zaproszeń", "@notificationRuleInviteForMe": {}, - "notificationRuleInviteForMeDescription": "Włącza powiadamia o zaproszeniach do pokoju.", + "notificationRuleInviteForMeDescription": "Włącza powiadomienia o zaproszeniach do pokoju.", "@notificationRuleInviteForMeDescription": {}, "notificationRuleMemberEvent": "Wyłącz dla zmian członkostwa", "@notificationRuleMemberEvent": {}, @@ -3232,7 +3232,7 @@ "@notificationRuleIsUserMentionDescription": {}, "notificationRuleContainsDisplayName": "Włącz dla wiadomości z nazwą wyświetlaną", "@notificationRuleContainsDisplayName": {}, - "notificationRuleContainsDisplayNameDescription": "Włącza powiadamia o wiadomościach zawierających Twoją nazwę wyświetlaną.", + "notificationRuleContainsDisplayNameDescription": "Włącza powiadomienia o wiadomościach zawierających Twoją nazwę wyświetlaną.", "@notificationRuleContainsDisplayNameDescription": {}, "notificationRuleIsRoomMention": "Włącz dla wzmianek pokoju", "@notificationRuleIsRoomMention": {}, @@ -3240,11 +3240,11 @@ "@notificationRuleIsRoomMentionDescription": {}, "notificationRuleRoomnotif": "Włącz dla powiadomień w pokoju", "@notificationRuleRoomnotif": {}, - "notificationRuleRoomnotifDescription": "Włącza powiadamia o wiadomościach zawierających „@room”.", + "notificationRuleRoomnotifDescription": "Włącza powiadomienia o wiadomościach zawierających „@room”.", "@notificationRuleRoomnotifDescription": {}, "notificationRuleTombstone": "Włącz dla „nagrobków”", "@notificationRuleTombstone": {}, - "notificationRuleTombstoneDescription": "Włącza powiadamia o komunikatach dezaktywacji pokojów.", + "notificationRuleTombstoneDescription": "Włącza powiadomienia o komunikatach dezaktywacji pokojów.", "@notificationRuleTombstoneDescription": {}, "notificationRuleReaction": "Wyłącz dla reakcji", "@notificationRuleReaction": {}, From 0e14f0f08611c7cfbe88365d90e654186c9072a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=84=82=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=9D?= =?UTF-8?q?=20=28=F0=9D=95=98=F0=9D=95=9A=F0=9D=95=A5=F0=9D=95=99?= =?UTF-8?q?=F0=9D=95=A6=F0=9D=95=93=2E=F0=9D=95=94=F0=9D=95=A0=F0=9D=95=9E?= =?UTF-8?q?/=E2=84=82=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=A0=F0=9D=95=9D=29?= Date: Thu, 15 May 2025 20:48:48 +0200 Subject: [PATCH 068/157] Translated using Weblate (Latvian) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ --- assets/l10n/intl_lv.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_lv.arb b/assets/l10n/intl_lv.arb index 6acd42492..5a2624e64 100644 --- a/assets/l10n/intl_lv.arb +++ b/assets/l10n/intl_lv.arb @@ -573,7 +573,7 @@ } } }, - "dateWithYear": "{day}.{month}.{year}.", + "dateWithYear": "{year}.{month}.{day}", "@dateWithYear": { "type": "String", "placeholders": { From 3fdbaef7b25a8c7c0f5574506bb38e6c40a5c7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 17 May 2025 17:23:35 +0200 Subject: [PATCH 069/157] Translated using Weblate (Estonian) Currently translated at 100.0% (775 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index e0e3da5ae..e99d511d2 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -3380,5 +3380,7 @@ "type": "String" } } - } + }, + "checkList": "Kontrollnimekiri", + "@checkList": {} } From 2f8547869f9e2a4db1013e984313a7f20d301598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:38:31 +0200 Subject: [PATCH 070/157] chore: Let users decide for the title in error reporter --- lib/utils/error_reporter.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/utils/error_reporter.dart b/lib/utils/error_reporter.dart index 7690e4370..95511a9e2 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -49,10 +49,7 @@ class ErrorReporter { onPressed: () => launchUrl( AppConfig.newIssueUrl.resolveUri( Uri( - queryParameters: { - 'template': 'bug_report.yaml', - 'title': '[BUG]: ${message ?? error.toString()}', - }, + queryParameters: {'template': 'bug_report.yaml'}, ), ), mode: LaunchMode.externalApplication, From d4332d5705a6faf5cee7c5497a1be1c37d3e0e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:38:47 +0200 Subject: [PATCH 071/157] ci: Experimental check for duplicate workflow --- .github/workflows/check_duplicates.yaml | 30 +++++++++++++++++++++++++ lib/utils/error_reporter.dart | 4 +--- 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/check_duplicates.yaml diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml new file mode 100644 index 000000000..2d38d99bd --- /dev/null +++ b/.github/workflows/check_duplicates.yaml @@ -0,0 +1,30 @@ +name: Check Duplicates + +on: + issue: + types: [opened] + +jobs: + check_duplicates: + runs-on: ubuntu-latest + steps: + - name: Search for similar issues + run: echo "ISSUES=$(gh issue list --search '${{ github.event.issue.title }}' --json title,body | jq '[.[] | {title, body: (.body[:200])}]')" + - name: Send message to GPT + if: "${{ env.ISSUES != '[]' }}" + run: | + curl -X POST \ + https://api.openai.com/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${{ secrets.OPENAI_KEY }}" \ + -d '{ + "model": "gpt-3.5-turbo", + "messages": [ + {"role": "user", "content": "Please link possible duplications of this issue."}, + {"role": "user", "content": "${{ github.event.issue.title }}\n${{ github.event.issue.body }}"}, + {"role": "user", "content": "${{ env.ISSUES }}"} + ], + "temperature": 0.7 + }' + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file diff --git a/lib/utils/error_reporter.dart b/lib/utils/error_reporter.dart index 95511a9e2..f4b5bce3b 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -48,9 +48,7 @@ class ErrorReporter { AdaptiveDialogAction( onPressed: () => launchUrl( AppConfig.newIssueUrl.resolveUri( - Uri( - queryParameters: {'template': 'bug_report.yaml'}, - ), + Uri(queryParameters: {'template': 'bug_report.yaml'}), ), mode: LaunchMode.externalApplication, ), From 417d162cdbe7bf8b120fc229f516b5479545e072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:42:58 +0200 Subject: [PATCH 072/157] ci: Fix experimental duplicate job --- .github/workflows/check_duplicates.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 2d38d99bd..f604a0a2b 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Search for similar issues - run: echo "ISSUES=$(gh issue list --search '${{ github.event.issue.title }}' --json title,body | jq '[.[] | {title, body: (.body[:200])}]')" + run: echo "ISSUES=$(gh issue list --search '${{ github.event.issue.title }}' --json title,body | jq '[.[] | {title, body: (.body[:200])}]')" >> $GITHUB_ENV - name: Send message to GPT if: "${{ env.ISSUES != '[]' }}" run: | From 0c8639e3fba25cc136f5d5cbd5e0ee092500aa0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:50:19 +0200 Subject: [PATCH 073/157] ci: Fix check duplicates workflow --- .github/workflows/check_duplicates.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index f604a0a2b..dc349b5c0 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -7,9 +7,12 @@ on: jobs: check_duplicates: runs-on: ubuntu-latest + env: + title: ${{ github.event.issue.title }} + body: ${{ github.event.issue.title }} steps: - name: Search for similar issues - run: echo "ISSUES=$(gh issue list --search '${{ github.event.issue.title }}' --json title,body | jq '[.[] | {title, body: (.body[:200])}]')" >> $GITHUB_ENV + run: echo "ISSUES=$(gh issue list --search '${{ env.title }}' --json title,body)" >> $GITHUB_ENV - name: Send message to GPT if: "${{ env.ISSUES != '[]' }}" run: | @@ -21,7 +24,7 @@ jobs: "model": "gpt-3.5-turbo", "messages": [ {"role": "user", "content": "Please link possible duplications of this issue."}, - {"role": "user", "content": "${{ github.event.issue.title }}\n${{ github.event.issue.body }}"}, + {"role": "user", "content": "${{ env.title }}\n${{ env.body }}"}, {"role": "user", "content": "${{ env.ISSUES }}"} ], "temperature": 0.7 From 415b7c4c5c91ba846bcf465c8bd754c407b18c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:52:45 +0200 Subject: [PATCH 074/157] ci: Fix duplicates workflow --- .github/workflows/check_duplicates.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index dc349b5c0..3da6be914 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -12,9 +12,8 @@ jobs: body: ${{ github.event.issue.title }} steps: - name: Search for similar issues - run: echo "ISSUES=$(gh issue list --search '${{ env.title }}' --json title,body)" >> $GITHUB_ENV + run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV - name: Send message to GPT - if: "${{ env.ISSUES != '[]' }}" run: | curl -X POST \ https://api.openai.com/v1/chat/completions \ @@ -25,7 +24,7 @@ jobs: "messages": [ {"role": "user", "content": "Please link possible duplications of this issue."}, {"role": "user", "content": "${{ env.title }}\n${{ env.body }}"}, - {"role": "user", "content": "${{ env.ISSUES }}"} + {"role": "user", "content": "${{ env.issues }}"} ], "temperature": 0.7 }' From 0ee3641fc01b123483083c17552a59806e83c924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 09:54:27 +0200 Subject: [PATCH 075/157] ci: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 3da6be914..6cd7be5a2 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -1,7 +1,7 @@ name: Check Duplicates on: - issue: + issues: types: [opened] jobs: From 2ff3a3b58771912dfb474439d2cf6c7b53f80860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:10:12 +0200 Subject: [PATCH 076/157] ci: Use Gemini instead of openai --- .github/workflows/check_duplicates.yaml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 6cd7be5a2..f30bc034f 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -15,18 +15,17 @@ jobs: run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV - name: Send message to GPT run: | - curl -X POST \ - https://api.openai.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${{ secrets.OPENAI_KEY }}" \ + curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ + -H 'Content-Type: application/json' \ + -X POST \ -d '{ - "model": "gpt-3.5-turbo", - "messages": [ - {"role": "user", "content": "Please link possible duplications of this issue."}, - {"role": "user", "content": "${{ env.title }}\n${{ env.body }}"}, - {"role": "user", "content": "${{ env.issues }}"} - ], - "temperature": 0.7 - }' + "contents": [{ + "parts":[ + {"text": "Please write a very short and nice response to this new issue and link possible duplications."}, + {"text": "${{ env.title }}\n${{ env.body }}"}, + {"text": "${{ env.issues }}"} + ] + }] + }' env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From ab0c06ec1f799216645ddf76dd2fe871e93aade8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:16:08 +0200 Subject: [PATCH 077/157] chore: Follow up find duplications workflow --- .github/workflows/check_duplicates.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index f30bc034f..df7de1a99 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -13,19 +13,18 @@ jobs: steps: - name: Search for similar issues run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV - - name: Send message to GPT + - name: Let Gemini reply run: | - curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ + RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ -H 'Content-Type: application/json' \ -X POST \ -d '{ "contents": [{ "parts":[ - {"text": "Please write a very short and nice response to this new issue and link possible duplications."}, - {"text": "${{ env.title }}\n${{ env.body }}"}, - {"text": "${{ env.issues }}"} + {"text": "Please write a very short and nice response to this new issue and link possible duplications.\n\n${{ env.title }}\n${{ env.body }}\n\nPossible Duplications:\n${{ env.issues }}"} ] }] - }' + }' | jq -r '.candidates[0].content.parts[0].text') + gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From b1da47561fa9da9c4520a906e7aed064a80db965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:16:57 +0200 Subject: [PATCH 078/157] chore: Fix check duplicates job --- .github/workflows/check_duplicates.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index df7de1a99..064e3e7b5 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -10,6 +10,7 @@ jobs: env: title: ${{ github.event.issue.title }} body: ${{ github.event.issue.title }} + GH_TOKEN: ${{ github.token }} steps: - name: Search for similar issues run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV @@ -25,6 +26,7 @@ jobs: ] }] }' | jq -r '.candidates[0].content.parts[0].text') + echo $RESPONSE gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From ae4459a7666f8a81e30cd9877dbabd3ce88932ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:20:29 +0200 Subject: [PATCH 079/157] chore: Enhance prompt --- .github/workflows/check_duplicates.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 064e3e7b5..9860c9218 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -16,13 +16,16 @@ jobs: run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV - name: Let Gemini reply run: | + echo "${{ env.issues }}" RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ -H 'Content-Type: application/json' \ -X POST \ -d '{ "contents": [{ "parts":[ - {"text": "Please write a very short and nice response to this new issue and link possible duplications.\n\n${{ env.title }}\n${{ env.body }}\n\nPossible Duplications:\n${{ env.issues }}"} + {"text": "Please write a very short and nice response to this new issue. If existing link possible duplications by using markdown links."}, + {"text": "${{ env.title }}\n${{ env.body }}"}, + {"text": "Possible duplications:\n${{ env.issues }}"} ] }] }' | jq -r '.candidates[0].content.parts[0].text') From 10c286e95874e8ce24f8fd50afb7de6b278ae1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:22:09 +0200 Subject: [PATCH 080/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 9860c9218..10c6ac824 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -12,6 +12,7 @@ jobs: body: ${{ github.event.issue.title }} GH_TOKEN: ${{ github.token }} steps: + - uses: actions/checkout@v4 - name: Search for similar issues run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV - name: Let Gemini reply From 806d759ea26a1e00ca958d064db152ea610c1e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:24:48 +0200 Subject: [PATCH 081/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 10c6ac824..b7b7c08f1 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Search for similar issues - run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url)" >> $GITHUB_ENV + run: echo "issues=$(gh issue list --search '${{ env.title }}')" >> $GITHUB_ENV - name: Let Gemini reply run: | echo "${{ env.issues }}" @@ -25,6 +25,7 @@ jobs: "contents": [{ "parts":[ {"text": "Please write a very short and nice response to this new issue. If existing link possible duplications by using markdown links."}, + {"text": "To create a link to a possible duplication, use 'https://github.com/krille-chan/fluffychat/issues/'"}, {"text": "${{ env.title }}\n${{ env.body }}"}, {"text": "Possible duplications:\n${{ env.issues }}"} ] From 2020a2f047cb6f3179cccbce1ba75d0ca3a9d43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:29:19 +0200 Subject: [PATCH 082/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index b7b7c08f1..b0069451a 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Search for similar issues - run: echo "issues=$(gh issue list --search '${{ env.title }}')" >> $GITHUB_ENV + run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url | jq -R '. | @json')" >> $GITHUB_ENV - name: Let Gemini reply run: | echo "${{ env.issues }}" @@ -25,7 +25,6 @@ jobs: "contents": [{ "parts":[ {"text": "Please write a very short and nice response to this new issue. If existing link possible duplications by using markdown links."}, - {"text": "To create a link to a possible duplication, use 'https://github.com/krille-chan/fluffychat/issues/'"}, {"text": "${{ env.title }}\n${{ env.body }}"}, {"text": "Possible duplications:\n${{ env.issues }}"} ] From 44af2d88a73857ffdee3c04d6bc11d6076372069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:46:36 +0200 Subject: [PATCH 083/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index b0069451a..84f28a822 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -10,26 +10,25 @@ jobs: env: title: ${{ github.event.issue.title }} body: ${{ github.event.issue.title }} + author: ${{ github.event.issue.author }} GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - name: Search for similar issues - run: echo "issues=$(gh issue list --search '${{ env.title }}' --json title,body,url | jq -R '. | @json')" >> $GITHUB_ENV + run: echo "issues=$(gh issue list --search '${{ env.title }}')" >> $GITHUB_ENV - name: Let Gemini reply run: | echo "${{ env.issues }}" RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ -H 'Content-Type: application/json' \ -X POST \ - -d '{ - "contents": [{ - "parts":[ - {"text": "Please write a very short and nice response to this new issue. If existing link possible duplications by using markdown links."}, - {"text": "${{ env.title }}\n${{ env.body }}"}, - {"text": "Possible duplications:\n${{ env.issues }}"} + -d "{ + \"contents\": [{ + \"parts\":[ + {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author @$author to check those. Otherwise just responde with an empty String.\n\nIssue title: $title\nIssue body: $body\n\nPossible duplications:\n$issues\"} ] - }] - }' | jq -r '.candidates[0].content.parts[0].text') + }] + }" | jq -r '.candidates[0].content.parts[0].text') echo $RESPONSE gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" env: From 9f864291fa8e319f981092e61f8a081e6e2b0390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:51:26 +0200 Subject: [PATCH 084/157] chore: Fix check duplicates --- .github/workflows/check_duplicates.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 84f28a822..b16f5f895 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -1,4 +1,4 @@ -name: Check Duplicates +name: Check duplicates on: issues: @@ -14,18 +14,17 @@ jobs: GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - - name: Search for similar issues - run: echo "issues=$(gh issue list --search '${{ env.title }}')" >> $GITHUB_ENV - - name: Let Gemini reply + - name: Check duplicates run: | - echo "${{ env.issues }}" + ISSUES=$(gh issue list --search '${{ env.title }}') + echo $ISSUES RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ -H 'Content-Type: application/json' \ -X POST \ -d "{ \"contents\": [{ \"parts\":[ - {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author @$author to check those. Otherwise just responde with an empty String.\n\nIssue title: $title\nIssue body: $body\n\nPossible duplications:\n$issues\"} + {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author @$author to check those. Otherwise just responde with an empty String.\n\nIssue title: $title\nIssue body: $body\n\nPossible duplications:\n$ISSUES\"} ] }] }" | jq -r '.candidates[0].content.parts[0].text') From 80c1650f0eeb668ae7fa5f8e598f79cd861fc879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:53:44 +0200 Subject: [PATCH 085/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index b16f5f895..276d40d24 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -24,7 +24,7 @@ jobs: -d "{ \"contents\": [{ \"parts\":[ - {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author @$author to check those. Otherwise just responde with an empty String.\n\nIssue title: $title\nIssue body: $body\n\nPossible duplications:\n$ISSUES\"} + {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. Otherwise just responde with an empty String.\n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} ] }] }" | jq -r '.candidates[0].content.parts[0].text') From a9bd48965e9363bb98f0076b76bcacd6e35c3809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 10:57:58 +0200 Subject: [PATCH 086/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 276d40d24..279247d36 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -24,11 +24,13 @@ jobs: -d "{ \"contents\": [{ \"parts\":[ - {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a short response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. Otherwise just responde with an empty String.\n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} + {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a very short and nice response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. If you don't find any duplications, just reply with 'false'. \n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} ] }] }" | jq -r '.candidates[0].content.parts[0].text') echo $RESPONSE - gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" + if [ "$RESPONSE" != "false" ]; then + gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" + fi env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From c005b320d42a4243371a54936a70247223400eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:05:24 +0200 Subject: [PATCH 087/157] chore: fix duplicates job --- .github/workflows/check_duplicates.yaml | 31 ++++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 279247d36..30b2b6641 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -11,26 +11,29 @@ jobs: title: ${{ github.event.issue.title }} body: ${{ github.event.issue.title }} author: ${{ github.event.issue.author }} + number: ${{ github.event.issue.number }} GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - name: Check duplicates run: | - ISSUES=$(gh issue list --search '${{ env.title }}') + ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ number }}"") echo $ISSUES - RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ - -H 'Content-Type: application/json' \ - -X POST \ - -d "{ - \"contents\": [{ - \"parts\":[ - {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a very short and nice response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. If you don't find any duplications, just reply with 'false'. \n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} - ] - }] - }" | jq -r '.candidates[0].content.parts[0].text') - echo $RESPONSE - if [ "$RESPONSE" != "false" ]; then - gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" + if [ "$ISSUES" != ""]; then + RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ + -H 'Content-Type: application/json' \ + -X POST \ + -d "{ + \"contents\": [{ + \"parts\":[ + {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a very short and nice response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. If you don't find any duplications, just reply with 'false'. \n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} + ] + }] + }" | jq -r '.candidates[0].content.parts[0].text') + echo $RESPONSE + if [ "$RESPONSE" != "false" ]; then + gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" + fi fi env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From cb1aedb9ae7b8ea1726357ad67e6ac112bd83cdc Mon Sep 17 00:00:00 2001 From: Jana Date: Sun, 18 May 2025 09:24:27 +0200 Subject: [PATCH 088/157] Translated using Weblate (German) Currently translated at 99.8% (774 of 775 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 96 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 77735f142..b78da3386 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -3291,5 +3291,99 @@ "notSupportedOnThisDevice": "Nicht unterstützt auf diesem Gerät", "@notSupportedOnThisDevice": {}, "ignoreUser": "Nutzer ignorieren", - "@ignoreUser": {} + "@ignoreUser": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Eins-zu-Eins-Chats.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "commandHint_roomupgrade": "Aktualisieren Sie diesen Raum auf die angegebene Raumversion", + "@commandHint_roomupgrade": {}, + "notificationRuleMemberEvent": "Mitgliederveranstaltung", + "@notificationRuleMemberEvent": {}, + "notificationRuleInviteForMeDescription": "Benachrichtigt den Benutzer, wenn er in einen Raum eingeladen wird.", + "@notificationRuleInviteForMeDescription": {}, + "notificationRuleIsUserMentionDescription": "Benachrichtigt den Benutzer, wenn er in einer Nachricht direkt erwähnt wird.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleRoomnotifDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht „@room“ enthält.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in Einzelchats.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleEncryptedDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Räumen.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsiDescription": "Benachrichtigt den Benutzer über Jitsi-Widget-Ereignisse.", + "@notificationRuleJitsiDescription": {}, + "checkList": "Checkliste", + "@checkList": {}, + "countInvited": "{count} invited", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notificationRuleIsUserMention": "Benutzererwähnung", + "@notificationRuleIsUserMention": {}, + "notificationRuleContainsDisplayName": "Enthält den Anzeigenamen", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht seinen Anzeigenamen enthält.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "Chat-Erwähnung", + "@notificationRuleIsRoomMention": {}, + "notificationRuleRoomnotif": "Chat-Benachritigung", + "@notificationRuleRoomnotif": {}, + "notificationRuleTombstoneDescription": "Benachrichtigt den Benutzer über Nachrichten zur Raumdeaktivierung.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Verschlüsselter Einzelchat", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleRoomOneToOne": "Einzelchat", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleServerAclDescription": "Unterdrückt Benachrichtigungen für Server-ACL-Ereignisse.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "Unbekannte Push-Regel '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Wenn Sie diese Benachrichtigungseinstellung löschen, kann dies nicht rückgängig gemacht werden.", + "@deletePushRuleCanNotBeUndone": {}, + "crossVerifiedDevices": "Cross-verifizierte Geräte", + "@crossVerifiedDevices": {}, + "notificationRuleIsRoomMentionDescription": "Benachrichtigt den Benutzer, wenn ein Raum erwähnt wird.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomServerAcl": "Raumserver-ACL", + "@notificationRuleRoomServerAcl": {}, + "crossVerifiedDevicesIfEnabled": "Cross-verifizierte Geräte, falls aktiviert", + "@crossVerifiedDevicesIfEnabled": {}, + "notificationRuleServerAcl": "Unterdrücken von Server-ACL-Ereignissen", + "@notificationRuleServerAcl": {}, + "notificationRuleMemberEventDescription": "Unterdrückt Benachrichtigungen zu Mitgliedschaftsereignissen.", + "@notificationRuleMemberEventDescription": {}, + "sentVoiceMessage": "🎙️ {duration} - {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "normalUser": "Normaler Benutzer", + "@normalUser": {}, + "setCustomPermissionLevel": "Benutzerdefinierte Berechtigungsstufe festlegen", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Bitte wählen Sie unten eine vordefinierte Rolle aus oder geben Sie eine benutzerdefinierte Berechtigungsstufe zwischen 0 und 100 ein.", + "@setPermissionsLevelDescription": {}, + "approve": "Genehmigen", + "@approve": {}, + "youHaveKnocked": "Du hast geklopft", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Bitte warte nun, bis dich jemand aus dem Raum auffordert.", + "@pleaseWaitUntilInvited": {} } From b1b57bd82bba148ec9ccb5051ce97fa01469b3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:12:55 +0200 Subject: [PATCH 089/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 30b2b6641..e86745876 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -11,13 +11,13 @@ jobs: title: ${{ github.event.issue.title }} body: ${{ github.event.issue.title }} author: ${{ github.event.issue.author }} - number: ${{ github.event.issue.number }} + number: ${{ github.event.issue.user.login }} GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - name: Check duplicates run: | - ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ number }}"") + ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}"") echo $ISSUES if [ "$ISSUES" != ""]; then RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ From c018fd418cd2cd2bc346641dc669ccb61d6fed0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:14:23 +0200 Subject: [PATCH 090/157] chore: Fix check duplicates --- .github/workflows/check_duplicates.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index e86745876..fe806b107 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -10,14 +10,14 @@ jobs: env: title: ${{ github.event.issue.title }} body: ${{ github.event.issue.title }} - author: ${{ github.event.issue.author }} - number: ${{ github.event.issue.user.login }} + author: ${{ github.event.issue.user.login }} + number: ${{ github.event.issue.number }} GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - name: Check duplicates run: | - ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}"") + ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}") echo $ISSUES if [ "$ISSUES" != ""]; then RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ From d417f1d9f46b9728735c71229f5a219d45581449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:24:02 +0200 Subject: [PATCH 091/157] chore: Fix check duplicates job --- .github/workflows/check_duplicates.yaml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index fe806b107..f6fe9b1f6 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -9,7 +9,6 @@ jobs: runs-on: ubuntu-latest env: title: ${{ github.event.issue.title }} - body: ${{ github.event.issue.title }} author: ${{ github.event.issue.user.login }} number: ${{ github.event.issue.number }} GH_TOKEN: ${{ github.token }} @@ -17,23 +16,9 @@ jobs: - uses: actions/checkout@v4 - name: Check duplicates run: | - ISSUES=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}") - echo $ISSUES + issues=$(gh issue list --search 'Unable to play video' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') if [ "$ISSUES" != ""]; then - RESPONSE=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${{ secrets.GEMINI_KEY }}" \ - -H 'Content-Type: application/json' \ - -X POST \ - -d "{ - \"contents\": [{ - \"parts\":[ - {\"text\": \"Please compare issue title and body to possible duplications. If you think this issue could be a duplication, write a very short and nice response to this Github issue and link the possible duplication issues and ask the author ${{ env.author }} to check those. If you don't find any duplications, just reply with 'false'. \n\nIssue title: ${{ env.title }}\nIssue body: ${{ env.body }}\n\nPossible duplications:\n$ISSUES\"} - ] - }] - }" | jq -r '.candidates[0].content.parts[0].text') - echo $RESPONSE - if [ "$RESPONSE" != "false" ]; then - gh issue comment ${{ github.event.issue.number }} --body "$RESPONSE" - fi + gh issue comment ${{ github.event.issue.number }} --body "Thanks for reporting @${{ env.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" fi env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From b060d70a48fce78d19902b3b6c905602a3cada99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:29:45 +0200 Subject: [PATCH 092/157] chore: Fix duplicates job --- .github/workflows/playground.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/workflows/playground.sh diff --git a/.github/workflows/playground.sh b/.github/workflows/playground.sh new file mode 100644 index 000000000..32474e905 --- /dev/null +++ b/.github/workflows/playground.sh @@ -0,0 +1,4 @@ +number=${{ env.number }} +issues=$(gh issue list --search 'Unable to play video' --json number,title,url | jq --arg num "$number" -r 'map(select(.number != ($num | tonumber))) | .[] | "- [" + .title + "](" + .url + ")"') + +echo -e "Thanks for reporting @${{ evn.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" From 417e3d4b92fb22d858692209462326c9bf78fe2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:30:11 +0200 Subject: [PATCH 093/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 2 +- .github/workflows/playground.sh | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 .github/workflows/playground.sh diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index f6fe9b1f6..5dbcccacc 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - name: Check duplicates run: | - issues=$(gh issue list --search 'Unable to play video' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') + issues=$(gh issue list --search '${{ title }}' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') if [ "$ISSUES" != ""]; then gh issue comment ${{ github.event.issue.number }} --body "Thanks for reporting @${{ env.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" fi diff --git a/.github/workflows/playground.sh b/.github/workflows/playground.sh deleted file mode 100644 index 32474e905..000000000 --- a/.github/workflows/playground.sh +++ /dev/null @@ -1,4 +0,0 @@ -number=${{ env.number }} -issues=$(gh issue list --search 'Unable to play video' --json number,title,url | jq --arg num "$number" -r 'map(select(.number != ($num | tonumber))) | .[] | "- [" + .title + "](" + .url + ")"') - -echo -e "Thanks for reporting @${{ evn.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" From fc300b74bf250e4dafad77eff33f22864ae5bcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:31:16 +0200 Subject: [PATCH 094/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 5dbcccacc..31260fc19 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - name: Check duplicates run: | - issues=$(gh issue list --search '${{ title }}' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') + issues=$(gh issue list --search '${{ env.title }}' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') if [ "$ISSUES" != ""]; then gh issue comment ${{ github.event.issue.number }} --body "Thanks for reporting @${{ env.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" fi From 7a70e0d36d421a3fec47d04e20c4790f53ad9622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:37:53 +0200 Subject: [PATCH 095/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 31260fc19..634171a25 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -16,9 +16,9 @@ jobs: - uses: actions/checkout@v4 - name: Check duplicates run: | - issues=$(gh issue list --search '${{ env.title }}' --json number,title,url | jq -r 'map(select(.number != ${{ env.number }})) | .[] | "- [" + .title + "](" + .url + ")"') + issues=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}") if [ "$ISSUES" != ""]; then - gh issue comment ${{ github.event.issue.number }} --body "Thanks for reporting @${{ env.author }}.\nPlease check if this could be a duplicate of one of these issues:\n$issues" + gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues" fi env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file From 445c212be9b3970639aa716b1d486221ae213bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:45:27 +0200 Subject: [PATCH 096/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 634171a25..db17d27a0 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -16,9 +16,9 @@ jobs: - uses: actions/checkout@v4 - name: Check duplicates run: | - issues=$(gh issue list --search '${{ env.title }}' | grep -v "${{ env.number }}") + issues=$(gh issue list --search '${{ env.title }}' --json number,title,url) + number=${{ env.number }} + issues_filtered=$(echo "$issues" | jq --arg num "$number" 'map(select(.number != ($num | tonumber)))') if [ "$ISSUES" != ""]; then - gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues" - fi - env: - OPENAI_KEY: ${{ secrets.OPENAI_KEY }} \ No newline at end of file + gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown=$" + fi \ No newline at end of file From 3a0ea19d39b18ab10c50a8a743f8f21a2bcad018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:46:34 +0200 Subject: [PATCH 097/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index db17d27a0..d38722559 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -20,5 +20,5 @@ jobs: number=${{ env.number }} issues_filtered=$(echo "$issues" | jq --arg num "$number" 'map(select(.number != ($num | tonumber)))') if [ "$ISSUES" != ""]; then - gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown=$" + gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown" fi \ No newline at end of file From f94d3a96d2102c5f737dac20d709ca6673113b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:47:46 +0200 Subject: [PATCH 098/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index d38722559..31c5e6f3b 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -19,6 +19,7 @@ jobs: issues=$(gh issue list --search '${{ env.title }}' --json number,title,url) number=${{ env.number }} issues_filtered=$(echo "$issues" | jq --arg num "$number" 'map(select(.number != ($num | tonumber)))') + issues_markdown=$(echo "$issues_filtered" | jq -r '.[] | "- [" + .title + "](" + .url + ")"') if [ "$ISSUES" != ""]; then gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown" fi \ No newline at end of file From 98642f676a0645e44ff18f9a1b51a6d96979e8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:52:02 +0200 Subject: [PATCH 099/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 31c5e6f3b..42f1a95b3 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -19,7 +19,9 @@ jobs: issues=$(gh issue list --search '${{ env.title }}' --json number,title,url) number=${{ env.number }} issues_filtered=$(echo "$issues" | jq --arg num "$number" 'map(select(.number != ($num | tonumber)))') - issues_markdown=$(echo "$issues_filtered" | jq -r '.[] | "- [" + .title + "](" + .url + ")"') - if [ "$ISSUES" != ""]; then + if [ "$(echo "$issues_filtered" | jq length)" -eq 0 ]; then + echo "No duplicates found." + else + issues_markdown=$(echo "$issues_filtered" | jq -r '.[] | "- [" + .title + "](" + .url + ")"') gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown" fi \ No newline at end of file From 616d36ad39190ba34e093bfc3c967c28032764dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sun, 18 May 2025 11:55:00 +0200 Subject: [PATCH 100/157] chore: Fix duplicates job --- .github/workflows/check_duplicates.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index 42f1a95b3..a3ded8d90 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -23,5 +23,6 @@ jobs: echo "No duplicates found." else issues_markdown=$(echo "$issues_filtered" | jq -r '.[] | "- [" + .title + "](" + .url + ")"') - gh issue comment ${{ github.event.issue.number }} --body "@${{ env.author }}\nPossible duplication of:\n$issues_markdown" + formatted_body=$(echo -e "@${{ env.author }}\nPossible duplication of:\n$issues_markdown") + gh issue comment ${{ github.event.issue.number }} --body "$formatted_body" fi \ No newline at end of file From 92d3e7f1c31a310913bac55088452eb34ceef37b Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 19 May 2025 19:36:49 +0200 Subject: [PATCH 101/157] chore: Follow up VideoPlayer in ImageViewer --- lib/pages/image_viewer/image_viewer_view.dart | 48 ++++++++++--------- lib/pages/image_viewer/video_player.dart | 5 ++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 983171be2..d5c55903b 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -75,6 +75,7 @@ class ImageViewerView extends StatelessWidget { focusNode: controller.focusNode, onKeyEvent: controller.onKeyEvent, child: PageView.builder( + scrollDirection: Axis.vertical, controller: controller.pageController, itemCount: controller.allEvents.length, itemBuilder: (context, i) { @@ -119,30 +120,33 @@ class ImageViewerView extends StatelessWidget { }, ), ), - if (hovered && controller.canGoBack) - Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: IconButton( - style: iconButtonStyle, - tooltip: L10n.of(context).previous, - icon: const Icon(Icons.chevron_left_outlined), - onPressed: controller.prevImage, - ), - ), - ), - if (hovered && controller.canGoNext) + if (hovered) Align( alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: IconButton( - style: iconButtonStyle, - tooltip: L10n.of(context).next, - icon: const Icon(Icons.chevron_right_outlined), - onPressed: controller.nextImage, - ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (controller.canGoBack) + Padding( + padding: const EdgeInsets.all(12.0), + child: IconButton( + style: iconButtonStyle, + tooltip: L10n.of(context).previous, + icon: const Icon(Icons.arrow_upward_outlined), + onPressed: controller.prevImage, + ), + ), + if (controller.canGoNext) + Padding( + padding: const EdgeInsets.all(12.0), + child: IconButton( + style: iconButtonStyle, + tooltip: L10n.of(context).next, + icon: const Icon(Icons.arrow_downward_outlined), + onPressed: controller.nextImage, + ), + ), + ], ), ), ], diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index b85d6799d..29c48275e 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -69,6 +69,10 @@ class EventVideoPlayerState extends State { await videoPlayerController.initialize(); + final infoMap = widget.event.content.tryGetMap('info'); + final videoWidth = infoMap?.tryGet('w') ?? 400; + final videoHeight = infoMap?.tryGet('h') ?? 300; + // Create a ChewieController on top. setState(() { _chewieController = ChewieController( @@ -77,6 +81,7 @@ class EventVideoPlayerState extends State { autoPlay: true, autoInitialize: true, looping: true, + aspectRatio: videoHeight == 0 ? null : videoWidth / videoHeight, ); }); } on IOException catch (e) { From 73e796c0b6656e46133b62eece16af49ce595e9a Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 19 May 2025 19:41:22 +0200 Subject: [PATCH 102/157] build: Update shared preferences android --- pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index 1590cc319..ae8630911 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1683,7 +1683,7 @@ packages: sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" shared_preferences_foundation: dependency: transitive description: From 12320d41984f4f362ca7dd601ab0c433a6d1814c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 19 May 2025 19:52:25 +0200 Subject: [PATCH 103/157] docs: Remove border for header and footer --- docs/index.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/index.html b/docs/index.html index 22cf4a39f..198412105 100644 --- a/docs/index.html +++ b/docs/index.html @@ -22,8 +22,7 @@ -
+
-
+