mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-06-27 07:40:29 -04:00
refactor and move stuff
This commit is contained in:
parent
8c22bf8cc0
commit
b54868cc55
28 changed files with 500 additions and 94 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,3 +42,6 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# WASM
|
||||||
|
/web/wasm/
|
13
_script_common
Normal file
13
_script_common
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
get_abs_filename() {
|
||||||
|
# $1 : relative filename
|
||||||
|
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Veilid location
|
||||||
|
VEILIDDIR=$(get_abs_filename "$SCRIPTDIR/../veilid")
|
||||||
|
if [ ! -d "$VEILIDDIR" ]; then
|
||||||
|
echo 'Veilid git clone needs to be at $VEILIDDIR'
|
||||||
|
exit 1
|
||||||
|
fi
|
20
lib/entities/profile.dart
Normal file
20
lib/entities/profile.dart
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class Profile {
|
||||||
|
String name;
|
||||||
|
String publicKey;
|
||||||
|
bool invisible;
|
||||||
|
|
||||||
|
Profile(this.name, this.publicKey) : invisible = false;
|
||||||
|
|
||||||
|
Profile.fromJson(Map<String, dynamic> json)
|
||||||
|
: name = json['name'],
|
||||||
|
publicKey = json['public_key'],
|
||||||
|
invisible = json['invisible'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'name': name,
|
||||||
|
'public_key': publicKey,
|
||||||
|
'invisible': invisible,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
2
lib/log/log.dart
Normal file
2
lib/log/log.dart
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export 'loggy.dart';
|
||||||
|
export 'state_logger.dart';
|
|
@ -15,12 +15,12 @@ class ChatPage extends ConsumerWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text("Home Page"),
|
const Text("Home Page"),
|
||||||
ElevatedButton(
|
// ElevatedButton(
|
||||||
onPressed: () {
|
// onPressed: () {
|
||||||
ref.watch(authNotifierProvider.notifier).logout();
|
// ref.watch(authNotifierProvider.notifier).logout();
|
||||||
},
|
// },
|
||||||
child: const Text("Logout"),
|
// child: const Text("Logout"),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -15,12 +15,12 @@ class HomePage extends ConsumerWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text("Home Page"),
|
const Text("Home Page"),
|
||||||
ElevatedButton(
|
// ElevatedButton(
|
||||||
onPressed: () {
|
// onPressed: () {
|
||||||
ref.watch(authNotifierProvider.notifier).logout();
|
// ref.watch(authNotifierProvider.notifier).logout();
|
||||||
},
|
// },
|
||||||
child: const Text("Logout"),
|
// child: const Text("Logout"),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -15,15 +15,15 @@ class LoginPage extends ConsumerWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text("Login Page"),
|
const Text("Login Page"),
|
||||||
ElevatedButton(
|
// ElevatedButton(
|
||||||
onPressed: () async {
|
// onPressed: () async {
|
||||||
ref.watch(authNotifierProvider.notifier).login(
|
// ref.watch(authNotifierProvider.notifier).login(
|
||||||
"myEmail",
|
// "myEmail",
|
||||||
"myPassword",
|
// "myPassword",
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
child: const Text("Login"),
|
// child: const Text("Login"),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
class NewAccountPage extends ConsumerWidget {
|
||||||
|
const NewAccountPage({super.key});
|
||||||
|
static const path = '/new_account';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: null,
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text("New Account Page"),
|
||||||
|
// ElevatedButton(
|
||||||
|
// onPressed: () async {
|
||||||
|
// ref.watch(authNotifierProvider.notifier).login(
|
||||||
|
// "myEmail",
|
||||||
|
// "myPassword",
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// child: const Text("Login"),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,4 @@
|
||||||
export 'home.dart';
|
export 'home.dart';
|
||||||
export 'splash.dart';
|
export 'index.dart';
|
||||||
|
export 'login.dart';
|
||||||
|
export 'new_account.dart';
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
class LoginPage extends ConsumerWidget {
|
||||||
|
const LoginPage({super.key});
|
||||||
|
static const path = '/settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: null,
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text("Settings Page"),
|
||||||
|
// ElevatedButton(
|
||||||
|
// onPressed: () async {
|
||||||
|
// ref.watch(authNotifierProvider.notifier).login(
|
||||||
|
// "myEmail",
|
||||||
|
// "myPassword",
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// child: const Text("Login"),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
import '../pages/pages.dart';
|
||||||
import 'router_notifier.dart';
|
import 'router_notifier.dart';
|
||||||
|
|
||||||
final _key = GlobalKey<NavigatorState>(debugLabel: 'routerKey');
|
final _key = GlobalKey<NavigatorState>(debugLabel: 'routerKey');
|
||||||
|
@ -14,7 +15,7 @@ final routerProvider = Provider.autoDispose<GoRouter>((ref) {
|
||||||
navigatorKey: _key,
|
navigatorKey: _key,
|
||||||
refreshListenable: notifier,
|
refreshListenable: notifier,
|
||||||
debugLogDiagnostics: true,
|
debugLogDiagnostics: true,
|
||||||
initialLocation: SplashPage.path,
|
initialLocation: IndexPage.path,
|
||||||
routes: notifier.routes,
|
routes: notifier.routes,
|
||||||
redirect: notifier.redirect,
|
redirect: notifier.redirect,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
||||||
|
|
||||||
import '../entities/user_role.dart';
|
|
||||||
import '../pages/pages.dart';
|
import '../pages/pages.dart';
|
||||||
import '../state/auth.dart';
|
import '../state/auth.dart';
|
||||||
import '../state/permissions.dart';
|
|
||||||
|
|
||||||
/// This notifier is meant to implement the [Listenable] our [GoRouter] needs.
|
/// This notifier is meant to implement the [Listenable] our [GoRouter] needs.
|
||||||
///
|
///
|
||||||
|
@ -45,9 +42,9 @@ class RouterNotifier extends AutoDisposeAsyncNotifier<void>
|
||||||
String? redirect(BuildContext context, GoRouterState state) {
|
String? redirect(BuildContext context, GoRouterState state) {
|
||||||
if (this.state.isLoading || this.state.hasError) return null;
|
if (this.state.isLoading || this.state.hasError) return null;
|
||||||
|
|
||||||
final isSplash = state.location == IndexPage.path;
|
final isIndex = state.location == IndexPage.path;
|
||||||
|
|
||||||
if (isSplash) {
|
if (isIndex) {
|
||||||
return isAuth ? HomePage.path : LoginPage.path;
|
return isAuth ? HomePage.path : LoginPage.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,47 +54,52 @@ class RouterNotifier extends AutoDisposeAsyncNotifier<void>
|
||||||
return isAuth ? null : IndexPage.path;
|
return isAuth ? null : IndexPage.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Our application routes. Obtained through code generation
|
/// Our application routes
|
||||||
List<GoRoute> get routes => [
|
List<GoRoute> get routes => [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: IndexPage.path,
|
path: IndexPage.path,
|
||||||
builder: (context, state) => const IndexPage(),
|
builder: (context, state) => const IndexPage(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: HomePage.path,
|
path: HomePage.path,
|
||||||
builder: (context, state) => const HomePage(),
|
builder: (context, state) => const HomePage(),
|
||||||
redirect: (context, state) async {
|
// redirect: (context, state) async {
|
||||||
if (state.location == HomePage.path) return null;
|
// if (state.location == HomePage.path) return null;
|
||||||
|
|
||||||
final roleListener = ProviderScope.containerOf(context).listen(
|
// // final roleListener = ProviderScope.containerOf(context).listen(
|
||||||
permissionsProvider.select((value) => value.valueOrNull),
|
// // permissionsProvider.select((value) => value.valueOrNull),
|
||||||
(previous, next) {},
|
// // (previous, next) {},
|
||||||
);
|
// // );
|
||||||
|
|
||||||
final userRole = roleListener.read();
|
// // final userRole = roleListener.read();
|
||||||
final redirectTo = userRole?.redirectBasedOn(state.location);
|
// // final redirectTo = userRole?.redirectBasedOn(state.location);
|
||||||
|
|
||||||
roleListener.close();
|
// // roleListener.close();
|
||||||
return redirectTo;
|
// // return redirectTo;
|
||||||
},
|
// },
|
||||||
routes: [
|
// routes: [
|
||||||
GoRoute(
|
// GoRoute(
|
||||||
path: AdminPage.path,
|
// path: AdminPage.path,
|
||||||
builder: (context, state) => const AdminPage(),
|
// builder: (context, state) => const AdminPage(),
|
||||||
),
|
// ),
|
||||||
GoRoute(
|
// GoRoute(
|
||||||
path: UserPage.path,
|
// path: UserPage.path,
|
||||||
builder: (context, state) => const UserPage(),
|
// builder: (context, state) => const UserPage(),
|
||||||
),
|
// ),
|
||||||
GoRoute(
|
// GoRoute(
|
||||||
path: GuestPage.path,
|
// path: GuestPage.path,
|
||||||
builder: (context, state) => const GuestPage(),
|
// builder: (context, state) => const GuestPage(),
|
||||||
)
|
// )
|
||||||
]),
|
// ]
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: LoginPage.path,
|
path: LoginPage.path,
|
||||||
builder: (context, state) => const LoginPage(),
|
builder: (context, state) => const LoginPage(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: NewAccountPage.path,
|
||||||
|
builder: (context, state) => const NewAccountPage(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Adds [GoRouter]'s listener as specified by its [Listenable].
|
/// Adds [GoRouter]'s listener as specified by its [Listenable].
|
||||||
|
@ -127,20 +129,20 @@ final routerNotifierProvider =
|
||||||
});
|
});
|
||||||
|
|
||||||
/// A simple extension to determine wherever should we redirect our users
|
/// A simple extension to determine wherever should we redirect our users
|
||||||
extension RedirecttionBasedOnRole on UserRole {
|
// extension RedirecttionBasedOnRole on UserRole {
|
||||||
/// Redirects the users based on [this] and its current [location]
|
// /// Redirects the users based on [this] and its current [location]
|
||||||
String? redirectBasedOn(String location) {
|
// String? redirectBasedOn(String location) {
|
||||||
switch (this) {
|
// switch (this) {
|
||||||
case UserRole.admin:
|
// case UserRole.admin:
|
||||||
return null;
|
// return null;
|
||||||
case UserRole.verifiedUser:
|
// case UserRole.verifiedUser:
|
||||||
case UserRole.unverifiedUser:
|
// case UserRole.unverifiedUser:
|
||||||
if (location == AdminPage.path) return HomePage.path;
|
// if (location == AdminPage.path) return HomePage.path;
|
||||||
return null;
|
// return null;
|
||||||
case UserRole.guest:
|
// case UserRole.guest:
|
||||||
case UserRole.none:
|
// case UserRole.none:
|
||||||
if (location != HomePage.path) return HomePage.path;
|
// if (location != HomePage.path) return HomePage.path;
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
110
lib/state/auth.dart
Normal file
110
lib/state/auth.dart
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class User {
|
||||||
|
final String publicKey;
|
||||||
|
final String secretKey;
|
||||||
|
const User(this.publicKey, this.secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mock of an Authenticated User
|
||||||
|
const _dummyUser = User("", "");
|
||||||
|
|
||||||
|
/// XXXX THIS IS TOTALLY BOGUS FOR NOW
|
||||||
|
/// This notifier holds and handles the authentication state of the application
|
||||||
|
class AuthNotifier extends AutoDisposeAsyncNotifier<User?> {
|
||||||
|
late SharedPreferences sharedPreferences;
|
||||||
|
static const _sharedPrefsKey = 'token';
|
||||||
|
|
||||||
|
/// Mock of the duration of a network request
|
||||||
|
@override
|
||||||
|
FutureOr<User?> build() async {
|
||||||
|
sharedPreferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
_persistenceRefreshLogic();
|
||||||
|
|
||||||
|
return await _loginRecoveryAttempt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to perform a login with the saved token on the persistant storage.
|
||||||
|
/// If _anything_ goes wrong, deletes the internal token and returns a [User.signedOut].
|
||||||
|
Future<User?> _loginRecoveryAttempt() async {
|
||||||
|
try {
|
||||||
|
final savedToken = sharedPreferences.getString(_sharedPrefsKey);
|
||||||
|
if (savedToken == null) {
|
||||||
|
throw const UnauthorizedException(
|
||||||
|
"Couldn't find the authentication token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _loginWithToken(savedToken);
|
||||||
|
} catch (_, __) {
|
||||||
|
await sharedPreferences.remove(_sharedPrefsKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock of a request performed on logout (might be common, or not, whatevs).
|
||||||
|
Future<void> logout() async {
|
||||||
|
await Future.delayed(networkRoundTripTime);
|
||||||
|
state = const AsyncValue.data(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock of a successful login attempt, which results come from the network.
|
||||||
|
Future<void> login(String publicKey, String password) async {
|
||||||
|
state = await AsyncValue.guard<User>(() async {
|
||||||
|
return Future.delayed(
|
||||||
|
networkRoundTripTime,
|
||||||
|
() => _dummyUser,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock of a login request performed with a saved token.
|
||||||
|
/// If such request fails, this method will throw an [UnauthorizedException].
|
||||||
|
Future<User> _loginWithToken(String token) async {
|
||||||
|
final logInAttempt = await Future.delayed(
|
||||||
|
networkRoundTripTime,
|
||||||
|
() => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (logInAttempt) return _dummyUser;
|
||||||
|
|
||||||
|
throw const UnauthorizedException('401 Unauthorized or something');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal method used to listen authentication state changes.
|
||||||
|
/// When the auth object is in a loading state, nothing happens.
|
||||||
|
/// When the auth object is in a error state, we choose to remove the token
|
||||||
|
/// Otherwise, we expect the current auth value to be reflected in our persistence API
|
||||||
|
void _persistenceRefreshLogic() {
|
||||||
|
ref.listenSelf((_, next) {
|
||||||
|
if (next.isLoading) return;
|
||||||
|
if (next.hasError) {
|
||||||
|
sharedPreferences.remove(_sharedPrefsKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final val = next.requireValue;
|
||||||
|
final isAuthenticated = val == null;
|
||||||
|
|
||||||
|
isAuthenticated
|
||||||
|
? sharedPreferences.remove(_sharedPrefsKey)
|
||||||
|
: sharedPreferences.setString(_sharedPrefsKey, val.publicKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final authNotifierProvider =
|
||||||
|
AutoDisposeAsyncNotifierProvider<AuthNotifier, User?>(() {
|
||||||
|
return AuthNotifier();
|
||||||
|
});
|
||||||
|
|
||||||
|
class UnauthorizedException implements Exception {
|
||||||
|
final String message;
|
||||||
|
const UnauthorizedException(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock of the duration of a network request
|
||||||
|
const networkRoundTripTime = Duration(milliseconds: 750);
|
|
@ -0,0 +1,16 @@
|
||||||
|
import '../tools/tools.dart';
|
||||||
|
|
||||||
|
enum ConnectionState {
|
||||||
|
detached,
|
||||||
|
detaching,
|
||||||
|
attaching,
|
||||||
|
attachedWeak,
|
||||||
|
attachedGood,
|
||||||
|
attachedStrong,
|
||||||
|
fullyAttached,
|
||||||
|
overAttached,
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalStreamState<ConnectionState> globalConnectionState =
|
||||||
|
ExternalStreamState<ConnectionState>(ConnectionState.detached);
|
||||||
|
var globalConnectionStateProvider = globalConnectionState.provider();
|
2
lib/state/state.dart
Normal file
2
lib/state/state.dart
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export 'connection_state.dart';
|
||||||
|
export 'auth.dart';
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'themes.dart';
|
import 'themes/themes.dart';
|
||||||
|
|
||||||
class ThemeService {
|
class ThemeService {
|
||||||
ThemeService._();
|
ThemeService._();
|
||||||
|
@ -18,9 +18,6 @@ class ThemeService {
|
||||||
final allThemes = <String, ThemeData>{
|
final allThemes = <String, ThemeData>{
|
||||||
'dark': darkTheme,
|
'dark': darkTheme,
|
||||||
'light': lightTheme,
|
'light': lightTheme,
|
||||||
'pink': pinkTheme,
|
|
||||||
'darkBlue': darkBlueTheme,
|
|
||||||
'halloween': halloweenTheme,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
String get previousThemeName {
|
String get previousThemeName {
|
||||||
|
|
19
lib/theme/themes/dark.dart
Normal file
19
lib/theme/themes/dark.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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);
|
19
lib/theme/themes/light.dart
Normal file
19
lib/theme/themes/light.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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);
|
2
lib/theme/themes/themes.dart
Normal file
2
lib/theme/themes/themes.dart
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export 'light.dart';
|
||||||
|
export 'dark.dart';
|
27
lib/tools/external_stream_state.dart
Normal file
27
lib/tools/external_stream_state.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
// Caches a state value which can be changed from anywhere
|
||||||
|
// Creates a provider interface that notices when the value changes
|
||||||
|
class ExternalStreamState<T> {
|
||||||
|
T currentState;
|
||||||
|
StreamController<T> streamController;
|
||||||
|
ExternalStreamState(T initialState)
|
||||||
|
: currentState = initialState,
|
||||||
|
streamController = StreamController<T>.broadcast();
|
||||||
|
void add(T newState) {
|
||||||
|
currentState = newState;
|
||||||
|
streamController.add(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoDisposeStreamProvider<T> provider() {
|
||||||
|
return AutoDisposeStreamProvider<T>((ref) async* {
|
||||||
|
if (await streamController.stream.isEmpty) {
|
||||||
|
yield currentState;
|
||||||
|
}
|
||||||
|
await for (final value in streamController.stream) {
|
||||||
|
yield value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
1
lib/tools/tools.dart
Normal file
1
lib/tools/tools.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export 'external_stream_state.dart';
|
|
@ -14,7 +14,7 @@ Future<String> getVeilidVersion() async {
|
||||||
|
|
||||||
// Initialize Veilid
|
// Initialize Veilid
|
||||||
// Call only once.
|
// Call only once.
|
||||||
void _init() {
|
void _initVeilid() {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
var platformConfig = VeilidWASMConfig(
|
var platformConfig = VeilidWASMConfig(
|
||||||
logging: VeilidWASMConfigLogging(
|
logging: VeilidWASMConfigLogging(
|
||||||
|
@ -44,7 +44,6 @@ void _init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from FlutterFlow stub initialize() function upon Main page load
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
Processor processor = Processor();
|
Processor processor = Processor();
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ Future<void> initializeVeilid() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init Veilid
|
// Init Veilid
|
||||||
_init();
|
_initVeilid();
|
||||||
|
|
||||||
// Startup Veilid
|
// Startup Veilid
|
||||||
await processor.startup();
|
await processor.startup();
|
||||||
|
|
|
@ -2,7 +2,8 @@ import 'dart:async';
|
||||||
import 'package:veilid/veilid.dart';
|
import 'package:veilid/veilid.dart';
|
||||||
import 'config.dart';
|
import 'config.dart';
|
||||||
import 'veilid_log.dart';
|
import 'veilid_log.dart';
|
||||||
import '../log/loggy.dart';
|
import '../log/log.dart';
|
||||||
|
import '../state/state.dart';
|
||||||
|
|
||||||
class Processor {
|
class Processor {
|
||||||
String _veilidVersion = "";
|
String _veilidVersion = "";
|
||||||
|
@ -57,48 +58,46 @@ class Processor {
|
||||||
//loggy.info("Attachment: ${updateAttachment.json}");
|
//loggy.info("Attachment: ${updateAttachment.json}");
|
||||||
|
|
||||||
// Set connection meter and ui state for connection state
|
// Set connection meter and ui state for connection state
|
||||||
var connectionState = "";
|
var cs = ConnectionState.detached;
|
||||||
var checkPublicInternet = false;
|
var checkPublicInternet = false;
|
||||||
switch (updateAttachment.state.state) {
|
switch (updateAttachment.state.state) {
|
||||||
case AttachmentState.detached:
|
case AttachmentState.detached:
|
||||||
connectionState = "detached";
|
cs = ConnectionState.detached;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.detaching:
|
case AttachmentState.detaching:
|
||||||
connectionState = "detaching";
|
cs = ConnectionState.detaching;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.attaching:
|
case AttachmentState.attaching:
|
||||||
connectionState = "attaching";
|
cs = ConnectionState.attaching;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.attachedWeak:
|
case AttachmentState.attachedWeak:
|
||||||
checkPublicInternet = true;
|
checkPublicInternet = true;
|
||||||
connectionState = "weak";
|
cs = ConnectionState.attachedWeak;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.attachedGood:
|
case AttachmentState.attachedGood:
|
||||||
checkPublicInternet = true;
|
checkPublicInternet = true;
|
||||||
connectionState = "good";
|
cs = ConnectionState.attachedGood;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.attachedStrong:
|
case AttachmentState.attachedStrong:
|
||||||
checkPublicInternet = true;
|
checkPublicInternet = true;
|
||||||
connectionState = "strong";
|
cs = ConnectionState.attachedStrong;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.fullyAttached:
|
case AttachmentState.fullyAttached:
|
||||||
checkPublicInternet = true;
|
checkPublicInternet = true;
|
||||||
connectionState = "full";
|
cs = ConnectionState.fullyAttached;
|
||||||
break;
|
break;
|
||||||
case AttachmentState.overAttached:
|
case AttachmentState.overAttached:
|
||||||
checkPublicInternet = true;
|
checkPublicInternet = true;
|
||||||
connectionState = "over";
|
cs = ConnectionState.overAttached;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (checkPublicInternet) {
|
if (checkPublicInternet) {
|
||||||
if (!updateAttachment.state.publicInternetReady) {
|
if (!updateAttachment.state.publicInternetReady) {
|
||||||
connectionState = "attaching";
|
cs = ConnectionState.attaching;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAppState().update(() {
|
globalConnectionState.add(cs);
|
||||||
FFAppState().ConnectionState = connectionState;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> processUpdateConfig(VeilidUpdateConfig updateConfig) async {
|
Future<void> processUpdateConfig(VeilidUpdateConfig updateConfig) async {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export 'config.dart';
|
||||||
|
export 'processor.dart';
|
||||||
|
export 'tools.dart';
|
||||||
|
export 'veilid_log.dart';
|
||||||
|
export 'init.dart';
|
41
setup_linux.sh
Executable file
41
setup_linux.sh
Executable file
|
@ -0,0 +1,41 @@
|
||||||
|
#!/bin/bash
|
||||||
|
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
source $SCRIPTDIR/_script_common
|
||||||
|
|
||||||
|
if [[ "$(uname)" != "Linux" ]]; then
|
||||||
|
echo Not running Linux
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(lsb_release -d | grep -qEi 'debian|buntu|mint')" ]; then
|
||||||
|
echo Not a supported Linux
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# # ensure unzip is installed
|
||||||
|
# if command -v unzip &> /dev/null; then
|
||||||
|
# echo '[X] unzip is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'unzip is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # ensure rsync is installed
|
||||||
|
# if command -v rsync &> /dev/null; then
|
||||||
|
# echo '[X] rsync is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'rsync is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # ensure sed is installed
|
||||||
|
# if command -v sed &> /dev/null; then
|
||||||
|
# echo '[X] sed is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'sed is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# run setup for veilid
|
||||||
|
$VEILIDDIR/setup_linux.sh
|
||||||
|
|
36
setup_macos.sh
Executable file
36
setup_macos.sh
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/bash
|
||||||
|
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
source $SCRIPTDIR/_script_common
|
||||||
|
|
||||||
|
if [[ "$(uname)" != "Darwin" ]]; then
|
||||||
|
echo Not running MacOS
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# # ensure unzip is installed
|
||||||
|
# if command -v unzip &> /dev/null; then
|
||||||
|
# echo '[X] unzip is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'unzip is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # ensure rsync is installed
|
||||||
|
# if command -v rsync &> /dev/null; then
|
||||||
|
# echo '[X] rsync is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'rsync is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # ensure sed is installed
|
||||||
|
# if command -v sed &> /dev/null; then
|
||||||
|
# echo '[X] sed is available in the path'
|
||||||
|
# else
|
||||||
|
# echo 'sed is not available in the path'
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# run setup for veilid
|
||||||
|
$VEILIDDIR/setup_macos.sh
|
||||||
|
|
25
wasm_update.sh
Executable file
25
wasm_update.sh
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
source $SCRIPTDIR/_script_common
|
||||||
|
|
||||||
|
pushd $SCRIPTDIR >/dev/null
|
||||||
|
|
||||||
|
# WASM output dir
|
||||||
|
WASMDIR=$SCRIPTDIR/web/wasm
|
||||||
|
|
||||||
|
# Build veilid-wasm, passing any arguments here to the build script
|
||||||
|
pushd $VEILIDDIR/veilid-wasm >/dev/null
|
||||||
|
PKGDIR=$(./wasm_build.sh $@ | grep SUCCESS:OUTPUTDIR | cut -d= -f2)
|
||||||
|
popd >/dev/null
|
||||||
|
|
||||||
|
# Copy wasm blob into place
|
||||||
|
echo Updating WASM from $PKGDIR to $WASMDIR
|
||||||
|
if [ -d $WASMDIR ]; then
|
||||||
|
rm -f $WASMDIR/*
|
||||||
|
fi
|
||||||
|
mkdir -p $WASMDIR
|
||||||
|
cp -f $PKGDIR/* $WASMDIR/
|
||||||
|
|
||||||
|
#### Done
|
||||||
|
|
||||||
|
popd >/dev/null
|
Loading…
Add table
Add a link
Reference in a new issue