diff --git a/.env.example b/.env.example index ccd98103..cb4c2a2d 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1 @@ -CONFIGCAT_API_KEY= -AMPLITUDE_API_KEY= NEXT_PUBLIC_FATHOM_ID= diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index dbde08cb..a13242b7 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -14,23 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v2.3.1 - with: - persist-credentials: false + uses: actions/checkout@v4 - - name: Cache 📦 - uses: actions/cache@v1 - with: - path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - name: Setup Node Environment ⬢ - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 20 + cache: 'npm' - name: Install 🔧 run: npm install diff --git a/app/api/events/route.ts b/app/api/events/route.ts deleted file mode 100644 index 28a19ee4..00000000 --- a/app/api/events/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NextResponse } from "next/server" -import { init, track, Types } from "@amplitude/analytics-node" -import { type Event } from "@/lib/types" - -init(process.env.AMPLITUDE_API_KEY!, { - logLevel: Types.LogLevel.Warn, -}) - -export async function POST(request: Request) { - const event = (await request.json()) as Event - track( - event.name, - { variantIds: event.variantIds }, - { - user_id: event.userId, - } - ) - return new NextResponse() -} diff --git a/app/components/EventLink.tsx b/app/components/EventLink.tsx index 1ab8499b..d4d8a739 100644 --- a/app/components/EventLink.tsx +++ b/app/components/EventLink.tsx @@ -1,10 +1,8 @@ "use client" import Link from "next/link" -import { type ReactNode, useEffect, useContext } from "react" -import { type Event } from "@/lib/types" +import { type ReactNode, useEffect } from "react" import va from "@vercel/analytics" -import { VariantContext } from "../context/VariantProvider" type Props = { children: ReactNode @@ -17,29 +15,11 @@ type Props = { onClick?: () => void } -async function logEvent(event: Event): Promise { - // Log vercel - event.params = event.params || {} - if (event.variantIds) { - event.variantIds.forEach((id) => { - event.params![id.substring(0, id.length - 1)] = id.slice(-1) - }) - } - va.track( - event.name, - Object.keys(event.params).length > 0 ? event.params : undefined - ) - - // Log amplitude - if (event.userId && event.variantIds) { - fetch("/api/events", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(event), - }) - } +async function logEvent( + name: string, + params?: Record +): Promise { + va.track(name, params && Object.keys(params).length > 0 ? params : undefined) } export default function EventLink({ @@ -52,36 +32,20 @@ export default function EventLink({ className, onClick, }: Props) { - const variantCtx = useContext(VariantContext) - useEffect(() => { - async function log() { - await logEvent({ - name: event + " Viewed", - params, - userId: variantCtx.userId, - variantIds: variantCtx.ids, - }) - } if (event && logView) { - // The timeout puts this in the back of the event queue, giving vercel analytics time to load. setTimeout(() => { - log() + logEvent(event + " Viewed", params) }, 0) } - }, [variantCtx, event, params, logView]) + }, [event, params, logView]) const handleClick = async () => { if (onClick) { onClick() } if (event) { - await logEvent({ - name: event + " Clicked", - params, - userId: variantCtx.userId, - variantIds: variantCtx.ids, - }) + await logEvent(event + " Clicked", params) } } diff --git a/app/components/Features.tsx b/app/components/Features.tsx index 041b4a28..b770b67e 100644 --- a/app/components/Features.tsx +++ b/app/components/Features.tsx @@ -1,12 +1,7 @@ import EventLink from "./EventLink" -import { getFeatures } from "@/lib/variants" -import { getUserId } from "@/lib/users" import items from "@/lib/features" -import { Features } from "@/lib/types" - -export default async function Features() { - const content = await getFeatures(getUserId()) +export default function Features() { return (

- {content.title} + Build on decentralized data without the headache.

{items.map(function (f, i) { diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx index e9ad2521..e422c0fc 100644 --- a/app/components/Hero.tsx +++ b/app/components/Hero.tsx @@ -1,38 +1,24 @@ import ButtonLink from "./ButtonLink" import Terminal from "./Terminal" -import { getHero } from "@/lib/variants" -import { getUserId } from "@/lib/users" -import { type Hero } from "@/lib/types" - -export default async function Hero() { - const content = await getHero(getUserId()) +export default function Hero() { return (

- + Read+write from anywhere

- + The decentralized cloud + database.

- + Tableland is an open source, permissionless cloud database built + on SQLite. Read and write tamperproof data from apps, data + pipelines, or EVM smart contracts.

- {content.cta} + Enter Studio

diff --git a/app/context/VariantProvider.tsx b/app/context/VariantProvider.tsx deleted file mode 100644 index 4a13e42f..00000000 --- a/app/context/VariantProvider.tsx +++ /dev/null @@ -1,17 +0,0 @@ -"use client" - -import { type ReactNode, createContext } from "react" -import { type VariantCtx } from "@/lib/types" - -type Props = { - children: ReactNode - ctx: VariantCtx -} - -export const VariantContext = createContext({}) - -export default function VariantProvider({ children, ctx }: Props) { - return ( - {children} - ) -} diff --git a/app/layout.tsx b/app/layout.tsx index 2e4c318e..475be3c7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,8 +7,6 @@ import FooterNav from "./components/FooterNav" import Fathom from "./components/Fathom" import { Analytics } from "@vercel/analytics/react" import { SpeedInsights } from "@vercel/speed-insights/next" -import VariantProvider from "./context/VariantProvider" -import { getContext } from "@/lib/variants" const title = "Tableland: The decentralized cloud database" const description = @@ -49,23 +47,15 @@ const orbitron = Orbitron({ variable: "--font-orbitron", }) -export default async function RootLayout({ - children, -}: { - children: ReactNode -}) { - const variantCtx = await getContext() - +export default function RootLayout({ children }: { children: ReactNode }) { return ( - - - {children} - - + + {children} + diff --git a/lib/configcat.ts b/lib/configcat.ts deleted file mode 100644 index 8bc3a303..00000000 --- a/lib/configcat.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { getClient, IConfigCatClient } from "configcat-js-ssr" - -let _client: IConfigCatClient - -function client(): IConfigCatClient { - if (_client) { - return _client - } - _client = getClient(process.env.CONFIGCAT_API_KEY!) - return _client -} - -export async function getBoolFlag( - key: string, - userId?: string -): Promise { - const value = await client().getValueAsync( - key, - false, - userId ? { identifier: userId } : undefined - ) - - return value ? "B" : "A" -} - -export async function getStringFlag( - key: string, - userId?: string -): Promise { - const value = await client().getValueAsync( - key, - "A", - userId ? { identifier: userId } : undefined - ) - return value -} diff --git a/lib/content.tsx b/lib/content.tsx deleted file mode 100644 index a4bff740..00000000 --- a/lib/content.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { type AB, type ABC, type Hero, type Features } from "@/lib/types" - -export const hero: ABC = { - flags: { - main: "hero", - sections: ["heroTag", "heroTitle", "heroLead", "heroCta"], - }, - a: { - tag: 'Read+write from anywhere', - title: 'The decentralized cloud database.', - lead: "Tableland is an open source, permissionless cloud database built on SQLite. Read and write tamperproof data from apps, data pipelines, or EVM smart contracts.", - cta: "Enter Studio", - }, - b: { - tag: "Read, write, grant access", - title: 'Build data-driven web3 apps.', - lead: "Built for performance. Designed for simplicity. Welcome to Tableland, the first web3-native database that allows you to write and query data from any app or smart contract.", - cta: "Enter Studio", - }, - c: { - tag: "SQL developer tools", - title: - 'Store and query contract metadata with SQL.', - lead: "Tableland is a decentralized database built on the SQLite engine, providing SQL APIs deployed to multiple blockchains, including Ethereum, Polygon, Arbitrum, and Optimism.", - cta: "Enter Studio", - }, -} - -export const features: AB = { - flags: { - main: "features", - sections: ["featuresTitle"], - }, - a: { - title: "Build on decentralized data without the headache.", - }, - b: { - title: "The only user-friendly Web3 database.", - }, -} diff --git a/lib/types.ts b/lib/types.ts index c1a8beff..6b868a44 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,46 +1,5 @@ import { type IconType } from "react-icons" -export type VariantCtx = { - userId?: string - ids?: string[] -} - -export type Event = { - name: string - params?: Record - userId?: string - variantIds?: string[] -} - -export type Flags = { - main: string - sections: string[] -} - -export type AB = { - flags: Flags - a: T - b: T -} - -export type ABC = { - flags: Flags - a: T - b: T - c: T -} - -export type Hero = { - tag: string - title: string - lead: string - cta: string -} - -export type Features = { - title: string -} - export type Feature = { title: string icon: IconType diff --git a/lib/users.ts b/lib/users.ts deleted file mode 100644 index 2ec581d8..00000000 --- a/lib/users.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { cookies, headers } from "next/headers" -import { anonUserCookieName } from "@/middleware" - -// Returns an anonomous user identifier from a cookie or the `set-cookie` header that is initially set via middleware. -export function getUserId(): string | undefined { - const cookieStore = cookies() - - // Try to get from cookie store - let id = cookieStore.get(anonUserCookieName)?.value - if (id) { - return id - } - - // Try to get from `set-cookie` header - const header = headers().get("set-cookie") - if (header) { - const match = header.match(/id=([a-z0-9]+);/i) - return match ? match[1] : undefined - } -} diff --git a/lib/variants.ts b/lib/variants.ts deleted file mode 100644 index 139d5f1d..00000000 --- a/lib/variants.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { getStringFlag, getBoolFlag } from "@/lib/configcat" -import { hero, features } from "@/lib/content" -import { - type VariantCtx, - type AB, - type ABC, - type Features, - type Hero, -} from "@/lib/types" -import { getUserId } from "@/lib/users" - -export async function getContext(): Promise { - const userId = getUserId() - - const [hero, features] = await Promise.all([ - getHeroFlags(userId), - getFeaturesFlags(userId), - ]) - - return { - userId, - ids: new Array().concat(hero, features), - } -} - -export async function getHeroFlags(userId?: string): Promise { - // Get main prerequisite flag - const main = await getStringFlag(hero.flags["main"], userId) - - // Collect section variants for reporting - // Note: These will currently all match, ie, all be of flavor A/B/C, but we still need to have them - // broken out for comparison with other tests later on. - return hero.flags.sections.map((v) => { - return v + main - }) -} - -export async function getHero(userId?: string): Promise { - const ids = await getHeroFlags(userId) - return getContent("hero", hero, ids) -} - -export async function getFeaturesFlags(userId?: string): Promise { - // Get main prerequisite flag - const main = await getBoolFlag(features.flags["main"], userId) - - // Collect section variants for reporting - // Note: These will currently all match, ie, all be of flavor A/B, but we still need to have them - // broken out for comparison with other tests later on. - return features.flags.sections.map((v) => { - return v + main - }) -} - -export async function getFeatures(userId?: string): Promise { - const ids = await getFeaturesFlags(userId) - return getContent("features", features, ids) -} - -function getContent( - flagPrefix: string, - test: AB | ABC, - variantIds: string[] -): T { - const content: any = {} - variantIds.forEach((id) => { - let section = id.replace(flagPrefix, "").toLowerCase() - section = section.substring(0, section.length - 1) - content[section] = (test as any)[id.slice(-1).toLowerCase()][section] - }) - return content -} diff --git a/middleware.ts b/middleware.ts deleted file mode 100644 index 2470d9ed..00000000 --- a/middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server" - -// Anonomous user identifier -export const anonUserCookieName = "id" - -export async function middleware(req: NextRequest) { - // Get or create identifier - const value = req.cookies.get(anonUserCookieName)?.value || newId() - - // Add the cookie to the response if it's not present - const res = NextResponse.next() - if (!req.cookies.has(anonUserCookieName)) { - const date = new Date() - date.setDate(new Date().getDate() + 7) // one week - res.cookies.set(anonUserCookieName, value, { - expires: date, - }) - } - return res -} - -// Returns a random (not necessarily unique) id of length 21. -function newId() { - return ( - Math.random().toString(36).substring(2) + - Math.random().toString(36).substring(2) - ) -} diff --git a/package-lock.json b/package-lock.json index 985c2d8a..937d3dfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,10 @@ "version": "0.0.0", "license": "MIT AND Apache-2.0", "dependencies": { - "@amplitude/analytics-node": "1.3.5", "@tailwindcss/typography": "0.5.9", "@vercel/analytics": "1.1.1", "@vercel/speed-insights": "^1.0.2", "autoprefixer": "10.4.16", - "configcat-js-ssr": "7.1.1", "embla-carousel-autoplay": "7.1.0", "embla-carousel-react": "7.1.0", "eslint": "8.52.0", @@ -57,30 +55,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@amplitude/analytics-core": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-1.2.5.tgz", - "integrity": "sha512-V7CVlHVN+1diKiOpdp2bCPZ0mbS4CmUYF+v+eXDwVfJL3M/t3sVcT1apXnmVYGYi14cGu9hQOD11rD6qKbUOsw==", - "dependencies": { - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/analytics-node": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-node/-/analytics-node-1.3.5.tgz", - "integrity": "sha512-PVdCs3duu4OQNIQ3ENCKdm6wnoMAOdpSZD3tpWbEQVKh08FlxWLrR8eQN48nvhs+i6yEwGGVr1SfRDPpsMog1w==", - "dependencies": { - "@amplitude/analytics-core": "^1.2.5", - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/analytics-types": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-1.3.4.tgz", - "integrity": "sha512-tR70gzqFkEzX9QpxvWYMfLCledT7vMhgd3d4/bkp3nnGXTOORaVUOCcSgOyxyuFdSx84T61aP/eZPKIcZcaP+A==" - }, "node_modules/@babel/runtime": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", @@ -844,11 +818,6 @@ "has-symbols": "^1.0.3" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -904,15 +873,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -1200,17 +1160,6 @@ "simple-swizzle": "^0.2.2" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1224,24 +1173,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/configcat-common": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/configcat-common/-/configcat-common-8.2.0.tgz", - "integrity": "sha512-NWCXiiYEPU83wwJkG/vzBGNyvVNRXn0+raIOmE1iG95wKjRkEwB63HdZFD4nCCx449smM9dSNu/Hl+FinNw30A==", - "dependencies": { - "tslib": "^2.4.1" - } - }, - "node_modules/configcat-js-ssr": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/configcat-js-ssr/-/configcat-js-ssr-7.1.1.tgz", - "integrity": "sha512-zITzPzueuRp3SnV/oHQ7qJACLFjKHR89ZcLfu9TqsNrfgP6cmEv8OmSFuMc41U4c3fx5qjKhNRckvmErD5aKqg==", - "dependencies": { - "axios": "^0.27.2", - "configcat-common": "^8.1.1", - "tslib": "^2.4.1" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1384,14 +1315,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-libc": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", @@ -2190,25 +2113,6 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, - "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2217,19 +2121,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3153,25 +3044,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", diff --git a/package.json b/package.json index f81f633a..6709f227 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "description": "The Tableland homepage", "version": "0.0.0", "private": true, + "engines": { + "node": "20.x" + }, "repository": { "type": "git", "url": "git+https://github.com/tablelandnetwork/site-tableland.git" @@ -32,12 +35,10 @@ "format": "npm run prettier:fix && npm run lint:fix" }, "dependencies": { - "@amplitude/analytics-node": "1.3.5", "@tailwindcss/typography": "0.5.9", "@vercel/analytics": "1.1.1", "@vercel/speed-insights": "^1.0.2", "autoprefixer": "10.4.16", - "configcat-js-ssr": "7.1.1", "embla-carousel-autoplay": "7.1.0", "embla-carousel-react": "7.1.0", "eslint": "8.52.0",