diff --git a/lib/components/drawer/sidebar.dart b/lib/components/drawer/sidebar.dart index 10e82026..f25cbf7c 100644 --- a/lib/components/drawer/sidebar.dart +++ b/lib/components/drawer/sidebar.dart @@ -10,6 +10,7 @@ import 'package:rtchat/models/audio.dart'; import 'package:rtchat/models/channels.dart'; import 'package:rtchat/models/layout.dart'; import 'package:rtchat/models/qr_code.dart'; +import 'package:rtchat/models/quick_links.dart'; import 'package:rtchat/models/user.dart'; import 'package:rtchat/screens/settings/qr.dart'; import 'package:rtchat/urls.dart'; @@ -225,110 +226,126 @@ class Sidebar extends StatefulWidget { } class _SidebarState extends State { - @override - Widget build(BuildContext context) { - final tiles = [ - ListTile( - leading: const Icon(Icons.add_link_sharp), - title: Text(AppLocalizations.of(context)!.configureQuickLinks), - onTap: () => - Navigator.of(context).pushNamed("/settings/quick-links")), - - const Divider(), - - // setting - Consumer(builder: (context, layoutModel, child) { - if (!layoutModel.locked) { + Widget _buildActionTile(BuildContext context, String actionId) { + switch (actionId) { + case 'rainMode': + return Consumer(builder: (context, layoutModel, _) { return ListTile( leading: const Icon(Icons.thunderstorm), - title: Text(AppLocalizations.of(context)!.enableRainMode), - subtitle: Text(AppLocalizations.of(context)!.enableRainModeSubtitle, + title: Text(layoutModel.locked + ? AppLocalizations.of(context)!.disableRainMode + : AppLocalizations.of(context)!.enableRainMode), + subtitle: Text( + layoutModel.locked + ? AppLocalizations.of(context)!.disableRainModeSubtitle + : AppLocalizations.of(context)!.enableRainModeSubtitle, overflow: TextOverflow.ellipsis), - onTap: () async { + onTap: () { layoutModel.locked = !layoutModel.locked; Navigator.pop(context); }, ); - } - - return ListTile( - leading: const Icon(Icons.thunderstorm), - title: Text(AppLocalizations.of(context)!.disableRainMode), - subtitle: Text(AppLocalizations.of(context)!.disableRainModeSubtitle, - overflow: TextOverflow.ellipsis), - onTap: () async { - layoutModel.locked = !layoutModel.locked; - Navigator.pop(context); - }, - ); - }), - - Consumer(builder: (context, audioModel, child) { - if (audioModel.sources.isEmpty) { - return Container(); - } - return ListTile( - leading: const Icon(Icons.cached_outlined), - title: Text(AppLocalizations.of(context)!.refreshAudioSources), - onTap: () async { - final scaffoldMessenger = ScaffoldMessenger.of(context); - final count = await audioModel.refreshAllSources(); - if (!context.mounted) return; - scaffoldMessenger.showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context)! - .refreshAudioSourcesCount(count)))); - }, - ); - }), + }); + case 'refreshAudio': + return Consumer(builder: (context, audioModel, _) { + if (audioModel.sources.isEmpty) return Container(); + return ListTile( + leading: const Icon(Icons.cached_outlined), + title: Text(AppLocalizations.of(context)!.refreshAudioSources), + onTap: () async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + final count = await audioModel.refreshAllSources(); + if (!context.mounted) return; + scaffoldMessenger.showSnackBar(SnackBar( + content: Text(AppLocalizations.of(context)! + .refreshAudioSourcesCount(count)))); + }, + ); + }); + case 'raid': + return Consumer(builder: (context, model, _) { + return ListTile( + leading: const Icon(Icons.connect_without_contact), + title: Text(AppLocalizations.of(context)!.raidAChannel), + onTap: () { + Navigator.of(context).pop(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => _buildRaidBottomSheet(context), + ); + }, + ); + }); + default: + return const SizedBox.shrink(); + } + } - //raid - Consumer(builder: (context, model, child) { - return ListTile( - leading: const Icon(Icons.connect_without_contact), - title: Text(AppLocalizations.of(context)!.raidAChannel), - onTap: () { - Navigator.of(context).pop(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16)), - ), - builder: (context) { - return DraggableScrollableSheet( - initialChildSize: 0.8, - maxChildSize: 0.9, - expand: false, - builder: (context, controller) { - final model = - Provider.of(context, listen: false); - final userChannel = model.userChannel; - return ChannelSearchBottomSheetWidget( - isRaid: true, - onChannelSelect: (channel) { - model.activeChannel = channel; - }, - onRaid: userChannel == model.activeChannel && - userChannel != null - ? (channel) { - final activeChannel = model.activeChannel; - if (activeChannel == null) { - return; - } - ActionsAdapter.instance - .raid(activeChannel, channel); - } - : null, - controller: controller, - ); - }, + Widget _buildRaidBottomSheet(BuildContext context) { + return DraggableScrollableSheet( + initialChildSize: 0.8, + maxChildSize: 0.9, + expand: false, + builder: (context, scrollController) { + return SingleChildScrollView( + controller: scrollController, + child: Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + left: 16, + right: 16, + ), + child: Consumer( + builder: (context, model, _) { + final userChannel = model.userChannel; + return ConstrainedBox( + constraints: BoxConstraints( + minHeight: MediaQuery.of(context).size.height * 0.8, + maxHeight: MediaQuery.of(context).size.height * 0.9, + ), + child: ChannelSearchBottomSheetWidget( + isRaid: true, + onChannelSelect: (channel) { + model.activeChannel = channel; + }, + onRaid: userChannel == model.activeChannel && + userChannel != null + ? (channel) { + final activeChannel = model.activeChannel; + if (activeChannel == null) return; + ActionsAdapter.instance + .raid(activeChannel, channel); + } + : null, + controller: scrollController, + ), ); }, - ); - }, + ), + ), ); - }), + }, + ); + } + @override + Widget build(BuildContext context) { + final tiles = [ + ListTile( + leading: const Icon(Icons.add_link_sharp), + title: Text(AppLocalizations.of(context)!.configureQuickLinks), + onTap: () => + Navigator.of(context).pushNamed("/settings/quick-links")), + const Divider(), + Consumer( + builder: (context, model, _) => Column( + children: QuickLinksModel.availableActions + .where((action) => model.isActionEnabled(action['id'])) + .map((action) => _buildActionTile(context, action['id'])) + .toList(), + ), + ), ListTile( leading: const Icon(Icons.build_outlined), title: Text(AppLocalizations.of(context)!.settings), diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 57696f1f..eb56cbd2 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,3 +1,4 @@ + { "sendAMessage": "Send a message...", "@sendAMessage": { @@ -992,5 +993,9 @@ "alertsEnabled" : "Alerts only", "@alertsEnabled" : { "description": "Message indicating that alerts have been enabled" + }, + "sidebarActions": "Sidebar Actions", + "@sidebarActions": { + "description": "Header for the toggleable sidebar actions section" } } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 3123ced6..47cea8f0 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1115,6 +1115,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Alerts only'** String get alertsEnabled; + + /// Header for the toggleable sidebar actions section + /// + /// In en, this message translates to: + /// **'Sidebar Actions'** + String get sidebarActions; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index e67b18b1..dc5329be 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -697,4 +697,7 @@ class AppLocalizationsAr extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_bn.dart b/lib/l10n/app_localizations_bn.dart index 864f6b20..776d85a9 100644 --- a/lib/l10n/app_localizations_bn.dart +++ b/lib/l10n/app_localizations_bn.dart @@ -700,4 +700,7 @@ class AppLocalizationsBn extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index d1ec4e34..2e8f5ec8 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -707,4 +707,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 7f24554d..4afc4f59 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -699,4 +699,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 9f8d0542..a5b9fae1 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -700,4 +700,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 470aa1cb..acb6bf4f 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -711,4 +711,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index ca0de206..41b85fb1 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -708,4 +708,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 6243c2ab..054b04d0 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -688,4 +688,7 @@ class AppLocalizationsJa extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index aecfcd46..95d291c2 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -688,4 +688,7 @@ class AppLocalizationsKo extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 896382c9..a3ca90d7 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -703,4 +703,7 @@ class AppLocalizationsNl extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index e8f2143a..8bf6c606 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -704,4 +704,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index a2adbd0a..d8f2cfe5 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -704,4 +704,7 @@ class AppLocalizationsPt extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index cbb436e8..4a6eb292 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -708,4 +708,7 @@ class AppLocalizationsRu extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index fa0e1470..fa5ffeab 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -698,4 +698,7 @@ class AppLocalizationsSv extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 5226f684..3fd43b02 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -707,4 +707,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 0863a765..2713a77c 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -687,6 +687,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get alertsEnabled => 'Alerts only'; + + @override + String get sidebarActions => 'Sidebar Actions'; } /// The translations for Chinese, using the Han script (`zh_Hant`). diff --git a/lib/models/quick_links.dart b/lib/models/quick_links.dart index 6f5fd906..8bea234d 100644 --- a/lib/models/quick_links.dart +++ b/lib/models/quick_links.dart @@ -34,9 +34,39 @@ class QuickLinkSource { class QuickLinksModel extends ChangeNotifier { final List _sources = []; + final Set _enabledActions = {}; List get sources => _sources; + static const List> availableActions = [ + { + 'id': 'rainMode', + 'icon': Icons.thunderstorm, + 'labelKey': 'enableRainMode', + }, + { + 'id': 'refreshAudio', + 'icon': Icons.cached_outlined, + 'labelKey': 'refreshAudioSources', + }, + { + 'id': 'raid', + 'icon': Icons.connect_without_contact, + 'labelKey': 'raidAChannel', + }, + ]; + + bool isActionEnabled(String actionId) => _enabledActions.contains(actionId); + + void toggleAction(String actionId) { + if (_enabledActions.contains(actionId)) { + _enabledActions.remove(actionId); + } else { + _enabledActions.add(actionId); + } + notifyListeners(); + } + void addSource(QuickLinkSource source) { _sources.add(source); notifyListeners(); @@ -63,9 +93,18 @@ class QuickLinksModel extends ChangeNotifier { _sources.add(QuickLinkSource.fromJson(source)); } } + final enabledActions = json['enabledActions']; + if (enabledActions != null) { + for (dynamic action in enabledActions) { + if (action is String) { + _enabledActions.add(action); + } + } + } } Map toJson() => { "sources": _sources.map((source) => source.toJson()).toList(), + "enabledActions": _enabledActions.toList(), }; } diff --git a/lib/screens/settings/quick_links.dart b/lib/screens/settings/quick_links.dart index 5d737add..686b853b 100644 --- a/lib/screens/settings/quick_links.dart +++ b/lib/screens/settings/quick_links.dart @@ -8,6 +8,21 @@ import 'package:rtchat/l10n/app_localizations.dart'; import 'package:rtchat/models/quick_links.dart'; import 'package:rtchat/screens/settings/dismissible_delete_background.dart'; +extension LocalizedActions on AppLocalizations { + String getActionLabel(String key) { + switch (key) { + case 'enableRainMode': + return enableRainMode; + case 'refreshAudioSources': + return refreshAudioSources; + case 'raidAChannel': + return raidAChannel; + default: + return ''; + } + } +} + class QuickLinksScreen extends StatefulWidget { const QuickLinksScreen({super.key}); @@ -245,6 +260,30 @@ class _QuickLinksScreenState extends State { ]), ])), ), + const Divider(), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(AppLocalizations.of(context)!.sidebarActions, + style: Theme.of(context).textTheme.titleMedium), + Consumer( + builder: (context, model, _) => Column( + children: QuickLinksModel.availableActions.map((action) { + return SwitchListTile( + title: Text(AppLocalizations.of(context)! + .getActionLabel(action['labelKey'])), + secondary: Icon(action['icon']), + value: model.isActionEnabled(action['id']), + onChanged: (_) => model.toggleAction(action['id']), + ); + }).toList(), + ), + ), + ], + ), + ), ]), ), );