mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-12-24 07:09:29 -05:00
navigation cleanup
This commit is contained in:
parent
5da68b2d94
commit
b3e9cbd4f3
@ -60,16 +60,16 @@
|
||||
},
|
||||
"accounts_menu": {
|
||||
"invite_contact": "Invite Contact",
|
||||
"create_invite": "Create Invite",
|
||||
"scan_invite": "Scan Invite",
|
||||
"paste_invite": "Paste Invite"
|
||||
"create_invite": "Create Invitation",
|
||||
"scan_invite": "Scan Invitation",
|
||||
"paste_invite": "Paste Invitation"
|
||||
},
|
||||
"send_invite_dialog": {
|
||||
"title": "Send Contact Invite",
|
||||
"create_invitation_dialog": {
|
||||
"title": "Create Contact Invitation",
|
||||
"connect_with_me": "Connect with me on VeilidChat!",
|
||||
"enter_message_hint": "enter message for contact (optional)",
|
||||
"enter_message_hint": "Enter message for contact (optional)",
|
||||
"message_to_contact": "Message to send with invitation (not encrypted)",
|
||||
"generate": "Generate Invite",
|
||||
"generate": "Generate Invitation",
|
||||
"message": "Message",
|
||||
"unlocked": "Unlocked",
|
||||
"pin": "PIN",
|
||||
@ -85,23 +85,23 @@
|
||||
"copy_invitation": "Copy Invitation",
|
||||
"invitation_copied": "Invitation Copied"
|
||||
},
|
||||
"invite_dialog": {
|
||||
"invitation_dialog": {
|
||||
"message_from_contact": "Message from contact",
|
||||
"validating": "Validating...",
|
||||
"failed_to_accept": "Failed to accept contact invite",
|
||||
"failed_to_reject": "Failed to reject contact invite",
|
||||
"failed_to_accept": "Failed to accept contact invitation",
|
||||
"failed_to_reject": "Failed to reject contact invitation",
|
||||
"invalid_invitation": "Invalid invitation",
|
||||
"protected_with_pin": "Contact invite is protected with a PIN",
|
||||
"protected_with_password": "Contact invite is protected with a password",
|
||||
"protected_with_pin": "Contact invitation is protected with a PIN",
|
||||
"protected_with_password": "Contact invitation is protected with a password",
|
||||
"invalid_pin": "Invalid PIN",
|
||||
"invalid_password": "Invalid password"
|
||||
},
|
||||
"paste_invite_dialog": {
|
||||
"paste_invitation_dialog": {
|
||||
"title": "Paste Contact Invite",
|
||||
"paste_invite_here": "Paste your contact invite here:",
|
||||
"paste": "Paste"
|
||||
},
|
||||
"scan_invite_dialog": {
|
||||
"scan_invitation_dialog": {
|
||||
"title": "Scan Contact Invite",
|
||||
"instructions": "Position the contact invite QR code in the frame",
|
||||
"scan_qr_here": "Click here to scan a contact invite QR code:",
|
||||
|
68
lib/app.dart
68
lib/app.dart
@ -38,37 +38,37 @@ class VeilidChatApp extends StatelessWidget {
|
||||
// Once init is done, we proceed with the app
|
||||
final localizationDelegate = LocalizedApp.of(context).delegate;
|
||||
return ThemeProvider(
|
||||
initTheme: initialThemeData,
|
||||
builder: (_, theme) => LocalizationProvider(
|
||||
state: LocalizationProvider.of(context).state,
|
||||
child: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<ConnectionStateCubit>(
|
||||
create: (context) => ConnectionStateCubit(
|
||||
ProcessorRepository.instance)),
|
||||
BlocProvider<RouterCubit>(
|
||||
create: (context) =>
|
||||
RouterCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<LocalAccountsCubit>(
|
||||
create: (context) =>
|
||||
LocalAccountsCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<UserLoginsCubit>(
|
||||
create: (context) =>
|
||||
UserLoginsCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<ActiveLocalAccountCubit>(
|
||||
create: (context) => ActiveLocalAccountCubit(
|
||||
AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<PreferencesCubit>(
|
||||
create: (context) =>
|
||||
PreferencesCubit(PreferencesRepository.instance),
|
||||
)
|
||||
],
|
||||
child: BackgroundTicker(
|
||||
builder: (context) => MaterialApp.router(
|
||||
initTheme: initialThemeData,
|
||||
builder: (_, theme) => LocalizationProvider(
|
||||
state: LocalizationProvider.of(context).state,
|
||||
child: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<ConnectionStateCubit>(
|
||||
create: (context) =>
|
||||
ConnectionStateCubit(ProcessorRepository.instance)),
|
||||
BlocProvider<RouterCubit>(
|
||||
create: (context) =>
|
||||
RouterCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<LocalAccountsCubit>(
|
||||
create: (context) =>
|
||||
LocalAccountsCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<UserLoginsCubit>(
|
||||
create: (context) =>
|
||||
UserLoginsCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<ActiveLocalAccountCubit>(
|
||||
create: (context) =>
|
||||
ActiveLocalAccountCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<PreferencesCubit>(
|
||||
create: (context) =>
|
||||
PreferencesCubit(PreferencesRepository.instance),
|
||||
)
|
||||
],
|
||||
child: BackgroundTicker(
|
||||
builder: (context) => MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
routerConfig: context.watch<RouterCubit>().router(),
|
||||
title: translate('app.title'),
|
||||
@ -82,9 +82,9 @@ class VeilidChatApp extends StatelessWidget {
|
||||
supportedLocales:
|
||||
localizationDelegate.supportedLocales,
|
||||
locale: localizationDelegate.currentLocale,
|
||||
),
|
||||
)),
|
||||
));
|
||||
)),
|
||||
)),
|
||||
);
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -135,7 +135,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
// Called when the local messages list gets a change
|
||||
void _updateLocalMessagesState(
|
||||
BlocBusyState<AsyncValue<IList<proto.Message>>> avmessages) {
|
||||
final localMessages = avmessages.state.data?.value;
|
||||
final localMessages = avmessages.state.asData?.value;
|
||||
if (localMessages == null) {
|
||||
return;
|
||||
}
|
||||
@ -147,7 +147,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
// Called when the remote messages list gets a change
|
||||
void _updateRemoteMessagesState(
|
||||
BlocBusyState<AsyncValue<IList<proto.Message>>> avmessages) {
|
||||
final remoteMessages = avmessages.state.data?.value;
|
||||
final remoteMessages = avmessages.state.asData?.value;
|
||||
if (remoteMessages == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -43,12 +43,12 @@ class ChatComponent extends StatelessWidget {
|
||||
// Get all watched dependendies
|
||||
final activeAccountInfo = context.watch<ActiveAccountInfo>();
|
||||
final accountRecordInfo =
|
||||
context.watch<AccountRecordCubit>().state.data?.value;
|
||||
context.watch<AccountRecordCubit>().state.asData?.value;
|
||||
if (accountRecordInfo == null) {
|
||||
return debugPage('should always have an account record here');
|
||||
}
|
||||
final contactList =
|
||||
context.watch<ContactListCubit>().state.state.data?.value;
|
||||
context.watch<ContactListCubit>().state.state.asData?.value;
|
||||
if (contactList == null) {
|
||||
return debugPage('should always have a contact list here');
|
||||
}
|
||||
@ -58,7 +58,7 @@ class ChatComponent extends StatelessWidget {
|
||||
if (avconversation == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
final conversation = avconversation.data?.value;
|
||||
final conversation = avconversation.asData?.value;
|
||||
if (conversation == null) {
|
||||
return avconversation.buildNotData();
|
||||
}
|
||||
@ -140,7 +140,7 @@ class ChatComponent extends StatelessWidget {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final chatTheme = makeChatTheme(scale, textTheme);
|
||||
|
||||
final messages = _messagesState.data?.value;
|
||||
final messages = _messagesState.asData?.value;
|
||||
if (messages == null) {
|
||||
return _messagesState.buildNotData();
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
|
||||
@override
|
||||
Future<void> updateState(TypedKey key, proto.Chat value) async {
|
||||
final contactList = _contactListCubit.state.state.data?.value;
|
||||
final contactList = _contactListCubit.state.state.asData?.value;
|
||||
if (contactList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
|
@ -56,7 +56,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
Future<void> updateState(
|
||||
TypedKey key, AsyncValue<ActiveConversationState> value) async {
|
||||
// Get the contact object for this single contact chat
|
||||
final contactList = _contactListCubit.state.state.data?.value;
|
||||
final contactList = _contactListCubit.state.state.asData?.value;
|
||||
if (contactList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
@ -71,7 +71,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
final contact = contactList[contactIndex];
|
||||
|
||||
// Get the chat object for this single contact chat
|
||||
final chatList = _chatListCubit.state.state.data?.value;
|
||||
final chatList = _chatListCubit.state.state.asData?.value;
|
||||
if (chatList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
|
@ -130,7 +130,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
/// StateMapFollowable /////////////////////////
|
||||
@override
|
||||
IMap<TypedKey, proto.Chat> getStateMap(ChatListCubitState state) {
|
||||
final stateValue = state.state.data?.value;
|
||||
final stateValue = state.state.asData?.value;
|
||||
if (stateValue == null) {
|
||||
return IMap();
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ class ContactInvitationListCubit
|
||||
// inbox with our list of extant invitations
|
||||
// If we're chatting to ourselves,
|
||||
// we are validating an invitation we have created
|
||||
final isSelf = state.state.data!.value.indexWhere((cir) =>
|
||||
final isSelf = state.state.asData!.value.indexWhere((cir) =>
|
||||
cir.contactRequestInbox.recordKey.toVeilid() ==
|
||||
contactRequestInboxKey) !=
|
||||
-1;
|
||||
@ -310,7 +310,7 @@ class ContactInvitationListCubit
|
||||
@override
|
||||
IMap<TypedKey, proto.ContactInvitationRecord> getStateMap(
|
||||
ContactInvitiationListState state) {
|
||||
final stateValue = state.state.data?.value;
|
||||
final stateValue = state.state.asData?.value;
|
||||
if (stateValue == null) {
|
||||
return IMap();
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||
var retryCount = 20;
|
||||
do {
|
||||
await conversation.refresh();
|
||||
remoteConversation = conversation.state.data?.value.remoteConversation;
|
||||
remoteConversation = conversation.state.asData?.value.remoteConversation;
|
||||
if (remoteConversation != null) {
|
||||
break;
|
||||
}
|
||||
|
@ -5,47 +5,29 @@ import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
class ContactInvitationDisplayDialog extends StatefulWidget {
|
||||
const ContactInvitationDisplayDialog({
|
||||
class ContactInvitationDisplayDialog extends StatelessWidget {
|
||||
const ContactInvitationDisplayDialog._({
|
||||
required this.modalContext,
|
||||
required this.message,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final BuildContext modalContext;
|
||||
final String message;
|
||||
|
||||
@override
|
||||
State<ContactInvitationDisplayDialog> createState() =>
|
||||
_ContactInvitationDisplayDialogState();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(StringProperty('message', message));
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactInvitationDisplayDialogState
|
||||
extends State<ContactInvitationDisplayDialog> {
|
||||
final focusNode = FocusNode();
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
focusNode.dispose();
|
||||
super.dispose();
|
||||
properties
|
||||
..add(StringProperty('message', message))
|
||||
..add(DiagnosticsProperty<BuildContext>('modalContext', modalContext));
|
||||
}
|
||||
|
||||
String makeTextInvite(String message, Uint8List data) {
|
||||
@ -72,61 +54,67 @@ class _ContactInvitationDisplayDialogState
|
||||
final cardsize =
|
||||
min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400);
|
||||
|
||||
return Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: cardsize,
|
||||
maxWidth: cardsize,
|
||||
minHeight: cardsize,
|
||||
maxHeight: cardsize),
|
||||
child: signedContactInvitationBytesV.when(
|
||||
loading: buildProgressIndicator,
|
||||
data: (data) => Form(
|
||||
key: formKey,
|
||||
child: Column(children: [
|
||||
FittedBox(
|
||||
child: Text(
|
||||
return PopControl(
|
||||
dismissible: !signedContactInvitationBytesV.isLoading,
|
||||
child: Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: cardsize,
|
||||
maxWidth: cardsize,
|
||||
minHeight: cardsize,
|
||||
maxHeight: cardsize),
|
||||
child: signedContactInvitationBytesV.when(
|
||||
loading: buildProgressIndicator,
|
||||
data: (data) => Column(children: [
|
||||
FittedBox(
|
||||
child: Text(
|
||||
translate(
|
||||
'create_invitation_dialog.contact_invitation'),
|
||||
style: textTheme.headlineSmall!
|
||||
.copyWith(color: Colors.black)))
|
||||
.paddingAll(8),
|
||||
FittedBox(
|
||||
child: QrImageView.withQr(
|
||||
size: 300,
|
||||
qr: QrCode.fromUint8List(
|
||||
data: data,
|
||||
errorCorrectLevel:
|
||||
QrErrorCorrectLevel.L)))
|
||||
.expanded(),
|
||||
Text(message,
|
||||
softWrap: true,
|
||||
style: textTheme.labelLarge!
|
||||
.copyWith(color: Colors.black))
|
||||
.paddingAll(8),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.copy),
|
||||
label: Text(translate(
|
||||
'create_invitation_dialog.copy_invitation')),
|
||||
onPressed: () async {
|
||||
showInfoToast(
|
||||
context,
|
||||
translate(
|
||||
'send_invite_dialog.contact_invitation'),
|
||||
style: textTheme.headlineSmall!
|
||||
.copyWith(color: Colors.black)))
|
||||
.paddingAll(8),
|
||||
FittedBox(
|
||||
child: QrImageView.withQr(
|
||||
size: 300,
|
||||
qr: QrCode.fromUint8List(
|
||||
data: data,
|
||||
errorCorrectLevel:
|
||||
QrErrorCorrectLevel.L)))
|
||||
.expanded(),
|
||||
Text(widget.message,
|
||||
softWrap: true,
|
||||
style: textTheme.labelLarge!
|
||||
.copyWith(color: Colors.black))
|
||||
.paddingAll(8),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.copy),
|
||||
label: Text(
|
||||
translate('send_invite_dialog.copy_invitation')),
|
||||
onPressed: () async {
|
||||
showInfoToast(
|
||||
context,
|
||||
translate(
|
||||
'send_invite_dialog.invitation_copied'));
|
||||
await Clipboard.setData(ClipboardData(
|
||||
text: makeTextInvite(widget.message, data)));
|
||||
},
|
||||
).paddingAll(16),
|
||||
])),
|
||||
error: errorPage)));
|
||||
'create_invitation_dialog.invitation_copied'));
|
||||
await Clipboard.setData(ClipboardData(
|
||||
text: makeTextInvite(message, data)));
|
||||
},
|
||||
).paddingAll(16),
|
||||
]),
|
||||
error: errorPage))));
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty<FocusNode>('focusNode', focusNode))
|
||||
..add(DiagnosticsProperty<GlobalKey<FormState>>('formKey', formKey));
|
||||
static Future<void> show(
|
||||
{required BuildContext context,
|
||||
required InvitationGeneratorCubit Function(BuildContext) create,
|
||||
required String message}) async {
|
||||
await showPopControlDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider(
|
||||
create: create,
|
||||
child: ContactInvitationDisplayDialog._(
|
||||
modalContext: context,
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -105,15 +105,12 @@ class ContactInvitationItemWidget extends StatelessWidget {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
await showDialog<void>(
|
||||
await ContactInvitationDisplayDialog.show(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider(
|
||||
create: (context) => InvitationGeneratorCubit
|
||||
.value(Uint8List.fromList(
|
||||
contactInvitationRecord.invitation)),
|
||||
child: ContactInvitationDisplayDialog(
|
||||
message: contactInvitationRecord.message,
|
||||
)));
|
||||
message: contactInvitationRecord.message,
|
||||
create: (context) => InvitationGeneratorCubit.value(
|
||||
Uint8List.fromList(
|
||||
contactInvitationRecord.invitation)));
|
||||
},
|
||||
title: Text(
|
||||
contactInvitationRecord.message.isEmpty
|
||||
|
@ -13,17 +13,17 @@ import '../../account_manager/account_manager.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
class SendInviteDialog extends StatefulWidget {
|
||||
const SendInviteDialog({required this.modalContext, super.key});
|
||||
class CreateInvitationDialog extends StatefulWidget {
|
||||
const CreateInvitationDialog._({required this.modalContext});
|
||||
|
||||
@override
|
||||
SendInviteDialogState createState() => SendInviteDialogState();
|
||||
CreateInvitationDialogState createState() => CreateInvitationDialogState();
|
||||
|
||||
static Future<void> show(BuildContext context) async {
|
||||
await showStyledDialog<void>(
|
||||
await StyledDialog.show<void>(
|
||||
context: context,
|
||||
title: translate('send_invite_dialog.title'),
|
||||
child: SendInviteDialog(modalContext: context));
|
||||
title: translate('create_invitation_dialog.title'),
|
||||
child: CreateInvitationDialog._(modalContext: context));
|
||||
}
|
||||
|
||||
final BuildContext modalContext;
|
||||
@ -36,9 +36,9 @@ class SendInviteDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
class CreateInvitationDialogState extends State<CreateInvitationDialog> {
|
||||
final _messageTextController = TextEditingController(
|
||||
text: translate('send_invite_dialog.connect_with_me'));
|
||||
text: translate('create_invitation_dialog.connect_with_me'));
|
||||
|
||||
EncryptionKeyType _encryptionKeyType = EncryptionKeyType.none;
|
||||
String _encryptionKey = '';
|
||||
@ -58,7 +58,7 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
}
|
||||
|
||||
Future<void> _onPinEncryptionSelected(bool selected) async {
|
||||
final description = translate('send_invite_dialog.pin_description');
|
||||
final description = translate('create_invitation_dialog.pin_description');
|
||||
final pin = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
@ -87,7 +87,7 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
return;
|
||||
}
|
||||
showErrorToast(
|
||||
context, translate('send_invite_dialog.pin_does_not_match'));
|
||||
context, translate('create_invitation_dialog.pin_does_not_match'));
|
||||
setState(() {
|
||||
_encryptionKeyType = EncryptionKeyType.none;
|
||||
_encryptionKey = '';
|
||||
@ -96,7 +96,8 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
}
|
||||
|
||||
Future<void> _onPasswordEncryptionSelected(bool selected) async {
|
||||
final description = translate('send_invite_dialog.password_description');
|
||||
final description =
|
||||
translate('create_invitation_dialog.password_description');
|
||||
final password = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => EnterPasswordDialog(description: description));
|
||||
@ -123,8 +124,8 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
showErrorToast(
|
||||
context, translate('send_invite_dialog.password_does_not_match'));
|
||||
showErrorToast(context,
|
||||
translate('create_invitation_dialog.password_does_not_match'));
|
||||
setState(() {
|
||||
_encryptionKeyType = EncryptionKeyType.none;
|
||||
_encryptionKey = '';
|
||||
@ -145,13 +146,10 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
message: _messageTextController.text,
|
||||
expiration: _expiration);
|
||||
|
||||
await showDialog<void>(
|
||||
await ContactInvitationDisplayDialog.show(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider(
|
||||
create: (context) => InvitationGeneratorCubit(generator),
|
||||
child: ContactInvitationDisplayDialog(
|
||||
message: _messageTextController.text,
|
||||
)));
|
||||
message: _messageTextController.text,
|
||||
create: (context) => InvitationGeneratorCubit(generator));
|
||||
|
||||
navigator.pop();
|
||||
}
|
||||
@ -176,7 +174,7 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
translate('send_invite_dialog.message_to_contact'),
|
||||
translate('create_invitation_dialog.message_to_contact'),
|
||||
).paddingAll(8),
|
||||
TextField(
|
||||
controller: _messageTextController,
|
||||
@ -185,26 +183,27 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: translate('send_invite_dialog.enter_message_hint'),
|
||||
labelText: translate('send_invite_dialog.message')),
|
||||
hintText:
|
||||
translate('create_invitation_dialog.enter_message_hint'),
|
||||
labelText: translate('create_invitation_dialog.message')),
|
||||
).paddingAll(8),
|
||||
const SizedBox(height: 10),
|
||||
Text(translate('send_invite_dialog.protect_this_invitation'),
|
||||
Text(translate('create_invitation_dialog.protect_this_invitation'),
|
||||
style: textTheme.labelLarge)
|
||||
.paddingAll(8),
|
||||
Wrap(spacing: 5, children: [
|
||||
ChoiceChip(
|
||||
label: Text(translate('send_invite_dialog.unlocked')),
|
||||
label: Text(translate('create_invitation_dialog.unlocked')),
|
||||
selected: _encryptionKeyType == EncryptionKeyType.none,
|
||||
onSelected: _onNoneEncryptionSelected,
|
||||
),
|
||||
ChoiceChip(
|
||||
label: Text(translate('send_invite_dialog.pin')),
|
||||
label: Text(translate('create_invitation_dialog.pin')),
|
||||
selected: _encryptionKeyType == EncryptionKeyType.pin,
|
||||
onSelected: _onPinEncryptionSelected,
|
||||
),
|
||||
ChoiceChip(
|
||||
label: Text(translate('send_invite_dialog.password')),
|
||||
label: Text(translate('create_invitation_dialog.password')),
|
||||
selected: _encryptionKeyType == EncryptionKeyType.password,
|
||||
onSelected: _onPasswordEncryptionSelected,
|
||||
)
|
||||
@ -216,13 +215,13 @@ class SendInviteDialogState extends State<SendInviteDialog> {
|
||||
child: ElevatedButton(
|
||||
onPressed: _onGenerateButtonPressed,
|
||||
child: Text(
|
||||
translate('send_invite_dialog.generate'),
|
||||
translate('create_invitation_dialog.generate'),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(translate('send_invite_dialog.note')).paddingAll(8),
|
||||
Text(translate('create_invitation_dialog.note')).paddingAll(8),
|
||||
Text(
|
||||
translate('send_invite_dialog.note_text'),
|
||||
translate('create_invitation_dialog.note_text'),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
).paddingAll(8),
|
||||
],
|
@ -11,8 +11,8 @@ import '../../contacts/contacts.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
class InviteDialog extends StatefulWidget {
|
||||
const InviteDialog(
|
||||
class InvitationDialog extends StatefulWidget {
|
||||
const InvitationDialog(
|
||||
{required this.modalContext,
|
||||
required this.onValidationCancelled,
|
||||
required this.onValidationSuccess,
|
||||
@ -27,13 +27,13 @@ class InviteDialog extends StatefulWidget {
|
||||
final bool Function() inviteControlIsValid;
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
InviteDialogState dialogState,
|
||||
InvitationDialogState dialogState,
|
||||
Future<void> Function({required Uint8List inviteData})
|
||||
validateInviteData) buildInviteControl;
|
||||
final BuildContext modalContext;
|
||||
|
||||
@override
|
||||
InviteDialogState createState() => InviteDialogState();
|
||||
InvitationDialogState createState() => InvitationDialogState();
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
@ -49,7 +49,7 @@ class InviteDialog extends StatefulWidget {
|
||||
..add(ObjectFlagProperty<
|
||||
Widget Function(
|
||||
BuildContext context,
|
||||
InviteDialogState dialogState,
|
||||
InvitationDialogState dialogState,
|
||||
Future<void> Function({required Uint8List inviteData})
|
||||
validateInviteData)>.has(
|
||||
'buildInviteControl', buildInviteControl))
|
||||
@ -57,7 +57,7 @@ class InviteDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class InviteDialogState extends State<InviteDialog> {
|
||||
class InvitationDialogState extends State<InvitationDialog> {
|
||||
ValidContactInvitation? _validInvitation;
|
||||
bool _isValidating = false;
|
||||
bool _isAccepting = false;
|
||||
@ -98,8 +98,8 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
showErrorToast(context, 'invite_dialog.failed_to_accept');
|
||||
if (mounted) {
|
||||
showErrorToast(context, 'invitation_dialog.failed_to_accept');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,8 +120,8 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
if (await validInvitation.reject()) {
|
||||
// do nothing right now
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
showErrorToast(context, 'invite_dialog.failed_to_reject');
|
||||
if (mounted) {
|
||||
showErrorToast(context, 'invitation_dialog.failed_to_reject');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,8 +153,8 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
encryptionKey = '';
|
||||
case EncryptionKeyType.pin:
|
||||
final description =
|
||||
translate('invite_dialog.protected_with_pin');
|
||||
if (!context.mounted) {
|
||||
translate('invitation_dialog.protected_with_pin');
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
final pin = await showDialog<String>(
|
||||
@ -167,8 +167,8 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
encryptionKey = pin;
|
||||
case EncryptionKeyType.password:
|
||||
final description =
|
||||
translate('invite_dialog.protected_with_password');
|
||||
if (!context.mounted) {
|
||||
translate('invitation_dialog.protected_with_password');
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
final password = await showDialog<String>(
|
||||
@ -208,13 +208,13 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
String errorText;
|
||||
switch (e.type) {
|
||||
case EncryptionKeyType.none:
|
||||
errorText = translate('invite_dialog.invalid_invitation');
|
||||
errorText = translate('invitation_dialog.invalid_invitation');
|
||||
case EncryptionKeyType.pin:
|
||||
errorText = translate('invite_dialog.invalid_pin');
|
||||
errorText = translate('invitation_dialog.invalid_pin');
|
||||
case EncryptionKeyType.password:
|
||||
errorText = translate('invite_dialog.invalid_password');
|
||||
errorText = translate('invitation_dialog.invalid_password');
|
||||
}
|
||||
if (context.mounted) {
|
||||
if (mounted) {
|
||||
showErrorToast(context, errorText);
|
||||
}
|
||||
setState(() {
|
||||
@ -259,7 +259,7 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
widget.buildInviteControl(context, this, _validateInviteData),
|
||||
if (_isValidating)
|
||||
Column(children: [
|
||||
Text(translate('invite_dialog.validating'))
|
||||
Text(translate('invitation_dialog.validating'))
|
||||
.paddingLTRB(0, 0, 0, 16),
|
||||
buildProgressIndicator().paddingAll(16),
|
||||
]).toCenter(),
|
||||
@ -267,7 +267,7 @@ class InviteDialogState extends State<InviteDialog> {
|
||||
!_isValidating &&
|
||||
widget.inviteControlIsValid())
|
||||
Column(children: [
|
||||
Text(translate('invite_dialog.invalid_invitation')),
|
||||
Text(translate('invitation_dialog.invalid_invitation')),
|
||||
const Icon(Icons.error)
|
||||
]).paddingAll(16).toCenter(),
|
||||
if (_validInvitation != null && !_isValidating)
|
@ -4,9 +4,9 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
import 'paste_invite_dialog.dart';
|
||||
import 'scan_invite_dialog.dart';
|
||||
import 'send_invite_dialog.dart';
|
||||
import 'paste_invitation_dialog.dart';
|
||||
import 'scan_invitation_dialog.dart';
|
||||
import 'create_invitation_dialog.dart';
|
||||
|
||||
Widget newContactInvitationBottomSheetBuilder(
|
||||
BuildContext sheetContext, BuildContext context) {
|
||||
@ -32,7 +32,7 @@ Widget newContactInvitationBottomSheetBuilder(
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await SendInviteDialog.show(context);
|
||||
await CreateInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.contact_page),
|
||||
@ -43,7 +43,7 @@ Widget newContactInvitationBottomSheetBuilder(
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await ScanInviteDialog.show(context);
|
||||
await ScanInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
@ -54,7 +54,7 @@ Widget newContactInvitationBottomSheetBuilder(
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await PasteInviteDialog.show(context);
|
||||
await PasteInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.paste),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -9,19 +8,19 @@ import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import 'invite_dialog.dart';
|
||||
import 'invitation_dialog.dart';
|
||||
|
||||
class PasteInviteDialog extends StatefulWidget {
|
||||
const PasteInviteDialog({required this.modalContext, super.key});
|
||||
class PasteInvitationDialog extends StatefulWidget {
|
||||
const PasteInvitationDialog({required this.modalContext, super.key});
|
||||
|
||||
@override
|
||||
PasteInviteDialogState createState() => PasteInviteDialogState();
|
||||
PasteInvitationDialogState createState() => PasteInvitationDialogState();
|
||||
|
||||
static Future<void> show(BuildContext context) async {
|
||||
await showStyledDialog<void>(
|
||||
await StyledDialog.show<void>(
|
||||
context: context,
|
||||
title: translate('paste_invite_dialog.title'),
|
||||
child: PasteInviteDialog(modalContext: context));
|
||||
title: translate('paste_invitation_dialog.title'),
|
||||
child: PasteInvitationDialog(modalContext: context));
|
||||
}
|
||||
|
||||
final BuildContext modalContext;
|
||||
@ -34,7 +33,7 @@ class PasteInviteDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class PasteInviteDialogState extends State<PasteInviteDialog> {
|
||||
class PasteInvitationDialogState extends State<PasteInvitationDialog> {
|
||||
final _pasteTextController = TextEditingController();
|
||||
|
||||
@override
|
||||
@ -89,7 +88,7 @@ class PasteInviteDialogState extends State<PasteInviteDialog> {
|
||||
|
||||
Widget buildInviteControl(
|
||||
BuildContext context,
|
||||
InviteDialogState dialogState,
|
||||
InvitationDialogState dialogState,
|
||||
Future<void> Function({required Uint8List inviteData})
|
||||
validateInviteData) {
|
||||
final theme = Theme.of(context);
|
||||
@ -105,7 +104,7 @@ class PasteInviteDialogState extends State<PasteInviteDialog> {
|
||||
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Text(
|
||||
translate('paste_invite_dialog.paste_invite_here'),
|
||||
translate('paste_invitation_dialog.paste_invite_here'),
|
||||
).paddingLTRB(0, 0, 0, 8),
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxHeight: 200),
|
||||
@ -122,7 +121,7 @@ class PasteInviteDialogState extends State<PasteInviteDialog> {
|
||||
hintText: '--- BEGIN VEILIDCHAT CONTACT INVITE ----\n'
|
||||
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
|
||||
'---- END VEILIDCHAT CONTACT INVITE -----\n',
|
||||
//labelText: translate('paste_invite_dialog.paste')
|
||||
//labelText: translate('paste_invitation_dialog.paste')
|
||||
),
|
||||
)).paddingLTRB(0, 0, 0, 8)
|
||||
]);
|
||||
@ -131,7 +130,7 @@ class PasteInviteDialogState extends State<PasteInviteDialog> {
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return InviteDialog(
|
||||
return InvitationDialog(
|
||||
modalContext: widget.modalContext,
|
||||
onValidationCancelled: onValidationCancelled,
|
||||
onValidationSuccess: onValidationSuccess,
|
@ -14,7 +14,7 @@ import 'package:zxing2/qrcode.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import 'invite_dialog.dart';
|
||||
import 'invitation_dialog.dart';
|
||||
|
||||
class BarcodeOverlay extends CustomPainter {
|
||||
BarcodeOverlay({
|
||||
@ -103,17 +103,17 @@ class ScannerOverlay extends CustomPainter {
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
class ScanInviteDialog extends StatefulWidget {
|
||||
const ScanInviteDialog({required this.modalContext, super.key});
|
||||
class ScanInvitationDialog extends StatefulWidget {
|
||||
const ScanInvitationDialog({required this.modalContext, super.key});
|
||||
|
||||
@override
|
||||
ScanInviteDialogState createState() => ScanInviteDialogState();
|
||||
ScanInvitationDialogState createState() => ScanInvitationDialogState();
|
||||
|
||||
static Future<void> show(BuildContext context) async {
|
||||
await showStyledDialog<void>(
|
||||
await StyledDialog.show<void>(
|
||||
context: context,
|
||||
title: translate('scan_invite_dialog.title'),
|
||||
child: ScanInviteDialog(modalContext: context));
|
||||
title: translate('scan_invitation_dialog.title'),
|
||||
child: ScanInvitationDialog(modalContext: context));
|
||||
}
|
||||
|
||||
final BuildContext modalContext;
|
||||
@ -126,7 +126,7 @@ class ScanInviteDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||
bool scanned = false;
|
||||
|
||||
@override
|
||||
@ -221,7 +221,8 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
height: 50,
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
translate('scan_invite_dialog.instructions'),
|
||||
translate(
|
||||
'scan_invitation_dialog.instructions'),
|
||||
overflow: TextOverflow.fade,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
@ -270,12 +271,12 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
} on MobileScannerException catch (e) {
|
||||
if (e.errorCode == MobileScannerErrorCode.permissionDenied) {
|
||||
showErrorToast(
|
||||
context, translate('scan_invite_dialog.permission_error'));
|
||||
context, translate('scan_invitation_dialog.permission_error'));
|
||||
} else {
|
||||
showErrorToast(context, translate('scan_invite_dialog.error'));
|
||||
showErrorToast(context, translate('scan_invitation_dialog.error'));
|
||||
}
|
||||
} on Exception catch (_) {
|
||||
showErrorToast(context, translate('scan_invite_dialog.error'));
|
||||
showErrorToast(context, translate('scan_invitation_dialog.error'));
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -285,7 +286,8 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
final imageBytes = await Pasteboard.image;
|
||||
if (imageBytes == null) {
|
||||
if (context.mounted) {
|
||||
showErrorToast(context, translate('scan_invite_dialog.not_an_image'));
|
||||
showErrorToast(
|
||||
context, translate('scan_invitation_dialog.not_an_image'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -293,8 +295,8 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
final image = img.decodeImage(imageBytes);
|
||||
if (image == null) {
|
||||
if (context.mounted) {
|
||||
showErrorToast(
|
||||
context, translate('scan_invite_dialog.could_not_decode_image'));
|
||||
showErrorToast(context,
|
||||
translate('scan_invitation_dialog.could_not_decode_image'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -319,7 +321,7 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
} on Exception catch (_) {
|
||||
if (context.mounted) {
|
||||
showErrorToast(
|
||||
context, translate('scan_invite_dialog.not_a_valid_qr_code'));
|
||||
context, translate('scan_invitation_dialog.not_a_valid_qr_code'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -327,7 +329,7 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
|
||||
Widget buildInviteControl(
|
||||
BuildContext context,
|
||||
InviteDialogState dialogState,
|
||||
InvitationDialogState dialogState,
|
||||
Future<void> Function({required Uint8List inviteData})
|
||||
validateInviteData) {
|
||||
//final theme = Theme.of(context);
|
||||
@ -339,7 +341,7 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
if (!scanned)
|
||||
Text(
|
||||
translate('scan_invite_dialog.scan_qr_here'),
|
||||
translate('scan_invitation_dialog.scan_qr_here'),
|
||||
).paddingLTRB(0, 0, 0, 8),
|
||||
if (!scanned)
|
||||
Container(
|
||||
@ -356,14 +358,14 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
await validateInviteData(inviteData: inviteData);
|
||||
}
|
||||
},
|
||||
child: Text(translate('scan_invite_dialog.scan'))),
|
||||
child: Text(translate('scan_invitation_dialog.scan'))),
|
||||
).paddingLTRB(0, 0, 0, 8)
|
||||
]);
|
||||
}
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
if (!scanned)
|
||||
Text(
|
||||
translate('scan_invite_dialog.paste_qr_here'),
|
||||
translate('scan_invitation_dialog.paste_qr_here'),
|
||||
).paddingLTRB(0, 0, 0, 8),
|
||||
if (!scanned)
|
||||
Container(
|
||||
@ -380,7 +382,7 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(translate('scan_invite_dialog.paste'))),
|
||||
child: Text(translate('scan_invitation_dialog.paste'))),
|
||||
).paddingLTRB(0, 0, 0, 8)
|
||||
]);
|
||||
}
|
||||
@ -388,7 +390,7 @@ class ScanInviteDialogState extends State<ScanInviteDialog> {
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return InviteDialog(
|
||||
return InvitationDialog(
|
||||
modalContext: widget.modalContext,
|
||||
onValidationCancelled: onValidationCancelled,
|
||||
onValidationSuccess: onValidationSuccess,
|
@ -1,8 +1,8 @@
|
||||
export 'contact_invitation_display.dart';
|
||||
export 'contact_invitation_item_widget.dart';
|
||||
export 'contact_invitation_list_widget.dart';
|
||||
export 'invite_dialog.dart';
|
||||
export 'create_invitation_dialog.dart';
|
||||
export 'invitation_dialog.dart';
|
||||
export 'new_contact_invitation_bottom_sheet.dart';
|
||||
export 'paste_invite_dialog.dart';
|
||||
export 'scan_invite_dialog.dart';
|
||||
export 'send_invite_dialog.dart';
|
||||
export 'paste_invitation_dialog.dart';
|
||||
export 'scan_invitation_dialog.dart';
|
||||
|
@ -162,7 +162,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final deleteSet = DelayedWaitSet();
|
||||
|
||||
if (localConversationCubit != null) {
|
||||
final data = localConversationCubit.state.data;
|
||||
final data = localConversationCubit.state.asData;
|
||||
if (data == null) {
|
||||
log.warning('could not delete local conversation');
|
||||
return false;
|
||||
@ -180,7 +180,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
}
|
||||
|
||||
if (remoteConversationCubit != null) {
|
||||
final data = remoteConversationCubit.state.data;
|
||||
final data = remoteConversationCubit.state.asData;
|
||||
if (data == null) {
|
||||
log.warning('could not delete remote conversation');
|
||||
return false;
|
||||
|
@ -75,7 +75,7 @@ class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
|
||||
|
||||
for (final entry in newState.entries) {
|
||||
final contactRequestInboxRecordKey = entry.key;
|
||||
final invStatus = entry.value.data?.value;
|
||||
final invStatus = entry.value.asData?.value;
|
||||
// Skip invitations that have not yet been accepted or rejected
|
||||
if (invStatus == null) {
|
||||
continue;
|
||||
@ -109,7 +109,7 @@ class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final account = context.watch<AccountRecordCubit>().state.data?.value;
|
||||
final account = context.watch<AccountRecordCubit>().state.asData?.value;
|
||||
if (account == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ class AccountPageState extends State<AccountPage> {
|
||||
final cilState = context.watch<ContactInvitationListCubit>().state;
|
||||
final cilBusy = cilState.busy;
|
||||
final contactInvitationRecordList =
|
||||
cilState.state.data?.value ?? const IListConst([]);
|
||||
cilState.state.asData?.value ?? const IListConst([]);
|
||||
|
||||
final ciState = context.watch<ContactListCubit>().state;
|
||||
final ciBusy = ciState.busy;
|
||||
final contactList = ciState.state.data?.value ?? const IListConst([]);
|
||||
final contactList = ciState.state.asData?.value ?? const IListConst([]);
|
||||
|
||||
return SizedBox(
|
||||
child: Column(children: <Widget>[
|
||||
|
@ -121,7 +121,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
'Scan Contact Invite',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
content: ScanInviteDialog(
|
||||
content: ScanInvitationDialog(
|
||||
modalContext: context,
|
||||
));
|
||||
});
|
||||
|
@ -23,7 +23,9 @@ class _SplashState extends State<Splash> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => DecoratedBox(
|
||||
Widget build(BuildContext context) => PopScope(
|
||||
canPop: false,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
@ -49,5 +51,5 @@ class _SplashState extends State<Splash> {
|
||||
'assets/images/title.svg',
|
||||
))
|
||||
]))),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class EnterPasswordDialog extends StatefulWidget {
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
EnterPasswordDialogState createState() => EnterPasswordDialogState();
|
||||
State<EnterPasswordDialog> createState() => _EnterPasswordDialogState();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
@ -28,7 +28,7 @@ class EnterPasswordDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||
class _EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||
final passwordController = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
@ -18,7 +18,7 @@ class EnterPinDialog extends StatefulWidget {
|
||||
final String? description;
|
||||
|
||||
@override
|
||||
EnterPinDialogState createState() => EnterPinDialogState();
|
||||
State<EnterPinDialog> createState() => _EnterPinDialogState();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
@ -29,7 +29,7 @@ class EnterPinDialog extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class EnterPinDialogState extends State<EnterPinDialog> {
|
||||
class _EnterPinDialogState extends State<EnterPinDialog> {
|
||||
final pinController = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
130
lib/tools/pop_control.dart
Normal file
130
lib/tools/pop_control.dart
Normal file
@ -0,0 +1,130 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PopControl extends StatelessWidget {
|
||||
const PopControl({
|
||||
required this.child,
|
||||
required this.dismissible,
|
||||
super.key,
|
||||
});
|
||||
|
||||
void _doDismiss(NavigatorState navigator) {
|
||||
if (!dismissible) {
|
||||
return;
|
||||
}
|
||||
navigator.pop();
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final navigator = Navigator.of(context);
|
||||
|
||||
final route = ModalRoute.of(context);
|
||||
if (route != null && route is PopControlDialogRoute) {
|
||||
route.barrierDismissible = dismissible;
|
||||
}
|
||||
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
_doDismiss(navigator);
|
||||
},
|
||||
child: child);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<bool>('dismissible', dismissible));
|
||||
}
|
||||
|
||||
final bool dismissible;
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
class PopControlDialogRoute<T> extends DialogRoute<T> {
|
||||
PopControlDialogRoute(
|
||||
{required super.context,
|
||||
required super.builder,
|
||||
super.themes,
|
||||
super.barrierColor = Colors.black54,
|
||||
super.barrierDismissible,
|
||||
super.barrierLabel,
|
||||
super.useSafeArea,
|
||||
super.settings,
|
||||
super.anchorPoint,
|
||||
super.traversalEdgeBehavior})
|
||||
: _barrierDismissible = barrierDismissible;
|
||||
|
||||
@override
|
||||
bool get barrierDismissible => _barrierDismissible;
|
||||
|
||||
set barrierDismissible(bool d) {
|
||||
_barrierDismissible = d;
|
||||
}
|
||||
|
||||
bool _barrierDismissible;
|
||||
}
|
||||
|
||||
bool _debugIsActive(BuildContext context) {
|
||||
if (context is Element && !context.debugIsActive) {
|
||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||
ErrorSummary('This BuildContext is no longer valid.'),
|
||||
ErrorDescription(
|
||||
'The showPopControlDialog function context parameter is a '
|
||||
'BuildContext that is no longer valid.'),
|
||||
ErrorHint(
|
||||
'This can commonly occur when the showPopControlDialog function is '
|
||||
'called after awaiting a Future. '
|
||||
'In this situation the BuildContext might refer to a widget that has '
|
||||
'already been disposed during the await. '
|
||||
'Consider using a parent context instead.',
|
||||
),
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<T?> showPopControlDialog<T>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
bool barrierDismissible = true,
|
||||
Color? barrierColor,
|
||||
String? barrierLabel,
|
||||
bool useSafeArea = true,
|
||||
bool useRootNavigator = true,
|
||||
RouteSettings? routeSettings,
|
||||
Offset? anchorPoint,
|
||||
TraversalEdgeBehavior? traversalEdgeBehavior,
|
||||
}) {
|
||||
assert(_debugIsActive(context), 'debug is active check');
|
||||
assert(debugCheckHasMaterialLocalizations(context),
|
||||
'check has material localizations');
|
||||
|
||||
final themes = InheritedTheme.capture(
|
||||
from: context,
|
||||
to: Navigator.of(
|
||||
context,
|
||||
rootNavigator: useRootNavigator,
|
||||
).context,
|
||||
);
|
||||
|
||||
return Navigator.of(context, rootNavigator: useRootNavigator)
|
||||
.push<T>(PopControlDialogRoute<T>(
|
||||
context: context,
|
||||
builder: builder,
|
||||
barrierColor: barrierColor ?? Colors.black54,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierLabel: barrierLabel,
|
||||
useSafeArea: useSafeArea,
|
||||
settings: routeSettings,
|
||||
themes: themes,
|
||||
anchorPoint: anchorPoint,
|
||||
traversalEdgeBehavior:
|
||||
traversalEdgeBehavior ?? TraversalEdgeBehavior.closedLoop,
|
||||
));
|
||||
}
|
58
lib/tools/styled_dialog.dart
Normal file
58
lib/tools/styled_dialog.dart
Normal file
@ -0,0 +1,58 @@
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
|
||||
class StyledDialog extends StatelessWidget {
|
||||
const StyledDialog({required this.title, required this.child, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final textTheme = theme.textTheme;
|
||||
|
||||
return AlertDialog(
|
||||
elevation: 0,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(4),
|
||||
backgroundColor: scale.primaryScale.border,
|
||||
title: Text(
|
||||
title,
|
||||
style: textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
titlePadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
content: DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.primaryScale.border,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16))),
|
||||
child: DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.primaryScale.appBackground,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12))),
|
||||
child: child.paddingAll(0))));
|
||||
}
|
||||
|
||||
static Future<T?> show<T>(
|
||||
{required BuildContext context,
|
||||
required String title,
|
||||
required Widget child}) async =>
|
||||
showDialog<T>(
|
||||
context: context,
|
||||
builder: (context) => StyledDialog(title: title, child: child));
|
||||
|
||||
final String title;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(StringProperty('title', title));
|
||||
}
|
||||
}
|
@ -3,10 +3,12 @@ export 'enter_password.dart';
|
||||
export 'enter_pin.dart';
|
||||
export 'loggy.dart';
|
||||
export 'phono_byte.dart';
|
||||
export 'pop_control.dart';
|
||||
export 'responsive.dart';
|
||||
export 'scanner_error_widget.dart';
|
||||
export 'shared_preferences.dart';
|
||||
export 'state_logger.dart';
|
||||
export 'stream_listenable.dart';
|
||||
export 'styled_dialog.dart';
|
||||
export 'widget_helpers.dart';
|
||||
export 'window_control.dart';
|
||||
|
@ -164,42 +164,6 @@ Widget styledTitleContainer(
|
||||
]));
|
||||
}
|
||||
|
||||
Future<T?> showStyledDialog<T>(
|
||||
{required BuildContext context,
|
||||
required String title,
|
||||
required Widget child}) async {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final textTheme = theme.textTheme;
|
||||
|
||||
return showDialog<T>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
elevation: 0,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(4),
|
||||
backgroundColor: scale.primaryScale.border,
|
||||
title: Text(
|
||||
title,
|
||||
style: textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
titlePadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
content: DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.primaryScale.border,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16))),
|
||||
child: DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.primaryScale.appBackground,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12))),
|
||||
child: child.paddingAll(0)))));
|
||||
}
|
||||
|
||||
bool get isPlatformDark =>
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark;
|
||||
|
@ -35,7 +35,7 @@ part 'async_value.freezed.dart';
|
||||
/// ```
|
||||
///
|
||||
/// If a consumer of an [AsyncValue] does not care about the loading/error
|
||||
/// state, consider using [data] to read the state:
|
||||
/// state, consider using [asData] to read the state:
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context, ScopedReader watch) {
|
||||
@ -127,7 +127,7 @@ abstract class AsyncValue<T> with _$AsyncValue<T> {
|
||||
/// The current data, or null if in loading/error.
|
||||
///
|
||||
/// This is safe to use, as Dart (will) have non-nullable types.
|
||||
/// As such reading [data] still forces to handle the loading/error cases
|
||||
/// As such reading [asData] still forces to handle the loading/error cases
|
||||
/// by having to check `data != null`.
|
||||
///
|
||||
/// ## Why does [AsyncValue<T>.data] return [AsyncData<T>] instead of [T]?
|
||||
@ -151,12 +151,32 @@ abstract class AsyncValue<T> with _$AsyncValue<T> {
|
||||
/// print(configs.data); // null, currently loading
|
||||
/// print(configs.data.value); // throws null exception
|
||||
/// ```
|
||||
AsyncData<T>? get data => map(
|
||||
AsyncData<T>? get asData => map(
|
||||
data: (data) => data,
|
||||
loading: (_) => null,
|
||||
error: (_) => null,
|
||||
);
|
||||
|
||||
bool get isData => asData != null;
|
||||
|
||||
/// Check if this is loading
|
||||
AsyncLoading<T>? get asLoading => map(
|
||||
data: (_) => null,
|
||||
loading: (loading) => loading,
|
||||
error: (_) => null,
|
||||
);
|
||||
|
||||
bool get isLoading => asLoading != null;
|
||||
|
||||
/// Check if this is an error
|
||||
AsyncError<T>? get asError => map(
|
||||
data: (_) => null,
|
||||
loading: (_) => null,
|
||||
error: (e) => e,
|
||||
);
|
||||
|
||||
bool get isError => asError != null;
|
||||
|
||||
/// Shorthand for [when] to handle only the `data` case.
|
||||
AsyncValue<R> whenData<R>(R Function(T value) cb) => when(
|
||||
data: (value) {
|
||||
|
@ -24,7 +24,7 @@ class AsyncTransformerCubit<T, S> extends Cubit<AsyncValue<T>> {
|
||||
} else if (newState is AsyncError<S>) {
|
||||
emit(AsyncValue.error(newState.error, newState.stackTrace));
|
||||
} else {
|
||||
final transformedState = await transform(newState.data!.value);
|
||||
final transformedState = await transform(newState.asData!.value);
|
||||
emit(transformedState);
|
||||
}
|
||||
} on Exception catch (e, st) {
|
||||
|
34
pubspec.lock
34
pubspec.lock
@ -179,10 +179,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.1"
|
||||
version: "8.9.2"
|
||||
cached_network_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -219,18 +219,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_android
|
||||
sha256: "1100e527b44a96906987a91ef78c8dacb539e34612a8058de89023380acf67f1"
|
||||
sha256: ae5b9a996dfb8d77b02031b67f5500873d6402f33bd6a5283e932eef08542a51
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.8+18"
|
||||
version: "0.10.9"
|
||||
camera_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_avfoundation
|
||||
sha256: "8b113e43ee4434c9244c03c905432a0d5956cedaded3cd7381abaab89ce50297"
|
||||
sha256: "5d009ae48de1c8ab621b1c4496dadb6e2a83f3223b76c6e6a4a252414105f561"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.14+1"
|
||||
version: "0.9.15"
|
||||
camera_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -243,10 +243,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_web
|
||||
sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d
|
||||
sha256: "9e9aba2fbab77ce2472924196ff8ac4dd8f9126c4f9a3096171cd1d870d6b26c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.2+4"
|
||||
version: "0.3.3"
|
||||
change_case:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -594,10 +594,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5"
|
||||
sha256: "24f77b50776d4285cc4b3a1665bb79852714c09b878363efbe64788c179c4284"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.7"
|
||||
version: "2.5.0"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -977,10 +977,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||
sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
version: "3.8.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1270,10 +1270,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
|
||||
sha256: "5ce2e1a15e822c3b4bfb5400455775e421da7098eed8adc8f26298ada7c9308c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1318,10 +1318,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: stylish_bottom_bar
|
||||
sha256: "54970e4753b4273239b6dea0d1175c56beabcf39b5c65df4cbf86f1b86568d2b"
|
||||
sha256: ca72557a5bd8f44caae9017eb3a73002e9189d7a9d2fac598fa55be13724f32b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.1.0"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1504,7 +1504,7 @@ packages:
|
||||
path: "../veilid/veilid-flutter"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.3.0"
|
||||
version: "0.3.1"
|
||||
veilid_support:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -76,7 +76,7 @@ dependencies:
|
||||
split_view: ^3.2.1
|
||||
stack_trace: ^1.11.1
|
||||
stream_transform: ^2.1.0
|
||||
stylish_bottom_bar: ^1.0.3
|
||||
stylish_bottom_bar: ^1.1.0
|
||||
uuid: ^4.3.3
|
||||
veilid:
|
||||
# veilid: ^0.0.1
|
||||
@ -89,7 +89,7 @@ dependencies:
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.9
|
||||
freezed: ^2.4.7
|
||||
freezed: ^2.5.0
|
||||
icons_launcher: ^2.1.7
|
||||
json_serializable: ^6.7.1
|
||||
lint_hard: ^4.0.0
|
||||
|
Loading…
Reference in New Issue
Block a user