mirror of
https://github.com/lux-tel/websites.git
synced 2026-10-30 20:31:36 +00:00
143 lines
4.6 KiB
TypeScript
143 lines
4.6 KiB
TypeScript
'use client';
|
|
|
|
import { useSearchParams } from 'next/navigation';
|
|
import { createContext, useContext, useState } from 'react';
|
|
|
|
export enum FEATURE_FLAG {
|
|
MOCK_STAKED_NODES = 'mockStakedNodes',
|
|
MOCK_NO_STAKED_NODES = 'mockNoStakedNodes',
|
|
MOCK_OPEN_NODES = 'mockOpenNodes',
|
|
MOCK_NO_OPEN_NODES = 'mockNoOpenNodes',
|
|
MOCK_PENDING_NODES_ONE = 'mockPendingNodesOne',
|
|
MOCK_PENDING_NODES_TWO = 'mockPendingNodesTwo',
|
|
MOCK_PENDING_NODES_THREE = 'mockPendingNodesThree',
|
|
MOCK_PENDING_NODES_MANY = 'mockPendingNodesMany',
|
|
MOCK_NO_PENDING_NODES = 'mockNoPendingNodes',
|
|
MOCK_REGISTRATION = 'mockRegistration',
|
|
CLEAR_ACCEPT_TOS = 'clearAcceptTOS',
|
|
}
|
|
|
|
export const FEATURE_FLAG_DESCRIPTION = {
|
|
[FEATURE_FLAG.MOCK_STAKED_NODES]: 'Replace staked nodes with mock data',
|
|
[FEATURE_FLAG.MOCK_NO_STAKED_NODES]: 'Replace staked nodes with no data',
|
|
[FEATURE_FLAG.MOCK_OPEN_NODES]: 'Replace open nodes with mock data',
|
|
[FEATURE_FLAG.MOCK_NO_OPEN_NODES]: 'Replace open nodes with no data',
|
|
[FEATURE_FLAG.MOCK_PENDING_NODES_ONE]: 'Replace pending nodes with 1 node with mock data',
|
|
[FEATURE_FLAG.MOCK_PENDING_NODES_TWO]: 'Replace pending nodes with 2 node with mock data',
|
|
[FEATURE_FLAG.MOCK_PENDING_NODES_THREE]: 'Replace pending nodes with 3 node with mock data',
|
|
[FEATURE_FLAG.MOCK_PENDING_NODES_MANY]: 'Replace pending nodes with many node with mock data',
|
|
[FEATURE_FLAG.MOCK_NO_PENDING_NODES]: 'Replace pending nodes with no data',
|
|
[FEATURE_FLAG.MOCK_REGISTRATION]: 'Use a mock node for the registration form',
|
|
[FEATURE_FLAG.CLEAR_ACCEPT_TOS]: 'Clear the accepted terms and conditions',
|
|
};
|
|
|
|
export const validFeatureFlags = Object.values(FEATURE_FLAG);
|
|
|
|
export const pageFeatureFlags: Record<string, Array<FEATURE_FLAG>> = {
|
|
mystakes: [FEATURE_FLAG.MOCK_STAKED_NODES, FEATURE_FLAG.MOCK_NO_STAKED_NODES],
|
|
stake: [
|
|
FEATURE_FLAG.MOCK_OPEN_NODES,
|
|
FEATURE_FLAG.MOCK_NO_OPEN_NODES,
|
|
FEATURE_FLAG.MOCK_PENDING_NODES_ONE,
|
|
FEATURE_FLAG.MOCK_PENDING_NODES_TWO,
|
|
FEATURE_FLAG.MOCK_PENDING_NODES_THREE,
|
|
FEATURE_FLAG.MOCK_PENDING_NODES_MANY,
|
|
FEATURE_FLAG.MOCK_NO_PENDING_NODES,
|
|
],
|
|
register: [FEATURE_FLAG.MOCK_REGISTRATION],
|
|
};
|
|
|
|
export const globalFeatureFlags = [FEATURE_FLAG.CLEAR_ACCEPT_TOS];
|
|
|
|
type FeatureFlags = Record<FEATURE_FLAG, boolean>;
|
|
|
|
type FeatureFlagContext = {
|
|
featureFlags: FeatureFlags;
|
|
setFeatureFlags: (featureFlags: FeatureFlags) => void;
|
|
};
|
|
|
|
const localStorageFeatureFlagKey = 'featureFlags';
|
|
|
|
function saveFeatureFlagsToStorage(featureFlags: FeatureFlags) {
|
|
localStorage.setItem(localStorageFeatureFlagKey, JSON.stringify(featureFlags));
|
|
|
|
// @ts-expect-error - Expose feature flags to window for debugging
|
|
window.featureFlags = featureFlags;
|
|
}
|
|
|
|
function loadFeatureFlagsFromStorage() {
|
|
const storedFeatureFlags = localStorage.getItem(localStorageFeatureFlagKey);
|
|
|
|
if (!storedFeatureFlags) {
|
|
return {};
|
|
}
|
|
|
|
return JSON.parse(storedFeatureFlags);
|
|
}
|
|
|
|
function bootstrapFeatureFlags() {
|
|
if (typeof window === 'undefined') {
|
|
return {};
|
|
}
|
|
const featureFlags = loadFeatureFlagsFromStorage();
|
|
const searchParams = useSearchParams();
|
|
|
|
const searchParamEntries = searchParams.entries();
|
|
|
|
for (const [key, value] of searchParamEntries) {
|
|
if (validFeatureFlags.includes(key as FEATURE_FLAG)) {
|
|
featureFlags[key as FEATURE_FLAG] = value === 'true';
|
|
}
|
|
}
|
|
|
|
saveFeatureFlagsToStorage(featureFlags);
|
|
|
|
return featureFlags;
|
|
}
|
|
|
|
const Context = createContext<FeatureFlagContext | undefined>(undefined);
|
|
|
|
export default function FeatureFlagProvider({ children }: { children: React.ReactNode }) {
|
|
const [featureFlags, setFeatureFlags] = useState(bootstrapFeatureFlags());
|
|
|
|
return <Context.Provider value={{ featureFlags, setFeatureFlags }}>{children}</Context.Provider>;
|
|
}
|
|
|
|
export const useFeatureFlags = () => {
|
|
const context = useContext(Context);
|
|
|
|
if (context === undefined) {
|
|
throw new Error('useFeatureFlags must be used inside FeatureFlagProvider');
|
|
}
|
|
|
|
return context.featureFlags;
|
|
};
|
|
|
|
export const useFeatureFlag = (flag: FEATURE_FLAG) => {
|
|
const context = useContext(Context);
|
|
|
|
if (context === undefined) {
|
|
throw new Error('useFeatureFlag must be used inside FeatureFlagProvider');
|
|
}
|
|
|
|
return context.featureFlags[flag] ?? false;
|
|
};
|
|
|
|
export const useSetFeatureFlag = () => {
|
|
const context = useContext(Context);
|
|
|
|
if (context === undefined) {
|
|
throw new Error('setFeatureFlag must be used inside FeatureFlagProvider');
|
|
}
|
|
|
|
const setFeatureFlag = (flag: FEATURE_FLAG, value: boolean) => {
|
|
const newFeatureFlags = { ...context.featureFlags, [flag]: value };
|
|
|
|
context.setFeatureFlags(newFeatureFlags);
|
|
|
|
saveFeatureFlagsToStorage(newFeatureFlags);
|
|
};
|
|
|
|
return { setFeatureFlag };
|
|
};
|