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(),
};