Moved User into application wide conntext.
Every page (after login) has access to the User object via context.read/watch<User>(). This reduces localstore and asnyc operations, as the screens do not have to load the user every time. Additionally this prevents anyone from using the without a user object.
This commit is contained in:
parent
42b6da7461
commit
bb9a8621a0
13 changed files with 471 additions and 527 deletions
172
lib/main.dart
172
lib/main.dart
|
@ -16,47 +16,6 @@ import './screens/auth.dart';
|
|||
import './backend/request.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
|
||||
// routes when user is not logged in
|
||||
final routesUnauthorized = RouteMap(routes: {
|
||||
'/welcome/': (_) => const MaterialPage(child: WelcomePage()),
|
||||
'/signup': (_) => const MaterialPage(child: AuthPage(mode: Mode.signup)),
|
||||
'/signupOTA': (_) =>
|
||||
const MaterialPage(child: AuthPage(mode: Mode.signupOTA)),
|
||||
'/signin': (_) => const MaterialPage(child: AuthPage(mode: Mode.signin)),
|
||||
}, onUnknownRoute: (_) => const MaterialPage(child: WelcomePage()));
|
||||
|
||||
// routes when user is logged in
|
||||
final routesLoggedIn = RouteMap(routes: {
|
||||
'/': (_) => const MaterialPage(child: HomePage()),
|
||||
'/settings': (_) => const MaterialPage(child: SettingsPage()),
|
||||
'/add-room/new': (_) => const MaterialPage(child: NewRoomPage()),
|
||||
'/add-room': (_) => const MaterialPage(child: JoinRoomPage()),
|
||||
'/r/:server/:tag/': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: RoomPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/edit': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: EditRoomPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/members': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: ManageRoomMembersPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/permissions': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: EditRoomPermissionSetPage(server, tag));
|
||||
},
|
||||
}, onUnknownRoute: (_) => const Redirect('/'));
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(const OutbagApp());
|
||||
|
@ -73,8 +32,8 @@ class _OutbagAppState extends State {
|
|||
// assume user is logged in
|
||||
// unless not userdata is found
|
||||
// or the userdata turns out to be wrong
|
||||
bool isAuthorized = true;
|
||||
AccountMeta? info;
|
||||
User? user;
|
||||
|
||||
AppTheme theme = AppTheme.auto;
|
||||
|
||||
|
@ -85,11 +44,17 @@ class _OutbagAppState extends State {
|
|||
// wait for user to be authorized
|
||||
User.listen((_) async {
|
||||
try {
|
||||
await User.fromDisk();
|
||||
final user = await User.fromDisk();
|
||||
setState(() {
|
||||
isAuthorized = true;
|
||||
this.user = user;
|
||||
});
|
||||
} catch (_) {}
|
||||
fetchInfo();
|
||||
} catch (_) {
|
||||
// no userdata found
|
||||
setState(() {
|
||||
user = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
AppTheme.listen((_) async {
|
||||
|
@ -106,15 +71,30 @@ class _OutbagAppState extends State {
|
|||
});
|
||||
})();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo());
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
try {
|
||||
final user = await User.fromDisk();
|
||||
setState(() {
|
||||
this.user = user;
|
||||
});
|
||||
fetchInfo();
|
||||
} catch (_) {
|
||||
// user unavailable
|
||||
// invalid credentials
|
||||
// log out
|
||||
setState(() {
|
||||
user = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void fetchInfo() {
|
||||
void fetchInfo() async {
|
||||
// try to obtain user account information
|
||||
// with existing details
|
||||
// NOTE: also functions as a way to verify ther data
|
||||
doNetworkRequest(null,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
target: (user?.server)!,
|
||||
path: 'getMyAccount',
|
||||
credentials: user!,
|
||||
|
@ -122,16 +102,16 @@ class _OutbagAppState extends State {
|
|||
onOK: (body) {
|
||||
final info = AccountMeta.fromJSON(body['data']);
|
||||
setState(() {
|
||||
isAuthorized = true;
|
||||
this.info = info;
|
||||
});
|
||||
},
|
||||
onServerErr: (body) {
|
||||
onServerErr: (_) {
|
||||
// credentials are wrong
|
||||
// log out
|
||||
|
||||
setState(() {
|
||||
isAuthorized = false;
|
||||
info = null;
|
||||
user = null;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
@ -140,20 +120,7 @@ class _OutbagAppState extends State {
|
|||
// approve login,
|
||||
// until user goes back offline
|
||||
// NOTE TODO: check user data once online
|
||||
setState(() {
|
||||
isAuthorized = true;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
onUserErr: () {
|
||||
// invalid credentials
|
||||
// log out
|
||||
setState(() {
|
||||
isAuthorized = false;
|
||||
});
|
||||
// do not show snackbar,
|
||||
// because the user was probably never logged in
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -164,7 +131,12 @@ class _OutbagAppState extends State {
|
|||
Provider<AccountMeta?>.value(
|
||||
value: info,
|
||||
),
|
||||
Provider<AppTheme>.value(value: theme)
|
||||
Provider<AppTheme>.value(value: theme),
|
||||
// conditional user provider
|
||||
// used to only provide a user outside of the login area
|
||||
...(user!=null)?[
|
||||
Provider<User>.value(value:user!)
|
||||
]:[]
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
title: "Outbag",
|
||||
|
@ -172,8 +144,74 @@ class _OutbagAppState extends State {
|
|||
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
||||
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
||||
routerDelegate: RoutemasterDelegate(
|
||||
routesBuilder: (context) =>
|
||||
isAuthorized ? routesLoggedIn : routesUnauthorized),
|
||||
routesBuilder: (context) => RouteMap(
|
||||
routes: {
|
||||
// pre authentification routes
|
||||
...(user == null)
|
||||
? {
|
||||
'/welcome/': (_) =>
|
||||
const MaterialPage(child: WelcomePage()),
|
||||
'/signup': (_) => const MaterialPage(
|
||||
child: AuthPage(mode: Mode.signup)),
|
||||
'/signupOTA': (_) => const MaterialPage(
|
||||
child: AuthPage(mode: Mode.signupOTA)),
|
||||
'/signin': (_) => const MaterialPage(
|
||||
child: AuthPage(mode: Mode.signin)),
|
||||
}
|
||||
: {},
|
||||
|
||||
// routes that need the user
|
||||
// to exis
|
||||
...(user!=null)?{
|
||||
'/': (_) => const MaterialPage(child: HomePage()),
|
||||
|
||||
// user-space settings
|
||||
'/settings': (_) =>
|
||||
const MaterialPage(child: SettingsPage()),
|
||||
|
||||
// server dashboard
|
||||
// TODO: add permission check
|
||||
// so routes are only available
|
||||
// if user is allowed to view dashboard
|
||||
...{
|
||||
|
||||
},
|
||||
// room related settings
|
||||
'/add-room/new': (_) =>
|
||||
const MaterialPage(child: NewRoomPage()),
|
||||
'/add-room': (_) =>
|
||||
const MaterialPage(child: JoinRoomPage()),
|
||||
'/r/:server/:tag/': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: RoomPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/edit': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(child: EditRoomPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/members': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(
|
||||
child: ManageRoomMembersPage(server, tag));
|
||||
},
|
||||
'/r/:server/:tag/permissions': (info) {
|
||||
final server = info.pathParameters['server'] ?? "";
|
||||
final tag = info.pathParameters['tag'] ?? "";
|
||||
|
||||
return MaterialPage(
|
||||
child: EditRoomPermissionSetPage(server, tag));
|
||||
},
|
||||
}:{}
|
||||
},
|
||||
onUnknownRoute: (_) => (user == null)
|
||||
? const MaterialPage(child: WelcomePage())
|
||||
: const Redirect('/'))),
|
||||
routeInformationParser: const RoutemasterParser(),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ 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 'package:crypto/crypto.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
enum Mode {
|
||||
signin,
|
||||
|
@ -225,7 +223,7 @@ class _AuthPageState extends State<AuthPage> {
|
|||
// hash password
|
||||
final password = hashPassword(_ctrPassword.text);
|
||||
|
||||
doNetworkRequest(scaffMgr, needUser: false, req: (_) {
|
||||
doNetworkRequest(scaffMgr, req: () {
|
||||
if (widget.mode == Mode.signin) {
|
||||
return postUnauthorized(
|
||||
target: server,
|
||||
|
|
|
@ -36,30 +36,28 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
void fetchList() async {
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
// load cached rooms
|
||||
try {
|
||||
final newRooms = await Room.listRooms();
|
||||
setState(() {
|
||||
rooms = newRooms;
|
||||
rooms = newRooms;
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
path: 'listRooms',
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {}),
|
||||
doNetworkRequest(
|
||||
sm,
|
||||
req: () => postWithCreadentials(
|
||||
path: 'listRooms', credentials: user, target: user.server, body: {}),
|
||||
onOK: (body) async {
|
||||
final List<Room> list = body['data'].map<Room>((json) {
|
||||
return Room.fromJSON(json);
|
||||
return Room.fromJSON(json);
|
||||
}).toList();
|
||||
for (Room r in list) {
|
||||
await r.toDisk();
|
||||
}
|
||||
},
|
||||
onUserErr: ()=>false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ 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 'package:routemaster/routemaster.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -44,13 +45,14 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
|||
void fetchInfo() {
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final rmaster = Routemaster.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'getRoomInfo',
|
||||
credentials: user!,
|
||||
target: (user.server),
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
onOK: (body) async {
|
||||
final room = Room.fromJSON(body['data']);
|
||||
|
@ -139,7 +141,8 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
|||
tooltip: "Go back",
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(child: Center(
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: ConstrainedBox(
|
||||
|
@ -159,8 +162,8 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title:
|
||||
const Text('Choose a room Icon'),
|
||||
title: const Text(
|
||||
'Choose a room Icon'),
|
||||
actions: const [],
|
||||
content: SizedBox(
|
||||
width: smallest * 0.3 * 3,
|
||||
|
@ -174,15 +177,19 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
|||
icon: SvgPicture
|
||||
.asset(
|
||||
icon.img,
|
||||
width: smallest *
|
||||
width:
|
||||
smallest *
|
||||
0.3,
|
||||
height: smallest *
|
||||
height:
|
||||
smallest *
|
||||
0.3,
|
||||
),
|
||||
tooltip: icon.text,
|
||||
tooltip:
|
||||
icon.text,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_ctrIcon = icon;
|
||||
_ctrIcon =
|
||||
icon;
|
||||
});
|
||||
Navigator.of(
|
||||
context)
|
||||
|
@ -239,11 +246,7 @@ class _EditRoomPageState extends State<EditRoomPage> {
|
|||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
))))),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () async {
|
||||
final scaffMgr = ScaffoldMessenger.of(context);
|
||||
|
|
|
@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_svg/flutter_svg.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 'package:routemaster/routemaster.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -18,11 +20,12 @@ class _JoinRoomPageState extends State {
|
|||
|
||||
void fetchRooms() {
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(null,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'listPublicRooms',
|
||||
credentials: user!,
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {}),
|
||||
onOK: (body) async {
|
||||
|
@ -33,9 +36,9 @@ class _JoinRoomPageState extends State {
|
|||
// use an empty blacklist when request is not successful
|
||||
final List<Room> blacklist = [];
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'listRooms',
|
||||
credentials: user!,
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {}),
|
||||
onOK: (body) {
|
||||
|
@ -245,12 +248,14 @@ class _JoinRoomPageState extends State {
|
|||
context);
|
||||
final nav =
|
||||
Navigator.of(context);
|
||||
final user =
|
||||
context.read<User>();
|
||||
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (user) =>
|
||||
req: () =>
|
||||
postWithCreadentials(
|
||||
credentials:
|
||||
user!,
|
||||
user,
|
||||
target: user
|
||||
.server,
|
||||
path:
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:flutter/material.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/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:provider/provider.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
|
||||
class RoomPage extends StatefulWidget {
|
||||
|
@ -28,6 +30,7 @@ class _RoomPageState extends State<RoomPage> {
|
|||
// fetch room information
|
||||
void fetchInfo() async {
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
try {
|
||||
final diskRoom =
|
||||
|
@ -38,10 +41,10 @@ class _RoomPageState extends State<RoomPage> {
|
|||
} catch (_) {}
|
||||
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'getRoomInfo',
|
||||
credentials: user!,
|
||||
target: (user.server),
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
onOK: (body) async {
|
||||
final info = RoomInfo.fromJSON(body['data']);
|
||||
|
|
|
@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:outbag_app/backend/permissions.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 'package:routemaster/routemaster.dart';
|
||||
|
||||
class ManageRoomMembersPage extends StatefulWidget {
|
||||
|
@ -22,13 +24,14 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
void fetchUserInfo() {
|
||||
final rmaster = Routemaster.of(context);
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'getRoomInfo',
|
||||
credentials: user!,
|
||||
target: (user.server),
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
onAnyErr: () {
|
||||
// user should not be here
|
||||
|
@ -49,10 +52,11 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
void fetchMembers() {
|
||||
final rmaster = Routemaster.of(context);
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
credentials: user!,
|
||||
req: () => postWithCreadentials(
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
path: 'getRoomMembers',
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
|
@ -99,12 +103,12 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
tooltip: "Go back",
|
||||
),
|
||||
//actions: [
|
||||
// // NOTE: Maybe add a search icon
|
||||
// // and general search functionality here
|
||||
//],
|
||||
),
|
||||
//actions: [
|
||||
// // NOTE: Maybe add a search icon
|
||||
// // and general search functionality here
|
||||
//],
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = list[index];
|
||||
|
@ -140,7 +144,9 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
title: Text(item.humanReadableName),
|
||||
subtitle: Text(role),
|
||||
leading: const Icon(Icons.person),
|
||||
onTap: !enable?null:() {
|
||||
onTap: !enable
|
||||
? null
|
||||
: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => BottomSheet(
|
||||
|
@ -158,7 +164,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
...((info?.isAdmin)! ||
|
||||
(info?.isOwner)! ||
|
||||
((info?.permissions)! &
|
||||
RoomPermission.changeAdmin !=
|
||||
RoomPermission
|
||||
.changeAdmin !=
|
||||
0))
|
||||
? [
|
||||
ListTile(
|
||||
|
@ -175,8 +182,10 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
icon: const Icon(Icons
|
||||
(context) =>
|
||||
AlertDialog(
|
||||
icon: const Icon(
|
||||
Icons
|
||||
.supervisor_account),
|
||||
title: Text(item
|
||||
.isAdmin
|
||||
|
@ -188,12 +197,12 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
: 'Do you really want to make ${item.humanReadableName} admin?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onPressed:
|
||||
() {
|
||||
// close popup
|
||||
// NOTE: cancel only closes the dialog
|
||||
// whilst OK closes both
|
||||
Navigator.of(
|
||||
context)
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
child: const Text(
|
||||
|
@ -204,20 +213,16 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
() async {
|
||||
// send request
|
||||
final scaffMgr =
|
||||
ScaffoldMessenger.of(
|
||||
context);
|
||||
ScaffoldMessenger.of(context);
|
||||
final nav =
|
||||
Navigator.of(
|
||||
context);
|
||||
Navigator.of(context);
|
||||
final user =
|
||||
context.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
scaffMgr,
|
||||
req: (user) =>
|
||||
postWithCreadentials(
|
||||
path: 'setAdminStatus',
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {
|
||||
req: () =>
|
||||
postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: {
|
||||
'room': widget.tag,
|
||||
'roomServer': widget.server,
|
||||
'server': item.serverTag,
|
||||
|
@ -233,8 +238,7 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
nav.pop();
|
||||
});
|
||||
},
|
||||
child:
|
||||
const Text(
|
||||
child: const Text(
|
||||
'OK'),
|
||||
)
|
||||
],
|
||||
|
@ -251,9 +255,10 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
0))
|
||||
? [
|
||||
ListTile(
|
||||
leading:
|
||||
const Icon(Icons.person_remove),
|
||||
title: const Text('Kick User'),
|
||||
leading: const Icon(
|
||||
Icons.person_remove),
|
||||
title:
|
||||
const Text('Kick User'),
|
||||
subtitle: const Text(
|
||||
"Temporarrily remove user from server (they'll be able to join the room again)"),
|
||||
onTap: () {
|
||||
|
@ -261,8 +266,10 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
icon: const Icon(Icons
|
||||
(context) =>
|
||||
AlertDialog(
|
||||
icon: const Icon(
|
||||
Icons
|
||||
.person_remove),
|
||||
title: const Text(
|
||||
'Kick User'),
|
||||
|
@ -270,13 +277,13 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
'Do you really want to kick ${item.humanReadableName}?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onPressed:
|
||||
() {
|
||||
// close popup
|
||||
// NOTE: cancel only closes the dialog
|
||||
// whilst OK closes both
|
||||
|
||||
Navigator.of(
|
||||
context)
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
child: const Text(
|
||||
|
@ -287,18 +294,18 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
|
|||
() async {
|
||||
// send request
|
||||
final scaffMgr =
|
||||
ScaffoldMessenger.of(
|
||||
context);
|
||||
ScaffoldMessenger.of(context);
|
||||
final nav =
|
||||
Navigator.of(
|
||||
context);
|
||||
Navigator.of(context);
|
||||
final user =
|
||||
context.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
scaffMgr,
|
||||
req: (user) =>
|
||||
req: () =>
|
||||
postWithCreadentials(
|
||||
path: 'kickMember',
|
||||
credentials: user!,
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {
|
||||
'room': widget.tag,
|
||||
|
|
|
@ -5,6 +5,7 @@ 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:provider/provider.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -77,8 +78,8 @@ class _NewRoomPageState extends State {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title:
|
||||
const Text('Choose a room Icon'),
|
||||
title: const Text(
|
||||
'Choose a room Icon'),
|
||||
actions: const [],
|
||||
content: SizedBox(
|
||||
width: smallest * 0.3 * 3,
|
||||
|
@ -92,15 +93,19 @@ class _NewRoomPageState extends State {
|
|||
icon: SvgPicture
|
||||
.asset(
|
||||
icon.img,
|
||||
width: smallest *
|
||||
width:
|
||||
smallest *
|
||||
0.3,
|
||||
height: smallest *
|
||||
height:
|
||||
smallest *
|
||||
0.3,
|
||||
),
|
||||
tooltip: icon.text,
|
||||
tooltip:
|
||||
icon.text,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_ctrIcon = icon;
|
||||
_ctrIcon =
|
||||
icon;
|
||||
});
|
||||
Navigator.of(
|
||||
context)
|
||||
|
@ -177,11 +182,7 @@ class _NewRoomPageState extends State {
|
|||
selectedIcon: Icon(_ctrVis.icon),
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
))))),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () async {
|
||||
final scaffMgr = ScaffoldMessenger.of(context);
|
||||
|
@ -200,24 +201,13 @@ class _NewRoomPageState extends State {
|
|||
|
||||
// name may not be empty
|
||||
if (_ctrName.text.isEmpty) {
|
||||
showSimpleSnackbar(
|
||||
scaffMgr,
|
||||
text: 'Please specify a room name',
|
||||
action: 'OK'
|
||||
);
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
text: 'Please specify a room name', action: 'OK');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
User user;
|
||||
try {
|
||||
user = await User.fromDisk();
|
||||
} catch (_) {
|
||||
// user data invalid
|
||||
// shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
final user = context.read<User>();
|
||||
final room = Room(
|
||||
id: _ctrID.text,
|
||||
serverTag: user.server.tag,
|
||||
|
@ -227,7 +217,7 @@ class _NewRoomPageState extends State {
|
|||
visibility: _ctrVis);
|
||||
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (_) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
target: user.server,
|
||||
credentials: user,
|
||||
path: 'createRoom',
|
||||
|
@ -244,10 +234,7 @@ class _NewRoomPageState extends State {
|
|||
await room.toDisk();
|
||||
// move to home page
|
||||
rmaster.replace('/');
|
||||
},
|
||||
// we manually fetch the user data above
|
||||
// because we need the serverTag
|
||||
needUser: false);
|
||||
});
|
||||
},
|
||||
label: const Text('Create'),
|
||||
icon: const Icon(Icons.add)),
|
||||
|
|
|
@ -3,8 +3,10 @@ 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/room.dart';
|
||||
import 'package:outbag_app/backend/user.dart';
|
||||
import 'dart:math';
|
||||
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:routemaster/routemaster.dart';
|
||||
|
||||
class AboutRoomPage extends StatefulWidget {
|
||||
|
@ -29,7 +31,7 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
double smallest = min(width, height);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child:Center(
|
||||
child: Center(
|
||||
child: Column(children: [
|
||||
// room meta display
|
||||
...(widget.room != null)
|
||||
|
@ -51,7 +53,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
'${widget.room?.id}@${widget.room?.serverTag}',
|
||||
style: textTheme.bodySmall,
|
||||
),
|
||||
Text(widget.room?.description ?? '',
|
||||
Text(
|
||||
widget.room?.description ?? '',
|
||||
style: textTheme.bodyMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -103,21 +106,19 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
final scaffMgr =
|
||||
ScaffoldMessenger.of(context);
|
||||
final nav = Navigator.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: (user) =>
|
||||
postWithCreadentials(
|
||||
req: () => 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!),
|
||||
credentials: user),
|
||||
onOK: (_) {
|
||||
Room r = widget.room!;
|
||||
r.visibility = vis;
|
||||
|
@ -193,7 +194,7 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
onTap: () {
|
||||
// show checkbox screen
|
||||
Routemaster.of(context).push(
|
||||
'/r/${widget.room?.serverTag}/${widget.room?.id}/permissions');
|
||||
'/r/${widget.room?.serverTag}/${widget.room?.id}/permissions');
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -262,20 +263,20 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
ScaffoldMessenger.of(ctx);
|
||||
final nav = Navigator.of(ctx);
|
||||
final rmaster = Routemaster.of(ctx);
|
||||
final user = ctx.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
scaffMgr,
|
||||
req: (user)=>postWithCreadentials(
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: () => postWithCreadentials(
|
||||
path: ((widget.info?.isOwner)!)
|
||||
? 'deleteRoom'
|
||||
: 'leaveRoom',
|
||||
target: (user?.server)!,
|
||||
target: user.server,
|
||||
body: {
|
||||
'room': widget.room?.id,
|
||||
'server':
|
||||
(widget.room?.serverTag)!,
|
||||
},
|
||||
credentials: user!),
|
||||
credentials: user),
|
||||
onOK: (_) async {
|
||||
// try delete room from disk
|
||||
try {
|
||||
|
@ -288,8 +289,7 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
after: () {
|
||||
// close popup
|
||||
nav.pop();
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Text(((widget.info?.isOwner)!)
|
||||
? 'Delete'
|
||||
|
@ -301,9 +301,6 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
|||
))
|
||||
]
|
||||
: [],
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:outbag_app/backend/permissions.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 'package:routemaster/routemaster.dart';
|
||||
|
||||
class EditRoomPermissionSetPage extends StatefulWidget {
|
||||
|
@ -24,13 +26,14 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
|||
void fetchInfo() {
|
||||
final rmaster = Routemaster.of(context);
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
doNetworkRequest(
|
||||
sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'getRoomInfo',
|
||||
credentials: user!,
|
||||
target: (user.server),
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {'room': widget.tag, 'server': widget.server}),
|
||||
onAnyErr: () {
|
||||
// user should not be here
|
||||
|
@ -94,11 +97,13 @@ class _EditRoomPermissionSetPageState extends State<EditRoomPermissionSetPage> {
|
|||
onPressed: () {
|
||||
final rmaster = Routemaster.of(context);
|
||||
final sm = ScaffoldMessenger.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
// update permissions
|
||||
doNetworkRequest(sm,
|
||||
req: (user) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'setRoomRight',
|
||||
credentials: user!,
|
||||
credentials: user,
|
||||
target: user.server,
|
||||
body: {
|
||||
'room': widget.tag,
|
||||
|
|
|
@ -4,7 +4,7 @@ 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 'package:provider/provider.dart';
|
||||
|
||||
class ChangePasswordDialog extends StatefulWidget {
|
||||
const ChangePasswordDialog({super.key});
|
||||
|
@ -18,39 +18,6 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
|||
final TextEditingController _ctrNewPassword = TextEditingController();
|
||||
final TextEditingController _ctrNewPasswordRepeat = TextEditingController();
|
||||
|
||||
User? user;
|
||||
|
||||
void loadUser() async {
|
||||
final rmaster = Routemaster.of(context);
|
||||
try {
|
||||
final u = await User.fromDisk();
|
||||
setState(() {
|
||||
user = u;
|
||||
});
|
||||
} catch (_) {
|
||||
// logout user
|
||||
await User.removeDisk();
|
||||
// move to welcome screen
|
||||
rmaster.replace('/');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
User.listen((data) async {
|
||||
try {
|
||||
final u = await User.fromDisk();
|
||||
setState(() {
|
||||
user = u;
|
||||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => loadUser());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
|
@ -120,6 +87,7 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
|||
onPressed: () async {
|
||||
final scaffMgr = ScaffoldMessenger.of(context);
|
||||
final nav = Navigator.of(context);
|
||||
final user = context.read<User>();
|
||||
|
||||
// validate password
|
||||
if (_ctrNewPassword.text.length < 6) {
|
||||
|
@ -139,7 +107,7 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
|||
_ctrNewPasswordRepeat.clear();
|
||||
return;
|
||||
}
|
||||
if (hashPassword(_ctrOldPassword.text) != user?.password) {
|
||||
if (hashPassword(_ctrOldPassword.text) != user.password) {
|
||||
// current password wrong
|
||||
showSimpleSnackbar(scaffMgr,
|
||||
text: 'Old password is wrong', action: 'Dismiss');
|
||||
|
@ -152,18 +120,17 @@ class _ChangePasswordDialogState extends State<ChangePasswordDialog> {
|
|||
|
||||
// send request
|
||||
doNetworkRequest(scaffMgr,
|
||||
needUser: false,
|
||||
req: (_) => postWithCreadentials(
|
||||
req: () => postWithCreadentials(
|
||||
path: 'changePassword',
|
||||
target: (user?.server)!,
|
||||
target: user.server,
|
||||
body: {'accountKey': password},
|
||||
credentials: user!),
|
||||
credentials: user),
|
||||
onOK: (_) async {
|
||||
// update local user struct
|
||||
final updatedUser = User(
|
||||
username: (user?.username)!,
|
||||
username: user.username,
|
||||
password: password,
|
||||
server: (user?.server)!);
|
||||
server: user.server);
|
||||
await updatedUser.toDisk();
|
||||
},
|
||||
after: () {
|
||||
|
|
|
@ -15,247 +15,160 @@ class SettingsPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _SettingsPageState extends State<SettingsPage> {
|
||||
User? user;
|
||||
AccountMeta? meta;
|
||||
|
||||
void fetchMeta() {
|
||||
doNetworkRequest(ScaffoldMessenger.of(context), req: (user) {
|
||||
setState(() {
|
||||
this.user = user;
|
||||
});
|
||||
|
||||
return postWithCreadentials(
|
||||
path: 'getMyAccount',
|
||||
credentials: user!,
|
||||
target: user.server,
|
||||
body: {});
|
||||
}, onOK: (body) {
|
||||
final meta = AccountMeta.fromJSON(body['data']);
|
||||
setState(() {
|
||||
this.meta = meta;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => fetchMeta());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context)
|
||||
.textTheme
|
||||
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||
.textTheme
|
||||
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
|
||||
|
||||
final user = context.watch<User>();
|
||||
final meta = context.watch<AccountMeta?>();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
// go back
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
tooltip: "Go back",
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
// go back
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
tooltip: "Go back",
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Column(children: [
|
||||
// uswer information widget
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: Card(
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Column(children: [
|
||||
// uswer information widget
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text('${user?.humanReadable}',
|
||||
style: textTheme.titleLarge)),
|
||||
ListTile(
|
||||
title: const Text('Room count limit:'),
|
||||
subtitle: const Text(
|
||||
'How many rooms you are allowed to own'),
|
||||
trailing: Text('${meta?.maxRoomCount ?? ""}'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Room size limit:'),
|
||||
subtitle: const Text(
|
||||
'How many items/products/categories each room may contain'),
|
||||
trailing: Text('${meta?.maxRoomSize ?? ""}'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Room member limit:'),
|
||||
subtitle: const Text(
|
||||
'How many members each of your rooms may have'),
|
||||
trailing:
|
||||
Text('${meta?.maxRoomMemberCount ?? ""}')),
|
||||
ListTile(
|
||||
title: const Text('Discoverable'),
|
||||
subtitle: const Text(
|
||||
'Determines if your account can be discovered by users from other servers'),
|
||||
trailing: Checkbox(
|
||||
tristate: true,
|
||||
value: meta?.discoverable,
|
||||
onChanged: (_) {},
|
||||
))
|
||||
],
|
||||
)))),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(user.humanReadable,
|
||||
style: textTheme.titleLarge)),
|
||||
ListTile(
|
||||
title: const Text('Room count limit:'),
|
||||
subtitle: const Text(
|
||||
'How many rooms you are allowed to own'),
|
||||
trailing: Text('${meta?.maxRoomCount ?? ""}'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Room size limit:'),
|
||||
subtitle: const Text(
|
||||
'How many items/products/categories each room may contain'),
|
||||
trailing: Text('${meta?.maxRoomSize ?? ""}'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Room member limit:'),
|
||||
subtitle: const Text(
|
||||
'How many members each of your rooms may have'),
|
||||
trailing:
|
||||
Text('${meta?.maxRoomMemberCount ?? ""}')),
|
||||
ListTile(
|
||||
title: const Text('Discoverable'),
|
||||
subtitle: const Text(
|
||||
'Determines if your account can be discovered by users from other servers'),
|
||||
trailing: Checkbox(
|
||||
tristate: true,
|
||||
value: meta?.discoverable,
|
||||
onChanged: (_) {},
|
||||
))
|
||||
],
|
||||
)))),
|
||||
|
||||
// change theme button
|
||||
ListTile(
|
||||
title: const Text('Change Theme'),
|
||||
subtitle: const Text(
|
||||
'You can change between a light theme, a dark theme and automatic theme selection'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Change Theme'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text('Choose your preferred theme'),
|
||||
),
|
||||
SegmentedButton<AppTheme>(
|
||||
selected: {context.watch<AppTheme>()},
|
||||
selectedIcon: Icon(context.watch<AppTheme>().icon),
|
||||
showSelectedIcon: true,
|
||||
multiSelectionEnabled: false,
|
||||
emptySelectionAllowed: false,
|
||||
segments: AppTheme.list().map((item) {
|
||||
return ButtonSegment<AppTheme>(
|
||||
value: item,
|
||||
icon: Icon(item.icon),
|
||||
label: Text(item.name));
|
||||
}).toList(),
|
||||
onSelectionChanged: (item) async {
|
||||
try {
|
||||
await item.first.toDisk();
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
actions: [
|
||||
FilledButton(
|
||||
child: const Text('Close'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
),
|
||||
// change theme button
|
||||
ListTile(
|
||||
title: const Text('Change Theme'),
|
||||
subtitle: const Text(
|
||||
'You can change between a light theme, a dark theme and automatic theme selection'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Change Theme'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text('Choose your preferred theme'),
|
||||
),
|
||||
SegmentedButton<AppTheme>(
|
||||
selected: {context.watch<AppTheme>()},
|
||||
selectedIcon: Icon(context.watch<AppTheme>().icon),
|
||||
showSelectedIcon: true,
|
||||
multiSelectionEnabled: false,
|
||||
emptySelectionAllowed: false,
|
||||
segments: AppTheme.list().map((item) {
|
||||
return ButtonSegment<AppTheme>(
|
||||
value: item,
|
||||
icon: Icon(item.icon),
|
||||
label: Text(item.name));
|
||||
}).toList(),
|
||||
onSelectionChanged: (item) async {
|
||||
try {
|
||||
await item.first.toDisk();
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
])),
|
||||
actions: [
|
||||
FilledButton(
|
||||
child: const Text('Close'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
),
|
||||
|
||||
// change password button
|
||||
ListTile(
|
||||
title: const Text('Change password'),
|
||||
subtitle: const Text('Choose a new password for your account'),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const ChangePasswordDialog());
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
// change password button
|
||||
ListTile(
|
||||
title: const Text('Change password'),
|
||||
subtitle: const Text('Choose a new password for your account'),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const ChangePasswordDialog());
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
|
||||
// export account to json
|
||||
ListTile(
|
||||
title: const Text('Export account'),
|
||||
subtitle: const Text('Export account data'),
|
||||
onTap: () {
|
||||
// TODO: show confirm dialog
|
||||
// NOTE: json dump the localstore
|
||||
// including users and rooms
|
||||
// NOTE: feature not confirmed
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
// export account to json
|
||||
ListTile(
|
||||
title: const Text('Export account'),
|
||||
subtitle: const Text('Export account data'),
|
||||
onTap: () {
|
||||
// TODO: show confirm dialog
|
||||
// NOTE: json dump the localstore
|
||||
// including users and rooms
|
||||
// NOTE: feature not confirmed
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
|
||||
// delete account button
|
||||
ListTile(
|
||||
title: const Text('Delete account'),
|
||||
subtitle: const Text('Delete your account from your homeserver'),
|
||||
onTap: () {
|
||||
// show confirm dialog
|
||||
// NOTE: same as logout
|
||||
// and performs a network request
|
||||
// but deletes account beforehand
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Delete account'),
|
||||
content: const Text(
|
||||
'Do you really want to delete your account?'),
|
||||
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: 'deleteAccount',
|
||||
target: (user?.server)!,
|
||||
body: {},
|
||||
credentials: user!),
|
||||
onOK: (_) async {
|
||||
// delete everything
|
||||
// delete user data (meta)
|
||||
try {
|
||||
await User.removeDisk();
|
||||
} catch (_) {}
|
||||
// TODO: delete all rooms
|
||||
|
||||
// go back home
|
||||
rmaster.replace('/');
|
||||
},
|
||||
after: () {
|
||||
// close popup
|
||||
nav.pop();
|
||||
});
|
||||
},
|
||||
child: const Text('Delete Account'),
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
|
||||
// logout button
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: FilledButton.tonal(
|
||||
child: const Text('Log out'),
|
||||
onPressed: () {
|
||||
// show confirm dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Log out'),
|
||||
content:
|
||||
const Text('Do you really want to log out?'),
|
||||
// delete account button
|
||||
ListTile(
|
||||
title: const Text('Delete account'),
|
||||
subtitle: const Text('Delete your account from your homeserver'),
|
||||
onTap: () {
|
||||
// show confirm dialog
|
||||
// NOTE: same as logout
|
||||
// and performs a network request
|
||||
// but deletes account beforehand
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Delete account'),
|
||||
content: const Text(
|
||||
'Do you really want to delete your account?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
|
@ -267,24 +180,82 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
FilledButton(
|
||||
onPressed: () async {
|
||||
// send request
|
||||
final scaffMgr = ScaffoldMessenger.of(ctx);
|
||||
final nav = Navigator.of(ctx);
|
||||
final rmaster = Routemaster.of(ctx);
|
||||
|
||||
// delete everything
|
||||
// delete user data (meta)
|
||||
try {
|
||||
await User.removeDisk();
|
||||
} catch (_) {}
|
||||
// TODO: delete all rooms
|
||||
doNetworkRequest(scaffMgr,
|
||||
req: () => postWithCreadentials(
|
||||
path: 'deleteAccount',
|
||||
target: user.server,
|
||||
body: {},
|
||||
credentials: user),
|
||||
onOK: (_) async {
|
||||
// delete everything
|
||||
// delete user data (meta)
|
||||
try {
|
||||
await User.removeDisk();
|
||||
} catch (_) {}
|
||||
// TODO: delete all rooms
|
||||
|
||||
// go back home
|
||||
rmaster.replace('/');
|
||||
// go back home
|
||||
rmaster.replace('/');
|
||||
},
|
||||
after: () {
|
||||
// close popup
|
||||
nav.pop();
|
||||
});
|
||||
},
|
||||
child: const Text('Log out'),
|
||||
child: const Text('Delete Account'),
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
));
|
||||
},
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
),
|
||||
|
||||
// logout button
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: FilledButton.tonal(
|
||||
child: const Text('Log out'),
|
||||
onPressed: () {
|
||||
// show confirm dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Log out'),
|
||||
content:
|
||||
const Text('Do you really want to log out?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// close popup
|
||||
Navigator.of(ctx).pop();
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
// send request
|
||||
final rmaster = Routemaster.of(ctx);
|
||||
|
||||
// delete everything
|
||||
// delete user data (meta)
|
||||
try {
|
||||
await User.removeDisk();
|
||||
} catch (_) {}
|
||||
// TODO: delete all rooms
|
||||
|
||||
// go back home
|
||||
rmaster.replace('/');
|
||||
},
|
||||
child: const Text('Log out'),
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
))
|
||||
]))));
|
||||
]))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +1,19 @@
|
|||
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,
|
||||
{required Future<Response> Function() req,
|
||||
Function(Map<String, dynamic> body)? onOK,
|
||||
bool Function()? onNetworkErr,
|
||||
bool Function()? onUnknownErr,
|
||||
bool Function()? onUserErr,
|
||||
Function()? onAnyErr,
|
||||
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 (onAnyErr != null) {
|
||||
onAnyErr();
|
||||
}
|
||||
if (after != null) {
|
||||
after();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Function(Map<String, dynamic>)? onServerErr}) async {
|
||||
Response res;
|
||||
try {
|
||||
res = await req(user);
|
||||
res = await req();
|
||||
} catch (_) {
|
||||
// network error
|
||||
bool showBar = true;
|
||||
|
|
Loading…
Reference in a new issue