mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
Add edit user page
This commit is contained in:
286
lib/dialog/edit_user_page.dart
Normal file
286
lib/dialog/edit_user_page.dart
Normal file
@@ -0,0 +1,286 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import '../api/user.dart';
|
||||
import '../components/labeled_checkbox.dart';
|
||||
import '../components/user_permissions_chips.dart';
|
||||
import '../globals.dart';
|
||||
|
||||
final _log = Logger("EditUserPage");
|
||||
|
||||
class EditUserPageExtra {
|
||||
const EditUserPageExtra({this.user});
|
||||
final BUser? user;
|
||||
}
|
||||
|
||||
class EditUserPage extends StatefulWidget {
|
||||
const EditUserPage(this.uid, {super.key, this.user});
|
||||
final int uid;
|
||||
final BUser? user;
|
||||
|
||||
static const routeName = "/dialog/user/edit/:uid";
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _EditUserPage();
|
||||
}
|
||||
|
||||
class _EditUserPage extends State<EditUserPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
BUser? _user;
|
||||
CancelToken? _fetchCancel;
|
||||
CancelToken? _requestCancel;
|
||||
bool _isLoading = false;
|
||||
bool _isRequesting = false;
|
||||
Object? _error;
|
||||
String? _username;
|
||||
String? _password;
|
||||
bool? _isAdmin;
|
||||
bool _revokeToken = false;
|
||||
bool _passwordVisible = false;
|
||||
UserPermissions? _permissions;
|
||||
Widget _buildWithVecticalPadding(Widget child) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_user = widget.user;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_fetchCancel?.cancel();
|
||||
_requestCancel?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _fetchData() async {
|
||||
try {
|
||||
_fetchCancel = CancelToken();
|
||||
_isLoading = true;
|
||||
final user =
|
||||
(await api.getUser(id: widget.uid, cancel: _fetchCancel)).unwrap();
|
||||
if (!_fetchCancel!.isCancelled) {
|
||||
setState(() {
|
||||
_user = user;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (!_fetchCancel!.isCancelled) {
|
||||
_log.severe("Failed to load user ${widget.uid}: $e");
|
||||
setState(() {
|
||||
_error = e;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _request() async {
|
||||
setState(() {
|
||||
_isRequesting = true;
|
||||
});
|
||||
try {
|
||||
_requestCancel = CancelToken();
|
||||
// 关闭对话框不中断连接
|
||||
final user = (await api.updateUser(
|
||||
id: widget.uid,
|
||||
username: _username,
|
||||
password: _password,
|
||||
isAdmin: _isAdmin,
|
||||
revokeToken: _revokeToken,
|
||||
permissions: _permissions?.code))
|
||||
.unwrap();
|
||||
if (!_requestCancel!.isCancelled) {
|
||||
setState(() {
|
||||
_isRequesting = false;
|
||||
_user = user;
|
||||
_username = null;
|
||||
_password = null;
|
||||
_isAdmin = null;
|
||||
_revokeToken = false;
|
||||
_permissions = null;
|
||||
});
|
||||
}
|
||||
listener.tryEmit("update_user", user);
|
||||
} catch (e) {
|
||||
_log.severe("Failed to update user ${widget.uid}: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!tryInitApi(context)) {
|
||||
return Container();
|
||||
}
|
||||
if (auth.isAdmin == false) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
context.go("/");
|
||||
});
|
||||
return Container();
|
||||
}
|
||||
if (_user != null && _user!.isAdmin && auth.isRoot == false) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
context.go("/users");
|
||||
});
|
||||
return Container();
|
||||
}
|
||||
final isLoading = _user == null && _error == null;
|
||||
if (isLoading && !_isLoading) _fetchData();
|
||||
final i18n = AppLocalizations.of(context)!;
|
||||
final maxWidth = MediaQuery.of(context).size.width;
|
||||
return Container(
|
||||
padding: maxWidth < 400
|
||||
? const EdgeInsets.symmetric(vertical: 20, horizontal: 10)
|
||||
: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
||||
child: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _error != null
|
||||
? SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Error: $_error"),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_fetchData();
|
||||
setState(() {
|
||||
_error = null;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: Text(i18n.retry)),
|
||||
],
|
||||
))
|
||||
: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Text(
|
||||
i18n.editUser,
|
||||
style:
|
||||
Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () => context.canPop()
|
||||
? context.pop()
|
||||
: context.go("/users"),
|
||||
icon: const Icon(Icons.close),
|
||||
)),
|
||||
],
|
||||
),
|
||||
_buildWithVecticalPadding(TextFormField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: i18n.username,
|
||||
),
|
||||
initialValue: _username ?? _user?.username,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_username =
|
||||
value.isEmpty || value == _user!.username
|
||||
? null
|
||||
: value;
|
||||
});
|
||||
},
|
||||
)),
|
||||
_buildWithVecticalPadding(TextFormField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: i18n.password,
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_passwordVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
color: Theme.of(context).primaryColorDark,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_passwordVisible = !_passwordVisible;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_password = value.isEmpty ? null : value;
|
||||
});
|
||||
},
|
||||
obscureText: !_passwordVisible,
|
||||
)),
|
||||
auth.isRoot == true && widget.uid != 0
|
||||
? _buildWithVecticalPadding(LabeledCheckbox(
|
||||
value: _isAdmin ?? _user!.isAdmin,
|
||||
onChanged: (b) {
|
||||
if (b != null) {
|
||||
setState(() {
|
||||
_isAdmin =
|
||||
b == _user!.isAdmin ? null : b;
|
||||
});
|
||||
}
|
||||
},
|
||||
label: Text(i18n.admin)))
|
||||
: Container(),
|
||||
_isAdmin == false ||
|
||||
(_isAdmin == null && !_user!.isAdmin)
|
||||
? _buildWithVecticalPadding(
|
||||
UserPermissionsChips(
|
||||
permissions: _permissions ??
|
||||
UserPermissions(
|
||||
_user!.permissions.code),
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_permissions = v.code ==
|
||||
_user!.permissions.code
|
||||
? null
|
||||
: v;
|
||||
});
|
||||
}))
|
||||
: Container(),
|
||||
_buildWithVecticalPadding(LabeledCheckbox(
|
||||
value: _revokeToken,
|
||||
onChanged: (b) {
|
||||
if (b != null) {
|
||||
setState(() {
|
||||
_revokeToken = b;
|
||||
});
|
||||
}
|
||||
},
|
||||
label: Text(i18n.revokeToken))),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: !_isRequesting &&
|
||||
(_username != null ||
|
||||
_password != null ||
|
||||
_isAdmin != null ||
|
||||
_permissions != null ||
|
||||
_revokeToken)
|
||||
? () {
|
||||
_request();
|
||||
}
|
||||
: null,
|
||||
child: Text(i18n.update))
|
||||
]),
|
||||
]))),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user