mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
home layout
This commit is contained in:
parent
f754f7d5ed
commit
ca6b00e021
@ -5,6 +5,11 @@
|
||||
"app_bar": {
|
||||
"settings_tooltip": "Settings"
|
||||
},
|
||||
"pager": {
|
||||
"account": "Account",
|
||||
"chats": "Chats",
|
||||
"contacts": "Contacts"
|
||||
},
|
||||
"account": {
|
||||
"form_name": "Name",
|
||||
"form_title": "Title (optional)",
|
||||
@ -22,25 +27,26 @@
|
||||
},
|
||||
"button": {
|
||||
"ok": "Ok",
|
||||
"cancel": "Cancel",
|
||||
"change_language": "Change Language"
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"language": {
|
||||
"name": {
|
||||
"en": "English"
|
||||
},
|
||||
"change_language": "Change Language",
|
||||
"selected_message": "Currently selected language is {language}",
|
||||
"selection": {
|
||||
"message": "Please select a language from the list",
|
||||
"title": "Language Selection"
|
||||
}
|
||||
},
|
||||
"plural": {
|
||||
"demo": {
|
||||
"zero": "Please start pushing the 'plus' button.",
|
||||
"one": "You have pushed the button one time.",
|
||||
"two": "You have pushed the button two times.",
|
||||
"other": "You have pushed the button {{value}} times."
|
||||
}
|
||||
"account_page": {
|
||||
"missing_account_title": "Missing Account",
|
||||
"missing_account_text": "Account is missing, removing from list",
|
||||
"invalid_account_title": "Missing Account",
|
||||
"invalid_account_text": "Account is missing, removing from list"
|
||||
},
|
||||
"themes": {
|
||||
"vapor": "Vapor"
|
||||
}
|
||||
}
|
@ -5,21 +5,45 @@ class Chat extends ConsumerWidget {
|
||||
const Chat({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => Scaffold(
|
||||
appBar: AppBar(title: const Text('Chat')),
|
||||
body: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Text('Home Page'),
|
||||
// ElevatedButton(
|
||||
// onPressed: () {
|
||||
// ref.watch(authNotifierProvider.notifier).logout();
|
||||
// },
|
||||
// child: const Text("Logout"),
|
||||
// ),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0),
|
||||
child: Text("current contact",
|
||||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(),
|
||||
child: Text("Chat"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
25
lib/components/chat_list.dart
Normal file
25
lib/components/chat_list.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ChatList extends ConsumerWidget {
|
||||
const ChatList({super.key});
|
||||
//final LocalAccount account;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//final logins = ref.watch(loginsProvider);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 300),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [const Expanded(child: Text('Chat List'))]));
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
//properties.add(DiagnosticsProperty<LocalAccount>('account', account));
|
||||
}
|
||||
}
|
36
lib/components/empty_chat_component_widget.dart
Normal file
36
lib/components/empty_chat_component_widget.dart
Normal file
@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class EmptyChatComponentWidget extends ConsumerWidget {
|
||||
const EmptyChatComponentWidget({super.key});
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.chat,
|
||||
color: Theme.of(context).disabledColor,
|
||||
size: 48,
|
||||
),
|
||||
Text(
|
||||
'Say Something',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
35
lib/components/empty_contact_list_component_widget.dart
Normal file
35
lib/components/empty_contact_list_component_widget.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class EmptyContactListComponentWidget extends ConsumerWidget {
|
||||
const EmptyContactListComponentWidget({super.key});
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.group_add,
|
||||
color: Theme.of(context).disabledColor,
|
||||
size: 48,
|
||||
),
|
||||
Text(
|
||||
'Start A Conversation',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
36
lib/components/no_conversation_component_widget.dart
Normal file
36
lib/components/no_conversation_component_widget.dart
Normal file
@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class NoContactComponentWidget extends ConsumerWidget {
|
||||
const NoContactComponentWidget({super.key});
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
//
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.emoji_people_outlined,
|
||||
color: Theme.of(context).disabledColor,
|
||||
size: 48,
|
||||
),
|
||||
Text(
|
||||
'Choose A Conversation To Chat',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
37
lib/components/profile.dart
Normal file
37
lib/components/profile.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ProfileWidget extends ConsumerWidget {
|
||||
const ProfileWidget({
|
||||
required this.name,
|
||||
this.title,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// final logins = ref.watch(loginsProvider);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 300),
|
||||
child: Column(children: [
|
||||
Text('Profile', style: Theme.of(context).textTheme.headlineMedium),
|
||||
Text(name, style: Theme.of(context).textTheme.bodyMedium),
|
||||
if (title != null && title!.isNotEmpty)
|
||||
Text(title!, style: Theme.of(context).textTheme.bodySmall),
|
||||
]));
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(StringProperty('name', name))
|
||||
..add(StringProperty('title', title));
|
||||
}
|
||||
}
|
@ -48,6 +48,8 @@ class LocalAccount with _$LocalAccount {
|
||||
// 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) =>
|
||||
|
@ -34,7 +34,9 @@ mixin _$LocalAccount {
|
||||
bool get biometricsEnabled =>
|
||||
throw _privateConstructorUsedError; // Keep account hidden unless account password is entered
|
||||
// (tries all hidden accounts with auth method (no biometrics))
|
||||
bool get hiddenAccount => throw _privateConstructorUsedError;
|
||||
bool get hiddenAccount =>
|
||||
throw _privateConstructorUsedError; // Display name for account until it is unlocked
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
@ -54,7 +56,8 @@ abstract class $LocalAccountCopyWith<$Res> {
|
||||
@Uint8ListJsonConverter() Uint8List identitySecretSaltBytes,
|
||||
EncryptionKeyType encryptionKeyType,
|
||||
bool biometricsEnabled,
|
||||
bool hiddenAccount});
|
||||
bool hiddenAccount,
|
||||
String name});
|
||||
|
||||
$IdentityMasterCopyWith<$Res> get identityMaster;
|
||||
}
|
||||
@ -78,6 +81,7 @@ class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount>
|
||||
Object? encryptionKeyType = null,
|
||||
Object? biometricsEnabled = null,
|
||||
Object? hiddenAccount = null,
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
identityMaster: null == identityMaster
|
||||
@ -104,6 +108,10 @@ class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount>
|
||||
? _value.hiddenAccount
|
||||
: hiddenAccount // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@ -130,7 +138,8 @@ abstract class _$$_LocalAccountCopyWith<$Res>
|
||||
@Uint8ListJsonConverter() Uint8List identitySecretSaltBytes,
|
||||
EncryptionKeyType encryptionKeyType,
|
||||
bool biometricsEnabled,
|
||||
bool hiddenAccount});
|
||||
bool hiddenAccount,
|
||||
String name});
|
||||
|
||||
@override
|
||||
$IdentityMasterCopyWith<$Res> get identityMaster;
|
||||
@ -153,6 +162,7 @@ class __$$_LocalAccountCopyWithImpl<$Res>
|
||||
Object? encryptionKeyType = null,
|
||||
Object? biometricsEnabled = null,
|
||||
Object? hiddenAccount = null,
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_$_LocalAccount(
|
||||
identityMaster: null == identityMaster
|
||||
@ -179,6 +189,10 @@ class __$$_LocalAccountCopyWithImpl<$Res>
|
||||
? _value.hiddenAccount
|
||||
: hiddenAccount // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -192,7 +206,8 @@ class _$_LocalAccount implements _LocalAccount {
|
||||
@Uint8ListJsonConverter() required this.identitySecretSaltBytes,
|
||||
required this.encryptionKeyType,
|
||||
required this.biometricsEnabled,
|
||||
required this.hiddenAccount});
|
||||
required this.hiddenAccount,
|
||||
required this.name});
|
||||
|
||||
factory _$_LocalAccount.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_LocalAccountFromJson(json);
|
||||
@ -218,10 +233,13 @@ class _$_LocalAccount implements _LocalAccount {
|
||||
// (tries all hidden accounts with auth method (no biometrics))
|
||||
@override
|
||||
final bool hiddenAccount;
|
||||
// Display name for account until it is unlocked
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LocalAccount(identityMaster: $identityMaster, identitySecretKeyBytes: $identitySecretKeyBytes, identitySecretSaltBytes: $identitySecretSaltBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount)';
|
||||
return 'LocalAccount(identityMaster: $identityMaster, identitySecretKeyBytes: $identitySecretKeyBytes, identitySecretSaltBytes: $identitySecretSaltBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -240,7 +258,8 @@ class _$_LocalAccount implements _LocalAccount {
|
||||
(identical(other.biometricsEnabled, biometricsEnabled) ||
|
||||
other.biometricsEnabled == biometricsEnabled) &&
|
||||
(identical(other.hiddenAccount, hiddenAccount) ||
|
||||
other.hiddenAccount == hiddenAccount));
|
||||
other.hiddenAccount == hiddenAccount) &&
|
||||
(identical(other.name, name) || other.name == name));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@ -252,7 +271,8 @@ class _$_LocalAccount implements _LocalAccount {
|
||||
const DeepCollectionEquality().hash(identitySecretSaltBytes),
|
||||
encryptionKeyType,
|
||||
biometricsEnabled,
|
||||
hiddenAccount);
|
||||
hiddenAccount,
|
||||
name);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -276,7 +296,8 @@ abstract class _LocalAccount implements LocalAccount {
|
||||
required final Uint8List identitySecretSaltBytes,
|
||||
required final EncryptionKeyType encryptionKeyType,
|
||||
required final bool biometricsEnabled,
|
||||
required final bool hiddenAccount}) = _$_LocalAccount;
|
||||
required final bool hiddenAccount,
|
||||
required final String name}) = _$_LocalAccount;
|
||||
|
||||
factory _LocalAccount.fromJson(Map<String, dynamic> json) =
|
||||
_$_LocalAccount.fromJson;
|
||||
@ -296,6 +317,8 @@ abstract class _LocalAccount implements LocalAccount {
|
||||
@override // Keep account hidden unless account password is entered
|
||||
// (tries all hidden accounts with auth method (no biometrics))
|
||||
bool get hiddenAccount;
|
||||
@override // Display name for account until it is unlocked
|
||||
String get name;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_LocalAccountCopyWith<_$_LocalAccount> get copyWith =>
|
||||
|
@ -17,6 +17,7 @@ _$_LocalAccount _$$_LocalAccountFromJson(Map<String, dynamic> json) =>
|
||||
EncryptionKeyType.fromJson(json['encryption_key_type']),
|
||||
biometricsEnabled: json['biometrics_enabled'] as bool,
|
||||
hiddenAccount: json['hidden_account'] as bool,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_LocalAccountToJson(_$_LocalAccount instance) =>
|
||||
@ -29,4 +30,5 @@ Map<String, dynamic> _$$_LocalAccountToJson(_$_LocalAccount instance) =>
|
||||
'encryption_key_type': instance.encryptionKeyType.toJson(),
|
||||
'biometrics_enabled': instance.biometricsEnabled,
|
||||
'hidden_account': instance.hiddenAccount,
|
||||
'name': instance.name,
|
||||
};
|
||||
|
@ -6,13 +6,13 @@ part 'preferences.g.dart';
|
||||
|
||||
// Theme supports light and dark mode, optionally selected by the
|
||||
// operating system
|
||||
enum DarkModePreference {
|
||||
enum BrightnessPreference {
|
||||
system,
|
||||
light,
|
||||
dark;
|
||||
|
||||
factory DarkModePreference.fromJson(dynamic j) =>
|
||||
DarkModePreference.values.byName((j as String).toCamelCase());
|
||||
factory BrightnessPreference.fromJson(dynamic j) =>
|
||||
BrightnessPreference.values.byName((j as String).toCamelCase());
|
||||
|
||||
String toJson() => name.toPascalCase();
|
||||
}
|
||||
@ -33,34 +33,20 @@ class LockPreference with _$LockPreference {
|
||||
|
||||
// Theme supports multiple color variants based on 'Radix'
|
||||
enum ColorPreference {
|
||||
amber,
|
||||
blue,
|
||||
bronze,
|
||||
brown,
|
||||
crimson,
|
||||
cyan,
|
||||
// Radix Colors
|
||||
scarlet,
|
||||
babydoll,
|
||||
vapor,
|
||||
gold,
|
||||
grass,
|
||||
gray,
|
||||
green,
|
||||
indigo,
|
||||
garden,
|
||||
forest,
|
||||
arctic,
|
||||
lapis,
|
||||
eggplant,
|
||||
lime,
|
||||
mauve,
|
||||
mint,
|
||||
olive,
|
||||
orange,
|
||||
pink,
|
||||
plum,
|
||||
purple,
|
||||
red,
|
||||
sage,
|
||||
sand,
|
||||
sky,
|
||||
slate,
|
||||
teal,
|
||||
tomato,
|
||||
violet,
|
||||
yellow;
|
||||
grim,
|
||||
// Accessible Colors
|
||||
contrast;
|
||||
|
||||
factory ColorPreference.fromJson(dynamic j) =>
|
||||
ColorPreference.values.byName((j as String).toCamelCase());
|
||||
@ -76,15 +62,25 @@ 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
|
||||
class Preferences with _$Preferences {
|
||||
const factory Preferences({
|
||||
required DarkModePreference darkMode,
|
||||
required ColorPreference themeColor,
|
||||
required ThemePreferences themePreferences,
|
||||
required LanguagePreference language,
|
||||
required int displayScale,
|
||||
required LockPreference locking,
|
||||
}) = _Preferences;
|
||||
|
||||
|
@ -198,16 +198,199 @@ 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 _$$_ThemePreferencesCopyWith<$Res>
|
||||
implements $ThemePreferencesCopyWith<$Res> {
|
||||
factory _$$_ThemePreferencesCopyWith(
|
||||
_$_ThemePreferences value, $Res Function(_$_ThemePreferences) then) =
|
||||
__$$_ThemePreferencesCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{BrightnessPreference brightnessPreference,
|
||||
ColorPreference colorPreference,
|
||||
double displayScale});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_ThemePreferencesCopyWithImpl<$Res>
|
||||
extends _$ThemePreferencesCopyWithImpl<$Res, _$_ThemePreferences>
|
||||
implements _$$_ThemePreferencesCopyWith<$Res> {
|
||||
__$$_ThemePreferencesCopyWithImpl(
|
||||
_$_ThemePreferences _value, $Res Function(_$_ThemePreferences) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? brightnessPreference = null,
|
||||
Object? colorPreference = null,
|
||||
Object? displayScale = null,
|
||||
}) {
|
||||
return _then(_$_ThemePreferences(
|
||||
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 _$_ThemePreferences implements _ThemePreferences {
|
||||
const _$_ThemePreferences(
|
||||
{required this.brightnessPreference,
|
||||
required this.colorPreference,
|
||||
required this.displayScale});
|
||||
|
||||
factory _$_ThemePreferences.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_ThemePreferencesFromJson(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 ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_ThemePreferences &&
|
||||
(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')
|
||||
_$$_ThemePreferencesCopyWith<_$_ThemePreferences> get copyWith =>
|
||||
__$$_ThemePreferencesCopyWithImpl<_$_ThemePreferences>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_ThemePreferencesToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ThemePreferences implements ThemePreferences {
|
||||
const factory _ThemePreferences(
|
||||
{required final BrightnessPreference brightnessPreference,
|
||||
required final ColorPreference colorPreference,
|
||||
required final double displayScale}) = _$_ThemePreferences;
|
||||
|
||||
factory _ThemePreferences.fromJson(Map<String, dynamic> json) =
|
||||
_$_ThemePreferences.fromJson;
|
||||
|
||||
@override
|
||||
BrightnessPreference get brightnessPreference;
|
||||
@override
|
||||
ColorPreference get colorPreference;
|
||||
@override
|
||||
double get displayScale;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_ThemePreferencesCopyWith<_$_ThemePreferences> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Preferences _$PreferencesFromJson(Map<String, dynamic> json) {
|
||||
return _Preferences.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Preferences {
|
||||
DarkModePreference get darkMode => throw _privateConstructorUsedError;
|
||||
ColorPreference get themeColor => throw _privateConstructorUsedError;
|
||||
ThemePreferences get themePreferences => throw _privateConstructorUsedError;
|
||||
LanguagePreference get language => throw _privateConstructorUsedError;
|
||||
int get displayScale => throw _privateConstructorUsedError;
|
||||
LockPreference get locking => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@ -223,12 +406,11 @@ abstract class $PreferencesCopyWith<$Res> {
|
||||
_$PreferencesCopyWithImpl<$Res, Preferences>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{DarkModePreference darkMode,
|
||||
ColorPreference themeColor,
|
||||
{ThemePreferences themePreferences,
|
||||
LanguagePreference language,
|
||||
int displayScale,
|
||||
LockPreference locking});
|
||||
|
||||
$ThemePreferencesCopyWith<$Res> get themePreferences;
|
||||
$LockPreferenceCopyWith<$Res> get locking;
|
||||
}
|
||||
|
||||
@ -245,29 +427,19 @@ class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? darkMode = null,
|
||||
Object? themeColor = null,
|
||||
Object? themePreferences = null,
|
||||
Object? language = null,
|
||||
Object? displayScale = null,
|
||||
Object? locking = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
darkMode: null == darkMode
|
||||
? _value.darkMode
|
||||
: darkMode // ignore: cast_nullable_to_non_nullable
|
||||
as DarkModePreference,
|
||||
themeColor: null == themeColor
|
||||
? _value.themeColor
|
||||
: themeColor // ignore: cast_nullable_to_non_nullable
|
||||
as ColorPreference,
|
||||
themePreferences: null == themePreferences
|
||||
? _value.themePreferences
|
||||
: themePreferences // ignore: cast_nullable_to_non_nullable
|
||||
as ThemePreferences,
|
||||
language: null == language
|
||||
? _value.language
|
||||
: language // ignore: cast_nullable_to_non_nullable
|
||||
as LanguagePreference,
|
||||
displayScale: null == displayScale
|
||||
? _value.displayScale
|
||||
: displayScale // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
locking: null == locking
|
||||
? _value.locking
|
||||
: locking // ignore: cast_nullable_to_non_nullable
|
||||
@ -275,6 +447,14 @@ 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 {
|
||||
@ -293,12 +473,12 @@ abstract class _$$_PreferencesCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{DarkModePreference darkMode,
|
||||
ColorPreference themeColor,
|
||||
{ThemePreferences themePreferences,
|
||||
LanguagePreference language,
|
||||
int displayScale,
|
||||
LockPreference locking});
|
||||
|
||||
@override
|
||||
$ThemePreferencesCopyWith<$Res> get themePreferences;
|
||||
@override
|
||||
$LockPreferenceCopyWith<$Res> get locking;
|
||||
}
|
||||
@ -314,29 +494,19 @@ class __$$_PreferencesCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? darkMode = null,
|
||||
Object? themeColor = null,
|
||||
Object? themePreferences = null,
|
||||
Object? language = null,
|
||||
Object? displayScale = null,
|
||||
Object? locking = null,
|
||||
}) {
|
||||
return _then(_$_Preferences(
|
||||
darkMode: null == darkMode
|
||||
? _value.darkMode
|
||||
: darkMode // ignore: cast_nullable_to_non_nullable
|
||||
as DarkModePreference,
|
||||
themeColor: null == themeColor
|
||||
? _value.themeColor
|
||||
: themeColor // ignore: cast_nullable_to_non_nullable
|
||||
as ColorPreference,
|
||||
themePreferences: null == themePreferences
|
||||
? _value.themePreferences
|
||||
: themePreferences // ignore: cast_nullable_to_non_nullable
|
||||
as ThemePreferences,
|
||||
language: null == language
|
||||
? _value.language
|
||||
: language // ignore: cast_nullable_to_non_nullable
|
||||
as LanguagePreference,
|
||||
displayScale: null == displayScale
|
||||
? _value.displayScale
|
||||
: displayScale // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
locking: null == locking
|
||||
? _value.locking
|
||||
: locking // ignore: cast_nullable_to_non_nullable
|
||||
@ -349,29 +519,23 @@ class __$$_PreferencesCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$_Preferences implements _Preferences {
|
||||
const _$_Preferences(
|
||||
{required this.darkMode,
|
||||
required this.themeColor,
|
||||
{required this.themePreferences,
|
||||
required this.language,
|
||||
required this.displayScale,
|
||||
required this.locking});
|
||||
|
||||
factory _$_Preferences.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_PreferencesFromJson(json);
|
||||
|
||||
@override
|
||||
final DarkModePreference darkMode;
|
||||
@override
|
||||
final ColorPreference themeColor;
|
||||
final ThemePreferences themePreferences;
|
||||
@override
|
||||
final LanguagePreference language;
|
||||
@override
|
||||
final int displayScale;
|
||||
@override
|
||||
final LockPreference locking;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Preferences(darkMode: $darkMode, themeColor: $themeColor, language: $language, displayScale: $displayScale, locking: $locking)';
|
||||
return 'Preferences(themePreferences: $themePreferences, language: $language, locking: $locking)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -379,21 +543,17 @@ class _$_Preferences implements _Preferences {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_Preferences &&
|
||||
(identical(other.darkMode, darkMode) ||
|
||||
other.darkMode == darkMode) &&
|
||||
(identical(other.themeColor, themeColor) ||
|
||||
other.themeColor == themeColor) &&
|
||||
(identical(other.themePreferences, themePreferences) ||
|
||||
other.themePreferences == themePreferences) &&
|
||||
(identical(other.language, language) ||
|
||||
other.language == language) &&
|
||||
(identical(other.displayScale, displayScale) ||
|
||||
other.displayScale == displayScale) &&
|
||||
(identical(other.locking, locking) || other.locking == locking));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, darkMode, themeColor, language, displayScale, locking);
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, themePreferences, language, locking);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -411,24 +571,18 @@ class _$_Preferences implements _Preferences {
|
||||
|
||||
abstract class _Preferences implements Preferences {
|
||||
const factory _Preferences(
|
||||
{required final DarkModePreference darkMode,
|
||||
required final ColorPreference themeColor,
|
||||
{required final ThemePreferences themePreferences,
|
||||
required final LanguagePreference language,
|
||||
required final int displayScale,
|
||||
required final LockPreference locking}) = _$_Preferences;
|
||||
|
||||
factory _Preferences.fromJson(Map<String, dynamic> json) =
|
||||
_$_Preferences.fromJson;
|
||||
|
||||
@override
|
||||
DarkModePreference get darkMode;
|
||||
@override
|
||||
ColorPreference get themeColor;
|
||||
ThemePreferences get themePreferences;
|
||||
@override
|
||||
LanguagePreference get language;
|
||||
@override
|
||||
int get displayScale;
|
||||
@override
|
||||
LockPreference get locking;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
|
@ -20,20 +20,31 @@ Map<String, dynamic> _$$_LockPreferenceToJson(_$_LockPreference instance) =>
|
||||
'lock_with_system_lock': instance.lockWithSystemLock,
|
||||
};
|
||||
|
||||
_$_ThemePreferences _$$_ThemePreferencesFromJson(Map<String, dynamic> json) =>
|
||||
_$_ThemePreferences(
|
||||
brightnessPreference:
|
||||
BrightnessPreference.fromJson(json['brightness_preference']),
|
||||
colorPreference: ColorPreference.fromJson(json['color_preference']),
|
||||
displayScale: (json['display_scale'] as num).toDouble(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_ThemePreferencesToJson(_$_ThemePreferences instance) =>
|
||||
<String, dynamic>{
|
||||
'brightness_preference': instance.brightnessPreference.toJson(),
|
||||
'color_preference': instance.colorPreference.toJson(),
|
||||
'display_scale': instance.displayScale,
|
||||
};
|
||||
|
||||
_$_Preferences _$$_PreferencesFromJson(Map<String, dynamic> json) =>
|
||||
_$_Preferences(
|
||||
darkMode: DarkModePreference.fromJson(json['dark_mode']),
|
||||
themeColor: ColorPreference.fromJson(json['theme_color']),
|
||||
themePreferences: ThemePreferences.fromJson(json['theme_preferences']),
|
||||
language: LanguagePreference.fromJson(json['language']),
|
||||
displayScale: json['display_scale'] as int,
|
||||
locking: LockPreference.fromJson(json['locking']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_PreferencesToJson(_$_Preferences instance) =>
|
||||
<String, dynamic>{
|
||||
'dark_mode': instance.darkMode.toJson(),
|
||||
'theme_color': instance.themeColor.toJson(),
|
||||
'theme_preferences': instance.themePreferences.toJson(),
|
||||
'language': instance.language.toJson(),
|
||||
'display_scale': instance.displayScale,
|
||||
'locking': instance.locking.toJson(),
|
||||
};
|
||||
|
@ -6,10 +6,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
|
||||
import '../tools/desktop_control.dart';
|
||||
import 'app.dart';
|
||||
import 'log/log.dart';
|
||||
import 'theming/theming.dart';
|
||||
import 'providers/window_control.dart';
|
||||
import 'tools/theme_service.dart';
|
||||
import 'veilid_support/veilid_support.dart';
|
||||
|
||||
void main() async {
|
||||
@ -32,7 +32,7 @@ void main() async {
|
||||
final initTheme = themeService.initial;
|
||||
|
||||
// Manage window on desktop platforms
|
||||
await setupDesktopWindow();
|
||||
await WindowControl.initialize();
|
||||
|
||||
// Make localization delegate
|
||||
final delegate = await LocalizationDelegate.create(
|
||||
|
@ -1,10 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:multi_split_view/multi_split_view.dart';
|
||||
import 'package:signal_strength_indicator/signal_strength_indicator.dart';
|
||||
|
||||
import '../components/chat.dart';
|
||||
import '../components/chat_list.dart';
|
||||
import '../providers/window_control.dart';
|
||||
import '../tools/tools.dart';
|
||||
import 'main_pager/main_pager.dart';
|
||||
|
||||
class HomePage extends ConsumerStatefulWidget {
|
||||
const HomePage({super.key});
|
||||
@ -17,6 +21,9 @@ class HomePage extends ConsumerStatefulWidget {
|
||||
class HomePageState extends ConsumerState<HomePage>
|
||||
with TickerProviderStateMixin {
|
||||
final _unfocusNode = FocusNode();
|
||||
|
||||
final MultiSplitViewController _splitController = MultiSplitViewController(
|
||||
areas: [Area(minimalSize: 300, weight: 0.25), Area(minimalSize: 300)]);
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
bool hasContainerTriggered = false;
|
||||
final animationsMap = {
|
||||
@ -38,12 +45,6 @@ class HomePageState extends ConsumerState<HomePage>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// // On page load action.
|
||||
// SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||
// await actions.initialize(
|
||||
// context,
|
||||
// );
|
||||
// });
|
||||
|
||||
setupAnimations(
|
||||
animationsMap.values.where((anim) =>
|
||||
@ -52,7 +53,11 @@ class HomePageState extends ConsumerState<HomePage>
|
||||
this,
|
||||
);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
setState(() {});
|
||||
await ref.read(windowControlProvider.notifier).changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.normal);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -61,250 +66,66 @@ class HomePageState extends ConsumerState<HomePage>
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget buildPhone(BuildContext context) {
|
||||
//
|
||||
return Material(
|
||||
color: Colors.transparent, elevation: 4, child: MainPager());
|
||||
}
|
||||
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget buildTabletLeftPane(BuildContext context) {
|
||||
//
|
||||
return Material(
|
||||
color: Colors.transparent, elevation: 4, child: MainPager());
|
||||
}
|
||||
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget buildTabletRightPane(BuildContext context) {
|
||||
//
|
||||
return Chat();
|
||||
}
|
||||
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget buildTablet(BuildContext context) {
|
||||
final children = [
|
||||
buildTabletLeftPane(context),
|
||||
buildTabletRightPane(context),
|
||||
];
|
||||
|
||||
final multiSplitView = MultiSplitView(
|
||||
// onWeightChange: _onWeightChange,
|
||||
// onDividerTap: _onDividerTap,
|
||||
// onDividerDoubleTap: _onDividerDoubleTap,
|
||||
controller: _splitController,
|
||||
children: children);
|
||||
|
||||
final theme = MultiSplitViewTheme(
|
||||
data: isDesktop
|
||||
? MultiSplitViewThemeData(
|
||||
dividerThickness: 1,
|
||||
dividerPainter: DividerPainters.grooved2(thickness: 1))
|
||||
: MultiSplitViewThemeData(
|
||||
dividerThickness: 3,
|
||||
dividerPainter: DividerPainters.grooved2(thickness: 1)),
|
||||
child: multiSplitView);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
key: scaffoldKey,
|
||||
appBar: AppBar(title: const Text('VeilidChat')),
|
||||
body: SafeArea(
|
||||
Widget build(BuildContext context) {
|
||||
ref.watch(windowControlProvider);
|
||||
|
||||
return SafeArea(
|
||||
child: GestureDetector(
|
||||
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (responsiveVisibility(
|
||||
child: responsiveVisibility(
|
||||
context: context,
|
||||
phone: false,
|
||||
))
|
||||
Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.66,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 0, 16, 0),
|
||||
child: Text(
|
||||
"current contact",
|
||||
// getJsonField(
|
||||
// FFAppState().CurrentContact,
|
||||
// r'''$.name''',
|
||||
// ).toString(),
|
||||
textAlign: TextAlign.start,
|
||||
// style: Theme.of(context)
|
||||
// .textTheme....
|
||||
// .override(
|
||||
// fontFamily: 'Noto Sans',
|
||||
// color: FlutterFlowTheme.of(context)
|
||||
// .header,
|
||||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 100,
|
||||
decoration: const BoxDecoration(),
|
||||
child: ChatComponentWidget(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// if (FFAppState().CurrentContact == null)
|
||||
// Container(
|
||||
// width: double.infinity,
|
||||
// height: double.infinity,
|
||||
// decoration: const BoxDecoration(),
|
||||
// child: NoContactComponentWidget(),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
).animateOnActionTrigger(
|
||||
animationsMap['containerOnActionTriggerAnimation']!,
|
||||
hasBeenTriggered: hasContainerTriggered),
|
||||
),
|
||||
if (responsiveVisibility(
|
||||
context: context,
|
||||
phone: false,
|
||||
))
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
elevation: 4,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.34,
|
||||
height: double.infinity,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.34,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 8, 16, 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Text(
|
||||
'Contacts',
|
||||
textAlign: TextAlign.start,
|
||||
// style: Theme.of(context).dialogTheme.titleTextStyle
|
||||
// .title2
|
||||
// .override(
|
||||
// fontFamily: 'Noto Sans',
|
||||
// color: FlutterFlowTheme.of(context)
|
||||
// .header,
|
||||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
SignalStrengthIndicator.bars(
|
||||
value: .5, //_signalStrength,
|
||||
size: 50,
|
||||
barCount: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 0,
|
||||
color: Color(0x33000000),
|
||||
offset: Offset(0, 0),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: ContactListComponentWidget(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (responsiveVisibility(
|
||||
context: context,
|
||||
tablet: false,
|
||||
tabletLandscape: false,
|
||||
desktop: false,
|
||||
))
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
elevation: 4,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
//color: Theme.of(context).secondaryColor,
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 8, 16, 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment:
|
||||
const AlignmentDirectional(-1, 0),
|
||||
child: Text(
|
||||
'Contacts',
|
||||
textAlign: TextAlign.start,
|
||||
// style: FlutterFlowTheme.of(context)
|
||||
// .title2
|
||||
// .override(
|
||||
// fontFamily: 'Noto Sans',
|
||||
// color: FlutterFlowTheme.of(context)
|
||||
// .header,
|
||||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
SignalStrengthIndicator.bars(
|
||||
value: .5, //_signalStrength,
|
||||
size: 50,
|
||||
barCount: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 0,
|
||||
color: Color(0x33000000),
|
||||
offset: Offset(0, 0),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: ContactListComponentWidget(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
? buildTablet(context)
|
||||
: buildPhone(context),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
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 '../tools/desktop_control.dart';
|
||||
import '../providers/window_control.dart';
|
||||
|
||||
class IndexPage extends StatelessWidget {
|
||||
class IndexPage extends ConsumerWidget {
|
||||
const IndexPage({super.key});
|
||||
static const path = '/';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
enableTitleBar(false);
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ref.watch(windowControlProvider);
|
||||
|
||||
return Scaffold(
|
||||
body: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
|
126
lib/pages/main_pager/account_page.dart
Normal file
126
lib/pages/main_pager/account_page.dart
Normal file
@ -0,0 +1,126 @@
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
import '../../components/profile.dart';
|
||||
import '../../entities/local_account.dart';
|
||||
import '../../entities/proto.dart' as proto;
|
||||
import '../../providers/account.dart';
|
||||
import '../../providers/local_accounts.dart';
|
||||
import '../../providers/logins.dart';
|
||||
import '../../tools/tools.dart';
|
||||
|
||||
class AccountPage extends ConsumerStatefulWidget {
|
||||
const AccountPage({super.key});
|
||||
|
||||
@override
|
||||
AccountPageState createState() => AccountPageState();
|
||||
}
|
||||
|
||||
class AccountPageState extends ConsumerState<AccountPage> {
|
||||
final _unfocusNode = FocusNode();
|
||||
TypedKey? _selectedAccount;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedAccount = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unfocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget buildAccountList(BuildContext context) {
|
||||
return Center(child: Text("account list"));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildUnlockAccount(
|
||||
BuildContext context,
|
||||
IList<LocalAccount> localAccounts,
|
||||
// ignore: prefer_expression_function_bodies
|
||||
) {
|
||||
return Center(child: Text("unlock account"));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildUserAccount(
|
||||
BuildContext context,
|
||||
IList<LocalAccount> localAccounts,
|
||||
TypedKey activeUserLogin,
|
||||
proto.Account account,
|
||||
// ignore: prefer_expression_function_bodies
|
||||
) {
|
||||
return ProfileWidget(
|
||||
name: account.profile.name, title: account.profile.title);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final localAccountsV = ref.watch(localAccountsProvider);
|
||||
final loginsV = ref.watch(loginsProvider);
|
||||
|
||||
if (!localAccountsV.hasValue || !loginsV.hasValue) {
|
||||
return waitingPage(context);
|
||||
}
|
||||
final localAccounts = localAccountsV.requireValue;
|
||||
final logins = loginsV.requireValue;
|
||||
|
||||
final activeUserLogin = logins.activeUserLogin;
|
||||
if (activeUserLogin == null) {
|
||||
// If no logged in user is active, show the list of account
|
||||
return buildAccountList(context);
|
||||
}
|
||||
final accountV = ref
|
||||
.watch(fetchAccountProvider(accountMasterRecordKey: activeUserLogin));
|
||||
if (!accountV.hasValue) {
|
||||
return waitingPage(context);
|
||||
}
|
||||
final account = accountV.requireValue;
|
||||
switch (account.status) {
|
||||
case AccountInfoStatus.noAccount:
|
||||
Future.delayed(0.ms, () async {
|
||||
await showErrorModal(
|
||||
context,
|
||||
translate('account_page.missing_account_title'),
|
||||
translate('account_page.missing_account_text'));
|
||||
// Delete account
|
||||
await ref
|
||||
.read(localAccountsProvider.notifier)
|
||||
.deleteAccount(activeUserLogin);
|
||||
});
|
||||
return waitingPage(context);
|
||||
case AccountInfoStatus.accountInvalid:
|
||||
Future.delayed(0.ms, () async {
|
||||
await showErrorModal(
|
||||
context,
|
||||
translate('account_page.invalid_account_title'),
|
||||
translate('account_page.invalid_account_text'));
|
||||
// Delete account
|
||||
await ref
|
||||
.read(localAccountsProvider.notifier)
|
||||
.deleteAccount(activeUserLogin);
|
||||
});
|
||||
return waitingPage(context);
|
||||
case AccountInfoStatus.accountLocked:
|
||||
// Show unlock widget
|
||||
return buildUnlockAccount(context, localAccounts);
|
||||
case AccountInfoStatus.accountReady:
|
||||
return buildUserAccount(
|
||||
context,
|
||||
localAccounts,
|
||||
activeUserLogin,
|
||||
account.account!,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
30
lib/pages/main_pager/chats_page.dart
Normal file
30
lib/pages/main_pager/chats_page.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ChatsPage extends ConsumerStatefulWidget {
|
||||
const ChatsPage({super.key});
|
||||
|
||||
@override
|
||||
ChatsPageState createState() => ChatsPageState();
|
||||
}
|
||||
|
||||
class ChatsPageState extends ConsumerState<ChatsPage> {
|
||||
final _unfocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unfocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return Center(child: Text("Conversations Page"));
|
||||
}
|
||||
}
|
30
lib/pages/main_pager/contacts_page.dart
Normal file
30
lib/pages/main_pager/contacts_page.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ContactsPage extends ConsumerStatefulWidget {
|
||||
const ContactsPage({super.key});
|
||||
|
||||
@override
|
||||
ContactsPageState createState() => ContactsPageState();
|
||||
}
|
||||
|
||||
class ContactsPageState extends ConsumerState<ContactsPage> {
|
||||
final _unfocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unfocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return Center(child: Text("Contacts Page"));
|
||||
}
|
||||
}
|
180
lib/pages/main_pager/main_pager.dart
Normal file
180
lib/pages/main_pager/main_pager.dart
Normal file
@ -0,0 +1,180 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:stylish_bottom_bar/model/bar_items.dart';
|
||||
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
|
||||
|
||||
import 'account_page.dart';
|
||||
import 'contacts_page.dart';
|
||||
import 'chats_page.dart';
|
||||
|
||||
class MainPager extends ConsumerStatefulWidget {
|
||||
const MainPager({super.key});
|
||||
|
||||
@override
|
||||
MainPagerState createState() => MainPagerState();
|
||||
}
|
||||
|
||||
class MainPagerState extends ConsumerState<MainPager>
|
||||
with TickerProviderStateMixin {
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
final _unfocusNode = FocusNode();
|
||||
|
||||
final _pageController = PageController();
|
||||
var _currentPage = 0;
|
||||
|
||||
final _selectedIconList = <IconData>[
|
||||
Icons.chat,
|
||||
Icons.contacts,
|
||||
Icons.person
|
||||
];
|
||||
// final _unselectedIconList = <IconData>[
|
||||
// Icons.chat_outlined,
|
||||
// Icons.contacts_outlined,
|
||||
// Icons.person_outlined
|
||||
// ];
|
||||
final _fabIconList = <IconData>[
|
||||
Icons.add_comment_sharp,
|
||||
Icons.person_add_sharp,
|
||||
Icons.settings_sharp,
|
||||
];
|
||||
final _labelList = <String>[
|
||||
translate('pager.chats'),
|
||||
translate('pager.contacts'),
|
||||
translate('pager.account'),
|
||||
];
|
||||
final List<Widget> _bottomBarPages = [
|
||||
const ChatsPage(),
|
||||
const ContactsPage(),
|
||||
const AccountPage(),
|
||||
];
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unfocusNode.dispose();
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool onScrollNotification(ScrollNotification notification) {
|
||||
if (notification is UserScrollNotification &&
|
||||
notification.metrics.axis == Axis.vertical) {
|
||||
switch (notification.direction) {
|
||||
case ScrollDirection.forward:
|
||||
// _hideBottomBarAnimationController.reverse();
|
||||
// _fabAnimationController.forward(from: 0);
|
||||
break;
|
||||
case ScrollDirection.reverse:
|
||||
// _hideBottomBarAnimationController.forward();
|
||||
// _fabAnimationController.reverse(from: 1);
|
||||
break;
|
||||
case ScrollDirection.idle:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BottomBarItem buildBottomBarItem(int index) {
|
||||
final theme = Theme.of(context);
|
||||
return BottomBarItem(
|
||||
title: Text(_labelList[index]),
|
||||
icon: Icon(_selectedIconList[index],
|
||||
color: theme.colorScheme.onPrimaryContainer),
|
||||
selectedIcon: Icon(_selectedIconList[index],
|
||||
color: theme.colorScheme.onPrimaryContainer),
|
||||
backgroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
//unSelectedColor: theme.colorScheme.primaryContainer,
|
||||
//selectedColor: theme.colorScheme.primary,
|
||||
//badge: const Text('9+'),
|
||||
//showBadge: true,
|
||||
);
|
||||
}
|
||||
|
||||
List<BottomBarItem> _buildBottomBarItems() {
|
||||
final bottomBarItems = List<BottomBarItem>.empty(growable: true);
|
||||
for (var index = 0; index < _bottomBarPages.length; index++) {
|
||||
final item = buildBottomBarItem(index);
|
||||
bottomBarItems.add(item);
|
||||
}
|
||||
return bottomBarItems;
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
extendBody: true,
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: onScrollNotification,
|
||||
child: PageView(
|
||||
controller: _pageController,
|
||||
//physics: const NeverScrollableScrollPhysics(),
|
||||
children: List.generate(
|
||||
_bottomBarPages.length, (index) => _bottomBarPages[index]),
|
||||
)),
|
||||
// appBar: AppBar(
|
||||
// toolbarHeight: 24,
|
||||
// title: Text(
|
||||
// 'C',
|
||||
// style: Theme.of(context).textTheme.headlineSmall,
|
||||
// ),
|
||||
// ),
|
||||
bottomNavigationBar: StylishBottomBar(
|
||||
backgroundColor: theme.colorScheme.primaryContainer,
|
||||
// gradient: LinearGradient(
|
||||
// begin: Alignment.topCenter,
|
||||
// end: Alignment.bottomCenter,
|
||||
// colors: <Color>[
|
||||
// theme.colorScheme.primary,
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// ]),
|
||||
option: AnimatedBarOptions(
|
||||
// iconSize: 32,
|
||||
//barAnimation: BarAnimation.fade,
|
||||
iconStyle: IconStyle.animated,
|
||||
inkEffect: true,
|
||||
inkColor: theme.colorScheme.primary,
|
||||
//opacity: 0.3,
|
||||
),
|
||||
items: _buildBottomBarItems(),
|
||||
hasNotch: true,
|
||||
fabLocation: StylishBarFabLocation.end,
|
||||
currentIndex: _currentPage,
|
||||
onTap: (index) async {
|
||||
await _pageController.animateToPage(index,
|
||||
duration: 250.ms, curve: Curves.bounceOut);
|
||||
setState(() {
|
||||
_currentPage = index;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
floatingActionButton: FloatingActionButton(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14))),
|
||||
//foregroundColor: theme.colorScheme.secondary,
|
||||
backgroundColor: theme.colorScheme.secondaryContainer,
|
||||
child: Icon(
|
||||
_fabIconList[_currentPage],
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
),
|
||||
onPressed: () {
|
||||
// xxx
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
||||
);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import '../components/default_app_bar.dart';
|
||||
import '../entities/proto.dart' as proto;
|
||||
import '../providers/local_accounts.dart';
|
||||
import '../providers/logins.dart';
|
||||
import '../providers/window_control.dart';
|
||||
import '../tools/tools.dart';
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
|
||||
@ -111,8 +112,7 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
enableTitleBar(true);
|
||||
portraitOnly();
|
||||
ref.watch(windowControlProvider);
|
||||
|
||||
final localAccounts = ref.watch(localAccountsProvider);
|
||||
final logins = ref.watch(loginsProvider);
|
||||
@ -132,15 +132,8 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
||||
try {
|
||||
await createAccount();
|
||||
} on Exception catch (e) {
|
||||
await QuickAlert.show(
|
||||
context: context,
|
||||
type: QuickAlertType.error,
|
||||
title: translate('new_account_page.error'),
|
||||
text: 'Exception: $e',
|
||||
//backgroundColor: Colors.black,
|
||||
//titleColor: Colors.white,
|
||||
//textColor: Colors.white,
|
||||
);
|
||||
await showErrorModal(
|
||||
context, translate('new_account_page.error'), 'Exception: $e');
|
||||
}
|
||||
},
|
||||
).paddingSymmetric(horizontal: 24, vertical: 8),
|
||||
|
112
lib/providers/account.dart
Normal file
112
lib/providers/account.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
import '../entities/entities.dart';
|
||||
import '../entities/proto.dart' as proto;
|
||||
import '../veilid_support/veilid_support.dart';
|
||||
|
||||
import 'local_accounts.dart';
|
||||
import 'logins.dart';
|
||||
|
||||
part 'account.g.dart';
|
||||
|
||||
enum AccountInfoStatus {
|
||||
noAccount,
|
||||
accountInvalid,
|
||||
accountLocked,
|
||||
accountReady,
|
||||
}
|
||||
|
||||
class AccountInfo {
|
||||
AccountInfo({
|
||||
required this.status,
|
||||
required this.active,
|
||||
this.account,
|
||||
});
|
||||
|
||||
AccountInfoStatus status;
|
||||
bool active;
|
||||
proto.Account? account;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<AccountInfo> fetchAccount(FetchAccountRef ref,
|
||||
{required TypedKey accountMasterRecordKey}) async {
|
||||
// Get which local account we want to fetch the profile for
|
||||
final veilid = await eventualVeilid.future;
|
||||
final localAccount = await ref.watch(
|
||||
fetchLocalAccountProvider(accountMasterRecordKey: accountMasterRecordKey)
|
||||
.future);
|
||||
if (localAccount == null) {
|
||||
// Local account does not exist
|
||||
return AccountInfo(status: AccountInfoStatus.noAccount, active: false);
|
||||
}
|
||||
|
||||
// See if we've logged into this account or if it is locked
|
||||
final activeUserLogin = await ref.watch(loginsProvider.future
|
||||
.select((value) async => (await value).activeUserLogin));
|
||||
final active = activeUserLogin == accountMasterRecordKey;
|
||||
|
||||
final login = await ref.watch(
|
||||
fetchLoginProvider(accountMasterRecordKey: accountMasterRecordKey)
|
||||
.future);
|
||||
if (login == null) {
|
||||
// Account was locked
|
||||
return AccountInfo(status: AccountInfoStatus.accountLocked, active: active);
|
||||
}
|
||||
|
||||
// Read the identity key to get the account keys
|
||||
final dhtctx = (await veilid.routingContext())
|
||||
.withPrivacy()
|
||||
.withSequencing(Sequencing.ensureOrdered);
|
||||
final identityRecordCrypto = await DHTRecordCryptoPrivate.fromSecret(
|
||||
localAccount.identityMaster.identityRecordKey.kind,
|
||||
login.identitySecret.value);
|
||||
|
||||
late final TypedKey accountRecordKey;
|
||||
late final KeyPair accountRecordOwner;
|
||||
|
||||
await (await DHTRecord.openRead(
|
||||
dhtctx, localAccount.identityMaster.identityRecordKey,
|
||||
crypto: identityRecordCrypto))
|
||||
.scope((identityRec) async {
|
||||
final identity = await identityRec.getJson(Identity.fromJson);
|
||||
if (identity == null) {
|
||||
// Identity could not be read or decrypted from DHT
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountInvalid, active: active);
|
||||
}
|
||||
final accountRecords = IMapOfSets.from(identity.accountRecords);
|
||||
final vcAccounts = accountRecords.get(veilidChatAccountKey);
|
||||
if (vcAccounts.length != 1) {
|
||||
// No veilidchat account, or multiple accounts
|
||||
// somehow associated with identity
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountInvalid, active: active);
|
||||
}
|
||||
final accountRecordInfo = vcAccounts.first;
|
||||
accountRecordKey = accountRecordInfo.key;
|
||||
accountRecordOwner = accountRecordInfo.owner;
|
||||
});
|
||||
|
||||
// Pull the account DHT key, decode it and return it
|
||||
final accountRecordCrypto = await DHTRecordCryptoPrivate.fromSecret(
|
||||
accountRecordKey.kind, accountRecordOwner.secret);
|
||||
late final proto.Account account;
|
||||
await (await DHTRecord.openRead(dhtctx, accountRecordKey,
|
||||
crypto: accountRecordCrypto))
|
||||
.scope((accountRec) async {
|
||||
final protoAccount = await accountRec.getProtobuf(proto.Account.fromBuffer);
|
||||
if (protoAccount == null) {
|
||||
// Account could not be read or decrypted from DHT
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountInvalid, active: active);
|
||||
}
|
||||
account = protoAccount;
|
||||
});
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountReady, active: active, account: account);
|
||||
}
|
113
lib/providers/account.g.dart
Normal file
113
lib/providers/account.g.dart
Normal file
@ -0,0 +1,113 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'account.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$fetchAccountHash() => r'4d94703d07a21509650e19f60ea67ac96a39742e';
|
||||
|
||||
/// 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));
|
||||
}
|
||||
}
|
||||
|
||||
typedef FetchAccountRef = AutoDisposeFutureProviderRef<AccountInfo>;
|
||||
|
||||
/// See also [fetchAccount].
|
||||
@ProviderFor(fetchAccount)
|
||||
const fetchAccountProvider = FetchAccountFamily();
|
||||
|
||||
/// See also [fetchAccount].
|
||||
class FetchAccountFamily extends Family<AsyncValue<AccountInfo>> {
|
||||
/// See also [fetchAccount].
|
||||
const FetchAccountFamily();
|
||||
|
||||
/// See also [fetchAccount].
|
||||
FetchAccountProvider call({
|
||||
required Typed<FixedEncodedString43> accountMasterRecordKey,
|
||||
}) {
|
||||
return FetchAccountProvider(
|
||||
accountMasterRecordKey: accountMasterRecordKey,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FetchAccountProvider getProviderOverride(
|
||||
covariant FetchAccountProvider 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'fetchAccountProvider';
|
||||
}
|
||||
|
||||
/// See also [fetchAccount].
|
||||
class FetchAccountProvider extends AutoDisposeFutureProvider<AccountInfo> {
|
||||
/// See also [fetchAccount].
|
||||
FetchAccountProvider({
|
||||
required this.accountMasterRecordKey,
|
||||
}) : super.internal(
|
||||
(ref) => fetchAccount(
|
||||
ref,
|
||||
accountMasterRecordKey: accountMasterRecordKey,
|
||||
),
|
||||
from: fetchAccountProvider,
|
||||
name: r'fetchAccountProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$fetchAccountHash,
|
||||
dependencies: FetchAccountFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
FetchAccountFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final Typed<FixedEncodedString43> accountMasterRecordKey;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FetchAccountProvider &&
|
||||
other.accountMasterRecordKey == accountMasterRecordKey;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, accountMasterRecordKey.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
|
@ -14,6 +14,8 @@ import 'logins.dart';
|
||||
|
||||
part 'local_accounts.g.dart';
|
||||
|
||||
const String veilidChatAccountKey = 'com.veilid.veilidchat';
|
||||
|
||||
// Local account manager
|
||||
@riverpod
|
||||
class LocalAccounts extends _$LocalAccounts
|
||||
@ -90,6 +92,7 @@ class LocalAccounts extends _$LocalAccounts
|
||||
encryptionKeyType: encryptionKeyType,
|
||||
biometricsEnabled: false,
|
||||
hiddenAccount: false,
|
||||
name: account.profile.name,
|
||||
);
|
||||
|
||||
/////// Add account with profile to DHT
|
||||
@ -114,8 +117,14 @@ class LocalAccounts extends _$LocalAccounts
|
||||
|
||||
await identityRec.eventualUpdateJson(Identity.fromJson,
|
||||
(oldIdentity) async {
|
||||
final accountRecords = IMapOfSets.from(oldIdentity.accountRecords)
|
||||
.add('com.veilid.veilidchat', newAccountRecordInfo)
|
||||
final oldAccountRecords = IMapOfSets.from(oldIdentity.accountRecords);
|
||||
// Only allow one account per identity for veilidchat
|
||||
if (oldAccountRecords.get(veilidChatAccountKey).isNotEmpty) {
|
||||
throw StateError(
|
||||
'Only one account per identity allowed for VeilidChat');
|
||||
}
|
||||
final accountRecords = oldAccountRecords
|
||||
.add(veilidChatAccountKey, newAccountRecordInfo)
|
||||
.asIMap();
|
||||
return oldIdentity.copyWith(accountRecords: accountRecords);
|
||||
});
|
||||
@ -151,3 +160,18 @@ class LocalAccounts extends _$LocalAccounts
|
||||
|
||||
/// Recover an account with the master identity secret
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,113 @@ part of 'local_accounts.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$localAccountsHash() => r'f69de269e15fabb83d1afbb7fdb6eb7693d0ce24';
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
typedef FetchLocalAccountRef = AutoDisposeFutureProviderRef<LocalAccount?>;
|
||||
|
||||
/// 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 this.accountMasterRecordKey,
|
||||
}) : super.internal(
|
||||
(ref) => fetchLocalAccount(
|
||||
ref,
|
||||
accountMasterRecordKey: accountMasterRecordKey,
|
||||
),
|
||||
from: fetchLocalAccountProvider,
|
||||
name: r'fetchLocalAccountProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$fetchLocalAccountHash,
|
||||
dependencies: FetchLocalAccountFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
FetchLocalAccountFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final Typed<FixedEncodedString43> accountMasterRecordKey;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
String _$localAccountsHash() => r'c8214abbc9720449910e74e32fc52d53ca4453c0';
|
||||
|
||||
/// See also [LocalAccounts].
|
||||
@ProviderFor(LocalAccounts)
|
||||
|
@ -163,3 +163,18 @@ class Logins extends _$Logins with AsyncTableDBBacked<ActiveLogins> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,111 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
typedef FetchLoginRef = AutoDisposeFutureProviderRef<UserLogin?>;
|
||||
|
||||
/// 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 this.accountMasterRecordKey,
|
||||
}) : super.internal(
|
||||
(ref) => fetchLogin(
|
||||
ref,
|
||||
accountMasterRecordKey: accountMasterRecordKey,
|
||||
),
|
||||
from: fetchLoginProvider,
|
||||
name: r'fetchLoginProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$fetchLoginHash,
|
||||
dependencies: FetchLoginFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
FetchLoginFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final Typed<FixedEncodedString43> accountMasterRecordKey;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
String _$loginsHash() => r'ed9dbe91a248f662ccb0fac6edf5b1892cf2ef92';
|
||||
|
||||
/// See also [Logins].
|
||||
|
@ -1,3 +1,5 @@
|
||||
export 'account_profile.dart';
|
||||
export 'connection_state.dart';
|
||||
export 'local_accounts.dart';
|
||||
export 'logins.dart';
|
||||
export 'window_control.dart';
|
||||
|
83
lib/providers/window_control.dart
Normal file
83
lib/providers/window_control.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../tools/responsive.dart';
|
||||
|
||||
export 'package:window_manager/window_manager.dart' show TitleBarStyle;
|
||||
|
||||
part 'window_control.g.dart';
|
||||
|
||||
enum OrientationCapability {
|
||||
normal,
|
||||
portraitOnly,
|
||||
landscapeOnly,
|
||||
}
|
||||
|
||||
// Local account manager
|
||||
@riverpod
|
||||
class WindowControl extends _$WindowControl {
|
||||
/// Change window control
|
||||
@override
|
||||
FutureOr<bool> build() async {
|
||||
await _doWindowSetup(TitleBarStyle.hidden, OrientationCapability.normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<void> initialize() async {
|
||||
if (isDesktop) {
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
const windowOptions = WindowOptions(
|
||||
size: Size(768, 1024),
|
||||
//minimumSize: Size(480, 640),
|
||||
center: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
skipTaskbar: false,
|
||||
);
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.show();
|
||||
await windowManager.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _doWindowSetup(TitleBarStyle titleBarStyle,
|
||||
OrientationCapability orientationCapability) async {
|
||||
if (isDesktop) {
|
||||
await windowManager.setTitleBarStyle(titleBarStyle);
|
||||
} else {
|
||||
switch (orientationCapability) {
|
||||
case OrientationCapability.normal:
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.landscapeLeft,
|
||||
DeviceOrientation.landscapeRight,
|
||||
]);
|
||||
case OrientationCapability.portraitOnly:
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
]);
|
||||
case OrientationCapability.landscapeOnly:
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.landscapeLeft,
|
||||
DeviceOrientation.landscapeRight,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Mutators and Selectors
|
||||
|
||||
/// Reorder accounts
|
||||
Future<void> changeWindowSetup(TitleBarStyle titleBarStyle,
|
||||
OrientationCapability orientationCapability) async {
|
||||
state = const AsyncValue.loading();
|
||||
await _doWindowSetup(titleBarStyle, orientationCapability);
|
||||
state = const AsyncValue.data(true);
|
||||
}
|
||||
}
|
25
lib/providers/window_control.g.dart
Normal file
25
lib/providers/window_control.g.dart
Normal file
@ -0,0 +1,25 @@
|
||||
// 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: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
|
@ -6,7 +6,7 @@ part of 'router_notifier.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$routerNotifierHash() => r'ef31219dde5e12b2bb224c79ca13ab4f414c81b4';
|
||||
String _$routerNotifierHash() => r'8e7b9debfa144253e25871edf920bf315f28a861';
|
||||
|
||||
/// See also [RouterNotifier].
|
||||
@ProviderFor(RouterNotifier)
|
||||
|
@ -1,54 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'themes/themes.dart';
|
||||
|
||||
class ThemeService {
|
||||
ThemeService._();
|
||||
static late SharedPreferences prefs;
|
||||
static ThemeService? _instance;
|
||||
|
||||
static Future<ThemeService> get instance async {
|
||||
if (_instance == null) {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
_instance = ThemeService._();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
final allThemes = <String, ThemeData>{
|
||||
'dark': darkTheme,
|
||||
'light': lightTheme,
|
||||
};
|
||||
|
||||
String get previousThemeName {
|
||||
var themeName = prefs.getString('previousThemeName');
|
||||
if (themeName == null) {
|
||||
final isPlatformDark =
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark;
|
||||
themeName = isPlatformDark ? 'dark' : 'light';
|
||||
}
|
||||
return themeName;
|
||||
}
|
||||
|
||||
ThemeData get initial {
|
||||
var themeName = prefs.getString('theme');
|
||||
if (themeName == null) {
|
||||
final isPlatformDark =
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark;
|
||||
themeName = isPlatformDark ? 'dark' : 'light';
|
||||
}
|
||||
return allThemes[themeName] ?? allThemes['light']!;
|
||||
}
|
||||
|
||||
Future<void> save(String newThemeName) async {
|
||||
final currentThemeName = prefs.getString('theme');
|
||||
if (currentThemeName != null) {
|
||||
await prefs.setString('previousThemeName', currentThemeName);
|
||||
}
|
||||
await prefs.setString('theme', newThemeName);
|
||||
}
|
||||
|
||||
ThemeData getByName(String name) => allThemes[name]!;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ThemeData darkTheme = ThemeData.dark();
|
||||
|
||||
// late Color primaryColor = const Color(0xFF343455);
|
||||
// late Color secondaryColor = const Color(0xFF3D3E77);
|
||||
// late Color tertiaryColor = const Color(0xFFBB108E);
|
||||
// late Color alternate = const Color(0xFF5086DF);
|
||||
// late Color primaryBackground = const Color(0xFF252534);
|
||||
// late Color secondaryBackground = const Color(0xFF292D44);
|
||||
// late Color primaryText = const Color(0xFFD0D0E0);
|
||||
// late Color secondaryText = const Color(0xFFB0B0D0);
|
||||
|
||||
// late Color disabledText = Color(0x808F8F8F);
|
||||
// late Color primaryEdge = Color(0xFF555594);
|
||||
// late Color header = Color(0xFF8A8AD8);
|
||||
// late Color textBackground = Color(0xFF181820);
|
||||
// late Color active = Color(0xFF463BAD);
|
||||
// late Color inactive = Color(0xFF2E2E3C);
|
@ -1,19 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ThemeData lightTheme = ThemeData.light();
|
||||
|
||||
// late Color primaryColor = const Color(0xFF6667AB);
|
||||
// late Color secondaryColor = const Color(0xFF7C7ED0);
|
||||
// late Color tertiaryColor = const Color(0xFFF259C9);
|
||||
// late Color alternate = const Color(0xFF77ABFF);
|
||||
// late Color primaryBackground = const Color(0xFFA0A0D0);
|
||||
// late Color secondaryBackground = const Color(0xFFB0B0D0);
|
||||
// late Color primaryText = const Color(0xFF101010);
|
||||
// late Color secondaryText = const Color(0xFF282840);
|
||||
|
||||
// late Color disabledText = Color(0x8047464F);
|
||||
// late Color primaryEdge = Color(0xFF44447F);
|
||||
// late Color header = Color(0xFFE0E0F0);
|
||||
// late Color textBackground = Color(0xFFE0E0F0);
|
||||
// late Color active = Color(0xFF5B4FFA);
|
||||
// late Color inactive = Color(0xFF3E3E72);
|
@ -1,2 +0,0 @@
|
||||
export 'dark.dart';
|
||||
export 'light.dart';
|
@ -1,2 +0,0 @@
|
||||
export 'theme_service.dart';
|
||||
export 'themes/themes.dart';
|
@ -1,48 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
Future<void> setupDesktopWindow() async {
|
||||
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
const windowOptions = WindowOptions(
|
||||
size: Size(768, 1024),
|
||||
minimumSize: Size(480, 640),
|
||||
center: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
skipTaskbar: false,
|
||||
titleBarStyle: TitleBarStyle.hidden,
|
||||
);
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.show();
|
||||
await windowManager.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> enableTitleBar(bool enabled) async {
|
||||
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||
if (enabled) {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
||||
} else {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> portraitOnly() async {
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> landscapeOnly() async {
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.landscapeLeft,
|
||||
DeviceOrientation.landscapeRight,
|
||||
]);
|
||||
}
|
445
lib/tools/radix_generator.dart
Normal file
445
lib/tools/radix_generator.dart
Normal file
@ -0,0 +1,445 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:radix_colors/radix_colors.dart';
|
||||
|
||||
enum RadixThemeColor {
|
||||
scarlet, // tomato + red + violet
|
||||
babydoll, // crimson + purple + pink
|
||||
vapor, // pink + cyan + plum
|
||||
gold, // yellow + amber + orange
|
||||
garden, // grass + orange + brown
|
||||
forest, // green + brown + amber
|
||||
arctic, // sky + teal + violet
|
||||
lapis, // blue + indigo + mint
|
||||
eggplant, // violet + purple + indigo
|
||||
lime, // lime + yellow + orange
|
||||
grim, // mauve + slate + sage
|
||||
}
|
||||
|
||||
enum _RadixBaseColor {
|
||||
tomato,
|
||||
red,
|
||||
crimson,
|
||||
pink,
|
||||
plum,
|
||||
purple,
|
||||
violet,
|
||||
indigo,
|
||||
blue,
|
||||
sky,
|
||||
cyan,
|
||||
teal,
|
||||
mint,
|
||||
green,
|
||||
grass,
|
||||
lime,
|
||||
yellow,
|
||||
amber,
|
||||
orange,
|
||||
brown,
|
||||
}
|
||||
|
||||
RadixColor _radixGraySteps(
|
||||
Brightness brightness, bool alpha, _RadixBaseColor baseColor) {
|
||||
switch (baseColor) {
|
||||
case _RadixBaseColor.tomato:
|
||||
case _RadixBaseColor.red:
|
||||
case _RadixBaseColor.crimson:
|
||||
case _RadixBaseColor.pink:
|
||||
case _RadixBaseColor.plum:
|
||||
case _RadixBaseColor.purple:
|
||||
case _RadixBaseColor.violet:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.mauveA
|
||||
: RadixColors.mauveA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.mauve
|
||||
: RadixColors.mauve);
|
||||
case _RadixBaseColor.indigo:
|
||||
case _RadixBaseColor.blue:
|
||||
case _RadixBaseColor.sky:
|
||||
case _RadixBaseColor.cyan:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.slateA
|
||||
: RadixColors.slateA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.slate
|
||||
: RadixColors.slate);
|
||||
case _RadixBaseColor.teal:
|
||||
case _RadixBaseColor.mint:
|
||||
case _RadixBaseColor.green:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.sageA
|
||||
: RadixColors.sageA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sage
|
||||
: RadixColors.sage);
|
||||
case _RadixBaseColor.lime:
|
||||
case _RadixBaseColor.grass:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.oliveA
|
||||
: RadixColors.oliveA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.olive
|
||||
: RadixColors.olive);
|
||||
case _RadixBaseColor.yellow:
|
||||
case _RadixBaseColor.amber:
|
||||
case _RadixBaseColor.orange:
|
||||
case _RadixBaseColor.brown:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.sandA
|
||||
: RadixColors.sandA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sand
|
||||
: RadixColors.sand);
|
||||
}
|
||||
}
|
||||
|
||||
RadixColor _radixColorSteps(
|
||||
Brightness brightness, bool alpha, _RadixBaseColor baseColor) {
|
||||
switch (baseColor) {
|
||||
case _RadixBaseColor.tomato:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.tomatoA
|
||||
: RadixColors.tomatoA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.tomato
|
||||
: RadixColors.tomato);
|
||||
case _RadixBaseColor.red:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.redA
|
||||
: RadixColors.redA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.red
|
||||
: RadixColors.red);
|
||||
case _RadixBaseColor.crimson:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.crimsonA
|
||||
: RadixColors.crimsonA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.crimson
|
||||
: RadixColors.crimson);
|
||||
case _RadixBaseColor.pink:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.pinkA
|
||||
: RadixColors.pinkA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.pink
|
||||
: RadixColors.pink);
|
||||
case _RadixBaseColor.plum:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.plumA
|
||||
: RadixColors.plumA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.plum
|
||||
: RadixColors.plum);
|
||||
case _RadixBaseColor.purple:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.purpleA
|
||||
: RadixColors.purpleA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.purple
|
||||
: RadixColors.purple);
|
||||
case _RadixBaseColor.violet:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.violetA
|
||||
: RadixColors.violetA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.violet
|
||||
: RadixColors.violet);
|
||||
case _RadixBaseColor.indigo:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.indigoA
|
||||
: RadixColors.indigoA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.indigo
|
||||
: RadixColors.indigo);
|
||||
case _RadixBaseColor.blue:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.blueA
|
||||
: RadixColors.blueA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.blue
|
||||
: RadixColors.blue);
|
||||
case _RadixBaseColor.sky:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.skyA
|
||||
: RadixColors.skyA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.sky
|
||||
: RadixColors.sky);
|
||||
case _RadixBaseColor.cyan:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.cyanA
|
||||
: RadixColors.cyanA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.cyan
|
||||
: RadixColors.cyan);
|
||||
case _RadixBaseColor.teal:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.tealA
|
||||
: RadixColors.tealA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.teal
|
||||
: RadixColors.teal);
|
||||
case _RadixBaseColor.mint:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.mintA
|
||||
: RadixColors.mintA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.mint
|
||||
: RadixColors.mint);
|
||||
case _RadixBaseColor.green:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.greenA
|
||||
: RadixColors.greenA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.green
|
||||
: RadixColors.green);
|
||||
case _RadixBaseColor.grass:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.grassA
|
||||
: RadixColors.grassA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.grass
|
||||
: RadixColors.grass);
|
||||
case _RadixBaseColor.lime:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.limeA
|
||||
: RadixColors.limeA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.lime
|
||||
: RadixColors.lime);
|
||||
case _RadixBaseColor.yellow:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.yellowA
|
||||
: RadixColors.yellowA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.yellow
|
||||
: RadixColors.yellow);
|
||||
case _RadixBaseColor.amber:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.amberA
|
||||
: RadixColors.amberA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.amber
|
||||
: RadixColors.amber);
|
||||
case _RadixBaseColor.orange:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.orangeA
|
||||
: RadixColors.orangeA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.orange
|
||||
: RadixColors.orange);
|
||||
case _RadixBaseColor.brown:
|
||||
return alpha
|
||||
? (brightness == Brightness.dark
|
||||
? RadixColors.dark.brownA
|
||||
: RadixColors.brownA)
|
||||
: (brightness == Brightness.dark
|
||||
? RadixColors.dark.brown
|
||||
: RadixColors.brown);
|
||||
}
|
||||
}
|
||||
|
||||
ColorScheme _radixColorScheme(
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Brightness brightness,
|
||||
RadixThemeColor themeColor) {
|
||||
late RadixColor primaryScale;
|
||||
late RadixColor primaryAlphaScale;
|
||||
late RadixColor secondaryScale;
|
||||
late RadixColor tertiaryScale;
|
||||
late RadixColor grayScale;
|
||||
late RadixColor errorScale;
|
||||
|
||||
switch (themeColor) {
|
||||
// tomato + red + violet
|
||||
case RadixThemeColor.scarlet:
|
||||
primaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato);
|
||||
secondaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.red);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.tomato);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.yellow);
|
||||
// crimson + purple + pink
|
||||
case RadixThemeColor.babydoll:
|
||||
primaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.crimson);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.crimson);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple);
|
||||
tertiaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.pink);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.crimson);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.orange);
|
||||
// pink + cyan + plum
|
||||
case RadixThemeColor.vapor:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.pink);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.pink);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.cyan);
|
||||
tertiaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.plum);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.pink);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.red);
|
||||
// yellow + amber + orange
|
||||
case RadixThemeColor.gold:
|
||||
primaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.yellow);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.yellow);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.amber);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.yellow);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.red);
|
||||
// grass + orange + brown
|
||||
case RadixThemeColor.garden:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.grass);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.grass);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.brown);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.grass);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.tomato);
|
||||
// green + brown + amber
|
||||
case RadixThemeColor.forest:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.green);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.green);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.brown);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.amber);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.green);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.tomato);
|
||||
// sky + teal + violet
|
||||
case RadixThemeColor.arctic:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.sky);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.sky);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.teal);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.sky);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.crimson);
|
||||
// blue + indigo + mint
|
||||
case RadixThemeColor.lapis:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.blue);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.blue);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo);
|
||||
tertiaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.mint);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.blue);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.crimson);
|
||||
// violet + purple + indigo
|
||||
case RadixThemeColor.eggplant:
|
||||
primaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.violet);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.violet);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.crimson);
|
||||
// lime + yellow + orange
|
||||
case RadixThemeColor.lime:
|
||||
primaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.lime);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.lime);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.yellow);
|
||||
tertiaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.orange);
|
||||
grayScale = _radixGraySteps(brightness, false, _RadixBaseColor.lime);
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.red);
|
||||
// mauve + slate + sage
|
||||
case RadixThemeColor.grim:
|
||||
primaryScale = _radixGraySteps(brightness, false, _RadixBaseColor.tomato);
|
||||
primaryAlphaScale =
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato);
|
||||
secondaryScale =
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo);
|
||||
tertiaryScale = _radixColorSteps(brightness, false, _RadixBaseColor.teal);
|
||||
grayScale = brightness == Brightness.dark
|
||||
? RadixColors.dark.gray
|
||||
: RadixColors.gray;
|
||||
errorScale = _radixColorSteps(brightness, false, _RadixBaseColor.red);
|
||||
}
|
||||
|
||||
return ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: primaryScale.step9,
|
||||
onPrimary: primaryScale.step12,
|
||||
primaryContainer: primaryScale.step3,
|
||||
onPrimaryContainer: primaryScale.step11,
|
||||
secondary: secondaryScale.step9,
|
||||
onSecondary: secondaryScale.step12,
|
||||
secondaryContainer: secondaryScale.step3,
|
||||
onSecondaryContainer: secondaryScale.step11,
|
||||
tertiary: tertiaryScale.step9,
|
||||
onTertiary: tertiaryScale.step12,
|
||||
tertiaryContainer: tertiaryScale.step3,
|
||||
onTertiaryContainer: tertiaryScale.step11,
|
||||
error: errorScale.step9,
|
||||
onError: errorScale.step12,
|
||||
errorContainer: errorScale.step3,
|
||||
onErrorContainer: errorScale.step11,
|
||||
background: primaryScale.step1, //gray scale?
|
||||
onBackground: primaryScale.step12,
|
||||
surface: primaryScale.step2, //gray scale?
|
||||
onSurface: primaryScale.step11,
|
||||
surfaceVariant: primaryScale.step3, //gray scale?
|
||||
onSurfaceVariant: primaryScale.step11,
|
||||
outline: primaryScale.step7,
|
||||
outlineVariant: primaryScale.step6,
|
||||
shadow: RadixColors.dark.gray.step1,
|
||||
scrim: primaryScale.step4,
|
||||
inverseSurface: primaryScale.step11,
|
||||
onInverseSurface: primaryScale.step2,
|
||||
inversePrimary: primaryScale.step10,
|
||||
surfaceTint: primaryAlphaScale.step9,
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
|
||||
TextTheme? textTheme;
|
||||
return ThemeData.from(
|
||||
colorScheme: _radixColorScheme(brightness, themeColor),
|
||||
textTheme: textTheme,
|
||||
useMaterial3: true);
|
||||
}
|
@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
|
||||
bool get isAndroid => !kIsWeb && Platform.isAndroid;
|
||||
bool get isiOS => !kIsWeb && Platform.isIOS;
|
||||
bool get isWeb => kIsWeb;
|
||||
bool get isDesktop =>
|
||||
Platform.isWindows || Platform.isLinux || Platform.isMacOS;
|
||||
|
||||
const kMobileWidthCutoff = 479.0;
|
||||
|
||||
|
91
lib/tools/theme_service.dart
Normal file
91
lib/tools/theme_service.dart
Normal file
@ -0,0 +1,91 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../entities/preferences.dart';
|
||||
import 'radix_generator.dart';
|
||||
|
||||
class ThemeService {
|
||||
ThemeService._();
|
||||
static late SharedPreferences prefs;
|
||||
static ThemeService? _instance;
|
||||
|
||||
static Future<ThemeService> get instance async {
|
||||
if (_instance == null) {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
_instance = ThemeService._();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static bool get isPlatformDark =>
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark;
|
||||
|
||||
ThemeData get initial {
|
||||
final themePreferencesJson = prefs.getString('themePreferences');
|
||||
final themePreferences = themePreferencesJson != null
|
||||
? ThemePreferences.fromJson(themePreferencesJson)
|
||||
: const ThemePreferences(
|
||||
colorPreference: ColorPreference.vapor,
|
||||
brightnessPreference: BrightnessPreference.system,
|
||||
displayScale: 1,
|
||||
);
|
||||
return get(themePreferences);
|
||||
}
|
||||
|
||||
Future<void> save(ThemePreferences themePreferences) async {
|
||||
await prefs.setString(
|
||||
'themePreferences', jsonEncode(themePreferences.toJson()));
|
||||
}
|
||||
|
||||
ThemeData get(ThemePreferences themePreferences) {
|
||||
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);
|
||||
}
|
||||
|
||||
return themeData;
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
export 'animations.dart';
|
||||
export 'desktop_control.dart';
|
||||
export 'external_stream_state.dart';
|
||||
export 'json_tools.dart';
|
||||
export 'phono_byte.dart';
|
||||
export 'protobuf_tools.dart';
|
||||
export 'radix_generator.dart';
|
||||
export 'responsive.dart';
|
||||
export 'theme_service.dart';
|
||||
export 'widget_helpers.dart';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:blurry_modal_progress_hud/blurry_modal_progress_hud.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:quickalert/quickalert.dart';
|
||||
|
||||
extension BorderExt on Widget {
|
||||
DecoratedBox debugBorder() => DecoratedBox(
|
||||
@ -13,10 +14,29 @@ extension ModalProgressExt on Widget {
|
||||
BlurryModalProgressHUD(
|
||||
inAsyncCall: isLoading,
|
||||
blurEffectIntensity: 4,
|
||||
progressIndicator: SpinKitFoldingCube(
|
||||
color: Theme.of(context).highlightColor,
|
||||
size: 90,
|
||||
),
|
||||
progressIndicator: buildProgressIndicator(context),
|
||||
color: Theme.of(context).shadowColor,
|
||||
child: this);
|
||||
}
|
||||
|
||||
Widget buildProgressIndicator(BuildContext context) => SpinKitFoldingCube(
|
||||
color: Theme.of(context).highlightColor,
|
||||
size: 90,
|
||||
);
|
||||
|
||||
Widget waitingPage(BuildContext context) => ColoredBox(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Center(child: buildProgressIndicator(context)));
|
||||
|
||||
Future<void> showErrorModal(
|
||||
BuildContext context, String title, String text) async {
|
||||
await QuickAlert.show(
|
||||
context: context,
|
||||
type: QuickAlertType.error,
|
||||
title: title,
|
||||
text: text,
|
||||
//backgroundColor: Colors.black,
|
||||
//titleColor: Colors.white,
|
||||
//textColor: Colors.white,
|
||||
);
|
||||
}
|
||||
|
@ -129,6 +129,17 @@ class DHTRecord {
|
||||
return jsonDecodeBytes(fromJson, data);
|
||||
}
|
||||
|
||||
Future<T?> getProtobuf<T extends GeneratedMessage>(
|
||||
T Function(List<int> i) fromBuffer,
|
||||
{int subkey = -1,
|
||||
bool forceRefresh = false}) async {
|
||||
final data = await get(subkey: subkey, forceRefresh: forceRefresh);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return fromBuffer(data.toList());
|
||||
}
|
||||
|
||||
Future<void> eventualWriteBytes(Uint8List newValue, {int subkey = -1}) async {
|
||||
subkey = subkeyOrDefault(subkey);
|
||||
newValue = await crypto.encrypt(newValue, subkey);
|
||||
|
24
pubspec.lock
24
pubspec.lock
@ -233,6 +233,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
circular_reveal_animation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: circular_reveal_animation
|
||||
sha256: "198f5a1fa27384dcf950807e0ae07a0da857c04df6233f7468755ee9db102b0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -669,6 +677,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
multi_split_view:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: multi_split_view
|
||||
sha256: d68e129bff71fc9e6b66de59e1b79deaf4b91f30940130bfbd2d704c1c713499
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1050,6 +1066,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
stylish_bottom_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: stylish_bottom_bar
|
||||
sha256: "54970e4753b4273239b6dea0d1175c56beabcf39b5c65df4cbf86f1b86568d2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -16,6 +16,7 @@ dependencies:
|
||||
change_case: ^1.1.0
|
||||
charcode: ^1.3.1
|
||||
circular_profile_avatar: ^2.0.5
|
||||
circular_reveal_animation: ^2.0.1
|
||||
cupertino_icons: ^1.0.2
|
||||
equatable: ^2.0.5
|
||||
fast_immutable_collections: ^9.1.5
|
||||
@ -38,6 +39,7 @@ dependencies:
|
||||
intl: ^0.18.0
|
||||
json_annotation: ^4.8.1
|
||||
loggy: ^2.0.3
|
||||
multi_split_view: ^2.4.0
|
||||
path: ^1.8.2
|
||||
path_provider: ^2.0.11
|
||||
protobuf: ^3.0.0
|
||||
@ -47,6 +49,7 @@ dependencies:
|
||||
riverpod_annotation: ^2.1.1
|
||||
shared_preferences: ^2.0.15
|
||||
signal_strength_indicator: ^0.4.1
|
||||
stylish_bottom_bar: ^1.0.3
|
||||
uuid: ^3.0.7
|
||||
veilid:
|
||||
# veilid: ^0.0.1
|
||||
|
Loading…
Reference in New Issue
Block a user