mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-06-02 21:23:15 -04:00
249 lines
9.6 KiB
Dart
249 lines
9.6 KiB
Dart
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_form_builder/flutter_form_builder.dart';
|
|
import 'package:flutter_translate/flutter_translate.dart';
|
|
|
|
import '../../proto/proto.dart' as proto;
|
|
import '../../theme/theme.dart';
|
|
import '../models/contact_spec.dart';
|
|
import 'availability_widget.dart';
|
|
|
|
const _kDoSubmitEditContact = 'doSubmitEditContact';
|
|
|
|
class EditContactForm extends StatefulWidget {
|
|
const EditContactForm({
|
|
required this.contact,
|
|
required this.onSubmit,
|
|
required this.submitText,
|
|
required this.submitDisabledText,
|
|
this.onModifiedState,
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
State createState() => _EditContactFormState();
|
|
|
|
final proto.Contact contact;
|
|
final String submitText;
|
|
final String submitDisabledText;
|
|
final Future<bool> Function(ContactSpec) onSubmit;
|
|
final void Function(bool)? onModifiedState;
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties
|
|
..add(ObjectFlagProperty<Future<bool> Function(ContactSpec p1)>.has(
|
|
'onSubmit', onSubmit))
|
|
..add(ObjectFlagProperty<void Function(bool p1)?>.has(
|
|
'onModifiedState', onModifiedState))
|
|
..add(DiagnosticsProperty<proto.Contact>('contact', contact))
|
|
..add(StringProperty('submitText', submitText))
|
|
..add(StringProperty('submitDisabledText', submitDisabledText));
|
|
}
|
|
|
|
static const String formFieldNickname = 'nickname';
|
|
static const String formFieldNotes = 'notes';
|
|
static const String formFieldShowAvailability = 'show_availability';
|
|
}
|
|
|
|
class _EditContactFormState extends State<EditContactForm> {
|
|
final _formKey = GlobalKey<FormBuilderState>();
|
|
|
|
@override
|
|
void initState() {
|
|
_savedValue = ContactSpec.fromProto(widget.contact);
|
|
_currentValueNickname = _savedValue.nickname;
|
|
|
|
super.initState();
|
|
}
|
|
|
|
ContactSpec _makeContactSpec() {
|
|
final nickname = _formKey.currentState!
|
|
.fields[EditContactForm.formFieldNickname]!.value as String;
|
|
final notes = _formKey
|
|
.currentState!.fields[EditContactForm.formFieldNotes]!.value as String;
|
|
final showAvailability = _formKey.currentState!
|
|
.fields[EditContactForm.formFieldShowAvailability]!.value as bool;
|
|
|
|
return ContactSpec(
|
|
nickname: nickname, notes: notes, showAvailability: showAvailability);
|
|
}
|
|
|
|
// Check if everything is the same and update state
|
|
void _onChanged() {
|
|
final currentValue = _makeContactSpec();
|
|
_isModified = currentValue != _savedValue;
|
|
final onModifiedState = widget.onModifiedState;
|
|
if (onModifiedState != null) {
|
|
onModifiedState(_isModified);
|
|
}
|
|
}
|
|
|
|
Widget _availabilityWidget(proto.Availability availability, Color color) =>
|
|
AvailabilityWidget(
|
|
availability: availability,
|
|
color: color,
|
|
vertical: false,
|
|
);
|
|
|
|
Widget _editContactForm(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final scale = theme.extension<ScaleScheme>()!;
|
|
final scaleConfig = theme.extension<ScaleConfig>()!;
|
|
final textTheme = theme.textTheme;
|
|
|
|
late final Color border;
|
|
if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) {
|
|
border = scale.primaryScale.elementBackground;
|
|
} else {
|
|
border = scale.primaryScale.subtleBorder;
|
|
}
|
|
|
|
return FormBuilder(
|
|
key: _formKey,
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
onChanged: _onChanged,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
styledCard(
|
|
context: context,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(children: [
|
|
const Spacer(),
|
|
AvatarWidget(
|
|
name: _currentValueNickname.isNotEmpty
|
|
? _currentValueNickname
|
|
: widget.contact.profile.name,
|
|
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()
|
|
]),
|
|
SelectableText(widget.contact.profile.name,
|
|
style: textTheme.bodyLarge)
|
|
.noEditDecoratorLabel(
|
|
context,
|
|
translate('contact_form.form_name'),
|
|
)
|
|
.paddingSymmetric(vertical: 4),
|
|
SelectableText(widget.contact.profile.pronouns,
|
|
style: textTheme.bodyLarge)
|
|
.noEditDecoratorLabel(
|
|
context,
|
|
translate('contact_form.form_pronouns'),
|
|
)
|
|
.paddingSymmetric(vertical: 4),
|
|
Row(mainAxisSize: MainAxisSize.min, children: [
|
|
_availabilityWidget(
|
|
widget.contact.profile.availability,
|
|
scale.primaryScale.appText),
|
|
SelectableText(widget.contact.profile.status,
|
|
style: textTheme.bodyMedium)
|
|
.paddingSymmetric(horizontal: 8)
|
|
])
|
|
.noEditDecoratorLabel(
|
|
context,
|
|
translate('contact_form.form_status'),
|
|
)
|
|
.paddingSymmetric(vertical: 4),
|
|
SelectableText(widget.contact.profile.about,
|
|
minLines: 1,
|
|
maxLines: 8,
|
|
style: textTheme.bodyMedium)
|
|
.noEditDecoratorLabel(
|
|
context,
|
|
translate('contact_form.form_about'),
|
|
)
|
|
.paddingSymmetric(vertical: 4),
|
|
SelectableText(
|
|
widget.contact.identityPublicKey.value
|
|
.toVeilid()
|
|
.toString(),
|
|
style: textTheme.bodyMedium!
|
|
.copyWith(fontFamily: 'Source Code Pro'))
|
|
.noEditDecoratorLabel(
|
|
context,
|
|
translate('contact_form.form_fingerprint'),
|
|
)
|
|
.paddingSymmetric(vertical: 4),
|
|
]).paddingAll(16))
|
|
.paddingLTRB(0, 0, 0, 16),
|
|
FormBuilderTextField(
|
|
name: EditContactForm.formFieldNickname,
|
|
initialValue: _currentValueNickname,
|
|
onChanged: (x) {
|
|
setState(() {
|
|
_currentValueNickname = x ?? '';
|
|
});
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: translate('contact_form.form_nickname')),
|
|
maxLength: 64,
|
|
textInputAction: TextInputAction.next,
|
|
),
|
|
FormBuilderCheckbox(
|
|
name: EditContactForm.formFieldShowAvailability,
|
|
initialValue: _savedValue.showAvailability,
|
|
side: BorderSide(color: scale.primaryScale.border, width: 2),
|
|
checkColor: scale.primaryScale.borderText,
|
|
activeColor: scale.primaryScale.border,
|
|
title: Text(translate('contact_form.form_show_availability'),
|
|
style: textTheme.labelMedium),
|
|
),
|
|
FormBuilderTextField(
|
|
name: EditContactForm.formFieldNotes,
|
|
initialValue: _savedValue.notes,
|
|
minLines: 1,
|
|
maxLines: 8,
|
|
maxLength: 1024,
|
|
decoration: InputDecoration(
|
|
labelText: translate('contact_form.form_notes')),
|
|
textInputAction: TextInputAction.newline,
|
|
),
|
|
ElevatedButton(
|
|
onPressed: _isModified ? _doSubmit : null,
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
|
Text(widget.submitText).paddingLTRB(0, 0, 4, 0)
|
|
]),
|
|
).paddingSymmetric(vertical: 4).alignAtCenter(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _doSubmit() {
|
|
final onSubmit = widget.onSubmit;
|
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
|
singleFuture((this, _kDoSubmitEditContact), () async {
|
|
final updatedContactSpec = _makeContactSpec();
|
|
final saved = await onSubmit(updatedContactSpec);
|
|
if (saved) {
|
|
setState(() {
|
|
_savedValue = updatedContactSpec;
|
|
});
|
|
_onChanged();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => _editContactForm(context);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
late ContactSpec _savedValue;
|
|
late String _currentValueNickname;
|
|
bool _isModified = false;
|
|
}
|