Skip to content

Commit

Permalink
Add user label settings page (thunder-app#1481)
Browse files Browse the repository at this point in the history
* Add user label settings page

* Invert logic
  • Loading branch information
micahmo authored Jul 10, 2024
1 parent e57b06d commit 3e3f4d5
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 52 deletions.
21 changes: 21 additions & 0 deletions lib/account/models/user_label.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,29 @@ class UserLabel {
}
}

static Future<List<UserLabel>> fetchAllUserLabels() async {
try {
final userLabelRows = await database.select(database.userLabels).get();
return userLabelRows
.map((userLabel) => UserLabel(
id: userLabel.id.toString(),
username: userLabel.username,
label: userLabel.label,
))
.toList();
} catch (e) {
debugPrint(e.toString());
return [];
}
}

/// Generates a username string that can be used to uniquely identify entries in the UserLabels table
static String usernameFromParts(String username, String actorId) {
return '$username@${fetchInstanceNameFromUrl(actorId)}';
}

/// Splits a username generated by [usernameFromParts] back into name and instance
static ({String username, String instance}) partsFromUsername(String username) {
return (username: username.split('@')[0], instance: username.split('@')[1]);
}
}
4 changes: 4 additions & 0 deletions lib/core/enums/local_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum LocalSettingsCategories {
floatingActionButton('floatingActionButton'),
accessibility('Accessibility'),
account('Account'),
userLabels('User Labels'),
about('About'),
debug('Debug'),
theming('Theming'),
Expand Down Expand Up @@ -253,6 +254,8 @@ enum LocalSettings {
subCategory: LocalSettingsSubCategories.names,
searchable: false),

userLabels(name: 'setting_user_labels', key: 'userLabels', category: LocalSettingsCategories.userLabels),

/// -------------------------- Gesture Related Settings --------------------------
// Sidebar Gesture Settings
sidebarBottomNavBarSwipeGesture(
Expand Down Expand Up @@ -476,6 +479,7 @@ extension LocalizationExt on AppLocalizations {
'videoAutoLoop': videoAutoLoop,
'videoAutoPlay': videoAutoPlay,
'videoDefaultPlaybackSpeed': videoDefaultPlaybackSpeed,
'userLabels': userLabels,
};

if (localizationMap.containsKey(key)) {
Expand Down
16 changes: 16 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,10 @@
"@deleteLocalPreferencesDescription": {
"description": "Description for confirmation action to delete local preferences"
},
"deleteUserLabelConfirmation": "Are you sure you want to delete the label?",
"@deleteUserLabelConfirmation": {
"description": "Confirmation message for deleting a label"
},
"deletedByCreator": "deleted by creator",
"@deletedByCreator": {
"description": "Placeholder text for a comment deleted by the creator. Be sure to keep this lowercase."
Expand Down Expand Up @@ -1395,6 +1399,10 @@
},
"noUserBlocks": "No blocked users.",
"@noUserBlocks": {},
"noUserLabels": "You have not created any user labels yet",
"@noUserLabels": {
"description": "Placeholder message for empty list of labels"
},
"noUsersFound": "No users found.",
"@noUsersFound": {},
"none": "None",
Expand Down Expand Up @@ -2531,6 +2539,14 @@
"@userLabelHint": {
"description": "Hint text for adding a new user label"
},
"userLabels": "User Labels",
"@userLabels": {
"description": "Heading for user labels settings"
},
"userLabelsSettingsPageDescription": "You can add, modify, or remove labels associated with users.",
"@userLabelsSettingsPageDescription": {
"description": "Description text for user labels settings page"
},
"userNameColor": "User Name Color",
"@userNameColor": {
"description": "Setting for username color"
Expand Down
53 changes: 2 additions & 51 deletions lib/post/utils/comment_action_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/instance/bloc/instance_bloc.dart';
import 'package:thunder/instance/enums/instance_action.dart';
import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/utils/user_label_utils.dart';
import 'package:thunder/post/widgets/report_comment_dialog.dart';
import 'package:thunder/shared/dialogs.dart';
import 'package:thunder/shared/multi_picker_item.dart';
import 'package:thunder/shared/picker_item.dart';
import 'package:thunder/shared/snackbar.dart';
Expand Down Expand Up @@ -562,56 +562,7 @@ class _CommentActionPickerState extends State<CommentActionPicker> {
break;
case CommentCardAction.userLabel:
action = () async {
// Load up any existing label
final String username = UserLabel.usernameFromParts(widget.commentView.creator.name, widget.commentView.creator.actorId);
final UserLabel? existingLabel = await UserLabel.fetchUserLabel(username);

if (!context.mounted) return;

final TextEditingController controller = TextEditingController(text: existingLabel?.label);

await showThunderDialog(
// We're checking context.mounted above, so ignore this warning
// ignore: use_build_context_synchronously
context: context,
title: l10n.addUserLabel,
contentWidgetBuilder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
controller: controller,
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: l10n.label,
hintText: l10n.userLabelHint,
),
autofocus: true,
),
],
);
},
tertiaryButtonText: existingLabel != null ? l10n.delete : null,
onTertiaryButtonPressed: (dialogContext) async {
Navigator.of(dialogContext).pop();
await UserLabel.deleteUserLabel(username);
},
secondaryButtonText: l10n.cancel,
onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(),
primaryButtonText: l10n.save,
onPrimaryButtonPressed: (dialogContext, _) async {
Navigator.of(dialogContext).pop();

if (controller.text.isNotEmpty) {
await UserLabel.upsertUserLabel(UserLabel(id: '', username: username, label: controller.text));
} else {
await UserLabel.deleteUserLabel(username);
}
},
);
await showUserLabelEditorDialog(context, UserLabel.usernameFromParts(widget.commentView.creator.name, widget.commentView.creator.actorId));
};
break;
case CommentCardAction.instanceActions:
Expand Down
73 changes: 73 additions & 0 deletions lib/post/utils/user_label_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:thunder/account/models/user_label.dart';
import 'package:thunder/shared/dialogs.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

