break everything

This commit is contained in:
Christien Rioux 2023-12-26 20:26:54 -05:00
parent e898074387
commit 29210c89d2
121 changed files with 2892 additions and 2608 deletions

View File

@ -1,45 +1,50 @@
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'router/router.dart';
import 'tick.dart';
class VeilidChatApp extends ConsumerWidget {
class VeilidChatApp extends StatelessWidget {
const VeilidChatApp({
required this.theme,
required this.themeData,
super.key,
});
final ThemeData theme;
final ThemeData themeData;
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
Widget build(BuildContext context) {
final localizationDelegate = LocalizedApp.of(context).delegate;
return ThemeProvider(
initTheme: theme,
initTheme: themeData,
builder: (_, theme) => LocalizationProvider(
state: LocalizationProvider.of(context).state,
child: BackgroundTicker(
builder: (context) => MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: router,
title: translate('app.title'),
theme: theme,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
FormBuilderLocalizations.delegate,
localizationDelegate
],
supportedLocales: localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
builder: (context) => BlocProvider(
create: (context) => RouterCubit(),
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: router(
routerCubit:
BlocProvider.of<RouterCubit>(context)),
title: translate('app.title'),
theme: theme,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
FormBuilderLocalizations.delegate,
localizationDelegate
],
supportedLocales:
localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
),
)),
));
}
@ -47,6 +52,6 @@ class VeilidChatApp extends ConsumerWidget {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<ThemeData>('theme', theme));
properties.add(DiagnosticsProperty<ThemeData>('themeData', themeData));
}
}

View File

@ -1,76 +0,0 @@
import 'dart:typed_data';
import 'package:change_case/change_case.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../proto/proto.dart' as proto;
import '../veilid_support/veilid_support.dart';
part 'local_account.freezed.dart';
part 'local_account.g.dart';
// Local account identitySecretKey is potentially encrypted with a key
// using the following mechanisms
// * None : no key, bytes are unencrypted
// * Pin : Code is a numeric pin (4-256 numeric digits) hashed with Argon2
// * Password: Code is a UTF-8 string that is hashed with Argon2
enum EncryptionKeyType {
none,
pin,
password;
factory EncryptionKeyType.fromJson(dynamic j) =>
EncryptionKeyType.values.byName((j as String).toCamelCase());
factory EncryptionKeyType.fromProto(proto.EncryptionKeyType p) {
// ignore: exhaustive_cases
switch (p) {
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_NONE:
return EncryptionKeyType.none;
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PIN:
return EncryptionKeyType.pin;
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PASSWORD:
return EncryptionKeyType.password;
}
throw StateError('unknown EncryptionKeyType enum value');
}
String toJson() => name.toPascalCase();
proto.EncryptionKeyType toProto() => switch (this) {
EncryptionKeyType.none =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_NONE,
EncryptionKeyType.pin =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PIN,
EncryptionKeyType.password =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PASSWORD,
};
}
// Local Accounts are stored in a table locally and not backed by a DHT key
// and represents the accounts that have been added/imported
// on the current device.
// Stores a copy of the IdentityMaster associated with the account
// and the identitySecretKey optionally encrypted by an unlock code
// This is the root of the account information tree for VeilidChat
//
@freezed
class LocalAccount with _$LocalAccount {
const factory LocalAccount({
// The master key record for the account, containing the identityPublicKey
required IdentityMaster identityMaster,
// The encrypted identity secret that goes with
// the identityPublicKey with appended salt
@Uint8ListJsonConverter() required Uint8List identitySecretBytes,
// The kind of encryption input used on the account
required EncryptionKeyType encryptionKeyType,
// If account is not hidden, password can be retrieved via
required bool biometricsEnabled,
// Keep account hidden unless account password is entered
// (tries all hidden accounts with auth method (no biometrics))
required bool hiddenAccount,
// Display name for account until it is unlocked
required String name,
}) = _LocalAccount;
factory LocalAccount.fromJson(dynamic json) =>
_$LocalAccountFromJson(json as Map<String, dynamic>);
}

47
lib/init.dart Normal file
View File

@ -0,0 +1,47 @@
import 'dart:async';
import 'local_account_manager/local_account_manager.dart';
import 'processor.dart';
import 'tools/tools.dart';
import 'veilid_support/veilid_support.dart';
const String appName = 'VeilidChat';
final Completer<Veilid> eventualVeilid = Completer<Veilid>();
final Processor processor = Processor();
final Completer<void> eventualInitialized = Completer<void>();
// Initialize Veilid
Future<void> initializeVeilid() async {
// Ensure this runs only once
if (eventualVeilid.isCompleted) {
return;
}
// Init Veilid
Veilid.instance.initializeVeilidCore(getDefaultVeilidPlatformConfig(appName));
// Veilid logging
initVeilidLog();
// Startup Veilid
await processor.startup();
// Share the initialized veilid instance to the rest of the app
eventualVeilid.complete(Veilid.instance);
}
// Initialize repositories
Future<void> initializeRepositories() async {
await AccountRepository.instance;
}
Future<void> initializeVeilidChat() async {
log.info("Initializing Veilid");
await initializeVeilid();
log.info("Initializing Repositories");
await initializeRepositories();
eventualInitialized.complete();
}

View File

@ -0,0 +1,285 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import '../../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import 'active_logins.dart';
import 'encryption_key_type.dart';
import 'local_account.dart';
import 'user_login.dart';
export 'active_logins.dart';
export 'encryption_key_type.dart';
export 'local_account.dart';
export 'user_login.dart';
const String veilidChatAccountKey = 'com.veilid.veilidchat';
enum AccountRepositoryChange { localAccounts, userLogins, activeUserLogin }
class AccountRepository {
AccountRepository._()
: _localAccounts = _initLocalAccounts(),
_activeLogins = _initActiveLogins();
static TableDBValue<IList<LocalAccount>> _initLocalAccounts() => TableDBValue(
tableName: 'local_account_manager',
tableKeyName: 'local_accounts',
valueFromJson: (obj) => obj != null
? IList<LocalAccount>.fromJson(
obj, genericFromJson(LocalAccount.fromJson))
: IList<LocalAccount>(),
valueToJson: (val) => val.toJson((la) => la.toJson()));
static TableDBValue<ActiveLogins> _initActiveLogins() => TableDBValue(
tableName: 'local_account_manager',
tableKeyName: 'active_logins',
valueFromJson: (obj) => obj != null
? ActiveLogins.fromJson(obj as Map<String, dynamic>)
: ActiveLogins.empty(),
valueToJson: (val) => val.toJson());
final TableDBValue<IList<LocalAccount>> _localAccounts;
final TableDBValue<ActiveLogins> _activeLogins;
//////////////////////////////////////////////////////////////
/// Singleton initialization
static AccountRepository? _instance;
static Future<AccountRepository> get instance async {
if (_instance == null) {
final accountRepository = AccountRepository._();
await accountRepository.init();
_instance = accountRepository;
}
return _instance!;
}
Future<void> init() async {
await _localAccounts.load();
await _activeLogins.load();
}
//////////////////////////////////////////////////////////////
/// Streams
Stream<AccountRepositoryChange> changes() async* {}
//////////////////////////////////////////////////////////////
/// Selectors
IList<LocalAccount> getLocalAccounts() => _localAccounts.requireValue;
IList<UserLogin> getUserLogins() => _activeLogins.requireValue.userLogins;
TypedKey? getActiveUserLogin() => _activeLogins.requireValue.activeUserLogin;
LocalAccount? fetchLocalAccount({required TypedKey accountMasterRecordKey}) {
final localAccounts = _localAccounts.requireValue;
final idx = localAccounts.indexWhere(
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
if (idx == -1) {
return null;
}
return localAccounts[idx];
}
UserLogin? fetchLogin({required TypedKey accountMasterRecordKey}) {
final userLogins = _activeLogins.requireValue.userLogins;
final idx = userLogins
.indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey);
if (idx == -1) {
return null;
}
return userLogins[idx];
}
//////////////////////////////////////////////////////////////
/// Mutators
/// Reorder accounts
Future<void> reorderAccount(int oldIndex, int newIndex) async {
final localAccounts = await _localAccounts.get();
final removedItem = Output<LocalAccount>();
final updated = localAccounts
.removeAt(oldIndex, removedItem)
.insert(newIndex, removedItem.value!);
await _localAccounts.set(updated);
}
/// Creates a new Account associated with master identity
/// Adds a logged-out LocalAccount to track its existence on this device
Future<LocalAccount> newLocalAccount(
{required IdentityMaster identityMaster,
required SecretKey identitySecret,
required String name,
required String pronouns,
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
String encryptionKey = ''}) async {
final localAccounts = await _localAccounts.get();
// Add account with profile to DHT
await identityMaster.addAccountToIdentity(
identitySecret: identitySecret,
accountKey: veilidChatAccountKey,
createAccountCallback: (parent) async {
// Make empty contact list
final contactList = await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make empty contact invitation record list
final contactInvitationRecords =
await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make empty chat record list
final chatRecords = await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make account object
final account = proto.Account()
..profile = (proto.Profile()
..name = name
..pronouns = pronouns)
..contactList = contactList.toProto()
..contactInvitationRecords = contactInvitationRecords.toProto()
..chatList = chatRecords.toProto();
return account;
});
// Encrypt identitySecret with key
final identitySecretBytes = await encryptSecretToBytes(
secret: identitySecret,
cryptoKind: identityMaster.identityRecordKey.kind,
encryptionKey: encryptionKey,
encryptionKeyType: encryptionKeyType);
// Create local account object
// Does not contain the account key or its secret
// as that is not to be persisted, and only pulled from the identity key
// and optionally decrypted with the unlock password
final localAccount = LocalAccount(
identityMaster: identityMaster,
identitySecretBytes: identitySecretBytes,
encryptionKeyType: encryptionKeyType,
biometricsEnabled: false,
hiddenAccount: false,
name: name,
);
// Add local account object to internal store
final newLocalAccounts = localAccounts.add(localAccount);
await _localAccounts.set(newLocalAccounts);
// Return local account object
return localAccount;
}
/// Remove an account and wipe the messages for this account from this device
Future<bool> deleteLocalAccount(TypedKey accountMasterRecordKey) async {
await logout(accountMasterRecordKey);
final localAccounts = await _localAccounts.get();
final newLocalAccounts = localAccounts.removeWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
await _localAccounts.set(newLocalAccounts);
// TO DO: wipe messages
return true;
}
/// Import an account from another VeilidChat instance
/// Recover an account with the master identity secret
/// Delete an account from all devices
Future<void> switchToAccount(TypedKey? accountMasterRecordKey) async {
final activeLogins = await _activeLogins.get();
if (accountMasterRecordKey != null) {
// Assert the specified record key can be found, will throw if not
final _ = activeLogins.userLogins.firstWhere(
(ul) => ul.accountMasterRecordKey == accountMasterRecordKey);
}
final newActiveLogins =
activeLogins.copyWith(activeUserLogin: accountMasterRecordKey);
await _activeLogins.set(newActiveLogins);
}
Future<bool> _decryptedLogin(
IdentityMaster identityMaster, SecretKey identitySecret) async {
final cs = await Veilid.instance
.getCryptoSystem(identityMaster.identityRecordKey.kind);
final keyOk = await cs.validateKeyPair(
identityMaster.identityPublicKey, identitySecret);
if (!keyOk) {
throw Exception('Identity is corrupted');
}
// Read the identity key to get the account keys
final accountRecordInfo = await identityMaster.readAccountFromIdentity(
identitySecret: identitySecret, accountKey: veilidChatAccountKey);
// Add to user logins and select it
final activeLogins = await _activeLogins.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);
return true;
}
Future<bool> login(TypedKey accountMasterRecordKey,
EncryptionKeyType encryptionKeyType, String encryptionKey) async {
final localAccounts = await _localAccounts.get();
// Get account, throws if not found
final localAccount = localAccounts.firstWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
// Log in with this local account
// Derive key from password
if (localAccount.encryptionKeyType != encryptionKeyType) {
throw Exception('Wrong authentication type');
}
final identitySecret = await decryptSecretFromBytes(
secretBytes: localAccount.identitySecretBytes,
cryptoKind: localAccount.identityMaster.identityRecordKey.kind,
encryptionKeyType: localAccount.encryptionKeyType,
encryptionKey: encryptionKey,
);
// Validate this secret with the identity public key and log in
return _decryptedLogin(localAccount.identityMaster, identitySecret);
}
Future<void> logout(TypedKey? accountMasterRecordKey) async {
final activeLogins = await _activeLogins.get();
final logoutUser = accountMasterRecordKey ?? activeLogins.activeUserLogin;
if (logoutUser == null) {
return;
}
final newActiveLogins = activeLogins.copyWith(
activeUserLogin: activeLogins.activeUserLogin == logoutUser
? null
: activeLogins.activeUserLogin,
userLogins: activeLogins.userLogins
.removeWhere((ul) => ul.accountMasterRecordKey == logoutUser));
await _activeLogins.set(newActiveLogins);
}
}

