mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 12:38:24 +00:00
Send errors with description from native side, only show app zaps
This commit is contained in:
+18
-6
@@ -287,7 +287,8 @@ class AndroidPackageManagerPlugin : FlutterPlugin, MethodCallHandler,
|
||||
appId: String,
|
||||
status: String,
|
||||
message: String? = null,
|
||||
errorCode: String? = null
|
||||
errorCode: String? = null,
|
||||
description: String? = null
|
||||
) {
|
||||
val emitNow = emit@{
|
||||
// Update watchdog regardless of whether Dart is listening.
|
||||
@@ -310,6 +311,9 @@ class AndroidPackageManagerPlugin : FlutterPlugin, MethodCallHandler,
|
||||
if (errorCode != null) {
|
||||
event["errorCode"] = errorCode
|
||||
}
|
||||
if (description != null) {
|
||||
event["description"] = description
|
||||
}
|
||||
Log.d(TAG, "Emitting to Dart: $event")
|
||||
sink.success(event)
|
||||
}
|
||||
@@ -496,8 +500,9 @@ class AndroidPackageManagerPlugin : FlutterPlugin, MethodCallHandler,
|
||||
emitInstallStatus(
|
||||
packageName,
|
||||
InstallStatus.FAILED,
|
||||
verification.errorMessage,
|
||||
verification.errorCode
|
||||
verification.errorTitle,
|
||||
verification.errorCode,
|
||||
verification.errorDescription
|
||||
)
|
||||
}
|
||||
verificationThreads.remove(packageName)
|
||||
@@ -648,14 +653,20 @@ class AndroidPackageManagerPlugin : FlutterPlugin, MethodCallHandler,
|
||||
|
||||
private data class VerificationResult(
|
||||
val isSuccess: Boolean,
|
||||
val errorMessage: String = "",
|
||||
val errorTitle: String = "",
|
||||
val errorDescription: String? = null,
|
||||
val errorCode: String? = null
|
||||
)
|
||||
|
||||
private fun verifyApk(file: File, expectedHash: String, expectedSize: Long): VerificationResult {
|
||||
if (!isValidApkFormat(file)) {
|
||||
file.delete()
|
||||
return VerificationResult(false, "Invalid APK file format", ErrorCode.INVALID_FILE)
|
||||
return VerificationResult(
|
||||
false,
|
||||
"Invalid APK file",
|
||||
"The downloaded file is not a valid APK format.",
|
||||
ErrorCode.INVALID_FILE
|
||||
)
|
||||
}
|
||||
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
@@ -671,7 +682,8 @@ class AndroidPackageManagerPlugin : FlutterPlugin, MethodCallHandler,
|
||||
if (actualHash.lowercase() != expectedHash.lowercase()) {
|
||||
return VerificationResult(
|
||||
false,
|
||||
"Hash verification failed. Expected: $expectedHash, Actual: $actualHash",
|
||||
"Hash verification failed",
|
||||
"The downloaded file hash does not match.\n\nExpected: $expectedHash\nActual: $actualHash",
|
||||
ErrorCode.HASH_MISMATCH
|
||||
)
|
||||
}
|
||||
|
||||
@@ -205,24 +205,22 @@ class _UserZapsList extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Collect addressable tags for apps and metadata IDs separately
|
||||
final allAppTags = <String, Set<String>>{};
|
||||
final metadataIds = <String>{};
|
||||
// Don't query zaps if user has no apps
|
||||
if (apps.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// Collect addressable tags for apps
|
||||
final allAppTags = <String, Set<String>>{};
|
||||
for (final app in apps) {
|
||||
final appTags = app.event.addressableIdTagMap;
|
||||
for (final entry in appTags.entries) {
|
||||
allAppTags[entry.key] = {...?allAppTags[entry.key], ...entry.value};
|
||||
}
|
||||
|
||||
final metadata = app.latestFileMetadata;
|
||||
if (metadata != null) {
|
||||
metadataIds.add(metadata.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Query zaps on apps (via #a tag) - separate from metadata zaps
|
||||
final appZapsState = ref.watch(
|
||||
// Query zaps on apps (via #a tag)
|
||||
final zapsState = ref.watch(
|
||||
query<Zap>(
|
||||
tags: allAppTags,
|
||||
source: const LocalAndRemoteSource(relays: 'social'),
|
||||
@@ -230,17 +228,7 @@ class _UserZapsList extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
// Query zaps on metadata (via #e tag) - separate query
|
||||
final metadataZapsState = ref.watch(
|
||||
query<Zap>(
|
||||
tags: {'#e': metadataIds},
|
||||
source: const LocalAndRemoteSource(relays: 'social'),
|
||||
subscriptionPrefix: 'user-metadata-zaps',
|
||||
),
|
||||
);
|
||||
|
||||
// Combine zaps from both queries
|
||||
final allZaps = {...appZapsState.models, ...metadataZapsState.models};
|
||||
final allZaps = zapsState.models;
|
||||
|
||||
if (allZaps.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
|
||||
@@ -99,10 +99,11 @@ final class AndroidPackageManager extends PackageManager {
|
||||
final statusRaw = event['status'] as String?;
|
||||
final message = event['message'] as String?;
|
||||
final errorCode = event['errorCode'] as String?;
|
||||
final description = event['description'] as String?;
|
||||
final status = InstallStatusX.tryParse(statusRaw);
|
||||
|
||||
debugPrint(
|
||||
'[PackageManager] Received event: appId=$appId, status=$status, msg=$message, errorCode=$errorCode',
|
||||
'[PackageManager] Received event: appId=$appId, status=$status, msg=$message, errorCode=$errorCode, desc=$description',
|
||||
);
|
||||
|
||||
if (appId == null || statusRaw == null) {
|
||||
@@ -196,6 +197,7 @@ final class AndroidPackageManager extends PackageManager {
|
||||
target: target,
|
||||
type: _errorCodeToFailureType(errorCode, message),
|
||||
message: message ?? 'Installation failed',
|
||||
description: description,
|
||||
filePath: filePath,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -132,12 +132,14 @@ class Uninstalling extends InstallOperation {
|
||||
class OperationFailed extends InstallOperation {
|
||||
final FailureType type;
|
||||
final String message;
|
||||
final String? description;
|
||||
final String? filePath;
|
||||
|
||||
const OperationFailed({
|
||||
required super.target,
|
||||
required this.type,
|
||||
required this.message,
|
||||
this.description,
|
||||
this.filePath,
|
||||
});
|
||||
|
||||
|
||||
@@ -275,24 +275,14 @@ class _ZappersListSection extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final latestMetadata = app.latestFileMetadata;
|
||||
if (latestMetadata == null) return const SizedBox.shrink();
|
||||
|
||||
return _ZappersListSectionWithMetadata(
|
||||
app: app,
|
||||
metadataId: latestMetadata.id,
|
||||
);
|
||||
return _ZappersListSectionWithMetadata(app: app);
|
||||
}
|
||||
}
|
||||
|
||||
class _ZappersListSectionWithMetadata extends ConsumerWidget {
|
||||
const _ZappersListSectionWithMetadata({
|
||||
required this.app,
|
||||
required this.metadataId,
|
||||
});
|
||||
const _ZappersListSectionWithMetadata({required this.app});
|
||||
|
||||
final App app;
|
||||
final String metadataId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -305,19 +295,7 @@ class _ZappersListSectionWithMetadata extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
// Query zaps on metadata (via #e tag)
|
||||
final zapsOnMetadataState = ref.watch(
|
||||
query<Zap>(
|
||||
tags: {
|
||||
'#e': {metadataId},
|
||||
},
|
||||
source: const LocalAndRemoteSource(relays: 'social'),
|
||||
subscriptionPrefix: 'metadata-zaps',
|
||||
),
|
||||
);
|
||||
|
||||
// Combine zaps from both queries
|
||||
final allZaps = {...zapsState.models, ...zapsOnMetadataState.models};
|
||||
final allZaps = zapsState.models;
|
||||
|
||||
if (allZaps.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
|
||||
@@ -604,7 +604,11 @@ class InstallButton extends ConsumerWidget {
|
||||
final operation = ref.read(installOperationProvider(app.identifier));
|
||||
if (operation is! OperationFailed) return;
|
||||
|
||||
context.showError(operation.message, actions: const []);
|
||||
context.showError(
|
||||
operation.message,
|
||||
description: operation.description,
|
||||
actions: const [],
|
||||
);
|
||||
|
||||
// Always clear error after showing it (reckless mode removed).
|
||||
final pm = ref.read(packageManagerProvider.notifier);
|
||||
@@ -617,6 +621,9 @@ class InstallButton extends ConsumerWidget {
|
||||
'Certificate mismatch',
|
||||
description: 'The app signature does not match. Force update required.',
|
||||
);
|
||||
} else if (operation.description != null) {
|
||||
// Errors with descriptions (from Kotlin) are shown as toasts
|
||||
context.showError(operation.message, description: operation.description);
|
||||
}
|
||||
// Other errors are shown when user taps the error button
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user