Support new NIP format

This commit is contained in:
franzap
2025-12-23 18:55:56 -03:00
parent 2a693e140f
commit 2e6b555149
10 changed files with 58 additions and 8 deletions
+5 -1
View File
@@ -43,7 +43,11 @@ class AppDetailScreen extends HookConsumerWidget {
'#f': {platform},
},
limit: 1,
and: (a) => {a.latestRelease, a.latestRelease.value?.latestMetadata},
and: (a) => {
a.latestRelease,
a.latestRelease.value?.latestMetadata,
a.latestRelease.value?.latestAsset,
},
// stream=true ensures cached/local results render immediately; remote
// results will merge in as they arrive.
source: const LocalAndRemoteSource(relays: 'AppCatalog', stream: false),
+1
View File
@@ -86,6 +86,7 @@ class _AppStackContentWithApps extends HookConsumerWidget {
and: (app) => {
app.latestRelease,
app.latestRelease.value?.latestMetadata,
app.latestRelease.value?.latestAsset,
},
source: const LocalAndRemoteSource(relays: 'AppCatalog'),
subscriptionPrefix: 'app-stack-apps-${stack!.identifier}',
+1
View File
@@ -164,6 +164,7 @@ class _SearchResultsSection extends HookConsumerWidget {
and: (app) => {
app.latestRelease,
app.latestRelease.value?.latestMetadata,
app.latestRelease.value?.latestAsset,
},
// Force the search to hit the default relay group (relay.zapstore.dev)
// so a connection appears in Debug Info when searching.
+1
View File
@@ -43,6 +43,7 @@ class UserScreen extends HookConsumerWidget {
and: (app) => {
app.latestRelease,
app.latestRelease.value?.latestMetadata,
app.latestRelease.value?.latestAsset,
},
source: const LocalAndRemoteSource(relays: 'AppCatalog', stream: false),
subscriptionPrefix: 'user-apps',
+1
View File
@@ -80,6 +80,7 @@ class CategorizedAppsNotifier extends Notifier<CategorizedApps> {
and: (app) => {
app.latestRelease,
app.latestRelease.value?.latestMetadata,
app.latestRelease.value?.latestAsset,
},
source: const LocalAndRemoteSource(relays: 'AppCatalog', stream: true),
andSource: const LocalAndRemoteSource(
+29
View File
@@ -38,8 +38,10 @@ extension AppExt on App {
ref.read(packageManagerProvider.notifier).isInstalled(identifier);
/// Latest file metadata associated to the latest release
/// Prefers SoftwareAsset (new format) over FileMetadata (old format)
/// Note: assumes latest metadata has been loaded for the current platform
FileMetadata? get latestFileMetadata =>
latestRelease.value?.latestAsset.value ??
latestRelease.value?.latestMetadata.value;
/// Whether there is an update available for the installed app
@@ -79,3 +81,30 @@ extension AppExt on App {
extension WidgetRefExt on WidgetRef {
Ref get ref => read(Provider((ref) => ref));
}
/// Extension to handle both old and new format certificate hashes
extension FileMetadataExt on FileMetadata {
/// Returns the APK certificate hash, checking new format first then old
/// New format (SoftwareAsset): apkCertificateHashes (Set)
/// Old format (FileMetadata): apkSignatureHash (String?)
String? get certificateHash {
// Try new format first (SoftwareAsset has apkCertificateHashes)
if (this is SoftwareAsset) {
final hashes = (this as SoftwareAsset).apkCertificateHashes;
if (hashes.isNotEmpty) return hashes.first;
}
// Fall back to old format
return apkSignatureHash;
}
/// Returns all APK certificate hashes
Set<String> get certificateHashes {
// Try new format first
if (this is SoftwareAsset) {
final hashes = (this as SoftwareAsset).apkCertificateHashes;
if (hashes.isNotEmpty) return hashes;
}
// Fall back to old format (single hash as set)
return apkSignatureHash != null ? {apkSignatureHash!} : {};
}
}
+1 -1
View File
@@ -718,7 +718,7 @@ class DebugVersionsSection extends HookConsumerWidget {
final releasesState = ref.watch(
query<Release>(
tags: app.event.addressableIdTagMap,
and: (release) => {release.latestMetadata},
and: (release) => {release.latestMetadata, release.latestAsset},
source: LocalAndRemoteSource(relays: 'AppCatalog', stream: false),
subscriptionPrefix: 'debug-releases',
),
+2 -2
View File
@@ -82,8 +82,8 @@ class AppInfoTable extends HookConsumerWidget {
);
}
if (fileMetadata?.apkSignatureHash != null) {
final full = fileMetadata!.apkSignatureHash!;
if (fileMetadata?.certificateHash != null) {
final full = fileMetadata!.certificateHash!;
rows.add(
_InfoRow(
label: 'Certificate hash',
+1 -2
View File
@@ -719,8 +719,7 @@ class InstallButton extends ConsumerWidget {
final updateVersion = app.latestFileMetadata?.version ?? 'Unknown';
final currentVersion = installedPackage?.version ?? 'Unknown';
final currentCertHash = installedPackage?.signatureHash ?? 'Unknown';
final updateCertHash =
app.latestFileMetadata?.apkSignatureHash ?? 'Unknown';
final updateCertHash = app.latestFileMetadata?.certificateHash ?? 'Unknown';
// Get author profile
final author = app.author.value;
+16 -2
View File
@@ -60,7 +60,10 @@ class LatestReleasesContainer extends HookConsumerWidget {
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 6,
),
child: Text(
'LATEST RELEASES',
style: context.textTheme.labelLarge?.copyWith(
@@ -286,6 +289,7 @@ class LatestReleasesNotifier extends StateNotifier<LatestReleasesState> {
and: (app) => {
app.latestRelease,
app.latestRelease.value?.latestMetadata,
app.latestRelease.value?.latestAsset,
},
// NOTE: It must stream=true
source: const LocalAndRemoteSource(relays: 'AppCatalog', stream: true),
@@ -358,7 +362,7 @@ class LatestReleasesNotifier extends StateNotifier<LatestReleasesState> {
source: const RemoteSource(stream: false),
);
// Load file metadata for the releases
// Load file metadata for the releases (old format)
if (releases.isNotEmpty) {
await ref.storage.query(
Request<FileMetadata>(
@@ -369,6 +373,16 @@ class LatestReleasesNotifier extends StateNotifier<LatestReleasesState> {
),
source: const RemoteSource(stream: false),
);
// Load software assets for the releases (new format)
await ref.storage.query(
Request<SoftwareAsset>(
releases
.map((r) => r.latestAsset.req?.filters.firstOrNull)
.nonNulls
.toList(),
),
source: const RemoteSource(stream: false),
);
}
// Load release authors from different relay group (social)