/// Shows a dialog which allows the user to create/modify/edit a label for the given [username].
/// Tip: Call `UserLabel.usernameFromParts` to generate a [username] in the right format.
/// If an existing user label was found (regardless of whether it was changed or deleted) or a new user label was created,
/// it will be returned in the record.
/// If a user label was found and deleted, the deleted flag will be set in the record.
Future<({UserLabel? userLabel, bool deleted})> showUserLabelEditorDialog(BuildContext context, String username) async {
final l10n = AppLocalizations.of(context)!;

// Load up any existing label
UserLabel? existingLabel = await UserLabel.fetchUserLabel(username);
bool deleted = false;

if (!context.mounted) return (userLabel: existingLabel, deleted: false);

final TextEditingController controller = TextEditingController(text: existingLabel?.label);

await showThunderDialog<UserLabel?>(
// We're checking context.mounted above, so ignore this warning
// ignore: use_build_context_synchronously
context: context,
title: l10n.addUserLabel,
contentWidgetBuilder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
controller: controller,
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: l10n.label,
hintText: l10n.userLabelHint,
),
autofocus: true,
),
],
);
},
tertiaryButtonText: existingLabel != null ? l10n.delete : null,
onTertiaryButtonPressed: (dialogContext) async {
await UserLabel.deleteUserLabel(username);
deleted = true;

if (dialogContext.mounted) {
Navigator.of(dialogContext).pop();
}
},
secondaryButtonText: l10n.cancel,
onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(),
primaryButtonText: l10n.save,
onPrimaryButtonPressed: (dialogContext, _) async {
if (controller.text.isNotEmpty) {
existingLabel = await UserLabel.upsertUserLabel(UserLabel(id: '', username: username, label: controller.text));
} else {
await UserLabel.deleteUserLabel(username);
deleted = true;
}

if (dialogContext.mounted) {
Navigator.of(dialogContext).pop();
}
},
);

return (userLabel: existingLabel, deleted: deleted);
}
11 changes: 11 additions & 0 deletions lib/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:thunder/settings/pages/general_settings_page.dart';
import 'package:thunder/settings/pages/gesture_settings_page.dart';
import 'package:thunder/settings/pages/post_appearance_settings_page.dart';
import 'package:thunder/settings/pages/theme_settings_page.dart';
import 'package:thunder/settings/pages/user_labels_settings_page.dart';
import 'package:thunder/settings/pages/video_player_settings.dart';
import 'package:thunder/settings/settings.dart';
import 'package:thunder/thunder/thunder.dart';
Expand Down Expand Up @@ -155,6 +156,16 @@ final GoRouter router = GoRouter(
);
},
),
GoRoute(
name: 'user_labels',
path: 'user_labels',
builder: (context, state) {
return BlocProvider.value(
value: (state.extra! as List)[0] as ThunderBloc,
child: UserLabelSettingsPage(settingToHighlight: (state.extra! as List).elementAtOrNull(1) as LocalSettings?),
);
},
),
GoRoute(
name: 'about',
path: 'about',
Expand Down
3 changes: 2 additions & 1 deletion lib/settings/pages/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class _SettingsPageState extends State<SettingsPage> {
SettingTopic(title: l10n.floatingActionButton, icon: Icons.settings_applications_rounded, path: SETTINGS_FAB_PAGE),
SettingTopic(title: l10n.accessibility, icon: Icons.accessibility, path: SETTINGS_ACCESSIBILITY_PAGE),
SettingTopic(title: l10n.account(0), icon: Icons.person_rounded, path: SETTINGS_ACCOUNT_PAGE),
SettingTopic(title: l10n.userLabels, icon: Icons.label_rounded, path: SETTINGS_USER_LABELS_PAGE),
SettingTopic(title: l10n.about, icon: Icons.info_rounded, path: SETTINGS_ABOUT_PAGE),
SettingTopic(title: l10n.debug, icon: Icons.developer_mode_rounded, path: SETTINGS_DEBUG_PAGE),
];
Expand Down Expand Up @@ -89,7 +90,7 @@ class _SettingsPageState extends State<SettingsPage> {
localSettings.length,
(index) => ListTile(
subtitle: Text(
"${l10n.getLocalSettingLocalization(localSettings[index].category!.toString())} > ${l10n.getLocalSettingLocalization(localSettings[index].subCategory.toString())}"),
"${l10n.getLocalSettingLocalization(localSettings[index].category!.toString())}${localSettings[index].subCategory == null ? '' : ' > ${l10n.getLocalSettingLocalization(localSettings[index].subCategory.toString())}'}"),
onTap: () {
navigateToSetting(context, localSettings[index]);
controller.closeView(null);
Expand Down
Loading

0 comments on commit 3e3f4d5

Please sign in to comment.