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/components/room_icon_picker.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 NewRoomPage extends StatefulWidget { String? server; String? tag; NewRoomPage({this.server, this.tag, super.key}); @override State createState() => _NewRoomPageState(); } class _NewRoomPageState extends State { final TextEditingController _ctrID = TextEditingController(); final TextEditingController _ctrName = TextEditingController(); final TextEditingController _ctrDescription = TextEditingController(); RoomVisibility _ctrVis = RoomVisibility.private; RoomIcon _ctrIcon = RoomIcon.other; Room? room; bool showSpinner = false; 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() async { final sm = ScaffoldMessenger.of(context); final router = GoRouter.of(context); final user = context.read(); try { final diskRoom = await Room.fromDisk(serverTag: widget.server!, id: widget.tag!); initFromRoom(diskRoom); } catch (_) {} 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(); initFromRoom(room); }, onNetworkErr: () { // no room data available // use data from disk (() async { // 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(); WidgetsBinding.instance.addPostFrameCallback((_) { if (isEditPage()) { fetchInfo(); } }); } bool isEditPage() { return widget.server != null && widget.tag != null; } @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(isEditPage() ? AppLocalizations.of(context)!.editRoomMetadata : AppLocalizations.of(context)!.newRoom), ), 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) => RoomIconPicker(onSelect: (icon) { setState(() { _ctrIcon = icon; }); context.pop(); })); }), Padding( padding: const EdgeInsets.all(8), child: TextField( enabled: !isEditPage(), 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(), ), ), ), ...(!isEditPage()) ? [ Text( AppLocalizations.of(context)! .roomVisibilityTitle, style: textTheme.labelLarge), Text( AppLocalizations.of(context)! .roomVisibilitySubtitle, style: textTheme.bodySmall), SegmentedButton( showSelectedIcon: true, multiSelectionEnabled: false, emptySelectionAllowed: false, segments: RoomVisibility.list().map((vis) { return ButtonSegment< RoomVisibility>( value: vis, label: Text(vis.text(context)), icon: Icon(vis.icon)); }).toList(), onSelectionChanged: ((vset) { setState(() { _ctrVis = vset.single; }); }), selected: {_ctrVis}, selectedIcon: Icon(_ctrVis.icon), ), ] : [] ], ))))), floatingActionButton: FloatingActionButton.extended( onPressed: () async { final scaffMgr = ScaffoldMessenger.of(context); final router = GoRouter.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; } final user = context.read(); if (isEditPage()) { final nav = Navigator.of(context); 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(); }); } else { // new room specific tests & request // ID should be at least three characters long if (_ctrID.text.length < 3) { showSimpleSnackbar(scaffMgr, text: _ctrID.text.isEmpty ? trans!.errorNoRoomId : trans!.errorRoomIdLength, action: trans.ok); return; } final room = Room( id: _ctrID.text, serverTag: user.server.tag, name: _ctrName.text, description: _ctrDescription.text, icon: _ctrIcon, visibility: _ctrVis); doNetworkRequest(scaffMgr, req: () => postWithCreadentials( target: user.server, credentials: user, path: 'createRoom', body: { 'room': room.id, 'title': room.name, 'description': room.description, 'icon': room.icon?.type, 'visibility': room.visibility?.type }), onOK: (_) async { // room was created // save room await room.toDisk(); // move to home page router.pushReplacementNamed('home'); }); } }, label: Text(isEditPage() ? AppLocalizations.of(context)!.editRoomMetadataShort : AppLocalizations.of(context)!.createRoomShort), icon: Icon(isEditPage() ? Icons.edit : Icons.add)), ); } }