refactor checkpoint

This commit is contained in:
Christien Rioux 2024-01-29 22:38:19 -05:00
parent 7bd426ce98
commit 1e6b9f4a43
12 changed files with 145 additions and 136 deletions

View File

@ -1,22 +1,25 @@
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../cubit/cubit.dart';
class ProfileWidget extends StatelessWidget { class ProfileWidget extends StatelessWidget {
const ProfileWidget({ const ProfileWidget({
required this.name,
this.pronouns,
super.key, super.key,
}); });
final String name;
final String? pronouns;
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
final accountData = context.watch<AccountRecordCubit>().state.data;
if (accountData == null) {
return waitingPage(context);
}
final account = accountData.value;
final theme = Theme.of(context); final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
@ -28,21 +31,14 @@ class ProfileWidget extends StatelessWidget {
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))), RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(children: [ child: Column(children: [
Text( Text(
name, account.profile.name,
style: textTheme.headlineSmall, style: textTheme.headlineSmall,
textAlign: TextAlign.left, textAlign: TextAlign.left,
).paddingAll(4), ).paddingAll(4),
if (pronouns != null && pronouns!.isNotEmpty) if (account.profile.pronouns.isNotEmpty)
Text(pronouns!, style: textTheme.bodyMedium).paddingLTRB(4, 0, 4, 4), Text(account.profile.pronouns, style: textTheme.bodyMedium)
.paddingLTRB(4, 0, 4, 4),
]), ]),
); );
} }
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(StringProperty('name', name))
..add(StringProperty('pronouns', pronouns));
}
} }

View File

