actions-test/lib/screens/room/edit.dart
Jakob Meier 90adcc6bb1
Fixed context bugs caused by moving to GoRouter
NOTE: Some bugs have not been fixed, and the member screen needs fixing,
but before that can happen, localstore has to be replaced,
because it causes loops on android and linux.
It also seems like it might lead to memory leaks
2023-03-28 17:21:35 +02:00

331 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.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:outbag_app/tools/fetch_wrapper.dart';
import 'package:provider/provider.dart';
import 'dart:math';
class EditRoomPage extends StatefulWidget {
final String server;
final String tag;
const EditRoomPage(this.server, this.tag, {super.key});
@override
State<StatefulWidget> createState() => _EditRoomPageState();
}
class _EditRoomPageState extends State<EditRoomPage> {
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<User>();
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('homoe');
}
})();
return true;
},
onServerErr: (json) {
// user not allowed to be here
// close screen
router.pushReplacementNamed('homoe');
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('Loading', style: textTheme.titleLarge),
])))
: Scaffold(
appBar: AppBar(
title: const Text('Edit Room'),
),
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: '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;
});
context.pop();
}));
}).toList())),
));
},
),
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
enabled: false,
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(),
),
),
),
],
))))),
floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
final scaffMgr = ScaffoldMessenger.of(context);
final nav = Navigator.of(context);
// 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;
}
Room clone = room!;
clone.name = _ctrName.text;
clone.description = _ctrDescription.text;
clone.icon = _ctrIcon;
try {
final resp = await 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,
});
if (resp.res == Result.ok) {
// room was created
// save room
await clone.toDisk();
nav.pop();
} 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('Update'),
icon: const Icon(Icons.edit)),
);
}
}