Merge branch 'zapstore:master' into feat/web-links

This commit is contained in:
frnandu
2024-12-20 00:15:11 +01:00
committed by GitHub
10 changed files with 82 additions and 44 deletions
+6
View File
@@ -1,3 +1,9 @@
# 0.1.8
- Bugfix: Prevent unnecessary relay initialization
- Bugfix: [purplebase] Block until enough relay EOSEs
- Bugfix: Fix regression, offline-first models
# 0.1.7
- Performance: Faster, background downloads (#172)
+26 -20
View File
@@ -13,11 +13,12 @@ import 'package:zapstore/widgets/latest_releases_container.dart';
// NOTE: Very important to use const in relay args to preserve equality in Riverpod families
// const kAppRelays = {'ws://10.0.2.2:3000'};
const kAppRelays = {'wss://relay.zapstore.dev'};
const kSocialRelays = {'wss://relay.damus.io', 'wss://relay.nostr.band'};
const kSocialRelays = {'wss://relay.damus.io', 'wss://relay.primal.net'};
mixin NostrAdapter<T extends DataModelMixin<T>> on Adapter<T> {
RelayMessageNotifier get relay =>
ref.read(relayProviderFamily(kAppRelays).notifier);
RelayMessageNotifier get socialRelays =>
ref.read(relayProviderFamily(kSocialRelays).notifier);
@@ -25,24 +26,29 @@ mixin NostrAdapter<T extends DataModelMixin<T>> on Adapter<T> {
Future<void> onInitialized() async {
await super.onInitialized();
// Upon adapter initialization, configure relays
// with the event verification caching function
relay.configure(
isEventVerified: (Map<String, dynamic> map) {
// If replaceable, we check for that ID
final identifier = (map['tags'] as Iterable)
.firstWhereOrNull((t) => t[0] == 'd')?[1]
?.toString();
final id = identifier != null
? (map['kind'] as int, map['pubkey'].toString(), identifier)
.formatted
: map['id'];
return ref.apps.nostrAdapter.existsId(id);
},
);
socialRelays.configure(
isEventVerified: (map) => ref.apps.nostrAdapter.existsId(map['pubkey']),
);
// DO NOT run on isolates, the mere fact of calling the
// relay getter will trigger its initialization
// as providers are obviously not cached across isolates
if (!inIsolate) {
// Upon adapter initialization, configure relays
// with the event verification caching function
relay.configure(
isEventVerified: (Map<String, dynamic> map) {
// If replaceable, we check for that ID
final identifier = (map['tags'] as Iterable)
.firstWhereOrNull((t) => t[0] == 'd')?[1]
?.toString();
final id = identifier != null
? (map['kind'] as int, map['pubkey'].toString(), identifier)
.formatted
: map['id'];
return ref.apps.nostrAdapter.existsId(id);
},
);
socialRelays.configure(
isEventVerified: (map) => ref.apps.nostrAdapter.existsId(map['pubkey']),
);
}
}
int get kind {
@@ -182,7 +188,7 @@ class RelayListenerNotifier extends Notifier<void> {
Future<void> fetch() async {
await Future.microtask(() async {
await ref.read(latestReleasesAppProvider.notifier).fetch();
await ref.read(latestReleasesAppProvider.notifier).fetchRemote();
await ref.apps.appAdapter.checkForUpdates();
});
}
+2 -2
View File
@@ -31,12 +31,12 @@ final appInitializer = FutureProvider<void>((ref) async {
if (ref.appCurationSets.countLocal == 0) {
// If we are here, local storage is empty
// Preload curation sets
await ref.appCurationSets.findAll();
await ref.appCurationSets.findAll(syncLocal: true);
// Preload updates in the background (no await)
ref.apps.appAdapter.checkForUpdates();
} else {
// Preload curation sets in the background (no await)
ref.appCurationSets.findAll();
ref.appCurationSets.findAll(syncLocal: true, background: true);
}
// Preload zapstore's nostr curation set
+12
View File
@@ -0,0 +1,12 @@
import 'package:flutter_data/flutter_data.dart';
abstract class PreloadingStateNotifier<T> extends StateNotifier<AsyncValue<T>> {
final Ref ref;
PreloadingStateNotifier(this.ref) : super(AsyncLoading()) {
state = fetchLocal();
fetchRemote();
}
AsyncValue<T> fetchLocal();
Future<void> fetchRemote();
}
+10 -4
View File
@@ -18,8 +18,11 @@ class AppCurationContainer extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final scrollController = useScrollController();
final selectedAppCurationSet = ref.watch(_selectedIdProvider);
final appCurationSets =
ref.appCurationSets.findAllLocal().sortedBy((s) => s.createdAt!);
final appCurationSets = ref.appCurationSets
.findAllLocal()
.sortedBy((s) => s.createdAt!)
.reversed
.toList();
// Custom curation set to place nostr set first (as its preloaded)
final nostrCurationSet = appCurationSets.firstWhereOrNull(
@@ -108,9 +111,12 @@ class AppCurationSetNotifier
Future<void> fetch() async {
final appCurationSet = await future;
state = AsyncLoading();
if (appCurationSet.apps.isEmpty) {
state = AsyncLoading();
}
state = await AsyncValue.guard(() async {
// Only load apps (no releases for now)
// Only load apps (no releases for now, includes=false)
// We can use ignoreReturn here as we do not care about releases
await ref.apps.findAll(
params: {
+7 -3
View File
@@ -7,7 +7,7 @@ import 'package:zapstore/widgets/rounded_image.dart';
class AuthorContainer extends StatelessWidget {
final User user;
final String beforeText;
final String afterText;
final String? afterText;
final bool oneLine;
final double size;
@@ -15,7 +15,7 @@ class AuthorContainer extends StatelessWidget {
super.key,
required this.user,
this.beforeText = 'Signed by',
this.afterText = '',
this.afterText,
this.oneLine = true,
this.size = 14,
});
@@ -49,7 +49,11 @@ class AuthorContainer extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(afterText),
if (afterText != null)
Text(
afterText!,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
],
)
],
+14 -10
View File
@@ -6,6 +6,7 @@ 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/utils/notifier.dart';
import 'package:zapstore/widgets/app_card.dart';
class LatestReleasesContainer extends HookConsumerWidget {
@@ -58,7 +59,7 @@ class LatestReleasesContainer extends HookConsumerWidget {
onPressed: () async {
return ref
.read(latestReleasesAppProvider.notifier)
.fetch(next: true);
.fetchRemote(next: true);
},
builder: (context, child, callback, state) {
return Padding(
@@ -83,16 +84,14 @@ class LatestReleasesContainer extends HookConsumerWidget {
}
}
class LatestReleasesAppNotifier extends StateNotifier<AsyncValue<List<App>>> {
class LatestReleasesAppNotifier extends PreloadingStateNotifier<List<App>> {
DateTime? _oldestCreatedAt;
int _page = 1;
final Ref ref;
LatestReleasesAppNotifier(this.ref) : super(AsyncLoading()) {
fetch();
}
LatestReleasesAppNotifier(super.ref);
Future<void> fetch({bool next = false}) async {
@override
Future<void> fetchRemote({bool next = false}) async {
if (next) {
_page++;
}
@@ -107,12 +106,17 @@ class LatestReleasesAppNotifier extends StateNotifier<AsyncValue<List<App>>> {
},
);
state = AsyncData(_fetchLocal());
state = fetchLocal();
}
List<App> _fetchLocal() {
@override
AsyncValue<List<App>> fetchLocal() {
final model = ref.apps.findAllLocal();
if (model.isEmpty) {
return AsyncLoading();
}
final apps = model
.where((a) => a.latestMetadata != null)
.sortedByLatest
@@ -123,7 +127,7 @@ class LatestReleasesAppNotifier extends StateNotifier<AsyncValue<List<App>>> {
if (apps.isNotEmpty) {
_oldestCreatedAt = apps.last.createdAt;
}
return apps;
return AsyncData(apps);
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ class SignerAndDeveloperRow extends ConsumerWidget {
settingsState.model!.trustedUsers
.contains(app.signer.value!)
? '(trusted)'
: '',
: null,
),
],
),
+2 -2
View File
@@ -938,8 +938,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: cbe8342031adf908c8243125e176f52739d400d8
resolved-ref: cbe8342031adf908c8243125e176f52739d400d8
ref: "7f0eefd1fd1bcbc947123dc377183dbae58ea97a"
resolved-ref: "7f0eefd1fd1bcbc947123dc377183dbae58ea97a"
url: "https://github.com/purplebase/purplebase"
source: git
version: "0.2.0"
+2 -2
View File
@@ -2,7 +2,7 @@ name: zapstore
description: The permissionless app store
publish_to: 'none'
version: 0.1.7+17
version: 0.1.8+18
environment:
sdk: '>=3.3.0 <4.0.0'
@@ -57,7 +57,7 @@ dependency_overrides:
# path: ../purplebase
git:
url: https://github.com/purplebase/purplebase
ref: cbe8342031adf908c8243125e176f52739d400d8
ref: 7f0eefd1fd1bcbc947123dc377183dbae58ea97a
flutter_data:
git:
url: https://github.com/zapstore/flutter_data