View File

@ -0,0 +1,25 @@
// 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 '../../veilid_support/veilid_support.dart';
import 'user_login.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

@ -0,0 +1,181 @@
// 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#custom-getters-and-methods');
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

@ -0,0 +1,24 @@
// 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

@ -0,0 +1,40 @@
// Local account identitySecretKey is potentially encrypted with a key
// using the following mechanisms
// * None : no key, bytes are unencrypted
// * Pin : Code is a numeric pin (4-256 numeric digits) hashed with Argon2
// * Password: Code is a UTF-8 string that is hashed with Argon2
import 'package:change_case/change_case.dart';
import '../../../proto/proto.dart' as proto;
enum EncryptionKeyType {
none,
pin,
password;
factory EncryptionKeyType.fromJson(dynamic j) =>
EncryptionKeyType.values.byName((j as String).toCamelCase());
factory EncryptionKeyType.fromProto(proto.EncryptionKeyType p) {
// ignore: exhaustive_cases
switch (p) {
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_NONE:
return EncryptionKeyType.none;
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PIN:
return EncryptionKeyType.pin;
case proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PASSWORD:
return EncryptionKeyType.password;
}
throw StateError('unknown EncryptionKeyType enum value');
}
String toJson() => name.toPascalCase();
proto.EncryptionKeyType toProto() => switch (this) {
EncryptionKeyType.none =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_NONE,
EncryptionKeyType.pin =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PIN,
EncryptionKeyType.password =>
proto.EncryptionKeyType.ENCRYPTION_KEY_TYPE_PASSWORD,
};
}

View File

@ -0,0 +1,39 @@
import 'dart:typed_data';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../veilid_support/veilid_support.dart';
import 'encryption_key_type.dart';
part 'local_account.g.dart';
part 'local_account.freezed.dart';
// Local Accounts are stored in a table locally and not backed by a DHT key
// and represents the accounts that have been added/imported
// on the current device.
// Stores a copy of the IdentityMaster associated with the account
// and the identitySecretKey optionally encrypted by an unlock code
// This is the root of the account information tree for VeilidChat
//
@freezed
class LocalAccount with _$LocalAccount {
const factory LocalAccount({
// The master key record for the account, containing the identityPublicKey
required IdentityMaster identityMaster,
// The encrypted identity secret that goes with
// the identityPublicKey with appended salt
@Uint8ListJsonConverter() required Uint8List identitySecretBytes,
// The kind of encryption input used on the account
required EncryptionKeyType encryptionKeyType,
// If account is not hidden, password can be retrieved via
required bool biometricsEnabled,
// Keep account hidden unless account password is entered
// (tries all hidden accounts with auth method (no biometrics))
required bool hiddenAccount,
// Display name for account until it is unlocked
required String name,
}) = _LocalAccount;
factory LocalAccount.fromJson(dynamic json) =>
_$LocalAccountFromJson(json as Map<String, dynamic>);
}

View File

@ -1,7 +1,6 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../veilid_support/veilid_support.dart';
import '../../veilid_support/veilid_support.dart';
part 'user_login.freezed.dart';
part 'user_login.g.dart';
@ -26,21 +25,3 @@ class UserLogin with _$UserLogin {
factory UserLogin.fromJson(dynamic json) =>
_$UserLoginFromJson(json as Map<String, dynamic>);
}
// Represents a set of user logins
// and the currently selected account
@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) =>
_$ActiveLoginsFromJson(json as Map<String, dynamic>);
}

View File

@ -238,169 +238,3 @@ abstract class _UserLogin implements UserLogin {
_$$UserLoginImplCopyWith<_$UserLoginImpl> get copyWith =>
throw _privateConstructorUsedError;
}
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

@ -24,20 +24,3 @@ Map<String, dynamic> _$$UserLoginImplToJson(_$UserLoginImpl instance) =>
'account_record_info': instance.accountRecordInfo.toJson(),
'last_active': instance.lastActive.toJson(),
};
_$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

