Skip to content

Commit

Permalink
Use strongly typed LD flags
Browse files Browse the repository at this point in the history
  • Loading branch information
qrtp committed Oct 19, 2023
1 parent 37f4e06 commit f8695e6
Show file tree
Hide file tree
Showing 16 changed files with 284 additions and 240 deletions.
8 changes: 7 additions & 1 deletion packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@
"version": "0.0.1",
"private": true,
"main": "build/src/index.js",
"typings": "build/src/index.d.ts"
"typings": "build/src/index.d.ts",
"dependencies": {
"@types/lodash": "^4.14.199",
"@types/lodash.merge": "^4.6.7",
"lodash": "^4.17.21",
"lodash.merge": "^4.6.2"
}
}
2 changes: 2 additions & 0 deletions packages/config/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import env from './env';

export * from './launchdarkly';

export type {DeepPartial} from './env/types';
export default env;
14 changes: 14 additions & 0 deletions packages/config/src/launchdarkly/defaultFlagValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type {LaunchDarklyFlagSet} from './types';

export const defaultFlagValues: LaunchDarklyFlagSet = {
'ecommerce-service-users-enable-chat': true,
'ecommerce-service-users-enable-chat-community': true,
'ecommerce-service-users-enable-chat-community-media': false,
'ecommerce-service-users-enable-chat-support-bubble': false,
'ecommerce-service-users-public-profile-address-verified-check': true,
'ecommerce-service-wallets-disable-badges': [],
'example-number': 0,
'example-string': '',
};

export default defaultFlagValues;
4 changes: 4 additions & 0 deletions packages/config/src/launchdarkly/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './keys';
export * from './types';
export * from './utils';
export {defaultFlagValues} from './defaultFlagValues';
18 changes: 18 additions & 0 deletions packages/config/src/launchdarkly/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type LaunchDarklyBooleanKey =
| 'ecommerce-service-users-enable-chat'
| 'ecommerce-service-users-enable-chat-community'
| 'ecommerce-service-users-enable-chat-community-media'
| 'ecommerce-service-users-enable-chat-support-bubble'
| 'ecommerce-service-users-public-profile-address-verified-check';

export type LaunchDarklyNumberKey = 'example-number';

export type LaunchDarklyStringKey = 'example-string';

export type LaunchDarklyJsonKey = 'ecommerce-service-wallets-disable-badges';

export type LaunchDarklyKey =
| LaunchDarklyBooleanKey
| LaunchDarklyNumberKey
| LaunchDarklyStringKey
| LaunchDarklyJsonKey;
38 changes: 38 additions & 0 deletions packages/config/src/launchdarkly/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {
LaunchDarklyBooleanKey,
LaunchDarklyJsonKey,
LaunchDarklyNumberKey,
LaunchDarklyStringKey,
} from './keys';

export interface LaunchDarklyFlagSet
extends Record<LaunchDarklyBooleanKey, boolean>,
Record<LaunchDarklyNumberKey, number>,
Record<LaunchDarklyStringKey, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Record<LaunchDarklyJsonKey, any> {}

export type KebabToCamelCase<S extends string> =
S extends `${infer T}-${infer U}`
? `${Lowercase<T>}${Capitalize<KebabToCamelCase<U>>}`
: S;

export interface LaunchDarklyCamelFlagSet
extends Record<KebabToCamelCase<LaunchDarklyBooleanKey>, boolean>,
Record<KebabToCamelCase<LaunchDarklyNumberKey>, number>,
Record<KebabToCamelCase<LaunchDarklyStringKey>, string>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Record<KebabToCamelCase<LaunchDarklyJsonKey>, any> {}

export type LaunchDarklyUserType = 'domain' | 'uid' | 'email' | 'anonymous';

export interface LaunchDarklyUserCustomAttributes {
type: LaunchDarklyUserType;
id?: string;
[key: string]:
| string
| number
| boolean
| Array<string | number | boolean>
| undefined;
}
19 changes: 19 additions & 0 deletions packages/config/src/launchdarkly/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {camelCase} from 'lodash';

