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