package com.example.gravitydemo import ai.gravityfield.gravity_sdk.GravitySDK import ai.gravityfield.gravity_sdk.models.* import ai.gravityfield.gravity_sdk.network.Campaign import ai.gravityfield.gravity_sdk.ui.product_view_builder.ProductViewBuilder import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import java.util.concurrent.ConcurrentHashMap // Адаптер API приложения, не метод Gravity SDK. // Запрос должен использовать авторизацию именно accountKey и поддерживать отмену. interface ReviewEligibilityAPI { suspend fun eligibleSkus(accountKey: String, skus: List): Set } class ReviewPromptIntegration( private val api: ReviewEligibilityAPI, private val selector: String, private val currentAccountKey: () -> String?, // Потокобезопасный снимок аккаунта. private val fallbackProductViewBuilder: ProductViewBuilder, private val onOpenReview: (accountKey: String, sku: String) -> Unit, ) { private data class ReviewedProduct(val accountKey: String, val sku: String) private val submitted = ConcurrentHashMap() fun preparation() = InAppPreparation(timeoutMillis = 5_000) { content, campaign -> if (campaign.selector != selector) return@InAppPreparation content val accountKey = currentAccountKey() ?: return@InAppPreparation null val skus = content.products?.slots.orEmpty() .mapNotNull { it.item["sku"] as? String }.distinct() if (skus.isEmpty()) return@InAppPreparation null // Один batch-запрос для всех кандидатов; исключения обрабатывает SDK. val eligible = api.eligibleSkus(accountKey, skus) if (currentAccountKey() != accountKey) return@InAppPreparation null content.filteringProducts { slot -> val sku = slot.item["sku"] as? String sku != null && sku in eligible && !submitted.containsKey(ReviewedProduct(accountKey, sku)) } } val productViewBuilder = object : ProductViewBuilder { @Composable override fun Build(slot: Slot, content: CampaignContent, campaign: Campaign) { if (campaign.selector != selector) { fallbackProductViewBuilder.Build(slot, content, campaign) return } val sku = slot.item["sku"] as? String ?: return val accountKey = currentAccountKey() ?: return Column(modifier = Modifier.width(160.dp)) { Text(slot.item["name"] as? String ?: sku) Button(onClick = { if (currentAccountKey() == accountKey) { GravitySDK.instance.sendProductEngagement( ProductClickEngagement(slot, content, campaign) ) GravitySDK.instance.dismissInApp(content.contentId) { dismissed -> if (dismissed && currentAccountKey() == accountKey) { onOpenReview(accountKey, sku) } } } }) { Text("Оставить отзыв") } } } } // Вызывайте после успешного сохранения отзыва в API приложения. // accountKey берите из открытой формы, а не из текущего аккаунта в момент ответа. fun didSubmitReview(accountKey: String, sku: String) { submitted[ReviewedProduct(accountKey, sku)] = true } }