reorganize and rename widgets

This commit is contained in:
Henry Hiles 2026-11-09 17:25:02 -05:00
commit d5f29dc344
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
8 changed files with 568 additions and 547 deletions

View file

@ -9,7 +9,7 @@ import "package:nexus/controllers/bot_command_draft.dart";
import "package:nexus/models/bot_argument_suggestion.dart";
import "package:nexus/models/bot_command.dart";
import "package:nexus/models/text_command.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/avatars/avatar_or_hash.dart";
class const BotCommandPanel(
final ComposerParams composer, {

View file

@ -17,7 +17,7 @@ import "package:nexus/models/composer_params.dart";
import "package:nexus/models/text_command.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";
import "package:nexus/widgets/emoji/emoji_picker.dart";
import "package:material_ui/material_ui.dart";
import "package:flutter/services.dart";
import "package:flutter_hooks/flutter_hooks.dart";
@ -35,10 +35,10 @@ import "package:nexus/models/content/image_pack.dart";
import "package:nexus/models/content/message.dart";
import "package:nexus/models/content/poll_start.dart";
import "package:nexus/models/image_pack.dart";
import "package:nexus/widgets/mxc_emoji.dart";
import "package:nexus/widgets/sticker_picker.dart";
import "package:nexus/widgets/create_poll_sheet.dart";
import "package:nexus/widgets/location_picker_sheet.dart";
import "package:nexus/widgets/emoji/mxc_emoji.dart";
import "package:nexus/widgets/emoji/sticker_picker.dart";
import "package:nexus/widgets/composer/create_poll_sheet.dart";
import "package:nexus/widgets/location/location_picker_sheet.dart";
import "package:nexus/models/location.dart";
import "package:nexus/widgets/composer/overlays/tagger_overlay.dart";
import "package:nexus/widgets/composer/paste_files_action.dart";

View file

@ -0,0 +1,109 @@
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:material_ui/material_ui.dart";
import "package:material_adaptive_sheets/material_adaptive_sheets.dart";
import "package:nexus/models/content/poll_start.dart";
class const CreatePollSheet({
required final void Function(PollStart poll) onCreate,
super.key,
}) extends HookWidget {
@override
Widget build(BuildContext context) {
final questionController = useTextEditingController();
final question = useValueListenable(questionController).text.trim();
// Keyed by ID, so fields keep their text when one above is removed
final nextId = useRef(2);
final answers = useState<IMap<int, String>>(.new({0: "", 1: ""}));
final filledAnswers = answers.value.entries
.map(
(answer) =>
PollAnswer(id: "${answer.key}", text: answer.value.trim()),
)
.where((answer) => answer.text.isNotEmpty)
.toIList();
final allowMultiple = useState(false);
final canCreate = question.isNotEmpty && filledAnswers.length >= 2;
void submit() => onCreate(
.new(
question: question,
answers: filledAnswers,
maxSelections: allowMultiple.value ? filledAnswers.length : 1,
),
);
return SheetLayout(
title: const Text("Create a Poll"),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text("Cancel"),
),
TextButton(
onPressed: canCreate ? submit : null,
child: const Text("Create"),
),
],
children: [
TextField(
controller: questionController,
autofocus: true,
textCapitalization: .sentences,
decoration: const .new(labelText: "Question"),
),
AnimatedSize(
duration: Durations.medium2,
curve: Curves.easeInOut,
alignment: .topCenter,
child: Column(
spacing: 12,
children: answers.value.entries
.mapIndexed(
(index, answer) => TextFormField(
key: ValueKey(answer.key),
initialValue: answer.value,
textCapitalization: .sentences,
onChanged: (text) =>
answers.value = answers.value.add(answer.key, text),
decoration: .new(
labelText: "Answer ${index + 1}",
suffixIcon: answers.value.length > 2
? IconButton(
tooltip: "Remove answer",
icon: const Icon(Icons.close),
onPressed: () => answers.value = answers.value
.remove(answer.key),
)
: null,
),
),
)
.toList(),
),
),
Align(
alignment: .centerLeft,
child: TextButton.icon(
onPressed: answers.value.length < PollStart.maxAnswers
? () => answers.value = answers.value.add(nextId.value++, "")
: null,
icon: const Icon(Icons.add),
label: const Text("Add Answer"),
),
),
SwitchListTile(
secondary: const Icon(Icons.checklist),
title: const Text("Allow multiple answers"),
subtitle: const Text("Voters can choose more than one"),
value: allowMultiple.value,
onChanged: (value) => allowMultiple.value = value,
),
],
);
}
}

View file

@ -3,7 +3,7 @@ import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/via.dart";
import "package:nexus/helpers/extensions/weighted_search.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/avatars/avatar_or_hash.dart";
class const RoomOverlay(
final String query, {

View file

@ -3,7 +3,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/widgets/composer/overlays/room_overlay.dart";
import "package:nexus/widgets/composer/overlays/user_overlay.dart";
import "package:nexus/widgets/composer/overlays/emoji_overlay.dart";
import "package:nexus/widgets/loading.dart";
import "package:nexus/widgets/common/loading.dart";
class const TaggerOverlay(
final String query, {

View file

@ -5,7 +5,7 @@ import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/helpers/extensions/weighted_search.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/avatars/avatar_or_hash.dart";
class const UserOverlay(
final String query, {

View file

@ -2,7 +2,7 @@ import "package:material_ui/material_ui.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/relation_type.dart";
import "package:nexus/widgets/event_preview.dart";
import "package:nexus/widgets/renderers/event_preview.dart";
class const RelationPreview(
final Event? relatedEvent, {

View file

@ -5,8 +5,8 @@ import "package:nexus/controllers/room_typing.dart";
import "package:nexus/controllers/user.dart";
import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/helpers/hooks/tap_recognizer.dart";
import "package:nexus/widgets/bouncing_dots.dart";
import "package:nexus/widgets/user_span.dart";
import "package:nexus/widgets/common/bouncing_dots.dart";
import "package:nexus/widgets/users/user_span.dart";
class const TypingIndicator(final String roomId, {super.key})
extends HookConsumerWidget {