diff --git a/commet/lib/client/components/read_receipts/read_receipt_component.dart b/commet/lib/client/components/read_receipts/read_receipt_component.dart index 0ea26b956..319c95e54 100644 --- a/commet/lib/client/components/read_receipts/read_receipt_component.dart +++ b/commet/lib/client/components/read_receipts/read_receipt_component.dart @@ -5,6 +5,8 @@ import 'package:commet/client/timeline_events/timeline_event.dart'; abstract class ReadReceiptComponent implements RoomComponent { Stream get onReadReceiptsUpdated; + bool? get usePublicReadReceiptsForRoom; + Future setUsePublicReadReceiptsForRoom(bool? value); List? getReceipts(TimelineEvent event); } diff --git a/commet/lib/client/components/typing_indicators/typing_indicator_component.dart b/commet/lib/client/components/typing_indicators/typing_indicator_component.dart index 4a8a7f35f..4cc05be64 100644 --- a/commet/lib/client/components/typing_indicators/typing_indicator_component.dart +++ b/commet/lib/client/components/typing_indicators/typing_indicator_component.dart @@ -6,6 +6,9 @@ abstract class TypingIndicatorComponent implements RoomComponent { Stream get onTypingUsersUpdated; + bool? get typingIndicatorEnabledForRoom; + Future setTypingIndicatorEnabledForRoom(bool? value); + List get typingUsers; Future setTypingStatus(bool status); diff --git a/commet/lib/client/components/user_presence/user_presence_component.dart b/commet/lib/client/components/user_presence/user_presence_component.dart index 55bd86f8d..7837e5397 100644 --- a/commet/lib/client/components/user_presence/user_presence_component.dart +++ b/commet/lib/client/components/user_presence/user_presence_component.dart @@ -29,6 +29,12 @@ class UserPresence { abstract class UserPresenceComponent implements Component { Stream<(String, UserPresence)> get onPresenceChanged; + bool get usePublicReadReceipts; + Future setUsePublicReadReceipts(bool value); + + bool get typingIndicatorEnabled; + Future setTypingIndicatorEnabled(bool value); + Future getUserPresence(String userId); Future setStatus(UserPresenceStatus status, diff --git a/commet/lib/client/matrix/components/read_receipts/matrix_read_receipt_component.dart b/commet/lib/client/matrix/components/read_receipts/matrix_read_receipt_component.dart index b235adb21..10361eaf5 100644 --- a/commet/lib/client/matrix/components/read_receipts/matrix_read_receipt_component.dart +++ b/commet/lib/client/matrix/components/read_receipts/matrix_read_receipt_component.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:commet/client/components/read_receipts/read_receipt_component.dart'; import 'package:commet/client/matrix/components/matrix_sync_listener.dart'; +import 'package:commet/client/matrix/components/user_presence/matrix_user_presence.dart'; import 'package:commet/client/matrix/matrix_client.dart'; import 'package:commet/client/matrix/matrix_room.dart'; import 'package:commet/client/matrix/matrix_timeline.dart'; @@ -18,6 +19,9 @@ class MatrixReadReceiptComponent @override MatrixRoom room; + static const String publicReadReceiptsKey = + "chat.commet.public_read_receipts"; + final StreamController _controller = StreamController.broadcast(); @@ -30,6 +34,22 @@ class MatrixReadReceiptComponent Map userToPreviousReceipt = {}; + @override + bool? get usePublicReadReceiptsForRoom { + var publicReadReceiptsForRoom = room + .matrixRoom.roomAccountData[publicReadReceiptsKey]?.content["enabled"]; + return publicReadReceiptsForRoom is bool ? publicReadReceiptsForRoom : null; + } + + @override + Future setUsePublicReadReceiptsForRoom(bool? value) async => + await client.matrixClient.setAccountDataPerRoom( + client.matrixClient.userID!, + room.matrixRoom.id, + publicReadReceiptsKey, + {"enabled": value}, + ); + @override onSync(JoinedRoomUpdate update) { final ephemeral = update.ephemeral; @@ -59,6 +79,10 @@ class MatrixReadReceiptComponent } } } + + client.matrixClient.receiptsPublicByDefault = client + .getComponent()! + .usePublicReadReceipts; } void handleEvent(String eventId, String userId) { diff --git a/commet/lib/client/matrix/components/typing_indicators/matrix_typing_indicators_component.dart b/commet/lib/client/matrix/components/typing_indicators/matrix_typing_indicators_component.dart index de7871618..8e192deb9 100644 --- a/commet/lib/client/matrix/components/typing_indicators/matrix_typing_indicators_component.dart +++ b/commet/lib/client/matrix/components/typing_indicators/matrix_typing_indicators_component.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:commet/client/components/typing_indicators/typing_indicator_component.dart'; import 'package:commet/client/matrix/components/matrix_sync_listener.dart'; +import 'package:commet/client/matrix/components/user_presence/matrix_user_presence.dart'; import 'package:commet/client/matrix/matrix_client.dart'; import 'package:commet/client/matrix/matrix_member.dart'; import 'package:commet/client/matrix/matrix_room.dart'; @@ -19,8 +20,29 @@ class MatrixTypingIndicatorsComponent MatrixTypingIndicatorsComponent(this.client, this.room); + static const String publicTypingIndicatorKey = + "chat.commet.private_typing_indicator"; + final StreamController _controller = StreamController.broadcast(); + @override + bool? get typingIndicatorEnabledForRoom { + var publicTypingIndicatorForRoom = room.matrixRoom + .roomAccountData[publicTypingIndicatorKey]?.content["enabled"]; + return publicTypingIndicatorForRoom is bool + ? publicTypingIndicatorForRoom + : null; + } + + @override + Future setTypingIndicatorEnabledForRoom(bool? value) async => + await client.matrixClient.setAccountDataPerRoom( + client.matrixClient.userID!, + room.matrixRoom.id, + publicTypingIndicatorKey, + {"enabled": value}, + ); + @override onSync(JoinedRoomUpdate update) { final ephemeral = update.ephemeral; @@ -44,7 +66,11 @@ class MatrixTypingIndicatorsComponent .toList(); @override - Future setTypingStatus(bool status) { - return room.matrixRoom.setTyping(status, timeout: 2000); + Future setTypingStatus(bool status) async { + var typingIndicatorEnabled = client + .getComponent()! + .typingIndicatorEnabled; + if (typingIndicatorEnabledForRoom ?? typingIndicatorEnabled) + return room.matrixRoom.setTyping(status, timeout: 2000); } } diff --git a/commet/lib/client/matrix/components/user_presence/matrix_user_presence.dart b/commet/lib/client/matrix/components/user_presence/matrix_user_presence.dart index a1f1f670b..ff775ee71 100644 --- a/commet/lib/client/matrix/components/user_presence/matrix_user_presence.dart +++ b/commet/lib/client/matrix/components/user_presence/matrix_user_presence.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:commet/client/components/user_presence/user_presence_component.dart'; import 'package:commet/client/components/user_presence/user_presence_lifecycle_watcher.dart'; +import 'package:commet/client/matrix/components/read_receipts/matrix_read_receipt_component.dart'; +import 'package:commet/client/matrix/components/typing_indicators/matrix_typing_indicators_component.dart'; import 'package:commet/client/matrix/matrix_client.dart'; import 'package:commet/utils/in_memory_cache.dart'; import 'package:matrix/matrix.dart'; @@ -28,6 +30,42 @@ class MatrixUserPresenceComponent UserPresenceLifecycleWatcher().init(); } + @override + bool get usePublicReadReceipts { + var publicReadReceipts = client + .matrixClient + .accountData[MatrixReadReceiptComponent.publicReadReceiptsKey] + ?.content["enabled"]; + return publicReadReceipts is bool ? publicReadReceipts : true; + } + + @override + Future setUsePublicReadReceipts(bool value) async { + await client.matrixClient.setAccountData( + client.matrixClient.userID!, + MatrixReadReceiptComponent.publicReadReceiptsKey, + {"enabled": value}, + ); + client.matrixClient.receiptsPublicByDefault = value; + } + + @override + bool get typingIndicatorEnabled { + var publicTypingIndicator = client + .matrixClient + .accountData[MatrixTypingIndicatorsComponent.publicTypingIndicatorKey] + ?.content["enabled"]; + return publicTypingIndicator is bool ? publicTypingIndicator : true; + } + + @override + Future setTypingIndicatorEnabled(bool value) async => + await client.matrixClient.setAccountData( + client.matrixClient.userID!, + MatrixTypingIndicatorsComponent.publicTypingIndicatorKey, + {"enabled": value}, + ); + @override Future getUserPresence(String userId) async { final presence = await client.matrixClient.fetchCurrentPresence(userId); diff --git a/commet/lib/client/matrix/matrix_room.dart b/commet/lib/client/matrix/matrix_room.dart index 0b1c3028e..6af321e96 100644 --- a/commet/lib/client/matrix/matrix_room.dart +++ b/commet/lib/client/matrix/matrix_room.dart @@ -13,6 +13,8 @@ import 'package:commet/client/components/room_component.dart'; import 'package:commet/client/components/user_color/user_color_component.dart'; import 'package:commet/client/matrix/components/calendar_room_component/matrix_calendar_room_component.dart'; import 'package:commet/client/matrix/components/emoticon/matrix_room_emoticon_component.dart'; +import 'package:commet/client/matrix/components/read_receipts/matrix_read_receipt_component.dart'; +import 'package:commet/client/matrix/components/user_presence/matrix_user_presence.dart'; import 'package:commet/client/matrix/matrix_attachment.dart'; import 'package:commet/client/matrix/matrix_client.dart'; import 'package:commet/client/matrix/matrix_member.dart'; @@ -844,8 +846,20 @@ class MatrixRoom extends Room { @override Future markAsRead() async { var tl = await matrixRoom.getTimeline(); + var readReceiptComponent = getComponent(); - await tl.setReadMarker(); + bool public = true; + var presenceComp = client.getComponent(); + + if (presenceComp?.usePublicReadReceipts != null) { + public = presenceComp!.usePublicReadReceipts; + } + + if (readReceiptComponent?.usePublicReadReceiptsForRoom != null) { + public = readReceiptComponent!.usePublicReadReceiptsForRoom!; + } + + await tl.setReadMarker(public: public); } @override diff --git a/commet/lib/client/matrix/matrix_timeline.dart b/commet/lib/client/matrix/matrix_timeline.dart index 6556b8001..1e853a78f 100644 --- a/commet/lib/client/matrix/matrix_timeline.dart +++ b/commet/lib/client/matrix/matrix_timeline.dart @@ -126,11 +126,12 @@ class MatrixTimeline extends Timeline { @override void markAsRead(TimelineEvent event) async { + var receipts = room.getComponent(); if (event.status == TimelineEventStatus.synced || event.status == TimelineEventStatus.sent) { - await _matrixTimeline?.setReadMarker(); + await _matrixTimeline?.setReadMarker( + public: receipts?.usePublicReadReceiptsForRoom); - var receipts = room.getComponent(); receipts?.handleEvent(event.eventId, room.client.self!.identifier); } } diff --git a/commet/lib/ui/pages/settings/categories/account/preferences/preferences_chat_privacy.dart b/commet/lib/ui/pages/settings/categories/account/preferences/preferences_chat_privacy.dart new file mode 100644 index 000000000..b0b6f6b23 --- /dev/null +++ b/commet/lib/ui/pages/settings/categories/account/preferences/preferences_chat_privacy.dart @@ -0,0 +1,130 @@ +import 'package:commet/client/client.dart'; +import 'package:commet/client/components/user_presence/user_presence_component.dart'; +import 'package:commet/utils/error_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:tiamat/tiamat.dart' as tiamat; + +class ChatPrivacyPreferences extends StatefulWidget { + const ChatPrivacyPreferences({required this.client, super.key}); + final T client; + + @override + State createState() => _ChatPrivacyPreferences(); +} + +class _ChatPrivacyPreferences extends State { + bool publicReadReceipts = true; + bool publicTypingIndicator = true; + + UserPresenceComponent get userPresenceComponent => + widget.client.getComponent()!; + + String get labelChatPrivacyTitle => Intl.message("Chat Privacy", + desc: "Header for the chat privacy section in settings", + name: "labelChatPrivacyTitle"); + + String get labelPublicReadReceiptsToggle => Intl.message("Read receipts", + desc: + "Label for the toggle for enabling and disabling sending read receipts", + name: "labelPublicReadReceiptsToggle"); + + String get labelPublicReadReceiptsDescription => Intl.message( + "Let other members of a room know when you have read their messages.", + desc: + "description for the toggle for enabling and disabling sending read receipts", + name: "labelPublicReadReceiptsDescriptionn"); + + String get labelTypingIndicatorsToggle => Intl.message("Typing indicator", + desc: + "Label for the toggle for enabling and disabling sending typing indicator", + name: "labelTypingIndicatorsToggle"); + + String get labelPublicTypingIndicatorDescription => Intl.message( + "Let other members of a room know when you are typing a message.", + desc: + "description for the toggle for enabling and disabling sending typing indicator", + name: "labelPublicTypingIndicatorDescription"); + + @override + void initState() { + setState(() { + publicReadReceipts = userPresenceComponent.usePublicReadReceipts; + publicTypingIndicator = userPresenceComponent.typingIndicatorEnabled; + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return tiamat.Panel( + header: labelChatPrivacyTitle, + mode: tiamat.TileType.surfaceContainerLow, + child: Column(children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + tiamat.Text.labelEmphasised( + labelPublicReadReceiptsToggle), + tiamat.Text.labelLow(labelPublicReadReceiptsDescription) + ]), + ), + tiamat.Switch( + state: publicReadReceipts, + onChanged: (value) async { + setState(() { + publicReadReceipts = value; + }); + await ErrorUtils.tryRun(context, () async { + await userPresenceComponent + .setUsePublicReadReceipts(value); + }); + setState(() => publicReadReceipts = value); + }) + ], + ), + ), + const SizedBox( + height: 10, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + tiamat.Text.labelEmphasised(labelTypingIndicatorsToggle), + tiamat.Text.labelLow( + labelPublicTypingIndicatorDescription) + ]), + ), + tiamat.Switch( + state: publicTypingIndicator, + onChanged: (value) async { + setState(() { + publicTypingIndicator = value; + }); + await ErrorUtils.tryRun(context, () async { + await userPresenceComponent + .setTypingIndicatorEnabled(value); + }); + setState(() => publicTypingIndicator = value); + }) + ], + ), + ), + ]), + ); + } +} diff --git a/commet/lib/ui/pages/settings/categories/account/preferences/preferences_tab.dart b/commet/lib/ui/pages/settings/categories/account/preferences/preferences_tab.dart new file mode 100644 index 000000000..a2a18137d --- /dev/null +++ b/commet/lib/ui/pages/settings/categories/account/preferences/preferences_tab.dart @@ -0,0 +1,47 @@ +import 'package:commet/client/client.dart'; +import 'package:commet/client/client_manager.dart'; +import 'package:commet/ui/molecules/account_selector.dart'; +import 'package:commet/ui/pages/settings/categories/account/preferences/preferences_chat_privacy.dart'; +import 'package:flutter/material.dart'; + +class AccountSettingsTab extends StatefulWidget { + const AccountSettingsTab({required this.clientManager, super.key}); + final ClientManager clientManager; + + @override + State createState() => _AccountSettingsTabState(); +} + +class _AccountSettingsTabState extends State { + late Client selectedClient; + + @override + void initState() { + selectedClient = widget.clientManager.clients.first; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + if (widget.clientManager.clients.length > 1) + AccountSelector( + widget.clientManager.clients, + onClientSelected: (client) { + setState(() { + selectedClient = client; + }); + }, + ), + const SizedBox( + height: 4, + ), + ChatPrivacyPreferences( + client: selectedClient, + key: ValueKey( + "chat-privacy-preferences_${selectedClient.identifier}")), + ], + ); + } +} diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart index 77c02aab1..ce48dd168 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart @@ -96,9 +96,12 @@ class _MatrixSecurityTabState extends State { mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 4, 0, 4), - child: crossSigningPanel(), + const SizedBox( + height: 4, + ), + crossSigningPanel(), + const SizedBox( + height: 4, ), sessionsPanel() ], diff --git a/commet/lib/ui/pages/settings/categories/account/settings_category_account.dart b/commet/lib/ui/pages/settings/categories/account/settings_category_account.dart index 142d73e4d..a94f92d4c 100644 --- a/commet/lib/ui/pages/settings/categories/account/settings_category_account.dart +++ b/commet/lib/ui/pages/settings/categories/account/settings_category_account.dart @@ -2,6 +2,7 @@ import 'package:commet/client/client_manager.dart'; import 'package:commet/main.dart'; import 'package:commet/ui/pages/settings/categories/account/account_emoji/account_emoji_tab.dart'; import 'package:commet/ui/pages/settings/categories/account/account_state/account_state_tab.dart'; +import 'package:commet/ui/pages/settings/categories/account/preferences/preferences_tab.dart'; import 'package:commet/ui/pages/settings/categories/account/profile/profile_edit_tab.dart'; import 'package:commet/ui/pages/settings/settings_category.dart'; import 'package:commet/ui/pages/settings/settings_tab.dart'; @@ -21,6 +22,10 @@ class SettingsCategoryAccount implements SettingsCategory { name: "labelSettingsTabProfile", desc: "Label for the Profile settings page"); + String get labelSettingsTabPrivacy => Intl.message("Privacy", + name: "labelSettingsTabPrivacy", + desc: "Label for the Privacy settings page"); + String get labelSettingsTabSecurity => Intl.message("Security", name: "labelSettingsTabSecurity", desc: "Label for the Security settings page"); @@ -58,6 +63,14 @@ class SettingsCategoryAccount implements SettingsCategory { clientManager: Provider.of(context), ); }), + SettingsTab( + label: labelSettingsTabPrivacy, + icon: Icons.public, + pageBuilder: (context) { + return AccountSettingsTab( + clientManager: Provider.of(context), + ); + }), SettingsTab( label: labelSettingsTabSecurity, icon: Icons.security, diff --git a/commet/lib/ui/pages/settings/categories/room/general/room_general_chat_privacy.dart b/commet/lib/ui/pages/settings/categories/room/general/room_general_chat_privacy.dart new file mode 100644 index 000000000..a38e1d4fc --- /dev/null +++ b/commet/lib/ui/pages/settings/categories/room/general/room_general_chat_privacy.dart @@ -0,0 +1,166 @@ +import 'dart:async'; + +import 'package:commet/client/components/read_receipts/read_receipt_component.dart'; +import 'package:commet/client/components/typing_indicators/typing_indicator_component.dart'; +import 'package:commet/client/components/user_presence/user_presence_component.dart'; +import 'package:commet/client/room.dart'; +import 'package:commet/utils/common_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' as material; +import 'package:intl/intl.dart'; +import 'package:tiamat/tiamat.dart' as tiamat; + +class RoomGeneralChatPrivacySettings extends StatefulWidget { + const RoomGeneralChatPrivacySettings(this.room); + + final Room room; + + @override + State createState() => + _RoomGeneralChatPrivacySettings(); +} + +class _RoomGeneralChatPrivacySettings + extends State { + bool? publicReadReceiptsForRoom; + bool? typingIndicatorEnabledForRoom; + + String labelDefaultReadReceiptsOption(pref) => Intl.message( + "Use global preference ($pref)", + desc: + "Label to use the global preference, showing the current global preference value", + name: "labelDefaultReadReceiptsOption", + args: [pref]); + + String labelDefaultTypingIndicatorsOption(pref) => Intl.message( + "Use global preference ($pref)", + desc: + "Label to use the global preference, showing the current global preference value", + name: "labelDefaultTypingIndicatorsOption", + args: [pref]); + + String get labelReadReceiptsTitle => Intl.message("Read receipts", + desc: + "Label for the toggle for enabling and disabling public read receipts", + name: "labelReadReceiptsTitle"); + + String get labelTypingIndicatorTitle => Intl.message("Typing indicators", + desc: "Label for the toggle for enabling and disabling typing indicators", + name: "labelTypingIndicatorTitle"); + + @override + void initState() { + setState(() { + publicReadReceiptsForRoom = widget.room + .getComponent()! + .usePublicReadReceiptsForRoom; + typingIndicatorEnabledForRoom = widget.room + .getComponent()! + .typingIndicatorEnabledForRoom; + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column(children: [ + publicReadReceiptsSettings(), + const SizedBox( + height: 10, + ), + publicTypingIndicatorSettings(), + ]); + } + + Widget publicReadReceiptsSettings() { + var usePublicReadReceipts = widget.room.client + .getComponent()! + .usePublicReadReceipts; + return tiamat.Panel( + mode: tiamat.TileType.surfaceContainerLow, + header: labelReadReceiptsTitle, + child: material.Material( + color: material.Colors.transparent, + child: Column( + children: [ + tiamat.RadioButton( + groupValue: publicReadReceiptsForRoom, + value: null, + icon: material.Icons.remove_outlined, + text: labelDefaultReadReceiptsOption(usePublicReadReceipts + ? CommonStrings.labelPublic + : CommonStrings.labelPrivate), + onChanged: onReadReceiptPrefChanged, + ), + tiamat.RadioButton( + groupValue: publicReadReceiptsForRoom, + value: false, + icon: material.Icons.hide_source, + text: CommonStrings.labelPrivate, + onChanged: onReadReceiptPrefChanged, + ), + tiamat.RadioButton( + groupValue: publicReadReceiptsForRoom, + value: true, + icon: material.Icons.public, + text: CommonStrings.labelPublic, + onChanged: onReadReceiptPrefChanged, + ), + ], + ), + )); + } + + Widget publicTypingIndicatorSettings() { + var typingIndicatorEnabled = widget.room.client + .getComponent()! + .typingIndicatorEnabled; + return tiamat.Panel( + mode: tiamat.TileType.surfaceContainerLow, + header: labelTypingIndicatorTitle, + child: material.Material( + color: material.Colors.transparent, + child: Column( + children: [ + tiamat.RadioButton( + groupValue: typingIndicatorEnabledForRoom, + value: null, + icon: material.Icons.remove_outlined, + text: labelDefaultTypingIndicatorsOption(typingIndicatorEnabled + ? CommonStrings.labelEnabled + : CommonStrings.labelDisabled), + onChanged: onTypingIndicatorPrefChanged, + ), + tiamat.RadioButton( + groupValue: typingIndicatorEnabledForRoom, + value: false, + icon: material.Icons.close, + text: CommonStrings.labelDisabled, + onChanged: onTypingIndicatorPrefChanged, + ), + tiamat.RadioButton( + groupValue: typingIndicatorEnabledForRoom, + value: true, + icon: material.Icons.check, + text: CommonStrings.labelEnabled, + onChanged: onTypingIndicatorPrefChanged, + ), + ], + ), + )); + } + + Future onReadReceiptPrefChanged(bool? value) async { + widget.room + .getComponent()! + .setUsePublicReadReceiptsForRoom(value); + setState(() => publicReadReceiptsForRoom = value); + } + + Future onTypingIndicatorPrefChanged(bool? value) async { + widget.room + .getComponent()! + .setTypingIndicatorEnabledForRoom(value); + setState(() => typingIndicatorEnabledForRoom = value); + } +} diff --git a/commet/lib/ui/pages/settings/categories/room/general/room_general_settings_page.dart b/commet/lib/ui/pages/settings/categories/room/general/room_general_settings_page.dart index f8c62a369..9f0dadc12 100644 --- a/commet/lib/ui/pages/settings/categories/room/general/room_general_settings_page.dart +++ b/commet/lib/ui/pages/settings/categories/room/general/room_general_settings_page.dart @@ -1,5 +1,6 @@ import 'package:commet/client/matrix/matrix_room.dart'; import 'package:commet/client/room.dart'; +import 'package:commet/ui/pages/settings/categories/room/general/room_general_chat_privacy.dart'; import 'package:commet/ui/pages/matrix/room_address_settings/matrix_room_address_settings.dart'; import 'package:commet/ui/pages/settings/categories/room/general/room_general_settings_view.dart'; import 'package:flutter/widgets.dart'; @@ -32,8 +33,13 @@ class _RoomGeneralSettingsPageState extends State { const SizedBox( height: 10, ), - if (widget.room is MatrixRoom) - MatrixRoomAddressSettings((widget.room as MatrixRoom).matrixRoom) + RoomGeneralChatPrivacySettings(widget.room), + if (widget.room is MatrixRoom) ...[ + const SizedBox( + height: 10, + ), + MatrixRoomAddressSettings((widget.room as MatrixRoom).matrixRoom), + ] ], ); } diff --git a/commet/lib/utils/common_strings.dart b/commet/lib/utils/common_strings.dart index bc37eee05..32f821fe9 100644 --- a/commet/lib/utils/common_strings.dart +++ b/commet/lib/utils/common_strings.dart @@ -87,6 +87,18 @@ class CommonStrings { static String get promptCopy => Intl.message("Copy", desc: "Prompt to copy text", name: "promptCopy"); + static String get labelPublic => + Intl.message("Public", desc: "Label for public", name: "labelPublic"); + + static String get labelPrivate => + Intl.message("Private", desc: "Label for private", name: "labelPrivate"); + + static String get labelEnabled => + Intl.message("Enabled", desc: "Label for enabled", name: "labelEnabled"); + + static String get labelDisabled => Intl.message("Disabled", + desc: "Label for disabled", name: "labelDisabled"); + static String get promptCopyComplete => Intl.message("Copied!", desc: "Prompt text for after a copy has been completed", name: "promptCopyComplete");