add thread support

Fixes #129
This commit is contained in:
Henry Hiles 2026-11-05 19:14:46 -05:00
commit 7f7bdbf525
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
5 changed files with 1477 additions and 577 deletions

View file

@ -33,12 +33,12 @@ class const BotCommandForm(
(index, parameter) => BotOptionPill(
parameter,
key: ValueKey(parameter.key),
roomId: draft.roomId,
composer: draft.composer,
onSubmit: onSubmit,
onRemove: parameter.optional
? () => ref
.read(
BotCommandDraftController.provider(draft.roomId)
BotCommandDraftController.provider(draft.composer)
.notifier,
)
.removeOption(parameter.key)

View file

@ -3,6 +3,7 @@ import "package:flutter/scheduler.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/models/composer_params.dart";
import "package:nexus/controllers/bot_argument_suggestions.dart";
import "package:nexus/controllers/bot_command_draft.dart";
import "package:nexus/models/bot_argument_suggestion.dart";
@ -10,7 +11,7 @@ import "package:nexus/models/bot_command.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
class const BotCommandPanel(
final String roomId, {
final ComposerParams composer, {
required final IList<BotCommand> commands,
required final TextEditingController controller,
required final void Function(BotCommand command) onPick,
@ -19,9 +20,9 @@ class const BotCommandPanel(
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final draftProvider = BotCommandDraftController.provider(roomId);
final draftProvider = BotCommandDraftController.provider(composer);
final suggestionsProvider = BotArgumentSuggestionsController.provider(
roomId,
composer,
);
final text = useState(controller.text);

View file

@ -2,12 +2,13 @@ import "package:flutter/services.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/models/composer_params.dart";
import "package:nexus/controllers/bot_command_draft.dart";
import "package:nexus/models/content/bot_command_description.dart";
class const BotOptionPill(
final BotParameter parameter, {
required final String roomId,
required final ComposerParams composer,
/// Called on backspace while empty, to remove the parameter or command.
final VoidCallback? onRemove,
@ -17,7 +18,7 @@ class const BotOptionPill(
@override
Widget build(BuildContext context, WidgetRef ref) {
final colorScheme = Theme.of(context).colorScheme;
final provider = BotCommandDraftController.provider(roomId);
final provider = BotCommandDraftController.provider(composer);
final (:text, :isFocused, :error, :isLast) = ref.watch(
provider.select(
(draft) => (

View file

@ -10,6 +10,7 @@ import "package:nexus/controllers/rooms.dart";
import "package:nexus/helpers/extensions/show_error_snack_bar.dart";
import "package:nexus/models/bot_command.dart";
import "package:nexus/models/bot_command_draft.dart";
import "package:nexus/models/composer_params.dart";
import "package:nexus/widgets/composer/bot_command_form.dart";
import "package:nexus/widgets/composer/bot_command_panel.dart";
import "package:nexus/widgets/emoji_picker.dart";
@ -75,6 +76,7 @@ class const Composer(
})
onSendLocation,
required final Future<void> Function(BotCommandDraft draft) onSendBotCommand,
final String? threadRoot,
final FocusNode? node,
super.key,
}) extends HookConsumerWidget {
@ -131,7 +133,9 @@ class const Composer(
final poll = useState<PollStart?>(null);
final location = useState<LocationMessageContent?>(null);
final botCommandProvider = BotCommandDraftController.provider(roomId);
final ComposerParams composer = (roomId: roomId, threadRoot: threadRoot);
final botCommandProvider = BotCommandDraftController.provider(composer);
final attachmentProvider = AttachmentController.provider(composer);
final botCommand = ref.watch(botCommandProvider);
useEffect(() {
sticker.value = null;
@ -150,8 +154,6 @@ class const Composer(
final isEditing = relationType == .edit && relatedEvent != null;
useEffect(() {
final attachmentProvider = AttachmentController.provider(roomId);
if (relationType == .edit && relatedEvent != null) {
textBefore.value ??= controller.text;
attachmentBefore.value ??= (ref.read(attachmentProvider),);
@ -247,13 +249,11 @@ class const Composer(
};
}, [roomId]);
final attachment = ref.watch(AttachmentController.provider(roomId));
final attachment = ref.watch(attachmentProvider);
Future<void> attach(Future<XFile?> pick) async {
if (await pick case final file?) {
await ref
.read(AttachmentController.provider(roomId).notifier)
.add(file);
await ref.read(attachmentProvider.notifier).add(file);
}
}
@ -266,9 +266,7 @@ class const Composer(
final file = await File(join(directory.path, filename))
.writeAsBytes(bytes);
await ref
.read(AttachmentController.provider(roomId).notifier)
.add(.new(file.path));
await ref.read(attachmentProvider.notifier).add(.new(file.path));
}
/// Attaches the first file on the clipboard, or the image on it if there
@ -283,7 +281,7 @@ class const Composer(
final files = await Pasteboard.files().onError((_, _) => const []);
if (files.firstOrNull case final path?) {
ref
.read(AttachmentController.provider(roomId).notifier)
.read(attachmentProvider.notifier)
.add(XFile(path))
.onError(showError);
return true;
@ -338,7 +336,9 @@ class const Composer(
if (hasReplacement) {
if (botCommand != null) {
if (ref
.read(BotArgumentSuggestionsController.provider(roomId).notifier)
.read(
BotArgumentSuggestionsController.provider(composer).notifier,
)
.pickTop()) {
return;
}
@ -432,8 +432,7 @@ class const Composer(
: Icon(Icons.file_copy),
title: Text(attachment.$1),
trailing: IconButton(
onPressed: () =>
ref.invalidate(AttachmentController.provider(roomId)),
onPressed: () => ref.invalidate(attachmentProvider),
icon: Icon(Icons.close),
),
),
@ -442,7 +441,7 @@ class const Composer(
duration: Durations.short4,
alignment: .bottomCenter,
child: BotCommandPanel(
roomId,
composer,
commands: botCommands,
controller: controller,
onPick: pickBotCommand,

View file

@ -29,7 +29,10 @@ class const RelationPreview(
Expanded(
child: Padding(
padding: .symmetric(vertical: 8),
child: EventPreview(relatedEvent!),
child: EventPreview(
relatedEvent!.rowId,
roomId: relatedEvent!.roomId,
),
),
),