navigation cleanup

This commit is contained in:
Christien Rioux 2024-04-05 22:03:04 -04:00
parent 5da68b2d94
commit b3e9cbd4f3
32 changed files with 475 additions and 314 deletions

View File

@ -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:",

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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,
)));
}
}

View File

@ -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

View File

@ -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),
],

View File

@ -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)

View File

@ -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),

View File

@ -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,

View File

@ -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,

View File

@ -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';

View File

@ -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;

View File

@ -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();
}

View File

@ -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>[

View File

@ -121,7 +121,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
'Scan Contact Invite',
style: TextStyle(fontSize: 24),
),
content: ScanInviteDialog(
content: ScanInvitationDialog(
modalContext: context,
));
});

View File

@ -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',
))
]))),
);
));
}

View File

@ -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>();

View File

@ -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
View 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,
));
}

View 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));
}
}

View File

@ -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';

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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:

View File

@ -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