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