|
| 1 | +import { useSDK } from "@/context/sdk" |
| 2 | +import { Persist, persisted } from "@/utils/persist" |
| 3 | +import { SessionStatus } from "@opencode-ai/sdk/v2" |
| 4 | +import { onCleanup } from "solid-js" |
| 5 | +import { createStore } from "solid-js/store" |
| 6 | +import { useSessionLayout } from "./session-layout" |
| 7 | +import { useDialog } from "@opencode-ai/ui/context" |
| 8 | +import { DialogUsageExceeded } from "@/components/dialog-usage-exceeded" |
| 9 | +import { useI18n } from "@opencode-ai/ui/context" |
| 10 | + |
| 11 | +const GO_UPSELL_FREE_TIER_LAST_SEEN_AT = "go_upsell_last_seen_at" |
| 12 | +const GO_UPSELL_FREE_TIER_DONT_SHOW = "go_upsell_dont_show" |
| 13 | +const GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT = "go_upsell_account_rate_limit_last_seen_at" |
| 14 | +const GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW = "go_upsell_account_rate_limit_dont_show" |
| 15 | +const GO_UPSELL_WINDOW = 86_400_000 // 24 hrs |
| 16 | +const GO_UPSELL_PROVIDERS = new Set(["opencode", "opencode-go"]) |
| 17 | + |
| 18 | +function goUpsellKeys(status: SessionStatus) { |
| 19 | + if (status.type !== "retry" || !status.action) return |
| 20 | + const { action } = status |
| 21 | + if (!GO_UPSELL_PROVIDERS.has(action.provider)) return |
| 22 | + if (action.reason === "free_tier_limit") { |
| 23 | + return { |
| 24 | + lastSeenAt: GO_UPSELL_FREE_TIER_LAST_SEEN_AT, |
| 25 | + dontShow: GO_UPSELL_FREE_TIER_DONT_SHOW, |
| 26 | + } as const |
| 27 | + } |
| 28 | + if (action.reason === "account_rate_limit") { |
| 29 | + return { |
| 30 | + lastSeenAt: GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT, |
| 31 | + dontShow: GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW, |
| 32 | + } as const |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +export function useUsageExceededDialogs() { |
| 37 | + const sdk = useSDK() |
| 38 | + const dialog = useDialog() |
| 39 | + const { params } = useSessionLayout() |
| 40 | + const { t, locale } = useI18n() |
| 41 | + const isEnglish = () => locale() === "en" |
| 42 | + |
| 43 | + const [goUpsellState, setGoUpsellState] = persisted( |
| 44 | + Persist.global("go-upsell"), |
| 45 | + createStore({ |
| 46 | + [GO_UPSELL_FREE_TIER_LAST_SEEN_AT]: null as null | number, |
| 47 | + [GO_UPSELL_FREE_TIER_DONT_SHOW]: null as null | number, |
| 48 | + [GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT]: null as null | number, |
| 49 | + [GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW]: null as null | number, |
| 50 | + }), |
| 51 | + ) |
| 52 | + |
| 53 | + onCleanup( |
| 54 | + sdk.event.on("session.status", (evt) => { |
| 55 | + if (evt.properties.sessionID !== params.id) return |
| 56 | + if (evt.properties.status.type !== "retry") return |
| 57 | + const { action } = evt.properties.status |
| 58 | + if (!action) return |
| 59 | + if (dialog.active) return |
| 60 | + |
| 61 | + const keys = goUpsellKeys(evt.properties.status) |
| 62 | + if (!keys) return |
| 63 | + |
| 64 | + const seen = goUpsellState[keys.lastSeenAt] |
| 65 | + if (seen && Date.now() - seen < GO_UPSELL_WINDOW) return |
| 66 | + if (goUpsellState[keys.dontShow]) return |
| 67 | + |
| 68 | + if (action.reason === "free_tier_limit") { |
| 69 | + dialog.show(() => ( |
| 70 | + <DialogUsageExceeded |
| 71 | + title={isEnglish() ? action.title : t("dialog.usageExceeded.freeTier.title")} |
| 72 | + description={isEnglish() ? action.message : t("dialog.usageExceeded.freeTier.description")} |
| 73 | + actionLabel={isEnglish() ? action.label : t("dialog.usageExceeded.freeTier.actionLabel")} |
| 74 | + link={action.link} |
| 75 | + onClose={(dontShowAgain) => { |
| 76 | + setGoUpsellState(keys.lastSeenAt, Date.now()) |
| 77 | + if (dontShowAgain) setGoUpsellState(keys.dontShow, Date.now()) |
| 78 | + else { |
| 79 | + void import("../../components/dialog-connect-provider").then((x) => |
| 80 | + dialog.show(() => <x.DialogConnectProvider provider="opencode-go" />), |
| 81 | + ) |
| 82 | + } |
| 83 | + }} |
| 84 | + /> |
| 85 | + )) |
| 86 | + } else if (action.reason === "account_rate_limit") { |
| 87 | + dialog.show(() => ( |
| 88 | + <DialogUsageExceeded |
| 89 | + title={isEnglish() ? action.title : t("dialog.usageExceeded.accountRateLimit.title")} |
| 90 | + description={isEnglish() ? action.message : t("dialog.usageExceeded.accountRateLimit.description")} |
| 91 | + actionLabel={isEnglish() ? action.label : t("dialog.usageExceeded.accountRateLimit.actionLabel")} |
| 92 | + link={action.link} |
| 93 | + onClose={(dontShowAgain) => { |
| 94 | + setGoUpsellState(keys.lastSeenAt, Date.now()) |
| 95 | + if (dontShowAgain) setGoUpsellState(keys.dontShow, Date.now()) |
| 96 | + }} |
| 97 | + /> |
| 98 | + )) |
| 99 | + } |
| 100 | + }), |
| 101 | + ) |
| 102 | +} |
0 commit comments