From 2b347b557278edfd183482de27cb2f47c743a7c1 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 26 Jun 2024 14:57:23 -0400 Subject: [PATCH] Add user settings for network capabilities --- .../network_capabilities_settings.dart | 121 ++++++++++++++++++ lib/models/timeline_network_info.dart | 12 ++ lib/screens/settings_screen.dart | 76 +++++++++++ lib/services/setting_service.dart | 29 +++++ lib/utils/known_network_extensions.dart | 63 +++++++++ 5 files changed, 301 insertions(+) create mode 100644 lib/models/settings/network_capabilities_settings.dart create mode 100644 lib/utils/known_network_extensions.dart diff --git a/lib/models/settings/network_capabilities_settings.dart b/lib/models/settings/network_capabilities_settings.dart new file mode 100644 index 0000000..02e16b0 --- /dev/null +++ b/lib/models/settings/network_capabilities_settings.dart @@ -0,0 +1,121 @@ +import '../timeline_network_info.dart'; + +class NetworkCapabilitiesSettings { + late final List _capabilities; + late final Map _capabilitiesKV; + + num get length => _capabilities.length; + + NetworkCapabilitiesSettings( + {required List capabilities}) { + _capabilities = capabilities; + _capabilitiesKV = {for (final c in capabilities) c.network: c}; + } + + NetworkCapabilitiesItem operator [](int i) => _capabilities[i]; + + NetworkCapabilitiesItem getCapabilities(KnownNetworks network) => + _capabilitiesKV[network] ?? NetworkCapabilitiesItem.unknown(); + + operator []=(int i, NetworkCapabilitiesItem item) { + _capabilities[i] = item; + _capabilitiesKV[item.network] = item; + } + + factory NetworkCapabilitiesSettings.defaultSettings() { + final networks = {}; + networks.add(KnownNetworks.friendica); + networks.add(KnownNetworks.mastodon); + networks.add(KnownNetworks.threads); + networks.add(KnownNetworks.bluesky); + networks.add(KnownNetworks.diaspora); + networks.add(KnownNetworks.pixelfed); + networks.add(KnownNetworks.peertube); + networks.add(KnownNetworks.unknown); + networks.addAll(KnownNetworks.values); + final capabilities = networks + .map((e) => switch (e) { + KnownNetworks.activityPub => NetworkCapabilitiesItem( + network: e, + react: true, + reshare: true, + comment: true, + ), + KnownNetworks.bluesky => NetworkCapabilitiesItem( + network: e, + react: true, + reshare: false, + comment: true, + ), + KnownNetworks.threads => NetworkCapabilitiesItem( + network: e, + react: true, + reshare: false, + comment: false, + ), + _ => NetworkCapabilitiesItem( + network: e, + react: true, + reshare: true, + comment: true, + ), + }) + .toList(); + return NetworkCapabilitiesSettings(capabilities: capabilities); + } + + factory NetworkCapabilitiesSettings.fromJson(List json) { + final capabilities = + json.map((j) => NetworkCapabilitiesItem.fromJson(j)).toList(); + return NetworkCapabilitiesSettings(capabilities: capabilities); + } + + List> toJson() { + return _capabilities.map((c) => c.toJson()).toList(); + } +} + +class NetworkCapabilitiesItem { + final KnownNetworks network; + final bool react; + final bool reshare; + final bool comment; + + NetworkCapabilitiesItem({ + required this.network, + required this.react, + required this.reshare, + required this.comment, + }); + + factory NetworkCapabilitiesItem.fromJson(Map json) => + NetworkCapabilitiesItem( + network: KnownNetworks.parse(json['network']), + react: json['react'] ?? true, + reshare: json['reshare'] ?? true, + comment: json['comment'] ?? true, + ); + + factory NetworkCapabilitiesItem.unknown() => NetworkCapabilitiesItem( + network: KnownNetworks.unknown, + react: true, + reshare: true, + comment: true, + ); + + NetworkCapabilitiesItem copyWith( + {bool? react, bool? reshare, bool? comment}) => + NetworkCapabilitiesItem( + network: network, + react: react ?? this.react, + reshare: reshare ?? this.reshare, + comment: comment ?? this.comment, + ); + + Map toJson() => { + 'network': network.name, + 'react': react, + 'reshare': reshare, + 'comment': comment, + }; +} diff --git a/lib/models/timeline_network_info.dart b/lib/models/timeline_network_info.dart index 0213410..bc734e6 100644 --- a/lib/models/timeline_network_info.dart +++ b/lib/models/timeline_network_info.dart @@ -23,6 +23,18 @@ enum KnownNetworks { threads, wordpress, unknown, + ; + + static KnownNetworks parse(String? text) { + if (text == null) { + return unknown; + } + + return values.firstWhere( + (e) => e.name == text, + orElse: () => unknown, + ); + } } class TimelineNetworkInfo { diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 49166e5..8f45ef5 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -12,6 +12,7 @@ import '../di_initialization.dart'; import '../globals.dart'; import '../routes.dart'; import '../services/setting_service.dart'; +import '../utils/known_network_extensions.dart'; import '../utils/theme_mode_extensions.dart'; class SettingsScreen extends StatelessWidget { @@ -34,6 +35,7 @@ class SettingsScreen extends StatelessWidget { if (!kReleaseMode) buildColorBlindnessTestSettings(settings), buildClearCaches(context), buildLogPanel(context, settings), + const NetworkCapabilitiesWidget(), ], ), ), @@ -138,3 +140,77 @@ class SettingsScreen extends StatelessWidget { ); } } + +class NetworkCapabilitiesWidget extends StatelessWidget { + const NetworkCapabilitiesWidget({super.key}); + + @override + Widget build(BuildContext context) { + final settings = context.watch(); + final nc = settings.networkCapabilities; + final rows = []; + + for (int i = 0; i < nc.length; i++) { + final e = nc[i]; + final r = DataRow( + cells: [ + DataCell( + Text( + '${e.network.forkAwesomeUnicode} ${e.network.labelName}', + style: const TextStyle(fontFamily: 'ForkAwesome'), + ), + ), + DataCell( + Checkbox( + value: e.react, + onChanged: (bool? value) { + nc[i] = e.copyWith(react: value ?? false); + settings.networkCapabilities = nc; + }, + ), + ), + DataCell( + Checkbox( + value: e.reshare, + onChanged: (bool? value) { + nc[i] = e.copyWith(reshare: value ?? false); + settings.networkCapabilities = nc; + }, + ), + ), + DataCell( + Checkbox( + value: e.comment, + onChanged: (bool? value) { + nc[i] = e.copyWith(comment: value ?? false); + settings.networkCapabilities = nc; + }, + ), + ), + ], + ); + rows.add(r); + } + + return ListTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Network Capabilities'), + ElevatedButton( + onPressed: null, + child: Text('Reset to Defaults'), + ), + ], + ), + subtitle: DataTable( + columns: [ + DataColumn(label: Text('Network')), + DataColumn(label: Text('React')), + DataColumn(label: Text('Reshare')), + DataColumn(label: Text('Comment')), + ], + rows: rows, + )); + } +} diff --git a/lib/services/setting_service.dart b/lib/services/setting_service.dart index 75bbc9f..19046eb 100644 --- a/lib/services/setting_service.dart +++ b/lib/services/setting_service.dart @@ -1,8 +1,11 @@ +import 'dart:convert'; + import 'package:color_blindness/color_blindness.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../models/settings/network_capabilities_settings.dart'; import '../utils/theme_mode_extensions.dart'; class SettingsService extends ChangeNotifier { @@ -51,6 +54,18 @@ class SettingsService extends ChangeNotifier { notifyListeners(); } + NetworkCapabilitiesSettings _networkCapabilities = + NetworkCapabilitiesSettings.defaultSettings(); + + NetworkCapabilitiesSettings get networkCapabilities => _networkCapabilities; + + set networkCapabilities(NetworkCapabilitiesSettings updatedCapabilities) { + _networkCapabilities = updatedCapabilities; + final jsonString = jsonEncode(_networkCapabilities.toJson()); + _prefs.setString(_networkCapabilitiesKey, jsonString); + notifyListeners(); + } + Future initialize() async { if (_initialized) { return; @@ -60,6 +75,7 @@ class SettingsService extends ChangeNotifier { _themeMode = ThemeModeExtensions.parse(_prefs.getString(_themeModeKey)); _colorBlindnessType = _colorBlindnessTypeFromPrefs(_prefs); _logLevel = _levelFromPrefs(_prefs); + _networkCapabilities = _networkCapabilitiesFromPrefs(_prefs); _initialized = true; } } @@ -68,6 +84,7 @@ const _lowBandwidthModeKey = 'LowBandwidthMode'; const _themeModeKey = 'ThemeMode'; const _colorBlindnessTestingModeKey = 'ColorBlindnessTestingMode'; const _logLevelKey = 'LogLevel'; +const _networkCapabilitiesKey = 'NetworkCapabilities'; ColorBlindnessType _colorBlindnessTypeFromPrefs(SharedPreferences prefs) { final cbString = prefs.getString(_colorBlindnessTestingModeKey); @@ -80,6 +97,18 @@ ColorBlindnessType _colorBlindnessTypeFromPrefs(SharedPreferences prefs) { ); } +NetworkCapabilitiesSettings _networkCapabilitiesFromPrefs( + SharedPreferences prefs) { + final ncString = prefs.getString(_networkCapabilitiesKey); + if (ncString?.isEmpty ?? true) { + return NetworkCapabilitiesSettings.defaultSettings(); + } + + final List json = jsonDecode(ncString!); + final nc = NetworkCapabilitiesSettings.fromJson(json); + return nc; +} + Level _levelFromPrefs(SharedPreferences prefs) { final levelString = prefs.getString(_logLevelKey); return switch (levelString) { diff --git a/lib/utils/known_network_extensions.dart b/lib/utils/known_network_extensions.dart new file mode 100644 index 0000000..551eb3a --- /dev/null +++ b/lib/utils/known_network_extensions.dart @@ -0,0 +1,63 @@ +import '../models/timeline_network_info.dart'; + +extension KnownNetworkExtensions on KnownNetworks { + String get labelName => switch (this) { + KnownNetworks.activityPub => 'ActivityPub', + KnownNetworks.bluesky => 'Bluesky', + KnownNetworks.calckey => 'Calckey', + KnownNetworks.diaspora => 'Diaspora', + KnownNetworks.drupal => 'Drupal', + KnownNetworks.firefish => 'Firefish', + KnownNetworks.friendica => 'Friendica', + KnownNetworks.funkwhale => 'Funkwhale', + KnownNetworks.gnu_social => 'GNU Social', + KnownNetworks.hubzilla => 'Hubzilla', + KnownNetworks.kbin => 'Kbin', + KnownNetworks.lemmy => 'Lemmy', + KnownNetworks.mastodon => 'Mastodon', + KnownNetworks.nextcloud => 'Nextcloud', + KnownNetworks.peertube => 'PeerTube', + KnownNetworks.pixelfed => 'Pixelfed', + KnownNetworks.pleroma => 'Pleroma', + KnownNetworks.plume => 'Plume', + KnownNetworks.red => 'Red', + KnownNetworks.redmatrix => 'RedMatrix', + KnownNetworks.socialhome => 'Socialhome', + KnownNetworks.threads => 'Threads', + KnownNetworks.wordpress => 'WordPress', + KnownNetworks.unknown => 'Unknown', + }; + + String get forkAwesomeUnicode => switch (this) { + KnownNetworks.activityPub => '\uf2f2', + KnownNetworks.bluesky => '\uf111', + KnownNetworks.calckey => '\uf1ec', + KnownNetworks.diaspora => '\uf2e5', + KnownNetworks.drupal => '\uf1a9', + KnownNetworks.firefish => '\uf06d', + KnownNetworks.friendica => '\uf2e6', + KnownNetworks.funkwhale => '\uf339', + KnownNetworks.gnu_social => '\uf2e7', + KnownNetworks.hubzilla => '\uf2eb', + KnownNetworks.kbin => '\uf058', + KnownNetworks.lemmy => '\uf0c0', + KnownNetworks.mastodon => '\uf2e1', + KnownNetworks.nextcloud => '\uf307', + KnownNetworks.peertube => '\uf2e4', + KnownNetworks.pixelfed => '\uf314', + KnownNetworks.pleroma => '\uf324', + KnownNetworks.plume => '\uf356', + KnownNetworks.red => '\uf2eb', + KnownNetworks.redmatrix => '\uf2eb', + KnownNetworks.socialhome => '\uf2ec', + KnownNetworks.threads => '\uf16d', + KnownNetworks.wordpress => '\uf19a', + KnownNetworks.unknown => '\uf059', + }; +} + +extension TimelineNetworkInfoExtensions on TimelineNetworkInfo { + String get labelName => network.labelName; + + String get forkAwesomeUnicode => network.forkAwesomeUnicode; +}