cfd54e3bb5
If the room was created successfully, the wizard automatically saves the room data on disk. This allows it to be cached, even if the user loses internet connection right before the wizard exists. That way the room will be cached.
295 lines
13 KiB
Dart
295 lines
13 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:outbag_app/backend/errors.dart';
|
|
import 'package:outbag_app/backend/request.dart';
|
|
import 'package:outbag_app/backend/room.dart';
|
|
import 'package:outbag_app/backend/user.dart';
|
|
import 'package:routemaster/routemaster.dart';
|
|
import 'dart:math';
|
|
|
|
class NewRoomPage extends StatefulWidget {
|
|
const NewRoomPage({super.key});
|
|
|
|
@override
|
|
State<StatefulWidget> 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;
|
|
|
|
bool showSpinner = false;
|
|
|
|
@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('Loading', style: textTheme.titleLarge),
|
|
])))
|
|
: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('New Room'),
|
|
leading: IconButton(
|
|
onPressed: () {
|
|
// go back
|
|
Routemaster.of(context).history.back();
|
|
},
|
|
icon: const Icon(Icons.arrow_back),
|
|
tooltip: "Go back",
|
|
),
|
|
),
|
|
body: Center(
|
|
child: Flexible(
|
|
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: 'Change room icon',
|
|
onPressed: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title:
|
|
const Text('Choose a room Icon'),
|
|
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;
|
|
});
|
|
Navigator.of(
|
|
context)
|
|
.pop();
|
|
}));
|
|
}).toList())),
|
|
));
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrID,
|
|
keyboardType: TextInputType.emailAddress,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.fact_check),
|
|
labelText: 'Room ID',
|
|
hintText: 'Unique room id',
|
|
helperText:
|
|
'the room id and server tag allow the room to be identified',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrName,
|
|
keyboardType: TextInputType.name,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.badge),
|
|
labelText: 'Room Name',
|
|
hintText: 'Give your room a name',
|
|
helperText:
|
|
'Easily identify a room with a human readable name',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrDescription,
|
|
keyboardType: TextInputType.text,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.dns),
|
|
labelText: 'Room Description',
|
|
hintText: 'Briefly describe your Room',
|
|
helperText:
|
|
'Make it easier for other to know what this room is used for',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
Text('Visibility', style: textTheme.labelLarge),
|
|
Text('Specify who has access to your room',
|
|
style: textTheme.bodySmall),
|
|
SegmentedButton<RoomVisibility>(
|
|
showSelectedIcon: true,
|
|
multiSelectionEnabled: false,
|
|
emptySelectionAllowed: false,
|
|
segments: RoomVisibility.list().map((vis) {
|
|
return ButtonSegment<RoomVisibility>(
|
|
value: vis,
|
|
label: Text(vis.text),
|
|
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 rmaster = Routemaster.of(context);
|
|
|
|
// ID should be at least three characters long
|
|
if (_ctrID.text.length < 3) {
|
|
final snackBar = SnackBar(
|
|
behavior: SnackBarBehavior.floating,
|
|
content: Text(_ctrID.text.isEmpty
|
|
? 'Please specify a Room ID'
|
|
: 'Room ID has to be at least three characters long'),
|
|
action: SnackBarAction(
|
|
label: 'Ok',
|
|
onPressed: () {
|
|
scaffMgr.hideCurrentSnackBar();
|
|
},
|
|
),
|
|
);
|
|
|
|
scaffMgr.hideCurrentSnackBar();
|
|
scaffMgr.showSnackBar(snackBar);
|
|
|
|
return;
|
|
}
|
|
|
|
// name may not be empty
|
|
if (_ctrName.text.isEmpty) {
|
|
final snackBar = SnackBar(
|
|
behavior: SnackBarBehavior.floating,
|
|
content: const Text('Please specify a room name'),
|
|
action: SnackBarAction(
|
|
label: 'Ok',
|
|
onPressed: () {
|
|
scaffMgr.hideCurrentSnackBar();
|
|
},
|
|
),
|
|
);
|
|
|
|
scaffMgr.hideCurrentSnackBar();
|
|
scaffMgr.showSnackBar(snackBar);
|
|
|
|
return;
|
|
}
|
|
|
|
User user;
|
|
try {
|
|
user = await User.fromDisk();
|
|
} catch (_) {
|
|
// user data invalid
|
|
// shouldn't happen
|
|
return;
|
|
}
|
|
|
|
final room = Room(
|
|
id: _ctrID.text,
|
|
serverTag: user.server.tag,
|
|
name: _ctrName.text,
|
|
description: _ctrDescription.text,
|
|
icon: _ctrIcon,
|
|
visibility: _ctrVis);
|
|
try {
|
|
final resp = await postWithCreadentials(
|
|
target: user.server,
|
|
credentials: user,
|
|
path: 'createRoom',
|
|
body: {
|
|
'room': room.id,
|
|
'server': room.serverTag,
|
|
'title': room.name,
|
|
'description': room.description,
|
|
'icon': room.icon?.type,
|
|
'visibility': room.visibility?.type
|
|
}
|
|
);
|
|
if (resp.res == Result.ok) {
|
|
// room was created
|
|
// save room
|
|
await room.toDisk();
|
|
// move to home page
|
|
rmaster.replace('/');
|
|
} else {
|
|
// error
|
|
final snackBar = SnackBar(
|
|
behavior: SnackBarBehavior.floating,
|
|
content: Text(errorAsString(resp.body)),
|
|
action: SnackBarAction(
|
|
label: 'Dismiss',
|
|
onPressed: () {
|
|
scaffMgr.hideCurrentSnackBar();
|
|
},
|
|
),
|
|
);
|
|
|
|
scaffMgr.hideCurrentSnackBar();
|
|
scaffMgr.showSnackBar(snackBar);
|
|
}
|
|
} catch (_) {
|
|
final snackBar = SnackBar(
|
|
behavior: SnackBarBehavior.floating,
|
|
content: const Text('Network error'),
|
|
action: SnackBarAction(
|
|
label: 'Dismiss',
|
|
onPressed: () {
|
|
scaffMgr.hideCurrentSnackBar();
|
|
},
|
|
),
|
|
);
|
|
|
|
scaffMgr.hideCurrentSnackBar();
|
|
scaffMgr.showSnackBar(snackBar);
|
|
}
|
|
},
|
|
label: const Text('Create'),
|
|
icon: const Icon(Icons.add)),
|
|
);
|
|
}
|
|
}
|