mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 20:48:24 +00:00
wip
This commit is contained in:
@@ -24,7 +24,9 @@ linter:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
prefer_const_constructors: false
|
||||
prefer_const_constructors_in_immutables: false
|
||||
avoid_print: false
|
||||
prefer_const_literals_to_create_immutables: false
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+104
-104
@@ -1,28 +1,39 @@
|
||||
// ignore_for_file: prefer_const_literals_to_create_immutables, prefer_const_constructors
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/screens/app_detail_screen.dart';
|
||||
import 'package:zapstore/screens/profile_screen.dart';
|
||||
import 'package:zapstore/screens/settings_screen.dart';
|
||||
import 'package:zapstore/screens/updates_screen.dart';
|
||||
import 'package:zapstore/utils/extensions.dart';
|
||||
import 'package:zapstore/widgets/app_drawer.dart';
|
||||
import 'package:zapstore/screens/search_screen.dart';
|
||||
|
||||
void main() {
|
||||
runApp(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
configureRepositoryLocalStorage(
|
||||
clear: LocalStorageClearStrategy.always),
|
||||
localStorageProvider.overrideWithValue(
|
||||
LocalStorage(
|
||||
baseDirFn: () async {
|
||||
final path = (await getApplicationSupportDirectory()).path;
|
||||
print('initializing local storage at $path');
|
||||
return path;
|
||||
},
|
||||
clear: LocalStorageClearStrategy.whenError,
|
||||
),
|
||||
)
|
||||
],
|
||||
child: const ZapstoreApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const kBackgroundColor = Color.fromARGB(255, 6, 6, 6);
|
||||
|
||||
class ZapstoreApp extends StatelessWidget {
|
||||
const ZapstoreApp({super.key});
|
||||
|
||||
@@ -31,11 +42,17 @@ class ZapstoreApp extends StatelessWidget {
|
||||
return MaterialApp.router(
|
||||
routerConfig: goRouter,
|
||||
debugShowCheckedModeBanner: false,
|
||||
darkTheme: ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.lightBlue,
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: 'Inter',
|
||||
useMaterial3: true,
|
||||
scaffoldBackgroundColor: kBackgroundColor,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
// textTheme: context.theme.textTheme.copyWith(
|
||||
// bodyLarge:
|
||||
// TextStyle(color: Colors.white, fontWeight: FontWeight.w300))
|
||||
),
|
||||
theme: ThemeData.dark(useMaterial3: true),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -44,21 +61,16 @@ class ZapstoreApp extends StatelessWidget {
|
||||
final _rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||
final _searchNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'search');
|
||||
final _updatesNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'updates');
|
||||
final _profileNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'profile');
|
||||
final _notificationsNavigatorKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'notifications');
|
||||
final _settingsNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'settings');
|
||||
|
||||
final goRouter = GoRouter(
|
||||
initialLocation: '/search',
|
||||
initialLocation: '/',
|
||||
// * Passing a navigatorKey causes an issue on hot reload:
|
||||
// * https://github.com/flutter/flutter/issues/113757#issuecomment-1518421380
|
||||
// * However it's still necessary otherwise the navigator pops back to
|
||||
// * root on hot reload
|
||||
navigatorKey: _rootNavigatorKey,
|
||||
// debugLogDiagnostics: true,
|
||||
routes: [
|
||||
// Stateful navigation based on:
|
||||
// https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/stateful_shell_route.dart
|
||||
StatefulShellRoute.indexedStack(
|
||||
builder: (context, state, navigationShell) {
|
||||
return ScaffoldWithNestedNavigation(navigationShell: navigationShell);
|
||||
@@ -68,15 +80,19 @@ final goRouter = GoRouter(
|
||||
navigatorKey: _searchNavigatorKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/search',
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: SearchScreen(),
|
||||
),
|
||||
path: '/',
|
||||
builder: (context, state) => SearchScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'details',
|
||||
builder: (context, state) =>
|
||||
AppDetailScreen(release: state.extra as Release),
|
||||
AppDetailScreen(app: state.extra as App),
|
||||
// pageBuilder: (context, state) =>
|
||||
// buildWithSlideTransition<void>(
|
||||
// context: context,
|
||||
// state: state,
|
||||
// child: AppDetailScreen(app: state.extra as App),
|
||||
// ),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -87,41 +103,18 @@ final goRouter = GoRouter(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/updates',
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => {},
|
||||
child: const Text('Updates coming soon!'),
|
||||
),
|
||||
),
|
||||
),
|
||||
builder: (context, state) => UpdatesScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _profileNavigatorKey,
|
||||
navigatorKey: _settingsNavigatorKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/profile',
|
||||
path: '/settings',
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: Center(
|
||||
child: ProfileScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _notificationsNavigatorKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/notifications',
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => {},
|
||||
child: const Text('Notifications coming soon!'),
|
||||
),
|
||||
child: SettingsScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -133,12 +126,12 @@ final goRouter = GoRouter(
|
||||
);
|
||||
|
||||
final newInitializer = FutureProvider<void>((ref) async {
|
||||
await ref.read(repositoryInitializerProvider.future);
|
||||
await ref.read(ndk.frameProvider.notifier).initialize('wss://relay.damus.io');
|
||||
await ref.read(initializeFlutterData(adapterProvidersMap).future);
|
||||
// ref
|
||||
// .read(p.relayMessageNotifierProvider.notifier)
|
||||
// .initialize(['wss://relay.zap.store', 'wss://relay.nostr.band']);
|
||||
});
|
||||
|
||||
// Stateful navigation based on:
|
||||
// https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/stateful_shell_route.dart
|
||||
class ScaffoldWithNestedNavigation extends HookConsumerWidget {
|
||||
const ScaffoldWithNestedNavigation({
|
||||
Key? key,
|
||||
@@ -163,7 +156,7 @@ class ScaffoldWithNestedNavigation extends HookConsumerWidget {
|
||||
return SafeArea(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth < 450) {
|
||||
if (constraints.maxWidth < 550) {
|
||||
return ScaffoldWithNavigationBar(
|
||||
body: navigationShell,
|
||||
selectedIndex: navigationShell.currentIndex,
|
||||
@@ -197,34 +190,48 @@ class ScaffoldWithNavigationBar extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final initializer = ref.watch(newInitializer);
|
||||
return Scaffold(
|
||||
body: initializer.when(
|
||||
data: (_) => body,
|
||||
error: (e, _) => const Text('error'),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
|
||||
child: initializer.when(
|
||||
data: (_) => body,
|
||||
error: (e, _) => const Text('error'),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
height: 60,
|
||||
backgroundColor: kBackgroundColor,
|
||||
indicatorColor: Colors.transparent,
|
||||
selectedIndex: selectedIndex,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||
destinations: const [
|
||||
NavigationDestination(
|
||||
label: 'Search',
|
||||
icon: Icon(Icons.search_outlined),
|
||||
label: 'Home',
|
||||
icon: Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(Icons.home_filled),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'Updates',
|
||||
icon: Icon(Icons.download_for_offline_outlined),
|
||||
selectedIcon: Icon(Icons.download_for_offline),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'Profile',
|
||||
icon: Icon(Icons.person_outline),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'Notifications',
|
||||
icon: Icon(Icons.notifications_outlined),
|
||||
label: 'Settings',
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
selectedIcon: Icon(Icons.settings),
|
||||
),
|
||||
],
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
),
|
||||
drawer: Drawer(
|
||||
child: Column(
|
||||
children: [
|
||||
DrawerHeader(
|
||||
child: AppDrawer(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -262,10 +269,6 @@ class ScaffoldWithNavigationRail extends StatelessWidget {
|
||||
label: Text('Profile'),
|
||||
icon: Icon(Icons.person_outline),
|
||||
),
|
||||
const NavigationRailDestination(
|
||||
label: Text('Notifications'),
|
||||
icon: Icon(Icons.notifications_outlined),
|
||||
),
|
||||
],
|
||||
),
|
||||
body,
|
||||
@@ -275,37 +278,34 @@ class ScaffoldWithNavigationRail extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// const borderColor = Color(0xFF805306);
|
||||
|
||||
// const sidebarColor = Color(0xFFF6A00C);
|
||||
// const backgroundStartColor = Color(0xFFFFD500);
|
||||
// const backgroundEndColor = Color(0xFFF6A00C);
|
||||
|
||||
// final buttonColors = WindowButtonColors(
|
||||
// iconNormal: const Color(0xFF805306),
|
||||
// mouseOver: const Color(0xFFF6A00C),
|
||||
// mouseDown: const Color(0xFF805306),
|
||||
// iconMouseOver: const Color(0xFF805306),
|
||||
// iconMouseDown: const Color(0xFFFFD500));
|
||||
|
||||
// final closeButtonColors = WindowButtonColors(
|
||||
// mouseOver: const Color(0xFFD32F2F),
|
||||
// mouseDown: const Color(0xFFB71C1C),
|
||||
// iconNormal: const Color(0xFF805306),
|
||||
// iconMouseOver: Colors.white);
|
||||
|
||||
// class WindowButtons extends StatelessWidget {
|
||||
// const WindowButtons({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Row(
|
||||
// children: [
|
||||
// MinimizeWindowButton(colors: buttonColors),
|
||||
// MaximizeWindowButton(colors: buttonColors),
|
||||
// CloseWindowButton(colors: closeButtonColors),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
CustomTransitionPage buildWithSlideTransition<T>({
|
||||
required BuildContext context,
|
||||
required GoRouterState state,
|
||||
required Widget child,
|
||||
}) {
|
||||
return CustomTransitionPage<T>(
|
||||
key: state.pageKey,
|
||||
child: child,
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
return SlideTransition(
|
||||
position:
|
||||
Tween<Offset>(begin: const Offset(1, 0), end: Offset.zero).animate(
|
||||
CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
color: context.theme.colorScheme.background,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+16
-55
@@ -4,70 +4,31 @@
|
||||
// ignore_for_file: directives_ordering, top_level_function_literal_block, depend_on_referenced_packages
|
||||
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/file_metadata.dart';
|
||||
import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
|
||||
// ignore: prefer_function_declarations_over_variables
|
||||
ConfigureRepositoryLocalStorage configureRepositoryLocalStorage = ({FutureFn<String>? baseDirFn, List<int>? encryptionKey, LocalStorageClearStrategy? clear}) {
|
||||
if (!kIsWeb) {
|
||||
baseDirFn ??= () => getApplicationDocumentsDirectory().then((dir) => dir.path);
|
||||
} else {
|
||||
baseDirFn ??= () => '';
|
||||
}
|
||||
|
||||
return hiveLocalStorageProvider.overrideWith(
|
||||
(ref) => HiveLocalStorage(
|
||||
hive: ref.read(hiveProvider),
|
||||
baseDirFn: baseDirFn,
|
||||
encryptionKey: encryptionKey,
|
||||
clear: clear,
|
||||
),
|
||||
);
|
||||
final adapterProvidersMap = <String, Provider<Adapter<DataModelMixin>>>{
|
||||
'apps': appsAdapterProvider,
|
||||
'fileMetadata': fileMetadataAdapterProvider,
|
||||
'releases': releasesAdapterProvider,
|
||||
'users': usersAdapterProvider
|
||||
};
|
||||
|
||||
final repositoryProviders = <String, Provider<Repository<DataModelMixin>>>{
|
||||
'fileMetadata': fileMetadataRepositoryProvider,
|
||||
'releases': releasesRepositoryProvider,
|
||||
'users': usersRepositoryProvider
|
||||
};
|
||||
|
||||
final repositoryInitializerProvider =
|
||||
FutureProvider<RepositoryInitializer>((ref) async {
|
||||
DataHelpers.setInternalType<FileMetadata>('fileMetadata');
|
||||
DataHelpers.setInternalType<Release>('releases');
|
||||
DataHelpers.setInternalType<User>('users');
|
||||
final adapters = <String, RemoteAdapter>{'fileMetadata': ref.watch(internalFileMetadataRemoteAdapterProvider), 'releases': ref.watch(internalReleasesRemoteAdapterProvider), 'users': ref.watch(internalUsersRemoteAdapterProvider)};
|
||||
final remotes = <String, bool>{'fileMetadata': true, 'releases': true, 'users': true};
|
||||
|
||||
await ref.watch(graphNotifierProvider).initialize();
|
||||
|
||||
// initialize and register
|
||||
for (final type in repositoryProviders.keys) {
|
||||
final repository = ref.read(repositoryProviders[type]!);
|
||||
repository.dispose();
|
||||
await repository.initialize(
|
||||
remote: remotes[type],
|
||||
adapters: adapters,
|
||||
);
|
||||
internalRepositories[type] = repository;
|
||||
}
|
||||
|
||||
return RepositoryInitializer();
|
||||
});
|
||||
extension RepositoryWidgetRefX on WidgetRef {
|
||||
Repository<FileMetadata> get fileMetadata => watch(fileMetadataRepositoryProvider)..remoteAdapter.internalWatch = watch;
|
||||
Repository<Release> get releases => watch(releasesRepositoryProvider)..remoteAdapter.internalWatch = watch;
|
||||
Repository<User> get users => watch(usersRepositoryProvider)..remoteAdapter.internalWatch = watch;
|
||||
extension AdapterWidgetRefX on WidgetRef {
|
||||
Adapter<App> get apps => watch(appsAdapterProvider)..internalWatch = watch;
|
||||
Adapter<FileMetadata> get fileMetadata => watch(fileMetadataAdapterProvider)..internalWatch = watch;
|
||||
Adapter<Release> get releases => watch(releasesAdapterProvider)..internalWatch = watch;
|
||||
Adapter<User> get users => watch(usersAdapterProvider)..internalWatch = watch;
|
||||
}
|
||||
|
||||
extension RepositoryRefX on Ref {
|
||||
extension AdapterRefX on Ref {
|
||||
|
||||
Repository<FileMetadata> get fileMetadata => watch(fileMetadataRepositoryProvider)..remoteAdapter.internalWatch = watch as Watcher;
|
||||
Repository<Release> get releases => watch(releasesRepositoryProvider)..remoteAdapter.internalWatch = watch as Watcher;
|
||||
Repository<User> get users => watch(usersRepositoryProvider)..remoteAdapter.internalWatch = watch as Watcher;
|
||||
Adapter<App> get apps => watch(appsAdapterProvider)..internalWatch = watch as Watcher;
|
||||
Adapter<FileMetadata> get fileMetadata => watch(fileMetadataAdapterProvider)..internalWatch = watch as Watcher;
|
||||
Adapter<Release> get releases => watch(releasesAdapterProvider)..internalWatch = watch as Watcher;
|
||||
Adapter<User> get users => watch(usersAdapterProvider)..internalWatch = watch as Watcher;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:purplebase/purplebase.dart';
|
||||
import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
|
||||
part 'app.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
@DataAdapter([NostrAdapter, CoolAdapter])
|
||||
class App extends ZapstoreEvent<App> with BaseApp, EquatableMixin {
|
||||
late final HasMany<Release> releases;
|
||||
App();
|
||||
factory App.fromJson(Map<String, dynamic> json) => _$AppFromJson(json);
|
||||
@override
|
||||
List<Object?> get props => [id];
|
||||
}
|
||||
|
||||
mixin CoolAdapter on Adapter<App> {
|
||||
@override
|
||||
Future<void> onInitialized() async {
|
||||
// db.execute('''
|
||||
// CREATE TABLE IF NOT EXISTS $internalType (
|
||||
// key INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
// data TEXT
|
||||
// );
|
||||
// ''');
|
||||
super.onInitialized();
|
||||
}
|
||||
|
||||
Future<List<App>> query(int kind) async {
|
||||
final result = db.select(
|
||||
"SELECT data FROM $internalType WHERE json_extract(data, '\$.kind') = ?",
|
||||
[kind]);
|
||||
return deserializeFromResult(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// AdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, duplicate_ignore
|
||||
|
||||
mixin _$AppAdapter on Adapter<App> {
|
||||
static final Map<String, RelationshipMeta> _kAppRelationshipMetas = {
|
||||
'releases': RelationshipMeta<Release>(
|
||||
name: 'releases',
|
||||
inverseName: 'app',
|
||||
type: 'releases',
|
||||
kind: 'HasMany',
|
||||
instance: (_) => (_ as App).releases,
|
||||
)
|
||||
};
|
||||
|
||||
@override
|
||||
Map<String, RelationshipMeta> get relationshipMetas => _kAppRelationshipMetas;
|
||||
|
||||
@override
|
||||
App deserializeLocal(map, {String? key}) {
|
||||
map = transformDeserialize(map);
|
||||
return internalWrapStopInit(() => App.fromJson(map), key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> serializeLocal(model, {bool withRelationships = true}) {
|
||||
final map = _$AppToJson(model);
|
||||
return transformSerialize(map, withRelationships: withRelationships);
|
||||
}
|
||||
}
|
||||
|
||||
final _appsFinders = <String, dynamic>{};
|
||||
|
||||
class $AppAdapter = Adapter<App>
|
||||
with _$AppAdapter, NostrAdapter<App>, CoolAdapter;
|
||||
|
||||
final appsAdapterProvider = Provider<Adapter<App>>(
|
||||
(ref) => $AppAdapter(ref, InternalHolder(_appsFinders)));
|
||||
|
||||
extension AppAdapterX on Adapter<App> {
|
||||
NostrAdapter<App> get nostrAdapter => this as NostrAdapter<App>;
|
||||
CoolAdapter get coolAdapter => this as CoolAdapter;
|
||||
}
|
||||
|
||||
extension AppRelationshipGraphNodeX on RelationshipGraphNode<App> {
|
||||
RelationshipGraphNode<Release> get releases {
|
||||
final meta = _$AppAdapter._kAppRelationshipMetas['releases']
|
||||
as RelationshipMeta<Release>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
App _$AppFromJson(Map<String, dynamic> json) => App()
|
||||
..id = json['id']
|
||||
..pubkey = json['pubkey'] as String
|
||||
..createdAt = DateTime.parse(json['createdAt'] as String)
|
||||
..content = json['content'] as String
|
||||
..kind = json['kind'] as int
|
||||
..tags = (json['tags'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
|
||||
.toList()
|
||||
..signature = json['signature'] as String?
|
||||
..releases =
|
||||
HasMany<Release>.fromJson(json['releases'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$AppToJson(App instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'pubkey': instance.pubkey,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'content': instance.content,
|
||||
'kind': instance.kind,
|
||||
'tags': instance.tags,
|
||||
'signature': instance.signature,
|
||||
'releases': instance.releases,
|
||||
};
|
||||
@@ -4,7 +4,8 @@ import 'package:android_package_installer/android_package_installer.dart';
|
||||
import 'package:android_package_manager/android_package_manager.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:purplebase/purplebase.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:zapstore/models/release.dart';
|
||||
@@ -16,17 +17,9 @@ part 'file_metadata.g.dart';
|
||||
|
||||
final packageManager = AndroidPackageManager();
|
||||
|
||||
@DataRepository([NostrAdapter, FileMetadataAdapter],
|
||||
fromJson: 'FileMetadata.fromMapFactory(map)', toJson: 'model.toMap()')
|
||||
class FileMetadata extends BaseEvent<FileMetadata> with ndk.FileMetadata {
|
||||
FileMetadata.fromMap(super.map) : super.fromMap();
|
||||
|
||||
factory FileMetadata.fromMapFactory(Map<String, dynamic> map) {
|
||||
final m = FileMetadata.fromMap(map);
|
||||
m.author = BelongsTo<User>.fromJson(map['author']);
|
||||
return m;
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@DataAdapter([NostrAdapter, FileMetadataAdapter])
|
||||
class FileMetadata extends ZapstoreEvent<FileMetadata> with BaseFileMetadata {
|
||||
late final BelongsTo<User> author;
|
||||
late final BelongsTo<Release> release = BelongsTo();
|
||||
|
||||
@@ -66,16 +59,14 @@ class FileMetadata extends BaseEvent<FileMetadata> with ndk.FileMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
mixin FileMetadataAdapter on RemoteAdapter<FileMetadata> {
|
||||
mixin FileMetadataAdapter on Adapter<FileMetadata> {
|
||||
@override
|
||||
Future<DeserializedData<FileMetadata>> deserialize(Object? data) async {
|
||||
DeserializedData<FileMetadata> deserialize(Object? data, {String? key}) {
|
||||
final list = data is Iterable ? data : [data as Map];
|
||||
for (final e in list) {
|
||||
final map = e as Map<String, dynamic>;
|
||||
map['author'] = map['pubkey'];
|
||||
}
|
||||
final result = await super.deserialize(data);
|
||||
print(result);
|
||||
return result;
|
||||
return super.deserialize(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
part of 'file_metadata.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RepositoryGenerator
|
||||
// AdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, duplicate_ignore
|
||||
|
||||
mixin $FileMetadataLocalAdapter on LocalAdapter<FileMetadata> {
|
||||
mixin _$FileMetadataAdapter on Adapter<FileMetadata> {
|
||||
static final Map<String, RelationshipMeta> _kFileMetadataRelationshipMetas = {
|
||||
'author': RelationshipMeta<User>(
|
||||
name: 'author',
|
||||
@@ -30,56 +30,74 @@ mixin $FileMetadataLocalAdapter on LocalAdapter<FileMetadata> {
|
||||
_kFileMetadataRelationshipMetas;
|
||||
|
||||
@override
|
||||
FileMetadata deserialize(map) {
|
||||
FileMetadata deserializeLocal(map, {String? key}) {
|
||||
map = transformDeserialize(map);
|
||||
return FileMetadata.fromMapFactory(map);
|
||||
return internalWrapStopInit(() => _$FileMetadataFromJson(map), key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> serialize(model, {bool withRelationships = true}) {
|
||||
final map = model.toMap();
|
||||
Map<String, dynamic> serializeLocal(model, {bool withRelationships = true}) {
|
||||
final map = _$FileMetadataToJson(model);
|
||||
return transformSerialize(map, withRelationships: withRelationships);
|
||||
}
|
||||
}
|
||||
|
||||
final _fileMetadataFinders = <String, dynamic>{};
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class $FileMetadataHiveLocalAdapter = HiveLocalAdapter<FileMetadata>
|
||||
with $FileMetadataLocalAdapter;
|
||||
class $FileMetadataAdapter = Adapter<FileMetadata>
|
||||
with _$FileMetadataAdapter, NostrAdapter<FileMetadata>, FileMetadataAdapter;
|
||||
|
||||
class $FileMetadataRemoteAdapter = RemoteAdapter<FileMetadata>
|
||||
with NostrAdapter<FileMetadata>, FileMetadataAdapter;
|
||||
final fileMetadataAdapterProvider = Provider<Adapter<FileMetadata>>(
|
||||
(ref) => $FileMetadataAdapter(ref, InternalHolder(_fileMetadataFinders)));
|
||||
|
||||
final internalFileMetadataRemoteAdapterProvider =
|
||||
Provider<RemoteAdapter<FileMetadata>>((ref) => $FileMetadataRemoteAdapter(
|
||||
$FileMetadataHiveLocalAdapter(ref),
|
||||
InternalHolder(_fileMetadataFinders)));
|
||||
|
||||
final fileMetadataRepositoryProvider =
|
||||
Provider<Repository<FileMetadata>>((ref) => Repository<FileMetadata>(ref));
|
||||
|
||||
extension FileMetadataDataRepositoryX on Repository<FileMetadata> {
|
||||
extension FileMetadataAdapterX on Adapter<FileMetadata> {
|
||||
NostrAdapter<FileMetadata> get nostrAdapter =>
|
||||
remoteAdapter as NostrAdapter<FileMetadata>;
|
||||
FileMetadataAdapter get fileMetadataAdapter =>
|
||||
remoteAdapter as FileMetadataAdapter;
|
||||
this as NostrAdapter<FileMetadata>;
|
||||
FileMetadataAdapter get fileMetadataAdapter => this as FileMetadataAdapter;
|
||||
}
|
||||
|
||||
extension FileMetadataRelationshipGraphNodeX
|
||||
on RelationshipGraphNode<FileMetadata> {
|
||||
RelationshipGraphNode<User> get author {
|
||||
final meta = $FileMetadataLocalAdapter
|
||||
._kFileMetadataRelationshipMetas['author'] as RelationshipMeta<User>;
|
||||
final meta = _$FileMetadataAdapter._kFileMetadataRelationshipMetas['author']
|
||||
as RelationshipMeta<User>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<Release> get release {
|
||||
final meta =
|
||||
$FileMetadataLocalAdapter._kFileMetadataRelationshipMetas['release']
|
||||
_$FileMetadataAdapter._kFileMetadataRelationshipMetas['release']
|
||||
as RelationshipMeta<Release>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
FileMetadata _$FileMetadataFromJson(Map<String, dynamic> json) => FileMetadata()
|
||||
..id = json['id']
|
||||
..pubkey = json['pubkey'] as String
|
||||
..createdAt = DateTime.parse(json['createdAt'] as String)
|
||||
..content = json['content'] as String
|
||||
..kind = json['kind'] as int
|
||||
..tags = (json['tags'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
|
||||
.toList()
|
||||
..signature = json['signature'] as String?
|
||||
..author = BelongsTo<User>.fromJson(json['author'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$FileMetadataToJson(FileMetadata instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'pubkey': instance.pubkey,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'content': instance.content,
|
||||
'kind': instance.kind,
|
||||
'tags': instance.tags,
|
||||
'signature': instance.signature,
|
||||
'author': instance.author,
|
||||
};
|
||||
|
||||
+18
-19
@@ -1,29 +1,22 @@
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:purplebase/purplebase.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/file_metadata.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
|
||||
part 'release.g.dart';
|
||||
|
||||
@DataRepository([NostrAdapter, ReleaseAdapter],
|
||||
fromJson: 'Release.fromMapFactory(map)', toJson: 'model.toMap()')
|
||||
class Release extends BaseEvent<Release> with ndk.Release {
|
||||
Release.fromMap(super.map) : super.fromMap();
|
||||
|
||||
String get identifier => tagMap['i']!.first;
|
||||
|
||||
factory Release.fromMapFactory(Map<String, dynamic> map) {
|
||||
final m = Release.fromMap(map);
|
||||
m.artifacts = HasMany<FileMetadata>.fromJson(map['artifacts']);
|
||||
return m;
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@DataAdapter([NostrAdapter, ReleaseAdapter])
|
||||
class Release extends ZapstoreEvent<Release> with BaseRelease {
|
||||
late final HasMany<FileMetadata> artifacts;
|
||||
late final BelongsTo<App> app;
|
||||
}
|
||||
|
||||
mixin ReleaseAdapter on RemoteAdapter<Release> {
|
||||
mixin ReleaseAdapter on Adapter<Release> {
|
||||
@override
|
||||
Future<DeserializedData<Release>> deserialize(Object? data) {
|
||||
DeserializedData<Release> deserialize(Object? data, {String? key}) {
|
||||
final list = data is Iterable ? data : [data as Map];
|
||||
for (final e in list) {
|
||||
final map = e as Map<String, dynamic>;
|
||||
@@ -31,9 +24,15 @@ mixin ReleaseAdapter on RemoteAdapter<Release> {
|
||||
.where((e) => e.first == 'e')
|
||||
.map((e) => e[1].toString());
|
||||
map['artifacts'] = eTags.toList();
|
||||
final appIdentifier =
|
||||
map['tags'].firstWhere((t) => t.first == 'd')[1].split('@').first;
|
||||
map['app'] = (adapters['apps'] as Adapter<App>)
|
||||
.findAllLocal()
|
||||
.where((a) => a.identifier == appIdentifier)
|
||||
// ignore: invalid_use_of_visible_for_testing_member
|
||||
.safeFirst
|
||||
?.id;
|
||||
}
|
||||
final result = super.deserialize(data);
|
||||
|
||||
return result;
|
||||
return super.deserialize(data);
|
||||
}
|
||||
}
|
||||
|
||||
+58
-23
@@ -3,12 +3,12 @@
|
||||
part of 'release.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RepositoryGenerator
|
||||
// AdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, duplicate_ignore
|
||||
|
||||
mixin $ReleaseLocalAdapter on LocalAdapter<Release> {
|
||||
mixin _$ReleaseAdapter on Adapter<Release> {
|
||||
static final Map<String, RelationshipMeta> _kReleaseRelationshipMetas = {
|
||||
'artifacts': RelationshipMeta<FileMetadata>(
|
||||
name: 'artifacts',
|
||||
@@ -16,6 +16,13 @@ mixin $ReleaseLocalAdapter on LocalAdapter<Release> {
|
||||
type: 'fileMetadata',
|
||||
kind: 'HasMany',
|
||||
instance: (_) => (_ as Release).artifacts,
|
||||
),
|
||||
'app': RelationshipMeta<App>(
|
||||
name: 'app',
|
||||
inverseName: 'releases',
|
||||
type: 'apps',
|
||||
kind: 'BelongsTo',
|
||||
instance: (_) => (_ as Release).app,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -24,45 +31,73 @@ mixin $ReleaseLocalAdapter on LocalAdapter<Release> {
|
||||
_kReleaseRelationshipMetas;
|
||||
|
||||
@override
|
||||
Release deserialize(map) {
|
||||
Release deserializeLocal(map, {String? key}) {
|
||||
map = transformDeserialize(map);
|
||||
return Release.fromMapFactory(map);
|
||||
return internalWrapStopInit(() => _$ReleaseFromJson(map), key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> serialize(model, {bool withRelationships = true}) {
|
||||
final map = model.toMap();
|
||||
Map<String, dynamic> serializeLocal(model, {bool withRelationships = true}) {
|
||||
final map = _$ReleaseToJson(model);
|
||||
return transformSerialize(map, withRelationships: withRelationships);
|
||||
}
|
||||
}
|
||||
|
||||
final _releasesFinders = <String, dynamic>{};
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class $ReleaseHiveLocalAdapter = HiveLocalAdapter<Release>
|
||||
with $ReleaseLocalAdapter;
|
||||
class $ReleaseAdapter = Adapter<Release>
|
||||
with _$ReleaseAdapter, NostrAdapter<Release>, ReleaseAdapter;
|
||||
|
||||
class $ReleaseRemoteAdapter = RemoteAdapter<Release>
|
||||
with NostrAdapter<Release>, ReleaseAdapter;
|
||||
final releasesAdapterProvider = Provider<Adapter<Release>>(
|
||||
(ref) => $ReleaseAdapter(ref, InternalHolder(_releasesFinders)));
|
||||
|
||||
final internalReleasesRemoteAdapterProvider = Provider<RemoteAdapter<Release>>(
|
||||
(ref) => $ReleaseRemoteAdapter(
|
||||
$ReleaseHiveLocalAdapter(ref), InternalHolder(_releasesFinders)));
|
||||
|
||||
final releasesRepositoryProvider =
|
||||
Provider<Repository<Release>>((ref) => Repository<Release>(ref));
|
||||
|
||||
extension ReleaseDataRepositoryX on Repository<Release> {
|
||||
NostrAdapter<Release> get nostrAdapter =>
|
||||
remoteAdapter as NostrAdapter<Release>;
|
||||
ReleaseAdapter get releaseAdapter => remoteAdapter as ReleaseAdapter;
|
||||
extension ReleaseAdapterX on Adapter<Release> {
|
||||
NostrAdapter<Release> get nostrAdapter => this as NostrAdapter<Release>;
|
||||
ReleaseAdapter get releaseAdapter => this as ReleaseAdapter;
|
||||
}
|
||||
|
||||
extension ReleaseRelationshipGraphNodeX on RelationshipGraphNode<Release> {
|
||||
RelationshipGraphNode<FileMetadata> get artifacts {
|
||||
final meta = $ReleaseLocalAdapter._kReleaseRelationshipMetas['artifacts']
|
||||
final meta = _$ReleaseAdapter._kReleaseRelationshipMetas['artifacts']
|
||||
as RelationshipMeta<FileMetadata>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<App> get app {
|
||||
final meta = _$ReleaseAdapter._kReleaseRelationshipMetas['app']
|
||||
as RelationshipMeta<App>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Release _$ReleaseFromJson(Map<String, dynamic> json) => Release()
|
||||
..id = json['id']
|
||||
..pubkey = json['pubkey'] as String
|
||||
..createdAt = DateTime.parse(json['createdAt'] as String)
|
||||
..content = json['content'] as String
|
||||
..kind = json['kind'] as int
|
||||
..tags = (json['tags'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
|
||||
.toList()
|
||||
..signature = json['signature'] as String?
|
||||
..artifacts =
|
||||
HasMany<FileMetadata>.fromJson(json['artifacts'] as Map<String, dynamic>)
|
||||
..app = BelongsTo<App>.fromJson(json['app'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$ReleaseToJson(Release instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'pubkey': instance.pubkey,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'content': instance.content,
|
||||
'kind': instance.kind,
|
||||
'tags': instance.tags,
|
||||
'signature': instance.signature,
|
||||
'artifacts': instance.artifacts,
|
||||
'app': instance.app,
|
||||
};
|
||||
|
||||
+53
-69
@@ -1,23 +1,19 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:purplebase/purplebase.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
abstract class BaseEvent<T extends BaseEvent<T>> = ndk.Event
|
||||
abstract class ZapstoreEvent<T extends ZapstoreEvent<T>> = BaseEvent
|
||||
with DataModelMixin<T>;
|
||||
|
||||
@DataRepository([NostrAdapter, UserAdapter],
|
||||
fromJson: 'User.fromMapFactory(map)', toJson: 'model.toMap()')
|
||||
class User extends BaseEvent<User> with ndk.User, EquatableMixin {
|
||||
@JsonSerializable()
|
||||
@DataAdapter([NostrAdapter, UserAdapter])
|
||||
class User extends ZapstoreEvent<User> with BaseUser, EquatableMixin {
|
||||
User();
|
||||
User.fromMap(super.map) : super.fromMap();
|
||||
|
||||
factory User.fromMapFactory(Map<String, dynamic> map) {
|
||||
final user = User.fromMap(map);
|
||||
user.following = HasMany<User>.fromJson(map['following']);
|
||||
return user;
|
||||
}
|
||||
|
||||
@DataRelationship(inverse: 'followers')
|
||||
late final HasMany<User> following;
|
||||
@DataRelationship(inverse: 'following')
|
||||
@@ -29,14 +25,14 @@ class User extends BaseEvent<User> with ndk.User, EquatableMixin {
|
||||
|
||||
mixin UserAdapter on NostrAdapter<User> {
|
||||
@override
|
||||
Future<DeserializedData<User>> deserialize(Object? data) async {
|
||||
DeserializedData<User> deserialize(Object? data, {String? key}) {
|
||||
final users = <User>[];
|
||||
final list = data is Iterable ? data : [data as Map];
|
||||
for (final e in list) {
|
||||
final map = e as Map<String, dynamic>;
|
||||
if (map['kind'] == 3) {
|
||||
for (final [_, id, ..._] in map['tags'] as Iterable) {
|
||||
users.add(User.fromMapFactory({
|
||||
users.add(User.fromMap({
|
||||
'id': id,
|
||||
'content': '',
|
||||
'pubkey': id,
|
||||
@@ -57,7 +53,7 @@ mixin UserAdapter on NostrAdapter<User> {
|
||||
if (users.isNotEmpty) {
|
||||
map['following'] = users.map((e) => e.id).toList();
|
||||
}
|
||||
final data0 = await super.deserialize(map);
|
||||
final data0 = super.deserialize(map);
|
||||
final user = data0.model;
|
||||
if (user != null) {
|
||||
users.add(user);
|
||||
@@ -70,86 +66,80 @@ mixin UserAdapter on NostrAdapter<User> {
|
||||
|
||||
@override
|
||||
Future<User?> findOne(Object id,
|
||||
{bool? remote,
|
||||
bool? background,
|
||||
{bool remote = true,
|
||||
bool background = false,
|
||||
Map<String, dynamic>? params,
|
||||
Map<String, String>? headers,
|
||||
OnSuccessOne<User>? onSuccess,
|
||||
OnErrorOne<User>? onError,
|
||||
DataRequestLabel? label}) async {
|
||||
if (remote == false) {
|
||||
final key = graph.getKeyForId(internalType, id.toString());
|
||||
return localAdapter.findOne(key!);
|
||||
String? publicKey;
|
||||
try {
|
||||
publicKey = '$id'.hexKey;
|
||||
} catch (_) {
|
||||
String username;
|
||||
String domain;
|
||||
try {
|
||||
[username, domain] = id.toString().split('@');
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
publicKey = await sendRequest<dynamic>(
|
||||
Uri.parse('https://$domain/.well-known/nostr.json?name=$username'),
|
||||
onSuccess: (response, label) {
|
||||
return (response.body as Map)['names']?[username];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final req = ndk.Req(
|
||||
authors: {id.toString()},
|
||||
if (publicKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final req = RelayRequest(
|
||||
authors: {publicKey},
|
||||
kinds: {kind, ...?params?.remove('kinds')},
|
||||
tags: params ?? {},
|
||||
);
|
||||
|
||||
print('[findOne] req $req');
|
||||
final result = await notifier.query(req);
|
||||
final data = await deserialize(result);
|
||||
|
||||
for (final m in data.models) {
|
||||
print('saving model of kind ${m.kind}: ${m.content}');
|
||||
m.init().saveLocal();
|
||||
}
|
||||
return data.models.firstWhere((e) => e.id == id);
|
||||
final result =
|
||||
await notifier.query(req, relayUrls: ['wss://relay.nostr.band']);
|
||||
final data = deserialize(result);
|
||||
return data.model?..saveLocal();
|
||||
}
|
||||
}
|
||||
|
||||
mixin NostrAdapter<T extends BaseEvent<T>> on RemoteAdapter<T> {
|
||||
// void Function()? _closeSub;
|
||||
late final ndk.FrameNotifier notifier;
|
||||
|
||||
@override
|
||||
Future<void> onInitialized() async {
|
||||
notifier = ref.read(ndk.frameProvider.notifier);
|
||||
// _closeSub = notifier.addListener((state) async {
|
||||
// if (state is ndk.EventFrame &&
|
||||
// typeKind[internalType] == state.event['kind']) {
|
||||
// final data = await deserialize(state.event);
|
||||
// for (final m in [...data.models, ...data.included]) {
|
||||
// print(
|
||||
// 'r deserialize/save (k:${(m as BaseEvent).kind}) ${m.id} for type $internalType');
|
||||
// // DataModel.adapterFor(m).saveLocal(m.init());
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
super.onInitialized();
|
||||
}
|
||||
|
||||
// @override
|
||||
// void dispose() {
|
||||
// _closeSub?.call();
|
||||
// super.dispose();
|
||||
// }
|
||||
mixin NostrAdapter<T extends ZapstoreEvent<T>> on Adapter<T> {
|
||||
late final RelayMessageNotifier notifier =
|
||||
ref.read(relayMessageNotifierProvider.notifier);
|
||||
|
||||
Map<int, String> kindType = {
|
||||
0: 'users',
|
||||
3: 'users',
|
||||
1063: 'fileMetadata',
|
||||
30063: 'releases',
|
||||
32267: 'apps'
|
||||
};
|
||||
|
||||
@override
|
||||
Future<DeserializedData<T>> deserialize(Object? data) async {
|
||||
DeserializedData<T> deserialize(Object? data, {String? key}) {
|
||||
final list = data is Iterable ? data : [data as Map];
|
||||
final models = <T>[];
|
||||
final included = <DataModelMixin>[];
|
||||
|
||||
for (final e in list) {
|
||||
final map = e as Map<String, dynamic>;
|
||||
map['createdAt'] =
|
||||
DateTime.fromMillisecondsSinceEpoch(map['created_at'] * 1000)
|
||||
.toIso8601String();
|
||||
final kind = map['kind'] as int;
|
||||
final xType = kindType[kind];
|
||||
if (xType != null) {
|
||||
if (xType == internalType) {
|
||||
final newData = await super.deserialize(map);
|
||||
final newData = super.deserialize(map);
|
||||
models.addAll(newData.models as Iterable<T>);
|
||||
} else {
|
||||
final newData = await adapters[xType]!.deserialize(map);
|
||||
final newData = adapters[xType]!.deserialize(map);
|
||||
included.addAll(newData.models as Iterable<DataModelMixin>);
|
||||
}
|
||||
}
|
||||
@@ -171,22 +161,16 @@ mixin NostrAdapter<T extends BaseEvent<T>> on RemoteAdapter<T> {
|
||||
OnErrorAll<T>? onError,
|
||||
DataRequestLabel? label}) async {
|
||||
if (remote == false) {
|
||||
return localAdapter.findAll();
|
||||
return findAllLocal();
|
||||
}
|
||||
|
||||
final req = ndk.Req(
|
||||
final req = RelayRequest(
|
||||
kinds: {kind, ...?params?.remove('kinds')},
|
||||
tags: params ?? {},
|
||||
);
|
||||
|
||||
print('req $req');
|
||||
final result = await notifier.query(req);
|
||||
final data = await deserialize(result);
|
||||
print('req result ${data.models.length}');
|
||||
for (final m in data.models) {
|
||||
print('saving model of kind ${m.kind}: ${m.content}');
|
||||
m.init().saveLocal();
|
||||
}
|
||||
return data.models;
|
||||
final deserialized = await deserializeAsync(result, save: true);
|
||||
return deserialized.models;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-22
@@ -3,12 +3,12 @@
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RepositoryGenerator
|
||||
// AdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, duplicate_ignore
|
||||
|
||||
mixin $UserLocalAdapter on LocalAdapter<User> {
|
||||
mixin _$UserAdapter on Adapter<User> {
|
||||
static final Map<String, RelationshipMeta> _kUserRelationshipMetas = {
|
||||
'following': RelationshipMeta<User>(
|
||||
name: 'following',
|
||||
@@ -31,50 +31,71 @@ mixin $UserLocalAdapter on LocalAdapter<User> {
|
||||
_kUserRelationshipMetas;
|
||||
|
||||
@override
|
||||
User deserialize(map) {
|
||||
User deserializeLocal(map, {String? key}) {
|
||||
map = transformDeserialize(map);
|
||||
return User.fromMapFactory(map);
|
||||
return internalWrapStopInit(() => _$UserFromJson(map), key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> serialize(model, {bool withRelationships = true}) {
|
||||
final map = model.toMap();
|
||||
Map<String, dynamic> serializeLocal(model, {bool withRelationships = true}) {
|
||||
final map = _$UserToJson(model);
|
||||
return transformSerialize(map, withRelationships: withRelationships);
|
||||
}
|
||||
}
|
||||
|
||||
final _usersFinders = <String, dynamic>{};
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class $UserHiveLocalAdapter = HiveLocalAdapter<User> with $UserLocalAdapter;
|
||||
class $UserAdapter = Adapter<User>
|
||||
with _$UserAdapter, NostrAdapter<User>, UserAdapter;
|
||||
|
||||
class $UserRemoteAdapter = RemoteAdapter<User>
|
||||
with NostrAdapter<User>, UserAdapter;
|
||||
final usersAdapterProvider = Provider<Adapter<User>>(
|
||||
(ref) => $UserAdapter(ref, InternalHolder(_usersFinders)));
|
||||
|
||||
final internalUsersRemoteAdapterProvider = Provider<RemoteAdapter<User>>(
|
||||
(ref) => $UserRemoteAdapter(
|
||||
$UserHiveLocalAdapter(ref), InternalHolder(_usersFinders)));
|
||||
|
||||
final usersRepositoryProvider =
|
||||
Provider<Repository<User>>((ref) => Repository<User>(ref));
|
||||
|
||||
extension UserDataRepositoryX on Repository<User> {
|
||||
NostrAdapter<User> get nostrAdapter => remoteAdapter as NostrAdapter<User>;
|
||||
UserAdapter get userAdapter => remoteAdapter as UserAdapter;
|
||||
extension UserAdapterX on Adapter<User> {
|
||||
NostrAdapter<User> get nostrAdapter => this as NostrAdapter<User>;
|
||||
UserAdapter get userAdapter => this as UserAdapter;
|
||||
}
|
||||
|
||||
extension UserRelationshipGraphNodeX on RelationshipGraphNode<User> {
|
||||
RelationshipGraphNode<User> get following {
|
||||
final meta = $UserLocalAdapter._kUserRelationshipMetas['following']
|
||||
final meta = _$UserAdapter._kUserRelationshipMetas['following']
|
||||
as RelationshipMeta<User>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
|
||||
RelationshipGraphNode<User> get followers {
|
||||
final meta = $UserLocalAdapter._kUserRelationshipMetas['followers']
|
||||
final meta = _$UserAdapter._kUserRelationshipMetas['followers']
|
||||
as RelationshipMeta<User>;
|
||||
return meta.clone(
|
||||
parent: this is RelationshipMeta ? this as RelationshipMeta : null);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> json) => User()
|
||||
..id = json['id']
|
||||
..pubkey = json['pubkey'] as String
|
||||
..createdAt = DateTime.parse(json['createdAt'] as String)
|
||||
..content = json['content'] as String
|
||||
..kind = json['kind'] as int
|
||||
..tags = (json['tags'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
|
||||
.toList()
|
||||
..signature = json['signature'] as String?
|
||||
..following =
|
||||
HasMany<User>.fromJson(json['following'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'pubkey': instance.pubkey,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'content': instance.content,
|
||||
'kind': instance.kind,
|
||||
'tags': instance.tags,
|
||||
'signature': instance.signature,
|
||||
'following': instance.following,
|
||||
};
|
||||
|
||||
+144
-184
@@ -1,212 +1,172 @@
|
||||
import 'package:async_button_builder/async_button_builder.dart';
|
||||
import 'package:expansion_tile_card/expansion_tile_card.dart';
|
||||
// ignore_for_file: prefer_const_literals_to_create_immutables
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/file_metadata.dart';
|
||||
import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/screens/profile_screen.dart';
|
||||
import 'package:zapstore/widgets/card.dart';
|
||||
import 'package:zapstore/widgets/pill_widget.dart';
|
||||
|
||||
final List<String> imgList = [
|
||||
'https://images.unsplash.com/photo-1520342868574-5fa3804e551c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=6ff92caffcdd63681a35134a6770ed3b&auto=format&fit=crop&w=1951&q=80',
|
||||
'https://images.unsplash.com/photo-1522205408450-add114ad53fe?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=368f45b0888aeb0b7b08e3a1084d3ede&auto=format&fit=crop&w=1950&q=80',
|
||||
'https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=94a1e718d89ca60a6337a6008341ca50&auto=format&fit=crop&w=1950&q=80',
|
||||
'https://images.unsplash.com/photo-1523205771623-e0faa4d2813d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89719a0d55dd05e2deae4120227e6efc&auto=format&fit=crop&w=1953&q=80',
|
||||
'https://images.unsplash.com/photo-1508704019882-f9cf40e475b4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8c6e5e3aba713b17aa1fe71ab4f0ae5b&auto=format&fit=crop&w=1352&q=80',
|
||||
'https://images.unsplash.com/photo-1519985176271-adb1088fa94c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a0c8d632e977f94e5d312d9893258f59&auto=format&fit=crop&w=1355&q=80'
|
||||
];
|
||||
|
||||
class AppDetailScreen extends HookConsumerWidget {
|
||||
final Release release;
|
||||
final App app;
|
||||
const AppDetailScreen({
|
||||
required this.release,
|
||||
required this.app,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final artifacts = release.artifacts.toList();
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: release.artifacts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final event = artifacts[index];
|
||||
return CardWidget(fileMetadata: event);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
final user = User.fromMap({
|
||||
'kind': 0,
|
||||
'created_at': 112899202,
|
||||
'pubkey':
|
||||
'a9e95a4eb32b55441b222ae5674f063949bfd0759b82deb03d7cd262e82d5626',
|
||||
'content': jsonEncode(
|
||||
{'name': 'test', 'picture': 'https://picsum.photos/200/300'}),
|
||||
'tags': [],
|
||||
});
|
||||
|
||||
class CardWidget extends HookConsumerWidget {
|
||||
final FileMetadata fileMetadata;
|
||||
|
||||
const CardWidget({super.key, required this.fileMetadata});
|
||||
|
||||
User? get author => fileMetadata.author.value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final currentUser = ref.read(loggedInUser);
|
||||
final fetchUser = useMemoized(() => ref.users.findOne(author!.id!));
|
||||
useFuture(fetchUser);
|
||||
|
||||
final isWebOfTrust = [...?currentUser?.following.toList()]
|
||||
.contains(fileMetadata.author.value);
|
||||
|
||||
return ExpansionTileCard(
|
||||
leading: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
child: Image.network(
|
||||
fileMetadata.release.value!.image,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, err, _) => Text(err.toString()),
|
||||
),
|
||||
),
|
||||
onExpansionChanged: (_) {
|
||||
ref.users.findOne(author!.id!);
|
||||
},
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
fileMetadata.content,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundImage: NetworkImage(author?.profilePicture ?? ''),
|
||||
maxRadius: 10,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
'${author?.name ?? author?.id}',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
return Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isWebOfTrust)
|
||||
Text(
|
||||
'You are following this author!',
|
||||
style: TextStyle(color: Colors.green),
|
||||
),
|
||||
if (!isWebOfTrust)
|
||||
Expanded(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
// SliverAppBar(
|
||||
// pinned: true,
|
||||
// leading: IconButton(
|
||||
// icon: Icon(Icons.arrow_back),
|
||||
// onPressed: () {
|
||||
// context.pop();
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text('Author is not in your web of trust',
|
||||
style: TextStyle(color: Colors.red)),
|
||||
GestureDetector(
|
||||
onTap: () => launchUrlString(
|
||||
'https://primal.net/p/${author?.id.toString().npub}',
|
||||
CircularImage(
|
||||
url: 'https://picsum.photos/200/200',
|
||||
size: 80,
|
||||
radius: 25,
|
||||
),
|
||||
Gap(16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
'Mutiny Wallet',
|
||||
minFontSize: 16,
|
||||
style: TextStyle(
|
||||
fontSize: 28, fontWeight: FontWeight.bold),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Gap(8),
|
||||
PillWidget(text: '0.5.6'),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
'see profile',
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
AsyncButtonBuilder(
|
||||
loadingWidget: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
height: 16.0,
|
||||
width: 16.0,
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
Gap(16),
|
||||
CarouselSlider(
|
||||
options: CarouselOptions(
|
||||
enableInfiniteScroll: false,
|
||||
),
|
||||
items: imgList
|
||||
.map((item) => Padding(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: Image.network(item,
|
||||
fit: BoxFit.cover, width: 1000),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
successWidget: const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
child: Icon(
|
||||
Icons.check,
|
||||
color: Colors.purpleAccent,
|
||||
Divider(height: 24),
|
||||
MarkdownBody(
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
h1: TextStyle(fontWeight: FontWeight.bold),
|
||||
h2: TextStyle(fontWeight: FontWeight.bold),
|
||||
p: TextStyle(fontSize: 16, fontWeight: FontWeight.w300),
|
||||
),
|
||||
selectable: false,
|
||||
data:
|
||||
'Lorem ipsum dolor sit amet\n - consectetur adipisicing elit\n - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, ',
|
||||
),
|
||||
onPressed: () async {
|
||||
try {
|
||||
await fileMetadata.install();
|
||||
showToastWidget(
|
||||
Text(
|
||||
'Success!',
|
||||
style: TextStyle(fontSize: 20),
|
||||
Gap(10),
|
||||
AuthorContainer(user: user, text: 'Built by', oneLine: false),
|
||||
Gap(10),
|
||||
Divider(height: 24),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[900],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [Text('Source'), Text('github link')],
|
||||
),
|
||||
// ignore: use_build_context_synchronously
|
||||
context: context,
|
||||
position: StyledToastPosition.center,
|
||||
animation: StyledToastAnimation.scale,
|
||||
reverseAnimation: StyledToastAnimation.fade,
|
||||
duration: Duration(seconds: 4),
|
||||
animDuration: Duration(seconds: 1),
|
||||
curve: Curves.elasticOut,
|
||||
reverseCurve: Curves.linear);
|
||||
} on Exception catch (e) {
|
||||
showToastWidget(
|
||||
Text(
|
||||
'Error! $e',
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [Text('Github stars'), Text('456')],
|
||||
),
|
||||
// ignore: use_build_context_synchronously
|
||||
context: context,
|
||||
position: StyledToastPosition.center,
|
||||
animation: StyledToastAnimation.scale,
|
||||
reverseAnimation: StyledToastAnimation.fade,
|
||||
duration: Duration(seconds: 4),
|
||||
animDuration: Duration(seconds: 1),
|
||||
curve: Curves.elasticOut,
|
||||
reverseCurve: Curves.linear);
|
||||
}
|
||||
},
|
||||
loadingSwitchInCurve: Curves.bounceInOut,
|
||||
loadingTransitionBuilder: (child, animation) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0, 1.0),
|
||||
end: const Offset(0, 0),
|
||||
).animate(animation),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
builder: (context, child, callback, state) {
|
||||
return Material(
|
||||
color: state.maybeWhen(
|
||||
success: () => Colors.purple[100],
|
||||
orElse: () => Colors.black12,
|
||||
),
|
||||
// This prevents the loading indicator showing below the
|
||||
// button
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: const StadiumBorder(),
|
||||
child: InkWell(
|
||||
onTap: callback,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: const Icon(Icons.download),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [Text('Github forks'), Text('3')],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.all(8),
|
||||
// color: Colors.blue,
|
||||
child: Center(
|
||||
child: Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size.fromHeight(50),
|
||||
),
|
||||
],
|
||||
onPressed: () {},
|
||||
child: const Text('Install'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,68 +1,109 @@
|
||||
// ignore_for_file: prefer_const_literals_to_create_immutables
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/screens/app_detail_screen.dart';
|
||||
|
||||
import '../main.data.dart';
|
||||
import 'package:zapstore/widgets/card.dart';
|
||||
import 'package:zapstore/widgets/user_avatar.dart';
|
||||
|
||||
const kAndroidMimeType = 'application/vnd.android.package-archive';
|
||||
|
||||
final searchStateProvider =
|
||||
StateProvider<DataState<List<App>>>((_) => DataState([]));
|
||||
|
||||
final screenInfoProvider = StateProvider(
|
||||
(ref) => 'Welcome to zap.store!\nUse the search bar to find apps');
|
||||
|
||||
class SearchScreen extends HookConsumerWidget {
|
||||
const SearchScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final searchState = useState<String?>(null);
|
||||
final labelState = useState<DataRequestLabel?>(null);
|
||||
final state = ref.watch(searchStateProvider);
|
||||
|
||||
final value = ref.fileMetadata.watchAll(remote: false);
|
||||
final artifacts = value.model
|
||||
.where((a) => a.release.isPresent && a.mimeType == kAndroidMimeType)
|
||||
.where((a) => searchState.value != null
|
||||
? a.release.value!.title
|
||||
.toLowerCase()
|
||||
.contains(searchState.value!.toLowerCase())
|
||||
: true)
|
||||
.toList();
|
||||
if (state.isLoading) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
children: [
|
||||
SearchBar(
|
||||
elevation: const MaterialStatePropertyAll(2.2),
|
||||
onSubmitted: (query) async {
|
||||
searchState.value = query;
|
||||
final apps = state.model;
|
||||
|
||||
await ref.releases.findAll(params: {
|
||||
'since': 1708658457,
|
||||
});
|
||||
await ref.fileMetadata.findAll(
|
||||
params: {
|
||||
// 'kinds': {1063},
|
||||
// 'limit': 20,
|
||||
// 'search': query,
|
||||
'#m': [kAndroidMimeType],
|
||||
'since': 1708658457,
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Scaffold.of(context).openDrawer(),
|
||||
child: UserAvatar(),
|
||||
),
|
||||
Gap(20),
|
||||
Expanded(
|
||||
child: SearchBar(
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
),
|
||||
),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 8, 6, 6),
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
color: Colors.blueGrey,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
hintText: 'Search for apps',
|
||||
hintStyle: MaterialStateProperty.all(
|
||||
TextStyle(color: Colors.grey[600])),
|
||||
autoFocus: true,
|
||||
elevation: MaterialStatePropertyAll(2.2),
|
||||
onSubmitted: (query) async {
|
||||
ref.read(screenInfoProvider.notifier).state = 'No items';
|
||||
final apps = [App(), App(), App(), App()];
|
||||
// await ref.apps.findAll(params: {'search': query});
|
||||
|
||||
// final releases = await ref.releases.findAll(
|
||||
// params: {'#i': apps.map((a) => a.identifier).toSet()});
|
||||
|
||||
// await ref.fileMetadata.findAll(
|
||||
// params: {
|
||||
// 'ids':
|
||||
// releases.map((r) => r.tagMap['e']!.first).toSet(),
|
||||
// '#m': [kAndroidMimeType],
|
||||
// },
|
||||
// );
|
||||
ref.read(searchStateProvider.notifier).state =
|
||||
DataState(apps);
|
||||
},
|
||||
label: labelState.value,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Gap(10),
|
||||
if (apps.isEmpty)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
ref.read(screenInfoProvider),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (apps.isNotEmpty)
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: artifacts.length,
|
||||
itemCount: apps.length,
|
||||
itemBuilder: (context, index) {
|
||||
final event = artifacts[index];
|
||||
return CardWidget(fileMetadata: event);
|
||||
final app = apps[index];
|
||||
return CardWidget(app: app);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class SettingsScreen extends HookConsumerWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final controller = useTextEditingController();
|
||||
return Expanded(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
maxLines: 10,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class UpdatesScreen extends HookConsumerWidget {
|
||||
const UpdatesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final controller = useTextEditingController();
|
||||
return Expanded(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
maxLines: 10,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
|
||||
final loggedInUser = StateProvider<User?>((ref) => null);
|
||||
@@ -0,0 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension ContextX on BuildContext {
|
||||
ThemeData get theme => Theme.of(this);
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'package:android_package_manager/android_package_manager.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:ndk/ndk.dart' as ndk;
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/services/session_service.dart';
|
||||
import 'package:zapstore/widgets/card.dart';
|
||||
|
||||
final loggedInUser = StateProvider<User?>((ref) => null);
|
||||
class AppDrawer extends HookConsumerWidget {
|
||||
AppDrawer({super.key});
|
||||
|
||||
class ProfileScreen extends HookConsumerWidget {
|
||||
const ProfileScreen({super.key});
|
||||
// final packageManager = AndroidPackageManager();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -19,19 +21,30 @@ class ProfileScreen extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
children: [
|
||||
if (u2 == null) TextField(controller: controller),
|
||||
SizedBox(height: 10),
|
||||
// Text('npub or NIP-05'),
|
||||
if (u2 == null) TextField(autocorrect: false, controller: controller),
|
||||
Gap(5),
|
||||
if (u2 == null)
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final pubkey = controller.text.hexKey;
|
||||
ref.read(loggedInUser.notifier).state =
|
||||
await ref.users.findOne(pubkey, params: {
|
||||
'kinds': {3}
|
||||
});
|
||||
await ref.users.findOne(controller.text);
|
||||
},
|
||||
child: Text('Log in with npub'),
|
||||
child: Text('Log in'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
ref.read(loggedInUser.notifier).state = null;
|
||||
controller.clear();
|
||||
// final as = await packageManager.getInstalledApplications();
|
||||
// for (final a in as!) {
|
||||
// if (!a.packageName!.contains('android')) {
|
||||
// print(a.packageName);
|
||||
// }
|
||||
// }
|
||||
},
|
||||
child: Text('check'),
|
||||
),
|
||||
if (u2 != null)
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -39,17 +52,18 @@ class ProfileScreen extends HookConsumerWidget {
|
||||
Card(
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: NetworkImage(u2.profilePicture ?? ''),
|
||||
leading: CircularImage(
|
||||
size: 20,
|
||||
url: u2.avatarUrl,
|
||||
),
|
||||
title: Text(
|
||||
u2.name ?? u2.id?.toString() ?? '',
|
||||
),
|
||||
subtitle: Text('following ${u2.following.length}'),
|
||||
// subtitle: Text('following ${u2.following.length}'),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
ref.read(loggedInUser.notifier).state = null;
|
||||
controller.clear();
|
||||
},
|
||||
@@ -0,0 +1,170 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:zapstore/widgets/pill_widget.dart';
|
||||
|
||||
class CardWidget extends HookConsumerWidget {
|
||||
final App app;
|
||||
|
||||
const CardWidget({super.key, required this.app});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final user = User.fromMap({
|
||||
'kind': 0,
|
||||
'created_at': 112899202,
|
||||
'pubkey':
|
||||
'a9e95a4eb32b55441b222ae5674f063949bfd0759b82deb03d7cd262e82d5626',
|
||||
'content': jsonEncode(
|
||||
{'name': 'test', 'picture': 'https://picsum.photos/200/300'}),
|
||||
'tags': [],
|
||||
});
|
||||
|
||||
return Card(
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
child: GestureDetector(
|
||||
onTap: () => context.go('/details', extra: app),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
CircularImage(
|
||||
url: 'https://picsum.photos/200/200',
|
||||
size: 80,
|
||||
radius: 25,
|
||||
),
|
||||
Gap(16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Mutiny Wallet - Mutiny Wallet - Muti',
|
||||
// '${app.name} - ${app.name}',
|
||||
style:
|
||||
TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: true,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
'Best web-based Lightning and Ecash wallet web-based Lightning and Ecash wallet',
|
||||
style: TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.w300),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
Gap(10),
|
||||
AuthorContainer(user: user),
|
||||
AuthorContainer(user: user),
|
||||
Gap(16),
|
||||
TagsContainer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TagsContainer extends StatelessWidget {
|
||||
const TagsContainer({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: const [
|
||||
PillWidget(text: 'nostr'),
|
||||
Gap(6),
|
||||
PillWidget(text: 'wallet'),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthorContainer extends StatelessWidget {
|
||||
final User user;
|
||||
final String text;
|
||||
final bool oneLine;
|
||||
const AuthorContainer({
|
||||
super.key,
|
||||
required this.user,
|
||||
this.text = 'Signed by',
|
||||
this.oneLine = true,
|
||||
});
|
||||
|
||||
String get name => user.name ?? user.npub;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
CircularImage(url: user.avatarUrl, size: oneLine ? 22 : 46),
|
||||
Gap(10),
|
||||
if (oneLine) Text('$text $name'),
|
||||
if (!oneLine)
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(text),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 1),
|
||||
child:
|
||||
Text(name, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CircularImage extends StatelessWidget {
|
||||
const CircularImage({
|
||||
super.key,
|
||||
this.url,
|
||||
this.size = 22,
|
||||
this.radius = 60,
|
||||
});
|
||||
|
||||
final String? url;
|
||||
final int size;
|
||||
final int radius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(radius.toDouble()),
|
||||
child: Container(
|
||||
width: size.toDouble(),
|
||||
height: size.toDouble(),
|
||||
color: Colors.grey[850],
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: url ?? '',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PillWidget extends StatelessWidget {
|
||||
final String text;
|
||||
const PillWidget({super.key, required this.text});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:zapstore/services/session_service.dart';
|
||||
|
||||
class UserAvatar extends HookConsumerWidget {
|
||||
const UserAvatar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final user = ref.watch(loggedInUser);
|
||||
return CircleAvatar(
|
||||
radius: 20,
|
||||
// backgroundImage: AssetImage('assets/images/logo.png'),
|
||||
backgroundColor: Colors.grey[850],
|
||||
foregroundImage: user != null ? NetworkImage(user.avatarUrl!) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <handy_window/handy_window_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
@@ -18,6 +19,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
||||
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
handy_window
|
||||
screen_retriever
|
||||
sqlite3_flutter_libs
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
)
|
||||
|
||||
@@ -7,12 +7,16 @@ import Foundation
|
||||
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import sqflite
|
||||
import sqlite3_flutter_libs
|
||||
import url_launcher_macos
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
}
|
||||
|
||||
+38
-1
@@ -5,6 +5,26 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- screen_retriever (0.0.1):
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.3):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.45.3+1)":
|
||||
- "sqlite3/common (= 3.45.3+1)"
|
||||
- "sqlite3/common (3.45.3+1)"
|
||||
- "sqlite3/fts5 (3.45.3+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/perf-threadsafe (3.45.3+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.45.3+1)":
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (~> 3.45.3+1)"
|
||||
- sqlite3/fts5
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- window_manager (0.2.0):
|
||||
- FlutterMacOS
|
||||
|
||||
@@ -12,8 +32,15 @@ DEPENDENCIES:
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
||||
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- sqlite3
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
@@ -21,6 +48,12 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
screen_retriever:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
sqflite:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
|
||||
sqlite3_flutter_libs:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
window_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||
|
||||
@@ -28,8 +61,12 @@ SPEC CHECKSUMS:
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 02d1f07eaaa01f80a1c16b4b31dfcbb3345ee01a
|
||||
sqlite3_flutter_libs: 8d204ef443cf0d5c1c8b058044eab53f3943a9c5
|
||||
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||
|
||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||
|
||||
COCOAPODS: 1.14.3
|
||||
COCOAPODS: 1.15.2
|
||||
|
||||
+207
-88
@@ -38,18 +38,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||
sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.10"
|
||||
version: "3.5.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -66,6 +66,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0+1"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: auto_size_text
|
||||
sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
bech32:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -126,10 +134,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.8"
|
||||
version: "2.4.9"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -150,10 +158,42 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.1"
|
||||
version: "8.9.2"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
carousel_slider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: carousel_slider
|
||||
sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -222,18 +262,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
version: "1.0.8"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
|
||||
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.4"
|
||||
version: "2.3.6"
|
||||
device_apps:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_apps
|
||||
sha256: e84dc74d55749993fd671148cc0bd53096e1be0c268fc364285511b1d8a4c19b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -287,14 +335,21 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.2"
|
||||
flutter_data:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_data
|
||||
sha256: ac6aba32a7c67d4f8e1b979ddb33eb3bdad3612378e54446780ca1bfc3003cab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
path: "../flutter_data"
|
||||
relative: true
|
||||
source: path
|
||||
version: "2.0.0-rc3"
|
||||
flutter_hooks:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -324,14 +379,22 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "9921f9deda326f8a885e202b1e35237eadfc1345239a0f6f0f1ff287e047547f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
flutter_riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: "4bce556b7ecbfea26109638d5237684538d4abc509d253e6c5c4c5733b360098"
|
||||
sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
version: "2.5.1"
|
||||
flutter_styled_toast:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -362,10 +425,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "4.0.0"
|
||||
gap:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gap
|
||||
sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -378,10 +449,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: "170c46e237d6eb0e6e9f0e8b3f56101e14fb64f787016e42edd74c39cf8b176a"
|
||||
sha256: b465e99ce64ba75e61c8c0ce3d87b66d8ac07f0b35d0a7e0263fcfc10f99e836
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.2.0"
|
||||
version: "13.2.5"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -406,22 +477,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
hive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
hooks_riverpod:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hooks_riverpod
|
||||
sha256: "758b07eba336e3cbacbd81dba481f2228a14102083fdde07045e8514e8054c49"
|
||||
sha256: "45b2030a18bcd6dbd680c2c91bc3b33e3fe7c323e3acb5ecec93a613e2fbaa8a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.10"
|
||||
version: "2.5.1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -490,18 +553,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
version: "4.9.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
|
||||
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.1"
|
||||
version: "6.8.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -542,6 +605,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
markdown:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: markdown
|
||||
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -574,13 +645,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
ndk:
|
||||
dependency: "direct main"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "../ndk"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.0"
|
||||
name: octo_image
|
||||
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -601,18 +673,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.2.4"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -649,10 +721,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44"
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.0"
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -665,10 +737,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b
|
||||
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.0"
|
||||
version: "9.4.4"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -681,10 +753,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78"
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -721,10 +793,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||
sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
version: "3.9.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -749,30 +821,29 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
recase:
|
||||
dependency: transitive
|
||||
purplebase:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: recase
|
||||
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
retry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: retry
|
||||
sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
path: "../purplebase"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.0"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: "548e2192eb7aeb826eb89387f814edb76594f3363e2c0bb99dd733d795ba3589"
|
||||
sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.5.1"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.27.7"
|
||||
screen_retriever:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -834,6 +905,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: "5ce2e1a15e822c3b4bfb5400455775e421da7098eed8adc8f26298ada7c9308c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "1abbeb84bf2b1a10e5e1138c913123c8aa9d83cd64e5f9a0dd847b3c83063202"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
sqlite3_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqlite3_flutter_libs
|
||||
sha256: fb2a106a2ea6042fe57de2c47074cc31539a941819c91e105b864744605da3f5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.21"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -874,6 +977,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -910,26 +1021,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
|
||||
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.5"
|
||||
version: "6.2.6"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
||||
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
version: "6.3.1"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.2.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -958,10 +1069,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
|
||||
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.1"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -974,10 +1085,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
|
||||
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.3"
|
||||
version: "4.4.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1006,26 +1117,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.5"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.5.0"
|
||||
window_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1034,6 +1145,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.8"
|
||||
ws:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ws
|
||||
sha256: bb40a7b9219324ac285133c6b3c11bc16015d9cd5889e6555b76bd3e3a64b3e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
+23
-44
@@ -20,6 +20,7 @@ version: 0.0.1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
@@ -32,7 +33,6 @@ dependencies:
|
||||
build_runner: ^2.4.8
|
||||
collection: ^1.18.0
|
||||
path_provider: ^2.1.2
|
||||
ndk: ^0.0.1
|
||||
expansion_tile_card: ^3.0.0
|
||||
crypto: ^3.0.3
|
||||
android_package_installer: ^0.0.2
|
||||
@@ -45,68 +45,47 @@ dependencies:
|
||||
flutter_styled_toast: ^2.2.1
|
||||
equatable: ^2.0.5
|
||||
url_launcher: ^6.2.5
|
||||
device_apps: ^2.2.0
|
||||
purplebase:
|
||||
sqlite3_flutter_libs: ^0.5.20
|
||||
carousel_slider: ^4.2.1
|
||||
flutter_markdown: ^0.7.1
|
||||
cached_network_image: ^3.3.1
|
||||
gap: ^3.0.1
|
||||
auto_size_text: ^3.0.0
|
||||
|
||||
dependency_overrides:
|
||||
android_package_installer:
|
||||
git:
|
||||
url: https://github.com/ImranR98/android_package_installer
|
||||
ref: main
|
||||
ndk:
|
||||
path: ../ndk
|
||||
purplebase:
|
||||
path: ../purplebase
|
||||
flutter_data:
|
||||
path: ../flutter_data
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
json_serializable: ^6.7.1
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_launcher_icons: "^0.13.1"
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
assets:
|
||||
- assets/images/logo.png
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
fonts:
|
||||
- family: Inter
|
||||
fonts:
|
||||
- asset: assets/fonts/Inter-Light.ttf
|
||||
weight: 300
|
||||
- asset: assets/fonts/Inter-Regular.ttf
|
||||
- asset: assets/fonts/Inter-Italic.ttf
|
||||
style: italic
|
||||
- asset: assets/fonts/Inter-Bold.ttf
|
||||
weight: 700
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: "launcher_icon"
|
||||
|
||||
Reference in New Issue
Block a user