Made content scrollable

Added SingleChildScrollView to new-room, edit-room
and the about-room tab.
NOTE: Added a padding to the new and edit room pages,
to prevent widgets from touching the screen bottom
This commit is contained in:
Jakob Meier 2023-03-24 18:21:23 +01:00
parent 09f7e4fdd1
commit ecd87cf2cd
No known key found for this signature in database
GPG key ID: 66BDC7E6A01A6152
3 changed files with 642 additions and 629 deletions

View file

@ -36,7 +36,7 @@ class _EditRoomPageState extends State<EditRoomPage> {
_ctrIcon = room.icon!; _ctrIcon = room.icon!;
setState(() { setState(() {
this.room = room; this.room = room;
}); });
} }
@ -48,10 +48,10 @@ class _EditRoomPageState extends State<EditRoomPage> {
doNetworkRequest( doNetworkRequest(
sm, sm,
req: (user) => postWithCreadentials( req: (user) => postWithCreadentials(
path: 'getRoomInfo', path: 'getRoomInfo',
credentials: user!, credentials: user!,
target: (user.server), target: (user.server),
body: {'room': widget.tag, 'server': widget.server}), body: {'room': widget.tag, 'server': widget.server}),
onOK: (body) async { onOK: (body) async {
final room = Room.fromJSON(body['data']); final room = Room.fromJSON(body['data']);
room.toDisk(); room.toDisk();
@ -60,15 +60,15 @@ class _EditRoomPageState extends State<EditRoomPage> {
// no room data available // no room data available
// use data from disk // use data from disk
(() async { (() async {
try { try {
final diskRoom = final diskRoom =
await Room.fromDisk(serverTag: widget.server, id: widget.tag); await Room.fromDisk(serverTag: widget.server, id: widget.tag);
initFromRoom(diskRoom); initFromRoom(diskRoom);
} catch (_) { } catch (_) {
// no room data available // no room data available
// close screen // close screen
rmaster.replace('/'); rmaster.replace('/');
} }
})(); })();
return true; return true;
}, },
@ -86,22 +86,22 @@ class _EditRoomPageState extends State<EditRoomPage> {
super.initState(); super.initState();
Room.listen((_) async { Room.listen((_) async {
// rooms changed on disk // rooms changed on disk
// probably this one, // probably this one,
// because it is currently open // because it is currently open
// NOTE: might be a different room // NOTE: might be a different room
// (if a background listener is implemented at some point, // (if a background listener is implemented at some point,
// checking if this room changed might improve performance) // checking if this room changed might improve performance)
try { try {
final room = final room =
await Room.fromDisk(serverTag: widget.server, id: widget.tag); await Room.fromDisk(serverTag: widget.server, id: widget.tag);
initFromRoom(room); initFromRoom(room);
setState(() { setState(() {
showSpinner = false; showSpinner = false;
}); });
} catch (_) {} } catch (_) {}
}); });
WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo()); WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo());
@ -110,225 +110,229 @@ class _EditRoomPageState extends State<EditRoomPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context) final textTheme = Theme.of(context)
.textTheme .textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface); .apply(displayColor: Theme.of(context).colorScheme.onSurface);
double width = MediaQuery.of(context).size.width; double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height; double height = MediaQuery.of(context).size.height;
double smallest = min(min(width, height), 400); double smallest = min(min(width, height), 400);
return showSpinner return showSpinner
? Scaffold( ? Scaffold(
body: Center( body: Center(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const CircularProgressIndicator(), const CircularProgressIndicator(),
Text('Loading', style: textTheme.titleLarge), Text('Loading', style: textTheme.titleLarge),
]))) ])))
: Scaffold( : Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Edit Room'), title: const Text('Edit Room'),
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
// go back // go back
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
tooltip: "Go back", tooltip: "Go back",
), ),
), ),
body: Center( body: SingleChildScrollView(child: Center(
child: ConstrainedBox( child: Padding(
constraints: const BoxConstraints(maxWidth: 400), padding: const EdgeInsets.all(14),
child: Column( child: ConstrainedBox(
mainAxisAlignment: MainAxisAlignment.center, constraints: const BoxConstraints(maxWidth: 400),
crossAxisAlignment: CrossAxisAlignment.center, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
IconButton( crossAxisAlignment: CrossAxisAlignment.center,
icon: SvgPicture.asset( children: [
_ctrIcon.img, IconButton(
width: smallest * 0.3, icon: SvgPicture.asset(
height: smallest * 0.3, _ctrIcon.img,
), width: smallest * 0.3,
tooltip: 'Change room icon', height: smallest * 0.3,
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( tooltip: 'Change room icon',
padding: const EdgeInsets.all(8), onPressed: () {
child: TextField( showDialog(
enabled: false, context: context,
controller: _ctrID, builder: (ctx) => AlertDialog(
keyboardType: TextInputType.emailAddress, title:
decoration: const InputDecoration( const Text('Choose a room Icon'),
prefixIcon: Icon(Icons.fact_check), actions: const [],
labelText: 'Room ID', content: SizedBox(
hintText: 'Unique room id', width: smallest * 0.3 * 3,
helperText: height: smallest * 0.3 * 3,
'the room id and server tag allow the room to be identified', child: GridView.count(
border: OutlineInputBorder(), 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(
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), Padding(
child: TextField( padding: const EdgeInsets.all(8),
controller: _ctrName, child: TextField(
keyboardType: TextInputType.name, controller: _ctrName,
decoration: const InputDecoration( keyboardType: TextInputType.name,
prefixIcon: Icon(Icons.badge), decoration: const InputDecoration(
labelText: 'Room Name', prefixIcon: Icon(Icons.badge),
hintText: 'Give your room a name', labelText: 'Room Name',
helperText: hintText: 'Give your room a name',
'Easily identify a room with a human readable name', helperText:
border: OutlineInputBorder(), 'Easily identify a room with a human readable name',
), border: OutlineInputBorder(),
), ),
), ),
Padding( ),
padding: const EdgeInsets.all(8), Padding(
child: TextField( padding: const EdgeInsets.all(8),
controller: _ctrDescription, child: TextField(
keyboardType: TextInputType.text, controller: _ctrDescription,
decoration: const InputDecoration( keyboardType: TextInputType.text,
prefixIcon: Icon(Icons.dns), decoration: const InputDecoration(
labelText: 'Room Description', prefixIcon: Icon(Icons.dns),
hintText: 'Briefly describe your Room', labelText: 'Room Description',
helperText: hintText: 'Briefly describe your Room',
'Make it easier for other to know what this room is used for', helperText:
border: OutlineInputBorder(), '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); floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
final scaffMgr = ScaffoldMessenger.of(context);
final nav = Navigator.of(context);
// name may not be empty // name may not be empty
if (_ctrName.text.isEmpty) { if (_ctrName.text.isEmpty) {
final snackBar = SnackBar( final snackBar = SnackBar(
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
content: const Text('Please specify a room name'), content: const Text('Please specify a room name'),
action: SnackBarAction( action: SnackBarAction(
label: 'Ok', label: 'Ok',
onPressed: () { onPressed: () {
scaffMgr.hideCurrentSnackBar(); 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)), );
);
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)),
);
} }
} }

View file

@ -27,225 +27,230 @@ class _NewRoomPageState extends State {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context) final textTheme = Theme.of(context)
.textTheme .textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface); .apply(displayColor: Theme.of(context).colorScheme.onSurface);
double width = MediaQuery.of(context).size.width; double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height; double height = MediaQuery.of(context).size.height;
double smallest = min(min(width, height), 400); double smallest = min(min(width, height), 400);
return showSpinner return showSpinner
? Scaffold( ? Scaffold(
body: Center( body: Center(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const CircularProgressIndicator(), const CircularProgressIndicator(),
Text('Loading', style: textTheme.titleLarge), Text('Loading', style: textTheme.titleLarge),
]))) ])))
: Scaffold( : Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('New Room'), title: const Text('New Room'),
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
// go back // go back
Routemaster.of(context).history.back(); Routemaster.of(context).history.back();
}, },
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
tooltip: "Go back", tooltip: "Go back",
), ),
), ),
body: Center( body: SingleChildScrollView(
child: ConstrainedBox( child: Center(
constraints: const BoxConstraints(maxWidth: 400), child: Padding(
child: Column( padding: const EdgeInsets.all(14),
mainAxisAlignment: MainAxisAlignment.center, child: ConstrainedBox(
crossAxisAlignment: CrossAxisAlignment.center, constraints: const BoxConstraints(maxWidth: 400),
children: [ child: Column(
IconButton( mainAxisAlignment: MainAxisAlignment.center,
icon: SvgPicture.asset( crossAxisAlignment: CrossAxisAlignment.center,
_ctrIcon.img, children: [
width: smallest * 0.3, IconButton(
height: smallest * 0.3, icon: SvgPicture.asset(
), _ctrIcon.img,
tooltip: 'Change room icon', width: smallest * 0.3,
onPressed: () { height: smallest * 0.3,
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( tooltip: 'Change room icon',
padding: const EdgeInsets.all(8), onPressed: () {
child: TextField( showDialog(
controller: _ctrID, context: context,
keyboardType: TextInputType.emailAddress, builder: (ctx) => AlertDialog(
decoration: const InputDecoration( title:
prefixIcon: Icon(Icons.fact_check), const Text('Choose a room Icon'),
labelText: 'Room ID', actions: const [],
hintText: 'Unique room id', content: SizedBox(
helperText: width: smallest * 0.3 * 3,
'the room id and server tag allow the room to be identified', height: smallest * 0.3 * 3,
border: OutlineInputBorder(), 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), Padding(
child: TextField( padding: const EdgeInsets.all(8),
controller: _ctrName, child: TextField(
keyboardType: TextInputType.name, controller: _ctrName,
decoration: const InputDecoration( keyboardType: TextInputType.name,
prefixIcon: Icon(Icons.badge), decoration: const InputDecoration(
labelText: 'Room Name', prefixIcon: Icon(Icons.badge),
hintText: 'Give your room a name', labelText: 'Room Name',
helperText: hintText: 'Give your room a name',
'Easily identify a room with a human readable name', helperText:
border: OutlineInputBorder(), 'Easily identify a room with a human readable name',
), border: OutlineInputBorder(),
), ),
), ),
Padding( ),
padding: const EdgeInsets.all(8), Padding(
child: TextField( padding: const EdgeInsets.all(8),
controller: _ctrDescription, child: TextField(
keyboardType: TextInputType.text, controller: _ctrDescription,
decoration: const InputDecoration( keyboardType: TextInputType.text,
prefixIcon: Icon(Icons.dns), decoration: const InputDecoration(
labelText: 'Room Description', prefixIcon: Icon(Icons.dns),
hintText: 'Briefly describe your Room', labelText: 'Room Description',
helperText: hintText: 'Briefly describe your Room',
'Make it easier for other to know what this room is used for', helperText:
border: OutlineInputBorder(), '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', Text('Visibility', style: textTheme.labelLarge),
style: textTheme.bodySmall), Text('Specify who has access to your room',
SegmentedButton<RoomVisibility>( style: textTheme.bodySmall),
showSelectedIcon: true, SegmentedButton<RoomVisibility>(
multiSelectionEnabled: false, showSelectedIcon: true,
emptySelectionAllowed: false, multiSelectionEnabled: false,
segments: RoomVisibility.list().map((vis) { emptySelectionAllowed: false,
return ButtonSegment<RoomVisibility>( segments: RoomVisibility.list().map((vis) {
value: vis, return ButtonSegment<RoomVisibility>(
label: Text(vis.text), value: vis,
icon: Icon(vis.icon)); label: Text(vis.text),
}).toList(), icon: Icon(vis.icon));
onSelectionChanged: ((vset) { }).toList(),
setState(() { onSelectionChanged: ((vset) {
_ctrVis = vset.single; setState(() {
}); _ctrVis = vset.single;
}), });
selected: {_ctrVis}, }),
selectedIcon: Icon(_ctrVis.icon), selected: {_ctrVis},
), selectedIcon: Icon(_ctrVis.icon),
], ),
) ],
) )
), )
floatingActionButton: FloatingActionButton.extended( )
onPressed: () async { )
final scaffMgr = ScaffoldMessenger.of(context); ),
final rmaster = Routemaster.of(context); floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
final scaffMgr = ScaffoldMessenger.of(context);
final rmaster = Routemaster.of(context);
// ID should be at least three characters long // ID should be at least three characters long
if (_ctrID.text.length < 3) { if (_ctrID.text.length < 3) {
showSimpleSnackbar(scaffMgr, showSimpleSnackbar(scaffMgr,
text: _ctrID.text.isEmpty text: _ctrID.text.isEmpty
? 'Please specify a Room ID' ? 'Please specify a Room ID'
: 'Room ID has to be at least three characters long', : 'Room ID has to be at least three characters long',
action: 'OK'); action: 'OK');
return; return;
} }
// name may not be empty // name may not be empty
if (_ctrName.text.isEmpty) { if (_ctrName.text.isEmpty) {
showSimpleSnackbar( showSimpleSnackbar(
scaffMgr, scaffMgr,
text: 'Please specify a room name', text: 'Please specify a room name',
action: 'OK' action: 'OK'
); );
return; return;
} }
User user; User user;
try { try {
user = await User.fromDisk(); user = await User.fromDisk();
} catch (_) { } catch (_) {
// user data invalid // user data invalid
// shouldn't happen // shouldn't happen
return; return;
} }
final room = Room( final room = Room(
id: _ctrID.text, id: _ctrID.text,
serverTag: user.server.tag, serverTag: user.server.tag,
name: _ctrName.text, name: _ctrName.text,
description: _ctrDescription.text, description: _ctrDescription.text,
icon: _ctrIcon, icon: _ctrIcon,
visibility: _ctrVis); visibility: _ctrVis);
doNetworkRequest(scaffMgr, doNetworkRequest(scaffMgr,
req: (_) => postWithCreadentials( req: (_) => postWithCreadentials(
target: user.server, target: user.server,
credentials: user, credentials: user,
path: 'createRoom', path: 'createRoom',
body: { body: {
'room': room.id, 'room': room.id,
'title': room.name, 'title': room.name,
'description': room.description, 'description': room.description,
'icon': room.icon?.type, 'icon': room.icon?.type,
'visibility': room.visibility?.type 'visibility': room.visibility?.type
}), }),
onOK: (_) async { onOK: (_) async {
// room was created // room was created
// save room // save room
await room.toDisk(); await room.toDisk();
// move to home page // move to home page
rmaster.replace('/'); rmaster.replace('/');
}, },
// we manually fetch the user data above // we manually fetch the user data above
// because we need the serverTag // because we need the serverTag
needUser: false); needUser: false);
}, },
label: const Text('Create'), label: const Text('Create'),
icon: const Icon(Icons.add)), icon: const Icon(Icons.add)),
); );
} }
} }

View file

@ -21,18 +21,19 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context) final textTheme = Theme.of(context)
.textTheme .textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface); .apply(displayColor: Theme.of(context).colorScheme.onSurface);
double width = MediaQuery.of(context).size.width; double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height; double height = MediaQuery.of(context).size.height;
double smallest = min(width, height); double smallest = min(width, height);
return Center( return SingleChildScrollView(
child:Center(
child: Column(children: [ child: Column(children: [
// room meta display // room meta display
...(widget.room != null) ...(widget.room != null)
? [ ? [
Padding( Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: Column( child: Column(
@ -51,29 +52,29 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
style: textTheme.bodySmall, style: textTheme.bodySmall,
), ),
Text(widget.room?.description ?? '', Text(widget.room?.description ?? '',
style: textTheme.bodyMedium), style: textTheme.bodyMedium),
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: SegmentedButton<int>( child: SegmentedButton<int>(
showSelectedIcon: true, showSelectedIcon: true,
multiSelectionEnabled: false, multiSelectionEnabled: false,
emptySelectionAllowed: false, emptySelectionAllowed: false,
segments: RoomVisibility.list().map((vis) { segments: RoomVisibility.list().map((vis) {
return ButtonSegment<int>( return ButtonSegment<int>(
value: vis.type, value: vis.type,
label: Text(vis.text), label: Text(vis.text),
icon: Icon(vis.icon)); icon: Icon(vis.icon));
}).toList(), }).toList(),
onSelectionChanged: ((vset) { onSelectionChanged: ((vset) {
// check permission // check permission
// only show confirm dialog when user // only show confirm dialog when user
// is admin, owner or has CHANGE_ADMIN permission // is admin, owner or has CHANGE_ADMIN permission
if (widget.info == null || if (widget.info == null ||
(!(widget.info?.isAdmin ?? false) && (!(widget.info?.isAdmin ?? false) &&
!(widget.info?.isOwner ?? false) && !(widget.info?.isOwner ?? false) &&
((widget.info?.permissions)! & ((widget.info?.permissions)! &
RoomPermission.ota == RoomPermission.ota ==
0))) { 0))) {
// action not permitted // action not permitted
// NOTE: no error dialog should be shown // NOTE: no error dialog should be shown
// because the action is supposed to be hidden // because the action is supposed to be hidden
@ -82,73 +83,73 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
final vis = RoomVisibility(vset.first); final vis = RoomVisibility(vset.first);
showDialog( showDialog(
context: context, context: context,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
title: title:
const Text('Change room visibility'), const Text('Change room visibility'),
content: Text( content: Text(
'Do you really want to change the room visibility to: ${vis.text}'), 'Do you really want to change the room visibility to: ${vis.text}'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: const Text('Cancel'), child: const Text('Cancel'),
), ),
FilledButton( FilledButton(
onPressed: () async { onPressed: () async {
final scaffMgr = final scaffMgr =
ScaffoldMessenger.of(context); ScaffoldMessenger.of(context);
final nav = Navigator.of(context); final nav = Navigator.of(context);
doNetworkRequest(scaffMgr, doNetworkRequest(scaffMgr,
req: (user) => req: (user) =>
postWithCreadentials( postWithCreadentials(
path: 'setVisibility', path: 'setVisibility',
target: (user?.server)!, target: (user?.server)!,
body: { body: {
'room': 'room':
widget.room?.id, widget.room?.id,
'server': (widget.room 'server': (widget.room
?.serverTag)!, ?.serverTag)!,
'visibility': 'visibility':
vset.first vset.first
},
credentials: user!),
onOK: (_) {
Room r = widget.room!;
r.visibility = vis;
r.toDisk();
},
after: () {
nav.pop();
});
}, },
child: const Text('Ok'), credentials: user!),
) onOK: (_) {
], Room r = widget.room!;
)); r.visibility = vis;
}), r.toDisk();
selected: {(widget.room?.visibility?.type)!}, },
selectedIcon: Icon((widget.room?.visibility?.icon)!), after: () {
)), nav.pop();
});
},
child: const Text('Ok'),
)
],
));
}),
selected: {(widget.room?.visibility?.type)!},
selectedIcon: Icon((widget.room?.visibility?.icon)!),
)),
], ],
), ),
) )
] ]
: [], : [],
Padding( Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: Column( child: Column(
children: [ children: [
// edit room meta button // edit room meta button
...(widget.info != null && ...(widget.info != null &&
((widget.info?.isAdmin ?? false) || ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & ((widget.info?.permissions)! &
RoomPermission.changeMeta != RoomPermission.changeMeta !=
0))) 0)))
? [ ? [
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
@ -160,123 +161,126 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
Routemaster.of(context).push( Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/edit'); '/r/${widget.room?.serverTag}/${widget.room?.id}/edit');
}, },
), ),
] ]
: [], : [],
// open members view // open members view
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
title: const Text('Members'), title: const Text('Members'),
subtitle: const Text('Show Member list'), subtitle: const Text('Show Member list'),
onTap: () { onTap: () {
// open member view screen // open member view screen
Routemaster.of(context).push( Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/members'); '/r/${widget.room?.serverTag}/${widget.room?.id}/members');
},
),
...(widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.ota !=
0)))
? [
ListTile(
trailing: const Icon(Icons.chevron_right),
title: const Text('OTA'),
subtitle: const Text('Add and delete OTAs'),
onTap: () {
// show manage ota screen
Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/ota');
},
),
ListTile(
trailing: const Icon(Icons.chevron_right),
title: const Text('Invites'),
subtitle: const Text('Invite people to this room'),
onTap: () {
// show manage ota screen
Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/invite');
},
),
]
: [],
],
)),
...(widget.info != null)
? [
Padding(
padding: const EdgeInsets.all(8),
child: FilledButton.tonal(
child: Text(((widget.info?.isOwner)!)
? 'Delete Room'
: 'Leave Room'),
onPressed: () {
// show confirm dialog
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(((widget.info?.isOwner)!)
? 'Delete Room'
: 'Leave Room'),
content: Text(
'Do you really want to ${((widget.info?.isOwner)!) ? "delete" : "leave"} the room?'),
actions: [
TextButton(
onPressed: () {
// close popup
Navigator.of(ctx).pop();
},
child: const Text('Cancel'),
),
FilledButton(
onPressed: () async {
// send request
final scaffMgr =
ScaffoldMessenger.of(ctx);
final nav = Navigator.of(ctx);
final rmaster = Routemaster.of(ctx);
doNetworkRequest(
scaffMgr,
req: (user)=>postWithCreadentials(
path: ((widget.info?.isOwner)!)
? 'deleteRoom'
: 'leaveRoom',
target: (user?.server)!,
body: {
'room': widget.room?.id,
'server':
(widget.room?.serverTag)!,
},
credentials: user!),
onOK: (_) async {
// try delete room from disk
try {
await widget.room?.removeDisk();
} catch (_) {}
// go back home
rmaster.replace('/');
},
after: () {
// close popup
nav.pop();
}
);
},
child: Text(((widget.info?.isOwner)!)
? 'Delete'
: 'Leave'),
)
],
));
}, },
)) ),
...(widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.ota !=
0)))
? [
ListTile(
trailing: const Icon(Icons.chevron_right),
title: const Text('OTA'),
subtitle: const Text('Add and delete OTAs'),
onTap: () {
// show manage ota screen
Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/ota');
},
),
ListTile(
trailing: const Icon(Icons.chevron_right),
title: const Text('Invites'),
subtitle: const Text('Invite people to this room'),
onTap: () {
// show manage ota screen
Routemaster.of(context).push(
'/r/${widget.room?.serverTag}/${widget.room?.id}/invite');
},
),
]
: [],
],
)),
...(widget.info != null)
? [
Padding(
padding: const EdgeInsets.all(8),
child: FilledButton.tonal(
child: Text(((widget.info?.isOwner)!)
? 'Delete Room'
: 'Leave Room'),
onPressed: () {
// show confirm dialog
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(((widget.info?.isOwner)!)
? 'Delete Room'
: 'Leave Room'),
content: Text(
'Do you really want to ${((widget.info?.isOwner)!) ? "delete" : "leave"} the room?'),
actions: [
TextButton(
onPressed: () {
// close popup
Navigator.of(ctx).pop();
},
child: const Text('Cancel'),
),
FilledButton(
onPressed: () async {
// send request
final scaffMgr =
ScaffoldMessenger.of(ctx);
final nav = Navigator.of(ctx);
final rmaster = Routemaster.of(ctx);
doNetworkRequest(
scaffMgr,
req: (user)=>postWithCreadentials(
path: ((widget.info?.isOwner)!)
? 'deleteRoom'
: 'leaveRoom',
target: (user?.server)!,
body: {
'room': widget.room?.id,
'server':
(widget.room?.serverTag)!,
},
credentials: user!),
onOK: (_) async {
// try delete room from disk
try {
await widget.room?.removeDisk();
} catch (_) {}
// go back home
rmaster.replace('/');
},
after: () {
// close popup
nav.pop();
}
);
},
child: Text(((widget.info?.isOwner)!)
? 'Delete'
: 'Leave'),
)
],
));
},
))
] ]
: [], : [],
])); ]
)
)
);
} }
} }