Skip to content

Commit

Permalink
Alert after user does not upgrade after 7 days
Browse files Browse the repository at this point in the history
  • Loading branch information
unnoq committed Feb 18, 2024
1 parent f030d1e commit 099eca4
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 16 deletions.
17 changes: 2 additions & 15 deletions @web/components/subscription-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Subscription } from '@api/lib/subscription'
import { Button } from '@web/components/ui/button'
import { useLifetimeAccessSubscription } from '@web/hooks/use-lifetime-access-subscription'
import { Tenant, useTenant } from '@web/lib/auth'
import { env } from '@web/lib/env'
import { trpc } from '@web/lib/trpc'
Expand All @@ -19,21 +20,7 @@ const teamFeatures = [...personalFeatures, 'Unlimited members']
export function SubscriptionCard() {
const tenant = useTenant()

const subscription = match(tenant.type)
.with('user', () => {
return tenant.publicMetadata.subscriptions.find(
(s) =>
s.variantId === env.LEMONSQUEEZY_PERSONAL_LIFETIME_ACCESS_VARIANT_ID &&
s.expiresAt === null,
)
})
.with('organization', () => {
return tenant.publicMetadata.subscriptions.find(
(s) =>
s.variantId === env.LEMONSQUEEZY_TEAM_LIFETIME_ACCESS_VARIANT_ID && s.expiresAt === null,
)
})
.exhaustive()
const subscription = useLifetimeAccessSubscription()

return (
<div>
Expand Down
23 changes: 23 additions & 0 deletions @web/hooks/use-lifetime-access-subscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useTenant } from '@web/lib/auth'
import { env } from '@web/lib/env'
import { match } from 'ts-pattern'

export function useLifetimeAccessSubscription() {
const tenant = useTenant()

return match(tenant.type)
.with('user', () => {
return tenant.publicMetadata.subscriptions.find(
(s) =>
s.variantId === env.LEMONSQUEEZY_PERSONAL_LIFETIME_ACCESS_VARIANT_ID &&
s.expiresAt === null,
)
})
.with('organization', () => {
return tenant.publicMetadata.subscriptions.find(
(s) =>
s.variantId === env.LEMONSQUEEZY_TEAM_LIFETIME_ACCESS_VARIANT_ID && s.expiresAt === null,
)
})
.exhaustive()
}
5 changes: 4 additions & 1 deletion @web/layouts/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RedirectToSignIn, SignedIn, SignedOut } from '@clerk/clerk-react'
import { BillingProvider } from '@web/providers/billing'
import { Outlet } from 'react-router-dom'

export function AuthLayout() {
Expand All @@ -16,7 +17,9 @@ export function AuthLayout() {
/>
</SignedOut>
<SignedIn>
<Outlet />
<BillingProvider>
<Outlet />
</BillingProvider>
</SignedIn>
</>
)
Expand Down
3 changes: 3 additions & 0 deletions @web/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function useAuthedUser() {
}

export type Tenant = BaseTenant & {
createdAt: Date | null | undefined
publicMetadata: TenantPublicMetadata
}

Expand All @@ -36,6 +37,7 @@ export function useTenant(): Tenant {
.with('org:admin', () => 'admin' as const)
.with('org:member', () => 'member' as const)
.exhaustive(),
createdAt: organization?.createdAt,
publicMetadata: tenantPublicMetadataSchema.parse(organization?.publicMetadata),
}
}
Expand All @@ -44,6 +46,7 @@ export function useTenant(): Tenant {
type: 'user',
id: auth.userId,
role: 'admin',
createdAt: user.createdAt,
publicMetadata: tenantPublicMetadataSchema.parse(user.publicMetadata),
}
}
57 changes: 57 additions & 0 deletions @web/providers/billing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { SubscriptionCard } from '@web/components/subscription-card'
import { Dialog, DialogContent } from '@web/components/ui/dialog'
import { ScrollArea } from '@web/components/ui/scroll-area'
import { useLifetimeAccessSubscription } from '@web/hooks/use-lifetime-access-subscription'
import { useListenToTRPCMutation } from '@web/hooks/use-listen-to-trpc-mutation'
import { useTenant } from '@web/lib/auth'
import { trpc } from '@web/lib/trpc'
import { useEffect, useRef, useState } from 'react'

export function BillingProvider(props: { children: React.ReactNode }) {
const [open, setOpen] = useState(false)
const lastCheckoutAt = useRef<Date | null>(null)

useListenToTRPCMutation(trpc.billing.checkout, {
onSuccess() {
lastCheckoutAt.current = new Date()
setOpen(false)
},
})

const subscription = useLifetimeAccessSubscription()
const tenant = useTenant()

useEffect(() => {
if (subscription) return

const id = window.setInterval(() => {
if (lastCheckoutAt.current && lastCheckoutAt.current.getTime() > Date.now() - 1000 * 60 * 5) {
return
}

if (tenant.createdAt && tenant.createdAt.getTime() > Date.now() - 1000 * 60 * 60 * 24 * 7) {
return
}

setOpen(true)
}, 1000 * 60)

return () => {
window.clearInterval(id)
}
}, [subscription, tenant])

return (
<>
{props.children}

<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="sm:max-w-6xl max-h-screen overflow-auto flex">
<ScrollArea className="flex-1 pt-8">
<SubscriptionCard />
</ScrollArea>
</DialogContent>
</Dialog>
</>
)
}

0 comments on commit 099eca4

Please sign in to comment.