mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-02-22 15:59:50 -05:00
refactor
This commit is contained in:
parent
2ccad50f9a
commit
360ba436f8
@ -1,23 +1,23 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../models/models.dart';
|
||||
import '../repository/account_repository.dart';
|
||||
|
||||
class ActiveAccountInfoCubit extends Cubit<AccountInfo> {
|
||||
ActiveAccountInfoCubit(AccountRepository accountRepository)
|
||||
class AccountInfoCubit extends Cubit<AccountInfo> {
|
||||
AccountInfoCubit(
|
||||
AccountRepository accountRepository, TypedKey superIdentityRecordKey)
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository
|
||||
.getAccountInfo(accountRepository.getActiveLocalAccount())) {
|
||||
super(accountRepository.getAccountInfo(superIdentityRecordKey)) {
|
||||
// Subscribe to streams
|
||||
_accountRepositorySubscription = _accountRepository.stream.listen((change) {
|
||||
switch (change) {
|
||||
case AccountRepositoryChange.activeLocalAccount:
|
||||
case AccountRepositoryChange.localAccounts:
|
||||
case AccountRepositoryChange.userLogins:
|
||||
emit(accountRepository
|
||||
.getAccountInfo(accountRepository.getActiveLocalAccount()));
|
||||
emit(accountRepository.getAccountInfo(superIdentityRecordKey));
|
||||
break;
|
||||
}
|
||||
});
|
@ -11,7 +11,7 @@ typedef AccountRecordState = proto.Account;
|
||||
/// The saved state of a VeilidChat Account on the DHT
|
||||
/// Used to synchronize status, profile, and options for a specific account
|
||||
/// across multiple clients. This DHT record is the 'source of truth' for an
|
||||
/// account and is privately encrypted with an owned recrod from the 'userLogin'
|
||||
/// account and is privately encrypted with an owned record from the 'userLogin'
|
||||
/// tabledb-local storage, encrypted by the unlock code for the account.
|
||||
class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
||||
AccountRecordCubit(
|
||||
|
35
lib/account_manager/cubits/active_local_account_cubit.dart
Normal file
35
lib/account_manager/cubits/active_local_account_cubit.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../repository/account_repository.dart';
|
||||
|
||||
class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
|
||||
ActiveLocalAccountCubit(AccountRepository accountRepository)
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository.getActiveLocalAccount()) {
|
||||
// Subscribe to streams
|
||||
_accountRepositorySubscription = _accountRepository.stream.listen((change) {
|
||||
switch (change) {
|
||||
case AccountRepositoryChange.activeLocalAccount:
|
||||
emit(_accountRepository.getActiveLocalAccount());
|
||||
break;
|
||||
// Ignore these
|
||||
case AccountRepositoryChange.localAccounts:
|
||||
case AccountRepositoryChange.userLogins:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await super.close();
|
||||
await _accountRepositorySubscription.cancel();
|
||||
}
|
||||
|
||||
final AccountRepository _accountRepository;
|
||||
late final StreamSubscription<AccountRepositoryChange>
|
||||
_accountRepositorySubscription;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
export 'account_info_cubit.dart';
|
||||
export 'account_record_cubit.dart';
|
||||
export 'account_records_bloc_map_cubit.dart';
|
||||
export 'active_account_info_cubit.dart';
|
||||
export 'active_local_account_cubit.dart';
|
||||
export 'local_accounts_cubit.dart';
|
||||
export 'user_logins_cubit.dart';
|
||||
|
@ -7,7 +7,7 @@ enum AccountInfoStatus {
|
||||
noAccount,
|
||||
accountInvalid,
|
||||
accountLocked,
|
||||
accountReady,
|
||||
accountUnlocked,
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
@ -129,7 +129,7 @@ class AccountRepository {
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountReady,
|
||||
status: AccountInfoStatus.accountUnlocked,
|
||||
active: active,
|
||||
unlockedAccountInfo:
|
||||
UnlockedAccountInfo(localAccount: localAccount, userLogin: userLogin),
|
||||
|
@ -70,9 +70,6 @@ class _EditAccountPageState extends State<EditAccountPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final displayModalHUD = _isInAsyncCall;
|
||||
final accountRecordsCubit = context.watch<AccountRecordsBlocMapCubit>();
|
||||
final accountRecordCubit = accountRecordsCubit
|
||||
.operate(widget.superIdentityRecordKey, closure: (c) => c);
|
||||
|
||||
return Scaffold(
|
||||
// resizeToAvoidBottomInset: false,
|
||||
@ -118,9 +115,15 @@ class _EditAccountPageState extends State<EditAccountPage> {
|
||||
_isInAsyncCall = true;
|
||||
});
|
||||
try {
|
||||
// Update account profile DHT record
|
||||
// This triggers ConversationCubits to update
|
||||
await accountRecordCubit.updateProfile(newProfile);
|
||||
// Look up account cubit for this specific account
|
||||
final accountRecordsCubit =
|
||||
context.read<AccountRecordsBlocMapCubit>();
|
||||
await accountRecordsCubit.operateAsync(
|
||||
widget.superIdentityRecordKey, closure: (c) async {
|
||||
// Update account profile DHT record
|
||||
// This triggers ConversationCubits to update
|
||||
await c.updateProfile(newProfile);
|
||||
});
|
||||
|
||||
// Update local account profile
|
||||
await AccountRepository.instance.editAccountProfile(
|
||||
|
@ -11,6 +11,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import 'account_manager/account_manager.dart';
|
||||
import 'account_manager/cubits/active_local_account_cubit.dart';
|
||||
import 'init.dart';
|
||||
import 'layout/splash.dart';
|
||||
import 'router/router.dart';
|
||||
@ -122,9 +123,9 @@ class VeilidChatApp extends StatelessWidget {
|
||||
create: (context) =>
|
||||
UserLoginsCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<ActiveAccountInfoCubit>(
|
||||
BlocProvider<ActiveLocalAccountCubit>(
|
||||
create: (context) =>
|
||||
ActiveAccountInfoCubit(AccountRepository.instance),
|
||||
ActiveLocalAccountCubit(AccountRepository.instance),
|
||||
),
|
||||
BlocProvider<PreferencesCubit>(
|
||||
create: (context) =>
|
||||
|
@ -1,19 +1,13 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../router/router.dart';
|
||||
|
||||
// XXX: if we ever want to have more than one chat 'open', we should put the
|
||||
// operations and state for that here.
|
||||
|
||||
class ActiveChatCubit extends Cubit<TypedKey?> {
|
||||
ActiveChatCubit(super.initialState, {required RouterCubit routerCubit})
|
||||
: _routerCubit = routerCubit;
|
||||
ActiveChatCubit(super.initialState);
|
||||
|
||||
void setActiveChat(TypedKey? activeChatLocalConversationRecordKey) {
|
||||
emit(activeChatLocalConversationRecordKey);
|
||||
_routerCubit.setHasActiveChat(activeChatLocalConversationRecordKey != null);
|
||||
}
|
||||
|
||||
final RouterCubit _routerCubit;
|
||||
}
|
||||
|
@ -27,15 +27,19 @@ const metadataKeyAttachments = 'attachments';
|
||||
|
||||
class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
ChatComponentCubit._({
|
||||
required Locator locator,
|
||||
required List<ActiveConversationCubit> conversationCubits,
|
||||
required SingleContactMessagesCubit messagesCubit,
|
||||
required types.User localUser,
|
||||
required IMap<TypedKey, types.User> remoteUsers,
|
||||
}) : _messagesCubit = messagesCubit,
|
||||
}) : _locator = locator,
|
||||
_conversationCubits = conversationCubits,
|
||||
_messagesCubit = messagesCubit,
|
||||
super(ChatComponentState(
|
||||
chatKey: GlobalKey<ChatState>(),
|
||||
scrollController: AutoScrollController(),
|
||||
localUser: localUser,
|
||||
remoteUsers: remoteUsers,
|
||||
localUser: null,
|
||||
remoteUsers: const IMap.empty(),
|
||||
historicalRemoteUsers: const IMap.empty(),
|
||||
unknownUsers: const IMap.empty(),
|
||||
messageWindow: const AsyncLoading(),
|
||||
title: '',
|
||||
)) {
|
||||
@ -43,58 +47,40 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
_initWait.add(_init);
|
||||
}
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static ChatComponentCubit singleContact(
|
||||
{required Locator locator,
|
||||
required ActiveConversationCubit activeConversationCubit,
|
||||
required SingleContactMessagesCubit messagesCubit}) {
|
||||
// Get account info
|
||||
final unlockedAccountInfo =
|
||||
locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final account = locator<AccountRecordCubit>().state.asData!.value;
|
||||
|
||||
// Make local 'User'
|
||||
final localUserIdentityKey = unlockedAccountInfo.identityTypedPublicKey;
|
||||
final localUser = types.User(
|
||||
id: localUserIdentityKey.toString(),
|
||||
firstName: account.profile.name,
|
||||
metadata: {metadataKeyIdentityPublicKey: localUserIdentityKey});
|
||||
|
||||
// Make remote 'User's
|
||||
final remoteUsers = {
|
||||
activeConversationState.contact.identityPublicKey.toVeilid(): types.User(
|
||||
id: activeConversationState.contact.identityPublicKey
|
||||
.toVeilid()
|
||||
.toString(),
|
||||
firstName: activeConversationState.contact.displayName,
|
||||
metadata: {
|
||||
metadataKeyIdentityPublicKey:
|
||||
activeConversationState.contact.identityPublicKey.toVeilid()
|
||||
})
|
||||
}.toIMap();
|
||||
|
||||
return ChatComponentCubit._(
|
||||
messagesCubit: messagesCubit,
|
||||
localUser: localUser,
|
||||
remoteUsers: remoteUsers,
|
||||
);
|
||||
}
|
||||
factory ChatComponentCubit.singleContact(
|
||||
{required Locator locator,
|
||||
required ActiveConversationCubit activeConversationCubit,
|
||||
required SingleContactMessagesCubit messagesCubit}) =>
|
||||
ChatComponentCubit._(
|
||||
locator: locator,
|
||||
conversationCubits: [activeConversationCubit],
|
||||
messagesCubit: messagesCubit,
|
||||
);
|
||||
|
||||
Future<void> _init() async {
|
||||
_messagesSubscription = _messagesCubit.stream.listen((messagesState) {
|
||||
emit(state.copyWith(
|
||||
messageWindow: _convertMessages(messagesState),
|
||||
));
|
||||
});
|
||||
emit(state.copyWith(
|
||||
messageWindow: _convertMessages(_messagesCubit.state),
|
||||
title: _getTitle(),
|
||||
));
|
||||
// Get local user info and account record cubit
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_localUserIdentityKey = unlockedAccountInfo.identityTypedPublicKey;
|
||||
_localUserAccountRecordCubit = _locator<AccountRecordCubit>();
|
||||
|
||||
// Subscribe to local user info
|
||||
_localUserAccountRecordSubscription = _localUserAccountRecordCubit.stream
|
||||
.listen(_onChangedLocalUserAccountRecord);
|
||||
_onChangedLocalUserAccountRecord(_localUserAccountRecordCubit.state);
|
||||
|
||||
// Subscribe to remote user info
|
||||
await _updateConversationSubscriptions();
|
||||
|
||||
// Subscribe to messages
|
||||
_messagesSubscription = _messagesCubit.stream.listen(_onChangedMessages);
|
||||
_onChangedMessages(_messagesCubit.state);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _initWait();
|
||||
await _localUserAccountRecordSubscription.cancel();
|
||||
await _messagesSubscription.cancel();
|
||||
await super.close();
|
||||
}
|
||||
@ -159,23 +145,130 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private Implementation
|
||||
|
||||
String _getTitle() {
|
||||
if (state.remoteUsers.length == 1) {
|
||||
final remoteUser = state.remoteUsers.values.first;
|
||||
return remoteUser.firstName ?? '<unnamed>';
|
||||
} else {
|
||||
return '<group chat with ${state.remoteUsers.length} users>';
|
||||
void _onChangedLocalUserAccountRecord(AsyncValue<proto.Account> avAccount) {
|
||||
final account = avAccount.asData?.value;
|
||||
if (account == null) {
|
||||
emit(state.copyWith(localUser: null));
|
||||
return;
|
||||
}
|
||||
// Make local 'User'
|
||||
final localUser = types.User(
|
||||
id: _localUserIdentityKey.toString(),
|
||||
firstName: account.profile.name,
|
||||
metadata: {metadataKeyIdentityPublicKey: _localUserIdentityKey});
|
||||
emit(state.copyWith(localUser: localUser));
|
||||
}
|
||||
|
||||
types.Message? _messageStateToChatMessage(MessageState message) {
|
||||
void _onChangedMessages(
|
||||
AsyncValue<WindowState<MessageState>> avMessagesState) {
|
||||
emit(_convertMessages(state, avMessagesState));
|
||||
}
|
||||
|
||||
void _onChangedConversation(
|
||||
TypedKey remoteIdentityPublicKey,
|
||||
AsyncValue<ActiveConversationState> avConversationState,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
types.User _convertRemoteUser(TypedKey remoteIdentityPublicKey,
|
||||
ActiveConversationState activeConversationState) =>
|
||||
types.User(
|
||||
id: remoteIdentityPublicKey.toString(),
|
||||
firstName: activeConversationState.contact.displayName,
|
||||
metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey});
|
||||
|
||||
types.User _convertUnknownUser(TypedKey remoteIdentityPublicKey) =>
|
||||
types.User(
|
||||
id: remoteIdentityPublicKey.toString(),
|
||||
firstName: '<$remoteIdentityPublicKey>',
|
||||
metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey});
|
||||
|
||||
Future<void> _updateConversationSubscriptions() async {
|
||||
// Get existing subscription keys and state
|
||||
final existing = _conversationSubscriptions.keys.toList();
|
||||
var currentRemoteUsersState = state.remoteUsers;
|
||||
|
||||
// Process cubit list
|
||||
for (final cc in _conversationCubits) {
|
||||
// Get the remote identity key
|
||||
final remoteIdentityPublicKey = cc.input.remoteIdentityPublicKey;
|
||||
|
||||
// If the cubit is already being listened to we have nothing to do
|
||||
if (existing.remove(remoteIdentityPublicKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the cubit is not already being listened to we should do that
|
||||
_conversationSubscriptions[remoteIdentityPublicKey] = cc.stream.listen(
|
||||
(avConv) => _onChangedConversation(remoteIdentityPublicKey, avConv));
|
||||
final activeConversationState = cc.state.asData?.value;
|
||||
if (activeConversationState != null) {
|
||||
final remoteUser = _convertRemoteUser(
|
||||
remoteIdentityPublicKey, activeConversationState);
|
||||
currentRemoteUsersState =
|
||||
currentRemoteUsersState.add(remoteIdentityPublicKey, remoteUser);
|
||||
}
|
||||
}
|
||||
// Purge remote users we didn't see in the cubit list any more
|
||||
final cancels = <Future<void>>[];
|
||||
for (final deadUser in existing) {
|
||||
currentRemoteUsersState = currentRemoteUsersState.remove(deadUser);
|
||||
cancels.add(_conversationSubscriptions.remove(deadUser)!.cancel());
|
||||
}
|
||||
await cancels.wait;
|
||||
|
||||
// Emit change to remote users state
|
||||
emit(_updateTitle(state.copyWith(remoteUsers: currentRemoteUsersState)));
|
||||
}
|
||||
|
||||
ChatComponentState _updateTitle(ChatComponentState currentState) {
|
||||
if (currentState.remoteUsers.length == 0) {
|
||||
return currentState.copyWith(title: 'Empty Chat');
|
||||
}
|
||||
if (currentState.remoteUsers.length == 1) {
|
||||
final remoteUser = currentState.remoteUsers.values.first;
|
||||
return currentState.copyWith(title: remoteUser.firstName ?? '<unnamed>');
|
||||
}
|
||||
return currentState.copyWith(
|
||||
title: '<group chat with ${state.remoteUsers.length} users>');
|
||||
}
|
||||
|
||||
(ChatComponentState, types.Message?) _messageStateToChatMessage(
|
||||
ChatComponentState currentState, MessageState message) {
|
||||
final authorIdentityPublicKey = message.content.author.toVeilid();
|
||||
final author =
|
||||
state.remoteUsers[authorIdentityPublicKey] ?? state.localUser;
|
||||
late final types.User author;
|
||||
if (authorIdentityPublicKey == _localUserIdentityKey &&
|
||||
currentState.localUser != null) {
|
||||
author = currentState.localUser!;
|
||||
} else {
|
||||
final remoteUser = currentState.remoteUsers[authorIdentityPublicKey];
|
||||
if (remoteUser != null) {
|
||||
author = remoteUser;
|
||||
} else {
|
||||
final historicalRemoteUser =
|
||||
currentState.historicalRemoteUsers[authorIdentityPublicKey];
|
||||
if (historicalRemoteUser != null) {
|
||||
author = historicalRemoteUser;
|
||||
} else {
|
||||
final unknownRemoteUser =
|
||||
currentState.unknownUsers[authorIdentityPublicKey];
|
||||
if (unknownRemoteUser != null) {
|
||||
author = unknownRemoteUser;
|
||||
} else {
|
||||
final unknownUser = _convertUnknownUser(authorIdentityPublicKey);
|
||||
currentState = currentState.copyWith(
|
||||
unknownUsers: currentState.unknownUsers
|
||||
.add(authorIdentityPublicKey, unknownUser));
|
||||
author = unknownUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
types.Status? status;
|
||||
if (message.sendState != null) {
|
||||
assert(author == state.localUser,
|
||||
assert(author.id == _localUserIdentityKey.toString(),
|
||||
'send state should only be on sent messages');
|
||||
switch (message.sendState!) {
|
||||
case MessageSendState.sending:
|
||||
@ -198,7 +291,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
text: contextText.text,
|
||||
showStatus: status != null,
|
||||
status: status);
|
||||
return textMessage;
|
||||
return (currentState, textMessage);
|
||||
case proto.Message_Kind.secret:
|
||||
case proto.Message_Kind.delete:
|
||||
case proto.Message_Kind.erase:
|
||||
@ -207,17 +300,24 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
case proto.Message_Kind.membership:
|
||||
case proto.Message_Kind.moderation:
|
||||
case proto.Message_Kind.notSet:
|
||||
return null;
|
||||
return (currentState, null);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncValue<WindowState<types.Message>> _convertMessages(
|
||||
ChatComponentState _convertMessages(ChatComponentState currentState,
|
||||
AsyncValue<WindowState<MessageState>> avMessagesState) {
|
||||
// Clear out unknown users
|
||||
currentState = state.copyWith(unknownUsers: const IMap.empty());
|
||||
|
||||
final asError = avMessagesState.asError;
|
||||
if (asError != null) {
|
||||
return AsyncValue.error(asError.error, asError.stackTrace);
|
||||
return currentState.copyWith(
|
||||
unknownUsers: const IMap.empty(),
|
||||
messageWindow: AsyncValue.error(asError.error, asError.stackTrace));
|
||||
} else if (avMessagesState.asLoading != null) {
|
||||
return const AsyncValue.loading();
|
||||
return currentState.copyWith(
|
||||
unknownUsers: const IMap.empty(),
|
||||
messageWindow: const AsyncValue.loading());
|
||||
}
|
||||
final messagesState = avMessagesState.asData!.value;
|
||||
|
||||
@ -225,7 +325,9 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
final chatMessages = <types.Message>[];
|
||||
final tsSet = <String>{};
|
||||
for (final message in messagesState.window) {
|
||||
final chatMessage = _messageStateToChatMessage(message);
|
||||
final (newState, chatMessage) =
|
||||
_messageStateToChatMessage(currentState, message);
|
||||
currentState = newState;
|
||||
if (chatMessage == null) {
|
||||
continue;
|
||||
}
|
||||
@ -238,12 +340,13 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
assert(false, 'should not have duplicate id');
|
||||
}
|
||||
}
|
||||
return AsyncValue.data(WindowState<types.Message>(
|
||||
window: chatMessages.toIList(),
|
||||
length: messagesState.length,
|
||||
windowTail: messagesState.windowTail,
|
||||
windowCount: messagesState.windowCount,
|
||||
follow: messagesState.follow));
|
||||
return currentState.copyWith(
|
||||
messageWindow: AsyncValue.data(WindowState<types.Message>(
|
||||
window: chatMessages.toIList(),
|
||||
length: messagesState.length,
|
||||
windowTail: messagesState.windowTail,
|
||||
windowCount: messagesState.windowCount,
|
||||
follow: messagesState.follow)));
|
||||
}
|
||||
|
||||
void _addTextMessage(
|
||||
@ -271,7 +374,18 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final _initWait = WaitSet<void>();
|
||||
|
||||
final Locator _locator;
|
||||
final List<ActiveConversationCubit> _conversationCubits;
|
||||
final SingleContactMessagesCubit _messagesCubit;
|
||||
|
||||
late final TypedKey _localUserIdentityKey;
|
||||
late final AccountRecordCubit _localUserAccountRecordCubit;
|
||||
late final StreamSubscription<AsyncValue<proto.Account>>
|
||||
_localUserAccountRecordSubscription;
|
||||
final Map<TypedKey, StreamSubscription<AsyncValue<ActiveConversationState>>>
|
||||
_conversationSubscriptions = {};
|
||||
late StreamSubscription<SingleContactMessagesState> _messagesSubscription;
|
||||
|
||||
double scrollOffset = 0;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
// Initialize everything
|
||||
Future<void> _init() async {
|
||||
_unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
|
||||
_unsentMessagesQueue = PersistentQueue<proto.Message>(
|
||||
table: 'SingleContactUnsentMessages',
|
||||
|
@ -20,9 +20,13 @@ class ChatComponentState with _$ChatComponentState {
|
||||
// ScrollController for the chat
|
||||
required AutoScrollController scrollController,
|
||||
// Local user
|
||||
required User localUser,
|
||||
// Remote users
|
||||
required User? localUser,
|
||||
// Active remote users
|
||||
required IMap<TypedKey, User> remoteUsers,
|
||||
// Historical remote users
|
||||
required IMap<TypedKey, User> historicalRemoteUsers,
|
||||
// Unknown users
|
||||
required IMap<TypedKey, User> unknownUsers,
|
||||
// Messages state
|
||||
required AsyncValue<WindowState<Message>> messageWindow,
|
||||
// Title of the chat
|
||||
|
@ -21,8 +21,13 @@ mixin _$ChatComponentState {
|
||||
throw _privateConstructorUsedError; // ScrollController for the chat
|
||||
AutoScrollController get scrollController =>
|
||||
throw _privateConstructorUsedError; // Local user
|
||||
User get localUser => throw _privateConstructorUsedError; // Remote users
|
||||
User? get localUser =>
|
||||
throw _privateConstructorUsedError; // Active remote users
|
||||
IMap<Typed<FixedEncodedString43>, User> get remoteUsers =>
|
||||
throw _privateConstructorUsedError; // Historical remote users
|
||||
IMap<Typed<FixedEncodedString43>, User> get historicalRemoteUsers =>
|
||||
throw _privateConstructorUsedError; // Unknown users
|
||||
IMap<Typed<FixedEncodedString43>, User> get unknownUsers =>
|
||||
throw _privateConstructorUsedError; // Messages state
|
||||
AsyncValue<WindowState<Message>> get messageWindow =>
|
||||
throw _privateConstructorUsedError; // Title of the chat
|
||||
@ -42,8 +47,10 @@ abstract class $ChatComponentStateCopyWith<$Res> {
|
||||
$Res call(
|
||||
{GlobalKey<ChatState> chatKey,
|
||||
AutoScrollController scrollController,
|
||||
User localUser,
|
||||
User? localUser,
|
||||
IMap<Typed<FixedEncodedString43>, User> remoteUsers,
|
||||
IMap<Typed<FixedEncodedString43>, User> historicalRemoteUsers,
|
||||
IMap<Typed<FixedEncodedString43>, User> unknownUsers,
|
||||
AsyncValue<WindowState<Message>> messageWindow,
|
||||
String title});
|
||||
|
||||
@ -65,8 +72,10 @@ class _$ChatComponentStateCopyWithImpl<$Res, $Val extends ChatComponentState>
|
||||
$Res call({
|
||||
Object? chatKey = null,
|
||||
Object? scrollController = null,
|
||||
Object? localUser = null,
|
||||
Object? localUser = freezed,
|
||||
Object? remoteUsers = null,
|
||||
Object? historicalRemoteUsers = null,
|
||||
Object? unknownUsers = null,
|
||||
Object? messageWindow = null,
|
||||
Object? title = null,
|
||||
}) {
|
||||
@ -79,14 +88,22 @@ class _$ChatComponentStateCopyWithImpl<$Res, $Val extends ChatComponentState>
|
||||
? _value.scrollController
|
||||
: scrollController // ignore: cast_nullable_to_non_nullable
|
||||
as AutoScrollController,
|
||||
localUser: null == localUser
|
||||
localUser: freezed == localUser
|
||||
? _value.localUser
|
||||
: localUser // ignore: cast_nullable_to_non_nullable
|
||||
as User,
|
||||
as User?,
|
||||
remoteUsers: null == remoteUsers
|
||||
? _value.remoteUsers
|
||||
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
historicalRemoteUsers: null == historicalRemoteUsers
|
||||
? _value.historicalRemoteUsers
|
||||
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
unknownUsers: null == unknownUsers
|
||||
? _value.unknownUsers
|
||||
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
messageWindow: null == messageWindow
|
||||
? _value.messageWindow
|
||||
: messageWindow // ignore: cast_nullable_to_non_nullable
|
||||
@ -119,8 +136,10 @@ abstract class _$$ChatComponentStateImplCopyWith<$Res>
|
||||
$Res call(
|
||||
{GlobalKey<ChatState> chatKey,
|
||||
AutoScrollController scrollController,
|
||||
User localUser,
|
||||
User? localUser,
|
||||
IMap<Typed<FixedEncodedString43>, User> remoteUsers,
|
||||
IMap<Typed<FixedEncodedString43>, User> historicalRemoteUsers,
|
||||
IMap<Typed<FixedEncodedString43>, User> unknownUsers,
|
||||
AsyncValue<WindowState<Message>> messageWindow,
|
||||
String title});
|
||||
|
||||
@ -141,8 +160,10 @@ class __$$ChatComponentStateImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? chatKey = null,
|
||||
Object? scrollController = null,
|
||||
Object? localUser = null,
|
||||
Object? localUser = freezed,
|
||||
Object? remoteUsers = null,
|
||||
Object? historicalRemoteUsers = null,
|
||||
Object? unknownUsers = null,
|
||||
Object? messageWindow = null,
|
||||
Object? title = null,
|
||||
}) {
|
||||
@ -155,14 +176,22 @@ class __$$ChatComponentStateImplCopyWithImpl<$Res>
|
||||
? _value.scrollController
|
||||
: scrollController // ignore: cast_nullable_to_non_nullable
|
||||
as AutoScrollController,
|
||||
localUser: null == localUser
|
||||
localUser: freezed == localUser
|
||||
? _value.localUser
|
||||
: localUser // ignore: cast_nullable_to_non_nullable
|
||||
as User,
|
||||
as User?,
|
||||
remoteUsers: null == remoteUsers
|
||||
? _value.remoteUsers
|
||||
: remoteUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
historicalRemoteUsers: null == historicalRemoteUsers
|
||||
? _value.historicalRemoteUsers
|
||||
: historicalRemoteUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
unknownUsers: null == unknownUsers
|
||||
? _value.unknownUsers
|
||||
: unknownUsers // ignore: cast_nullable_to_non_nullable
|
||||
as IMap<Typed<FixedEncodedString43>, User>,
|
||||
messageWindow: null == messageWindow
|
||||
? _value.messageWindow
|
||||
: messageWindow // ignore: cast_nullable_to_non_nullable
|
||||
@ -183,6 +212,8 @@ class _$ChatComponentStateImpl implements _ChatComponentState {
|
||||
required this.scrollController,
|
||||
required this.localUser,
|
||||
required this.remoteUsers,
|
||||
required this.historicalRemoteUsers,
|
||||
required this.unknownUsers,
|
||||
required this.messageWindow,
|
||||
required this.title});
|
||||
|
||||
@ -194,10 +225,16 @@ class _$ChatComponentStateImpl implements _ChatComponentState {
|
||||
final AutoScrollController scrollController;
|
||||
// Local user
|
||||
@override
|
||||
final User localUser;
|
||||
// Remote users
|
||||
final User? localUser;
|
||||
// Active remote users
|
||||
@override
|
||||
final IMap<Typed<FixedEncodedString43>, User> remoteUsers;
|
||||
// Historical remote users
|
||||
@override
|
||||
final IMap<Typed<FixedEncodedString43>, User> historicalRemoteUsers;
|
||||
// Unknown users
|
||||
@override
|
||||
final IMap<Typed<FixedEncodedString43>, User> unknownUsers;
|
||||
// Messages state
|
||||
@override
|
||||
final AsyncValue<WindowState<Message>> messageWindow;
|
||||
@ -207,7 +244,7 @@ class _$ChatComponentStateImpl implements _ChatComponentState {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatComponentState(chatKey: $chatKey, scrollController: $scrollController, localUser: $localUser, remoteUsers: $remoteUsers, messageWindow: $messageWindow, title: $title)';
|
||||
return 'ChatComponentState(chatKey: $chatKey, scrollController: $scrollController, localUser: $localUser, remoteUsers: $remoteUsers, historicalRemoteUsers: $historicalRemoteUsers, unknownUsers: $unknownUsers, messageWindow: $messageWindow, title: $title)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -222,14 +259,26 @@ class _$ChatComponentStateImpl implements _ChatComponentState {
|
||||
other.localUser == localUser) &&
|
||||
(identical(other.remoteUsers, remoteUsers) ||
|
||||
other.remoteUsers == remoteUsers) &&
|
||||
(identical(other.historicalRemoteUsers, historicalRemoteUsers) ||
|
||||
other.historicalRemoteUsers == historicalRemoteUsers) &&
|
||||
(identical(other.unknownUsers, unknownUsers) ||
|
||||
other.unknownUsers == unknownUsers) &&
|
||||
(identical(other.messageWindow, messageWindow) ||
|
||||
other.messageWindow == messageWindow) &&
|
||||
(identical(other.title, title) || other.title == title));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, chatKey, scrollController,
|
||||
localUser, remoteUsers, messageWindow, title);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
chatKey,
|
||||
scrollController,
|
||||
localUser,
|
||||
remoteUsers,
|
||||
historicalRemoteUsers,
|
||||
unknownUsers,
|
||||
messageWindow,
|
||||
title);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -243,8 +292,11 @@ abstract class _ChatComponentState implements ChatComponentState {
|
||||
const factory _ChatComponentState(
|
||||
{required final GlobalKey<ChatState> chatKey,
|
||||
required final AutoScrollController scrollController,
|
||||
required final User localUser,
|
||||
required final User? localUser,
|
||||
required final IMap<Typed<FixedEncodedString43>, User> remoteUsers,
|
||||
required final IMap<Typed<FixedEncodedString43>, User>
|
||||
historicalRemoteUsers,
|
||||
required final IMap<Typed<FixedEncodedString43>, User> unknownUsers,
|
||||
required final AsyncValue<WindowState<Message>> messageWindow,
|
||||
required final String title}) = _$ChatComponentStateImpl;
|
||||
|
||||
@ -253,9 +305,13 @@ abstract class _ChatComponentState implements ChatComponentState {
|
||||
@override // ScrollController for the chat
|
||||
AutoScrollController get scrollController;
|
||||
@override // Local user
|
||||
User get localUser;
|
||||
@override // Remote users
|
||||
User? get localUser;
|
||||
@override // Active remote users
|
||||
IMap<Typed<FixedEncodedString43>, User> get remoteUsers;
|
||||
@override // Historical remote users
|
||||
IMap<Typed<FixedEncodedString43>, User> get historicalRemoteUsers;
|
||||
@override // Unknown users
|
||||
IMap<Typed<FixedEncodedString43>, User> get unknownUsers;
|
||||
@override // Messages state
|
||||
AsyncValue<WindowState<Message>> get messageWindow;
|
||||
@override // Title of the chat
|
||||
|
@ -8,7 +8,6 @@ import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../chat.dart';
|
||||
@ -147,6 +146,11 @@ class ChatComponentWidget extends StatelessWidget {
|
||||
final chatComponentCubit = context.watch<ChatComponentCubit>();
|
||||
final chatComponentState = chatComponentCubit.state;
|
||||
|
||||
final localUser = chatComponentState.localUser;
|
||||
if (localUser == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
|
||||
final messageWindow = chatComponentState.messageWindow.asData?.value;
|
||||
if (messageWindow == null) {
|
||||
return chatComponentState.messageWindow.buildNotData();
|
||||
@ -269,7 +273,7 @@ class ChatComponentWidget extends StatelessWidget {
|
||||
_handleSendPressed(chatComponentCubit, pt),
|
||||
//showUserAvatars: false,
|
||||
//showUserNames: true,
|
||||
user: chatComponentState.localUser,
|
||||
user: localUser,
|
||||
emptyState: const EmptyChatWidget())),
|
||||
),
|
||||
),
|
||||
|
@ -69,7 +69,7 @@ class ContactInvitationListCubit
|
||||
final contactRequestWriter = await crcs.generateKeyPair();
|
||||
|
||||
final activeAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final profile = _locator<AccountRecordCubit>().state.asData!.value.profile;
|
||||
|
||||
final idcs = await activeAccountInfo.identityCryptoSystem;
|
||||
@ -247,7 +247,8 @@ class ContactInvitationListCubit
|
||||
await (await pool.openRecordRead(contactRequestInboxKey,
|
||||
debugName: 'ContactInvitationListCubit::validateInvitation::'
|
||||
'ContactRequestInbox',
|
||||
parent: _accountRecordKey))
|
||||
parent: pool.getParentRecordKey(contactRequestInboxKey) ??
|
||||
_accountRecordKey))
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
//
|
||||
final contactRequest = await contactRequestInbox
|
||||
|
@ -23,7 +23,7 @@ class ContactRequestInboxCubit
|
||||
final pool = DHTRecordPool.instance;
|
||||
|
||||
final unlockedAccountInfo =
|
||||
locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
|
||||
final writerSecret = contactInvitationRecord.writerSecret.toVeilid();
|
||||
|
@ -31,7 +31,7 @@ class ValidContactInvitation {
|
||||
final pool = DHTRecordPool.instance;
|
||||
try {
|
||||
final unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final identityPublicKey = unlockedAccountInfo.identityPublicKey;
|
||||
|
||||
@ -43,7 +43,8 @@ class ValidContactInvitation {
|
||||
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
||||
debugName: 'ValidContactInvitation::accept::'
|
||||
'ContactRequestInbox',
|
||||
parent: accountRecordKey))
|
||||
parent: pool.getParentRecordKey(_contactRequestInboxKey) ??
|
||||
accountRecordKey))
|
||||
// ignore: prefer_expression_function_bodies
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
// Create local conversation key for this
|
||||
@ -96,7 +97,7 @@ class ValidContactInvitation {
|
||||
final pool = DHTRecordPool.instance;
|
||||
|
||||
final unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final identityPublicKey = unlockedAccountInfo.identityPublicKey;
|
||||
|
||||
|
@ -74,7 +74,7 @@ class InvitationDialogState extends State<InvitationDialog> {
|
||||
|
||||
Future<void> _onAccept() async {
|
||||
final navigator = Navigator.of(context);
|
||||
final activeAccountInfo = widget._locator<UnlockedAccountInfo>();
|
||||
final accountInfo = widget._locator<AccountInfoCubit>().state;
|
||||
final contactList = widget._locator<ContactListCubit>();
|
||||
|
||||
setState(() {
|
||||
@ -86,7 +86,7 @@ class InvitationDialogState extends State<InvitationDialog> {
|
||||
if (acceptedContact != null) {
|
||||
// initiator when accept is received will create
|
||||
// contact in the case of a 'note to self'
|
||||
final isSelf = activeAccountInfo.identityPublicKey ==
|
||||
final isSelf = accountInfo.unlockedAccountInfo!.identityPublicKey ==
|
||||
acceptedContact.remoteIdentity.currentInstance.publicKey;
|
||||
if (!isSelf) {
|
||||
await contactList.createContact(
|
||||
|
@ -4,6 +4,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
@ -11,29 +12,23 @@ import '../../tools/tools.dart';
|
||||
import 'invitation_dialog.dart';
|
||||
|
||||
class PasteInvitationDialog extends StatefulWidget {
|
||||
const PasteInvitationDialog({required this.modalContext, super.key});
|
||||
const PasteInvitationDialog({required Locator locator, super.key})
|
||||
: _locator = locator;
|
||||
|
||||
@override
|
||||
PasteInvitationDialogState createState() => PasteInvitationDialogState();
|
||||
|
||||
static Future<void> show(BuildContext context) async {
|
||||
final modalContext = context;
|
||||
final locator = context.read;
|
||||
|
||||
await showPopControlDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => StyledDialog(
|
||||
title: translate('paste_invitation_dialog.title'),
|
||||
child: PasteInvitationDialog(modalContext: modalContext)));
|
||||
child: PasteInvitationDialog(locator: locator)));
|
||||
}
|
||||
|
||||
final BuildContext modalContext;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
.add(DiagnosticsProperty<BuildContext>('modalContext', modalContext));
|
||||
}
|
||||
final Locator _locator;
|
||||
}
|
||||
|
||||
class PasteInvitationDialogState extends State<PasteInvitationDialog> {
|
||||
@ -138,7 +133,7 @@ class PasteInvitationDialogState extends State<PasteInvitationDialog> {
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return InvitationDialog(
|
||||
modalContext: widget.modalContext,
|
||||
locator: widget._locator,
|
||||
onValidationCancelled: onValidationCancelled,
|
||||
onValidationSuccess: onValidationSuccess,
|
||||
onValidationFailed: onValidationFailed,
|
||||
|
@ -9,6 +9,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
import 'package:pasteboard/pasteboard.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:zxing2/qrcode.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
@ -102,28 +103,22 @@ class ScannerOverlay extends CustomPainter {
|
||||
}
|
||||
|
||||
class ScanInvitationDialog extends StatefulWidget {
|
||||
const ScanInvitationDialog({required this.modalContext, super.key});
|
||||
const ScanInvitationDialog({required Locator locator, super.key})
|
||||
: _locator = locator;
|
||||
|
||||
@override
|
||||
ScanInvitationDialogState createState() => ScanInvitationDialogState();
|
||||
|
||||
static Future<void> show(BuildContext context) async {
|
||||
final modalContext = context;
|
||||
final locator = context.read;
|
||||
await showPopControlDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => StyledDialog(
|
||||
title: translate('scan_invitation_dialog.title'),
|
||||
child: ScanInvitationDialog(modalContext: modalContext)));
|
||||
child: ScanInvitationDialog(locator: locator)));
|
||||
}
|
||||
|
||||
final BuildContext modalContext;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
.add(DiagnosticsProperty<BuildContext>('modalContext', modalContext));
|
||||
}
|
||||
final Locator _locator;
|
||||
}
|
||||
|
||||
class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||
@ -396,7 +391,7 @@ class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return InvitationDialog(
|
||||
modalContext: widget.modalContext,
|
||||
locator: widget._locator,
|
||||
onValidationCancelled: onValidationCancelled,
|
||||
onValidationSuccess: onValidationSuccess,
|
||||
onValidationFailed: onValidationFailed,
|
||||
|
@ -46,7 +46,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
_remoteConversationRecordKey = remoteConversationRecordKey,
|
||||
super(const AsyncValue.loading()) {
|
||||
final unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
_identityWriter = unlockedAccountInfo.identityWriter;
|
||||
|
||||
@ -76,7 +76,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final crypto = await _cachedConversationCrypto();
|
||||
final record = await pool.openRecordRead(_remoteConversationRecordKey,
|
||||
debugName: 'ConversationCubit::RemoteConversation',
|
||||
parent: _accountRecordKey,
|
||||
parent: pool.getParentRecordKey(_remoteConversationRecordKey) ??
|
||||
_accountRecordKey,
|
||||
crypto: crypto);
|
||||
return record;
|
||||
});
|
||||
@ -117,7 +118,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final crypto = await _cachedConversationCrypto();
|
||||
final account = _locator<AccountRecordCubit>().state.asData!.value;
|
||||
final unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final writer = unlockedAccountInfo.identityWriter;
|
||||
|
||||
@ -359,7 +360,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
return conversationCrypto;
|
||||
}
|
||||
final unlockedAccountInfo =
|
||||
_locator<ActiveAccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
conversationCrypto = await unlockedAccountInfo
|
||||
.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||
_conversationCrypto = conversationCrypto;
|
||||
@ -368,6 +369,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Fields
|
||||
TypedKey get remoteIdentityPublicKey => _remoteIdentityPublicKey;
|
||||
|
||||
final Locator _locator;
|
||||
late final TypedKey _accountRecordKey;
|
||||
late final KeyPair _identityWriter;
|
||||
|
37
lib/layout/home/active_account_page_controller_wrapper.dart
Normal file
37
lib/layout/home/active_account_page_controller_wrapper.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
|
||||
class ActiveAccountPageControllerWrapper {
|
||||
ActiveAccountPageControllerWrapper(Locator locator, int initialPage) {
|
||||
pageController = PageController(initialPage: initialPage, keepPage: false);
|
||||
|
||||
final activeLocalAccountCubit = locator<ActiveLocalAccountCubit>();
|
||||
_subscription =
|
||||
activeLocalAccountCubit.stream.listen((activeLocalAccountRecordKey) {
|
||||
singleFuture(this, () async {
|
||||
final localAccounts = locator<LocalAccountsCubit>().state;
|
||||
final activeIndex = localAccounts.indexWhere(
|
||||
(x) => x.superIdentity.recordKey == activeLocalAccountRecordKey);
|
||||
if (pageController.page == activeIndex) {
|
||||
return;
|
||||
}
|
||||
await pageController.animateToPage(activeIndex,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
unawaited(_subscription.cancel());
|
||||
}
|
||||
|
||||
late PageController pageController;
|
||||
late StreamSubscription<TypedKey?> _subscription;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@ -102,15 +103,12 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
}
|
||||
|
||||
Widget _getAccountList(
|
||||
{required TypedKey? activeLocalAccount,
|
||||
{required IList<LocalAccount> localAccounts,
|
||||
required TypedKey? activeLocalAccount,
|
||||
required AccountRecordsBlocMapState accountRecords}) {
|
||||
final theme = Theme.of(context);
|
||||
final scaleScheme = theme.extension<ScaleScheme>()!;
|
||||
|
||||
final accountRepo = AccountRepository.instance;
|
||||
final localAccounts = accountRepo.getLocalAccounts();
|
||||
//final userLogins = accountRepo.getUserLogins();
|
||||
|
||||
final loggedInAccounts = <Widget>[];
|
||||
final loggedOutAccounts = <Widget>[];
|
||||
|
||||
@ -234,8 +232,9 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
//final textTheme = theme.textTheme;
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
final accountRecords = context.watch<AccountRecordsBlocMapCubit>().state;
|
||||
final activeLocalAccount = context.watch<ActiveAccountInfoCubit>().state;
|
||||
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
||||
final gradient = LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@ -276,8 +275,8 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
])),
|
||||
const Spacer(),
|
||||
_getAccountList(
|
||||
activeLocalAccount:
|
||||
activeLocalAccount.unlockedAccountInfo?.superIdentityRecordKey,
|
||||
localAccounts: localAccounts,
|
||||
activeLocalAccount: activeLocalAccount,
|
||||
accountRecords: accountRecords),
|
||||
_getBottomButtons(),
|
||||
const Spacer(),
|
||||
|
@ -1,7 +1,8 @@
|
||||
export 'active_account_page_controller_wrapper.dart';
|
||||
export 'drawer_menu/drawer_menu.dart';
|
||||
export 'home_account_invalid.dart';
|
||||
export 'home_account_locked.dart';
|
||||
export 'home_account_missing.dart';
|
||||
export 'home_account_ready/home_account_ready.dart';
|
||||
export 'home_no_active.dart';
|
||||
export 'home_shell.dart';
|
||||
export 'home_screen.dart';
|
||||
|
@ -5,6 +5,7 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:preload_page_view/preload_page_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
|
||||
|
||||
import '../../../../chat/chat.dart';
|
||||
@ -117,7 +118,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
content: ScanInvitationDialog(
|
||||
modalContext: context,
|
||||
locator: context.read,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
@ -14,24 +14,24 @@ 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 '../../tools/tools.dart';
|
||||
import 'active_account_page_controller_wrapper.dart';
|
||||
import 'drawer_menu/drawer_menu.dart';
|
||||
import 'home_account_invalid.dart';
|
||||
import 'home_account_locked.dart';
|
||||
import 'home_account_missing.dart';
|
||||
import 'home_account_ready/home_account_ready.dart';
|
||||
import 'home_no_active.dart';
|
||||
|
||||
class HomeShell extends StatefulWidget {
|
||||
const HomeShell({required this.child, super.key});
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
HomeShellState createState() => HomeShellState();
|
||||
|
||||
final Widget child;
|
||||
HomeScreenState createState() => HomeScreenState();
|
||||
}
|
||||
|
||||
class HomeShellState extends State<HomeShell> {
|
||||
class HomeScreenState extends State<HomeScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -84,8 +84,22 @@ class HomeShellState extends State<HomeShell> {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildActiveAccount(BuildContext context) {
|
||||
final accountRecordKey = context.select<ActiveAccountInfoCubit, TypedKey>(
|
||||
Widget _buildAccountReadyDeviceSpecific(BuildContext context) {
|
||||
final hasActiveChat = context.watch<ActiveChatCubit>().state != null;
|
||||
if (responsiveVisibility(
|
||||
context: context,
|
||||
tablet: false,
|
||||
tabletLandscape: false,
|
||||
desktop: false)) {
|
||||
if (hasActiveChat) {
|
||||
return const HomeAccountReadyChat();
|
||||
}
|
||||
}
|
||||
return const HomeAccountReadyMain();
|
||||
}
|
||||
|
||||
Widget _buildUnlockedAccount(BuildContext context) {
|
||||
final accountRecordKey = context.select<AccountInfoCubit, TypedKey>(
|
||||
(c) => c.state.unlockedAccountInfo!.accountRecordKey);
|
||||
final contactListRecordPointer =
|
||||
context.select<AccountRecordCubit, OwnedDHTRecordPointer?>(
|
||||
@ -124,8 +138,9 @@ class HomeShellState extends State<HomeShell> {
|
||||
)),
|
||||
// Chat Cubits
|
||||
BlocProvider(
|
||||
create: (context) => ActiveChatCubit(null,
|
||||
routerCubit: context.read<RouterCubit>())),
|
||||
create: (context) => ActiveChatCubit(
|
||||
null,
|
||||
)),
|
||||
BlocProvider(
|
||||
create: (context) => ChatListCubit(
|
||||
locator: context.read,
|
||||
@ -146,27 +161,21 @@ class HomeShellState extends State<HomeShell> {
|
||||
WaitingInvitationsBlocMapState>(
|
||||
listener: _invitationStatusListener,
|
||||
)
|
||||
], child: widget.child));
|
||||
], child: Builder(builder: _buildAccountReadyDeviceSpecific)));
|
||||
}
|
||||
|
||||
Widget _buildWithLogin(BuildContext context) {
|
||||
Widget _buildAccount(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();
|
||||
}
|
||||
.select<AccountInfoCubit, (AccountInfoStatus, bool, TypedKey?)>((c) => (
|
||||
c.state.status,
|
||||
c.state.active,
|
||||
c.state.unlockedAccountInfo?.superIdentityRecordKey
|
||||
));
|
||||
|
||||
switch (accountInfoStatus) {
|
||||
case AccountInfoStatus.noAccount:
|
||||
@ -175,7 +184,7 @@ class HomeShellState extends State<HomeShell> {
|
||||
return const HomeAccountInvalid();
|
||||
case AccountInfoStatus.accountLocked:
|
||||
return const HomeAccountLocked();
|
||||
case AccountInfoStatus.accountReady:
|
||||
case AccountInfoStatus.accountUnlocked:
|
||||
|
||||
// Get the current active account record cubit
|
||||
final activeAccountRecordCubit =
|
||||
@ -190,10 +199,50 @@ class HomeShellState extends State<HomeShell> {
|
||||
return MultiBlocProvider(providers: [
|
||||
BlocProvider<AccountRecordCubit>.value(
|
||||
value: activeAccountRecordCubit),
|
||||
], child: Builder(builder: _buildActiveAccount));
|
||||
], child: Builder(builder: _buildUnlockedAccount));
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAccountPageView(BuildContext context) {
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
final activeLocalAccountCubit = context.read<ActiveLocalAccountCubit>();
|
||||
|
||||
final activeIndex = localAccounts.indexWhere(
|
||||
(x) => x.superIdentity.recordKey == activeLocalAccountCubit.state);
|
||||
if (activeIndex == -1) {
|
||||
return const HomeNoActive();
|
||||
}
|
||||
|
||||
return Provider<ActiveAccountPageControllerWrapper>(
|
||||
lazy: false,
|
||||
create: (context) =>
|
||||
ActiveAccountPageControllerWrapper(context.read, activeIndex),
|
||||
dispose: (context, value) {
|
||||
value.dispose();
|
||||
},
|
||||
child: Builder(
|
||||
builder: (context) => PageView.builder(
|
||||
itemCount: localAccounts.length,
|
||||
onPageChanged: (idx) {
|
||||
singleFuture(this, () async {
|
||||
await AccountRepository.instance.switchToAccount(
|
||||
localAccounts[idx].superIdentity.recordKey);
|
||||
});
|
||||
},
|
||||
controller: context
|
||||
.read<ActiveAccountPageControllerWrapper>()
|
||||
.pageController,
|
||||
itemBuilder: (context, index) {
|
||||
final localAccount = localAccounts[index];
|
||||
return BlocProvider<AccountInfoCubit>(
|
||||
key: ValueKey(localAccount.superIdentity.recordKey),
|
||||
create: (context) => AccountInfoCubit(
|
||||
AccountRepository.instance,
|
||||
localAccount.superIdentity.recordKey),
|
||||
child: Builder(builder: _buildAccount));
|
||||
})));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
@ -219,7 +268,7 @@ class HomeShellState extends State<HomeShell> {
|
||||
color: scale.primaryScale.activeElementBackground),
|
||||
child: Provider<ZoomDrawerController>.value(
|
||||
value: _zoomDrawerController,
|
||||
child: Builder(builder: _buildWithLogin))),
|
||||
child: Builder(builder: _buildAccountPageView))),
|
||||
borderRadius: 24,
|
||||
showShadow: true,
|
||||
angle: 0,
|
||||
@ -239,50 +288,3 @@ class HomeShellState extends State<HomeShell> {
|
||||
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();
|
||||
// }
|
||||
// }
|
@ -20,13 +20,12 @@ part 'router_cubit.freezed.dart';
|
||||
part 'router_cubit.g.dart';
|
||||
|
||||
final _rootNavKey = GlobalKey<NavigatorState>(debugLabel: 'rootNavKey');
|
||||
final _homeNavKey = GlobalKey<NavigatorState>(debugLabel: 'homeNavKey');
|
||||
|
||||
@freezed
|
||||
class RouterState with _$RouterState {
|
||||
const factory RouterState(
|
||||
{required bool hasAnyAccount,
|
||||
required bool hasActiveChat}) = _RouterState;
|
||||
const factory RouterState({
|
||||
required bool hasAnyAccount,
|
||||
}) = _RouterState;
|
||||
|
||||
factory RouterState.fromJson(dynamic json) =>
|
||||
_$RouterStateFromJson(json as Map<String, dynamic>);
|
||||
@ -36,7 +35,6 @@ class RouterCubit extends Cubit<RouterState> {
|
||||
RouterCubit(AccountRepository accountRepository)
|
||||
: super(RouterState(
|
||||
hasAnyAccount: accountRepository.getLocalAccounts().isNotEmpty,
|
||||
hasActiveChat: false,
|
||||
)) {
|
||||
// Subscribe to repository streams
|
||||
_accountRepositorySubscription = accountRepository.stream.listen((event) {
|
||||
@ -52,10 +50,6 @@ class RouterCubit extends Cubit<RouterState> {
|
||||
});
|
||||
}
|
||||
|
||||
void setHasActiveChat(bool active) {
|
||||
emit(state.copyWith(hasActiveChat: active));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _accountRepositorySubscription.cancel();
|
||||
@ -64,19 +58,9 @@ class RouterCubit extends Cubit<RouterState> {
|
||||
|
||||
/// Our application routes
|
||||
List<RouteBase> get routes => [
|
||||
ShellRoute(
|
||||
navigatorKey: _homeNavKey,
|
||||
builder: (context, state, child) => HomeShell(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => const HomeAccountReadyMain(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/chat',
|
||||
builder: (context, state) => const HomeAccountReadyChat(),
|
||||
),
|
||||
],
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => const HomeScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/edit_account',
|
||||
@ -116,31 +100,6 @@ class RouterCubit extends Cubit<RouterState> {
|
||||
if (!state.hasAnyAccount) {
|
||||
return '/new_account';
|
||||
}
|
||||
if (responsiveVisibility(
|
||||
context: context,
|
||||
tablet: false,
|
||||
tabletLandscape: false,
|
||||
desktop: false)) {
|
||||
if (state.hasActiveChat) {
|
||||
return '/chat';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
case '/chat':
|
||||
if (!state.hasAnyAccount) {
|
||||
return '/new_account';
|
||||
}
|
||||
if (responsiveVisibility(
|
||||
context: context,
|
||||
tablet: false,
|
||||
tabletLandscape: false,
|
||||
desktop: false)) {
|
||||
if (!state.hasActiveChat) {
|
||||
return '/';
|
||||
}
|
||||
} else {
|
||||
return '/';
|
||||
}
|
||||
return null;
|
||||
case '/new_account':
|
||||
return null;
|
||||
|
@ -21,7 +21,6 @@ RouterState _$RouterStateFromJson(Map<String, dynamic> json) {
|
||||
/// @nodoc
|
||||
mixin _$RouterState {
|
||||
bool get hasAnyAccount => throw _privateConstructorUsedError;
|
||||
bool get hasActiveChat => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
@ -35,7 +34,7 @@ abstract class $RouterStateCopyWith<$Res> {
|
||||
RouterState value, $Res Function(RouterState) then) =
|
||||
_$RouterStateCopyWithImpl<$Res, RouterState>;
|
||||
@useResult
|
||||
$Res call({bool hasAnyAccount, bool hasActiveChat});
|
||||
$Res call({bool hasAnyAccount});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -52,17 +51,12 @@ class _$RouterStateCopyWithImpl<$Res, $Val extends RouterState>
|
||||
@override
|
||||
$Res call({
|
||||
Object? hasAnyAccount = null,
|
||||
Object? hasActiveChat = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
hasAnyAccount: null == hasAnyAccount
|
||||
? _value.hasAnyAccount
|
||||
: hasAnyAccount // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
hasActiveChat: null == hasActiveChat
|
||||
? _value.hasActiveChat
|
||||
: hasActiveChat // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@ -75,7 +69,7 @@ abstract class _$$RouterStateImplCopyWith<$Res>
|
||||
__$$RouterStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool hasAnyAccount, bool hasActiveChat});
|
||||
$Res call({bool hasAnyAccount});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -90,17 +84,12 @@ class __$$RouterStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? hasAnyAccount = null,
|
||||
Object? hasActiveChat = null,
|
||||
}) {
|
||||
return _then(_$RouterStateImpl(
|
||||
hasAnyAccount: null == hasAnyAccount
|
||||
? _value.hasAnyAccount
|
||||
: hasAnyAccount // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
hasActiveChat: null == hasActiveChat
|
||||
? _value.hasActiveChat
|
||||
: hasActiveChat // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -108,20 +97,17 @@ class __$$RouterStateImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$RouterStateImpl with DiagnosticableTreeMixin implements _RouterState {
|
||||
const _$RouterStateImpl(
|
||||
{required this.hasAnyAccount, required this.hasActiveChat});
|
||||
const _$RouterStateImpl({required this.hasAnyAccount});
|
||||
|
||||
factory _$RouterStateImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$RouterStateImplFromJson(json);
|
||||
|
||||
@override
|
||||
final bool hasAnyAccount;
|
||||
@override
|
||||
final bool hasActiveChat;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'RouterState(hasAnyAccount: $hasAnyAccount, hasActiveChat: $hasActiveChat)';
|
||||
return 'RouterState(hasAnyAccount: $hasAnyAccount)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -129,8 +115,7 @@ class _$RouterStateImpl with DiagnosticableTreeMixin implements _RouterState {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'RouterState'))
|
||||
..add(DiagnosticsProperty('hasAnyAccount', hasAnyAccount))
|
||||
..add(DiagnosticsProperty('hasActiveChat', hasActiveChat));
|
||||
..add(DiagnosticsProperty('hasAnyAccount', hasAnyAccount));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -139,14 +124,12 @@ class _$RouterStateImpl with DiagnosticableTreeMixin implements _RouterState {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$RouterStateImpl &&
|
||||
(identical(other.hasAnyAccount, hasAnyAccount) ||
|
||||
other.hasAnyAccount == hasAnyAccount) &&
|
||||
(identical(other.hasActiveChat, hasActiveChat) ||
|
||||
other.hasActiveChat == hasActiveChat));
|
||||
other.hasAnyAccount == hasAnyAccount));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, hasAnyAccount, hasActiveChat);
|
||||
int get hashCode => Object.hash(runtimeType, hasAnyAccount);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -163,9 +146,8 @@ class _$RouterStateImpl with DiagnosticableTreeMixin implements _RouterState {
|
||||
}
|
||||
|
||||
abstract class _RouterState implements RouterState {
|
||||
const factory _RouterState(
|
||||
{required final bool hasAnyAccount,
|
||||
required final bool hasActiveChat}) = _$RouterStateImpl;
|
||||
const factory _RouterState({required final bool hasAnyAccount}) =
|
||||
_$RouterStateImpl;
|
||||
|
||||
factory _RouterState.fromJson(Map<String, dynamic> json) =
|
||||
_$RouterStateImpl.fromJson;
|
||||
@ -173,8 +155,6 @@ abstract class _RouterState implements RouterState {
|
||||
@override
|
||||
bool get hasAnyAccount;
|
||||
@override
|
||||
bool get hasActiveChat;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$RouterStateImplCopyWith<_$RouterStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
@ -9,11 +9,9 @@ part of 'router_cubit.dart';
|
||||
_$RouterStateImpl _$$RouterStateImplFromJson(Map<String, dynamic> json) =>
|
||||
_$RouterStateImpl(
|
||||
hasAnyAccount: json['has_any_account'] as bool,
|
||||
hasActiveChat: json['has_active_chat'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$RouterStateImplToJson(_$RouterStateImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'has_any_account': instance.hasAnyAccount,
|
||||
'has_active_chat': instance.hasActiveChat,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user