Skip to content

Commit 4f4b0e8

Browse files
committed
Make segment flag usage configurable with UI preference
1 parent 841fab4 commit 4f4b0e8

5 files changed

Lines changed: 99 additions & 41 deletions

File tree

ui/src/app/preferences/Preferences.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ import Select from '~/components/forms/Select';
66
import { useTimezone } from '~/data/hooks/timezone';
77
import { Theme, Timezone } from '~/types/Preferences';
88
import {
9+
selectLoadSegmentFlagReferences,
910
selectTheme,
1011
selectTimezone,
12+
loadSegmentFlagReferencesChanged,
1113
themeChanged,
1214
timezoneChanged
1315
} from './preferencesSlice';
1416

1517
export default function Preferences() {
1618
const timezone = useSelector(selectTimezone);
1719
const theme = useSelector(selectTheme);
20+
const loadSegmentFlagReferences = useSelector(
21+
selectLoadSegmentFlagReferences
22+
);
1823

1924
const dispatch = useDispatch();
2025

@@ -61,6 +66,32 @@ export default function Preferences() {
6166
}}
6267
/>
6368
</div>
69+
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:pt-5">
70+
<span
71+
className="text-muted-foreground text-sm font-bold"
72+
id="label-switch-segment-flags"
73+
>
74+
Segment flag usage
75+
<p className="mt-2 text-xs font-normal">
76+
Load which flags reference each segment on the segment page
77+
and segments list. Turning this off avoids extra API requests
78+
when you have many flags.
79+
</p>
80+
</span>
81+
<dd className="sm:col-span-2 sm:mt-0 sm:text-right">
82+
<Switch
83+
checked={loadSegmentFlagReferences}
84+
aria-labelledby="label-switch-segment-flags"
85+
onCheckedChange={() => {
86+
dispatch(
87+
loadSegmentFlagReferencesChanged(
88+
!loadSegmentFlagReferences
89+
)
90+
);
91+
}}
92+
/>
93+
</dd>
94+
</div>
6495
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:pt-5">
6596
<span
6697
className="text-muted-foreground text-sm font-bold"

ui/src/app/preferences/preferencesSlice.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ interface IPreferencesState {
1010
theme: Theme;
1111
timezone: Timezone;
1212
sidebar: Sidebar;
13+
/** When true, the UI loads which flags reference each segment (extra API calls). */
14+
loadSegmentFlagReferences?: boolean;
1315
}
1416

1517
const initialState: IPreferencesState = {
1618
theme: Theme.SYSTEM,
1719
timezone: Timezone.LOCAL,
18-
sidebar: Sidebar.OPEN
20+
sidebar: Sidebar.OPEN,
21+
loadSegmentFlagReferences: false
1922
};
2023

