Merge branch 'main' of krille-chan/fluffychat

This commit is contained in:
Alexey 2026-03-25 14:39:04 +03:00
commit b93b263427
200 changed files with 1285 additions and 943 deletions

View file

@ -1,21 +1,11 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:collection/collection.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:file_picker/file_picker.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:pasteboard/pasteboard.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
@ -38,6 +28,16 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/share_scaffold_dialog.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:mime/mime.dart';
import 'package:pasteboard/pasteboard.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import '../../utils/account_bundles.dart';
import '../../utils/localized_exception_extension.dart';
import 'send_file_dialog.dart';
@ -554,6 +554,7 @@ class ChatController extends State<ChatPageWithRoom>
timeline?.cancelSubscriptions();
timeline = null;
inputFocus.removeListener(_inputFocusListener);
if (currentlyTyping) room.setTyping(false);
super.dispose();
}
@ -706,7 +707,7 @@ class ChatController extends State<ChatPageWithRoom>
String path,
int duration,
List<int> waveform,
String? fileName,
String fileName,
) async {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final audioFile = XFile(path);
@ -718,9 +719,17 @@ class ChatController extends State<ChatPageWithRoom>
final bytes = bytesResult.result;
if (bytes == null) return;
final mimeType = lookupMimeType(fileName, headerBytes: bytes);
final extension = mimeType == null ? null : extensionFromMime(mimeType);
if (extension != null) {
fileName =
'voice_message_${DateTime.now().millisecondsSinceEpoch}.$extension';
}
final file = MatrixAudioFile(
bytes: bytes,
name: fileName ?? audioFile.path,
name: fileName,
mimeType: mimeType,
);
room
@ -1298,6 +1307,7 @@ class ChatController extends State<ChatPageWithRoom>
if (AppSettings.sendTypingNotifications.value) {
typingCoolDown?.cancel();
typingCoolDown = Timer(const Duration(seconds: 2), () {
if (!mounted) return;
typingCoolDown = null;
currentlyTyping = false;
room.setTyping(false);

View file

@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
class ChatAppBarListTile extends StatelessWidget {
final Widget? leading;

View file

@ -1,8 +1,3 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@ -11,6 +6,9 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/sync_status_localization.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/presence_builder.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
class ChatAppBarTitle extends StatelessWidget {
final ChatController controller;

View file

@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/sticker_picker_dialog.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'chat.dart';
class ChatEmojiPicker extends StatelessWidget {

View file

@ -1,9 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@ -13,6 +8,9 @@ import 'package:fluffychat/pages/chat/typing_indicators.dart';
import 'package:fluffychat/utils/account_config.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class ChatEventList extends StatelessWidget {
final ChatController controller;

View file

@ -1,8 +1,4 @@
import 'package:flutter/material.dart';
import 'package:emoji_picker_flutter/locales/default_emoji_set_locale.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/recording_input_row.dart';
@ -12,6 +8,9 @@ import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../config/themes.dart';
import 'chat.dart';
import 'input_bar.dart';

View file

@ -1,11 +1,7 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:badges/badges.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
@ -14,6 +10,7 @@ import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
import 'package:fluffychat/pages/chat/chat_event_list.dart';
import 'package:fluffychat/pages/chat/encryption_button.dart';
import 'package:fluffychat/pages/chat/jitsi_popup_button.dart';
import 'package:fluffychat/pages/chat/pinned_events.dart';
import 'package:fluffychat/pages/chat/reply_display.dart';
import 'package:fluffychat/utils/account_config.dart';
@ -23,6 +20,9 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../utils/stream_extension.dart';
import 'chat_emoji_picker.dart';
import 'chat_input_row.dart';
@ -224,14 +224,16 @@ class ChatView extends StatelessWidget {
],
),
] else if (!controller.room.isArchived) ...[
if (AppSettings.experimentalVoip.value &&
if ((AppSettings.experimentalVoip.value &&
Matrix.of(context).voipPlugin != null &&
controller.room.isDirectChat)
controller.room.isDirectChat))
IconButton(
onPressed: controller.onPhoneButtonTap,
icon: const Icon(Icons.call_outlined),
tooltip: L10n.of(context).placeCall,
),
)
else if (AppSettings.jitsiFeature.value)
JitsiPopupButton(controller.room),
EncryptionButton(controller.room),
ChatSettingsPopupMenu(controller.room, true),
],

View file

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:badges/badges.dart' as b;
import 'package:fluffychat/l10n/l10n.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import '../../widgets/matrix.dart';
class EncryptionButton extends StatelessWidget {

View file

@ -1,14 +1,12 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
extension EventInfoDialogExtension on Event {
void showInfoDialog(BuildContext context) => showAdaptiveBottomSheet(

View file

@ -1,22 +1,21 @@
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';

View file

@ -1,11 +1,9 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class CuteContent extends StatefulWidget {
final Event event;

View file

@ -1,18 +1,17 @@
import 'package:collection/collection.dart';
import 'package:fluffychat/utils/code_highlight_theme.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 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:highlight/highlight.dart' show highlight;
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as parser;
import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/code_highlight_theme.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';
class HtmlMessage extends StatelessWidget {

View file

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import '../../../widgets/blur_hash.dart';
class ImageBubble extends StatelessWidget {

View file

@ -1,12 +1,6 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
@ -18,6 +12,11 @@ import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
import '../../../config/app_config.dart';
import 'message_content.dart';
import 'message_reactions.dart';

View file

@ -1,11 +1,5 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/events/poll.dart';
@ -16,6 +10,11 @@ import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
import '../../../config/app_config.dart';
import '../../../utils/event_checkbox_extension.dart';
import '../../../utils/platform_infos.dart';

View file

@ -1,13 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
class MessageDownloadContent extends StatelessWidget {
final Event event;

View file

@ -1,13 +1,11 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class MessageReactions extends StatelessWidget {
final Event event;

View file

@ -1,15 +1,13 @@
import 'package:flutter/material.dart';
import 'package:async/async.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart' hide Result;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart' hide Result;
class PollWidget extends StatelessWidget {
final Event event;

View file

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../../config/app_config.dart';
class ReplyContent extends StatelessWidget {

View file

@ -1,12 +1,11 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../../config/app_config.dart';
class StateMessage extends StatelessWidget {

View file

@ -1,16 +1,15 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/blur_hash.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import '../../image_viewer/image_viewer.dart';
class EventVideoPlayer extends StatelessWidget {

View file

@ -1,15 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:matrix/matrix.dart';
import 'package:slugify/slugify.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/markdown_context_builder.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:matrix/matrix.dart';
import 'package:slugify/slugify.dart';
import '../../widgets/avatar.dart';
import '../../widgets/matrix.dart';
import 'command_hints.dart';

View file

@ -0,0 +1,175 @@
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:url_launcher/url_launcher.dart';
class JitsiPopupButton extends StatelessWidget {
final Room room;
const JitsiPopupButton(this.room, {super.key});
Future<void> _startCall(BuildContext context, bool isAudioOnly) async {
final l10n = L10n.of(context);
final urlResult = await showFutureLoadingDialog(
context: context,
future: () async {
final conferenceId = room.client.generateUniqueTransactionId();
final domain = AppSettings.jitsiDomain.value;
final uri = Uri(
scheme: 'https',
host: domain,
path: conferenceId,
fragment: isAudioOnly ? 'config.startWithVideoMuted=true' : null,
);
await room.addWidget(
MatrixWidget(
room: room,
name: 'Jitsi Meet',
type: 'jitsi',
url: uri.toString(),
data: {
'domain': domain,
'isAudioOnly': isAudioOnly,
'conferenceId': conferenceId,
'roomName': room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)),
),
},
),
);
return uri;
},
);
final url = urlResult.result;
if (url == null) return;
await launchUrl(url);
if (!context.mounted) return;
final consent = await showOkCancelAlertDialog(
context: context,
title: l10n.removeCallFromChat,
message: l10n.removeCallFromChatDescription,
okLabel: l10n.remove,
);
if (consent != OkCancelResult.ok) return;
if (!context.mounted) return;
await _endAllCalls(context);
}
Future<void> _endAllCalls(BuildContext context) => showFutureLoadingDialog(
context: context,
future: () async {
final activeJitsiCalls = room.states['im.vector.modular.widgets']?.values
.where((state) => state.content['type'] == 'jitsi');
if (activeJitsiCalls == null) return;
for (final call in activeJitsiCalls) {
await room.deleteWidget(call.stateKey!);
}
},
);
@override
Widget build(BuildContext context) {
final l10n = L10n.of(context);
final activeJitsiCalls = room.widgets
.where((widget) => widget.type == 'jitsi')
.map((widget) {
final isAudioOnly = widget.data?.tryGet<bool>('isAudioOnly') ?? false;
final domain = widget.data?.tryGet<String>('domain');
final conferenceId = widget.data?.tryGet<String>('conferenceId');
return (
isAudioOnly: isAudioOnly,
domain: domain,
conferenceId: conferenceId,
);
})
.toList();
final canEditCalls = room.canChangeStateEvent('im.vector.modular.widgets');
if (activeJitsiCalls.isEmpty && !canEditCalls) {
return const SizedBox.shrink();
}
return PopupMenuButton(
itemBuilder: (context) => [
...activeJitsiCalls.map(
(call) => PopupMenuItem(
onTap: () => launchUrl(
Uri(
scheme: 'https',
host: call.domain,
path: call.conferenceId,
fragment: call.isAudioOnly
? 'config.startWithVideoMuted=true'
: null,
),
),
child: Row(
mainAxisSize: .min,
children: [
Icon(call.isAudioOnly ? Icons.add_call : Icons.video_call),
const SizedBox(width: 12),
Text(
call.isAudioOnly ? l10n.joinVoiceCall : l10n.joinVideoCall,
),
],
),
),
),
if (canEditCalls) ...[
if (activeJitsiCalls.isEmpty) ...[
PopupMenuItem(
onTap: () => _startCall(context, true),
child: Row(
mainAxisSize: .min,
children: [
const Icon(Icons.add_call),
const SizedBox(width: 12),
Text(l10n.startVoiceCall),
],
),
),
PopupMenuItem(
onTap: () => _startCall(context, false),
child: Row(
mainAxisSize: .min,
children: [
const Icon(Icons.video_call),
const SizedBox(width: 12),
Text(l10n.startVideoCall),
],
),
),
] else
PopupMenuItem(
onTap: () => _endAllCalls(context),
child: Row(
mainAxisSize: .min,
children: [
Icon(
Icons.call_end_outlined,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(width: 12),
Text(
l10n.removeCallForEveryone,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
],
),
),
],
],
icon: Badge(
label: Text(l10n.live),
isLabelVisible: activeJitsiCalls.isNotEmpty,
child: Icon(Icons.video_call_outlined),
),
);
}
}

View file

@ -1,15 +1,13 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class PinnedEvents extends StatelessWidget {
final ChatController controller;

View file

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat_input_row.dart';
import 'package:fluffychat/pages/chat/recording_view_model.dart';
import 'package:flutter/material.dart';
class RecordingInputRow extends StatelessWidget {
final RecordingViewModelState state;
final Future<void> Function(String, int, List<int>, String?) onSend;
final Future<void> Function(String, int, List<int>, String) onSend;
const RecordingInputRow({
required this.state,
required this.onSend,

View file

@ -1,20 +1,19 @@
import 'dart:async';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:matrix/matrix.dart';
import 'package:path/path.dart' as path_lib;
import 'package:path_provider/path_provider.dart';
import 'package:record/record.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'events/audio_player.dart';
class RecordingViewModel extends StatefulWidget {
@ -68,7 +67,7 @@ class RecordingViewModelState extends State<RecordingViewModel> {
? AudioEncoder.opus
: AudioEncoder.aacLc;
fileName =
'recording${DateTime.now().microsecondsSinceEpoch}.${codec.fileExtension}';
'voice_message_${DateTime.now().millisecondsSinceEpoch}.${codec.fileExtension}';
String? path;
if (!kIsWeb) {
final tempDir = await getTemporaryDirectory();
@ -169,7 +168,7 @@ class RecordingViewModelState extends State<RecordingViewModel> {
String path,
int duration,
List<int> waveform,
String? fileName,
String fileName,
)
onSend,
) async {
@ -190,7 +189,7 @@ class RecordingViewModelState extends State<RecordingViewModel> {
isSending = true;
});
try {
await onSend(path, duration.inMilliseconds, waveform, fileName);
await onSend(path, duration.inMilliseconds, waveform, fileName!);
} catch (e, s) {
Logs().e('Unable to send voice message', e, s);
setState(() {

View file

@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../config/themes.dart';
import 'chat.dart';
import 'events/reply_content.dart';

View file

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class SeenByRow extends StatelessWidget {
final Event event;

View file

@ -1,11 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:async/async.dart' show Result;
import 'package:cross_file/cross_file.dart';
import 'package:matrix/matrix.dart' hide Result;
import 'package:mime/mime.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
@ -15,6 +9,11 @@ import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/size_string.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/dialog_text_field.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart' hide Result;
import 'package:mime/mime.dart';
import '../../utils/resize_video.dart';
class SendFileDialog extends StatefulWidget {

View file

@ -1,15 +1,13 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/events/map_bubble.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:matrix/matrix.dart';
class SendLocationDialog extends StatefulWidget {
final Room room;

View file

@ -1,9 +1,7 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class StartPollBottomSheet extends StatefulWidget {
final Room room;

View file

@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import '../../widgets/avatar.dart';
class StickerPickerDialog extends StatefulWidget {

View file

@ -1,12 +1,11 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class TypingIndicators extends StatelessWidget {
final ChatController controller;