mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-21 13:48:43 -04:00
ui cleanup
This commit is contained in:
parent
d460a0388c
commit
77c68aa45f
57 changed files with 1158 additions and 914 deletions
|
@ -13,7 +13,7 @@ import '../../theme/theme.dart';
|
|||
import '../../veilid_processor/veilid_processor.dart';
|
||||
import '../models/models.dart';
|
||||
|
||||
const _kDoUpdateSubmit = 'doUpdateSubmit';
|
||||
const _kDoSubmitEditProfile = 'doSubmitEditProfile';
|
||||
|
||||
class EditProfileForm extends StatefulWidget {
|
||||
const EditProfileForm({
|
||||
|
@ -21,9 +21,9 @@ class EditProfileForm extends StatefulWidget {
|
|||
required this.instructions,
|
||||
required this.submitText,
|
||||
required this.submitDisabledText,
|
||||
required this.initialValueCallback,
|
||||
this.onUpdate,
|
||||
this.onSubmit,
|
||||
required this.initialValue,
|
||||
required this.onSubmit,
|
||||
this.onModifiedState,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -32,11 +32,11 @@ class EditProfileForm extends StatefulWidget {
|
|||
|
||||
final String header;
|
||||
final String instructions;
|
||||
final Future<void> Function(AccountSpec)? onUpdate;
|
||||
final Future<void> Function(AccountSpec)? onSubmit;
|
||||
final Future<bool> Function(AccountSpec) onSubmit;
|
||||
final void Function(bool)? onModifiedState;
|
||||
final String submitText;
|
||||
final String submitDisabledText;
|
||||
final Object Function(String key) initialValueCallback;
|
||||
final AccountSpec initialValue;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
|
@ -44,14 +44,13 @@ class EditProfileForm extends StatefulWidget {
|
|||
properties
|
||||
..add(StringProperty('header', header))
|
||||
..add(StringProperty('instructions', instructions))
|
||||
..add(ObjectFlagProperty<Future<void> Function(AccountSpec)?>.has(
|
||||
'onUpdate', onUpdate))
|
||||
..add(StringProperty('submitText', submitText))
|
||||
..add(StringProperty('submitDisabledText', submitDisabledText))
|
||||
..add(ObjectFlagProperty<Object Function(String key)?>.has(
|
||||
'initialValueCallback', initialValueCallback))
|
||||
..add(ObjectFlagProperty<Future<void> Function(AccountSpec)?>.has(
|
||||
'onSubmit', onSubmit));
|
||||
..add(ObjectFlagProperty<Future<bool> Function(AccountSpec)>.has(
|
||||
'onSubmit', onSubmit))
|
||||
..add(ObjectFlagProperty<void Function(bool p1)?>.has(
|
||||
'onModifiedState', onModifiedState))
|
||||
..add(DiagnosticsProperty<AccountSpec>('initialValue', initialValue));
|
||||
}
|
||||
|
||||
static const String formFieldName = 'name';
|
||||
|
@ -71,8 +70,9 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_autoAwayEnabled =
|
||||
widget.initialValueCallback(EditProfileForm.formFieldAutoAway) as bool;
|
||||
_savedValue = widget.initialValue;
|
||||
_currentValueName = widget.initialValue.name;
|
||||
_currentValueAutoAway = widget.initialValue.autoAway;
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
@ -82,13 +82,10 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
final initialValueX =
|
||||
widget.initialValueCallback(EditProfileForm.formFieldAvailability)
|
||||
as proto.Availability;
|
||||
final initialValue =
|
||||
initialValueX == proto.Availability.AVAILABILITY_UNSPECIFIED
|
||||
_savedValue.availability == proto.Availability.AVAILABILITY_UNSPECIFIED
|
||||
? proto.Availability.AVAILABILITY_FREE
|
||||
: initialValueX;
|
||||
: _savedValue.availability;
|
||||
|
||||
final availabilities = [
|
||||
proto.Availability.AVAILABILITY_FREE,
|
||||
|
@ -109,7 +106,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
value: x,
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
AvailabilityWidget.availabilityIcon(
|
||||
x, scale.primaryScale.primaryText),
|
||||
x, scale.primaryScale.appText),
|
||||
Text(x == proto.Availability.AVAILABILITY_OFFLINE
|
||||
? translate('availability.always_show_offline')
|
||||
: AvailabilityWidget.availabilityName(x))
|
||||
|
@ -138,6 +135,12 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
.fields[EditProfileForm.formFieldAwayMessage]!.value as String;
|
||||
final busyMessage = _formKey.currentState!
|
||||
.fields[EditProfileForm.formFieldBusyMessage]!.value as String;
|
||||
|
||||
const proto.DataReference? avatar = null;
|
||||
// final avatar = _formKey.currentState!
|
||||
// .fields[EditProfileForm.formFieldAvatar]!.value
|
||||
//as proto.DataReference?;
|
||||
|
||||
final autoAway = _formKey
|
||||
.currentState!.fields[EditProfileForm.formFieldAutoAway]!.value as bool;
|
||||
final autoAwayTimeoutString = _formKey.currentState!
|
||||
|
@ -153,11 +156,21 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
freeMessage: freeMessage,
|
||||
awayMessage: awayMessage,
|
||||
busyMessage: busyMessage,
|
||||
avatar: null,
|
||||
avatar: avatar,
|
||||
autoAway: autoAway,
|
||||
autoAwayTimeout: autoAwayTimeout);
|
||||
}
|
||||
|
||||
// Check if everything is the same and update state
|
||||
void _onChanged() {
|
||||
final currentValue = _makeAccountSpec();
|
||||
_isModified = currentValue != _savedValue;
|
||||
final onModifiedState = widget.onModifiedState;
|
||||
if (onModifiedState != null) {
|
||||
onModifiedState(_isModified);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _editProfileForm(
|
||||
BuildContext context,
|
||||
) {
|
||||
|
@ -176,24 +189,32 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
return FormBuilder(
|
||||
key: _formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
onChanged: _onChanged,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
AvatarWidget(
|
||||
name: _formKey.currentState?.value[EditProfileForm.formFieldName]
|
||||
as String? ??
|
||||
'?',
|
||||
size: 128,
|
||||
borderColor: border,
|
||||
foregroundColor: scale.primaryScale.primaryText,
|
||||
backgroundColor: scale.primaryScale.primary,
|
||||
scaleConfig: scaleConfig,
|
||||
textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64),
|
||||
).paddingLTRB(0, 0, 0, 16),
|
||||
Row(children: [
|
||||
const Spacer(),
|
||||
AvatarWidget(
|
||||
name: _currentValueName,
|
||||
size: 128,
|
||||
borderColor: border,
|
||||
foregroundColor: scale.primaryScale.primaryText,
|
||||
backgroundColor: scale.primaryScale.primary,
|
||||
scaleConfig: scaleConfig,
|
||||
textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64),
|
||||
).paddingLTRB(0, 0, 0, 16),
|
||||
const Spacer()
|
||||
]),
|
||||
FormBuilderTextField(
|
||||
autofocus: true,
|
||||
name: EditProfileForm.formFieldName,
|
||||
initialValue: widget
|
||||
.initialValueCallback(EditProfileForm.formFieldName) as String,
|
||||
initialValue: _savedValue.name,
|
||||
onChanged: (x) {
|
||||
setState(() {
|
||||
_currentValueName = x ?? '';
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: translate('account.form_name'),
|
||||
|
@ -204,23 +225,20 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
FormBuilderValidators.required(),
|
||||
]),
|
||||
textInputAction: TextInputAction.next,
|
||||
).onFocusChange(_onFocusChange),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldPronouns,
|
||||
initialValue:
|
||||
widget.initialValueCallback(EditProfileForm.formFieldPronouns)
|
||||
as String,
|
||||
initialValue: _savedValue.pronouns,
|
||||
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(EditProfileForm.formFieldAbout) as String,
|
||||
initialValue: _savedValue.about,
|
||||
maxLength: 1024,
|
||||
maxLines: 8,
|
||||
minLines: 1,
|
||||
|
@ -229,74 +247,69 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
labelText: translate('account.form_about'),
|
||||
hintText: translate('account.empty_about')),
|
||||
textInputAction: TextInputAction.newline,
|
||||
).onFocusChange(_onFocusChange),
|
||||
_availabilityDropDown(context)
|
||||
.paddingLTRB(0, 0, 0, 16)
|
||||
.onFocusChange(_onFocusChange),
|
||||
),
|
||||
_availabilityDropDown(context).paddingLTRB(0, 0, 0, 16),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldFreeMessage,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldFreeMessage) as String,
|
||||
initialValue: _savedValue.freeMessage,
|
||||
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(
|
||||
EditProfileForm.formFieldAwayMessage) as String,
|
||||
initialValue: _savedValue.awayMessage,
|
||||
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(
|
||||
EditProfileForm.formFieldBusyMessage) as String,
|
||||
initialValue: _savedValue.busyMessage,
|
||||
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(EditProfileForm.formFieldAutoAway)
|
||||
as bool,
|
||||
initialValue: _savedValue.autoAway,
|
||||
side: BorderSide(color: scale.primaryScale.border, width: 2),
|
||||
checkColor: scale.primaryScale.borderText,
|
||||
activeColor: scale.primaryScale.border,
|
||||
title: Text(translate('account.form_auto_away'),
|
||||
style: textTheme.labelMedium),
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_autoAwayEnabled = v ?? false;
|
||||
_currentValueAutoAway = v ?? false;
|
||||
});
|
||||
},
|
||||
).onFocusChange(_onFocusChange),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: EditProfileForm.formFieldAutoAwayTimeout,
|
||||
enabled: _autoAwayEnabled,
|
||||
initialValue: widget.initialValueCallback(
|
||||
EditProfileForm.formFieldAutoAwayTimeout) as String,
|
||||
enabled: _currentValueAutoAway,
|
||||
initialValue: _savedValue.autoAwayTimeout.toString(),
|
||||
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),
|
||||
if (widget.onSubmit != null)
|
||||
]).paddingSymmetric(vertical: 16),
|
||||
Row(children: [
|
||||
const Spacer(),
|
||||
Builder(builder: (context) {
|
||||
final networkReady = context
|
||||
.watch<ConnectionStateCubit>()
|
||||
|
@ -307,7 +320,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
false;
|
||||
|
||||
return ElevatedButton(
|
||||
onPressed: networkReady ? _doSubmit : null,
|
||||
onPressed: (networkReady && _isModified) ? _doSubmit : null,
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
||||
Text(networkReady
|
||||
|
@ -317,36 +330,24 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
]),
|
||||
);
|
||||
}),
|
||||
const Spacer()
|
||||
])
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
singleFuture((this, _kDoSubmitEditProfile), () async {
|
||||
final updatedAccountSpec = _makeAccountSpec();
|
||||
final saved = await onSubmit(updatedAccountSpec);
|
||||
if (saved) {
|
||||
setState(() {
|
||||
_savedValue = updatedAccountSpec;
|
||||
});
|
||||
_onChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -358,5 +359,8 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
|||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
late bool _autoAwayEnabled;
|
||||
late AccountSpec _savedValue;
|
||||
late bool _currentValueAutoAway;
|
||||
late String _currentValueName;
|
||||
bool _isModified = false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue