import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:outbag_app/backend/request.dart'; import 'package:outbag_app/backend/room.dart'; import 'package:outbag_app/backend/user.dart'; import 'package:outbag_app/tools/fetch_wrapper.dart'; import 'package:outbag_app/tools/snackbar.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'dart:math'; class EditRoomPage extends StatefulWidget { final String server; final String tag; const EditRoomPage(this.server, this.tag, {super.key}); @override State createState() => _EditRoomPageState(); } class _EditRoomPageState extends State { final TextEditingController _ctrName = TextEditingController(); final TextEditingController _ctrID = TextEditingController(); final TextEditingController _ctrDescription = TextEditingController(); RoomIcon _ctrIcon = RoomIcon.other; Room? room; // show spinner by default // until data has been fetched bool showSpinner = true; void initFromRoom(Room room) { _ctrID.text = room.id; _ctrName.text = room.name; _ctrDescription.text = room.description; _ctrIcon = room.icon!; setState(() { this.room = room; }); } // fetch room information void fetchInfo() { final sm = ScaffoldMessenger.of(context); final router = GoRouter.of(context); final user = context.read(); doNetworkRequest( sm, req: () => postWithCreadentials( path: 'getRoomInfo', credentials: user, target: user.server, body: {'room': widget.tag, 'server': widget.server}), onOK: (body) async { final room = Room.fromJSON(body['data']); room.toDisk(); }, onNetworkErr: () { // no room data available // use data from disk (() async { try { final diskRoom = await Room.fromDisk(serverTag: widget.server, id: widget.tag); initFromRoom(diskRoom); } catch (_) { // no room data available // close screen router.pushReplacementNamed('home'); } })(); return true; }, onServerErr: (json) { // user not allowed to be here // close screen router.pushReplacementNamed('home'); return true; }, ); } @override void initState() { super.initState(); Room.listen((_) async { // rooms changed on disk // probably this one, // because it is currently open // NOTE: might be a different room // (if a background listener is implemented at some point, // checking if this room changed might improve performance) try { final room = await Room.fromDisk(serverTag: widget.server, id: widget.tag); initFromRoom(room); setState(() { showSpinner = false; }); } catch (_) {} }); WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo()); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) .textTheme .apply(displayColor: Theme.of(context).colorScheme.onSurface); double width = MediaQuery.of(context).size.width; double height = MediaQuery.of(context).size.height; double smallest = min(min(width, height), 400); return showSpinner ? Scaffold( body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(), Text(AppLocalizations.of(context)!.loading, style: textTheme.titleLarge), ]))) : Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context)!.editRoomMetadataShort), ), body: SingleChildScrollView( child: Center( child: Padding( padding: const EdgeInsets.all(14), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ IconButton( icon: SvgPicture.asset( _ctrIcon.img, width: smallest * 0.3, height: smallest * 0.3, ), tooltip: AppLocalizations.of(context)!.changeRoomIcon, onPressed: () { showDialog( context: context, builder: (ctx) => AlertDialog( title: Text( AppLocalizations.of(context)!.chooseRoomIcon), actions: const [], content: SizedBox( width: smallest * 0.3 * 3, height: smallest * 0.3 * 3, child: GridView.count( crossAxisCount: 3, children: RoomIcon.list() .map((icon) { return GridTile( child: IconButton( icon: SvgPicture .asset( icon.img, width: smallest * 0.3, height: smallest * 0.3, ), tooltip: icon.text, onPressed: () { setState(() { _ctrIcon = icon; }); context.pop(); })); }).toList())), )); }, ), Padding( padding: const EdgeInsets.all(8), child: TextField( enabled: false, controller: _ctrID, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( prefixIcon: const Icon(Icons.fact_check), labelText: AppLocalizations.of(context)! .inputRoomIdLabel, hintText: AppLocalizations.of(context)! .inputRoomIdHint, helperText: AppLocalizations.of(context)! .inputRoomIdHelp, border: const OutlineInputBorder(), ), ), ), Padding( padding: const EdgeInsets.all(8), child: TextField( controller: _ctrName, keyboardType: TextInputType.name, decoration: InputDecoration( prefixIcon: const Icon(Icons.badge), labelText: AppLocalizations.of(context)! .inputRoomNameLabel, hintText: AppLocalizations.of(context)! .inputRoomNameHint, helperText: AppLocalizations.of(context)! .inputRoomNameHelp, border: const OutlineInputBorder(), ), ), ), Padding( padding: const EdgeInsets.all(8), child: TextField( controller: _ctrDescription, keyboardType: TextInputType.text, decoration: InputDecoration( labelText: AppLocalizations.of(context)! .inputRoomDescriptionLabel, hintText: AppLocalizations.of(context)! .inputRoomDescriptionHint, helperText: AppLocalizations.of(context)! .inputRoomDescriptionHelp, prefixIcon: const Icon(Icons.dns), border: const OutlineInputBorder(), ), ), ), ], ))))), floatingActionButton: FloatingActionButton.extended( onPressed: () async { final scaffMgr = ScaffoldMessenger.of(context); final nav = Navigator.of(context); final trans = AppLocalizations.of(context); // name may not be empty if (_ctrName.text.isEmpty) { showSimpleSnackbar(scaffMgr, text: trans!.errorNoRoomName, action: trans.ok); return; } User user; try { user = await User.fromDisk(); } catch (_) { // user data invalid // shouldn't happen return; } Room clone = room!; clone.name = _ctrName.text; clone.description = _ctrDescription.text; clone.icon = _ctrIcon; doNetworkRequest(scaffMgr, req: ()=>postWithCreadentials( target: user.server, credentials: user, path: 'changeRoomMeta', body: { 'room': clone.id, 'server': clone.serverTag, 'title': clone.name, 'description': clone.description, 'icon': clone.icon?.type, }), onOK: (_) async { // room was created // save room await clone.toDisk(); nav.pop(); } ); }, label: Text(AppLocalizations.of(context)!.update), icon: const Icon(Icons.edit)), ); } }