Files
Client/lib/view/pages/talk/widgets/poll_create_sheet.dart
T

186 lines
5.6 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../api/marianumcloud/talk/create_poll/create_poll.dart';
import '../../../../api/marianumcloud/talk/create_poll/create_poll_params.dart';
import '../../../../api/marianumcloud/talk/get_poll/get_poll_state_response.dart';
import '../../../../widget/async_action_button.dart';
import '../../../../widget/demo_restricted.dart';
import '../../../../widget/details_bottom_sheet.dart';
/// Opens the poll creation form. On success the server posts the poll message
/// into the chat itself, so the caller only needs to refresh via [onCreated].
void showPollCreateSheet(
BuildContext context, {
required String token,
required VoidCallback onCreated,
}) {
showDetailsBottomSheet(
context,
header: const ListTile(
leading: Icon(Icons.poll_outlined),
title: Text('Umfrage erstellen'),
),
children: (sheetCtx) => [_PollCreateBody(token: token, onCreated: onCreated)],
);
}
class _PollCreateBody extends StatefulWidget {
final String token;
final VoidCallback onCreated;
const _PollCreateBody({required this.token, required this.onCreated});
@override
State<_PollCreateBody> createState() => _PollCreateBodyState();
}
class _PollCreateBodyState extends State<_PollCreateBody> {
final TextEditingController _question = TextEditingController();
final List<TextEditingController> _options = [
TextEditingController(),
TextEditingController(),
];
final AsyncActionController _submitController = AsyncActionController();
bool _hideResults = false;
bool _multipleChoice = false;
String? _validationError;
@override
void dispose() {
_question.dispose();
for (final controller in _options) {
controller.dispose();
}
_submitController.dispose();
super.dispose();
}
void _addOption() {
setState(() => _options.add(TextEditingController()));
}
void _removeOption(int index) {
setState(() {
_options.removeAt(index).dispose();
});
}
Future<void> _submit() async {
if (guardDemoAction(context)) return;
final question = _question.text.trim();
final options = _options
.map((c) => c.text.trim())
.where((text) => text.isNotEmpty)
.toList();
if (question.isEmpty) {
setState(() => _validationError = 'Bitte gib eine Frage ein.');
return;
}
if (options.length < 2) {
setState(
() => _validationError = 'Bitte gib mindestens zwei Optionen an.',
);
return;
}
setState(() => _validationError = null);
await CreatePoll(
token: widget.token,
params: CreatePollParams(
question: question,
options: options,
resultMode: _hideResults
? pollResultModeHidden
: pollResultModePublic,
maxVotes: _multipleChoice ? 0 : 1,
),
).run();
if (!mounted) return;
Navigator.of(context).pop();
widget.onCreated();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: TextField(
controller: _question,
textCapitalization: TextCapitalization.sentences,
decoration: const InputDecoration(
labelText: 'Frage',
hintText: 'Worüber soll abgestimmt werden?',
),
),
),
...List.generate(_options.length, (index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Row(
children: [
Expanded(
child: TextField(
controller: _options[index],
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: 'Option ${index + 1}',
),
),
),
IconButton(
tooltip: 'Option entfernen',
icon: const Icon(Icons.remove_circle_outline),
onPressed: _options.length > 2
? () => _removeOption(index)
: null,
),
],
),
);
}),
Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
onPressed: _addOption,
icon: const Icon(Icons.add),
label: const Text('Option hinzufügen'),
),
),
SwitchListTile(
value: _hideResults,
onChanged: (value) => setState(() => _hideResults = value),
title: const Text('Ergebnisse bis zum Schließen verbergen'),
),
SwitchListTile(
value: _multipleChoice,
onChanged: (value) => setState(() => _multipleChoice = value),
title: const Text('Mehrfachauswahl erlauben'),
),
if (_validationError != null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Text(
_validationError!,
style: TextStyle(color: theme.colorScheme.error),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: AsyncActionButton(
controller: _submitController,
onPressed: _submit,
child: const Text('Umfrage erstellen'),
),
),
],
);
}
}