Show rating prompt after share sheet is dismissed

iOS: moved registerSuccessfulExport() into onDismiss so it fires
after the share sheet closes, not while it is still open.
Android: hold launchReview() until the activity is RESUMED so the
dialog cannot overlap the share chooser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 14:55:09 +02:00
parent 01cdaf1861
commit 022e309873
3 changed files with 16 additions and 5 deletions

View File

@@ -219,7 +219,10 @@ struct LoadsView: View {
} }
) )
} }
.sheet(item: $overviewShareItem, onDismiss: cleanupOverviewShareItem) { item in .sheet(item: $overviewShareItem, onDismiss: {
cleanupOverviewShareItem()
ReviewPrompt.registerSuccessfulExport()
}) { item in
ShareSheet(items: item.shareItems) ShareSheet(items: item.shareItems)
} }
.alert( .alert(
@@ -1133,7 +1136,6 @@ struct LoadsView: View {
await MainActor.run { await MainActor.run {
overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url) overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url)
isExportingOverview = false isExportingOverview = false
ReviewPrompt.registerSuccessfulExport()
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
@@ -1163,7 +1165,6 @@ struct LoadsView: View {
"system": snapshot.systemName, "system": snapshot.systemName,
]) ])
overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url) overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url)
ReviewPrompt.registerSuccessfulExport()
} else { } else {
overviewExportError = OverviewExportError( overviewExportError = OverviewExportError(
message: String(localized: "overview.share.diagram.error", defaultValue: "Could not generate diagram. Check your internet connection.") message: String(localized: "overview.share.diagram.error", defaultValue: "Could not generate diagram. Check your internet connection.")

View File

@@ -301,7 +301,10 @@ struct SystemBillOfMaterialsView: View {
} }
} }
.accessibilityIdentifier("system-bom-view") .accessibilityIdentifier("system-bom-view")
.sheet(item: $activeShareItem, onDismiss: cleanupShareItem) { item in .sheet(item: $activeShareItem, onDismiss: {
cleanupShareItem()
ReviewPrompt.registerSuccessfulExport()
}) { item in
ShareSheet(items: [item.url]) ShareSheet(items: [item.url])
} }
.alert(item: $exportError) { error in .alert(item: $exportError) { error in
@@ -346,7 +349,6 @@ struct SystemBillOfMaterialsView: View {
) )
await MainActor.run { await MainActor.run {
activeShareItem = ExportedPDFShareItem(url: url) activeShareItem = ExportedPDFShareItem(url: url)
ReviewPrompt.registerSuccessfulExport()
} }
} catch { } catch {
await MainActor.run { await MainActor.run {

View File

@@ -7,11 +7,14 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import app.voltplan.cable.BuildConfig import app.voltplan.cable.BuildConfig
import app.voltplan.cable.analytics.Analytics import app.voltplan.cable.analytics.Analytics
import com.google.android.play.core.ktx.launchReview import com.google.android.play.core.ktx.launchReview
import com.google.android.play.core.ktx.requestReview import com.google.android.play.core.ktx.requestReview
import com.google.android.play.core.review.ReviewManagerFactory import com.google.android.play.core.review.ReviewManagerFactory
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
/** /**
@@ -113,6 +116,11 @@ object ReviewPrompt {
runCatching { runCatching {
val manager = ReviewManagerFactory.create(context) val manager = ReviewManagerFactory.create(context)
val reviewInfo = manager.requestReview() val reviewInfo = manager.requestReview()
// Wait until the activity is resumed so the dialog doesn't overlap a share sheet
// that was just launched (startActivity returns immediately, so we may still be paused).
(activity as? LifecycleOwner)?.lifecycle?.currentStateFlow
?.filter { state: Lifecycle.State -> state.isAtLeast(Lifecycle.State.RESUMED) }
?.first()
manager.launchReview(activity, reviewInfo) manager.launchReview(activity, reviewInfo)
} }
} }