@ -265,6 +265,7 @@ class ContactInvitationListCubit
out = ValidContactInvitation( out = ValidContactInvitation(
activeAccountInfo: _activeAccountInfo, activeAccountInfo: _activeAccountInfo,
account: _account,
contactRequestInboxKey: contactRequestInboxKey, contactRequestInboxKey: contactRequestInboxKey,
contactRequestPrivate: contactRequestPrivate, contactRequestPrivate: contactRequestPrivate,
contactIdentityMaster: contactIdentityMaster, contactIdentityMaster: contactIdentityMaster,
@ -345,12 +346,12 @@ class ContactInvitationListCubit
contactInvitationRecord.localConversationRecordKey); contactInvitationRecord.localConversationRecordKey);
return conversation.initLocalConversation( return conversation.initLocalConversation(
existingConversationRecordKey: localConversationRecordKey, existingConversationRecordKey: localConversationRecordKey,
profile: xxx LOCAL PROFILE HERE NOT REMOTE profile: _account.profile,
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
callback: (localConversation) async { callback: (localConversation) async {
return InvitationStatus( return InvitationStatus(
acceptedContact: AcceptedContact( acceptedContact: AcceptedContact(
profile: remoteConversation.profile, remoteProfile: remoteConversation.profile,
remoteIdentity: contactIdentityMaster, remoteIdentity: contactIdentityMaster,
remoteConversationRecordKey: remoteConversationRecordKey, remoteConversationRecordKey: remoteConversationRecordKey,
localConversationRecordKey: localConversationRecordKey)); localConversationRecordKey: localConversationRecordKey));

View File

@ -6,13 +6,13 @@ import '../../proto/proto.dart' as proto;
@immutable @immutable
class AcceptedContact { class AcceptedContact {
const AcceptedContact({ const AcceptedContact({
required this.profile, required this.remoteProfile,
required this.remoteIdentity, required this.remoteIdentity,
required this.remoteConversationRecordKey, required this.remoteConversationRecordKey,
required this.localConversationRecordKey, required this.localConversationRecordKey,
}); });
final proto.Profile profile; final proto.Profile remoteProfile;
final IdentityMaster remoteIdentity; final IdentityMaster remoteIdentity;
final TypedKey remoteConversationRecordKey; final TypedKey remoteConversationRecordKey;
final TypedKey localConversationRecordKey; final TypedKey localConversationRecordKey;

View File

@ -2,10 +2,10 @@ import 'package:meta/meta.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../contacts/contacts.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart'; import '../../tools/tools.dart';
import 'models.dart'; import 'models.dart';
import '../cubits/contact_invitation_list_cubit.dart';
////////////////////////////////////////////////// //////////////////////////////////////////////////
/// ///
@ -14,11 +14,13 @@ class ValidContactInvitation {
@internal @internal
ValidContactInvitation( ValidContactInvitation(
{required ActiveAccountInfo activeAccountInfo, {required ActiveAccountInfo activeAccountInfo,
required proto.Account account,
required TypedKey contactRequestInboxKey, required TypedKey contactRequestInboxKey,
required proto.ContactRequestPrivate contactRequestPrivate, required proto.ContactRequestPrivate contactRequestPrivate,
required IdentityMaster contactIdentityMaster, required IdentityMaster contactIdentityMaster,
required KeyPair writer}) required KeyPair writer})
: _activeAccountInfo = activeAccountInfo, : _activeAccountInfo = activeAccountInfo,
_account = account,
_contactRequestInboxKey = contactRequestInboxKey, _contactRequestInboxKey = contactRequestInboxKey,
_contactRequestPrivate = contactRequestPrivate, _contactRequestPrivate = contactRequestPrivate,
_contactIdentityMaster = contactIdentityMaster, _contactIdentityMaster = contactIdentityMaster,
@ -38,10 +40,12 @@ class ValidContactInvitation {
.maybeDeleteScope(!isSelf, (contactRequestInbox) async { .maybeDeleteScope(!isSelf, (contactRequestInbox) async {
// Create local conversation key for this // Create local conversation key for this
// contact and send via contact response // contact and send via contact response
return createConversation( final conversation = ConversationManager(
activeAccountInfo: _activeAccountInfo, activeAccountInfo: _activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey:
_contactIdentityMaster.identityPublicTypedKey(), _contactIdentityMaster.identityPublicTypedKey());
return conversation.initLocalConversation(
profile: _account.profile,
callback: (localConversation) async { callback: (localConversation) async {
final contactResponse = proto.ContactResponse() final contactResponse = proto.ContactResponse()
..accept = true ..accept = true
@ -72,7 +76,7 @@ class ValidContactInvitation {
throw Exception('failed to accept contact invitation'); throw Exception('failed to accept contact invitation');
} }
return AcceptedContact( return AcceptedContact(
profile: _contactRequestPrivate.profile, remoteProfile: _contactRequestPrivate.profile,
remoteIdentity: _contactIdentityMaster, remoteIdentity: _contactIdentityMaster,
remoteConversationRecordKey: proto.TypedKeyProto.fromProto( remoteConversationRecordKey: proto.TypedKeyProto.fromProto(
_contactRequestPrivate.chatRecordKey), _contactRequestPrivate.chatRecordKey),
@ -131,6 +135,7 @@ class ValidContactInvitation {
// //
final ActiveAccountInfo _activeAccountInfo; final ActiveAccountInfo _activeAccountInfo;
final proto.Account _account;
final TypedKey _contactRequestInboxKey; final TypedKey _contactRequestInboxKey;
final IdentityMaster _contactIdentityMaster; final IdentityMaster _contactIdentityMaster;
final KeyPair _writer; final KeyPair _writer;

View File

@ -7,20 +7,23 @@ 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_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';
class InvitationGeneratorCubit extends FutureCubit<Uint8List> {
InvitationGeneratorCubit(super.fut);
}
class ContactInvitationDisplayDialog extends StatefulWidget { class ContactInvitationDisplayDialog extends StatefulWidget {
const ContactInvitationDisplayDialog({ const ContactInvitationDisplayDialog({
required this.name,
required this.message, required this.message,
required this.generator, required this.generator,
super.key, super.key,
}); });
final String name;
final String message; final String message;
final FutureOr<Uint8List> generator; final FutureOr<Uint8List> generator;
@ -32,7 +35,6 @@ class ContactInvitationDisplayDialog extends StatefulWidget {
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
properties properties
..add(StringProperty('name', name))
..add(StringProperty('message', message)) ..add(StringProperty('message', message))
..add(DiagnosticsProperty<FutureOr<Uint8List>?>('generator', generator)); ..add(DiagnosticsProperty<FutureOr<Uint8List>?>('generator', generator));
} }
@ -42,14 +44,10 @@ class ContactInvitationDisplayDialogState
extends State<ContactInvitationDisplayDialog> { extends State<ContactInvitationDisplayDialog> {
final focusNode = FocusNode(); final focusNode = FocusNode();
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
late final AutoDisposeFutureProvider<Uint8List?> _generateFutureProvider;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_generateFutureProvider =
AutoDisposeFutureProvider<Uint8List>((ref) async => widget.generator);
} }
@override @override
@ -76,7 +74,9 @@ class ContactInvitationDisplayDialogState
//final scale = theme.extension<ScaleScheme>()!; //final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
final signedContactInvitationBytesV = ref.watch(_generateFutureProvider); final signedContactInvitationBytesV =
context.watch<InvitationGeneratorCubit>().state;
final cardsize = final cardsize =
min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400); min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400);
@ -90,49 +90,43 @@ class ContactInvitationDisplayDialogState
maxHeight: cardsize), maxHeight: cardsize),
child: signedContactInvitationBytesV.when( child: signedContactInvitationBytesV.when(
loading: () => buildProgressIndicator(context), loading: () => buildProgressIndicator(context),
data: (data) { data: (data) => Form(
if (data == null) { key: formKey,
Navigator.of(context).pop(); child: Column(children: [
return const Text(''); FittedBox(
} child: Text(
return Form( translate(
key: formKey, 'send_invite_dialog.contact_invitation'),
child: Column(children: [ style: textTheme.headlineSmall!
FittedBox( .copyWith(color: Colors.black)))
child: Text( .paddingAll(8),
translate( FittedBox(
'send_invite_dialog.contact_invitation'), child: QrImageView.withQr(
style: textTheme.headlineSmall! size: 300,
.copyWith(color: Colors.black))) qr: QrCode.fromUint8List(
.paddingAll(8), data: data,
FittedBox( errorCorrectLevel:
child: QrImageView.withQr( QrErrorCorrectLevel.L)))
size: 300, .expanded(),
qr: QrCode.fromUint8List( Text(widget.message,
data: data, softWrap: true,
errorCorrectLevel: style: textTheme.labelLarge!
QrErrorCorrectLevel.L))) .copyWith(color: Colors.black))
.expanded(), .paddingAll(8),
Text(widget.message, ElevatedButton.icon(
softWrap: true, icon: const Icon(Icons.copy),
style: textTheme.labelLarge! label: Text(
.copyWith(color: Colors.black)) translate('send_invite_dialog.copy_invitation')),
.paddingAll(8), onPressed: () async {
ElevatedButton.icon( showInfoToast(
icon: const Icon(Icons.copy), context,
label: Text( translate(
translate('send_invite_dialog.copy_invitation')), 'send_invite_dialog.invitation_copied'));
onPressed: () async { await Clipboard.setData(ClipboardData(
showInfoToast( text: makeTextInvite(widget.message, data)));
context, },
translate( ).paddingAll(16),
'send_invite_dialog.invitation_copied')); ])),
await Clipboard.setData(ClipboardData(
text: makeTextInvite(widget.message, data)));
},
).paddingAll(16),
]));
},
error: (e, s) { error: (e, s) {
Navigator.of(context).pop(); Navigator.of(context).pop();
showErrorToast(context, showErrorToast(context,

View File

@ -1,10 +1,11 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import 'contact_invitation_display.dart'; import '../contact_invitation.dart';
class ContactInvitationItemWidget extends StatelessWidget { class ContactInvitationItemWidget extends StatelessWidget {
const ContactInvitationItemWidget( const ContactInvitationItemWidget(
@ -48,15 +49,11 @@ class ContactInvitationItemWidget extends StatelessWidget {
// A SlidableAction can have an icon and/or a label. // A SlidableAction can have an icon and/or a label.
SlidableAction( SlidableAction(
onPressed: (context) async { onPressed: (context) async {
final activeAccountInfo = final contactInvitationListCubit =
await ref.read(fetchActiveAccountProvider.future); context.read<ContactInvitationListCubit>();
if (activeAccountInfo != null) { await contactInvitationListCubit.deleteInvitation(
await deleteContactInvitation( accepted: false,
accepted: false, contactInvitationRecord: contactInvitationRecord);
activeAccountInfo: activeAccountInfo,
contactInvitationRecord: contactInvitationRecord);
ref.invalidate(fetchContactInvitationRecordsProvider);
}
}, },
backgroundColor: scale.tertiaryScale.background, backgroundColor: scale.tertiaryScale.background,
foregroundColor: scale.tertiaryScale.text, foregroundColor: scale.tertiaryScale.text,
@ -93,22 +90,21 @@ class ContactInvitationItemWidget extends StatelessWidget {
child: ListTile( child: ListTile(
//title: Text(translate('contact_list.invitation')), //title: Text(translate('contact_list.invitation')),
onTap: () async { onTap: () async {
final activeAccountInfo = // ignore: use_build_context_synchronously
await ref.read(fetchActiveAccountProvider.future); if (!context.mounted) {
if (activeAccountInfo != null) { return;
// ignore: use_build_context_synchronously
if (!context.mounted) {
return;
}
await showDialog<void>(
context: context,
builder: (context) => ContactInvitationDisplayDialog(
name: activeAccountInfo.localAccount.name,
message: contactInvitationRecord.message,
generator: Uint8List.fromList(
contactInvitationRecord.invitation),
));
} }
await showDialog<void>(
context: context,
builder: (context) => BlocProvider(
create: (context) => InvitationGeneratorCubit(
Future.value(Uint8List.fromList(
contactInvitationRecord.invitation))),
child: ContactInvitationDisplayDialog(
message: contactInvitationRecord.message,
generator: Uint8List.fromList(
contactInvitationRecord.invitation),
)));
}, },
title: Text( title: Text(
contactInvitationRecord.message.isEmpty contactInvitationRecord.message.isEmpty

View File

@ -5,6 +5,7 @@ import 'package:awesome_extensions/awesome_extensions.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:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
@ -129,9 +130,11 @@ class SendInviteDialogState extends State<SendInviteDialog> {
Future<void> _onGenerateButtonPressed() async { Future<void> _onGenerateButtonPressed() async {
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
xxx continue here
// Start generation // Start generation
final activeAccountInfo = final activeAccountInfo = context.read<ActiveAccountInfo>();
await AccountRepository.instance.fetchActiveAccountInfo();
if (activeAccountInfo == null) { if (activeAccountInfo == null) {
navigator.pop(); navigator.pop();
return; return;

View File

@ -7,10 +7,10 @@ import '../../proto/proto.dart' as proto;
import '../../../packages/veilid_support/veilid_support.dart'; import '../../../packages/veilid_support/veilid_support.dart';
import '../../tools/tools.dart'; import '../../tools/tools.dart';
import 'account.dart'; import '../../old_to_refactor/providers/account.dart';
import 'chat.dart'; import '../../old_to_refactor/providers/chat.dart';
part 'contact.g.dart'; part '../../old_to_refactor/providers/contact.g.dart';
Future<void> createContact({ Future<void> createContact({
required ActiveAccountInfo activeAccountInfo, required ActiveAccountInfo activeAccountInfo,

View File

@ -1,6 +1,7 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
@ -58,10 +59,12 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
case AccountInfoStatus.accountLocked: case AccountInfoStatus.accountLocked:
return const HomeAccountLocked(); return const HomeAccountLocked();
case AccountInfoStatus.accountReady: case AccountInfoStatus.accountReady:
return BlocProvider( return Provider.value(
create: (context) => AccountRecordCubit( value: accountInfo.activeAccountInfo,
record: accountInfo.activeAccountInfo!.accountRecord), child: BlocProvider(
child: HomeAccountReady()); create: (context) => AccountRecordCubit(
record: accountInfo.activeAccountInfo!.accountRecord),
child: HomeAccountReady()));
} }
} }

View File

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -9,21 +7,12 @@ import 'package:go_router/go_router.dart';
import '../../../account_manager/account_manager.dart'; import '../../../account_manager/account_manager.dart';
import '../../../contact_invitation/contact_invitation.dart'; import '../../../contact_invitation/contact_invitation.dart';
import '../../../proto/proto.dart' as proto;
import '../../../theme/theme.dart'; import '../../../theme/theme.dart';
import '../../../tools/tools.dart'; import '../../../tools/tools.dart';
import 'main_pager/main_pager.dart'; import 'main_pager/main_pager.dart';
class HomeAccountReady extends StatefulWidget { class HomeAccountReady extends StatefulWidget {
const HomeAccountReady( const HomeAccountReady({super.key});
{required ActiveAccountInfo activeAccountInfo,
required proto.Account account,
super.key})
: _activeAccountInfo = activeAccountInfo,
_account = account;
final ActiveAccountInfo _activeAccountInfo;
final proto.Account _account;
@override @override
HomeAccountReadyState createState() => HomeAccountReadyState(); HomeAccountReadyState createState() => HomeAccountReadyState();
@ -64,10 +53,7 @@ class HomeAccountReadyState extends State<HomeAccountReady>
onPressed: () async { onPressed: () async {
context.go('/home/settings'); context.go('/home/settings');
}).paddingLTRB(0, 0, 8, 0), }).paddingLTRB(0, 0, 8, 0),
ProfileWidget( const ProfileWidget().expanded(),
name: widget._account.profile.name,
pronouns: widget._account.profile.pronouns,
).expanded(),
]).paddingAll(8), ]).paddingAll(8),
const MainPager().expanded() const MainPager().expanded()
]); ]);
@ -107,14 +93,22 @@ class HomeAccountReadyState extends State<HomeAccountReady>
} }
@override @override
Widget build(BuildContext context) => BlocProvider( Widget build(BuildContext context) {
create: (context) => ContactInvitationListCubit( final activeAccountInfo = context.watch<ActiveAccountInfo>();
activeAccountInfo: widget._activeAccountInfo, final accountData = context.watch<AccountRecordCubit>().state.data;
account: widget._account),
child: responsiveVisibility( if (accountData == null) {
context: context, return waitingPage(context);
phone: false, }
)
? buildTablet(context) return BlocProvider(
: buildPhone(context)); create: (context) => ContactInvitationListCubit(
activeAccountInfo: activeAccountInfo, account: accountData.value),
child: responsiveVisibility(
context: context,
phone: false,
)
? buildTablet(context)
: buildPhone(context));
}
} }

View File

@ -0,0 +1,16 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import '../veilid_support.dart';
abstract class FutureCubit<State> extends Cubit<AsyncValue<State>> {
FutureCubit(Future<State> fut) : super(const AsyncValue.loading()) {
unawaited(fut.then((value) {
emit(AsyncValue.data(value));
// ignore: avoid_types_on_closure_parameters
}, onError: (Object e, StackTrace stackTrace) {
emit(AsyncValue.error(e, stackTrace));
}));
}
}

View File

@ -9,6 +9,7 @@ export 'dht_support/dht_support.dart';
export 'src/async_tag_lock.dart'; export 'src/async_tag_lock.dart';
export 'src/async_value.dart'; export 'src/async_value.dart';
export 'src/config.dart'; export 'src/config.dart';
export 'src/future_cubit.dart';
export 'src/identity.dart'; export 'src/identity.dart';
export 'src/json_tools.dart'; export 'src/json_tools.dart';
export 'src/protobuf_tools.dart'; export 'src/protobuf_tools.dart';