more refactor

This commit is contained in:
Christien Rioux 2024-02-13 22:03:26 -05:00
parent 45ab494969
commit 9219e1307e
19 changed files with 181 additions and 467 deletions

View File

@ -1,4 +1,4 @@
export 'cubit/cubit.dart';
export 'cubits/cubits.dart';
export 'models/models.dart';
export 'repository/repository.dart';
export 'views/views.dart';

View File

@ -1,3 +0,0 @@
part of 'active_user_login_cubit.dart';
typedef ActiveUserLoginState = TypedKey?;

View File

@ -1,4 +0,0 @@
export 'account_record_cubit.dart';
export 'active_user_login_cubit/active_user_login_cubit.dart';
export 'local_accounts_cubit/local_accounts_cubit.dart';
export 'user_logins_cubit/user_logins_cubit.dart';

View File

@ -1,14 +0,0 @@
part of 'local_accounts_cubit.dart';
typedef LocalAccountsState = IList<LocalAccount>;
extension LocalAccountsStateExt on LocalAccountsState {
LocalAccount? fetchLocalAccount({required TypedKey accountMasterRecordKey}) {
final idx = indexWhere(
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
if (idx == -1) {
return null;
}
return this[idx];
}
}

View File

@ -1,14 +0,0 @@
part of 'user_logins_cubit.dart';
typedef UserLoginsState = IList<UserLogin>;
extension UserLoginsStateExt on UserLoginsState {
UserLogin? fetchUserLogin({required TypedKey accountMasterRecordKey}) {
final idx =
indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey);
if (idx == -1) {
return null;
}
return this[idx];
}
}

View File

