Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user label settings page #1481

Merged
merged 3 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading