clean up context locators

This commit is contained in:
Christien Rioux 2024-06-15 23:29:15 -04:00
parent 751022e743
commit 2ccad50f9a
31 changed files with 603 additions and 542 deletions

View file

@ -1,3 +1,2 @@
export 'home_account_ready_chat.dart';
export 'home_account_ready_main.dart';
export 'home_account_ready_shell.dart';

View file

@ -6,6 +6,7 @@ import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
import '../../../account_manager/account_manager.dart';
import '../../../chat/chat.dart';
import '../../../proto/proto.dart' as proto;
import '../../../theme/theme.dart';
import '../../../tools/tools.dart';
import 'main_pager/main_pager.dart';
@ -29,7 +30,8 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
}
Widget buildUserPanel() => Builder(builder: (context) {
final account = context.watch<AccountRecordCubit>().state;
final profile = context.select<AccountRecordCubit, proto.Profile>(
(c) => c.state.asData!.value.profile);
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
@ -50,9 +52,7 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
await ctrl.toggle?.call();
//await GoRouterHelper(context).push('/settings');
}).paddingLTRB(0, 0, 8, 0),
asyncValueBuilder(account,
(_, account) => ProfileWidget(profile: account.profile))
.expanded(),
ProfileWidget(profile: profile).expanded(),
]).paddingAll(8),
const MainPager().expanded()
]);
@ -72,8 +72,8 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
return const NoConversationWidget();
}
return ChatComponentWidget.builder(
localConversationRecordKey: activeChatLocalConversationKey,
);
localConversationRecordKey: activeChatLocalConversationKey,
key: ValueKey(activeChatLocalConversationKey));
}
// ignore: prefer_expression_function_bodies

View file

@ -1,158 +0,0 @@
import 'package:async_tools/async_tools.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../account_manager/account_manager.dart';
import '../../../chat/chat.dart';
import '../../../chat_list/chat_list.dart';
import '../../../contact_invitation/contact_invitation.dart';
import '../../../contacts/contacts.dart';
import '../../../conversation/conversation.dart';
import '../../../router/router.dart';
import '../../../theme/theme.dart';
class HomeAccountReadyShell extends StatefulWidget {
factory HomeAccountReadyShell(
{required BuildContext context, required Widget child, Key? key}) {
// These must exist in order for the account to
// be considered 'ready' for this widget subtree
final unlockedAccountInfo = context.watch<UnlockedAccountInfo>();
final routerCubit = context.read<RouterCubit>();
return HomeAccountReadyShell._(
unlockedAccountInfo: unlockedAccountInfo,
routerCubit: routerCubit,
key: key,
child: child);
}
const HomeAccountReadyShell._(
{required this.unlockedAccountInfo,
required this.routerCubit,
required this.child,
super.key});
@override
HomeAccountReadyShellState createState() => HomeAccountReadyShellState();
final Widget child;
final UnlockedAccountInfo unlockedAccountInfo;
final RouterCubit routerCubit;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<UnlockedAccountInfo>(
'unlockedAccountInfo', unlockedAccountInfo))
..add(DiagnosticsProperty<RouterCubit>('routerCubit', routerCubit));
}
}
class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
final SingleStateProcessor<WaitingInvitationsBlocMapState>
_singleInvitationStatusProcessor = SingleStateProcessor();
@override
void initState() {
super.initState();
}
// Process all accepted or rejected invitations
void _invitationStatusListener(
BuildContext context, WaitingInvitationsBlocMapState state) {
_singleInvitationStatusProcessor.updateState(state, (newState) async {
final contactListCubit = context.read<ContactListCubit>();
final contactInvitationListCubit =
context.read<ContactInvitationListCubit>();
for (final entry in newState.entries) {
final contactRequestInboxRecordKey = entry.key;
final invStatus = entry.value.asData?.value;
// Skip invitations that have not yet been accepted or rejected
if (invStatus == null) {
continue;
}
// Delete invitation and process the accepted or rejected contact
final acceptedContact = invStatus.acceptedContact;
if (acceptedContact != null) {
await contactInvitationListCubit.deleteInvitation(
accepted: true,
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
// Accept
await contactListCubit.createContact(
remoteProfile: acceptedContact.remoteProfile,
remoteSuperIdentity: acceptedContact.remoteIdentity,
remoteConversationRecordKey:
acceptedContact.remoteConversationRecordKey,
localConversationRecordKey:
acceptedContact.localConversationRecordKey,
);
} else {
// Reject
await contactInvitationListCubit.deleteInvitation(
accepted: false,
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
}
}
});
}
@override
Widget build(BuildContext context) {
// XXX: Should probably eliminate this in favor
// of streaming changes into other cubits. Too much rebuilding!
// should not need to 'watch' all these cubits
final account = context.watch<AccountRecordCubit>().state.asData?.value;
if (account == null) {
return waitingPage();
}
return MultiBlocProvider(
providers: [
// Contact Cubits
BlocProvider(
create: (context) => ContactInvitationListCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)),
BlocProvider(
create: (context) => ContactListCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)),
BlocProvider(
create: (context) => WaitingInvitationsBlocMapCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)
..follow(context.read<ContactInvitationListCubit>())),
// Chat Cubits
BlocProvider(
create: (context) => ActiveChatCubit(null,
routerCubit: context.read<RouterCubit>())),
BlocProvider(
create: (context) => ChatListCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
activeChatCubit: context.read<ActiveChatCubit>(),
account: account)),
// Conversation Cubits
BlocProvider(
create: (context) => ActiveConversationsBlocMapCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
contactListCubit: context.read<ContactListCubit>(),
accountRecordCubit: context.read<AccountRecordCubit>())
..follow(context.read<ChatListCubit>())),
BlocProvider(
create: (context) => ActiveSingleContactChatBlocMapCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
contactListCubit: context.read<ContactListCubit>(),
chatListCubit: context.read<ChatListCubit>())
..follow(context.read<ActiveConversationsBlocMapCubit>())),
],
child: MultiBlocListener(listeners: [
BlocListener<WaitingInvitationsBlocMapCubit,
WaitingInvitationsBlocMapState>(
listener: _invitationStatusListener,
)
], child: widget.child));
}
}

View file

@ -1,11 +1,20 @@
import 'dart:math';
import 'package:async_tools/async_tools.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
import 'package:provider/provider.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../chat/chat.dart';
import '../../chat_list/chat_list.dart';
import '../../contact_invitation/contact_invitation.dart';
import '../../contacts/contacts.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto;
import '../../router/router.dart';
import '../../theme/theme.dart';
import 'drawer_menu/drawer_menu.dart';
import 'home_account_invalid.dart';
@ -33,25 +42,133 @@ class HomeShellState extends State<HomeShell> {
super.dispose();
}
Widget buildWithLogin(BuildContext context) {
final accountInfo = context.watch<ActiveAccountInfoCubit>().state;
final accountRecordsCubit = context.watch<AccountRecordsBlocMapCubit>();
if (!accountInfo.active) {
// Process all accepted or rejected invitations
void _invitationStatusListener(
BuildContext context, WaitingInvitationsBlocMapState state) {
_singleInvitationStatusProcessor.updateState(state, (newState) async {
final contactListCubit = context.read<ContactListCubit>();
final contactInvitationListCubit =
context.read<ContactInvitationListCubit>();
for (final entry in newState.entries) {
final contactRequestInboxRecordKey = entry.key;
final invStatus = entry.value.asData?.value;
// Skip invitations that have not yet been accepted or rejected
if (invStatus == null) {
continue;
}
// Delete invitation and process the accepted or rejected contact
final acceptedContact = invStatus.acceptedContact;
if (acceptedContact != null) {
await contactInvitationListCubit.deleteInvitation(
accepted: true,
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
// Accept
await contactListCubit.createContact(
profile: acceptedContact.remoteProfile,
remoteSuperIdentity: acceptedContact.remoteIdentity,
remoteConversationRecordKey:
acceptedContact.remoteConversationRecordKey,
localConversationRecordKey:
acceptedContact.localConversationRecordKey,
);
} else {
// Reject
await contactInvitationListCubit.deleteInvitation(
accepted: false,
contactRequestInboxRecordKey: contactRequestInboxRecordKey);
}
}
});
}
Widget _buildActiveAccount(BuildContext context) {
final accountRecordKey = context.select<ActiveAccountInfoCubit, TypedKey>(
(c) => c.state.unlockedAccountInfo!.accountRecordKey);
final contactListRecordPointer =
context.select<AccountRecordCubit, OwnedDHTRecordPointer?>(
(c) => c.state.asData?.value.contactList.toVeilid());
final contactInvitationListRecordPointer =
context.select<AccountRecordCubit, OwnedDHTRecordPointer?>(
(c) => c.state.asData?.value.contactInvitationRecords.toVeilid());
final chatListRecordPointer =
context.select<AccountRecordCubit, OwnedDHTRecordPointer?>(
(c) => c.state.asData?.value.chatList.toVeilid());
if (contactListRecordPointer == null ||
contactInvitationListRecordPointer == null ||
chatListRecordPointer == null) {
return waitingPage();
}
return MultiBlocProvider(
providers: [
// Contact Cubits
BlocProvider(
create: (context) => ContactInvitationListCubit(
locator: context.read,
accountRecordKey: accountRecordKey,
contactInvitationListRecordPointer:
contactInvitationListRecordPointer,
)),
BlocProvider(
create: (context) => ContactListCubit(
locator: context.read,
accountRecordKey: accountRecordKey,
contactListRecordPointer: contactListRecordPointer)),
BlocProvider(
create: (context) => WaitingInvitationsBlocMapCubit(
locator: context.read,
)),
// Chat Cubits
BlocProvider(
create: (context) => ActiveChatCubit(null,
routerCubit: context.read<RouterCubit>())),
BlocProvider(
create: (context) => ChatListCubit(
locator: context.read,
accountRecordKey: accountRecordKey,
chatListRecordPointer: chatListRecordPointer)),
// Conversation Cubits
BlocProvider(
create: (context) => ActiveConversationsBlocMapCubit(
locator: context.read,
)),
BlocProvider(
create: (context) => ActiveSingleContactChatBlocMapCubit(
locator: context.read,
)),
],
child: MultiBlocListener(listeners: [
BlocListener<WaitingInvitationsBlocMapCubit,
WaitingInvitationsBlocMapState>(
listener: _invitationStatusListener,
)
], child: widget.child));
}
Widget _buildWithLogin(BuildContext context) {
// Get active account info status
final (
accountInfoStatus,
accountInfoActive,
superIdentityRecordKey
) = context
.select<ActiveAccountInfoCubit, (AccountInfoStatus, bool, TypedKey?)>(
(c) => (
c.state.status,
c.state.active,
c.state.unlockedAccountInfo?.superIdentityRecordKey
));
if (!accountInfoActive) {
// If no logged in user is active, show the loading panel
return const HomeNoActive();
}
final superIdentityRecordKey =
accountInfo.unlockedAccountInfo?.superIdentityRecordKey;
final activeCubit = superIdentityRecordKey == null
? null
: accountRecordsCubit.tryOperate(superIdentityRecordKey,
closure: (c) => c);
if (activeCubit == null) {
return waitingPage();
}
switch (accountInfo.status) {
switch (accountInfoStatus) {
case AccountInfoStatus.noAccount:
return const HomeAccountMissing();
case AccountInfoStatus.accountInvalid:
@ -59,17 +176,21 @@ class HomeShellState extends State<HomeShell> {
case AccountInfoStatus.accountLocked:
return const HomeAccountLocked();
case AccountInfoStatus.accountReady:
return MultiBlocProvider(
providers: [
BlocProvider<AccountRecordCubit>.value(value: activeCubit),
],
child: MultiProvider(providers: [
Provider<UnlockedAccountInfo>.value(
value: accountInfo.unlockedAccountInfo!,
),
Provider<ZoomDrawerController>.value(
value: _zoomDrawerController),
], child: widget.child));
// Get the current active account record cubit
final activeAccountRecordCubit =
context.select<AccountRecordsBlocMapCubit, AccountRecordCubit?>(
(c) => superIdentityRecordKey == null
? null
: c.tryOperate(superIdentityRecordKey, closure: (x) => x));
if (activeAccountRecordCubit == null) {
return waitingPage();
}
return MultiBlocProvider(providers: [
BlocProvider<AccountRecordCubit>.value(
value: activeAccountRecordCubit),
], child: Builder(builder: _buildActiveAccount));
}
}
@ -96,7 +217,9 @@ class HomeShellState extends State<HomeShell> {
mainScreen: DecoratedBox(
decoration: BoxDecoration(
color: scale.primaryScale.activeElementBackground),
child: buildWithLogin(context)),
child: Provider<ZoomDrawerController>.value(
value: _zoomDrawerController,
child: Builder(builder: _buildWithLogin))),
borderRadius: 24,
showShadow: true,
angle: 0,
@ -112,5 +235,54 @@ class HomeShellState extends State<HomeShell> {
)));
}
final ZoomDrawerController _zoomDrawerController = ZoomDrawerController();
final _zoomDrawerController = ZoomDrawerController();
final _singleInvitationStatusProcessor =
SingleStateProcessor<WaitingInvitationsBlocMapState>();
}
// class HomeAccountReadyShell extends StatefulWidget {
// factory HomeAccountReadyShell(
// {required BuildContext context, required Widget child, Key? key}) {
// // These must exist in order for the account to
// // be considered 'ready' for this widget subtree
// final unlockedAccountInfo = context.watch<UnlockedAccountInfo>();
// final routerCubit = context.read<RouterCubit>();
// return HomeAccountReadyShell._(
// unlockedAccountInfo: unlockedAccountInfo,
// routerCubit: routerCubit,
// key: key,
// child: child);
// }
// const HomeAccountReadyShell._(
// {required this.unlockedAccountInfo,
// required this.routerCubit,
// required this.child,
// super.key});
// @override
// HomeAccountReadyShellState createState() => HomeAccountReadyShellState();
// final Widget child;
// final UnlockedAccountInfo unlockedAccountInfo;
// final RouterCubit routerCubit;
// @override
// void debugFillProperties(DiagnosticPropertiesBuilder properties) {
// super.debugFillProperties(properties);
// properties
// ..add(DiagnosticsProperty<UnlockedAccountInfo>(
// 'unlockedAccountInfo', unlockedAccountInfo))
// ..add(DiagnosticsProperty<RouterCubit>('routerCubit', routerCubit));
// }
// }
// class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
// final SingleStateProcessor<WaitingInvitationsBlocMapState>
// _singleInvitationStatusProcessor = SingleStateProcessor();
// @override
// void initState() {
// super.initState();
// }
// }