mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-04-25 09:39:12 -04:00
304 lines
11 KiB
Dart
304 lines
11 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:async_tools/async_tools.dart';
|
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_translate/flutter_translate.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:veilid_support/veilid_support.dart';
|
|
|
|
import '../../layout/default_app_bar.dart';
|
|
import '../../notifications/notifications.dart';
|
|
import '../../theme/theme.dart';
|
|
import '../../tools/tools.dart';
|
|
import '../../veilid_processor/veilid_processor.dart';
|
|
import '../account_manager.dart';
|
|
import 'edit_profile_form.dart';
|
|
|
|
const _kDoBackArrow = 'doBackArrow';
|
|
|
|
class EditAccountPage extends StatefulWidget {
|
|
const EditAccountPage(
|
|
{required this.superIdentityRecordKey,
|
|
required this.initialValue,
|
|
required this.accountRecord,
|
|
super.key});
|
|
|
|
@override
|
|
State createState() => _EditAccountPageState();
|
|
|
|
final TypedKey superIdentityRecordKey;
|
|
final AccountSpec initialValue;
|
|
final OwnedDHTRecordPointer accountRecord;
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties
|
|
..add(DiagnosticsProperty<TypedKey>(
|
|
'superIdentityRecordKey', superIdentityRecordKey))
|
|
..add(DiagnosticsProperty<AccountSpec>('initialValue', initialValue))
|
|
..add(DiagnosticsProperty<OwnedDHTRecordPointer>(
|
|
'accountRecord', accountRecord));
|
|
}
|
|
}
|
|
|
|
class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
|
_EditAccountPageState()
|
|
: super(
|
|
titleBarStyle: TitleBarStyle.normal,
|
|
orientationCapability: OrientationCapability.portraitOnly);
|
|
|
|
EditProfileForm _editAccountForm(BuildContext context) => EditProfileForm(
|
|
header: translate('edit_account_page.header'),
|
|
instructions: translate('edit_account_page.instructions'),
|
|
submitText: translate('button.update'),
|
|
submitDisabledText: translate('button.waiting_for_network'),
|
|
onSubmit: _onSubmit,
|
|
onModifiedState: _onModifiedState,
|
|
initialValue: widget.initialValue,
|
|
);
|
|
|
|
Future<void> _onRemoveAccount() async {
|
|
final confirmed = await StyledDialog.show<bool>(
|
|
context: context,
|
|
title: translate('edit_account_page.remove_account_confirm'),
|
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
Text(translate('edit_account_page.remove_account_confirm_message'))
|
|
.paddingLTRB(24, 24, 24, 0),
|
|
Text(translate('confirmation.are_you_sure')).paddingAll(8),
|
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(false);
|
|
},
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0),
|
|
Text(translate('button.no')).paddingLTRB(0, 0, 4, 0)
|
|
])),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(true);
|
|
},
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
|
Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0)
|
|
]))
|
|
]).paddingAll(24)
|
|
]));
|
|
if (confirmed != null && confirmed && mounted) {
|
|
// dismiss the keyboard by unfocusing the textfield
|
|
FocusScope.of(context).unfocus();
|
|
|
|
try {
|
|
setState(() {
|
|
_isInAsyncCall = true;
|
|
});
|
|
try {
|
|
final success = await AccountRepository.instance.deleteLocalAccount(
|
|
widget.superIdentityRecordKey, widget.accountRecord);
|
|
if (mounted) {
|
|
if (success) {
|
|
context
|
|
.read<NotificationsCubit>()
|
|
.info(text: translate('edit_account_page.account_removed'));
|
|
GoRouterHelper(context).pop();
|
|
} else {
|
|
context.read<NotificationsCubit>().error(
|
|
title: translate('edit_account_page.failed_to_remove_title'),
|
|
text: translate('edit_account_page.try_again_network'));
|
|
}
|
|
}
|
|
} finally {
|
|
setState(() {
|
|
_isInAsyncCall = false;
|
|
});
|
|
}
|
|
} on Exception catch (e, st) {
|
|
if (mounted) {
|
|
await showErrorStacktraceModal(
|
|
context: context, error: e, stackTrace: st);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _onDestroyAccount() async {
|
|
final confirmed = await StyledDialog.show<bool>(
|
|
context: context,
|
|
title: translate('edit_account_page.destroy_account_confirm'),
|
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
Text(translate('edit_account_page.destroy_account_confirm_message'))
|
|
.paddingLTRB(24, 24, 24, 0),
|
|
Text(translate(
|
|
'edit_account_page.destroy_account_confirm_message_details'))
|
|
.paddingLTRB(24, 24, 24, 0),
|
|
Text(translate('confirmation.are_you_sure')).paddingAll(8),
|
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(false);
|
|
},
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0),
|
|
Text(translate('button.no')).paddingLTRB(0, 0, 4, 0)
|
|
])),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(true);
|
|
},
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
|
Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0)
|
|
]))
|
|
]).paddingAll(24)
|
|
]));
|
|
if (confirmed != null && confirmed && mounted) {
|
|
// dismiss the keyboard by unfocusing the textfield
|
|
FocusScope.of(context).unfocus();
|
|
|
|
try {
|
|
setState(() {
|
|
_isInAsyncCall = true;
|
|
});
|
|
try {
|
|
final success = await AccountRepository.instance.destroyAccount(
|
|
widget.superIdentityRecordKey, widget.accountRecord);
|
|
if (mounted) {
|
|
if (success) {
|
|
context
|
|
.read<NotificationsCubit>()
|
|
.info(text: translate('edit_account_page.account_destroyed'));
|
|
GoRouterHelper(context).pop();
|
|
} else {
|
|
context.read<NotificationsCubit>().error(
|
|
title: translate('edit_account_page.failed_to_destroy_title'),
|
|
text: translate('edit_account_page.try_again_network'));
|
|
}
|
|
}
|
|
} finally {
|
|
setState(() {
|
|
_isInAsyncCall = false;
|
|
});
|
|
}
|
|
} on Exception catch (e, st) {
|
|
if (mounted) {
|
|
await showErrorStacktraceModal(
|
|
context: context, error: e, stackTrace: st);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _onModifiedState(bool isModified) {
|
|
setState(() {
|
|
_isModified = isModified;
|
|
});
|
|
}
|
|
|
|
Future<bool> _onSubmit(AccountSpec accountSpec) async {
|
|
try {
|
|
setState(() {
|
|
_isInAsyncCall = true;
|
|
});
|
|
try {
|
|
// Look up account cubit for this specific account
|
|
final perAccountCollectionBlocMapCubit =
|
|
context.read<PerAccountCollectionBlocMapCubit>();
|
|
final accountRecordCubit = await perAccountCollectionBlocMapCubit
|
|
.operate(widget.superIdentityRecordKey,
|
|
closure: (c) async => c.accountRecordCubit);
|
|
if (accountRecordCubit == null) {
|
|
return false;
|
|
}
|
|
|
|
// Update account profile DHT record
|
|
// This triggers ConversationCubits to update
|
|
accountRecordCubit.updateAccount(accountSpec, () async {
|
|
// Update local account profile
|
|
await AccountRepository.instance
|
|
.updateLocalAccount(widget.superIdentityRecordKey, accountSpec);
|
|
});
|
|
|
|
return true;
|
|
} finally {
|
|
setState(() {
|
|
_isInAsyncCall = false;
|
|
});
|
|
}
|
|
} on Exception catch (e, st) {
|
|
if (mounted) {
|
|
await showErrorStacktraceModal(
|
|
context: context, error: e, stackTrace: st);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final displayModalHUD = _isInAsyncCall;
|
|
|
|
return StyledScaffold(
|
|
// resizeToAvoidBottomInset: false,
|
|
appBar: DefaultAppBar(
|
|
title: Text(translate('edit_account_page.titlebar')),
|
|
leading: Navigator.canPop(context)
|
|
? IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () {
|
|
singleFuture((this, _kDoBackArrow), () async {
|
|
if (_isModified) {
|
|
final ok = await showConfirmModal(
|
|
context: context,
|
|
title:
|
|
translate('confirmation.discard_changes'),
|
|
text: translate(
|
|
'confirmation.are_you_sure_discard'));
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
}
|
|
if (context.mounted) {
|
|
Navigator.pop(context);
|
|
}
|
|
});
|
|
})
|
|
: null,
|
|
actions: [
|
|
const SignalStrengthMeterWidget(),
|
|
IconButton(
|
|
icon: const Icon(Icons.settings),
|
|
tooltip: translate('menu.settings_tooltip'),
|
|
onPressed: () async {
|
|
await GoRouterHelper(context).push('/settings');
|
|
})
|
|
]),
|
|
body: SingleChildScrollView(
|
|
child: Column(children: [
|
|
_editAccountForm(context).paddingLTRB(0, 0, 0, 32),
|
|
OptionBox(
|
|
instructions:
|
|
translate('edit_account_page.remove_account_description'),
|
|
buttonIcon: Icons.person_remove_alt_1,
|
|
buttonText: translate('edit_account_page.remove_account'),
|
|
onClick: _onRemoveAccount,
|
|
),
|
|
OptionBox(
|
|
instructions:
|
|
translate('edit_account_page.destroy_account_description'),
|
|
buttonIcon: Icons.person_off,
|
|
buttonText: translate('edit_account_page.destroy_account'),
|
|
onClick: _onDestroyAccount,
|
|
)
|
|
]).paddingSymmetric(horizontal: 24, vertical: 8)))
|
|
.withModalHUD(context, displayModalHUD);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool _isInAsyncCall = false;
|
|
bool _isModified = false;
|
|
}
|