Skip to content

com.stratkit.rate-us-trigger

Shared decision layer for the rate-us popup. Evaluates favourable-gameplay conditions and emits a neutral signal; game-specific packages consume the signal and present their own UI.

Scope

This package decides when to ask, not how it looks or what happens afterwards.

  • ✅ Config (thresholds), condition evaluation, signal emission.
  • ❌ No dialog UI, no cooldown/rated persistence, no native store review — those belong to the consumer.

The signal

public struct RateUsTriggerRequest : IComponentData {
    public InactivityKind Kind;   // why it fired (NoInteraction / NoAction)
    public int CreatedFrame;      // internal — used by the cleanup system, consumers ignore it
}

Created on its own entity in the persistent world when all gates pass. Short-livedRateUsTriggerCleanupSystem destroys any request not consumed within a couple of frames (compared against CreatedFrame), so a stale request never lingers.

Consumers query for RateUsTriggerRequest, read Kind, open their dialog, and destroy the entity.

Gates (all must pass)

Gate Source
AccountAgeDay >= MinAccountAgeDays UserActivityLogComponent
SessionLength >= MinSessionLength UserActivityLogComponent
SessionCount >= MinSessionCount UserActivityLogComponent
DailyLoginStreak >= MinDailyLoginStreak UserActivityLogComponent
PointsTrend >= RequireTrendAtLeast UserActivityLogComponent
player is idle UserActivityTrackerUtils.GetInactivityKind (idle thresholds from UserActivityTrackerConfig)
not suppressed RateUsTriggerSuppressedTag (see below)
not already fired this session RateUsTriggerFiredTag
persistent cooldown elapsed RateUsTriggerCooldownComponent (see below)

Ask-later / persistent cooldown

When the player picks "ask later", the consumer calls RateUsTriggerUtils.IssueAskLaterDelay(). This persists "don't ask until now + AskLaterDelay" (config default 2 days) to PlayerPrefs, survives app restarts, and gates the trigger via RateUsTriggerCooldownComponent.

RateUsTriggerUtils.IssueAskLaterDelay();      // uses the configured delay (2d)
RateUsTriggerUtils.IssueDelay(604800);        // generic — e.g. 7d re-ask after a low rating
RateUsTriggerUtils.ClearDelay();              // testing

The cooldown is a single device-wide value (not per-account). If you need server-synced / per-source cooldowns (legacy behaviour), the consumer can layer its own persistence and use Suppress() instead.

Consumer responsibilities

  • Suppress when permanently ineligible. Call RateUsTriggerUtils.Suppress() when the player has already rated (never ask again). Unsuppress() to allow again. Persisted to PlayerPrefs and re-applied automatically at module setup — survives app restarts.
  • Issue the ask-later delay on the corresponding dialog button (IssueAskLaterDelay).
  • Consume the request each time it appears: open the dialog, then destroy the RateUsTriggerRequest entity.

Once per session

RateUsTriggerFiredTag is created after the first emit and lives for the app session, so the popup is offered at most once per launch (matches legacy ReviewTrigger). It is not persisted — a new launch can fire again, gated by the consumer's Suppress.

Setup

Create a RateUsTriggerModule asset, add it to the persistent world bootstrap (alongside UserActivityTrackerModule). Thresholds are edited inline on the module asset.

Game-specific consumers (the UI packages) depend on this package, consume the signal, and present their own popup.