Add ability to toggle off the spoiler alert/CW system app wide

Implements Feature Request #42
This commit is contained in:
Hank Grabowski 2024-06-27 20:03:54 -04:00
parent 6504277005
commit f8ac2a05c0
8 changed files with 53 additions and 9 deletions

View file

@ -32,6 +32,8 @@
* Notifications are grouped by type, starting with mentions, within the unread and read groupings of the * Notifications are grouped by type, starting with mentions, within the unread and read groupings of the
notification list. Defaults to on by default but can be toggled off in notification list. Defaults to on by default but can be toggled off in
settings.([Feature #65](https://gitlab.com/mysocialportal/relatica/-/issues/65)) settings.([Feature #65](https://gitlab.com/mysocialportal/relatica/-/issues/65))
* Ability to turn off Spoiler Alert/CWs at the application
level. Defaults to on. ([Feature #42](https://gitlab.com/mysocialportal/relatica/-/issues/42))
## Version 0.10.1 (beta) ## Version 0.10.1 (beta)

View file

@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../globals.dart';
import '../models/timeline_entry.dart'; import '../models/timeline_entry.dart';
import '../services/auth_service.dart'; import '../services/auth_service.dart';
import '../services/setting_service.dart';
import '../utils/clipboard_utils.dart'; import '../utils/clipboard_utils.dart';
import '../utils/url_opening_utils.dart'; import '../utils/url_opening_utils.dart';
import 'html_text_viewer_control.dart'; import 'html_text_viewer_control.dart';
@ -27,6 +29,7 @@ class SearchResultStatusControl extends StatefulWidget {
} }
class _SearchResultStatusControlState extends State<SearchResultStatusControl> { class _SearchResultStatusControlState extends State<SearchResultStatusControl> {
var showSpoilerControl = true;
var showContent = false; var showContent = false;
TimelineEntry get status => widget.status; TimelineEntry get status => widget.status;
@ -34,7 +37,9 @@ class _SearchResultStatusControlState extends State<SearchResultStatusControl> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
showContent = widget.status.spoilerText.isEmpty; showSpoilerControl = getIt<SettingsService>().spoilerHidingEnabled;
showContent =
!showSpoilerControl ? true : widget.status.spoilerText.isEmpty;
} }
@override @override
@ -77,7 +82,7 @@ class _SearchResultStatusControlState extends State<SearchResultStatusControl> {
const VerticalPadding( const VerticalPadding(
height: 5, height: 5,
), ),
if (status.spoilerText.isNotEmpty) if (showSpoilerControl && status.spoilerText.isNotEmpty)
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
@ -85,7 +90,7 @@ class _SearchResultStatusControlState extends State<SearchResultStatusControl> {
}); });
}, },
child: Text( child: Text(
'Content Summary: ${status.spoilerText} (Click to ${showContent ? "Hide" : "Show"}}')), 'Content Summary: ${status.spoilerText} (Click to ${showContent ? "Hide" : "Show"})')),
if (showContent) ...[ if (showContent) ...[
buildBody(context), buildBody(context),
const VerticalPadding( const VerticalPadding(

View file

@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:relatica/utils/snackbar_builder.dart';
import 'package:result_monad/result_monad.dart'; import 'package:result_monad/result_monad.dart';
import '../../globals.dart'; import '../../globals.dart';
@ -13,6 +12,7 @@ import '../../models/filters/timeline_entry_filter.dart';
import '../../models/flattened_tree_item.dart'; import '../../models/flattened_tree_item.dart';
import '../../models/timeline_entry.dart'; import '../../models/timeline_entry.dart';
import '../../services/auth_service.dart'; import '../../services/auth_service.dart';
import '../../services/setting_service.dart';
import '../../services/timeline_entry_filter_service.dart'; import '../../services/timeline_entry_filter_service.dart';
import '../../services/timeline_manager.dart'; import '../../services/timeline_manager.dart';
import '../../utils/active_profile_selector.dart'; import '../../utils/active_profile_selector.dart';
@ -20,6 +20,7 @@ import '../../utils/clipboard_utils.dart';
import '../../utils/filter_runner.dart'; import '../../utils/filter_runner.dart';
import '../../utils/html_to_edit_text_helper.dart'; import '../../utils/html_to_edit_text_helper.dart';
import '../../utils/responsive_sizes_calculator.dart'; import '../../utils/responsive_sizes_calculator.dart';
import '../../utils/snackbar_builder.dart';
import '../../utils/url_opening_utils.dart'; import '../../utils/url_opening_utils.dart';
import '../html_text_viewer_control.dart'; import '../html_text_viewer_control.dart';
import '../media_attachment_viewer_control.dart'; import '../media_attachment_viewer_control.dart';
@ -49,6 +50,7 @@ class FlattenedTreeEntryControl extends StatefulWidget {
class _StatusControlState extends State<FlattenedTreeEntryControl> { class _StatusControlState extends State<FlattenedTreeEntryControl> {
static final _logger = Logger('$FlattenedTreeEntryControl'); static final _logger = Logger('$FlattenedTreeEntryControl');
var showSpoilerControl = true;
var showContent = true; var showContent = true;
var showFilteredPost = false; var showFilteredPost = false;
var showComments = false; var showComments = false;
@ -67,7 +69,8 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
showContent = entry.spoilerText.isEmpty; showSpoilerControl = getIt<SettingsService>().spoilerHidingEnabled;
showContent = !showSpoilerControl ? true : entry.spoilerText.isEmpty;
showComments = isPost ? false : true; showComments = isPost ? false : true;
} }
@ -164,7 +167,7 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
const VerticalPadding( const VerticalPadding(
height: 5, height: 5,
), ),
if (entry.spoilerText.isNotEmpty) if (showSpoilerControl && entry.spoilerText.isNotEmpty)
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() { setState(() {

View file

@ -38,8 +38,6 @@ class _PostControlState extends State<PostControl> {
final ItemPositionsListener itemPositionsListener = final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create(); ItemPositionsListener.create();
var showContent = true;
EntryTreeItem get item => widget.originalItem; EntryTreeItem get item => widget.originalItem;
TimelineEntry get entry => item.entry; TimelineEntry get entry => item.entry;
@ -47,7 +45,6 @@ class _PostControlState extends State<PostControl> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
showContent = entry.spoilerText.isEmpty;
} }
@override @override

View file

@ -7,6 +7,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../../models/TimelineIdentifiers.dart'; import '../../models/TimelineIdentifiers.dart';
import '../../services/network_status_service.dart'; import '../../services/network_status_service.dart';
import '../../services/setting_service.dart';
import '../../services/timeline_manager.dart'; import '../../services/timeline_manager.dart';
import '../../utils/active_profile_selector.dart'; import '../../utils/active_profile_selector.dart';
import 'post_control.dart'; import 'post_control.dart';
@ -36,6 +37,7 @@ class TimelinePanel extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_logger.finer('Build'); _logger.finer('Build');
context.watch<SettingsService>().spoilerHidingEnabled;
final nss = getIt<NetworkStatusService>(); final nss = getIt<NetworkStatusService>();
final manager = context final manager = context
.watch<ActiveProfileSelector<TimelineManager>>() .watch<ActiveProfileSelector<TimelineManager>>()

View file

@ -76,6 +76,16 @@ class OAuthCredentials implements ICredentials {
'id': id, 'id': id,
}; };
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OAuthCredentials &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
static OAuthCredentials fromJson(Map<String, dynamic> json) => static OAuthCredentials fromJson(Map<String, dynamic> json) =>
OAuthCredentials( OAuthCredentials(
clientId: json['clientId'], clientId: json['clientId'],

View file

@ -32,6 +32,7 @@ class SettingsScreen extends StatelessWidget {
buildVersionString(), buildVersionString(),
buildLowBandwidthWidget(settings), buildLowBandwidthWidget(settings),
buildNotificationGroupingWidget(settings), buildNotificationGroupingWidget(settings),
buildSpoilerHidingEnabledWidget(settings),
buildThemeWidget(settings), buildThemeWidget(settings),
if (!kReleaseMode) buildColorBlindnessTestSettings(settings), if (!kReleaseMode) buildColorBlindnessTestSettings(settings),
buildClearCaches(context), buildClearCaches(context),
@ -80,6 +81,18 @@ class SettingsScreen extends StatelessWidget {
); );
} }
Widget buildSpoilerHidingEnabledWidget(SettingsService settings) {
return ListTile(
title: const Text('Spoiler/Content Warning Hiding'),
trailing: Switch(
onChanged: (value) {
settings.spoilerHidingEnabled = value;
},
value: settings.spoilerHidingEnabled,
),
);
}
Widget buildThemeWidget(SettingsService settings) { Widget buildThemeWidget(SettingsService settings) {
return ListTile( return ListTile(
title: const Text('Dark Mode Theme:'), title: const Text('Dark Mode Theme:'),

View file

@ -34,6 +34,16 @@ class SettingsService extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
var _spoilerHidingEnabled = true;
bool get spoilerHidingEnabled => _spoilerHidingEnabled;
set spoilerHidingEnabled(bool value) {
_spoilerHidingEnabled = value;
_prefs.setBool(_spoilerHidingEnabledKey, _spoilerHidingEnabled);
notifyListeners();
}
var _themeMode = ThemeMode.system; var _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode; ThemeMode get themeMode => _themeMode;
@ -83,6 +93,7 @@ class SettingsService extends ChangeNotifier {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
_lowBandwidthMode = _prefs.getBool(_lowBandwidthModeKey) ?? false; _lowBandwidthMode = _prefs.getBool(_lowBandwidthModeKey) ?? false;
_notificationGrouping = _prefs.getBool(_notificationGroupingKey) ?? true; _notificationGrouping = _prefs.getBool(_notificationGroupingKey) ?? true;
_spoilerHidingEnabled = _prefs.getBool(_spoilerHidingEnabledKey) ?? true;
_themeMode = ThemeModeExtensions.parse(_prefs.getString(_themeModeKey)); _themeMode = ThemeModeExtensions.parse(_prefs.getString(_themeModeKey));
_colorBlindnessType = _colorBlindnessTypeFromPrefs(_prefs); _colorBlindnessType = _colorBlindnessTypeFromPrefs(_prefs);
_logLevel = _levelFromPrefs(_prefs); _logLevel = _levelFromPrefs(_prefs);
@ -97,6 +108,7 @@ const _colorBlindnessTestingModeKey = 'ColorBlindnessTestingMode';
const _logLevelKey = 'LogLevel'; const _logLevelKey = 'LogLevel';
const _networkCapabilitiesKey = 'NetworkCapabilities'; const _networkCapabilitiesKey = 'NetworkCapabilities';
const _notificationGroupingKey = 'NotificationGrouping'; const _notificationGroupingKey = 'NotificationGrouping';
const _spoilerHidingEnabledKey = 'SpoilerHidingEnabled';
ColorBlindnessType _colorBlindnessTypeFromPrefs(SharedPreferences prefs) { ColorBlindnessType _colorBlindnessTypeFromPrefs(SharedPreferences prefs) {
final cbString = prefs.getString(_colorBlindnessTestingModeKey); final cbString = prefs.getString(_colorBlindnessTestingModeKey);