actions-test/lib/screens/settings/main.dart
Jakob Meier 30a19fcc1e
Started working on settings screen
Added:
- changePassword to change the password
NOTE: this requires the old password,
just to prevent account hijacking.
- some basic user limit information
- theme selector
NOTE: the system theme is meant to function like auto-theme,
and is directly translated into a flutter ThemeMode,
however, this does not appear to be working on the web.

This commit also adds the logout and delete account buttons,
but they do not yet delete all rooms,
nor do they properly logout the user.
BUG: User is not logged out correctly,
reloading the page fixes this.
Maybe localstore.listen does not detect deletion?
2023-03-25 14:29:28 +01:00

267 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:outbag_app/backend/request.dart';
import 'package:outbag_app/backend/themes.dart';
import 'package:outbag_app/backend/user.dart';
import 'package:outbag_app/screens/settings/dialogs/password.dart';
import 'package:outbag_app/tools/fetch_wrapper.dart';
import 'package:provider/provider.dart';
import 'package:routemaster/routemaster.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<StatefulWidget> createState() => _SettingsPageState();
}
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);
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",
),
),
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: (_) {},
))
],
)))),
// 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'),
// NOTE: have the trailing item be a value select widget
trailing: 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(_) {}
},
),
),
// change password button
ListTile(
title: const Text('Change password'),
subtitle: const Text('Choose a new password for your account'),
onTap: () {
// TODO: show confirm dialog
// NOTE: needs an input field for the current password
// NOTE: might want to show a message explaining,
// that there is no way to reset the password
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),
),
// 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?'),
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'),
)
],
));
},
))
]))));
}
}