Skip to content

Commit

Permalink
load the new featureFlags into global state and allow updating via qu…
Browse files Browse the repository at this point in the history
…ery params (prefix: `pinboardFeatureFlag_`)
  • Loading branch information
twrichards committed Nov 5, 2024
1 parent a4c4519 commit 6657969
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 0 deletions.
8 changes: 8 additions & 0 deletions client/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,11 @@ export const gqlVisitTourStep = gql`
}
}
`;

export const gqlChangeFeatureFlag = gql`
mutation changeFeatureFlag($flagId: String!, $newValue: Boolean!) {
changeFeatureFlag(flagId: $flagId, newValue: $newValue) {
${myUserReturnFields}
}
}
`;
24 changes: 24 additions & 0 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ import { getAgateFontFaceIfApplicable } from "../fontNormaliser";
import { Global } from "@emotion/react";
import { TourStateProvider } from "./tour/tourState";
import { demoMentionableUsers, demoUser } from "./tour/tourConstants";
import {
consumeFeatureFlagQueryParamsAndUpdateAccordingly,
extractFeatureFlags,
} from "./featureFlags";

const PRESELECT_PINBOARD_HTML_TAG = "pinboard-preselect";
const PRESET_UNREAD_NOTIFICATIONS_COUNT_HTML_TAG = "pinboard-bubble-preset";
Expand Down Expand Up @@ -357,6 +361,25 @@ export const PinBoardApp = ({

const hasApolloAuthError = useReactiveVar(hasApolloAuthErrorVar);

const featureFlags = useMemo(
() => extractFeatureFlags(me?.featureFlags),
[me?.featureFlags]
);
consumeFeatureFlagQueryParamsAndUpdateAccordingly(
apolloClient,
meQuery.updateQuery
)
.then(() =>
console.log(
"Processed any Pinboard pinboard flag changes which were present in the URL."
)
)
.catch(console.error);

useEffect(() => {
console.log("test feature flag:", featureFlags["test"]);
}, [featureFlags]);

return (
<TelemetryContext.Provider value={sendTelemetryEvent}>
<ApolloProvider client={apolloClient}>
Expand All @@ -380,6 +403,7 @@ export const PinBoardApp = ({
}
hasEverUsedTour={me?.hasEverUsedTour}
visitTourStep={visitTourStep}
featureFlags={featureFlags}
>
<TourStateProvider>
<Global styles={agateFontFaceIfApplicable} />
Expand Down
38 changes: 38 additions & 0 deletions client/src/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { readAndThenSilentlyDropQueryParamFromURL } from "./util";
import { ApolloClient } from "@apollo/client";
import { MyUser } from "shared/graphql/graphql";
import { gqlChangeFeatureFlag } from "../gql";

export const ALLOWED_FEATURE_FLAGS = ["test"] as const;
export type AllowedFeatureFlags = (typeof ALLOWED_FEATURE_FLAGS)[number];

export type FeatureFlags = Partial<Record<AllowedFeatureFlags, boolean>>;

export const extractFeatureFlags = (
featureFlagsStr: string | null | undefined
): FeatureFlags =>
featureFlagsStr ? (JSON.parse(featureFlagsStr) as FeatureFlags) : {};

export const consumeFeatureFlagQueryParamsAndUpdateAccordingly = async (
apolloClient: ApolloClient<unknown>,
updateMyUserFunc: (mapFn: () => { getMyUser: MyUser }) => void
) => {
for (const flagId in ALLOWED_FEATURE_FLAGS) {
//need to process sequentially
const flagStringValue = readAndThenSilentlyDropQueryParamFromURL(
`pinboardFeatureFlag_${flagId}`
);
if (flagStringValue !== null) {
const updatedUserResult = await apolloClient.mutate({
mutation: gqlChangeFeatureFlag,
variables: {
flagId,
newValue: flagStringValue === "true",
},
});
updateMyUserFunc(() => ({
getMyUser: updatedUserResult.data?.changeFeatureFlag,
}));
}
}
};
7 changes: 7 additions & 0 deletions client/src/globalState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import { UserLookup } from "./types/UserLookup";
import { demoPinboardData } from "./tour/tourConstants";
import { readAndThenSilentlyDropQueryParamFromURL } from "./util";
import { FeatureFlags } from "./featureFlags";

const LOCAL_STORAGE_KEY_EXPLICIT_POSITION = "pinboard-explicit-position";

Expand Down Expand Up @@ -70,6 +71,8 @@ interface GlobalStateContextShape {
hasEverUsedTour: boolean | undefined;
visitTourStep: (tourStepId: string) => void;

featureFlags: FeatureFlags;

showNotification: (item: Item) => void;
hasWebPushSubscription: boolean | null | undefined;

Expand Down Expand Up @@ -130,6 +133,7 @@ interface GlobalStateProviderProps {
presetUnreadNotificationCount: number | undefined;
hasEverUsedTour: boolean | undefined;
visitTourStep: (tourStepId: string) => void;
featureFlags: FeatureFlags;
}

export const GlobalStateProvider = ({
Expand All @@ -150,6 +154,7 @@ export const GlobalStateProvider = ({
clearDesktopNotificationsForPinboardId,
hasEverUsedTour,
visitTourStep,
featureFlags,
children,
}: PropsWithChildren<GlobalStateProviderProps>) => {
const [activeTab, setActiveTab] = useState<Tab>(ChatTab);
Expand Down Expand Up @@ -562,6 +567,8 @@ export const GlobalStateProvider = ({
hasEverUsedTour,
visitTourStep,

featureFlags,

showNotification,
hasWebPushSubscription,

Expand Down

0 comments on commit 6657969

Please sign in to comment.