@ -0,0 +1,42 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import '../../veilid_support/veilid_support.dart';
import '../account_repository/account_repository.dart';
part 'active_user_login_state.dart';
class ActiveUserLoginCubit extends Cubit<ActiveUserLoginState> {
ActiveUserLoginCubit({required AccountRepository accountRepository})
: _accountRepository = accountRepository,
super(null) {
// Subscribe to streams
_initAccountRepositorySubscription();
}
void _initAccountRepositorySubscription() {
_accountRepositorySubscription =
_accountRepository.changes().listen((change) {
switch (change) {
case AccountRepositoryChange.activeUserLogin:
emit(_accountRepository.getActiveUserLogin());
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;
}

View File

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

View File

@ -0,0 +1,3 @@
export 'account_repository/account_repository.dart';
export 'local_accounts_cubit/local_accounts_cubit.dart';
export 'user_logins_cubit/user_logins_cubit.dart';

View File

@ -0,0 +1,42 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import '../account_repository/account_repository.dart';
part 'local_accounts_state.dart';
class LocalAccountsCubit extends Cubit<LocalAccountsState> {
LocalAccountsCubit({required AccountRepository accountRepository})
: _accountRepository = accountRepository,
super(LocalAccountsState()) {
// Subscribe to streams
_initAccountRepositorySubscription();
}
void _initAccountRepositorySubscription() {
_accountRepositorySubscription =
_accountRepository.changes().listen((change) {
switch (change) {
case AccountRepositoryChange.localAccounts:
emit(_accountRepository.getLocalAccounts());
break;
// Ignore these
case AccountRepositoryChange.userLogins:
case AccountRepositoryChange.activeUserLogin:
break;
}
});
}
@override
Future<void> close() async {
await super.close();
await _accountRepositorySubscription.cancel();
}
final AccountRepository _accountRepository;
late final StreamSubscription<AccountRepositoryChange>
_accountRepositorySubscription;
}

View File

@ -0,0 +1,3 @@
part of 'local_accounts_cubit.dart';
typedef LocalAccountsState = IList<LocalAccount>;

View File

@ -0,0 +1,42 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import '../account_repository/account_repository.dart';
part 'user_logins_state.dart';
class UserLoginsCubit extends Cubit<UserLoginsState> {
UserLoginsCubit({required AccountRepository accountRepository})
: _accountRepository = accountRepository,
super(UserLoginsState()) {
// Subscribe to streams
_initAccountRepositorySubscription();
}
void _initAccountRepositorySubscription() {
_accountRepositorySubscription =
_accountRepository.changes().listen((change) {
switch (change) {
case AccountRepositoryChange.userLogins:
emit(_accountRepository.getUserLogins());
break;
// Ignore these
case AccountRepositoryChange.localAccounts:
case AccountRepositoryChange.activeUserLogin:
break;
}
});
}
@override
Future<void> close() async {
await super.close();
await _accountRepositorySubscription.cancel();
}
final AccountRepository _accountRepository;
late final StreamSubscription<AccountRepositoryChange>
_accountRepositorySubscription;
}

View File

@ -0,0 +1,3 @@
part of 'user_logins_cubit.dart';
typedef UserLoginsState = IList<UserLogin>;

View File

@ -4,14 +4,16 @@ import 'dart:io';
import 'package:ansicolor/ansicolor.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'app.dart';
import 'providers/window_control.dart';
import 'old_to_refactor/providers/window_control.dart';
import 'theme/theme.dart';
import 'tools/tools.dart';
import 'veilid_init.dart';
import 'init.dart';
const String appName = "VeilidChat";
void main() async {
// Disable all debugprints in release mode
@ -34,8 +36,8 @@ void main() async {
// Prepare theme
WidgetsFlutterBinding.ensureInitialized();
final themeService = await ThemeService.instance;
final initTheme = themeService.initial;
final themeRepository = await ThemeRepository.instance;
final themeData = themeRepository.themeData();
// Manage window on desktop platforms
await WindowControl.initialize();
@ -50,9 +52,7 @@ void main() async {
// Run the app
// Hot reloads will only restart this part, not Veilid
runApp(ProviderScope(
observers: const [StateLogger()],
child: LocalizedApp(delegate, VeilidChatApp(theme: initTheme))));
runApp(LocalizedApp(delegate, VeilidChatApp(themeData: themeData)));
}, (error, stackTrace) {
log.error('Dart Runtime: {$error}\n{$stackTrace}');
});

View File

@ -6,7 +6,7 @@ import 'package:flutter_translate/flutter_translate.dart';
import '../proto/proto.dart' as proto;
import '../providers/account.dart';
import '../providers/chat.dart';
import '../tools/theme_service.dart';
import '../theme/theme.dart';
class ChatSingleContactItemWidget extends ConsumerWidget {
const ChatSingleContactItemWidget({required this.contact, super.key});

View File

@ -9,7 +9,7 @@ import '../pages/main_pager/main_pager.dart';
import '../providers/account.dart';
import '../providers/chat.dart';
import '../providers/contact.dart';
import '../tools/theme_service.dart';
import '../theme/theme.dart';
class ContactItemWidget extends ConsumerWidget {
const ContactItemWidget({required this.contact, super.key});

View File

@ -4,19 +4,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'preferences.freezed.dart';
part 'preferences.g.dart';
// Theme supports light and dark mode, optionally selected by the
// operating system
enum BrightnessPreference {
system,
light,
dark;
factory BrightnessPreference.fromJson(dynamic j) =>
BrightnessPreference.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
}
// Lock preference changes how frequently the messenger locks its
// interface and requires the identitySecretKey to be entered (pin/password/etc)
@freezed
@ -31,28 +18,6 @@ class LockPreference with _$LockPreference {
_$LockPreferenceFromJson(json as Map<String, dynamic>);
}
// Theme supports multiple color variants based on 'Radix'
enum ColorPreference {
// Radix Colors
scarlet,
babydoll,
vapor,
gold,
garden,
forest,
arctic,
lapis,
eggplant,
lime,
grim,
// Accessible Colors
contrast;
factory ColorPreference.fromJson(dynamic j) =>
ColorPreference.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
}
// Theme supports multiple translations
enum LanguagePreference {
englishUS;
@ -62,18 +27,6 @@ enum LanguagePreference {
String toJson() => name.toPascalCase();
}
@freezed
class ThemePreferences with _$ThemePreferences {
const factory ThemePreferences({
required BrightnessPreference brightnessPreference,
required ColorPreference colorPreference,
required double displayScale,
}) = _ThemePreferences;
factory ThemePreferences.fromJson(dynamic json) =>
_$ThemePreferencesFromJson(json as Map<String, dynamic>);
}
// Preferences are stored in a table locally and globally affect all
// accounts imported/added and the app in general
@freezed

View File

@ -199,192 +199,6 @@ abstract class _LockPreference implements LockPreference {
throw _privateConstructorUsedError;
}
ThemePreferences _$ThemePreferencesFromJson(Map<String, dynamic> json) {
return _ThemePreferences.fromJson(json);
}
/// @nodoc
mixin _$ThemePreferences {
BrightnessPreference get brightnessPreference =>
throw _privateConstructorUsedError;
ColorPreference get colorPreference => throw _privateConstructorUsedError;
double get displayScale => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ThemePreferencesCopyWith<ThemePreferences> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ThemePreferencesCopyWith<$Res> {
factory $ThemePreferencesCopyWith(
ThemePreferences value, $Res Function(ThemePreferences) then) =
_$ThemePreferencesCopyWithImpl<$Res, ThemePreferences>;
@useResult
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
double displayScale});
}
/// @nodoc
class _$ThemePreferencesCopyWithImpl<$Res, $Val extends ThemePreferences>
implements $ThemePreferencesCopyWith<$Res> {
_$ThemePreferencesCopyWithImpl(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? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
}) {
return _then(_value.copyWith(
brightnessPreference: null == brightnessPreference
? _value.brightnessPreference
: brightnessPreference // ignore: cast_nullable_to_non_nullable
as BrightnessPreference,
colorPreference: null == colorPreference
? _value.colorPreference
: colorPreference // ignore: cast_nullable_to_non_nullable
as ColorPreference,
displayScale: null == displayScale
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
) as $Val);
}
}
/// @nodoc
abstract class _$$ThemePreferencesImplCopyWith<$Res>
implements $ThemePreferencesCopyWith<$Res> {
factory _$$ThemePreferencesImplCopyWith(_$ThemePreferencesImpl value,
$Res Function(_$ThemePreferencesImpl) then) =
__$$ThemePreferencesImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
double displayScale});
}
/// @nodoc
class __$$ThemePreferencesImplCopyWithImpl<$Res>
extends _$ThemePreferencesCopyWithImpl<$Res, _$ThemePreferencesImpl>
implements _$$ThemePreferencesImplCopyWith<$Res> {
__$$ThemePreferencesImplCopyWithImpl(_$ThemePreferencesImpl _value,
$Res Function(_$ThemePreferencesImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
}) {
return _then(_$ThemePreferencesImpl(
brightnessPreference: null == brightnessPreference
? _value.brightnessPreference
: brightnessPreference // ignore: cast_nullable_to_non_nullable
as BrightnessPreference,
colorPreference: null == colorPreference
? _value.colorPreference
: colorPreference // ignore: cast_nullable_to_non_nullable
as ColorPreference,
displayScale: null == displayScale
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ThemePreferencesImpl implements _ThemePreferences {
const _$ThemePreferencesImpl(
{required this.brightnessPreference,
required this.colorPreference,
required this.displayScale});
factory _$ThemePreferencesImpl.fromJson(Map<String, dynamic> json) =>
_$$ThemePreferencesImplFromJson(json);
@override
final BrightnessPreference brightnessPreference;
@override
final ColorPreference colorPreference;
@override
final double displayScale;
@override
String toString() {
return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ThemePreferencesImpl &&
(identical(other.brightnessPreference, brightnessPreference) ||
other.brightnessPreference == brightnessPreference) &&
(identical(other.colorPreference, colorPreference) ||
other.colorPreference == colorPreference) &&
(identical(other.displayScale, displayScale) ||
other.displayScale == displayScale));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, brightnessPreference, colorPreference, displayScale);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith =>
__$$ThemePreferencesImplCopyWithImpl<_$ThemePreferencesImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ThemePreferencesImplToJson(
this,
);
}
}
abstract class _ThemePreferences implements ThemePreferences {
const factory _ThemePreferences(
{required final BrightnessPreference brightnessPreference,
required final ColorPreference colorPreference,
required final double displayScale}) = _$ThemePreferencesImpl;
factory _ThemePreferences.fromJson(Map<String, dynamic> json) =
_$ThemePreferencesImpl.fromJson;
@override
BrightnessPreference get brightnessPreference;
@override
ColorPreference get colorPreference;
@override
double get displayScale;
@override
@JsonKey(ignore: true)
_$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Preferences _$PreferencesFromJson(Map<String, dynamic> json) {
return _Preferences.fromJson(json);
}
@ -412,7 +226,6 @@ abstract class $PreferencesCopyWith<$Res> {
LanguagePreference language,
LockPreference locking});
$ThemePreferencesCopyWith<$Res> get themePreferences;
$LockPreferenceCopyWith<$Res> get locking;
}
@ -429,12 +242,12 @@ class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? themePreferences = null,
Object? themePreferences = freezed,
Object? language = null,
Object? locking = null,
}) {
return _then(_value.copyWith(
themePreferences: null == themePreferences
themePreferences: freezed == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
@ -449,14 +262,6 @@ class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences>
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$ThemePreferencesCopyWith<$Res> get themePreferences {
return $ThemePreferencesCopyWith<$Res>(_value.themePreferences, (value) {
return _then(_value.copyWith(themePreferences: value) as $Val);
});
}
@override
@pragma('vm:prefer-inline')
$LockPreferenceCopyWith<$Res> get locking {
@ -479,8 +284,6 @@ abstract class _$$PreferencesImplCopyWith<$Res>
LanguagePreference language,
LockPreference locking});
@override
$ThemePreferencesCopyWith<$Res> get themePreferences;
@override
$LockPreferenceCopyWith<$Res> get locking;
}
@ -496,12 +299,12 @@ class __$$PreferencesImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? themePreferences = null,
Object? themePreferences = freezed,
Object? language = null,
Object? locking = null,
}) {
return _then(_$PreferencesImpl(
themePreferences: null == themePreferences
themePreferences: freezed == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
@ -545,8 +348,8 @@ class _$PreferencesImpl implements _Preferences {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PreferencesImpl &&
(identical(other.themePreferences, themePreferences) ||
other.themePreferences == themePreferences) &&
const DeepCollectionEquality()
.equals(other.themePreferences, themePreferences) &&
(identical(other.language, language) ||
other.language == language) &&
(identical(other.locking, locking) || other.locking == locking));
@ -554,8 +357,8 @@ class _$PreferencesImpl implements _Preferences {
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, themePreferences, language, locking);
int get hashCode => Object.hash(runtimeType,
const DeepCollectionEquality().hash(themePreferences), language, locking);
@JsonKey(ignore: true)
@override

View File

@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/window_control.dart';
import 'home.dart';
class ChatOnlyPage extends ConsumerStatefulWidget {
class ChatOnlyPage extends StatefulWidget {
const ChatOnlyPage({super.key});
@override

View File

@ -13,8 +13,8 @@ import 'package:loggy/loggy.dart';
import 'package:quickalert/quickalert.dart';
import 'package:xterm/xterm.dart';
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
final globalDebugTerminal = Terminal(
maxLines: 50000,
@ -25,7 +25,7 @@ const kDefaultTerminalStyle = TerminalStyle(
// height: 1.2,
fontFamily: 'Source Code Pro');
class DeveloperPage extends ConsumerStatefulWidget {
class DeveloperPage extends StatefulWidget {
const DeveloperPage({super.key});
@override

View File

@ -6,22 +6,22 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:go_router/go_router.dart';
import '../proto/proto.dart' as proto;
import '../components/chat_component.dart';
import '../components/empty_chat_widget.dart';
import '../components/profile_widget.dart';
import '../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../components/chat_component.dart';
import '../../components/empty_chat_widget.dart';
import '../../components/profile_widget.dart';
import '../../entities/local_account.dart';
import '../providers/account.dart';
import '../providers/chat.dart';
import '../providers/contact.dart';
import '../providers/local_accounts.dart';
import '../../local_accounts/local_accounts.dart';
import '../providers/logins.dart';
import '../providers/window_control.dart';
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import 'main_pager/main_pager.dart';
class HomePage extends ConsumerStatefulWidget {
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override

View File

@ -1,17 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:radix_colors/radix_colors.dart';
import '../providers/window_control.dart';
class IndexPage extends ConsumerWidget {
class IndexPage extends StatelessWidget {
const IndexPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(windowControlProvider);
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
final monoTextStyle = textTheme.labelSmall!

View File

@ -7,15 +7,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../components/contact_invitation_list_widget.dart';
import '../../components/contact_list_widget.dart';
import '../../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../../components/contact_invitation_list_widget.dart';
import '../../../components/contact_list_widget.dart';
import '../../../entities/local_account.dart';
import '../../../proto/proto.dart' as proto;
import '../../providers/contact.dart';
import '../../providers/contact_invite.dart';
import '../../tools/theme_service.dart';
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import '../../../theme/theme.dart';
import '../../../tools/tools.dart';
import '../../../veilid_support/veilid_support.dart';
class AccountPage extends ConsumerStatefulWidget {
const AccountPage({

View File

@ -3,17 +3,17 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../components/chat_single_contact_list_widget.dart';
import '../../components/empty_chat_list_widget.dart';
import '../../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../../components/chat_single_contact_list_widget.dart';
import '../../../components/empty_chat_list_widget.dart';
import '../../../entities/local_account.dart';
import '../../../proto/proto.dart' as proto;
import '../../providers/account.dart';
import '../../providers/chat.dart';
import '../../providers/contact.dart';
import '../../providers/local_accounts.dart';
import '../../../local_accounts/local_accounts.dart';
import '../../providers/logins.dart';
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import '../../../tools/tools.dart';
import '../../../veilid_support/veilid_support.dart';
class ChatsPage extends ConsumerStatefulWidget {
const ChatsPage({super.key});

View File

@ -13,14 +13,14 @@ import 'package:preload_page_view/preload_page_view.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../components/bottom_sheet_action_button.dart';
import '../../components/paste_invite_dialog.dart';
import '../../components/scan_invite_dialog.dart';
import '../../components/send_invite_dialog.dart';
import '../../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import '../../../components/bottom_sheet_action_button.dart';
import '../../../components/paste_invite_dialog.dart';
import '../../../components/scan_invite_dialog.dart';
import '../../../components/send_invite_dialog.dart';
import '../../../entities/local_account.dart';
import '../../../proto/proto.dart' as proto;
import '../../../tools/tools.dart';
import '../../../veilid_support/veilid_support.dart';
import 'account.dart';
import 'chats.dart';

View File

@ -7,16 +7,16 @@ import 'package:flutter_translate/flutter_translate.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:go_router/go_router.dart';
import '../components/default_app_bar.dart';
import '../components/signal_strength_meter.dart';
import '../entities/entities.dart';
import '../providers/local_accounts.dart';
import '../../components/default_app_bar.dart';
import '../../components/signal_strength_meter.dart';
import '../../entities/entities.dart';
import '../../local_accounts/local_accounts.dart';
import '../providers/logins.dart';
import '../providers/window_control.dart';
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
class NewAccountPage extends ConsumerStatefulWidget {
class NewAccountPage extends StatefulWidget {
const NewAccountPage({super.key});
@override

View File

@ -6,13 +6,13 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../components/default_app_bar.dart';
import '../components/signal_strength_meter.dart';
import '../entities/preferences.dart';
import '../../components/default_app_bar.dart';
import '../../components/signal_strength_meter.dart';
import '../../entities/preferences.dart';
import '../providers/window_control.dart';
import '../tools/tools.dart';
import '../../tools/tools.dart';
class SettingsPage extends ConsumerStatefulWidget {
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override

View File

@ -1,11 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../entities/local_account.dart';
import '../entities/user_login.dart';
import '../proto/proto.dart' as proto;
import '../veilid_support/veilid_support.dart';
import 'local_accounts.dart';
import '../../entities/local_account.dart';
import '../../entities/user_login.dart';
import '../../proto/proto.dart' as proto;
import '../../veilid_support/veilid_support.dart';
import '../../local_accounts/local_accounts.dart';
import 'logins.dart';
part 'account.g.dart';

View File

@ -2,9 +2,9 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../proto/proto.dart' as proto;
import '../../proto/proto.dart' as proto;
import '../veilid_support/veilid_support.dart';
import '../../veilid_support/veilid_support.dart';
import 'account.dart';
part 'chat.g.dart';

View File

@ -1,7 +1,7 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../veilid_support/veilid_support.dart';
import '../../veilid_support/veilid_support.dart';
part 'connection_state.freezed.dart';

View File

@ -3,10 +3,10 @@ import 'dart:convert';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../proto/proto.dart' as proto;
import '../../proto/proto.dart' as proto;
import '../veilid_support/veilid_support.dart';
import '../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import '../../tools/tools.dart';
import 'account.dart';
import 'chat.dart';

View File

@ -4,10 +4,10 @@ import 'package:flutter/foundation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:mutex/mutex.dart';
import '../entities/entities.dart';
import '../proto/proto.dart' as proto;
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import '../../entities/entities.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import 'account.dart';
part 'contact_invitation_list_manager.g.dart';

View File

@ -4,10 +4,10 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:fixnum/fixnum.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../entities/local_account.dart';
import '../proto/proto.dart' as proto;
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import '../../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import 'account.dart';
import 'conversation.dart';

View File

@ -7,11 +7,11 @@ import 'dart:convert';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../proto/proto.dart' as proto;
import '../../proto/proto.dart' as proto;
import '../tools/tools.dart';
import '../veilid_init.dart';
import '../veilid_support/veilid_support.dart';
import '../../tools/tools.dart';
import '../../veilid_init.dart';
import '../../veilid_support/veilid_support.dart';
import 'account.dart';
import 'chat.dart';
import 'contact.dart';

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:window_manager/window_manager.dart';
import '../tools/responsive.dart';
import '../../tools/responsive.dart';
export 'package:window_manager/window_manager.dart' show TitleBarStyle;

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:veilid/veilid.dart';
import 'providers/connection_state.dart';
import 'old_to_refactor/providers/connection_state.dart';
import 'tools/tools.dart';
import 'veilid_support/src/config.dart';
import 'veilid_support/src/veilid_log.dart';

View File

@ -1,200 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'account.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchAccountInfoHash() => r'3d2e3b3ddce5158d03bceaf82cdb35bae000280c';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
@ProviderFor(fetchAccountInfo)
const fetchAccountInfoProvider = FetchAccountInfoFamily();
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
class FetchAccountInfoFamily extends Family<AsyncValue<AccountInfo>> {
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
const FetchAccountInfoFamily();
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
FetchAccountInfoProvider call({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) {
return FetchAccountInfoProvider(
accountMasterRecordKey: accountMasterRecordKey,
);
}
@override
FetchAccountInfoProvider getProviderOverride(
covariant FetchAccountInfoProvider provider,
) {
return call(
accountMasterRecordKey: provider.accountMasterRecordKey,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'fetchAccountInfoProvider';
}
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
class FetchAccountInfoProvider extends AutoDisposeFutureProvider<AccountInfo> {
/// Get an account from the identity key and if it is logged in and we
/// have its secret available, return the account record contents
///
/// Copied from [fetchAccountInfo].
FetchAccountInfoProvider({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) : this._internal(
(ref) => fetchAccountInfo(
ref as FetchAccountInfoRef,
accountMasterRecordKey: accountMasterRecordKey,
),
from: fetchAccountInfoProvider,
name: r'fetchAccountInfoProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchAccountInfoHash,
dependencies: FetchAccountInfoFamily._dependencies,
allTransitiveDependencies:
FetchAccountInfoFamily._allTransitiveDependencies,
accountMasterRecordKey: accountMasterRecordKey,
);
FetchAccountInfoProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.accountMasterRecordKey,
}) : super.internal();
final Typed<FixedEncodedString43> accountMasterRecordKey;
@override
Override overrideWith(
FutureOr<AccountInfo> Function(FetchAccountInfoRef provider) create,
) {
return ProviderOverride(
origin: this,
override: FetchAccountInfoProvider._internal(
(ref) => create(ref as FetchAccountInfoRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
accountMasterRecordKey: accountMasterRecordKey,
),
);
}
@override
AutoDisposeFutureProviderElement<AccountInfo> createElement() {
return _FetchAccountInfoProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is FetchAccountInfoProvider &&
other.accountMasterRecordKey == accountMasterRecordKey;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, accountMasterRecordKey.hashCode);
return _SystemHash.finish(hash);
}
}
mixin FetchAccountInfoRef on AutoDisposeFutureProviderRef<AccountInfo> {
/// The parameter `accountMasterRecordKey` of this provider.
Typed<FixedEncodedString43> get accountMasterRecordKey;
}
class _FetchAccountInfoProviderElement
extends AutoDisposeFutureProviderElement<AccountInfo>
with FetchAccountInfoRef {
_FetchAccountInfoProviderElement(super.provider);
@override
Typed<FixedEncodedString43> get accountMasterRecordKey =>
(origin as FetchAccountInfoProvider).accountMasterRecordKey;
}
String _$fetchActiveAccountInfoHash() =>
r'85276ff85b0e82c8d3c6313250954f5b578697d1';
/// Get the active account info
///
/// Copied from [fetchActiveAccountInfo].
@ProviderFor(fetchActiveAccountInfo)
final fetchActiveAccountInfoProvider =
AutoDisposeFutureProvider<ActiveAccountInfo?>.internal(
fetchActiveAccountInfo,
name: r'fetchActiveAccountInfoProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchActiveAccountInfoHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef FetchActiveAccountInfoRef
= AutoDisposeFutureProviderRef<ActiveAccountInfo?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,28 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chat.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchChatListHash() => r'0c166082625799862128dff09d9286f64785ba6c';
/// Get the active account contact list
///
/// Copied from [fetchChatList].
@ProviderFor(fetchChatList)
final fetchChatListProvider =
AutoDisposeFutureProvider<IList<proto.Chat>?>.internal(
fetchChatList,
name: r'fetchChatListProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchChatListHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef FetchChatListRef = AutoDisposeFutureProviderRef<IList<proto.Chat>?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,29 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contact.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchContactListHash() => r'03e5b90435c331be87495d999a62a97af5b74d9e';
/// Get the active account contact list
///
/// Copied from [fetchContactList].
@ProviderFor(fetchContactList)
final fetchContactListProvider =
AutoDisposeFutureProvider<IList<proto.Contact>?>.internal(
fetchContactList,
name: r'fetchContactListProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchContactListHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef FetchContactListRef
= AutoDisposeFutureProviderRef<IList<proto.Contact>?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,202 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contact_invitation_list_manager.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$contactInvitationListManagerHash() =>
r'8dda8e5005f0c0c921e3e8b7ce06e54bb5682085';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
abstract class _$ContactInvitationListManager
extends BuildlessAutoDisposeAsyncNotifier<
IList<proto.ContactInvitationRecord>> {
late final ActiveAccountInfo activeAccountInfo;
FutureOr<IList<proto.ContactInvitationRecord>> build(
ActiveAccountInfo activeAccountInfo,
);
}
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
@ProviderFor(ContactInvitationListManager)
const contactInvitationListManagerProvider =
ContactInvitationListManagerFamily();
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
class ContactInvitationListManagerFamily
extends Family<AsyncValue<IList<proto.ContactInvitationRecord>>> {
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
const ContactInvitationListManagerFamily();
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
ContactInvitationListManagerProvider call(
ActiveAccountInfo activeAccountInfo,
) {
return ContactInvitationListManagerProvider(
activeAccountInfo,
);
}
@override
ContactInvitationListManagerProvider getProviderOverride(
covariant ContactInvitationListManagerProvider provider,
) {
return call(
provider.activeAccountInfo,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'contactInvitationListManagerProvider';
}
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
class ContactInvitationListManagerProvider
extends AutoDisposeAsyncNotifierProviderImpl<ContactInvitationListManager,
IList<proto.ContactInvitationRecord>> {
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///
/// Copied from [ContactInvitationListManager].
ContactInvitationListManagerProvider(
ActiveAccountInfo activeAccountInfo,
) : this._internal(
() => ContactInvitationListManager()
..activeAccountInfo = activeAccountInfo,
from: contactInvitationListManagerProvider,
name: r'contactInvitationListManagerProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$contactInvitationListManagerHash,
dependencies: ContactInvitationListManagerFamily._dependencies,
allTransitiveDependencies:
ContactInvitationListManagerFamily._allTransitiveDependencies,
activeAccountInfo: activeAccountInfo,
);
ContactInvitationListManagerProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.activeAccountInfo,
}) : super.internal();
final ActiveAccountInfo activeAccountInfo;
@override
FutureOr<IList<proto.ContactInvitationRecord>> runNotifierBuild(
covariant ContactInvitationListManager notifier,
) {
return notifier.build(
activeAccountInfo,
);
}
@override
Override overrideWith(ContactInvitationListManager Function() create) {
return ProviderOverride(
origin: this,
override: ContactInvitationListManagerProvider._internal(
() => create()..activeAccountInfo = activeAccountInfo,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
activeAccountInfo: activeAccountInfo,
),
);
}
@override
AutoDisposeAsyncNotifierProviderElement<ContactInvitationListManager,
IList<proto.ContactInvitationRecord>> createElement() {
return _ContactInvitationListManagerProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is ContactInvitationListManagerProvider &&
other.activeAccountInfo == activeAccountInfo;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, activeAccountInfo.hashCode);
return _SystemHash.finish(hash);
}
}
mixin ContactInvitationListManagerRef on AutoDisposeAsyncNotifierProviderRef<
IList<proto.ContactInvitationRecord>> {
/// The parameter `activeAccountInfo` of this provider.
ActiveAccountInfo get activeAccountInfo;
}
class _ContactInvitationListManagerProviderElement
extends AutoDisposeAsyncNotifierProviderElement<
ContactInvitationListManager, IList<proto.ContactInvitationRecord>>
with ContactInvitationListManagerRef {
_ContactInvitationListManagerProviderElement(super.provider);
@override
ActiveAccountInfo get activeAccountInfo =>
(origin as ContactInvitationListManagerProvider).activeAccountInfo;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,30 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contact_invite.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchContactInvitationRecordsHash() =>
r'ff0b2c68d42cb106602982b1fb56a7bd8183c04a';
/// Get the active account contact invitation list
///
/// Copied from [fetchContactInvitationRecords].
@ProviderFor(fetchContactInvitationRecords)
final fetchContactInvitationRecordsProvider =
AutoDisposeFutureProvider<IList<proto.ContactInvitationRecord>?>.internal(
fetchContactInvitationRecords,
name: r'fetchContactInvitationRecordsProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchContactInvitationRecordsHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef FetchContactInvitationRecordsRef
= AutoDisposeFutureProviderRef<IList<proto.ContactInvitationRecord>?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,28 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'conversation.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$activeConversationMessagesHash() =>
r'5579a9386f2046b156720ae799a0e77aca119b09';
/// See also [ActiveConversationMessages].
@ProviderFor(ActiveConversationMessages)
final activeConversationMessagesProvider = AutoDisposeAsyncNotifierProvider<
ActiveConversationMessages, IList<proto.Message>?>.internal(
ActiveConversationMessages.new,
name: r'activeConversationMessagesProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$activeConversationMessagesHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ActiveConversationMessages
= AutoDisposeAsyncNotifier<IList<proto.Message>?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,167 +0,0 @@
import 'dart:async';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../entities/entities.dart';
import '../proto/proto.dart' as proto;
import '../tools/tools.dart';
import '../veilid_init.dart';
import '../veilid_support/veilid_support.dart';
import 'logins.dart';
part 'local_accounts.g.dart';
const String veilidChatAccountKey = 'com.veilid.veilidchat';
// Local accounts table
@riverpod
class LocalAccounts extends _$LocalAccounts
with AsyncTableDBBacked<IList<LocalAccount>> {
//////////////////////////////////////////////////////////////
/// AsyncTableDBBacked
@override
String tableName() => 'local_account_manager';
@override
String tableKeyName() => 'local_accounts';
@override
IList<LocalAccount> valueFromJson(Object? obj) => obj != null
? IList<LocalAccount>.fromJson(
obj, genericFromJson(LocalAccount.fromJson))
: IList<LocalAccount>();
@override
Object? valueToJson(IList<LocalAccount> val) =>
val.toJson((la) => la.toJson());
/// Get all local account information
@override
FutureOr<IList<LocalAccount>> build() async {
try {
await eventualVeilid.future;
return await load();
} on Exception catch (e) {
log.error('Failed to load LocalAccounts table: $e', e);
return const IListConst([]);
}
}
//////////////////////////////////////////////////////////////
/// Mutators and Selectors
/// Reorder accounts
Future<void> reorderAccount(int oldIndex, int newIndex) async {
final localAccounts = state.requireValue;
final removedItem = Output<LocalAccount>();
final updated = localAccounts
.removeAt(oldIndex, removedItem)
.insert(newIndex, removedItem.value!);
await store(updated);
state = AsyncValue.data(updated);
}
/// Creates a new Account associated with master identity
/// Adds a logged-out LocalAccount to track its existence on this device
Future<LocalAccount> newLocalAccount(
{required IdentityMaster identityMaster,
required SecretKey identitySecret,
required String name,
required String pronouns,
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
String encryptionKey = ''}) async {
final localAccounts = state.requireValue;
// Add account with profile to DHT
await identityMaster.addAccountToIdentity(
identitySecret: identitySecret,
accountKey: veilidChatAccountKey,
createAccountCallback: (parent) async {
// Make empty contact list
final contactList = await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make empty contact invitation record list
final contactInvitationRecords =
await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make empty chat record list
final chatRecords = await (await DHTShortArray.create(parent: parent))
.scope((r) async => r.record.ownedDHTRecordPointer);
// Make account object
final account = proto.Account()
..profile = (proto.Profile()
..name = name
..pronouns = pronouns)
..contactList = contactList.toProto()
..contactInvitationRecords = contactInvitationRecords.toProto()
..chatList = chatRecords.toProto();
return account;
});
// Encrypt identitySecret with key
final identitySecretBytes = await encryptSecretToBytes(
secret: identitySecret,
cryptoKind: identityMaster.identityRecordKey.kind,
encryptionKey: encryptionKey,
encryptionKeyType: encryptionKeyType);
// Create local account object
// Does not contain the account key or its secret
// as that is not to be persisted, and only pulled from the identity key
// and optionally decrypted with the unlock password
final localAccount = LocalAccount(
identityMaster: identityMaster,
identitySecretBytes: identitySecretBytes,
encryptionKeyType: encryptionKeyType,
biometricsEnabled: false,
hiddenAccount: false,
name: name,
);
// Add local account object to internal store
final newLocalAccounts = localAccounts.add(localAccount);
await store(newLocalAccounts);
state = AsyncValue.data(newLocalAccounts);
// Return local account object
return localAccount;
}
/// Remove an account and wipe the messages for this account from this device
Future<bool> deleteLocalAccount(TypedKey accountMasterRecordKey) async {
final logins = ref.read(loginsProvider.notifier);
await logins.logout(accountMasterRecordKey);
final localAccounts = state.requireValue;
final updated = localAccounts.removeWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
await store(updated);
state = AsyncValue.data(updated);
// TO DO: wipe messages
return true;
}
/// Import an account from another VeilidChat instance
/// Recover an account with the master identity secret
/// Delete an account from all devices
}
@riverpod
Future<LocalAccount?> fetchLocalAccount(FetchLocalAccountRef ref,
{required TypedKey accountMasterRecordKey}) async {
final localAccounts = await ref.watch(localAccountsProvider.future);
try {
return localAccounts.firstWhere(
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
} on Exception catch (e) {
if (e is StateError) {
return null;
}
rethrow;
}
}

View File

@ -1,179 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'local_accounts.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchLocalAccountHash() => r'e9f8ea0dd15031cc8145532e9cac73ab7f0f81be';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [fetchLocalAccount].
@ProviderFor(fetchLocalAccount)
const fetchLocalAccountProvider = FetchLocalAccountFamily();
/// See also [fetchLocalAccount].
class FetchLocalAccountFamily extends Family<AsyncValue<LocalAccount?>> {
/// See also [fetchLocalAccount].
const FetchLocalAccountFamily();
/// See also [fetchLocalAccount].
FetchLocalAccountProvider call({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) {
return FetchLocalAccountProvider(
accountMasterRecordKey: accountMasterRecordKey,
);
}
@override
FetchLocalAccountProvider getProviderOverride(
covariant FetchLocalAccountProvider provider,
) {
return call(
accountMasterRecordKey: provider.accountMasterRecordKey,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'fetchLocalAccountProvider';
}
/// See also [fetchLocalAccount].
class FetchLocalAccountProvider
extends AutoDisposeFutureProvider<LocalAccount?> {
/// See also [fetchLocalAccount].
FetchLocalAccountProvider({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) : this._internal(
(ref) => fetchLocalAccount(
ref as FetchLocalAccountRef,
accountMasterRecordKey: accountMasterRecordKey,
),
from: fetchLocalAccountProvider,
name: r'fetchLocalAccountProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchLocalAccountHash,
dependencies: FetchLocalAccountFamily._dependencies,
allTransitiveDependencies:
FetchLocalAccountFamily._allTransitiveDependencies,
accountMasterRecordKey: accountMasterRecordKey,
);
FetchLocalAccountProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.accountMasterRecordKey,
}) : super.internal();
final Typed<FixedEncodedString43> accountMasterRecordKey;
@override
Override overrideWith(
FutureOr<LocalAccount?> Function(FetchLocalAccountRef provider) create,
) {
return ProviderOverride(
origin: this,
override: FetchLocalAccountProvider._internal(
(ref) => create(ref as FetchLocalAccountRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
accountMasterRecordKey: accountMasterRecordKey,
),
);
}
@override
AutoDisposeFutureProviderElement<LocalAccount?> createElement() {
return _FetchLocalAccountProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is FetchLocalAccountProvider &&
other.accountMasterRecordKey == accountMasterRecordKey;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, accountMasterRecordKey.hashCode);
return _SystemHash.finish(hash);
}
}
mixin FetchLocalAccountRef on AutoDisposeFutureProviderRef<LocalAccount?> {
/// The parameter `accountMasterRecordKey` of this provider.
Typed<FixedEncodedString43> get accountMasterRecordKey;
}
class _FetchLocalAccountProviderElement
extends AutoDisposeFutureProviderElement<LocalAccount?>
with FetchLocalAccountRef {
_FetchLocalAccountProviderElement(super.provider);
@override
Typed<FixedEncodedString43> get accountMasterRecordKey =>
(origin as FetchLocalAccountProvider).accountMasterRecordKey;
}
String _$localAccountsHash() => r'f19ec560b585d353219be82bc383b2c091660c53';
/// See also [LocalAccounts].
@ProviderFor(LocalAccounts)
final localAccountsProvider = AutoDisposeAsyncNotifierProvider<LocalAccounts,
IList<LocalAccount>>.internal(
LocalAccounts.new,
name: r'localAccountsProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$localAccountsHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LocalAccounts = AutoDisposeAsyncNotifier<IList<LocalAccount>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,150 +0,0 @@
import 'dart:async';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../entities/entities.dart';
import '../tools/tools.dart';
import '../veilid_init.dart';
import '../veilid_support/veilid_support.dart';
import 'local_accounts.dart';
part 'logins.g.dart';
// Local account manager
@riverpod
class Logins extends _$Logins with AsyncTableDBBacked<ActiveLogins> {
//////////////////////////////////////////////////////////////
/// AsyncTableDBBacked
@override
String tableName() => 'local_account_manager';
@override
String tableKeyName() => 'active_logins';
@override
ActiveLogins valueFromJson(Object? obj) => obj != null
? ActiveLogins.fromJson(obj as Map<String, dynamic>)
: ActiveLogins.empty();
@override
Object? valueToJson(ActiveLogins val) => val.toJson();
/// Get all local account information
@override
FutureOr<ActiveLogins> build() async {
try {
await eventualVeilid.future;
return await load();
} on Exception catch (e) {
log.error('Failed to load ActiveLogins table: $e', e);
return const ActiveLogins(userLogins: IListConst([]));
}
}
//////////////////////////////////////////////////////////////
/// Mutators and Selectors
Future<void> switchToAccount(TypedKey? accountMasterRecordKey) async {
final current = state.requireValue;
if (accountMasterRecordKey != null) {
// Assert the specified record key can be found, will throw if not
final _ = current.userLogins.firstWhere(
(ul) => ul.accountMasterRecordKey == accountMasterRecordKey);
}
final updated = current.copyWith(activeUserLogin: accountMasterRecordKey);
await store(updated);
state = AsyncValue.data(updated);
}
Future<bool> _decryptedLogin(
IdentityMaster identityMaster, SecretKey identitySecret) async {
final veilid = await eventualVeilid.future;
final cs =
await veilid.getCryptoSystem(identityMaster.identityRecordKey.kind);
final keyOk = await cs.validateKeyPair(
identityMaster.identityPublicKey, identitySecret);
if (!keyOk) {
throw Exception('Identity is corrupted');
}
// Read the identity key to get the account keys
final accountRecordInfo = await identityMaster.readAccountFromIdentity(
identitySecret: identitySecret, accountKey: veilidChatAccountKey);
// Add to user logins and select it
final current = state.requireValue;
final now = veilid.now();
final updated = current.copyWith(
userLogins: current.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 store(updated);
state = AsyncValue.data(updated);
return true;
}
Future<bool> login(TypedKey accountMasterRecordKey,
EncryptionKeyType encryptionKeyType, String encryptionKey) async {
final localAccounts = ref.read(localAccountsProvider).requireValue;
// Get account, throws if not found
final localAccount = localAccounts.firstWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
// Log in with this local account
// Derive key from password
if (localAccount.encryptionKeyType != encryptionKeyType) {
throw Exception('Wrong authentication type');
}
final identitySecret = await decryptSecretFromBytes(
secretBytes: localAccount.identitySecretBytes,
cryptoKind: localAccount.identityMaster.identityRecordKey.kind,
encryptionKeyType: localAccount.encryptionKeyType,
encryptionKey: encryptionKey,
);
// Validate this secret with the identity public key and log in
return _decryptedLogin(localAccount.identityMaster, identitySecret);
}
Future<void> logout(TypedKey? accountMasterRecordKey) async {
final current = state.requireValue;
final logoutUser = accountMasterRecordKey ?? current.activeUserLogin;
if (logoutUser == null) {
return;
}
final updated = current.copyWith(
activeUserLogin: current.activeUserLogin == logoutUser
? null
: current.activeUserLogin,
userLogins: current.userLogins
.removeWhere((ul) => ul.accountMasterRecordKey == logoutUser));
await store(updated);
state = AsyncValue.data(updated);
}
}
@riverpod
Future<UserLogin?> fetchLogin(FetchLoginRef ref,
{required TypedKey accountMasterRecordKey}) async {
final activeLogins = await ref.watch(loginsProvider.future);
try {
return activeLogins.userLogins
.firstWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey);
} on Exception catch (e) {
if (e is StateError) {
return null;
}
rethrow;
}
}

View File

@ -1,176 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'logins.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchLoginHash() => r'cfe13f5152f1275e6eccc698142abfd98170d9b9';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [fetchLogin].
@ProviderFor(fetchLogin)
const fetchLoginProvider = FetchLoginFamily();
/// See also [fetchLogin].
class FetchLoginFamily extends Family<AsyncValue<UserLogin?>> {
/// See also [fetchLogin].
const FetchLoginFamily();
/// See also [fetchLogin].
FetchLoginProvider call({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) {
return FetchLoginProvider(
accountMasterRecordKey: accountMasterRecordKey,
);
}
@override
FetchLoginProvider getProviderOverride(
covariant FetchLoginProvider provider,
) {
return call(
accountMasterRecordKey: provider.accountMasterRecordKey,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'fetchLoginProvider';
}
/// See also [fetchLogin].
class FetchLoginProvider extends AutoDisposeFutureProvider<UserLogin?> {
/// See also [fetchLogin].
FetchLoginProvider({
required Typed<FixedEncodedString43> accountMasterRecordKey,
}) : this._internal(
(ref) => fetchLogin(
ref as FetchLoginRef,
accountMasterRecordKey: accountMasterRecordKey,
),
from: fetchLoginProvider,
name: r'fetchLoginProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchLoginHash,
dependencies: FetchLoginFamily._dependencies,
allTransitiveDependencies:
FetchLoginFamily._allTransitiveDependencies,
accountMasterRecordKey: accountMasterRecordKey,
);
FetchLoginProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.accountMasterRecordKey,
}) : super.internal();
final Typed<FixedEncodedString43> accountMasterRecordKey;
@override
Override overrideWith(
FutureOr<UserLogin?> Function(FetchLoginRef provider) create,
) {
return ProviderOverride(
origin: this,
override: FetchLoginProvider._internal(
(ref) => create(ref as FetchLoginRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
accountMasterRecordKey: accountMasterRecordKey,
),
);
}
@override
AutoDisposeFutureProviderElement<UserLogin?> createElement() {
return _FetchLoginProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is FetchLoginProvider &&
other.accountMasterRecordKey == accountMasterRecordKey;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, accountMasterRecordKey.hashCode);
return _SystemHash.finish(hash);
}
}
mixin FetchLoginRef on AutoDisposeFutureProviderRef<UserLogin?> {
/// The parameter `accountMasterRecordKey` of this provider.
Typed<FixedEncodedString43> get accountMasterRecordKey;
}
class _FetchLoginProviderElement
extends AutoDisposeFutureProviderElement<UserLogin?> with FetchLoginRef {
_FetchLoginProviderElement(super.provider);
@override
Typed<FixedEncodedString43> get accountMasterRecordKey =>
(origin as FetchLoginProvider).accountMasterRecordKey;
}
String _$loginsHash() => r'2660f71bb7903464187a93fba5c07e22041e8c40';
/// See also [Logins].
@ProviderFor(Logins)
final loginsProvider =
AutoDisposeAsyncNotifierProvider<Logins, ActiveLogins>.internal(
Logins.new,
name: r'loginsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$loginsHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$Logins = AutoDisposeAsyncNotifier<ActiveLogins>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'window_control.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$windowControlHash() => r'c6afcbe1d4bfcfc580c30393aac60624c5ceabe0';
/// See also [WindowControl].
@ProviderFor(WindowControl)
final windowControlProvider =
AutoDisposeAsyncNotifierProvider<WindowControl, bool>.internal(
WindowControl.new,
name: r'windowControlProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$windowControlHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$WindowControl = AutoDisposeAsyncNotifier<bool>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -0,0 +1,152 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:go_router/go_router.dart';
import '../../init.dart';
import '../../local_account_manager/account_repository/account_repository.dart';
import '../../old_to_refactor/pages/chat_only.dart';
import '../../old_to_refactor/pages/developer.dart';
import '../../old_to_refactor/pages/home.dart';
import '../../old_to_refactor/pages/index.dart';
import '../../old_to_refactor/pages/new_account.dart';
import '../../old_to_refactor/pages/settings.dart';
import '../../tools/tools.dart';
part 'router_cubit.freezed.dart';
part 'router_cubit.g.dart';
part 'router_state.dart';
class RouterCubit extends Cubit<RouterState> {
RouterCubit(AccountRepository accountRepository)
: super(const RouterState(
isInitialized: false,
hasAnyAccount: false,
hasActiveChat: false,
)) {
// Watch for changes that the router will care about
Future.delayed(Duration.zero, () async {
await eventualInitialized.future;
emit(state.copyWith(isInitialized: true));
});
// Subscribe to repository streams
_accountRepositorySubscription =
accountRepository.changes().listen((event) {
switch (event) {
case AccountRepositoryChange.localAccounts:
emit(state.copyWith(
hasAnyAccount: accountRepository.getLocalAccounts().isNotEmpty));
break;
case AccountRepositoryChange.userLogins:
case AccountRepositoryChange.activeUserLogin:
break;
}
});
_chatListRepositorySubscription = ...
}
@override
Future<void> close() async {
await _accountRepositorySubscription.cancel();
await super.close();
}
/// Our application routes
List<GoRoute> get routes => [
GoRoute(
path: '/',
builder: (context, state) => const IndexPage(),
),
GoRoute(
path: '/home',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'settings',
builder: (context, state) => const SettingsPage(),
),
GoRoute(
path: 'chat',
builder: (context, state) => const ChatOnlyPage(),
),
],
),
GoRoute(
path: '/new_account',
builder: (context, state) => const NewAccountPage(),
routes: [
GoRoute(
path: 'settings',
builder: (context, state) => const SettingsPage(),
),
],
),
GoRoute(
path: '/developer',
builder: (context, state) => const DeveloperPage(),
)
];
/// Redirects when our state changes
String? redirect(BuildContext context, GoRouterState goRouterState) {
// if (state.isLoading || state.hasError) {
// return null;
// }
// No matter where we are, if there's not
switch (goRouterState.matchedLocation) {
case '/':
// Wait for veilid to be initialized
if (!eventualVeilid.isCompleted) {
return null;
}
return state.hasAnyAccount ? '/home' : '/new_account';
case '/new_account':
return state.hasAnyAccount ? '/home' : null;
case '/home':
if (!state.hasAnyAccount) {
return '/new_account';
}
if (responsiveVisibility(
context: context,
tablet: false,
tabletLandscape: false,
desktop: false)) {
if (state.hasActiveChat) {
return '/home/chat';
}
}
return null;
case '/home/chat':
if (!state.hasAnyAccount) {
return '/new_account';
}
if (responsiveVisibility(
context: context,
tablet: false,
tabletLandscape: false,
desktop: false)) {
if (!state.hasActiveChat) {
return '/home';
}
} else {
return '/home';
}
return null;
case '/home/settings':
case '/new_account/settings':
return null;
case '/developer':
return null;
default:
return state.hasAnyAccount ? null : '/new_account';
}
}
late final StreamSubscription<AccountRepositoryChange>
_accountRepositorySubscription;
}

View File

@ -0,0 +1,193 @@
// 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 'router_cubit.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#custom-getters-and-methods');
RouterState _$RouterStateFromJson(Map<String, dynamic> json) {
return _RouterState.fromJson(json);
}
/// @nodoc
mixin _$RouterState {
bool get isInitialized => throw _privateConstructorUsedError;
bool get hasAnyAccount => throw _privateConstructorUsedError;
bool get hasActiveChat => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$RouterStateCopyWith<RouterState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RouterStateCopyWith<$Res> {
factory $RouterStateCopyWith(
RouterState value, $Res Function(RouterState) then) =
_$RouterStateCopyWithImpl<$Res, RouterState>;
@useResult
$Res call({bool isInitialized, bool hasAnyAccount, bool hasActiveChat});
}
/// @nodoc
class _$RouterStateCopyWithImpl<$Res, $Val extends RouterState>
implements $RouterStateCopyWith<$Res> {
_$RouterStateCopyWithImpl(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? isInitialized = null,
Object? hasAnyAccount = null,
Object? hasActiveChat = null,
}) {
return _then(_value.copyWith(
isInitialized: null == isInitialized
? _value.isInitialized
: isInitialized // ignore: cast_nullable_to_non_nullable
as bool,
hasAnyAccount: null == hasAnyAccount
? _value.hasAnyAccount
: hasAnyAccount // ignore: cast_nullable_to_non_nullable
as bool,
hasActiveChat: null == hasActiveChat
? _value.hasActiveChat
: hasActiveChat // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
/// @nodoc
abstract class _$$RouterStateImplCopyWith<$Res>
implements $RouterStateCopyWith<$Res> {
factory _$$RouterStateImplCopyWith(
_$RouterStateImpl value, $Res Function(_$RouterStateImpl) then) =
__$$RouterStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool isInitialized, bool hasAnyAccount, bool hasActiveChat});
}
/// @nodoc
class __$$RouterStateImplCopyWithImpl<$Res>
extends _$RouterStateCopyWithImpl<$Res, _$RouterStateImpl>
implements _$$RouterStateImplCopyWith<$Res> {
__$$RouterStateImplCopyWithImpl(
_$RouterStateImpl _value, $Res Function(_$RouterStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isInitialized = null,
Object? hasAnyAccount = null,
Object? hasActiveChat = null,
}) {
return _then(_$RouterStateImpl(
isInitialized: null == isInitialized
? _value.isInitialized
: isInitialized // ignore: cast_nullable_to_non_nullable
as bool,
hasAnyAccount: null == hasAnyAccount
? _value.hasAnyAccount
: hasAnyAccount // ignore: cast_nullable_to_non_nullable
as bool,
hasActiveChat: null == hasActiveChat
? _value.hasActiveChat
: hasActiveChat // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RouterStateImpl implements _RouterState {
const _$RouterStateImpl(
{required this.isInitialized,
required this.hasAnyAccount,
required this.hasActiveChat});
factory _$RouterStateImpl.fromJson(Map<String, dynamic> json) =>
_$$RouterStateImplFromJson(json);
@override
final bool isInitialized;
@override
final bool hasAnyAccount;
@override
final bool hasActiveChat;
@override
String toString() {
return 'RouterState(isInitialized: $isInitialized, hasAnyAccount: $hasAnyAccount, hasActiveChat: $hasActiveChat)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RouterStateImpl &&
(identical(other.isInitialized, isInitialized) ||
other.isInitialized == isInitialized) &&
(identical(other.hasAnyAccount, hasAnyAccount) ||
other.hasAnyAccount == hasAnyAccount) &&
(identical(other.hasActiveChat, hasActiveChat) ||
other.hasActiveChat == hasActiveChat));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, isInitialized, hasAnyAccount, hasActiveChat);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$RouterStateImplCopyWith<_$RouterStateImpl> get copyWith =>
__$$RouterStateImplCopyWithImpl<_$RouterStateImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$RouterStateImplToJson(
this,
);
}
}
abstract class _RouterState implements RouterState {
const factory _RouterState(
{required final bool isInitialized,
required final bool hasAnyAccount,
required final bool hasActiveChat}) = _$RouterStateImpl;
factory _RouterState.fromJson(Map<String, dynamic> json) =
_$RouterStateImpl.fromJson;
@override
bool get isInitialized;
@override
bool get hasAnyAccount;
@override
bool get hasActiveChat;
@override
@JsonKey(ignore: true)
_$$RouterStateImplCopyWith<_$RouterStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'router_cubit.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$RouterStateImpl _$$RouterStateImplFromJson(Map<String, dynamic> json) =>
_$RouterStateImpl(
isInitialized: json['is_initialized'] as bool,
hasAnyAccount: json['has_any_account'] as bool,
hasActiveChat: json['has_active_chat'] as bool,
);
Map<String, dynamic> _$$RouterStateImplToJson(_$RouterStateImpl instance) =>
<String, dynamic>{
'is_initialized': instance.isInitialized,
'has_any_account': instance.hasAnyAccount,
'has_active_chat': instance.hasActiveChat,
};

View File

@ -0,0 +1,12 @@
part of 'router_cubit.dart';
@freezed
class RouterState with _$RouterState {
const factory RouterState(
{required bool isInitialized,
required bool hasAnyAccount,
required bool hasActiveChat}) = _RouterState;
factory RouterState.fromJson(dynamic json) =>
_$RouterStateFromJson(json as Map<String, dynamic>);
}

View File

@ -0,0 +1,20 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:stream_transform/stream_transform.dart';
import '../tools/stream_listenable.dart';
import 'cubit/router_cubit.dart';
final _key = GlobalKey<NavigatorState>(debugLabel: 'routerKey');
/// This simple provider caches our GoRouter.
GoRouter router({required RouterCubit routerCubit}) => GoRouter(
navigatorKey: _key,
refreshListenable: StreamListenable(
routerCubit.stream.startWith(routerCubit.state).distinct()),
debugLogDiagnostics: kDebugMode,
initialLocation: '/',
routes: routerCubit.routes,
redirect: routerCubit.redirect,
);

View File

@ -1,23 +1,2 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'router_notifier.dart';
part 'router.g.dart';
final _key = GlobalKey<NavigatorState>(debugLabel: 'routerKey');
/// This simple provider caches our GoRouter.
@riverpod
GoRouter router(RouterRef ref) {
final notifier = ref.watch(routerNotifierProvider.notifier);
return GoRouter(
navigatorKey: _key,
refreshListenable: notifier,
debugLogDiagnostics: true,
initialLocation: '/',
routes: notifier.routes,
redirect: notifier.redirect,
);
}
export 'cubit/router_cubit.dart';
export 'make_router.dart';

View File

@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'router.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$routerHash() => r'86eecb1955be62ef8e6f6efcec0fa615289cb823';
/// This simple provider caches our GoRouter.
///
/// Copied from [router].
@ProviderFor(router)
final routerProvider = AutoDisposeProvider<GoRouter>.internal(
router,
name: r'routerProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$routerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -1,160 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../pages/chat_only.dart';
import '../pages/developer.dart';
import '../pages/home.dart';
import '../pages/index.dart';
import '../pages/new_account.dart';
import '../pages/settings.dart';
import '../providers/chat.dart';
import '../providers/local_accounts.dart';
import '../tools/responsive.dart';
import '../veilid_init.dart';
part 'router_notifier.g.dart';
@riverpod
class RouterNotifier extends _$RouterNotifier implements Listenable {
/// GoRouter listener
VoidCallback? routerListener;
/// Do we need to make or import an account immediately?
bool hasAnyAccount = false;
bool hasActiveChat = false;
/// AsyncNotifier build
@override
Future<void> build() async {
hasAnyAccount = await ref.watch(
localAccountsProvider.selectAsync((data) => data.isNotEmpty),
);
hasActiveChat = ref.watch(activeChatStateProvider) != null;
// When this notifier's state changes, inform GoRouter
ref.listenSelf((_, __) {
if (state.isLoading) {
return;
}
routerListener?.call();
});
}
/// Redirects when our state changes
String? redirect(BuildContext context, GoRouterState state) {
if (this.state.isLoading || this.state.hasError) {
return null;
}
// No matter where we are, if there's not
switch (state.matchedLocation) {
case '/':
// Wait for veilid to be initialized
if (!eventualVeilid.isCompleted) {
return null;
}
return hasAnyAccount ? '/home' : '/new_account';
case '/new_account':
return hasAnyAccount ? '/home' : null;
case '/home':
if (!hasAnyAccount) {
return '/new_account';
}
if (responsiveVisibility(
context: context,
tablet: false,
tabletLandscape: false,
desktop: false)) {
if (hasActiveChat) {
return '/home/chat';
}
}
return null;
case '/home/chat':
if (!hasAnyAccount) {
return '/new_account';
}
if (responsiveVisibility(
context: context,
tablet: false,
tabletLandscape: false,
desktop: false)) {
if (!hasActiveChat) {
return '/home';
}
} else {
return '/home';
}
return null;
case '/home/settings':
case '/new_account/settings':
return null;
case '/developer':
return null;
default:
return hasAnyAccount ? null : '/new_account';
}
}
/// Our application routes
List<GoRoute> get routes => [
GoRoute(
path: '/',
builder: (context, state) => const IndexPage(),
),
GoRoute(
path: '/home',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'settings',
builder: (context, state) => const SettingsPage(),
),
GoRoute(
path: 'chat',
builder: (context, state) => const ChatOnlyPage(),
),
],
),
GoRoute(
path: '/new_account',
builder: (context, state) => const NewAccountPage(),
routes: [
GoRoute(
path: 'settings',
builder: (context, state) => const SettingsPage(),
),
],
),
GoRoute(
path: '/developer',
builder: (context, state) => const DeveloperPage(),
)
];
///////////////////////////////////////////////////////////////////////////
/// Listenable
/// Adds [GoRouter]'s listener as specified by its [Listenable].
/// [GoRouteInformationProvider] uses this method on creation to handle its
/// internal [ChangeNotifier].
/// Check out the internal implementation of [GoRouter] and
/// [GoRouteInformationProvider] to see this in action.
@override
void addListener(VoidCallback listener) {
routerListener = listener;
}
/// Removes [GoRouter]'s listener as specified by its [Listenable].
/// [GoRouteInformationProvider] uses this method when disposing,
/// so that it removes its callback when destroyed.
/// Check out the internal implementation of [GoRouter] and
/// [GoRouteInformationProvider] to see this in action.
@override
void removeListener(VoidCallback listener) {
routerListener = null;
}
}

View File

@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'router_notifier.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$routerNotifierHash() => r'6f52ed95f090f2d198d358e7526a91511c0a61e5';
/// See also [RouterNotifier].
@ProviderFor(RouterNotifier)
final routerNotifierProvider =
AutoDisposeAsyncNotifierProvider<RouterNotifier, void>.internal(
RouterNotifier.new,
name: r'routerNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$routerNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$RouterNotifier = AutoDisposeAsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:radix_colors/radix_colors.dart';
import 'theme_service.dart';
import 'scale_color.dart';
import 'scale_scheme.dart';
enum RadixThemeColor {
scarlet, // tomato + red + violet

View File

@ -0,0 +1,91 @@
import 'dart:ui';
class ScaleColor {
ScaleColor({
required this.appBackground,
required this.subtleBackground,
required this.elementBackground,
required this.hoverElementBackground,
required this.activeElementBackground,
required this.subtleBorder,
required this.border,
required this.hoverBorder,
required this.background,
required this.hoverBackground,
required this.subtleText,
required this.text,
});
Color appBackground;
Color subtleBackground;
Color elementBackground;
Color hoverElementBackground;
Color activeElementBackground;
Color subtleBorder;
Color border;
Color hoverBorder;
Color background;
Color hoverBackground;
Color subtleText;
Color text;
ScaleColor copyWith(
{Color? appBackground,
Color? subtleBackground,
Color? elementBackground,
Color? hoverElementBackground,
Color? activeElementBackground,
Color? subtleBorder,
Color? border,
Color? hoverBorder,
Color? background,
Color? hoverBackground,
Color? subtleText,
Color? text}) =>
ScaleColor(
appBackground: appBackground ?? this.appBackground,
subtleBackground: subtleBackground ?? this.subtleBackground,
elementBackground: elementBackground ?? this.elementBackground,
hoverElementBackground:
hoverElementBackground ?? this.hoverElementBackground,
activeElementBackground:
activeElementBackground ?? this.activeElementBackground,
subtleBorder: subtleBorder ?? this.subtleBorder,
border: border ?? this.border,
hoverBorder: hoverBorder ?? this.hoverBorder,
background: background ?? this.background,
hoverBackground: hoverBackground ?? this.hoverBackground,
subtleText: subtleText ?? this.subtleText,
text: text ?? this.text,
);
// ignore: prefer_constructors_over_static_methods
static ScaleColor lerp(ScaleColor a, ScaleColor b, double t) => ScaleColor(
appBackground: Color.lerp(a.appBackground, b.appBackground, t) ??
const Color(0x00000000),
subtleBackground:
Color.lerp(a.subtleBackground, b.subtleBackground, t) ??
const Color(0x00000000),
elementBackground:
Color.lerp(a.elementBackground, b.elementBackground, t) ??
const Color(0x00000000),
hoverElementBackground:
Color.lerp(a.hoverElementBackground, b.hoverElementBackground, t) ??
const Color(0x00000000),
activeElementBackground: Color.lerp(
a.activeElementBackground, b.activeElementBackground, t) ??
const Color(0x00000000),
subtleBorder: Color.lerp(a.subtleBorder, b.subtleBorder, t) ??
const Color(0x00000000),
border: Color.lerp(a.border, b.border, t) ?? const Color(0x00000000),
hoverBorder: Color.lerp(a.hoverBorder, b.hoverBorder, t) ??
const Color(0x00000000),
background: Color.lerp(a.background, b.background, t) ??
const Color(0x00000000),
hoverBackground: Color.lerp(a.hoverBackground, b.hoverBackground, t) ??
const Color(0x00000000),
subtleText: Color.lerp(a.subtleText, b.subtleText, t) ??
const Color(0x00000000),
text: Color.lerp(a.text, b.text, t) ?? const Color(0x00000000),
);
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'scale_color.dart';
class ScaleScheme extends ThemeExtension<ScaleScheme> {
ScaleScheme(
{required this.primaryScale,
required this.primaryAlphaScale,
required this.secondaryScale,
required this.tertiaryScale,
required this.grayScale,
required this.errorScale});
final ScaleColor primaryScale;
final ScaleColor primaryAlphaScale;
final ScaleColor secondaryScale;
final ScaleColor tertiaryScale;
final ScaleColor grayScale;
final ScaleColor errorScale;
@override
ScaleScheme copyWith(
{ScaleColor? primaryScale,
ScaleColor? primaryAlphaScale,
ScaleColor? secondaryScale,
ScaleColor? tertiaryScale,
ScaleColor? grayScale,
ScaleColor? errorScale}) =>
ScaleScheme(
primaryScale: primaryScale ?? this.primaryScale,
primaryAlphaScale: primaryAlphaScale ?? this.primaryAlphaScale,
secondaryScale: secondaryScale ?? this.secondaryScale,
tertiaryScale: tertiaryScale ?? this.tertiaryScale,
grayScale: grayScale ?? this.grayScale,
errorScale: errorScale ?? this.errorScale,
);
@override
ScaleScheme lerp(ScaleScheme? other, double t) {
if (other is! ScaleScheme) {
return this;
}
return ScaleScheme(
primaryScale: ScaleColor.lerp(primaryScale, other.primaryScale, t),
primaryAlphaScale:
ScaleColor.lerp(primaryAlphaScale, other.primaryAlphaScale, t),
secondaryScale: ScaleColor.lerp(secondaryScale, other.secondaryScale, t),
tertiaryScale: ScaleColor.lerp(tertiaryScale, other.tertiaryScale, t),
grayScale: ScaleColor.lerp(grayScale, other.grayScale, t),
errorScale: ScaleColor.lerp(errorScale, other.errorScale, t),
);
}
}

3
lib/theme/theme.dart Normal file
View File

@ -0,0 +1,3 @@
export 'scale_scheme.dart';
export 'theme_preference.dart';
export 'theme_repository.dart';

View File

@ -0,0 +1,52 @@
import 'package:change_case/change_case.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'theme_preference.freezed.dart';
part 'theme_preference.g.dart';
// Theme supports light and dark mode, optionally selected by the
// operating system
enum BrightnessPreference {
system,
light,
dark;
factory BrightnessPreference.fromJson(dynamic j) =>
BrightnessPreference.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
}
// Theme supports multiple color variants based on 'Radix'
enum ColorPreference {
// Radix Colors
scarlet,
babydoll,
vapor,
gold,
garden,
forest,
arctic,
lapis,
eggplant,
lime,
grim,
// Accessible Colors
contrast;
factory ColorPreference.fromJson(dynamic j) =>
ColorPreference.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
}
@freezed
class ThemePreferences with _$ThemePreferences {
const factory ThemePreferences({
required BrightnessPreference brightnessPreference,
required ColorPreference colorPreference,
required double displayScale,
}) = _ThemePreferences;
factory ThemePreferences.fromJson(dynamic json) =>
_$ThemePreferencesFromJson(json as Map<String, dynamic>);
}

View File

@ -0,0 +1,201 @@
// 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 'theme_preference.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#custom-getters-and-methods');
ThemePreferences _$ThemePreferencesFromJson(Map<String, dynamic> json) {
return _ThemePreferences.fromJson(json);
}
/// @nodoc
mixin _$ThemePreferences {
BrightnessPreference get brightnessPreference =>
throw _privateConstructorUsedError;
ColorPreference get colorPreference => throw _privateConstructorUsedError;
double get displayScale => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ThemePreferencesCopyWith<ThemePreferences> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ThemePreferencesCopyWith<$Res> {
factory $ThemePreferencesCopyWith(
ThemePreferences value, $Res Function(ThemePreferences) then) =
_$ThemePreferencesCopyWithImpl<$Res, ThemePreferences>;
@useResult
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
double displayScale});
}
/// @nodoc
class _$ThemePreferencesCopyWithImpl<$Res, $Val extends ThemePreferences>
implements $ThemePreferencesCopyWith<$Res> {
_$ThemePreferencesCopyWithImpl(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? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
}) {
return _then(_value.copyWith(
brightnessPreference: null == brightnessPreference
? _value.brightnessPreference
: brightnessPreference // ignore: cast_nullable_to_non_nullable
as BrightnessPreference,
colorPreference: null == colorPreference
? _value.colorPreference
: colorPreference // ignore: cast_nullable_to_non_nullable
as ColorPreference,
displayScale: null == displayScale
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
) as $Val);
}
}
/// @nodoc
abstract class _$$ThemePreferencesImplCopyWith<$Res>
implements $ThemePreferencesCopyWith<$Res> {
factory _$$ThemePreferencesImplCopyWith(_$ThemePreferencesImpl value,
$Res Function(_$ThemePreferencesImpl) then) =
__$$ThemePreferencesImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
double displayScale});
}
/// @nodoc
class __$$ThemePreferencesImplCopyWithImpl<$Res>
extends _$ThemePreferencesCopyWithImpl<$Res, _$ThemePreferencesImpl>
implements _$$ThemePreferencesImplCopyWith<$Res> {
__$$ThemePreferencesImplCopyWithImpl(_$ThemePreferencesImpl _value,
$Res Function(_$ThemePreferencesImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
}) {
return _then(_$ThemePreferencesImpl(
brightnessPreference: null == brightnessPreference
? _value.brightnessPreference
: brightnessPreference // ignore: cast_nullable_to_non_nullable
as BrightnessPreference,
colorPreference: null == colorPreference
? _value.colorPreference
: colorPreference // ignore: cast_nullable_to_non_nullable
as ColorPreference,
displayScale: null == displayScale
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ThemePreferencesImpl implements _ThemePreferences {
const _$ThemePreferencesImpl(
{required this.brightnessPreference,
required this.colorPreference,
required this.displayScale});
factory _$ThemePreferencesImpl.fromJson(Map<String, dynamic> json) =>
_$$ThemePreferencesImplFromJson(json);
@override
final BrightnessPreference brightnessPreference;
@override
final ColorPreference colorPreference;
@override
final double displayScale;
@override
String toString() {
return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ThemePreferencesImpl &&
(identical(other.brightnessPreference, brightnessPreference) ||
other.brightnessPreference == brightnessPreference) &&
(identical(other.colorPreference, colorPreference) ||
other.colorPreference == colorPreference) &&
(identical(other.displayScale, displayScale) ||
other.displayScale == displayScale));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, brightnessPreference, colorPreference, displayScale);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith =>
__$$ThemePreferencesImplCopyWithImpl<_$ThemePreferencesImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ThemePreferencesImplToJson(
this,
);
}
}
abstract class _ThemePreferences implements ThemePreferences {
const factory _ThemePreferences(
{required final BrightnessPreference brightnessPreference,
required final ColorPreference colorPreference,
required final double displayScale}) = _$ThemePreferencesImpl;
factory _ThemePreferences.fromJson(Map<String, dynamic> json) =
_$ThemePreferencesImpl.fromJson;
@override
BrightnessPreference get brightnessPreference;
@override
ColorPreference get colorPreference;
@override
double get displayScale;
@override
@JsonKey(ignore: true)
_$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'theme_preference.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ThemePreferencesImpl _$$ThemePreferencesImplFromJson(
Map<String, dynamic> json) =>
_$ThemePreferencesImpl(
brightnessPreference:
BrightnessPreference.fromJson(json['brightness_preference']),
colorPreference: ColorPreference.fromJson(json['color_preference']),
displayScale: (json['display_scale'] as num).toDouble(),
);
Map<String, dynamic> _$$ThemePreferencesImplToJson(
_$ThemePreferencesImpl instance) =>
<String, dynamic>{
'brightness_preference': instance.brightnessPreference.toJson(),
'color_preference': instance.colorPreference.toJson(),
'display_scale': instance.displayScale,
};

View File

@ -0,0 +1,132 @@
// ignore_for_file: always_put_required_named_parameters_first
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'radix_generator.dart';
import 'theme_preference.dart';
////////////////////////////////////////////////////////////////////////
class ThemeRepository {
ThemeRepository._({required SharedPreferences sharedPreferences})
: _sharedPreferences = sharedPreferences,
_themePreferences = defaultThemePreferences;
final SharedPreferences _sharedPreferences;
ThemePreferences _themePreferences;
ThemeData? _cachedThemeData;
/// Singleton instance of ThemeRepository
static ThemeRepository? _instance;
static Future<ThemeRepository> get instance async {
if (_instance == null) {
final sharedPreferences = await SharedPreferences.getInstance();
final instance = ThemeRepository._(sharedPreferences: sharedPreferences);
await instance.load();
_instance = instance;
}
return _instance!;
}
static bool get isPlatformDark =>
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark;
/// Defaults
static ThemePreferences get defaultThemePreferences => const ThemePreferences(
colorPreference: ColorPreference.vapor,
brightnessPreference: BrightnessPreference.system,
displayScale: 1,
);
/// Get theme preferences
ThemePreferences get themePreferences => _themePreferences;
/// Set theme preferences
void setThemePreferences(ThemePreferences themePreferences) {
_themePreferences = themePreferences;
_cachedThemeData = null;
}
/// Load theme preferences from storage
Future<void> load() async {
final themePreferencesJson =
_sharedPreferences.getString('themePreferences');
ThemePreferences? newThemePreferences;
if (themePreferencesJson != null) {
try {
newThemePreferences =
ThemePreferences.fromJson(jsonDecode(themePreferencesJson));
// ignore: avoid_catches_without_on_clauses
} catch (_) {
// ignore
}
}
setThemePreferences(newThemePreferences ?? defaultThemePreferences);
}
/// Save theme preferences to storage
Future<void> save() async {
await _sharedPreferences.setString(
'themePreferences', jsonEncode(_themePreferences.toJson()));
}
/// Get material 'ThemeData' for existinb
ThemeData themeData() {
final cachedThemeData = _cachedThemeData;
if (cachedThemeData != null) {
return cachedThemeData;
}
late final Brightness brightness;
switch (_themePreferences.brightnessPreference) {
case BrightnessPreference.system:
if (isPlatformDark) {
brightness = Brightness.dark;
} else {
brightness = Brightness.light;
}
case BrightnessPreference.light:
brightness = Brightness.light;
case BrightnessPreference.dark:
brightness = Brightness.dark;
}
late final ThemeData themeData;
switch (_themePreferences.colorPreference) {
// Special cases
case ColorPreference.contrast:
// xxx do contrastGenerator
themeData = radixGenerator(brightness, RadixThemeColor.grim);
// Generate from Radix
case ColorPreference.scarlet:
themeData = radixGenerator(brightness, RadixThemeColor.scarlet);
case ColorPreference.babydoll:
themeData = radixGenerator(brightness, RadixThemeColor.babydoll);
case ColorPreference.vapor:
themeData = radixGenerator(brightness, RadixThemeColor.vapor);
case ColorPreference.gold:
themeData = radixGenerator(brightness, RadixThemeColor.gold);
case ColorPreference.garden:
themeData = radixGenerator(brightness, RadixThemeColor.garden);
case ColorPreference.forest:
themeData = radixGenerator(brightness, RadixThemeColor.forest);
case ColorPreference.arctic:
themeData = radixGenerator(brightness, RadixThemeColor.arctic);
case ColorPreference.lapis:
themeData = radixGenerator(brightness, RadixThemeColor.lapis);
case ColorPreference.eggplant:
themeData = radixGenerator(brightness, RadixThemeColor.eggplant);
case ColorPreference.lime:
themeData = radixGenerator(brightness, RadixThemeColor.lime);
case ColorPreference.grim:
themeData = radixGenerator(brightness, RadixThemeColor.grim);
}
_cachedThemeData = themeData;
return themeData;
}
}

Some files were not shown because too many files have changed in this diff Show More