Skip to content

Commit 78dcba9

Browse files
committed
feat: Disable ads for logged in users
1 parent 434240c commit 78dcba9

File tree

8 files changed

+169
-50
lines changed

8 files changed

+169
-50
lines changed

src/components/Doc.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Toc } from './Toc'
99
import { twMerge } from 'tailwind-merge'
1010
import { TocMobile } from './TocMobile'
1111
import { GamLeader } from './Gam'
12+
import { AdGate } from '~/contexts/AdsContext'
1213
import { useWidthToggle } from '~/components/DocsLayout'
1314
import {
1415
BsArrowsCollapseVertical,
@@ -123,7 +124,9 @@ export function Doc({
123124
isTocVisible && '!pr-0'
124125
)}
125126
>
126-
<GamLeader />
127+
<AdGate>
128+
<GamLeader />
129+
</AdGate>
127130
{title ? (
128131
<div className="flex items-center justify-between gap-4">
129132
<DocTitle>{title}</DocTitle>

src/components/DocsLayout.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { DocsCalloutBytes } from '~/components/DocsCalloutBytes'
2626
import { twMerge } from 'tailwind-merge'
2727
import { partners } from '~/utils/partners'
2828
import { GamFooter, GamLeftRailSquare, GamRightRailSquare } from './Gam'
29+
import { AdGate } from '~/contexts/AdsContext'
2930
import { SearchButton } from './SearchButton'
3031

3132
// Create context for width toggle state
@@ -575,9 +576,11 @@ export function DocsLayout({
575576
>
576577
{children}
577578
</div>
578-
<div className="mb-8 !py-0 mx-auto max-w-full overflow-x-hidden">
579-
<GamFooter />
580-
</div>
579+
<AdGate>
580+
<div className="mb-8 !py-0 mx-auto max-w-full overflow-x-hidden">
581+
<GamFooter />
582+
</div>
583+
</AdGate>
581584
<div className="sticky flex items-center flex-wrap bottom-2 z-10 right-0 text-xs md:text-sm px-1 print:hidden">
582585
<div className="w-1/2 px-1 flex justify-end flex-wrap">
583586
{prevItem ? (
@@ -689,13 +692,17 @@ export function DocsLayout({
689692
</div>
690693
) : null}
691694

692-
<div className="bg-white dark:bg-black/40 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-2 space-y-2 rounded-l-lg">
693-
<GamRightRailSquare />
694-
</div>
695+
<AdGate>
696+
<div className="bg-white dark:bg-black/40 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-2 space-y-2 rounded-l-lg">
697+
<GamRightRailSquare />
698+
</div>
699+
</AdGate>
695700

696-
<div className="bg-white dark:bg-black/40 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-2 space-y-2 rounded-l-lg">
697-
<GamLeftRailSquare />
698-
</div>
701+
<AdGate>
702+
<div className="bg-white dark:bg-black/40 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-2 space-y-2 rounded-l-lg">
703+
<GamLeftRailSquare />
704+
</div>
705+
</AdGate>
699706

700707
{/* <div className="bg-white dark:bg-black/40 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-4 space-y-2 rounded-l-lg">
701708
<Carbon />

src/components/LandingPageGad.tsx

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,44 @@
11
import { Link } from '@tanstack/react-router'
22
import { GamFooter } from './Gam'
3+
import { AdGate } from '~/contexts/AdsContext'
34

45
export default function LandingPageGad() {
56
return (
6-
<div className={`lg:max-[400px] px-4 mx-auto`}>
7-
<div className="flex flex-col gap-4 items-center">
8-
<div className="shadow-lg rounded-lg overflow-hidden bg-white dark:bg-gray-800 dark:text-white mx-auto">
9-
<GamFooter />
10-
</div>
11-
<div
12-
className="text-xs bg-gray-500 bg-opacity-10 py-2 px-4 rounded text-gray-600 dark:text-gray-300
7+
<AdGate>
8+
<div className={`lg:max-[400px] px-4 mx-auto`}>
9+
<div className="flex flex-col gap-4 items-center">
10+
<div className="shadow-lg rounded-lg overflow-hidden bg-white dark:bg-gray-800 dark:text-white mx-auto">
11+
<GamFooter />
12+
<div
13+
className="text-xs bg-gray-500 bg-opacity-10 py-2 px-4 rounded text-gray-600 dark:text-gray-300
1314
dark:bg-opacity-20 self-center text-center w-[500px] max-w-full space-y-2"
14-
>
15-
<div>
16-
<span className="font-medium italic">
17-
An ad on an open source project?
18-
</span>{' '}
19-
<span className="font-black">What is this, 1999?</span>
20-
</div>
21-
<div>
22-
<span className="font-medium italic">Please...</span> TanStack is
23-
100% privately owned, with no paid products, venture capital, or
24-
acquisition plans. We're a small team dedicated to creating software
25-
used by millions daily. What did you expect?
26-
</div>
27-
<div>
28-
<Link
29-
to="/ethos"
30-
className="text-gray-600 dark:text-gray-200 font-bold underline"
3115
>
32-
Check out our ethos
33-
</Link>{' '}
34-
to learn more about how we plan on sticking around (and staying
35-
relevant) for the long-haul.
16+
<div>
17+
<span className="font-medium italic">
18+
An ad on an open source project?
19+
</span>{' '}
20+
<span className="font-black">What is this, 1999?</span>
21+
</div>
22+
<div>
23+
<span className="font-medium italic">Please...</span> TanStack
24+
is 100% privately owned, with no paid products, venture capital,
25+
or acquisition plans. We're a small team dedicated to creating
26+
software used by millions daily. What did you expect?
27+
</div>
28+
<div>
29+
<Link
30+
to="/ethos"
31+
className="text-gray-600 dark:text-gray-200 font-bold underline"
32+
>
33+
Check out our ethos
34+
</Link>{' '}
35+
to learn more about how we plan on sticking around (and staying
36+
relevant) for the long-haul.
37+
</div>
38+
</div>
3639
</div>
3740
</div>
3841
</div>
39-
</div>
42+
</AdGate>
4043
)
4144
}

src/contexts/AdsContext.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from 'react'
2+
import { useAdsPreference } from '~/stores/userSettings'
3+
4+
export function AdGate({
5+
children,
6+
deferUntilReady = false,
7+
}: {
8+
children: React.ReactNode
9+
deferUntilReady?: boolean
10+
}) {
11+
const { status, adsEnabled } = useAdsPreference()
12+
if (status === 'unknown') return deferUntilReady ? <>{children}</> : null
13+
return adsEnabled ? <>{children}</> : null
14+
}

src/routes/_libraries/account.$.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,49 @@
1+
import * as React from 'react'
12
import {
23
SignedIn,
34
SignedOut,
45
UserProfile,
56
SignInButton,
67
SignOutButton,
8+
useUser,
79
} from '@clerk/tanstack-react-start'
10+
import { useUserSettingsStore } from '~/stores/userSettings'
811
import { FaSignOutAlt } from 'react-icons/fa'
912

1013
export const Route = createFileRoute({
1114
component: AccountPage,
1215
})
1316

1417
function AccountPage() {
18+
const { isLoaded } = useUser()
19+
const adsDisabled = useUserSettingsStore((s) => s.settings.adsDisabled)
20+
const toggleAds = useUserSettingsStore((s) => s.toggleAds)
21+
1522
return (
1623
<div className="min-h-screen mx-auto p-4 md:p-8 w-full">
1724
<SignedIn>
1825
<div className="space-y-2">
1926
<UserProfile path="/account" />
27+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-4">
28+
<div className="flex items-center justify-between">
29+
<label className="flex items-start gap-3 cursor-pointer">
30+
<input
31+
type="checkbox"
32+
className="h-4 w-4 accent-blue-600 my-1"
33+
checked={adsDisabled}
34+
onChange={toggleAds}
35+
disabled={!isLoaded}
36+
aria-label="Disable Ads"
37+
/>
38+
<div>
39+
<div className="font-semibold">Disable Ads</div>
40+
<div className="text-sm opacity-70">
41+
Hide ads when you are signed in
42+
</div>
43+
</div>
44+
</label>
45+
</div>
46+
</div>
2047
<SignOutButton>
2148
<button className="flex gap-2 items-center text-xs bg-gray-500 hover:bg-red-500 text-white font-black uppercase py-2 px-4 rounded-md transition-colors">
2249
<FaSignOutAlt /> Log Out

src/routes/stats/npm/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
GamLeftRailSquare,
2626
GamRightRailSquare,
2727
} from '~/components/Gam'
28+
import { AdGate } from '~/contexts/AdsContext'
2829
import { twMerge } from 'tailwind-merge'
2930
import logoColor100w from '~/images/logo-color-100w.png'
3031
import {
@@ -2167,23 +2168,29 @@ function RouteComponent() {
21672168
</a>
21682169
</div>
21692170
</div>
2170-
<div className="bg-white dark:bg-black/40 shadow-xl flex flex-col p-4 space-y-2 rounded-lg">
2171-
<div className="w-[258px] xl:w-[300px] overflow-x-hidden">
2172-
<GamRightRailSquare />
2171+
<AdGate>
2172+
<div className="bg-white dark:bg-black/40 shadow-xl flex flex-col p-4 space-y-2 rounded-lg">
2173+
<div className="w-[258px] xl:w-[300px] overflow-x-hidden">
2174+
<GamRightRailSquare />
2175+
</div>
21732176
</div>
2174-
</div>
2177+
</AdGate>
21752178

2176-
<div className="bg-white dark:bg-black/40 shadow-xl flex flex-col p-4 space-y-2 rounded-lg">
2177-
<div className="w-[258px] xl:w-[300px] overflow-x-hidden">
2178-
<GamLeftRailSquare />
2179+
<AdGate>
2180+
<div className="bg-white dark:bg-black/40 shadow-xl flex flex-col p-4 space-y-2 rounded-lg">
2181+
<div className="w-[258px] xl:w-[300px] overflow-x-hidden">
2182+
<GamLeftRailSquare />
2183+
</div>
21792184
</div>
2180-
</div>
2185+
</AdGate>
21812186
</div>
21822187
</div>
21832188
</div>
2184-
<div className="!mt-24 mx-auto max-w-full overflow-x-hidden">
2185-
<GamFooter />
2186-
</div>
2189+
<AdGate>
2190+
<div className="!mt-24 mx-auto max-w-full overflow-x-hidden">
2191+
<GamFooter />
2192+
</div>
2193+
</AdGate>
21872194
</div>
21882195
)
21892196
}

src/stores/userSettings.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { useUser } from '@clerk/tanstack-react-start'
2+
import { create } from 'zustand'
3+
import { persist, createJSONStorage } from 'zustand/middleware'
4+
5+
export type UserSettings = {
6+
adsDisabled: boolean
7+
}
8+
9+
type UserSettingsState = {
10+
settings: UserSettings
11+
hasHydrated: boolean
12+
setHasHydrated: (value: boolean) => void
13+
toggleAds: () => void
14+
}
15+
16+
export const useUserSettingsStore = create<UserSettingsState>()(
17+
persist(
18+
(set, get) => ({
19+
settings: {
20+
adsDisabled: false,
21+
},
22+
hasHydrated: false,
23+
setHasHydrated: (value) => set({ hasHydrated: value }),
24+
toggleAds: () =>
25+
set({
26+
settings: {
27+
...get().settings,
28+
adsDisabled: !get().settings.adsDisabled,
29+
},
30+
}),
31+
}),
32+
{
33+
name: 'user_settings_v1',
34+
storage: createJSONStorage(() => localStorage),
35+
onRehydrateStorage: () => (state, error) => {
36+
if (!state || error) return
37+
state.setHasHydrated(true)
38+
},
39+
partialize: (state) => ({ settings: state.settings }),
40+
}
41+
)
42+
)
43+
44+
export function useAdsPreference() {
45+
const { isSignedIn } = useUser()
46+
const { settings, hasHydrated } = useUserSettingsStore((s) => ({
47+
settings: s.settings,
48+
hasHydrated: s.hasHydrated,
49+
}))
50+
51+
const adsEnabled = isSignedIn && !settings.adsDisabled
52+
const status = hasHydrated ? (adsEnabled ? 'enabled' : 'disabled') : 'unknown'
53+
return { status: status as 'unknown' | 'enabled' | 'disabled', adsEnabled }
54+
}

src/styles/app.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ mark {
678678

679679
/* Clerk */
680680

681+
.cl-cardBox {
682+
@apply shadow-md;
683+
}
684+
681685
.user-profile-container {
682686
container-type: inline-size;
683687
}

0 commit comments

Comments
 (0)