Simplified network requests & snackbars
and rewrote the component's network requests. showSimpleSnackbar, allows displaying a simple snackbar, with text and one action button, that can be clicked. doNetworkRequest is supposed to be a wrapper for the already existing post* functions. It aims to make network requests and error handling easier, by containing all the try&catch blocks and being able to show snackbars.
This commit is contained in:
parent
eabb6b93ae
commit
5d333522a5
11 changed files with 637 additions and 751 deletions
|
@ -187,10 +187,10 @@ class RoomIcon {
|
|||
}
|
||||
|
||||
class Room {
|
||||
final String id;
|
||||
final String serverTag;
|
||||
final String name;
|
||||
final String description;
|
||||
String id;
|
||||
String serverTag;
|
||||
String name;
|
||||
String description;
|
||||
RoomIcon? icon = RoomIcon.other;
|
||||
RoomVisibility? visibility = RoomVisibility.private;
|
||||
|
||||
|
@ -202,6 +202,17 @@ class Room {
|
|||
this.icon,
|
||||
this.visibility});
|
||||
|
||||
int compareTo(Room r) {
|
||||
final me = humanReadable;
|
||||
final other = r.humanReadable;
|
||||
|
||||
return me.compareTo(other);
|
||||
}
|
||||
|
||||
String get humanReadable {
|
||||
return '$id@$serverTag';
|
||||
}
|
||||
|
||||
// get list of all known rooms
|
||||
static Future<List<Room>> listRooms() async {
|
||||
final db = Localstore.instance;
|
||||
|
|
|
@ -30,6 +30,12 @@ class User {
|
|||
);
|
||||
}
|
||||
|
||||
static Future<void> removeDisk() async {
|
||||
final db = Localstore.instance;
|
||||
await db.collection('meta').doc('auth').delete();
|
||||
return;
|
||||
}
|
||||
|
||||
static listen(Function(Map<String, dynamic>) cb) async {
|
||||
final db = Localstore.instance;
|
||||
final stream = db.collection('meta').stream;
|
||||
|
@ -60,7 +66,6 @@ class AccountMeta {
|
|||
maxRoomSize: json['maxRoomSize'],
|
||||
maxRoomCount: json['maxRooms'],
|
||||
maxRoomMemberCount: json['maxUsersPerRoom'],
|
||||
discvoverable: json['viewable'] == 1
|
||||
);
|
||||
discvoverable: json['viewable'] == 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'package:outbag_app/screens/room/join.dart';
|
||||
import 'package:outbag_app/screens/room/new.dart';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import './screens/home.dart';
|
||||
import './screens/welcome.dart';
|
||||
|
@ -58,38 +59,30 @@ class _OutbagAppState extends State {
|
|||
// try to obtain user account information
|
||||
// with existing details
|
||||
// NOTE: also functions as a way to verify ther data
|
||||
(() async {
|
||||
User credentials;
|
||||
try {
|
||||
credentials = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// invalid credentials
|
||||
// log out
|
||||
setState(() {
|
||||
isAuthorized = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
target: credentials.server,
|
||||
doNetworkRequest(
|
||||
null,
|
||||
req: (user) => postWithCreadentials(
|
||||
target: (user?.server)!,
|
||||
path: 'getMyAccount',
|
||||
credentials: credentials,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
final info = AccountMeta.fromJSON(resp.body['data']);
|
||||
credentials: user!,
|
||||
body: {}),
|
||||
onOK: (body) {
|
||||
final info = AccountMeta.fromJSON(body['data']);
|
||||
setState(() {
|
||||
isAuthorized = true;
|
||||
this.info = info;
|
||||
});
|
||||
} else {
|
||||
},
|
||||
onServerErr: (body) {
|
||||
// credentials are wrong
|
||||
// log out
|
||||
|
||||
setState(() {
|
||||
isAuthorized = false;
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
return true;
|
||||
},
|
||||
onNetworkErr: () {
|
||||
// user is currently offline
|
||||
// approve login,
|
||||
// until user goes back offline
|
||||
|
@ -97,8 +90,17 @@ class _OutbagAppState extends State {
|
|||
setState(() {
|
||||
isAuthorized = true;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
onUserErr: () {
|
||||
// invalid credentials
|
||||
// log out
|
||||
setState(() {
|
||||
isAuthorized = false;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
})();
|
||||
);
|
||||
|
||||
// wait for user to be authorized
|
||||
User.listen((data) async {
|
||||
|
@ -115,7 +117,9 @@ class _OutbagAppState extends State {
|
|||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider<AccountMeta?>.value(value: info,),
|
||||
Provider<AccountMeta?>.value(
|
||||
value: info,
|
||||
),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
title: "Outbag",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:outbag_app/backend/request.dart';
|
||||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:outbag_app/tools/snackbar.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
import '../backend/resolve_url.dart';
|
||||
import '../backend/errors.dart';
|
||||
|
@ -173,6 +175,8 @@ class _AuthPageState extends State<AuthPage> {
|
|||
showSpinner = true;
|
||||
});
|
||||
|
||||
final scaffMgr = ScaffoldMessenger.of(context);
|
||||
|
||||
// verify that both passwords are the same
|
||||
if (widget.mode != Mode.signin) {
|
||||
if (_ctrPassword.text != _ctrPasswordRpt.text) {
|
||||
|
@ -180,19 +184,8 @@ class _AuthPageState extends State<AuthPage> {
|
|||
showSpinner = false;
|
||||
});
|
||||
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: const Text('Passwords do not match'),
|
||||
action: SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
text: 'Passwords do not match', action: 'Dismiss');
|
||||
|
||||
_ctrPasswordRpt.clear();
|
||||
return;
|
||||
|
@ -205,28 +198,15 @@ class _AuthPageState extends State<AuthPage> {
|
|||
showSpinner = false;
|
||||
});
|
||||
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: const Text(
|
||||
'Password has to be at least 6 characters long'),
|
||||
action: SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
text: 'Password has to be at least 6 characters longs',
|
||||
action: 'Dismiss');
|
||||
|
||||
_ctrPasswordRpt.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
final scaffMgr = ScaffoldMessenger.of(context);
|
||||
|
||||
// TODO: resolve homeserver url
|
||||
// resolve homeserver url
|
||||
OutbagServer server;
|
||||
try {
|
||||
server = await getOutbagServerUrl(_ctrServer.text);
|
||||
|
@ -238,31 +218,24 @@ class _AuthPageState extends State<AuthPage> {
|
|||
showSpinner = false;
|
||||
});
|
||||
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: Text(
|
||||
'Unable to find valid outbag server on ${_ctrServer.text}'),
|
||||
action: SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(snackBar);
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
text:
|
||||
'Unable to find valid outbag server on ${_ctrServer.text}',
|
||||
action: 'Dismiss');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// hash password
|
||||
var bytes = utf8.encode(_ctrPassword.text);
|
||||
final password = sha256.convert(bytes).toString();
|
||||
try {
|
||||
Response resp;
|
||||
// validate account
|
||||
|
||||
doNetworkRequest(
|
||||
scaffMgr,
|
||||
needUser: false,
|
||||
req: (_) {
|
||||
if (widget.mode == Mode.signin) {
|
||||
resp = await postUnauthorized(
|
||||
return postUnauthorized(
|
||||
target: server,
|
||||
path: 'signin',
|
||||
body: {
|
||||
|
@ -271,7 +244,7 @@ class _AuthPageState extends State<AuthPage> {
|
|||
'accountKey': password
|
||||
});
|
||||
} else if (widget.mode == Mode.signup) {
|
||||
resp = await postUnauthorized(
|
||||
return postUnauthorized(
|
||||
target: server,
|
||||
path: 'signup',
|
||||
body: {
|
||||
|
@ -281,7 +254,7 @@ class _AuthPageState extends State<AuthPage> {
|
|||
});
|
||||
} else {
|
||||
// signup OTA
|
||||
resp = await postUnauthorized(
|
||||
return postUnauthorized(
|
||||
target: server,
|
||||
path: 'signupOTA',
|
||||
body: {
|
||||
|
@ -291,49 +264,21 @@ class _AuthPageState extends State<AuthPage> {
|
|||
'OTA': _ctrOTA.text
|
||||
});
|
||||
}
|
||||
|
||||
if (resp.res == Result.err) {
|
||||
// error
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: Text(errorAsString(resp.body)),
|
||||
action: SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(snackBar);
|
||||
} else {
|
||||
onOK: (body) async {
|
||||
// authorize user
|
||||
await User(
|
||||
username: _ctrUsername.text,
|
||||
password: password,
|
||||
server: server)
|
||||
.toDisk();
|
||||
}
|
||||
} catch (_) {
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: const Text('Network error'),
|
||||
action: SnackBarAction(
|
||||
label: 'Dismiss',
|
||||
onPressed: () {
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(snackBar);
|
||||
}
|
||||
|
||||
after: () {
|
||||
setState(() {
|
||||
showSpinner = false;
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
label: Text(modeName),
|
||||
icon: const Icon(Icons.check),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||
import 'package:outbag_app/backend/permissions.dart';
|
||||
import 'package:outbag_app/backend/request.dart';
|
||||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
import '../backend/room.dart';
|
||||
|
@ -31,6 +32,13 @@ class _HomePageState extends State<HomePage> {
|
|||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
|
||||
// load cached rooms
|
||||
(() async {
|
||||
|
@ -42,32 +50,20 @@ class _HomePageState extends State<HomePage> {
|
|||
} catch (_) {}
|
||||
})();
|
||||
|
||||
// fetch room list
|
||||
(() async {
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// probably not logged in
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
path: 'listRooms',
|
||||
credentials: user,
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
final List<Room> list = resp.body['data'].map<Room>((json){
|
||||
body: {}),
|
||||
onOK: (body) async {
|
||||
final List<Room> list = body['data'].map<Room>((json) {
|
||||
return Room.fromJSON(json);
|
||||
}).toList();
|
||||
for (Room r in list) {
|
||||
await r.toDisk();
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -106,7 +102,10 @@ class _HomePageState extends State<HomePage> {
|
|||
Routemaster.of(context).push("/settings");
|
||||
}),
|
||||
...(context.watch<AccountMeta?>() != null &&
|
||||
(context.watch<AccountMeta?>()?.permissions)! & ServerPermission.allManagement != 0)?[
|
||||
(context.watch<AccountMeta?>()?.permissions)! &
|
||||
ServerPermission.allManagement !=
|
||||
0)
|
||||
? [
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(Icons.dns),
|
||||
child: const Text('Server Dashboard'),
|
||||
|
@ -114,7 +113,8 @@ class _HomePageState extends State<HomePage> {
|
|||
// show settings screen
|
||||
Routemaster.of(context).push("/server");
|
||||
}),
|
||||
]:[],
|
||||
]
|
||||
: [],
|
||||
MenuItemButton(
|
||||
leadingIcon: const Icon(Icons.info_rounded),
|
||||
child: const Text('About'),
|
||||
|
|
|
@ -4,6 +4,7 @@ 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:routemaster/routemaster.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -17,45 +18,42 @@ class JoinRoomPage extends StatefulWidget {
|
|||
class _JoinRoomPageState extends State {
|
||||
List<Room> rooms = [];
|
||||
|
||||
void fetchData() async {
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
|
||||
doNetworkRequest(null,
|
||||
req: (user) => postWithCreadentials(
|
||||
path: 'listPublicRooms',
|
||||
credentials: user,
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
body: {}),
|
||||
onOK: (body) async {
|
||||
// parse rooms
|
||||
final list = resp.body['data'];
|
||||
final list = 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(
|
||||
final List<Room> blacklist = [];
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
path: 'listRooms',
|
||||
credentials: user,
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {});
|
||||
if (resp.res == Result.ok) {
|
||||
final List<Room> list = resp.body['data'].map<Room>((json) {
|
||||
body: {}),
|
||||
onOK: (body) {
|
||||
final List<Room> list = body['data'].map<Room>((json) {
|
||||
return Room.fromJSON(json);
|
||||
}).toList();
|
||||
for (Room r in list) {
|
||||
blacklist.add(r);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
// process the list of public rooms
|
||||
List<Room> builder = [];
|
||||
final List<Room> builder = [];
|
||||
processor:
|
||||
for (dynamic raw in list) {
|
||||
try {
|
||||
|
@ -65,7 +63,7 @@ class _JoinRoomPageState extends State {
|
|||
// only add room to list,
|
||||
// if not on blacklist
|
||||
for (Room r in blacklist) {
|
||||
if (room.serverTag == r.serverTag && room.id == r.id) {
|
||||
if (r.compareTo(room) == 0) {
|
||||
// server on white list
|
||||
// move to next iteration on outer for loop
|
||||
continue processor;
|
||||
|
@ -77,25 +75,15 @@ class _JoinRoomPageState extends State {
|
|||
// ignore room
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@ -133,7 +121,7 @@ class _JoinRoomPageState extends State {
|
|||
tooltip: "Refresh",
|
||||
onPressed: () {
|
||||
// fetch public rooms again
|
||||
fetchData();
|
||||
didChangeDependencies();
|
||||
},
|
||||
),
|
||||
MenuAnchor(
|
||||
|
@ -261,24 +249,13 @@ class _JoinRoomPageState extends State {
|
|||
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,
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (user) =>
|
||||
postWithCreadentials(
|
||||
credentials:
|
||||
user!,
|
||||
target: user
|
||||
.server,
|
||||
path:
|
||||
'joinPublicRoom',
|
||||
body: {
|
||||
|
@ -286,65 +263,13 @@ class _JoinRoomPageState extends State {
|
|||
room.id,
|
||||
'server': room
|
||||
.serverTag
|
||||
},
|
||||
credentials:
|
||||
user);
|
||||
if (resp.res ==
|
||||
Result.ok) {
|
||||
// successfully joined room
|
||||
}),
|
||||
onOK: (body) async {
|
||||
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);
|
||||
}
|
||||
});
|
||||
},
|
||||
))
|
||||
])
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:outbag_app/screens/room/pages/about.dart';
|
|||
import 'package:outbag_app/screens/room/pages/categories.dart';
|
||||
import 'package:outbag_app/screens/room/pages/products.dart';
|
||||
import 'package:outbag_app/screens/room/pages/list.dart';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
|
||||
class RoomPage extends StatefulWidget {
|
||||
|
@ -27,61 +28,52 @@ class _RoomPageState extends State<RoomPage> {
|
|||
|
||||
// fetch room information
|
||||
void fetchInfo() async {
|
||||
bool foundData = false;
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
|
||||
try {
|
||||
final diskRoom =
|
||||
await Room.fromDisk(serverTag: widget.server, id: widget.tag);
|
||||
foundData = true;
|
||||
setState(() {
|
||||
room = diskRoom;
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
// fetch additional data from web
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// probably not logged in
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
path: 'getRoomInfo',
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {'room': widget.tag, 'server': widget.server});
|
||||
if (resp.res == Result.ok) {
|
||||
final info = RoomInfo.fromJSON(resp.body['data']);
|
||||
final room = Room.fromJSON(resp.body['data']);
|
||||
credentials: user!,
|
||||
target: (user.server),
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
onOK: (body) async {
|
||||
final info = RoomInfo.fromJSON(body['data']);
|
||||
final room = Room.fromJSON(body['data']);
|
||||
|
||||
room.toDisk();
|
||||
foundData = true;
|
||||
|
||||
setState(() {
|
||||
this.info = info;
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if (!foundData) {
|
||||
return true;
|
||||
},
|
||||
onNetworkErr: () {
|
||||
// user offline
|
||||
if (room == null) {
|
||||
// no room data available
|
||||
// TODO: close room
|
||||
// or show snackbar
|
||||
// BUG: there is currently no way of implementing this,
|
||||
// because there is no context available here
|
||||
// NOTE: close room?
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onServerErr: (json) {
|
||||
// user no longer in room
|
||||
// TODO: close room
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// schedule info-get
|
||||
fetchInfo();
|
||||
|
||||
_ctr.addListener(() {
|
||||
setState(() {
|
||||
page = _ctr.page?.toInt() ?? _ctr.initialPage;
|
||||
|
@ -104,6 +96,13 @@ class _RoomPageState extends State<RoomPage> {
|
|||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
// schedule info-get
|
||||
fetchInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
|
@ -4,6 +4,8 @@ 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:outbag_app/tools/snackbar.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -182,41 +184,23 @@ class _NewRoomPageState extends State {
|
|||
|
||||
// ID should be at least three characters long
|
||||
if (_ctrID.text.length < 3) {
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: Text(_ctrID.text.isEmpty
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
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);
|
||||
: 'Room ID has to be at least three characters long',
|
||||
action: 'OK');
|
||||
|
||||
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();
|
||||
},
|
||||
),
|
||||
showSimpleSnackbar(
|
||||
scaffMgr,
|
||||
text: 'Please specify a room name',
|
||||
action: 'OK'
|
||||
);
|
||||
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(snackBar);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -236,8 +220,9 @@ class _NewRoomPageState extends State {
|
|||
description: _ctrDescription.text,
|
||||
icon: _ctrIcon,
|
||||
visibility: _ctrVis);
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (_) => postWithCreadentials(
|
||||
target: user.server,
|
||||
credentials: user,
|
||||
path: 'createRoom',
|
||||
|
@ -247,45 +232,17 @@ class _NewRoomPageState extends State {
|
|||
'description': room.description,
|
||||
'icon': room.icon?.type,
|
||||
'visibility': room.visibility?.type
|
||||
}
|
||||
);
|
||||
if (resp.res == Result.ok) {
|
||||
}),
|
||||
onOK: (_) async {
|
||||
// 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);
|
||||
}
|
||||
// we manually fetch the user data above
|
||||
// because we need the serverTag
|
||||
needUser: false);
|
||||
},
|
||||
label: const Text('Create'),
|
||||
icon: const Icon(Icons.add)),
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'package:outbag_app/screens/room/edit.dart';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
|
||||
class AboutRoomPage extends StatefulWidget {
|
||||
|
@ -109,71 +110,28 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
ScaffoldMessenger.of(context);
|
||||
final nav = Navigator.of(context);
|
||||
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// probably not logged in
|
||||
nav.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp =
|
||||
await postWithCreadentials(
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (user) =>
|
||||
postWithCreadentials(
|
||||
path: 'setVisibility',
|
||||
target: user.server,
|
||||
target: (user?.server)!,
|
||||
body: {
|
||||
'room': widget.room?.id,
|
||||
'server': (widget
|
||||
.room?.serverTag)!,
|
||||
'visibility': vset.first
|
||||
'room':
|
||||
widget.room?.id,
|
||||
'server': (widget.room
|
||||
?.serverTag)!,
|
||||
'visibility':
|
||||
vset.first
|
||||
},
|
||||
credentials: user);
|
||||
if (resp.res == Result.ok) {
|
||||
credentials: user!),
|
||||
onOK: (_) {
|
||||
Room r = widget.room!;
|
||||
r.visibility = vis;
|
||||
r.toDisk();
|
||||
} 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);
|
||||
}
|
||||
|
||||
after: () {
|
||||
nav.pop();
|
||||
});
|
||||
},
|
||||
child: const Text('Ok'),
|
||||
)
|
||||
|
@ -210,10 +168,9 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
// show edit room screen
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context)=>Dialog.fullscreen(
|
||||
builder: (context) => Dialog.fullscreen(
|
||||
child: EditRoomPage(widget.room!),
|
||||
)
|
||||
);
|
||||
));
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -284,28 +241,20 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
final nav = Navigator.of(ctx);
|
||||
final rmaster = Routemaster.of(ctx);
|
||||
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// probably not logged in
|
||||
nav.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp = await postWithCreadentials(
|
||||
doNetworkRequest(
|
||||
scaffMgr,
|
||||
req: (user)=>postWithCreadentials(
|
||||
path: ((widget.info?.isOwner)!)
|
||||
? 'deleteRoom'
|
||||
: 'leaveRoom',
|
||||
target: user.server,
|
||||
target: (user?.server)!,
|
||||
body: {
|
||||
'room': widget.room?.id,
|
||||
'server':
|
||||
(widget.room?.serverTag)!,
|
||||
},
|
||||
credentials: user);
|
||||
if (resp.res == Result.ok) {
|
||||
credentials: user!),
|
||||
onOK: (_) async {
|
||||
// try delete room from disk
|
||||
try {
|
||||
await widget.room?.removeDisk();
|
||||
|
@ -313,42 +262,12 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
|
||||
// go back home
|
||||
rmaster.replace('/');
|
||||
} 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);
|
||||
}
|
||||
|
||||
after: () {
|
||||
// close popup
|
||||
nav.pop();
|
||||
}
|
||||
);
|
||||
},
|
||||
child: Text(((widget.info?.isOwner)!)
|
||||
? 'Delete'
|
||||
|
|
100
lib/tools/fetch_wrapper.dart
Normal file
100
lib/tools/fetch_wrapper.dart
Normal file
|
@ -0,0 +1,100 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:outbag_app/backend/errors.dart';
|
||||
import 'package:outbag_app/backend/request.dart';
|
||||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'package:outbag_app/tools/snackbar.dart';
|
||||
|
||||
void doNetworkRequest(ScaffoldMessengerState? sm,
|
||||
{required Future<Response> Function(User?) req,
|
||||
Function(Map<String, dynamic>)? onOK,
|
||||
bool Function()? onNetworkErr,
|
||||
bool Function()? onUnknownErr,
|
||||
bool Function()? onUserErr,
|
||||
Function()? after,
|
||||
bool Function(Map<String, dynamic>)? onServerErr,
|
||||
bool needUser = true}) async {
|
||||
User? user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// no user data available
|
||||
if (needUser) {
|
||||
// show error & quit
|
||||
bool showBar = true;
|
||||
|
||||
if (onUserErr != null) {
|
||||
showBar = onUserErr();
|
||||
}
|
||||
|
||||
if (showBar && sm != null) {
|
||||
showSimpleSnackbar(sm, text: 'No user found', action: 'Authorize',
|
||||
onTap: () {
|
||||
try {
|
||||
// try to remove broken user
|
||||
User.removeDisk();
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
if (after != null) {
|
||||
after();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Response res;
|
||||
try {
|
||||
res = await req(user);
|
||||
} catch (_) {
|
||||
// network error
|
||||
bool showBar = true;
|
||||
|
||||
if (onNetworkErr != null) {
|
||||
showBar = onNetworkErr();
|
||||
}
|
||||
|
||||
if (showBar && sm != null) {
|
||||
showSimpleSnackbar(sm, text: 'Network Error', action: 'Dismiss');
|
||||
}
|
||||
|
||||
if (after != null) {
|
||||
after();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (res.res == Result.ok) {
|
||||
if (onOK != null) {
|
||||
await onOK(res.body);
|
||||
}
|
||||
} else {
|
||||
// server error
|
||||
bool showBar = true;
|
||||
|
||||
if (onServerErr != null) {
|
||||
showBar = onServerErr(res.body);
|
||||
}
|
||||
|
||||
if (showBar && sm != null) {
|
||||
showSimpleSnackbar(sm, text: errorAsString(res.body), action: 'OK');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
bool showBar = true;
|
||||
|
||||
print(e);
|
||||
|
||||
if (onUnknownErr != null) {
|
||||
showBar = onUnknownErr();
|
||||
}
|
||||
|
||||
if (showBar && sm != null) {
|
||||
showSimpleSnackbar(sm, text: 'Unknown Error', action: 'OK');
|
||||
}
|
||||
}
|
||||
|
||||
if (after != null) {
|
||||
after();
|
||||
}
|
||||
}
|
21
lib/tools/snackbar.dart
Normal file
21
lib/tools/snackbar.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
void showSimpleSnackbar(ScaffoldMessengerState scaffMgr,
|
||||
{required String text, required String action, Function? onTap}) {
|
||||
final snackBar = SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
content: Text(text),
|
||||
action: SnackBarAction(
|
||||
label: action,
|
||||
onPressed: () {
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
if (onTap != null) {
|
||||
onTap();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
scaffMgr.hideCurrentSnackBar();
|
||||
scaffMgr.showSnackBar(snackBar);
|
||||
}
|
Loading…
Reference in a new issue