@ -3,12 +3,10 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../repository/account_repository/account_repository.dart';
import '../repository/account_repository/account_repository.dart';
part 'active_user_login_state.dart';
class ActiveUserLoginCubit extends Cubit<ActiveUserLoginState> {
ActiveUserLoginCubit(AccountRepository accountRepository)
class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
ActiveLocalAccountCubit(AccountRepository accountRepository)
: _accountRepository = accountRepository,
super(null) {
// Subscribe to streams
@ -18,8 +16,8 @@ class ActiveUserLoginCubit extends Cubit<ActiveUserLoginState> {
void _initAccountRepositorySubscription() {
_accountRepositorySubscription = _accountRepository.stream.listen((change) {
switch (change) {
case AccountRepositoryChange.activeUserLogin:
emit(_accountRepository.getActiveUserLogin());
case AccountRepositoryChange.activeLocalAccount:
emit(_accountRepository.getActiveLocalAccount());
break;
// Ignore these
case AccountRepositoryChange.localAccounts:

View File

@ -0,0 +1,4 @@
export 'account_record_cubit.dart';
export 'active_local_account_cubit.dart';
export 'local_accounts_cubit.dart';
export 'user_logins_cubit.dart';

View File

@ -2,17 +2,14 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../models/models.dart';
import '../../repository/account_repository/account_repository.dart';
import '../models/models.dart';
import '../repository/account_repository/account_repository.dart';
part 'local_accounts_state.dart';
class LocalAccountsCubit extends Cubit<LocalAccountsState> {
class LocalAccountsCubit extends Cubit<IList<LocalAccount>> {
LocalAccountsCubit(AccountRepository accountRepository)
: _accountRepository = accountRepository,
super(LocalAccountsState()) {
super(IList<LocalAccount>()) {
// Subscribe to streams
_initAccountRepositorySubscription();
}
@ -25,7 +22,7 @@ class LocalAccountsCubit extends Cubit<LocalAccountsState> {
break;
// Ignore these
case AccountRepositoryChange.userLogins:
case AccountRepositoryChange.activeUserLogin:
case AccountRepositoryChange.activeLocalAccount:
break;
}
});

View File

@ -2,17 +2,14 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../models/models.dart';
import '../../repository/account_repository/account_repository.dart';
import '../models/models.dart';
import '../repository/account_repository/account_repository.dart';
part 'user_logins_state.dart';
class UserLoginsCubit extends Cubit<UserLoginsState> {
class UserLoginsCubit extends Cubit<IList<UserLogin>> {
UserLoginsCubit(AccountRepository accountRepository)
: _accountRepository = accountRepository,
super(UserLoginsState()) {
super(IList<UserLogin>()) {
// Subscribe to streams
_initAccountRepositorySubscription();
}
@ -25,7 +22,7 @@ class UserLoginsCubit extends Cubit<UserLoginsState> {
break;
// Ignore these
case AccountRepositoryChange.localAccounts:
case AccountRepositoryChange.activeUserLogin:
case AccountRepositoryChange.activeLocalAccount:
break;
}
});

View File

@ -6,16 +6,16 @@ import 'package:veilid_support/veilid_support.dart';
import '../../../../proto/proto.dart' as proto;
import '../../../tools/tools.dart';
import '../../models/models.dart';
import 'active_logins.dart';
const String veilidChatAccountKey = 'com.veilid.veilidchat';
enum AccountRepositoryChange { localAccounts, userLogins, activeUserLogin }
enum AccountRepositoryChange { localAccounts, userLogins, activeLocalAccount }
class AccountRepository {
AccountRepository._()
: _localAccounts = _initLocalAccounts(),
_activeLogins = _initActiveLogins(),
_userLogins = _initUserLogins(),
_activeLocalAccount = _initActiveAccount(),
_streamController =
StreamController<AccountRepositoryChange>.broadcast();
@ -28,16 +28,23 @@ class AccountRepository {
: IList<LocalAccount>(),
valueToJson: (val) => val.toJson((la) => la.toJson()));
static TableDBValue<ActiveLogins> _initActiveLogins() => TableDBValue(
static TableDBValue<IList<UserLogin>> _initUserLogins() => TableDBValue(
tableName: 'local_account_manager',
tableKeyName: 'active_logins',
tableKeyName: 'user_logins',
valueFromJson: (obj) => obj != null
? ActiveLogins.fromJson(obj as Map<String, dynamic>)
: ActiveLogins.empty(),
valueToJson: (val) => val.toJson());
? IList<UserLogin>.fromJson(obj, genericFromJson(UserLogin.fromJson))
: IList<UserLogin>(),
valueToJson: (val) => val.toJson((la) => la.toJson()));
static TableDBValue<TypedKey?> _initActiveAccount() => TableDBValue(
tableName: 'local_account_manager',
tableKeyName: 'active_local_account',
valueFromJson: (obj) => obj == null ? null : TypedKey.fromJson(obj),
valueToJson: (val) => val?.toJson());
final TableDBValue<IList<LocalAccount>> _localAccounts;
final TableDBValue<ActiveLogins> _activeLogins;
final TableDBValue<IList<UserLogin>> _userLogins;
final TableDBValue<TypedKey?> _activeLocalAccount;
final StreamController<AccountRepositoryChange> _streamController;
//////////////////////////////////////////////////////////////
@ -47,7 +54,8 @@ class AccountRepository {
Future<void> init() async {
await _localAccounts.load();
await _activeLogins.load();
await _userLogins.load();
await _activeLocalAccount.load();
await _openLoggedInDHTRecords();
}
@ -59,10 +67,16 @@ class AccountRepository {
//////////////////////////////////////////////////////////////
/// Selectors
IList<LocalAccount> getLocalAccounts() => _localAccounts.requireValue;
IList<UserLogin> getUserLogins() => _activeLogins.requireValue.userLogins;
TypedKey? getActiveUserLogin() => _activeLogins.requireValue.activeUserLogin;
TypedKey? getActiveLocalAccount() => _activeLocalAccount.requireValue;
IList<UserLogin> getUserLogins() => _userLogins.requireValue;
UserLogin? getActiveUserLogin() {
final activeLocalAccount = _activeLocalAccount.requireValue;
return activeLocalAccount == null
? null
: fetchUserLogin(activeLocalAccount);
}
LocalAccount? fetchLocalAccount({required TypedKey accountMasterRecordKey}) {
LocalAccount? fetchLocalAccount(TypedKey accountMasterRecordKey) {
final localAccounts = _localAccounts.requireValue;
final idx = localAccounts.indexWhere(
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
@ -72,8 +86,8 @@ class AccountRepository {
return localAccounts[idx];
}
UserLogin? fetchUserLogin({required TypedKey accountMasterRecordKey}) {
final userLogins = _activeLogins.requireValue.userLogins;
UserLogin? fetchUserLogin(TypedKey accountMasterRecordKey) {
final userLogins = _userLogins.requireValue;
final idx = userLogins
.indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey);
if (idx == -1) {
@ -82,34 +96,33 @@ class AccountRepository {
return userLogins[idx];
}
AccountInfo? getAccountInfo({TypedKey? accountMasterRecordKey}) {
// Get active user if we have one
AccountInfo getAccountInfo(TypedKey? accountMasterRecordKey) {
// Get active account if we have one
final activeLocalAccount = getActiveLocalAccount();
if (accountMasterRecordKey == null) {
final activeUserLogin = getActiveUserLogin();
if (activeUserLogin == null) {
if (activeLocalAccount == null) {
// No user logged in
return null;
return const AccountInfo(
status: AccountInfoStatus.noAccount,
active: false,
activeAccountInfo: null);
}
accountMasterRecordKey = activeUserLogin;
accountMasterRecordKey = activeLocalAccount;
}
final active = accountMasterRecordKey == activeLocalAccount;
// Get which local account we want to fetch the profile for
final localAccount =
fetchLocalAccount(accountMasterRecordKey: accountMasterRecordKey);
final localAccount = fetchLocalAccount(accountMasterRecordKey);
if (localAccount == null) {
// Local account does not exist
return const AccountInfo(
// account does not exist
return AccountInfo(
status: AccountInfoStatus.noAccount,
active: false,
active: active,
activeAccountInfo: null);
}
// See if we've logged into this account or if it is locked
final activeUserLogin = getActiveUserLogin();
final active = activeUserLogin == accountMasterRecordKey;
final userLogin =
fetchUserLogin(accountMasterRecordKey: accountMasterRecordKey);
final userLogin = fetchUserLogin(accountMasterRecordKey);
if (userLogin == null) {
// Account was locked
return AccountInfo(
@ -268,22 +281,20 @@ class AccountRepository {
/// Delete an account from all devices
Future<void> switchToAccount(TypedKey? accountMasterRecordKey) async {
final activeLogins = await _activeLogins.get();
final activeLocalAccount = await _activeLocalAccount.get();
if (activeLogins.activeUserLogin == accountMasterRecordKey) {
if (activeLocalAccount == accountMasterRecordKey) {
// Nothing to do
return;
}
if (accountMasterRecordKey != null) {
// Assert the specified record key can be found, will throw if not
final _ = activeLogins.userLogins.firstWhere(
final _ = _userLogins.requireValue.firstWhere(
(ul) => ul.accountMasterRecordKey == accountMasterRecordKey);
}
final newActiveLogins =
activeLogins.copyWith(activeUserLogin: accountMasterRecordKey);
await _activeLogins.set(newActiveLogins);
_streamController.add(AccountRepositoryChange.activeUserLogin);
await _activeLocalAccount.set(accountMasterRecordKey);
_streamController.add(AccountRepositoryChange.activeLocalAccount);
}
Future<bool> _decryptedLogin(
@ -301,25 +312,25 @@ class AccountRepository {
identitySecret: identitySecret, accountKey: veilidChatAccountKey);
// Add to user logins and select it
final activeLogins = await _activeLogins.get();
final userLogins = await _userLogins.get();
final now = Veilid.instance.now();
final newActiveLogins = activeLogins.copyWith(
userLogins: activeLogins.userLogins.replaceFirstWhere(
(ul) => ul.accountMasterRecordKey == identityMaster.masterRecordKey,
(ul) => ul != null
? ul.copyWith(lastActive: now)
: UserLogin(
accountMasterRecordKey: identityMaster.masterRecordKey,
identitySecret:
TypedSecret(kind: cs.kind(), value: identitySecret),
accountRecordInfo: accountRecordInfo,
lastActive: now),
addIfNotFound: true),
activeUserLogin: identityMaster.masterRecordKey);
await _activeLogins.set(newActiveLogins);
final newUserLogins = userLogins.replaceFirstWhere(
(ul) => ul.accountMasterRecordKey == identityMaster.masterRecordKey,
(ul) => ul != null
? ul.copyWith(lastActive: now)
: UserLogin(
accountMasterRecordKey: identityMaster.masterRecordKey,
identitySecret:
TypedSecret(kind: cs.kind(), value: identitySecret),
accountRecordInfo: accountRecordInfo,
lastActive: now),
addIfNotFound: true);
await _userLogins.set(newUserLogins);
await _activeLocalAccount.set(identityMaster.masterRecordKey);
_streamController
..add(AccountRepositoryChange.activeUserLogin)
..add(AccountRepositoryChange.userLogins);
..add(AccountRepositoryChange.userLogins)
..add(AccountRepositoryChange.activeLocalAccount);
// Ensure all logins are opened
await _openLoggedInDHTRecords();
@ -355,34 +366,31 @@ class AccountRepository {
Future<void> logout(TypedKey? accountMasterRecordKey) async {
// Resolve which user to log out
final activeLogins = await _activeLogins.get();
final logoutUser = accountMasterRecordKey ?? activeLogins.activeUserLogin;
//final userLogins = await _userLogins.get();
final activeLocalAccount = await _activeLocalAccount.get();
final logoutUser = accountMasterRecordKey ?? activeLocalAccount;
if (logoutUser == null) {
log.error('missing user in logout: $accountMasterRecordKey');
return;
}
final logoutUserLogin = fetchUserLogin(accountMasterRecordKey: logoutUser);
if (logoutUserLogin != null) {
// Close DHT records for this account
final pool = DHTRecordPool.instance;
final accountRecordKey =
logoutUserLogin.accountRecordInfo.accountRecord.recordKey;
final accountRecord = pool.getOpenedRecord(accountRecordKey);
await accountRecord?.close();
final logoutUserLogin = fetchUserLogin(logoutUser);
if (logoutUserLogin == null) {
// Already logged out
return;
}
// Close DHT records for this account
final pool = DHTRecordPool.instance;
final accountRecordKey =
logoutUserLogin.accountRecordInfo.accountRecord.recordKey;
final accountRecord = pool.getOpenedRecord(accountRecordKey);
await accountRecord?.close();
// Remove user from active logins list
final newActiveLogins = activeLogins.copyWith(
activeUserLogin: activeLogins.activeUserLogin == logoutUser
? null
: activeLogins.activeUserLogin,
userLogins: activeLogins.userLogins
.removeWhere((ul) => ul.accountMasterRecordKey == logoutUser));
await _activeLogins.set(newActiveLogins);
if (activeLogins.activeUserLogin == logoutUser) {
_streamController.add(AccountRepositoryChange.activeUserLogin);
}
final newUserLogins = (await _userLogins.get())
.removeWhere((ul) => ul.accountMasterRecordKey == logoutUser);
await _userLogins.set(newUserLogins);
_streamController.add(AccountRepositoryChange.userLogins);
}
@ -390,15 +398,15 @@ class AccountRepository {
final pool = DHTRecordPool.instance;
// For all user logins if they arent open yet
final activeLogins = await _activeLogins.get();
for (final userLogin in activeLogins.userLogins) {
final userLogins = await _userLogins.get();
for (final userLogin in userLogins) {
//// Account record key /////////////////////////////
final accountRecordKey =
userLogin.accountRecordInfo.accountRecord.recordKey;
final existingAccountRecord = pool.getOpenedRecord(accountRecordKey);
if (existingAccountRecord == null) {
final localAccount = fetchLocalAccount(
accountMasterRecordKey: userLogin.accountMasterRecordKey);
final localAccount =
fetchLocalAccount(userLogin.accountMasterRecordKey);
// Record not yet open, do it
final record = await pool.openOwned(
@ -413,8 +421,8 @@ class AccountRepository {
Future<void> _closeLoggedInDHTRecords() async {
final pool = DHTRecordPool.instance;
final activeLogins = await _activeLogins.get();
for (final userLogin in activeLogins.userLogins) {
final userLogins = await _userLogins.get();
for (final userLogin in userLogins) {
//// Account record key /////////////////////////////
final accountRecordKey =
userLogin.accountRecordInfo.accountRecord.recordKey;

View File

@ -1,25 +0,0 @@
// Represents a set of user logins and the currently selected account
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../models/models.dart';
part 'active_logins.g.dart';
part 'active_logins.freezed.dart';
@freezed
class ActiveLogins with _$ActiveLogins {
const factory ActiveLogins({
// The list of current logged in accounts
required IList<UserLogin> userLogins,
// The current selected account indexed by master record key
TypedKey? activeUserLogin,
}) = _ActiveLogins;
factory ActiveLogins.empty() =>
const ActiveLogins(userLogins: IListConst([]));
factory ActiveLogins.fromJson(dynamic json) =>
_ActiveLogins.fromJson(json as Map<String, dynamic>);
}

View File

@ -1,181 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'active_logins.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
ActiveLogins _$ActiveLoginsFromJson(Map<String, dynamic> json) {
return _ActiveLogins.fromJson(json);
}
/// @nodoc
mixin _$ActiveLogins {
// The list of current logged in accounts
IList<UserLogin> get userLogins =>
throw _privateConstructorUsedError; // The current selected account indexed by master record key
Typed<FixedEncodedString43>? get activeUserLogin =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ActiveLoginsCopyWith<ActiveLogins> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ActiveLoginsCopyWith<$Res> {
factory $ActiveLoginsCopyWith(
ActiveLogins value, $Res Function(ActiveLogins) then) =
_$ActiveLoginsCopyWithImpl<$Res, ActiveLogins>;
@useResult
$Res call(
{IList<UserLogin> userLogins,
Typed<FixedEncodedString43>? activeUserLogin});
}
/// @nodoc
class _$ActiveLoginsCopyWithImpl<$Res, $Val extends ActiveLogins>
implements $ActiveLoginsCopyWith<$Res> {
_$ActiveLoginsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userLogins = null,
Object? activeUserLogin = freezed,
}) {
return _then(_value.copyWith(
userLogins: null == userLogins
? _value.userLogins
: userLogins // ignore: cast_nullable_to_non_nullable
as IList<UserLogin>,
activeUserLogin: freezed == activeUserLogin
? _value.activeUserLogin
: activeUserLogin // ignore: cast_nullable_to_non_nullable
as Typed<FixedEncodedString43>?,
) as $Val);
}
}
/// @nodoc
abstract class _$$ActiveLoginsImplCopyWith<$Res>
implements $ActiveLoginsCopyWith<$Res> {
factory _$$ActiveLoginsImplCopyWith(
_$ActiveLoginsImpl value, $Res Function(_$ActiveLoginsImpl) then) =
__$$ActiveLoginsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{IList<UserLogin> userLogins,
Typed<FixedEncodedString43>? activeUserLogin});
}
/// @nodoc
class __$$ActiveLoginsImplCopyWithImpl<$Res>
extends _$ActiveLoginsCopyWithImpl<$Res, _$ActiveLoginsImpl>
implements _$$ActiveLoginsImplCopyWith<$Res> {
__$$ActiveLoginsImplCopyWithImpl(
_$ActiveLoginsImpl _value, $Res Function(_$ActiveLoginsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userLogins = null,
Object? activeUserLogin = freezed,
}) {
return _then(_$ActiveLoginsImpl(
userLogins: null == userLogins
? _value.userLogins
: userLogins // ignore: cast_nullable_to_non_nullable
as IList<UserLogin>,
activeUserLogin: freezed == activeUserLogin
? _value.activeUserLogin
: activeUserLogin // ignore: cast_nullable_to_non_nullable
as Typed<FixedEncodedString43>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ActiveLoginsImpl implements _ActiveLogins {
const _$ActiveLoginsImpl({required this.userLogins, this.activeUserLogin});
factory _$ActiveLoginsImpl.fromJson(Map<String, dynamic> json) =>
_$$ActiveLoginsImplFromJson(json);
// The list of current logged in accounts
@override
final IList<UserLogin> userLogins;
// The current selected account indexed by master record key
@override
final Typed<FixedEncodedString43>? activeUserLogin;
@override
String toString() {
return 'ActiveLogins(userLogins: $userLogins, activeUserLogin: $activeUserLogin)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ActiveLoginsImpl &&
const DeepCollectionEquality()
.equals(other.userLogins, userLogins) &&
(identical(other.activeUserLogin, activeUserLogin) ||
other.activeUserLogin == activeUserLogin));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType,
const DeepCollectionEquality().hash(userLogins), activeUserLogin);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ActiveLoginsImplCopyWith<_$ActiveLoginsImpl> get copyWith =>
__$$ActiveLoginsImplCopyWithImpl<_$ActiveLoginsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ActiveLoginsImplToJson(
this,
);
}
}
abstract class _ActiveLogins implements ActiveLogins {
const factory _ActiveLogins(
{required final IList<UserLogin> userLogins,
final Typed<FixedEncodedString43>? activeUserLogin}) = _$ActiveLoginsImpl;
factory _ActiveLogins.fromJson(Map<String, dynamic> json) =
_$ActiveLoginsImpl.fromJson;
@override // The list of current logged in accounts
IList<UserLogin> get userLogins;
@override // The current selected account indexed by master record key
Typed<FixedEncodedString43>? get activeUserLogin;
@override
@JsonKey(ignore: true)
_$$ActiveLoginsImplCopyWith<_$ActiveLoginsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -1,24 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'active_logins.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ActiveLoginsImpl _$$ActiveLoginsImplFromJson(Map<String, dynamic> json) =>
_$ActiveLoginsImpl(
userLogins: IList<UserLogin>.fromJson(
json['user_logins'], (value) => UserLogin.fromJson(value)),
activeUserLogin: json['active_user_login'] == null
? null
: Typed<FixedEncodedString43>.fromJson(json['active_user_login']),
);
Map<String, dynamic> _$$ActiveLoginsImplToJson(_$ActiveLoginsImpl instance) =>
<String, dynamic>{
'user_logins': instance.userLogins.toJson(
(value) => value.toJson(),
),
'active_user_login': instance.activeUserLogin?.toJson(),
};

View File

@ -47,9 +47,9 @@ class VeilidChatApp extends StatelessWidget {
create: (context) =>
UserLoginsCubit(AccountRepository.instance),
),
BlocProvider<ActiveUserLoginCubit>(
BlocProvider<ActiveLocalAccountCubit>(
create: (context) =>
ActiveUserLoginCubit(AccountRepository.instance),
ActiveLocalAccountCubit(AccountRepository.instance),
),
BlocProvider<PreferencesCubit>(
create: (context) =>

View File

@ -2,9 +2,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:veilid_support/veilid_support.dart';
class ActiveChatCubit extends Cubit<TypedKey?> {
ActiveChatCubit(super.initialState);
ActiveChatCubit(super.initialState, this.setHasActiveChat);
void setActiveChat(TypedKey? activeChatRemoteConversationRecordKey) {
setHasActiveChat(activeChatRemoteConversationRecordKey != null);
emit(activeChatRemoteConversationRecordKey);
}
void Function(bool) setHasActiveChat;
}

View File

@ -7,6 +7,7 @@ import '../../../chat/chat.dart';
import '../../../chat_list/chat_list.dart';
import '../../../contact_invitation/contact_invitation.dart';
import '../../../contacts/contacts.dart';
import '../../../router/router.dart';
import '../../../tools/tools.dart';
class HomeAccountReadyShell extends StatefulWidget {
@ -18,58 +19,28 @@ class HomeAccountReadyShell extends StatefulWidget {
final Widget child;
}
class HomeAccountReadyShellState extends State<HomeAccountReadyShell>
with TickerProviderStateMixin {
class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
//
@override
void initState() {
super.initState();
}
// xxx figure out how to do this switch
// Widget buildWithLogin(BuildContext context) {
// final activeUserLogin = context.watch<ActiveUserLoginCubit>().state;
// if (activeUserLogin == null) {
// // If no logged in user is active, show the loading panel
// return const HomeNoActive();
// }
// final accountInfo = AccountRepository.instance
// .getAccountInfo(accountMasterRecordKey: activeUserLogin)!;
// switch (accountInfo.status) {
// case AccountInfoStatus.noAccount:
// return const HomeAccountMissing();
// case AccountInfoStatus.accountInvalid:
// return const HomeAccountInvalid();
// case AccountInfoStatus.accountLocked:
// return const HomeAccountLocked();
// case AccountInfoStatus.accountReady:
// return Provider<ActiveAccountInfo>.value(
// value: accountInfo.activeAccountInfo!,
// child: BlocProvider(
// create: (context) => AccountRecordCubit(
// record: accountInfo.activeAccountInfo!.accountRecord),
// child: const HomeAccountReady()));
// }
// }
@override
Widget build(BuildContext context) {
// These must be valid already before making this widget,
// per the ShellRoute above it
final activeUserLogin = context.read<ActiveUserLoginCubit>().state!;
final accountInfo = AccountRepository.instance
.getAccountInfo(accountMasterRecordKey: activeUserLogin)!;
final activeLocalAccount = context.read<ActiveLocalAccountCubit>().state!;
final accountInfo =
AccountRepository.instance.getAccountInfo(activeLocalAccount);
final activeAccountInfo = accountInfo.activeAccountInfo!;
final routerCubit = context.read<RouterCubit>();
return Provider<ActiveAccountInfo>.value(
value: activeAccountInfo,
child: BlocProvider(
create: (context) => AccountRecordCubit(
record: accountInfo.activeAccountInfo!.accountRecord),
create: (context) =>
AccountRecordCubit(record: activeAccountInfo.accountRecord),
child: Builder(builder: (context) {
final account =
context.watch<AccountRecordCubit>().state.data?.value;
@ -92,7 +63,9 @@ class HomeAccountReadyShellState extends State<HomeAccountReadyShell>
BlocProvider(
create: (context) => ActiveConversationsCubit(
activeAccountInfo: activeAccountInfo)),
BlocProvider(create: (context) => ActiveChatCubit(null))
BlocProvider(
create: (context) =>
ActiveChatCubit(null, routerCubit.setHasActiveChat))
], child: widget.child);
})));
}

View File

@ -1,6 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import '../../account_manager/account_manager.dart';
import '../../theme/theme.dart';
import 'home_account_invalid.dart';
import 'home_account_locked.dart';
import 'home_account_missing.dart';
import 'home_no_active.dart';
class HomeShell extends StatefulWidget {
const HomeShell({required this.child, super.key});
@ -25,6 +32,34 @@ class HomeShellState extends State<HomeShell> {
super.dispose();
}
Widget buildWithLogin(BuildContext context, Widget child) {
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
if (activeLocalAccount == null) {
// If no logged in user is active, show the loading panel
return const HomeNoActive();
}
final accountInfo =
AccountRepository.instance.getAccountInfo(activeLocalAccount);
switch (accountInfo.status) {
case AccountInfoStatus.noAccount:
return const HomeAccountMissing();
case AccountInfoStatus.accountInvalid:
return const HomeAccountInvalid();
case AccountInfoStatus.accountLocked:
return const HomeAccountLocked();
case AccountInfoStatus.accountReady:
return Provider<ActiveAccountInfo>.value(
value: accountInfo.activeAccountInfo!,
child: BlocProvider(
create: (context) => AccountRecordCubit(
record: accountInfo.activeAccountInfo!.accountRecord),
child: child));
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@ -37,6 +72,6 @@ class HomeShellState extends State<HomeShell> {
child: DecoratedBox(
decoration: BoxDecoration(
color: scale.primaryScale.activeElementBackground),
child: widget.child)));
child: buildWithLogin(context, widget.child))));
}
}

View File

@ -20,8 +20,6 @@ part 'router_state.dart';
final _rootNavKey = GlobalKey<NavigatorState>(debugLabel: 'rootNavKey');
final _homeNavKey = GlobalKey<NavigatorState>(debugLabel: 'homeNavKey');
final _readyAccountNavKey =
GlobalKey<NavigatorState>(debugLabel: 'readyAccountNavKey');
class RouterCubit extends Cubit<RouterState> {
RouterCubit(AccountRepository accountRepository)
@ -44,12 +42,16 @@ class RouterCubit extends Cubit<RouterState> {
hasAnyAccount: accountRepository.getLocalAccounts().isNotEmpty));
break;
case AccountRepositoryChange.userLogins:
case AccountRepositoryChange.activeUserLogin:
case AccountRepositoryChange.activeLocalAccount:
break;
}
});
}
void setHasActiveChat(bool active) {
emit(state.copyWith(hasActiveChat: active));
}
@override
Future<void> close() async {
await _accountRepositorySubscription.cancel();
@ -63,37 +65,20 @@ class RouterCubit extends Cubit<RouterState> {
builder: (context, state) => const IndexPage(),
),
ShellRoute(
navigatorKey: _homeNavKey,
builder: (context, state, child) => HomeShell(child: child),
routes: [
GoRoute(
path: '/home/no_active',
builder: (context, state) => const HomeNoActive(),
),
GoRoute(
path: '/home/account_missing',
builder: (context, state) => const HomeAccountMissing(),
),
GoRoute(
path: '/home/account_locked',
builder: (context, state) => const HomeAccountLocked(),
),
ShellRoute(
navigatorKey: _readyAccountNavKey,
builder: (context, state, child) =>
HomeAccountReadyShell(child: child),
routes: [
GoRoute(
path: '/home',
builder: (context, state) => const HomeAccountReadyMain(),
),
GoRoute(
path: '/home/chat',
builder: (context, state) => const HomeAccountReadyChat(),
),
],
),
]),
navigatorKey: _homeNavKey,
builder: (context, state, child) =>
HomeShell(child: HomeAccountReadyShell(child: child)),
routes: [
GoRoute(
path: '/home',
builder: (context, state) => const HomeAccountReadyMain(),
),
GoRoute(
path: '/home/chat',
builder: (context, state) => const HomeAccountReadyChat(),
),
],
),
GoRoute(
path: '/new_account',
builder: (context, state) => const NewAccountPage(),
@ -127,9 +112,6 @@ class RouterCubit extends Cubit<RouterState> {
if (!state.hasAnyAccount) {
return '/new_account';
}
if (!state.hasActiveChat) { xxx stop using hasActiveChat here... we need a pager for the accounts and a way to get the current account state maybe a 'activeAccountCubit' or something, we may have this alraeady but it needs to work even if logged out.``
return '/home/no_active';
}
if (responsiveVisibility(
context: context,
tablet: false,
@ -144,9 +126,6 @@ class RouterCubit extends Cubit<RouterState> {
if (!state.hasAnyAccount) {
return '/new_account';
}
if (!state.hasActiveChat) {
return '/home/no_active';
}
if (responsiveVisibility(
context: context,
tablet: false,
@ -159,21 +138,6 @@ class RouterCubit extends Cubit<RouterState> {
return '/home';
}
return null;
case '/home/no_active':
if (state.hasActiveChat) {
return '/home';
}
return null;
case '/home/account_missing':
if (!state.hasActiveChat) {
return '/home/no_active';
}
return null;
case '/home/account_locked':
if (!state.hasActiveChat) {
return '/home/no_active';
}
return null;
case '/settings':
return null;
case '/developer':