import defaultFlagValues from './defaultFlagValues';
import type {LaunchDarklyKey} from './keys';
import type {KebabToCamelCase, LaunchDarklyCamelFlagSet} from './types';

export const kebabToCamel = <T extends string>(str: T): KebabToCamelCase<T> =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
camelCase(str) as any;

export const getLaunchDarklyDefaults = (): LaunchDarklyCamelFlagSet => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const defaults: any = {};

for (const key in defaultFlagValues) {
defaults[kebabToCamel(key)] = defaultFlagValues[key as LaunchDarklyKey];
}
return defaults;
};
20 changes: 14 additions & 6 deletions server/actions/featureFlagActions.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import {notifyError} from 'lib/error';
import {fetchApi} from 'lib/fetchApi';
import {useQuery} from 'react-query';

import {getLaunchDarklyDefaults} from '@unstoppabledomains/config';
import type {LaunchDarklyCamelFlagSet} from '@unstoppabledomains/config';

const BASE_QUERY_KEY = 'featureFlags';
const queryKey = {
featureFlags: () => [BASE_QUERY_KEY],
};

export type FeatureFlags = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
variations?: Record<string, any>;
variations?: LaunchDarklyCamelFlagSet;
};

export const DEFAULT_FEATURE_FLAGS = {
variations: {},
variations: getLaunchDarklyDefaults(),
} as FeatureFlags;

export const fetchFeatureFlags = async (
domainName: string = '',
): Promise<FeatureFlags> => {
const queryString = domainName ? `?domainName=${domainName}` : '';
const featureFlags = await fetchApi(`/feature-flags${queryString}`);
return {...DEFAULT_FEATURE_FLAGS, ...featureFlags};
try {
const queryString = domainName ? `?domainName=${domainName}` : '';
const featureFlags = await fetchApi(`/feature-flags${queryString}`, {});
return {...DEFAULT_FEATURE_FLAGS, ...featureFlags};
} catch (e) {
notifyError(e);
}
return DEFAULT_FEATURE_FLAGS;
};

