ui improvements for invitations

This commit is contained in:
Christien Rioux 2024-07-27 18:36:06 -04:00
parent d962f98786
commit 01c6490ec4
10 changed files with 179 additions and 77 deletions

View file

@ -11,6 +11,7 @@ import 'package:provider/provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../notifications/notifications.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
@ -20,17 +21,20 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
const ContactInvitationDisplayDialog._({
required this.locator,
required this.message,
required this.fingerprint,
});
final Locator locator;
final String message;
final String fingerprint;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(StringProperty('message', message))
..add(DiagnosticsProperty<Locator>('locator', locator));
..add(DiagnosticsProperty<Locator>('locator', locator))
..add(StringProperty('fingerprint', fingerprint));
}
String makeTextInvite(String message, Uint8List data) {
@ -38,10 +42,12 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
base64UrlNoPadEncode(data), '\n', 40,
repeat: true);
final msg = message.isNotEmpty ? '$message\n' : '';
return '$msg'
'--- BEGIN VEILIDCHAT CONTACT INVITE ----\n'
'$invite\n'
'---- END VEILIDCHAT CONTACT INVITE -----\n';
'---- END VEILIDCHAT CONTACT INVITE -----\n'
'Fingerprint:\n$fingerprint\n';
}
@override
@ -97,18 +103,27 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
.copyWith(color: Colors.black)))
.paddingAll(8),
FittedBox(
child: QrImageView.withQr(
size: 300,
qr: QrCode.fromUint8List(
data: data.$1,
errorCorrectLevel:
QrErrorCorrectLevel.L)))
.expanded(),
child: QrImageView.withQr(
size: 300,
qr: QrCode.fromUint8List(
data: data.$1,
errorCorrectLevel:
QrErrorCorrectLevel.L)),
).expanded(),
Text(message,
softWrap: true,
style: textTheme.labelLarge!
.copyWith(color: Colors.black))
.paddingAll(8),
Text(
'${translate('create_invitation_dialog.fingerprint')}\n'
'$fingerprint',
softWrap: true,
textAlign: TextAlign.center,
style: textTheme.labelSmall!.copyWith(
color: Colors.black,
fontFamily: 'Source Code Pro'))
.paddingAll(2),
ElevatedButton.icon(
icon: const Icon(Icons.copy),
style: ElevatedButton.styleFrom(
@ -129,11 +144,15 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
error: errorPage)))));
}
static Future<void> show(
{required BuildContext context,
required Locator locator,
required InvitationGeneratorCubit Function(BuildContext) create,
required String message}) async {
static Future<void> show({
required BuildContext context,
required Locator locator,
required InvitationGeneratorCubit Function(BuildContext) create,
required String message,
}) async {
final fingerprint =
locator<AccountInfoCubit>().state.identityPublicKey.toString();
await showPopControlDialog<void>(
context: context,
builder: (context) => BlocProvider(
@ -141,6 +160,7 @@ class ContactInvitationDisplayDialog extends StatelessWidget {
child: ContactInvitationDisplayDialog._(
locator: locator,
message: message,
fingerprint: fingerprint,
)));
}
}

View file

@ -37,8 +37,7 @@ class CreateInvitationDialog extends StatefulWidget {
}
class CreateInvitationDialogState extends State<CreateInvitationDialog> {
final _messageTextController = TextEditingController(
text: translate('create_invitation_dialog.connect_with_me'));
late final TextEditingController _messageTextController;
EncryptionKeyType _encryptionKeyType = EncryptionKeyType.none;
String _encryptionKey = '';
@ -46,6 +45,12 @@ class CreateInvitationDialogState extends State<CreateInvitationDialog> {
@override
void initState() {
final accountInfo = widget.locator<AccountRecordCubit>().state;
final name = accountInfo.asData?.value.profile.name ??
translate('create_invitation_dialog.me');
_messageTextController = TextEditingController(
text: translate('create_invitation_dialog.connect_with_me',
args: {'name': name}));
super.initState();
}
@ -152,13 +157,13 @@ class CreateInvitationDialogState extends State<CreateInvitationDialog> {
message: _messageTextController.text,
expiration: _expiration);
navigator.pop();
await ContactInvitationDisplayDialog.show(
context: context,
locator: widget.locator,
message: _messageTextController.text,
create: (context) => InvitationGeneratorCubit(generator));
navigator.pop();
}
@override

View file

@ -1,5 +1,6 @@
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';
@ -61,17 +62,19 @@ class InvitationDialog extends StatefulWidget {
}
class InvitationDialogState extends State<InvitationDialog> {
ValidContactInvitation? _validInvitation;
bool _isValidating = false;
bool _isAccepting = false;
@override
void initState() {
super.initState();
}
bool get isValidating => _isValidating;
bool get isAccepting => _isAccepting;
Future<void> _onCancel() async {
final navigator = Navigator.of(context);
_cancelRequest.cancel();
setState(() {
_isAccepting = false;
});
navigator.pop();
}
Future<void> _onAccept() async {
final navigator = Navigator.of(context);
@ -153,6 +156,7 @@ class InvitationDialogState extends State<InvitationDialog> {
final validatedContactInvitation =
await contactInvitationListCubit.validateInvitation(
inviteData: inviteData,
cancelRequest: _cancelRequest,
getEncryptionKeyCallback:
(cs, encryptionKeyType, encryptedSecret) async {
String encryptionKey;
@ -234,6 +238,9 @@ class InvitationDialogState extends State<InvitationDialog> {
late final String errorText;
if (e is VeilidAPIExceptionTryAgain) {
errorText = translate('invitation_dialog.try_again_online');
}
if (e is VeilidAPIExceptionKeyNotFound) {
errorText = translate('invitation_dialog.key_not_found');
} else {
errorText = translate('invitation_dialog.invalid_invitation');
}
@ -245,6 +252,12 @@ class InvitationDialogState extends State<InvitationDialog> {
_validInvitation = null;
widget.onValidationFailed();
});
} on CancelException {
setState(() {
_isValidating = false;
_validInvitation = null;
widget.onValidationCancelled();
});
} on Exception catch (e) {
log.debug('exception: $e', e);
setState(() {
@ -264,6 +277,11 @@ class InvitationDialogState extends State<InvitationDialog> {
Text(translate('invitation_dialog.validating'))
.paddingLTRB(0, 0, 0, 16),
buildProgressIndicator().paddingAll(16),
ElevatedButton.icon(
icon: const Icon(Icons.cancel),
label: Text(translate('button.cancel')),
onPressed: _onCancel,
).paddingAll(16),
]).toCenter(),
if (_validInvitation == null &&
!_isValidating &&
@ -315,13 +333,25 @@ class InvitationDialogState extends State<InvitationDialog> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: _isAccepting
? [buildProgressIndicator().paddingAll(16)]
? [
buildProgressIndicator().paddingAll(16),
]
: _buildPreAccept()),
),
);
return PopControl(dismissible: dismissible, child: dialog);
}
////////////////////////////////////////////////////////////////////////////
ValidContactInvitation? _validInvitation;
bool _isValidating = false;
bool _isAccepting = false;
final _cancelRequest = CancelRequest();
bool get isValidating => _isValidating;
bool get isAccepting => _isAccepting;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);