Skip to content

Commit

Permalink
feat: generic feature enabled rollout logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ericHgorski committed Nov 15, 2024
1 parent 4521b27 commit 47540e6
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 49 deletions.
27 changes: 27 additions & 0 deletions src/hooks/useFeatureEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// hooks/useFeatureRollout.js
import { useEffect, useState } from "react";

import { getCookie, setCookie } from "@/utils/cookies";

export function useFeatureEnabled(featureName: string): boolean {
const [isFeatureEnabled, setIsFeatureEnabled] = useState(false);
const rolloutPercentage = parseInt(process.env.NEXT_PUBLIC_FEATURE_ROLLOUT_PERCENTAGE || "0", 10);

useEffect(() => {
if (typeof window !== "undefined") {
let featureEnabled = getCookie(featureName);

if (featureEnabled === undefined) {
const randomNumber = Math.random() * 100;
featureEnabled = randomNumber < rolloutPercentage ? "true" : "false";

// Set the cookie for 1 day
setCookie(featureName, featureEnabled, 1);
}

setIsFeatureEnabled(featureEnabled === "true");
}
}, [featureName, rolloutPercentage]);

return isFeatureEnabled;
}
22 changes: 22 additions & 0 deletions src/hooks/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect, useState } from "react";

export function useTheme() {
const [theme, setTheme] = useState<"light" | "dark">();

useEffect(() => {
if (typeof window !== "undefined") {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) {
setTheme("light");
} else {
setTheme("dark");
}

window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", (event) => {
const newColorScheme = event.matches ? "light" : "dark";
setTheme(newColorScheme);
});
}
}, []);

return theme;
}
31 changes: 0 additions & 31 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createClient } from "@vercel/edge-config";
import { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies";
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

Expand Down Expand Up @@ -55,36 +54,6 @@ const isPreview = (str: string) => {
// Donetsk and Luhansk Regions of Ukraine, Russia, Crimea, Cuba, Iran, North Korea or Syria
const BLOCKED_COUNTRY = ["RU", "CU", "IR", "KP", "SY"];

const TREATMENT_BUCKET_PERCENTAGE = 0.5;
const COOKIE_NAME = "ab-test"; // name of the cookie to store the variant

const abTestMiddleware = (request: NextRequest, response: NextResponse) => {
const randomlySetBucket: RequestCookie =
Math.random() < TREATMENT_BUCKET_PERCENTAGE
? {
name: COOKIE_NAME,
value: "new",
}
: {
name: COOKIE_NAME,
value: "old",
};

const url = request.nextUrl.clone();

const RequestCookie = request.cookies.get(COOKIE_NAME) || randomlySetBucket;

if (RequestCookie.value === "new") {
url.pathname = "/widgetv2";
response = NextResponse.rewrite(url);
}

if (!request.cookies.get(COOKIE_NAME)) {
response.cookies.set(RequestCookie);
}
return response;
};

const geoBlockMiddleware = (request: NextRequest) => {
if (request.nextUrl.pathname === "/") {
const country = request.geo?.country || "US";
Expand Down
24 changes: 6 additions & 18 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
import { useEffect, useMemo, useState } from "react";
import { useMemo } from "react";
import { defaultTheme, lightTheme, Widget } from "widgetv2";

import DiscordButton from "@/components/DiscordButton";
import { LogoGo } from "@/components/LogoGo";
import WidgetButton from "@/components/WidgetButton";
import { useFeatureEnabled } from "@/hooks/useFeatureEnabled";
import { useTheme } from "@/hooks/useTheme";
import { useURLQueryParams } from "@/hooks/useURLQueryParams";
import { apiURL, endpointOptions } from "@/lib/skip-go-widget";
import { isMobile } from "@/utils/os";
import { cn } from "@/utils/ui";

export default function Home() {
const defaultRoute = useURLQueryParams();
const [theme, setTheme] = useState<"light" | "dark">();
useEffect(() => {
if (typeof window !== "undefined") {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) {
setTheme("light");
} else {
setTheme("dark");
}

window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", (event) => {
const newColorScheme = event.matches ? "light" : "dark";
setTheme(newColorScheme);
});
}
}, []);

const goFast = useFeatureEnabled("goFastEnabled");
const theme = useTheme();
const mobile = useMemo(() => isMobile(), []);

if (!theme) return null;
Expand Down Expand Up @@ -67,7 +55,7 @@ export default function Home() {
apiUrl={apiURL}
defaultRoute={defaultRoute}
routeConfig={{
goFast: true,
goFast,
}}
/>
</div>
Expand Down
20 changes: 20 additions & 0 deletions src/utils/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// utils/cookies.ts

export const getCookie = (name: string): string | undefined => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop()?.split(";").shift();
}
return undefined;
};

export const setCookie = (name: string, value: string, days?: number): void => {
let expires = "";
if (days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = `; expires=${date.toUTCString()}`;
}
document.cookie = `${name}=${encodeURIComponent(value || "")}${expires}; path=/`;
};

0 comments on commit 47540e6

Please sign in to comment.