Finished join-public-room screen
NOTE: If the user is already in a public room, the room will not be shown in the list. NOTE: The search funtionality has not been implemented yet. NOTE: The join invite-only room screen will be part of a later commit. But the endpoint has already been set to /add-room/by-id
This commit is contained in:
parent
596c8cc4eb
commit
6347476b2f
1 changed files with 298 additions and 24 deletions
|
@ -1,9 +1,11 @@
|
|||
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 JoinRoomPage extends StatefulWidget {
|
||||
const JoinRoomPage({super.key});
|
||||
|
@ -15,45 +17,97 @@ class JoinRoomPage extends StatefulWidget {
|
|||
class _JoinRoomPageState extends State {
|
||||
List<Room> rooms = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
(() async {
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
void fetchData() async {
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
path: 'listPublicRooms',
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
// parse rooms
|
||||
final list = resp.body['data'];
|
||||
|
||||
// try to fetch a list of rooms the user is a member of
|
||||
// use an empty blacklist when request is not successful
|
||||
final blacklist = [];
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
path: 'listPublicRooms',
|
||||
path: 'listRooms',
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
// parse rooms
|
||||
final List<Room> list = resp.body['data'].map<Room>((json) {
|
||||
return Room.fromJSON(json);
|
||||
}).toList();
|
||||
setState(() {
|
||||
rooms = list;
|
||||
});
|
||||
} else {
|
||||
throw Error();
|
||||
for (Room r in list) {
|
||||
blacklist.add(r);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// process the list of public rooms
|
||||
List<Room> builder = [];
|
||||
processor:
|
||||
for (dynamic raw in list) {
|
||||
try {
|
||||
final room = Room.fromJSON(raw);
|
||||
|
||||
// figure out if room is on blacklist
|
||||
// only add room to list,
|
||||
// if not on blacklist
|
||||
for (Room r in blacklist) {
|
||||
if (room.serverTag == r.serverTag && room.id == r.id) {
|
||||
// server on white list
|
||||
// move to next iteration on outer for loop
|
||||
continue processor;
|
||||
}
|
||||
}
|
||||
|
||||
builder.add(room);
|
||||
} catch (_) {
|
||||
// ignore room
|
||||
}
|
||||
} catch (_) {
|
||||
// network error
|
||||
// unable to load room list
|
||||
// NOTE: might want to show snackbar
|
||||
// with warning
|
||||
}
|
||||
})();
|
||||
builder.sort();
|
||||
setState(() {
|
||||
rooms = builder;
|
||||
});
|
||||
} else {
|
||||
throw Error();
|
||||
}
|
||||
} catch (_) {
|
||||
// network error
|
||||
// unable to load room list
|
||||
// NOTE: might want to show snackbar
|
||||
// with warning
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchData();
|
||||
}
|
||||
|
||||
@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(width, height);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Join Room'),
|
||||
|
@ -65,8 +119,57 @@ class _JoinRoomPageState extends State {
|
|||
icon: const Icon(Icons.arrow_back),
|
||||
tooltip: "Go back",
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
tooltip: "Search",
|
||||
onPressed: () {
|
||||
// show searchbar
|
||||
// NOTE: location currently unknown
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: "Refresh",
|
||||
onPressed: () {
|
||||
// fetch public rooms again
|
||||
fetchData();
|
||||
},
|
||||
),
|
||||
MenuAnchor(
|
||||
builder: (ctx, controller, child) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.more_vert),
|
||||
);
|
||||
},
|
||||
menuChildren: [
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(Icons.drafts),
|
||||
child: const Text('Join invite-only room'),
|
||||
onPressed: () {
|
||||
// show settings screen
|
||||
Routemaster.of(context).push("/add-room/by-id");
|
||||
}),
|
||||
])
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
body: rooms.isEmpty
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text('No new Rooms found', style: textTheme.titleLarge),
|
||||
],
|
||||
))
|
||||
: ListView.builder(
|
||||
itemCount: rooms.length,
|
||||
itemBuilder: (ctx, i) {
|
||||
final room = rooms[i];
|
||||
|
@ -79,6 +182,177 @@ class _JoinRoomPageState extends State {
|
|||
// TODO: show modalBottomSheet
|
||||
// with room information
|
||||
// and join button
|
||||
showModalBottomSheet(
|
||||
context: ctx,
|
||||
builder: (ctx) {
|
||||
return BottomSheet(
|
||||
onClosing: () {},
|
||||
builder: (ctx) {
|
||||
return Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: Column(children: [
|
||||
// room icon
|
||||
SvgPicture.asset(
|
||||
(room.icon?.img)!,
|
||||
width: smallest * 0.2,
|
||||
height: smallest * 0.2,
|
||||
),
|
||||
// room name
|
||||
Text(
|
||||
room.name,
|
||||
style: textTheme.displayMedium,
|
||||
),
|
||||
Text(
|
||||
'${room.id}@${room.serverTag}',
|
||||
style: textTheme.labelSmall,
|
||||
),
|
||||
// description
|
||||
Text(room.description,
|
||||
style: textTheme.bodyLarge),
|
||||
// visibility
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(room.visibility?.icon),
|
||||
Text((room
|
||||
.visibility?.text)!),
|
||||
]),
|
||||
])),
|
||||
// action buttons
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
// cancel button
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(14),
|
||||
child: ElevatedButton.icon(
|
||||
icon:
|
||||
const Icon(Icons.close),
|
||||
label: const Text('Cancel'),
|
||||
onPressed: () {
|
||||
// close sheet
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
// join room button
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(14),
|
||||
child: FilledButton.icon(
|
||||
icon:
|
||||
const Icon(Icons.check),
|
||||
label: const Text('Join'),
|
||||
onPressed: () async {
|
||||
final scaffMgr =
|
||||
ScaffoldMessenger.of(
|
||||
context);
|
||||
final rmaster =
|
||||
Routemaster.of(
|
||||
context);
|
||||
final nav =
|
||||
Navigator.of(context);
|
||||
|
||||
// join room & close screen
|
||||
User user;
|
||||
try {
|
||||
user = await User
|
||||
.fromDisk();
|
||||
} catch (_) {
|
||||
// user data invalid
|
||||
// NOTE: shouldn't happen
|
||||
// because the main.dart watches the auth meta data
|
||||
// and auto logs-out the user
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp =
|
||||
await postWithCreadentials(
|
||||
target:
|
||||
user.server,
|
||||
path:
|
||||
'joinPublicRoom',
|
||||
body: {
|
||||
'room':
|
||||
room.id,
|
||||
'server': room
|
||||
.serverTag
|
||||
},
|
||||
credentials:
|
||||
user);
|
||||
if (resp.res ==
|
||||
Result.ok) {
|
||||
// successfully joined room
|
||||
await room.toDisk();
|
||||
nav.pop();
|
||||
rmaster.replace(
|
||||
'/r/${room.serverTag}/${room.id}');
|
||||
} else {
|
||||
// server 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 (_) {
|
||||
// network error
|
||||
final snackBar =
|
||||
SnackBar(
|
||||
behavior:
|
||||
SnackBarBehavior
|
||||
.floating,
|
||||
content: const Text(
|
||||
'Network error'),
|
||||
action:
|
||||
SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
scaffMgr
|
||||
.hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
scaffMgr
|
||||
.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(
|
||||
snackBar);
|
||||
}
|
||||
},
|
||||
))
|
||||
])
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
||||
|
|
Loading…
Reference in a new issue