2124
export const preferencesSlice = createSlice({
@@ -30,6 +33,12 @@ export const preferencesSlice = createSlice({
3033
},
3134
sidebarChanged: (state, action: PayloadAction<boolean>) => {
3235
state.sidebar = action.payload ? Sidebar.OPEN : Sidebar.CLOSE;
36+
},
37+
loadSegmentFlagReferencesChanged: (
38+
state,
39+
action: PayloadAction<boolean>
40+
) => {
41+
state.loadSegmentFlagReferences = action.payload;
3342
}
3443
},
3544
extraReducers(builder) {
@@ -57,12 +66,19 @@ export const preferencesSlice = createSlice({
5766
}
5867
});
5968

60-
export const { themeChanged, timezoneChanged, sidebarChanged } =
61-
preferencesSlice.actions;
69+
export const {
70+
themeChanged,
71+
timezoneChanged,
72+
sidebarChanged,
73+
loadSegmentFlagReferencesChanged
74+
} = preferencesSlice.actions;
6275

6376
export const selectTheme = (state: RootState) => state.preferences.theme;
6477
export const selectTimezone = (state: RootState) => state.preferences.timezone;
6578
export const selectSidebar = (state: RootState) =>
6679
state.preferences.sidebar === Sidebar.OPEN;
80+
/** Off by default; must be explicitly enabled in Preferences. */
81+
export const selectLoadSegmentFlagReferences = (state: RootState) =>
82+
state.preferences.loadSegmentFlagReferences === true;
6783

6884
export default preferencesSlice.reducer;

ui/src/app/segments/Segment.tsx

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
selectCurrentNamespace,
99
selectNamespaces
1010
} from '~/app/namespaces/namespacesSlice';
11+
import { selectLoadSegmentFlagReferences } from '~/app/preferences/preferencesSlice';
1112
import {
1213
useCopySegmentMutation,
1314
useDeleteConstraintMutation,
@@ -92,6 +93,9 @@ export default function Segment() {
9293
const namespaces = useSelector(selectNamespaces);
9394
const namespace = useSelector(selectCurrentNamespace);
9495
const readOnly = useSelector(selectReadonly);
96+
const loadSegmentFlagReferences = useSelector(
97+
selectLoadSegmentFlagReferences
98+
);
9599

96100
const {
97101
data: segment,
@@ -109,7 +113,7 @@ export default function Segment() {
109113
segmentKey: segmentKey || ''
110114
},
111115
{
112-
skip: !segmentKey,
116+
skip: !segmentKey || !loadSegmentFlagReferences,
113117
refetchOnMountOrArgChange: true
114118
}
115119
);
@@ -269,43 +273,45 @@ export default function Segment() {
269273
<SegmentForm segment={segment} />
270274
</div>
271275

272-
<div className="mb-8">
273-
<h3 className="text-secondary-foreground leading-6 font-medium">
274-
Used in flags
275-
</h3>
276-
<p className="text-muted-foreground mt-1 mb-3 text-sm">
277-
Flags that reference this segment (in rules or rollouts). Changing
278-
constraints may affect how these flags evaluate.
279-
</p>
280-
{!flagsForSegment ? (
281-
<Loading size="sm" variant="start" />
282-
) : flagsForSegment.flags.length === 0 ? (
283-
<p className="text-secondary-foreground/80 text-sm">
284-
This segment is not used by any flags.
276+
{loadSegmentFlagReferences && (
277+
<div className="mb-8">
278+
<h3 className="text-secondary-foreground leading-6 font-medium">
279+
Used in flags
280+
</h3>
281+
<p className="text-muted-foreground mt-1 mb-3 text-sm">
282+
Flags that reference this segment (in rules or rollouts). Changing
283+
constraints may affect how these flags evaluate.
285284
</p>
286-
) : (
287-
<ul className="flex flex-wrap gap-2">
288-
{flagsForSegment.flags.map((ref) => (
289-
<li key={ref.key}>
290-
<Link
291-
tabIndex={0}
292-
to={`/namespaces/${namespace.key}/flags/${ref.key}`}
293-
className="focus:ring-offset-brand focus:ring-brand block shrink-0 cursor-pointer rounded-md text-xs focus:ring-1 focus:ring-offset-1"
294-
>
295-
<Badge
296-
variant="secondary"
297-
title={ref.name + ' | ' + ref.key}
298-
className="shrink-0 gap-1.5 border-0 px-3 py-1.5 text-xs"
285+
{!flagsForSegment ? (
286+
<Loading size="sm" variant="start" />
287+
) : flagsForSegment.flags.length === 0 ? (
288+
<p className="text-secondary-foreground/80 text-sm">
289+
This segment is not used by any flags.
290+
</p>
291+
) : (
292+
<ul className="flex flex-wrap gap-2">
293+
{flagsForSegment.flags.map((ref) => (
294+
<li key={ref.key}>
295+
<Link
296+
tabIndex={0}
297+
to={`/namespaces/${namespace.key}/flags/${ref.key}`}
298+
className="focus:ring-offset-brand focus:ring-brand block shrink-0 cursor-pointer rounded-md text-xs focus:ring-1 focus:ring-offset-1"
299299
>
300-
<FlagIcon className="h-4 w-4" />{' '}
301-
<span className="max-w-24 truncate">{ref.name}</span>
302-
</Badge>
303-
</Link>
304-
</li>
305-
))}
306-
</ul>
307-
)}
308-
</div>
300+
<Badge
301+
variant="secondary"
302+
title={ref.name + ' | ' + ref.key}
303+
className="shrink-0 gap-1.5 border-0 px-3 py-1.5 text-xs"
304+
>
305+
<FlagIcon className="h-4 w-4" />{' '}
306+
<span className="max-w-24 truncate">{ref.name}</span>
307+
</Badge>
308+
</Link>
309+
</li>
310+
))}
311+
</ul>
312+
)}
313+
</div>
314+
)}
309315

310316
<div>
311317
<div className="sm:flex sm:items-center">

ui/src/components/segments/SegmentTable.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { DataTableViewOptions } from '~/components/ui/table-view-options';
2626
import { DataTablePagination } from '~/components/ui/table-pagination';
2727
import { AsteriskIcon, FlagIcon, SigmaIcon } from 'lucide-react';
2828
import { useError } from '~/data/hooks/error';
29+
import { selectLoadSegmentFlagReferences } from '~/app/preferences/preferencesSlice';
2930
import {
3031
useGetSegmentFlagCountsQuery,
3132
useListSegmentsQuery
@@ -149,11 +150,14 @@ export default function SegmentTable(props: SegmentTableProps) {
149150
const [filter, setFilter] = useState<string>('');
150151

151152
const sorting = useSelector(selectSorting);
153+
const loadSegmentFlagReferences = useSelector(
154+
selectLoadSegmentFlagReferences
155+
);
152156
const { data, isLoading, error } = useListSegmentsQuery(namespace.key);
153157
const { data: flagCounts } = useGetSegmentFlagCountsQuery(
154158
{ namespaceKey: namespace.key },
155159
{
156-
skip: !data?.segments?.length,
160+
skip: !data?.segments?.length || !loadSegmentFlagReferences,
157161
refetchOnMountOrArgChange: true
158162
}
159163
);

ui/src/store.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ listenerMiddleware.startListening({
4949
matcher: isAnyOf(
5050
preferencesSlice.actions.themeChanged,
5151
preferencesSlice.actions.timezoneChanged,
52-
preferencesSlice.actions.sidebarChanged
52+
preferencesSlice.actions.sidebarChanged,
53+
preferencesSlice.actions.loadSegmentFlagReferencesChanged
5354
),
5455
effect: (_action, api) => {
5556
// save to local storage

0 commit comments

Comments
 (0)