export const useFeatureFlags = (
Expand Down
10 changes: 2 additions & 8 deletions server/components/Badges/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,8 @@ const Badge: React.FC<Props> = ({
classes.badgeIconContainerWithCircle,
classes.badgeTierStandard,
{
[classes.badgeTierFeatured2]:
gallery?.tier === 2 &&
featureFlags.variations
?.ecommerceServiceEnablePartnerTokenGallery,
[classes.badgeTierFeatured3]:
gallery?.tier === 3 &&
featureFlags.variations
?.ecommerceServiceEnablePartnerTokenGallery,
[classes.badgeTierFeatured2]: gallery?.tier === 2,
[classes.badgeTierFeatured3]: gallery?.tier === 3,
},
)}
src={logo}
Expand Down
76 changes: 23 additions & 53 deletions server/components/Domain/ProfilePicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import type {Theme} from '@mui/material/styles';
import {useFeatureFlags} from 'actions/featureFlagActions';
import useTranslationContext from 'lib/i18n';
import React, {useEffect, useState} from 'react';

Expand Down Expand Up @@ -174,8 +173,6 @@ const ProfilePicture: React.FC<Props> = ({
const [t] = useTranslationContext();
const {classes, cx} = useStyles();
const [isNft, setIsNft] = useState<boolean>(false);
const {data: featureFlags} = useFeatureFlags(false, domain);
const showQR = featureFlags.variations?.ecommerceNftQrCode;

useEffect(() => {
setIsNft(() => imageType === 'onChain');
Expand Down Expand Up @@ -211,55 +208,35 @@ const ProfilePicture: React.FC<Props> = ({
</Box>
)}
<div>
<div
className={showQR ? classes.theCardHover : classes.theCard}
data-testid={'avatar'}
>
<div className={classes.theCardHover} data-testid={'avatar'}>
{src && imageType !== 'default' ? (
<>
{showQR && (
<div className={cx(classes.theBack)}>
<div>
<ProfileQrCode
domain={domain}
className={cx(
'profile-qr-code',
classes.round,

featureFlags.variations?.ecommerceServiceNftPfpStyle &&
isNft
? classes.nftPFPstyleBorder
: '',
)}
/>
</div>
<div className={cx(classes.theBack)}>
<div>
<ProfileQrCode
domain={domain}
className={cx(
'profile-qr-code',
classes.round,
isNft ? classes.nftPFPstyleBorder : '',
)}
/>
</div>
)}
</div>
<div className={classes.theFront}>
<div
data-testid={
featureFlags.variations?.ecommerceServiceNftPfpStyle &&
isNft
? 'hexagonBorder'
: 'imageBorder'
}
data-testid={isNft ? 'hexagonBorder' : 'imageBorder'}
className={cx(
'pfp',
classes.round,
featureFlags.variations?.ecommerceServiceNftPfpStyle &&
isNft
? classes.nftPFPstyleBorder
: '',
isNft ? classes.nftPFPstyleBorder : '',
)}
>
<img
className={cx(
'pfp',
classes.round,
featureFlags.variations?.ecommerceServiceNftPfpStyle &&
isNft
? classes.nftPFPstyle
: '',
isNft ? classes.nftPFPstyle : '',
)}
src={src}
width={80}
Expand All @@ -273,12 +250,7 @@ const ProfilePicture: React.FC<Props> = ({
<>
<div className={classes.theFront}>
<div
data-testid={
featureFlags.variations?.ecommerceServiceNftPfpStyle &&
isNft
? 'hexagonBorder'
: 'imageBorder'
}
data-testid={isNft ? 'hexagonBorder' : 'imageBorder'}
className={cx(
classes.round,
classes.profilePlaceholderContainer,
Expand All @@ -290,16 +262,14 @@ const ProfilePicture: React.FC<Props> = ({
/>
</div>
</div>
{showQR && (
<div className={cx(classes.theBack)}>
<div>
<ProfileQrCode
domain={domain}
className={cx('profile-qr-code', classes.round)}
/>
</div>
<div className={cx(classes.theBack)}>
<div>
<ProfileQrCode
domain={domain}
className={cx('profile-qr-code', classes.round)}
/>
</div>
)}
</div>
</>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion server/components/Header/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const LoginButton: React.FC<LoginButtonProps> = ({
>
{isGoogle || isTwitter || (isEmail && big) || (isWallet && big)
? getLoginMethodIcon(method, classes, isWhiteBg)
: t(`auth.buttons.${method}`)}
: t(`auth.loginWithUnstoppable`)}
</Button>
);
};
Expand Down
1 change: 1 addition & 0 deletions server/components/Image/UnstoppableAnimated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const UnstoppableAnimated: React.FC<Props> = ({theme, hovering}) => {
width={22}
height={22}
alt="wallet"
unoptimized={true}
/>
</Box>
))}
Expand Down
8 changes: 1 addition & 7 deletions server/components/TokenGallery/NftCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import CircularProgress from '@mui/material/CircularProgress';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import type {Theme} from '@mui/material/styles';
import {useFeatureFlags} from 'actions/featureFlagActions';
import {CryptoIcon} from 'components/Image/CryptoIcon';
import useTranslationContext from 'lib/i18n';
import type {AllCurrenciesType} from 'lib/types/blockchain';
Expand Down Expand Up @@ -78,7 +77,6 @@ const NftCard = ({nft, domain, placeholder}: Props) => {
const [isVisible, setIsVisible] = useState(false);
const {classes, cx} = useStyles();
const [open, setOpen] = useState<boolean>(false);
const {data: featureFlags} = useFeatureFlags(false, domain);

const css = `
.NFT-container {
Expand Down Expand Up @@ -165,11 +163,7 @@ const NftCard = ({nft, domain, placeholder}: Props) => {
if (!nft.link) {
return;
}
if (featureFlags.variations?.ecommerceServicePublicProfileNftGallery) {
setOpen(true);
return;
}
window.open(nft.link, '_blank');
setOpen(true);
};

useEffect(() => {
Expand Down
3 changes: 3 additions & 0 deletions server/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"auth": {
"loginWithUnstoppable": "Login with Unstoppable"
},
"apps": {
"clearAll": "Clear all",
"featured": "Featured"
Expand Down
Loading

0 comments on commit f8695e6

Please sign in to comment.