Use feature flags to control beta features (#185)
* Use feature flags to control beta features * Remove references to beta env variable
This commit is contained in:
@@ -23,7 +23,6 @@ ARG NEXT_PUBLIC_SOCKET_URL
|
|||||||
ARG NEXT_PUBLIC_HOST
|
ARG NEXT_PUBLIC_HOST
|
||||||
ARG NEXT_PUBLIC_SENTRY_DSN
|
ARG NEXT_PUBLIC_SENTRY_DSN
|
||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
ARG NEXT_PUBLIC_FF_SHOW_BETA_FEATURES
|
|
||||||
|
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
COPY --from=deps /code/node_modules ./node_modules
|
COPY --from=deps /code/node_modules ./node_modules
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import { IoStatsChartOutline } from "react-icons/io5";
|
|||||||
import { RiHome3Line, RiFlaskLine } from "react-icons/ri";
|
import { RiHome3Line, RiFlaskLine } from "react-icons/ri";
|
||||||
import { FaRobot } from "react-icons/fa";
|
import { FaRobot } from "react-icons/fa";
|
||||||
import { signIn, useSession } from "next-auth/react";
|
import { signIn, useSession } from "next-auth/react";
|
||||||
import { env } from "~/env.mjs";
|
|
||||||
import ProjectMenu from "./ProjectMenu";
|
import ProjectMenu from "./ProjectMenu";
|
||||||
import NavSidebarOption from "./NavSidebarOption";
|
import NavSidebarOption from "./NavSidebarOption";
|
||||||
import IconLink from "./IconLink";
|
import IconLink from "./IconLink";
|
||||||
import { BetaModal } from "./BetaModal";
|
import { BetaModal } from "./BetaModal";
|
||||||
|
import { useAppStore } from "~/state/store";
|
||||||
|
|
||||||
const Divider = () => <Box h="1px" bgColor="gray.300" w="full" />;
|
const Divider = () => <Box h="1px" bgColor="gray.300" w="full" />;
|
||||||
|
|
||||||
@@ -167,6 +167,8 @@ export default function AppShell({
|
|||||||
}
|
}
|
||||||
}, [requireAuth, user, authLoading]);
|
}, [requireAuth, user, authLoading]);
|
||||||
|
|
||||||
|
const flags = useAppStore((s) => s.featureFlags.featureFlags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex h={vh} w="100vw">
|
<Flex h={vh} w="100vw">
|
||||||
@@ -178,7 +180,7 @@ export default function AppShell({
|
|||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
{requireBeta && !env.NEXT_PUBLIC_FF_SHOW_BETA_FEATURES && <BetaModal />}
|
{requireBeta && !flags.betaAccess && <BetaModal />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_SOCKET_URL: z.string().url().default("http://localhost:3318"),
|
NEXT_PUBLIC_SOCKET_URL: z.string().url().default("http://localhost:3318"),
|
||||||
NEXT_PUBLIC_HOST: z.string().url().default("http://localhost:3000"),
|
NEXT_PUBLIC_HOST: z.string().url().default("http://localhost:3000"),
|
||||||
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
||||||
NEXT_PUBLIC_FF_SHOW_BETA_FEATURES: z.string().optional(),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +67,6 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
|
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
|
||||||
OPENPIPE_API_KEY: process.env.OPENPIPE_API_KEY,
|
OPENPIPE_API_KEY: process.env.OPENPIPE_API_KEY,
|
||||||
NEXT_PUBLIC_FF_SHOW_BETA_FEATURES: process.env.NEXT_PUBLIC_FF_SHOW_BETA_FEATURES,
|
|
||||||
SENDER_EMAIL: process.env.SENDER_EMAIL,
|
SENDER_EMAIL: process.env.SENDER_EMAIL,
|
||||||
SMTP_HOST: process.env.SMTP_HOST,
|
SMTP_HOST: process.env.SMTP_HOST,
|
||||||
SMTP_PORT: process.env.SMTP_PORT,
|
SMTP_PORT: process.env.SMTP_PORT,
|
||||||
|
|||||||
20
app/src/state/featureFlags.ts
Normal file
20
app/src/state/featureFlags.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { type SliceCreator } from "./store";
|
||||||
|
|
||||||
|
export type FeatureFlagsSlice = {
|
||||||
|
featureFlags: {
|
||||||
|
betaAccess: boolean;
|
||||||
|
};
|
||||||
|
setFeatureFlags: (flags: string[] | undefined) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFeatureFlagsSlice: SliceCreator<FeatureFlagsSlice> = (set) => ({
|
||||||
|
featureFlags: {
|
||||||
|
betaAccess: false,
|
||||||
|
},
|
||||||
|
setFeatureFlags: (flags) =>
|
||||||
|
set((state) => {
|
||||||
|
state.featureFlags.featureFlags = {
|
||||||
|
betaAccess: flags?.includes("betaAccess") ?? false,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -11,7 +11,8 @@ import { type APIClient } from "~/utils/api";
|
|||||||
import { type PersistedState, persistOptions } from "./persist";
|
import { type PersistedState, persistOptions } from "./persist";
|
||||||
import { type SelectedLogsSlice, createSelectedLogsSlice } from "./selectedLogsSlice";
|
import { type SelectedLogsSlice, createSelectedLogsSlice } from "./selectedLogsSlice";
|
||||||
import { type LogFiltersSlice, createLogFiltersSlice } from "./logFiltersSlice";
|
import { type LogFiltersSlice, createLogFiltersSlice } from "./logFiltersSlice";
|
||||||
import { createColumnVisibilitySlice, type ColumnVisibilitySlice } from "./columnVisiblitySlice";
|
import { type ColumnVisibilitySlice, createColumnVisibilitySlice } from "./columnVisiblitySlice";
|
||||||
|
import { type FeatureFlagsSlice, createFeatureFlagsSlice } from "./featureFlags";
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ export type State = {
|
|||||||
selectedLogs: SelectedLogsSlice;
|
selectedLogs: SelectedLogsSlice;
|
||||||
logFilters: LogFiltersSlice;
|
logFilters: LogFiltersSlice;
|
||||||
columnVisibility: ColumnVisibilitySlice;
|
columnVisibility: ColumnVisibilitySlice;
|
||||||
|
featureFlags: FeatureFlagsSlice;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], [], T>;
|
export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], [], T>;
|
||||||
@@ -62,6 +64,7 @@ const useBaseStore = create<State, [["zustand/persist", PersistedState], ["zusta
|
|||||||
selectedLogs: createSelectedLogsSlice(set, get, ...rest),
|
selectedLogs: createSelectedLogsSlice(set, get, ...rest),
|
||||||
logFilters: createLogFiltersSlice(set, get, ...rest),
|
logFilters: createLogFiltersSlice(set, get, ...rest),
|
||||||
columnVisibility: createColumnVisibilitySlice(set, get, ...rest),
|
columnVisibility: createColumnVisibilitySlice(set, get, ...rest),
|
||||||
|
featureFlags: createFeatureFlagsSlice(set, get, ...rest),
|
||||||
})),
|
})),
|
||||||
persistOptions,
|
persistOptions,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import React, { type ReactNode, useEffect } from "react";
|
import React, { type ReactNode, useEffect } from "react";
|
||||||
import { PostHogProvider } from "posthog-js/react";
|
import { PostHogProvider, useActiveFeatureFlags } from "posthog-js/react";
|
||||||
|
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useAppStore } from "~/state/store";
|
||||||
|
|
||||||
// Make sure we're in the browser
|
// Make sure we're in the browser
|
||||||
const inBrowser = typeof window !== "undefined";
|
const inBrowser = typeof window !== "undefined";
|
||||||
@@ -24,6 +25,14 @@ export const PosthogAppProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
};
|
};
|
||||||
}, [router.events]);
|
}, [router.events]);
|
||||||
|
|
||||||
|
const setFeatureFlags = useAppStore((s) => s.featureFlags.setFeatureFlags);
|
||||||
|
const activeFlags = useActiveFeatureFlags();
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeFlags) {
|
||||||
|
setFeatureFlags(activeFlags);
|
||||||
|
}
|
||||||
|
}, [activeFlags, setFeatureFlags]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (env.NEXT_PUBLIC_POSTHOG_KEY && inBrowser && session && session.user) {
|
if (env.NEXT_PUBLIC_POSTHOG_KEY && inBrowser && session && session.user) {
|
||||||
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
|
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
|
||||||
|
|||||||
Reference in New Issue
Block a user