Add block button to profiles

Fixes #152
This commit is contained in:
Henry Hiles 2026-11-02 12:44:41 -05:00
commit 979068fc3a
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 228 additions and 0 deletions

View file

@ -0,0 +1,66 @@
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/account_data.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/helpers/hooks/saving.dart";
import "package:nexus/main.dart";
/// Blocks a user by their ID, hiding their messages and invites.
class const BlockUserField({super.key}) extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final saving = Saving.use();
final controller = useTextEditingController();
final userId = useValueListenable(controller).text.trim();
final blocked = ref.watch(
AccountDataController.provider.select(
(data) => data.ignoredUserList.contains(userId),
),
);
final ownId = ref.watch(
ClientStateController.provider.select((state) => state?.userId),
);
final error = userId.isEmpty || userId.isValidUserId
? userId == ownId
? "You can't block yourself"
: blocked
? "Already blocked"
: null
: "Enter a user ID, like @user:example.com";
final canBlock = userId.isNotEmpty && error == null && !saving.active;
void block() {
if (!canBlock) return;
saving
.run(() async {
await ref
.read(AccountDataController.provider.notifier)
.setIgnored(userId, ignored: true);
if (context.mounted) controller.clear();
})
.onError(showError);
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8).copyWith(top: 8),
child: TextField(
controller: controller,
enabled: !saving.active,
onSubmitted: (_) => block(),
decoration: .new(
labelText: "User ID",
hintText: "@user:example.com",
errorText: error,
suffixIcon: IconButton(
tooltip: "Block user",
icon: Icon(Icons.block),
onPressed: canBlock ? block : null,
),
),
),
);
}
}

View file

@ -6,6 +6,7 @@ import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:intl/intl.dart";
import "package:m3e_buttons/m3e_buttons.dart";
import "package:nexus/controllers/account_data.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/controllers/mutual_rooms.dart";
@ -17,6 +18,7 @@ import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/create_room.dart";
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
import "package:nexus/helpers/extensions/success_scheme.dart";
import "package:nexus/helpers/extensions/with_progress_snackbar.dart";
import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/loading.dart";
@ -145,7 +147,65 @@ final class const UserBottomSheet(
if (!editable) ...[
Row(
mainAxisAlignment: .end,
spacing: 8,
children: [
if (userId !=
ref.watch(ClientStateController.provider)?.userId)
Consumer(
builder: (context, ref, _) {
final ignored = ref.watch(
AccountDataController.provider.select(
(data) => data.ignoredUserList.contains(userId),
),
);
final name = member.displayName ?? userId.localpart;
Future<void> setIgnored(
bool ignored,
) => context.withProgressSnackbar(
"${ignored ? "Blocking" : "Unblocking"} $name...",
() => ref
.read(AccountDataController.provider.notifier)
.setIgnored(userId, ignored: ignored),
);
return M3EButton(
tooltip: ignored ? "Unblock" : "Block",
onPressed: ignored
? () => setIgnored(false)
: () => context.showAdaptiveSheet(
(context) => SheetLayout(
title: Text("Block $name"),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
setIgnored(true);
},
style: TextButton.styleFrom(
foregroundColor:
theme.colorScheme.error,
),
child: Text("Block"),
),
],
children: [
Text(
"Are you sure you want to block $userId? You won't see their new messages or invites.",
),
],
),
),
style: .tonal,
child: Icon(
ignored ? Icons.lock_open : Icons.block,
),
);
},
),
M3EButton(
onPressed: Navigator.of(context).pop,
child: Icon(Icons.close),