import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/types/poll.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:uuid/uuid.dart'; class PollEditorDialog extends StatefulWidget { final SnPoll? poll; const PollEditorDialog({super.key, this.poll}); @override State createState() => _PollEditorDialogState(); } class _PollEditorDialogState extends State { final TextEditingController _linkController = TextEditingController(); final List _pollOptions = List.empty(growable: true); bool _isBusy = false; Future _fetchPoll() async { if (_linkController.text.isEmpty) return; try { setState(() => _isBusy = true); final sn = context.read(); final resp = await sn.client.get('/cgi/co/polls/${_linkController.text}'); final out = SnPoll.fromJson(resp.data); if (!mounted) return; Navigator.pop(context, out); } catch (err) { if (!mounted) return; context.showErrorDialog(err); } finally { setState(() => _isBusy = false); } } Future _applyPost() async { try { setState(() => _isBusy = true); final sn = context.read(); final resp = widget.poll == null ? await sn.client.post('/cgi/co/polls', data: { 'options': _pollOptions.where((ele) => ele.name.isNotEmpty).toList(), }) : await sn.client.put('/cgi/co/polls/${widget.poll!.id}', data: { 'options': _pollOptions.where((ele) => ele.name.isNotEmpty).toList(), }); final out = SnPoll.fromJson(resp.data); if (!mounted) return; Navigator.pop(context, out); } catch (err) { if (!mounted) return; context.showErrorDialog(err); } finally { setState(() => _isBusy = false); } } Future _deletePoll() async { final confirm = await context.showConfirmDialog( 'pollEditorDelete'.tr(), 'pollEditorDeleteDescription'.tr(), ); if (!confirm) return; if (!mounted) return; try { setState(() => _isBusy = true); final sn = context.read(); await sn.client.delete('/cgi/co/polls/${widget.poll!.id}'); if (!mounted) return; Navigator.pop(context, false); } catch (err) { if (!mounted) return; context.showErrorDialog(err); } finally { setState(() => _isBusy = false); } } @override void initState() { super.initState(); _pollOptions.addAll(widget.poll?.options ?? []); } @override void dispose() { _linkController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AlertDialog( title: Text(widget.poll == null ? 'pollEditorNew' : 'pollEditorEdit').tr(), content: Column( mainAxisSize: MainAxisSize.min, spacing: 16, children: [ if (widget.poll == null) TextField( controller: _linkController, decoration: InputDecoration( isDense: true, labelText: 'pollLinkExisting'.tr(), prefixText: '#', suffixIcon: IconButton( visualDensity: const VisualDensity(horizontal: -4, vertical: -4), constraints: const BoxConstraints(), padding: EdgeInsets.zero, onPressed: _isBusy ? null : () => _fetchPoll(), icon: const Icon(Icons.keyboard_arrow_right), ), border: const OutlineInputBorder(), ), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), Card( margin: EdgeInsets.zero, child: Column( children: [ for (int i = 0; i < _pollOptions.length; i++) ListTile( leading: const Icon(Symbols.circle), title: TextFormField( decoration: InputDecoration.collapsed( hintText: 'pollOptionName'.tr(), ), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), initialValue: _pollOptions[i].name, onChanged: (value) { // Looks like we don't need set state here cuz it got internal updated. _pollOptions[i] = _pollOptions[i].copyWith(name: value); }, ), trailing: IconButton( visualDensity: const VisualDensity(horizontal: -4, vertical: -4), padding: EdgeInsets.zero, constraints: const BoxConstraints(), onPressed: () { setState(() => _pollOptions.removeAt(i)); }, icon: const Icon(Icons.close), ), ), ListTile( leading: const Icon(Symbols.add), title: Text('pollOptionAdd').tr(), onTap: () { setState( () => _pollOptions.add( SnPollOption(id: const Uuid().v4(), icon: '', name: '', description: ''), ), ); }, ), ], ), ), if (widget.poll != null) Card( margin: EdgeInsets.zero, child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Symbols.delete), trailing: const Icon(Symbols.chevron_right), title: Text('pollEditorDelete').tr(), onTap: _isBusy ? null : () => _deletePoll(), ), ListTile( leading: const Icon(Symbols.link_off), trailing: const Icon(Symbols.chevron_right), title: Text('pollEditorUnlink').tr(), onTap: _isBusy ? null : () => Navigator.pop(context, false), ), ], ), ), ], ), actions: [ TextButton( onPressed: _isBusy ? null : () => Navigator.pop(context), child: Text('cancel'.tr()), ), TextButton( onPressed: _isBusy ? null : () => _applyPost(), child: Text('dialogConfirm'.tr()), ), ], ); } }