import 'package:flutter/material.dart';
+import 'package:built_collection/built_collection.dart';
import 'package:proxmox_login_manager/proxmox_general_settings_form.dart';
import 'package:proxmox_login_manager/proxmox_login_form.dart';
import 'package:proxmox_login_manager/proxmox_login_model.dart';
import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'
as proxclient;
+import 'package:proxmox_login_manager/extension.dart';
+import 'package:proxmox_login_manager/proxmox_password_store.dart';
typedef OnLoginCallback = Function(proxclient.ProxmoxApiClient client);
class ProxmoxLoginSelector extends StatefulWidget {
- final OnLoginCallback onLogin;
+ final OnLoginCallback? onLogin;
- const ProxmoxLoginSelector({Key key, this.onLogin}) : super(key: key);
+ const ProxmoxLoginSelector({Key? key, this.onLogin}) : super(key: key);
@override
_ProxmoxLoginSelectorState createState() => _ProxmoxLoginSelectorState();
}
class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
- Future<ProxmoxLoginStorage> loginStorage;
+ Future<ProxmoxLoginStorage?>? loginStorage;
@override
void initState() {
super.initState();
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
+ backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- 'PROXMOX',
+ 'Proxmox',
style: TextStyle(
- fontFamily: 'Proxmox',
- fontSize: 26,
+ fontSize: 14,
),
),
Text(
'Virtual Environment',
style: TextStyle(
- fontSize: 12,
+ fontSize: 14,
),
)
],
})
],
),
- body: FutureBuilder<ProxmoxLoginStorage>(
+ body: FutureBuilder<ProxmoxLoginStorage?>(
future: loginStorage,
builder: (context, snapshot) {
- if (snapshot.hasData && (snapshot.data.logins?.isEmpty ?? true)) {
+ if (!snapshot.hasData) {
+ return Center(
+ child: CircularProgressIndicator(),
+ );
+ }
+ if (snapshot.hasData &&
+ (snapshot.data!.logins?.isEmpty ?? true)) {
return Center(
child: Text('Add an account'),
);
}
+ var items = <Widget>[];
+ final BuiltList<ProxmoxLoginModel> logins =
+ snapshot.data?.logins ?? BuiltList<ProxmoxLoginModel>();
+
+ final activeSessions =
+ logins.rebuild((b) => b.where((b) => b.activeSession));
+
+ if (activeSessions.isNotEmpty) {
+ items.addAll([
+ Padding(
+ padding: const EdgeInsets.all(12.0),
+ child: Text(
+ 'Active Sessions',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ...activeSessions.map((s) => ListTile(
+ title: Text(s.fullHostname),
+ subtitle: Text(s.fullUsername),
+ trailing: Icon(Icons.navigate_next),
+ leading: PopupMenuButton(
+ icon: Icon(Icons.more_vert, color: Colors.green),
+ itemBuilder: (context) => [
+ PopupMenuItem(
+ child: ListTile(
+ dense: true,
+ leading: Icon(Icons.logout),
+ title: Text('Logout'),
+ onTap: () async {
+ await snapshot.data!
+ .rebuild((b) => b.logins
+ .rebuildWhere((m) => s == m,
+ (b) => b..ticket = ''))
+ .saveToDisk();
+ refreshFromStorage();
+ Navigator.of(context).pop();
+ },
+ ),
+ ),
+ ]),
+ onTap: () => _login(user: s),
+ )),
+ ]);
+ }
+ items.addAll([
+ Padding(
+ padding: const EdgeInsets.all(12.0),
+ child: Text(
+ 'Available Sites',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ...logins.where((b) => !b.activeSession).map((login) =>
+ ListTile(
+ title: Text(login.fullHostname),
+ subtitle: Text(login.fullUsername),
+ trailing: Icon(Icons.navigate_next),
+ leading: PopupMenuButton(
+ itemBuilder: (context) => [
+ if (login.passwordSaved ?? false)
+ PopupMenuItem(
+ child: ListTile(
+ dense: true,
+ leading: Icon(Icons.key_off),
+ title: Text('Delete Password'),
+ onTap: () async {
+ await deletePassword(login.identifier!);
+ await snapshot.data!
+ .rebuild((b) => b
+ ..logins.rebuildWhere(
+ (m) => m == login,
+ (b) =>
+ b..passwordSaved = false))
+ .saveToDisk();
+ refreshFromStorage();
+ Navigator.of(context).pop();
+ },
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ dense: true,
+ leading: Icon(Icons.delete),
+ title: Text('Delete'),
+ onTap: () async {
+ await deletePassword(login.identifier!);
+ await snapshot.data!
+ .rebuild(
+ (b) => b.logins.remove(login))
+ .saveToDisk();
+ refreshFromStorage();
+ Navigator.of(context).pop();
+ },
+ ),
+ ),
+ ]),
+ onTap: () => _login(user: login),
+ ))
+ ]);
return ListView(
- children: snapshot.data?.logins
- ?.map((l) => ListTile(
- title: Text(l.origin.host),
- subtitle: Text(l.fullUsername),
- trailing: Icon(Icons.navigate_next),
- leading: PopupMenuButton(
- itemBuilder: (context) => [
- PopupMenuItem(
- child: ListTile(
- dense: true,
- leading: Icon(Icons.delete),
- title: Text('Delete'),
- onTap: () {
- snapshot.data
- .rebuild(
- (b) => b.logins.remove(l))
- .saveToDisk();
- refreshFromStorage();
- Navigator.of(context).pop();
- },
- ),
- )
- ]),
- onTap: () => _login(user: l),
- ))
- ?.toList() ??
- [],
+ children: items,
);
}),
floatingActionButton: FloatingActionButton.extended(
);
}
- Future<void> _login({ProxmoxLoginModel user, bool isCreate = false}) async {
+ Future<void> _login({ProxmoxLoginModel? user, bool isCreate = false}) async {
+ String? password;
+ String ticket = user?.ticket ?? '';
+ bool passwordSaved = user != null && (user.passwordSaved ?? false);
+
+ if (ticket == '' && passwordSaved) {
+ password = await getPassword(user.identifier!);
+ }
+
final client = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProxmoxLoginPage(
userModel: user,
isCreate: isCreate,
+ ticket: user?.ticket,
+ password: password,
)));
refreshFromStorage();
if (client != null) {
- widget.onLogin(client);
+ widget.onLogin!(client);
}
}