mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-08-03 03:36:23 -04:00
ui improvements for invitations
This commit is contained in:
parent
d962f98786
commit
01c6490ec4
10 changed files with 179 additions and 77 deletions
|
@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue