Files
sm-emby-share/lib/shared/widgets/server_change_password_dialog.dart
2026-07-14 11:11:36 +08:00

184 lines
5.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:forui/forui.dart';
import '../../core/domain/errors.dart';
import '../../providers/di_providers.dart';
import '../constants/breakpoints.dart';
import '../utils/user_error_formatter.dart';
import 'adaptive_modal.dart';
import 'app_inline_alert.dart';
import 'app_loading_ring.dart';
import 'app_snack_bar.dart';
Future<void> showServerChangePasswordDialog({
required BuildContext context,
required String serverId,
required String serverName,
}) => showAdaptiveModal<void>(
context: context,
maxWidth: 420,
compactMaxHeightFactor: 0.9,
builder: (_) =>
_ServerChangePasswordDialog(serverId: serverId, serverName: serverName),
);
class _ServerChangePasswordDialog extends ConsumerStatefulWidget {
final String serverId;
final String serverName;
const _ServerChangePasswordDialog({
required this.serverId,
required this.serverName,
});
@override
ConsumerState<_ServerChangePasswordDialog> createState() =>
_ServerChangePasswordDialogState();
}
class _ServerChangePasswordDialogState
extends ConsumerState<_ServerChangePasswordDialog> {
final _currentPwCtrl = TextEditingController();
final _newPwCtrl = TextEditingController();
final _confirmPwCtrl = TextEditingController();
bool _loading = false;
String? _error;
@override
void dispose() {
_currentPwCtrl.dispose();
_newPwCtrl.dispose();
_confirmPwCtrl.dispose();
super.dispose();
}
Future<void> _submit() async {
final currentPw = _currentPwCtrl.text;
final newPw = _newPwCtrl.text;
final confirmPw = _confirmPwCtrl.text;
if (currentPw.isEmpty || newPw.isEmpty || confirmPw.isEmpty) {
setState(() => _error = '所有字段不能为空');
return;
}
if (newPw != confirmPw) {
setState(() => _error = '两次输入的新密码不一致');
return;
}
setState(() {
_loading = true;
_error = null;
});
try {
final uc = await ref.read(changePasswordUseCaseProvider.future);
await uc.execute(
serverId: widget.serverId,
currentPassword: currentPw,
newPassword: newPw,
);
if (mounted) Navigator.of(context).pop();
if (mounted) {
showAppSnackBar(context, '密码已修改', tone: AppSnackTone.neutral);
}
} catch (e) {
if (!mounted) return;
setState(() {
_loading = false;
_error = e is DomainError ? formatUserError(e) : '修改失败,请检查当前密码是否正确';
});
}
}
@override
Widget build(BuildContext context) {
final isCompact = Breakpoints.isCompact(MediaQuery.sizeOf(context).width);
final title = Text('修改密码 · ${widget.serverName}');
final body = SingleChildScrollView(
padding: EdgeInsets.fromLTRB(24, isCompact ? 16 : 24, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
title,
const SizedBox(height: 18),
TextField(
controller: _currentPwCtrl,
obscureText: true,
enabled: !_loading,
textInputAction: TextInputAction.next,
autocorrect: false,
enableSuggestions: false,
decoration: const InputDecoration(labelText: '当前密码'),
),
const SizedBox(height: 12),
TextField(
controller: _newPwCtrl,
obscureText: true,
enabled: !_loading,
textInputAction: TextInputAction.next,
autocorrect: false,
enableSuggestions: false,
decoration: const InputDecoration(labelText: '新密码'),
),
const SizedBox(height: 12),
TextField(
controller: _confirmPwCtrl,
obscureText: true,
enabled: !_loading,
textInputAction: TextInputAction.done,
autocorrect: false,
enableSuggestions: false,
onSubmitted: (_) => _submit(),
decoration: const InputDecoration(labelText: '确认新密码'),
),
if (_error != null) ...[
const SizedBox(height: 12),
AppInlineAlert(message: _error!),
],
const SizedBox(height: 20),
if (isCompact)
FButton(
onPress: _loading ? null : _submit,
child: _loading
? const AppLoadingRing(size: 16, color: Colors.white)
: const Text('确认修改'),
)
else
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FButton(
variant: FButtonVariant.ghost,
onPress: _loading
? null
: () => Navigator.of(context).pop(),
child: const Text('取消'),
),
const SizedBox(width: 8),
FButton(
onPress: _loading ? null : _submit,
child: _loading
? const AppLoadingRing(size: 16, color: Colors.white)
: const Text('确认修改'),
),
],
),
if (isCompact) ...[
const SizedBox(height: 8),
FButton(
variant: FButtonVariant.outline,
onPress: _loading ? null : () => Navigator.of(context).pop(),
child: const Text('取消'),
),
],
],
),
);
return PopScope(canPop: !_loading, child: body);
}
}