mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-08-18 10:48:07 -04:00
profile edit happens without requiring save button
This commit is contained in:
parent
b6a812af87
commit
030f9d9651
19 changed files with 499 additions and 266 deletions
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
|
@ -7,6 +8,10 @@ import '../../proto/proto.dart' as proto;
|
|||
import '../account_manager.dart';
|
||||
|
||||
typedef AccountRecordState = proto.Account;
|
||||
typedef _sspUpdateState = (
|
||||
AccountSpec accountSpec,
|
||||
Future<void> Function() onSuccess
|
||||
);
|
||||
|
||||
/// The saved state of a VeilidChat Account on the DHT
|
||||
/// Used to synchronize status, profile, and options for a specific account
|
||||
|
@ -34,16 +39,25 @@ class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
|||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _sspUpdate.close();
|
||||
await super.close();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Public Interface
|
||||
|
||||
Future<void> updateAccount(
|
||||
AccountSpec accountSpec,
|
||||
) async {
|
||||
void updateAccount(
|
||||
AccountSpec accountSpec, Future<void> Function() onSuccess) {
|
||||
_sspUpdate.updateState((accountSpec, onSuccess), (state) async {
|
||||
await _updateAccountAsync(state.$1, state.$2);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _updateAccountAsync(
|
||||
AccountSpec accountSpec, Future<void> Function() onSuccess) async {
|
||||
var changed = false;
|
||||
await record.eventualUpdateProtobuf(proto.Account.fromBuffer, (old) async {
|
||||
changed = false;
|
||||
if (old == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -63,7 +77,6 @@ class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
|||
..awayMessage = accountSpec.awayMessage
|
||||
..busyMessage = accountSpec.busyMessage;
|
||||
|
||||
var changed = false;
|
||||
if (newAccount.profile != old.profile ||
|
||||
newAccount.invisible != old.invisible ||
|
||||
newAccount.autodetectAway != old.autodetectAway ||
|
||||
|
@ -78,5 +91,10 @@ class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
|||
}
|
||||
return null;
|
||||
});
|
||||
if (changed) {
|
||||
await onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
final _sspUpdate = SingleStateProcessor<_sspUpdateState>();
|
||||
}
|
||||
|
|
|
@ -50,13 +50,13 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
|||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
Widget _editAccountForm(BuildContext context,
|
||||
{required Future<void> Function(AccountSpec) onSubmit}) =>
|
||||
{required Future<void> Function(AccountSpec) onUpdate}) =>
|
||||
EditProfileForm(
|
||||
header: translate('edit_account_page.header'),
|
||||
instructions: translate('edit_account_page.instructions'),
|
||||
submitText: translate('edit_account_page.update'),
|
||||
submitDisabledText: translate('button.waiting_for_network'),
|
||||
onSubmit: onSubmit,
|
||||
onUpdate: onUpdate,
|
||||
initialValueCallback: (key) => switch (key) {
|
||||
EditProfileForm.formFieldName => widget.existingAccount.profile.name,
|
||||
EditProfileForm.formFieldPronouns =>
|
||||
|
@ -76,7 +76,7 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
|||
EditProfileForm.formFieldAutoAway =>
|
||||
widget.existingAccount.autodetectAway,
|
||||
EditProfileForm.formFieldAutoAwayTimeout =>
|
||||
widget.existingAccount.autoAwayTimeoutMin,
|
||||
widget.existingAccount.autoAwayTimeoutMin.toString(),
|
||||
String() => throw UnimplementedError(),
|
||||
},
|
||||
);
|
||||
|
@ -214,51 +214,24 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _onSubmit(AccountSpec accountSpec) async {
|
||||
// dismiss the keyboard by unfocusing the textfield
|
||||
FocusScope.of(context).unfocus();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Update account profile DHT record
|
||||
// This triggers ConversationCubits to update
|
||||
await accountRecordCubit.updateAccount(accountSpec);
|
||||
|
||||
// Update local account profile
|
||||
await AccountRepository.instance
|
||||
.updateLocalAccount(widget.superIdentityRecordKey, accountSpec);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.canPop(context)
|
||||
? GoRouterHelper(context).pop()
|
||||
: GoRouterHelper(context).go('/');
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isInAsyncCall = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
if (mounted) {
|
||||
await showErrorModal(
|
||||
context, translate('edit_account_page.error'), 'Exception: $e');
|
||||
}
|
||||
Future<void> _onUpdate(AccountSpec accountSpec) async {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -290,7 +263,7 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
|||
child: Column(children: [
|
||||
_editAccountForm(
|
||||
context,
|
||||
onSubmit: _onSubmit,
|
||||
onUpdate: _onUpdate,
|
||||
).paddingLTRB(0, 0, 0, 32),
|
||||
OptionBox(
|
||||
instructions:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -10,15 +11,18 @@ import '../../proto/proto.dart' as proto;
|
|||
import '../../theme/theme.dart';
|
||||
import '../models/models.dart';
|
||||
|
||||
const _kDoUpdateSubmit = 'doUpdateSubmit';
|
||||
|
||||
class EditProfileForm extends StatefulWidget {
|
||||
const EditProfileForm({
|
||||
required this.header,
|
||||
required this.instructions,
|
||||
required this.submitText,
|
||||
required this.submitDisabledText,
|
||||
super.key,
|
||||
required this.initialValueCallback,
|
||||
this.onUpdate,
|
||||
this.onSubmit,
|
||||
this.initialValueCallback,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -26,10 +30,11 @@ class EditProfileForm extends StatefulWidget {
|
|||
|
||||
final String header;
|
||||
final String instructions;
|
||||
final Future<void> Function(AccountSpec)? onUpdate;
|
||||
final Future<void> Function(AccountSpec)? onSubmit;
|
||||
final String submitText;
|
||||
final String submitDisabledText;
|
||||
final Object? Function(String key)? initialValueCallback;
|
||||
final Object Function(String key) initialValueCallback;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
|
@ -38,11 +43,13 @@ class EditProfileForm extends StatefulWidget {
|
|||
..add(StringProperty('header', header))
|
||||
..add(StringProperty('instructions', instructions))
|
||||
..add(ObjectFlagProperty<Future<void> Function(AccountSpec)?>.has(
|
||||
'onSubmit', onSubmit))
|
||||
'onUpdate', onUpdate))
|
||||
..add(StringProperty('submitText', submitText))
|
||||
..add(StringProperty('submitDisabledText', submitDisabledText))
|
||||
..add(ObjectFlagProperty<Object? Function(String key)?>.has(
|
||||
'initialValueCallback', initialValueCallback));
|
||||
..add(ObjectFlagProperty<Object Function(String key)?>.has(
|
||||
'initialValueCallback', initialValueCallback))
|
||||
..add(ObjectFlagProperty<Future<void> Function(AccountSpec)?>.has(
|
||||
'onSubmit', onSubmit));
|
||||
}
|
||||
|
||||
static const String formFieldName = 'name';
|
||||
|
@ -62,15 +69,17 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_autoAwayEnabled =
|
||||
widget.initialValueCallback(EditProfileForm.formFieldAutoAway) as bool;
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
FormBuilderDropdown<proto.Availability> _availabilityDropDown(
|
||||
BuildContext context) {
|
||||
final initialValueX =
|
||||
widget.initialValueCallback?.call(EditProfileForm.formFieldAvailability)
|
||||
as proto.Availability? ??
|
||||
proto.Availability.AVAILABILITY_FREE;
|
||||
widget.initialValueCallback(EditProfileForm.formFieldAvailability)
|
||||
as proto.Availability;
|
||||
final initialValue =
|
||||
initialValueX == proto.Availability.AVAILABILITY_UNSPECIFIED
|
||||
? proto.Availability.AVAILABILITY_FREE
|
||||
|
@ -86,14 +95,19 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
return FormBuilderDropdown<proto.Availability>(
|
||||
name: EditProfileForm.formFieldAvailability,
|
||||
initialValue: initialValue,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_availability'),
|
||||
hintText: translate('account.empty_busy_message')),
|
||||
items: availabilities
|
||||
.map((x) => DropdownMenuItem<proto.Availability>(
|
||||
value: x,
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Icon(AvailabilityWidget.availabilityIcon(x)),
|
||||
AvailabilityWidget.availabilityIcon(x),
|
||||
Text(x == proto.Availability.AVAILABILITY_OFFLINE
|
||||
? translate('availability.always_show_offline')
|
||||
: AvailabilityWidget.availabilityName(x)),
|
||||
? translate('availability.always_show_offline')
|
||||
: AvailabilityWidget.availabilityName(x))
|
||||
.paddingLTRB(8, 0, 0, 0),
|
||||
])))
|
||||
.toList(),
|
||||
);
|
||||
|
@ -103,34 +117,26 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
final name = _formKey
|
||||
.currentState!.fields[EditProfileForm.formFieldName]!.value as String;
|
||||
final pronouns = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldPronouns]!.value as String? ??
|
||||
'';
|
||||
final about = _formKey.currentState!.fields[EditProfileForm.formFieldAbout]!
|
||||
.value as String? ??
|
||||
'';
|
||||
.fields[EditProfileForm.formFieldPronouns]!.value as String;
|
||||
final about = _formKey
|
||||
.currentState!.fields[EditProfileForm.formFieldAbout]!.value as String;
|
||||
final availability = _formKey
|
||||
.currentState!
|
||||
.fields[EditProfileForm.formFieldAvailability]!
|
||||
.value as proto.Availability? ??
|
||||
proto.Availability.AVAILABILITY_FREE;
|
||||
.currentState!
|
||||
.fields[EditProfileForm.formFieldAvailability]!
|
||||
.value as proto.Availability;
|
||||
|
||||
final invisible = availability == proto.Availability.AVAILABILITY_OFFLINE;
|
||||
|
||||
final freeMessage = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldFreeMessage]!.value as String? ??
|
||||
'';
|
||||
.fields[EditProfileForm.formFieldFreeMessage]!.value as String;
|
||||
final awayMessage = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldAwayMessage]!.value as String? ??
|
||||
'';
|
||||
.fields[EditProfileForm.formFieldAwayMessage]!.value as String;
|
||||
final busyMessage = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldBusyMessage]!.value as String? ??
|
||||
'';
|
||||
final autoAway = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldAutoAway]!.value as bool? ??
|
||||
false;
|
||||
final autoAwayTimeout = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldAutoAwayTimeout]!.value as int? ??
|
||||
30;
|
||||
.fields[EditProfileForm.formFieldBusyMessage]!.value as String;
|
||||
final autoAway = _formKey
|
||||
.currentState!.fields[EditProfileForm.formFieldAutoAway]!.value as bool;
|
||||
final autoAwayTimeoutString = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldAutoAwayTimeout]!.value as String;
|
||||
final autoAwayTimeout = int.parse(autoAwayTimeoutString);
|
||||
|
||||
return AccountSpec(
|
||||
name: name,
|
||||
|
@ -163,6 +169,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
|
||||
return FormBuilder(
|
||||
key: _formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
children: [
|
||||
AvatarWidget(
|
||||
|
@ -179,9 +186,10 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
FormBuilderTextField(
|
||||
autofocus: true,
|
||||
name: EditProfileForm.formFieldName,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldName) as String?,
|
||||
initialValue: widget
|
||||
.initialValueCallback(EditProfileForm.formFieldName) as String,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_name'),
|
||||
hintText: translate('account.empty_name')),
|
||||
maxLength: 64,
|
||||
|
@ -190,113 +198,149 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
FormBuilderValidators.required(),
|
||||
]),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldPronouns,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldPronouns) as String?,
|
||||
initialValue:
|
||||
widget.initialValueCallback(EditProfileForm.formFieldPronouns)
|
||||
as String,
|
||||
maxLength: 64,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_pronouns'),
|
||||
hintText: translate('account.empty_pronouns')),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldAbout,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldAbout) as String?,
|
||||
initialValue: widget
|
||||
.initialValueCallback(EditProfileForm.formFieldAbout) as String,
|
||||
maxLength: 1024,
|
||||
maxLines: 8,
|
||||
minLines: 1,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_about'),
|
||||
hintText: translate('account.empty_about')),
|
||||
textInputAction: TextInputAction.newline,
|
||||
),
|
||||
_availabilityDropDown(context),
|
||||
).onFocusChange(_onFocusChange),
|
||||
_availabilityDropDown(context)
|
||||
.paddingLTRB(0, 0, 0, 16)
|
||||
.onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldFreeMessage,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldFreeMessage) as String?,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldFreeMessage) as String,
|
||||
maxLength: 128,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_free_message'),
|
||||
hintText: translate('account.empty_free_message')),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldAwayMessage,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldAwayMessage) as String?,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldAwayMessage) as String,
|
||||
maxLength: 128,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_away_message'),
|
||||
hintText: translate('account.empty_away_message')),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldBusyMessage,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldBusyMessage) as String?,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldBusyMessage) as String,
|
||||
maxLength: 128,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_busy_message'),
|
||||
hintText: translate('account.empty_busy_message')),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderCheckbox(
|
||||
name: EditProfileForm.formFieldAutoAway,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldAutoAway) as bool? ??
|
||||
false,
|
||||
initialValue:
|
||||
widget.initialValueCallback(EditProfileForm.formFieldAutoAway)
|
||||
as bool,
|
||||
side: BorderSide(color: scale.primaryScale.border, width: 2),
|
||||
title: Text(translate('account.form_auto_away'),
|
||||
style: textTheme.labelMedium),
|
||||
),
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_autoAwayEnabled = v ?? false;
|
||||
});
|
||||
},
|
||||
).onFocusChange(_onFocusChange),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldAutoAwayTimeout,
|
||||
enabled: _formKey.currentState
|
||||
?.value[EditProfileForm.formFieldAutoAway] as bool? ??
|
||||
false,
|
||||
initialValue: widget.initialValueCallback
|
||||
?.call(EditProfileForm.formFieldAutoAwayTimeout)
|
||||
as String? ??
|
||||
'15',
|
||||
enabled: _autoAwayEnabled,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldAutoAwayTimeout) as String,
|
||||
decoration: InputDecoration(
|
||||
labelText: translate('account.form_auto_away_timeout'),
|
||||
),
|
||||
validator: FormBuilderValidators.positiveNumber(),
|
||||
textInputAction: TextInputAction.next,
|
||||
),
|
||||
).onFocusChange(_onFocusChange),
|
||||
Row(children: [
|
||||
const Spacer(),
|
||||
Text(widget.instructions).toCenter().flexible(flex: 6),
|
||||
const Spacer(),
|
||||
]).paddingSymmetric(vertical: 4),
|
||||
ElevatedButton(
|
||||
onPressed: widget.onSubmit == null
|
||||
? null
|
||||
: () async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final aus = _makeAccountSpec();
|
||||
await widget.onSubmit!(aus);
|
||||
}
|
||||
},
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
||||
Text((widget.onSubmit == null)
|
||||
? widget.submitDisabledText
|
||||
: widget.submitText)
|
||||
.paddingLTRB(0, 0, 4, 0)
|
||||
]),
|
||||
).paddingSymmetric(vertical: 4).alignAtCenterRight(),
|
||||
if (widget.onSubmit != null)
|
||||
ElevatedButton(
|
||||
onPressed: widget.onSubmit == null ? null : _doSubmit,
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
||||
Text((widget.onSubmit == null)
|
||||
? widget.submitDisabledText
|
||||
: widget.submitText)
|
||||
.paddingLTRB(0, 0, 4, 0)
|
||||
]),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onFocusChange(bool focused) {
|
||||
if (!focused) {
|
||||
_doUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void _doUpdate() {
|
||||
final onUpdate = widget.onUpdate;
|
||||
if (onUpdate != null) {
|
||||
singleFuture((this, _kDoUpdateSubmit), () async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final aus = _makeAccountSpec();
|
||||
await onUpdate(aus);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _doSubmit() {
|
||||
final onSubmit = widget.onSubmit;
|
||||
if (onSubmit != null) {
|
||||
singleFuture((this, _kDoUpdateSubmit), () async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final aus = _makeAccountSpec();
|
||||
await onSubmit(aus);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _editProfileForm(
|
||||
context,
|
||||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
late bool _autoAwayEnabled;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import 'package:flutter_translate/flutter_translate.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../layout/default_app_bar.dart';
|
||||
import '../../notifications/cubits/notifications_cubit.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../../veilid_processor/veilid_processor.dart';
|
||||
|
@ -26,25 +28,44 @@ class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
|||
titleBarStyle: TitleBarStyle.normal,
|
||||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
Widget _newAccountForm(BuildContext context,
|
||||
{required Future<void> Function(AccountSpec) onSubmit}) {
|
||||
final networkReady = context
|
||||
.watch<ConnectionStateCubit>()
|
||||
.state
|
||||
.asData
|
||||
?.value
|
||||
.isPublicInternetReady ??
|
||||
false;
|
||||
final canSubmit = networkReady;
|
||||
|
||||
return EditProfileForm(
|
||||
header: translate('new_account_page.header'),
|
||||
instructions: translate('new_account_page.instructions'),
|
||||
submitText: translate('new_account_page.create'),
|
||||
submitDisabledText: translate('button.waiting_for_network'),
|
||||
onSubmit: !canSubmit ? null : onSubmit);
|
||||
Object _defaultAccountValues(String key) {
|
||||
switch (key) {
|
||||
case EditProfileForm.formFieldName:
|
||||
return '';
|
||||
case EditProfileForm.formFieldPronouns:
|
||||
return '';
|
||||
case EditProfileForm.formFieldAbout:
|
||||
return '';
|
||||
case EditProfileForm.formFieldAvailability:
|
||||
return proto.Availability.AVAILABILITY_FREE;
|
||||
case EditProfileForm.formFieldFreeMessage:
|
||||
return '';
|
||||
case EditProfileForm.formFieldAwayMessage:
|
||||
return '';
|
||||
case EditProfileForm.formFieldBusyMessage:
|
||||
return '';
|
||||
// case EditProfileForm.formFieldAvatar:
|
||||
// return null;
|
||||
case EditProfileForm.formFieldAutoAway:
|
||||
return false;
|
||||
case EditProfileForm.formFieldAutoAwayTimeout:
|
||||
return '15';
|
||||
default:
|
||||
throw StateError('missing form element');
|
||||
}
|
||||
}
|
||||
|
||||
Widget _newAccountForm(
|
||||
BuildContext context,
|
||||
) =>
|
||||
EditProfileForm(
|
||||
header: translate('new_account_page.header'),
|
||||
instructions: translate('new_account_page.instructions'),
|
||||
submitText: translate('new_account_page.create'),
|
||||
submitDisabledText: translate('button.waiting_for_network'),
|
||||
initialValueCallback: _defaultAccountValues,
|
||||
onSubmit: _onSubmit);
|
||||
|
||||
Future<void> _onSubmit(AccountSpec accountSpec) async {
|
||||
// dismiss the keyboard by unfocusing the textfield
|
||||
FocusScope.of(context).unfocus();
|
||||
|
@ -54,6 +75,22 @@ class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
|||
_isInAsyncCall = true;
|
||||
});
|
||||
try {
|
||||
final networkReady = context
|
||||
.read<ConnectionStateCubit>()
|
||||
.state
|
||||
.asData
|
||||
?.value
|
||||
.isPublicInternetReady ??
|
||||
false;
|
||||
|
||||
final canSubmit = networkReady;
|
||||
if (!canSubmit) {
|
||||
context.read<NotificationsCubit>().error(
|
||||
text: translate('new_account_page.network_is_offline'),
|
||||
title: translate('new_account_page.error'));
|
||||
return;
|
||||
}
|
||||
|
||||
final writableSuperIdentity = await AccountRepository.instance
|
||||
.createWithNewSuperIdentity(accountSpec);
|
||||
GoRouterHelper(context).pushReplacement('/new_account/recovery_key',
|
||||
|
@ -100,7 +137,6 @@ class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
|||
body: SingleChildScrollView(
|
||||
child: _newAccountForm(
|
||||
context,
|
||||
onSubmit: _onSubmit,
|
||||
)).paddingSymmetric(horizontal: 24, vertical: 8),
|
||||
).withModalHUD(context, displayModalHUD);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue