Persist draft agent feature preferences
This commit is contained in:
@@ -962,13 +962,14 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void updatePreferences(
|
void updatePreferences(
|
||||||
mergeProviderPreferences({
|
(current) =>
|
||||||
preferences,
|
mergeProviderPreferences({
|
||||||
provider: agent.provider,
|
preferences: current,
|
||||||
updates: {
|
provider: agent.provider,
|
||||||
model: modelId,
|
updates: {
|
||||||
},
|
model: modelId,
|
||||||
}),
|
},
|
||||||
|
}),
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
console.warn("[AgentStatusBar] persist model preference failed", error);
|
console.warn("[AgentStatusBar] persist model preference failed", error);
|
||||||
});
|
});
|
||||||
@@ -978,7 +979,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
}}
|
}}
|
||||||
favoriteKeys={favoriteKeys}
|
favoriteKeys={favoriteKeys}
|
||||||
onToggleFavoriteModel={(provider, modelId) => {
|
onToggleFavoriteModel={(provider, modelId) => {
|
||||||
void updatePreferences(toggleFavoriteModel({ preferences, provider, modelId })).catch((error) => {
|
void updatePreferences((current) => toggleFavoriteModel({ preferences: current, provider, modelId })).catch((error) => {
|
||||||
console.warn("[AgentStatusBar] toggle favorite model failed", error);
|
console.warn("[AgentStatusBar] toggle favorite model failed", error);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -991,16 +992,17 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
const activeModelId = modelSelection.activeModelId;
|
const activeModelId = modelSelection.activeModelId;
|
||||||
if (activeModelId) {
|
if (activeModelId) {
|
||||||
void updatePreferences(
|
void updatePreferences(
|
||||||
mergeProviderPreferences({
|
(current) =>
|
||||||
preferences,
|
mergeProviderPreferences({
|
||||||
provider: agent.provider,
|
preferences: current,
|
||||||
updates: {
|
provider: agent.provider,
|
||||||
model: activeModelId,
|
updates: {
|
||||||
thinkingByModel: {
|
model: activeModelId,
|
||||||
[activeModelId]: thinkingOptionId,
|
thinkingByModel: {
|
||||||
|
[activeModelId]: thinkingOptionId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
console.warn("[AgentStatusBar] persist thinking preference failed", error);
|
console.warn("[AgentStatusBar] persist thinking preference failed", error);
|
||||||
});
|
});
|
||||||
@@ -1014,6 +1016,20 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
if (!client) {
|
if (!client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
void updatePreferences(
|
||||||
|
(current) =>
|
||||||
|
mergeProviderPreferences({
|
||||||
|
preferences: current,
|
||||||
|
provider: agent.provider,
|
||||||
|
updates: {
|
||||||
|
featureValues: {
|
||||||
|
[featureId]: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).catch((error) => {
|
||||||
|
console.warn("[AgentStatusBar] persist feature preference failed", error);
|
||||||
|
});
|
||||||
void client.setAgentFeature(agentId, featureId, value).catch((error) => {
|
void client.setAgentFeature(agentId, featureId, value).catch((error) => {
|
||||||
console.warn("[AgentStatusBar] setAgentFeature failed", error);
|
console.warn("[AgentStatusBar] setAgentFeature failed", error);
|
||||||
});
|
});
|
||||||
@@ -1083,7 +1099,7 @@ export function DraftAgentStatusBar({
|
|||||||
onSelect={onSelectProviderAndModel}
|
onSelect={onSelectProviderAndModel}
|
||||||
favoriteKeys={favoriteKeys}
|
favoriteKeys={favoriteKeys}
|
||||||
onToggleFavorite={(provider, modelId) => {
|
onToggleFavorite={(provider, modelId) => {
|
||||||
void updatePreferences(toggleFavoriteModel({ preferences, provider, modelId })).catch((error) => {
|
void updatePreferences((current) => toggleFavoriteModel({ preferences: current, provider, modelId })).catch((error) => {
|
||||||
console.warn("[DraftAgentStatusBar] toggle favorite model failed", error);
|
console.warn("[DraftAgentStatusBar] toggle favorite model failed", error);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -1129,7 +1145,7 @@ export function DraftAgentStatusBar({
|
|||||||
isModelLoading={isAllModelsLoading}
|
isModelLoading={isAllModelsLoading}
|
||||||
favoriteKeys={favoriteKeys}
|
favoriteKeys={favoriteKeys}
|
||||||
onToggleFavoriteModel={(provider, modelId) => {
|
onToggleFavoriteModel={(provider, modelId) => {
|
||||||
void updatePreferences(toggleFavoriteModel({ preferences, provider, modelId })).catch((error) => {
|
void updatePreferences((current) => toggleFavoriteModel({ preferences: current, provider, modelId })).catch((error) => {
|
||||||
console.warn("[DraftAgentStatusBar] toggle favorite model failed", error);
|
console.warn("[DraftAgentStatusBar] toggle favorite model failed", error);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { resolveFeatureValues } from "./feature-preferences";
|
||||||
|
|
||||||
|
describe("feature-preferences", () => {
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
type: "toggle" as const,
|
||||||
|
id: "fast_mode",
|
||||||
|
label: "Fast",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "toggle" as const,
|
||||||
|
id: "plan_mode",
|
||||||
|
label: "Plan",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it("restores persisted values for available features", () => {
|
||||||
|
expect(
|
||||||
|
resolveFeatureValues({
|
||||||
|
features,
|
||||||
|
persistedFeatureValues: {
|
||||||
|
fast_mode: true,
|
||||||
|
unknown_feature: true,
|
||||||
|
},
|
||||||
|
localFeatureValues: {},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
fast_mode: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prefers local values over persisted values", () => {
|
||||||
|
expect(
|
||||||
|
resolveFeatureValues({
|
||||||
|
features,
|
||||||
|
persistedFeatureValues: {
|
||||||
|
fast_mode: true,
|
||||||
|
plan_mode: false,
|
||||||
|
},
|
||||||
|
localFeatureValues: {
|
||||||
|
fast_mode: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
fast_mode: false,
|
||||||
|
plan_mode: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { AgentFeature } from "@server/server/agent/agent-sdk-types";
|
||||||
|
|
||||||
|
export function pruneFeatureValues(
|
||||||
|
featureValues: Record<string, unknown>,
|
||||||
|
features: AgentFeature[],
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const allowedFeatureIds = new Set(features.map((feature) => feature.id));
|
||||||
|
let changed = false;
|
||||||
|
const next: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const [featureId, value] of Object.entries(featureValues)) {
|
||||||
|
if (!allowedFeatureIds.has(featureId)) {
|
||||||
|
changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next[featureId] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? next : featureValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyFeatureValues(
|
||||||
|
features: AgentFeature[],
|
||||||
|
featureValues: Record<string, unknown>,
|
||||||
|
): AgentFeature[] {
|
||||||
|
if (Object.keys(featureValues).length === 0) {
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
return features.map((feature) => {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(featureValues, feature.id)) {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...feature,
|
||||||
|
value: featureValues[feature.id],
|
||||||
|
} as AgentFeature;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveFeatureValues(args: {
|
||||||
|
features: AgentFeature[];
|
||||||
|
persistedFeatureValues: Record<string, unknown>;
|
||||||
|
localFeatureValues: Record<string, unknown>;
|
||||||
|
}): Record<string, unknown> {
|
||||||
|
const next: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const feature of args.features) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(args.localFeatureValues, feature.id)) {
|
||||||
|
next[feature.id] = args.localFeatureValues[feature.id];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(args.persistedFeatureValues, feature.id)) {
|
||||||
|
next[feature.id] = args.persistedFeatureValues[feature.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
@@ -651,30 +651,29 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
|
|||||||
const persistFormPreferences = useCallback(async () => {
|
const persistFormPreferences = useCallback(async () => {
|
||||||
const resolvedModel = resolveEffectiveModel(availableModels, formState.model);
|
const resolvedModel = resolveEffectiveModel(availableModels, formState.model);
|
||||||
const modelId = resolvedModel?.id ?? formState.model;
|
const modelId = resolvedModel?.id ?? formState.model;
|
||||||
const nextPreferences = mergeProviderPreferences({
|
await updatePreferences((current) =>
|
||||||
preferences: preferences ?? {},
|
mergeProviderPreferences({
|
||||||
provider: formState.provider,
|
preferences: current,
|
||||||
updates: {
|
provider: formState.provider,
|
||||||
model: modelId || undefined,
|
updates: {
|
||||||
mode: formState.modeId || undefined,
|
model: modelId || undefined,
|
||||||
...(modelId && formState.thinkingOptionId
|
mode: formState.modeId || undefined,
|
||||||
? {
|
...(modelId && formState.thinkingOptionId
|
||||||
thinkingByModel: {
|
? {
|
||||||
[modelId]: formState.thinkingOptionId,
|
thinkingByModel: {
|
||||||
},
|
[modelId]: formState.thinkingOptionId,
|
||||||
}
|
},
|
||||||
: {}),
|
}
|
||||||
} satisfies Partial<ProviderPreferences>,
|
: {}),
|
||||||
});
|
} satisfies Partial<ProviderPreferences>,
|
||||||
|
}),
|
||||||
await updatePreferences(nextPreferences);
|
);
|
||||||
}, [
|
}, [
|
||||||
availableModels,
|
availableModels,
|
||||||
formState.model,
|
formState.model,
|
||||||
formState.modeId,
|
formState.modeId,
|
||||||
formState.provider,
|
formState.provider,
|
||||||
formState.thinkingOptionId,
|
formState.thinkingOptionId,
|
||||||
preferences?.providerPreferences,
|
|
||||||
updatePreferences,
|
updatePreferences,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,16 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type {
|
import type {
|
||||||
AgentFeature,
|
|
||||||
AgentProvider,
|
AgentProvider,
|
||||||
AgentSessionConfig,
|
AgentSessionConfig,
|
||||||
} from "@server/server/agent/agent-sdk-types";
|
} from "@server/server/agent/agent-sdk-types";
|
||||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
||||||
|
import { mergeProviderPreferences, useFormPreferences } from "./use-form-preferences";
|
||||||
function pruneFeatureValues(
|
import {
|
||||||
featureValues: Record<string, unknown>,
|
applyFeatureValues,
|
||||||
features: AgentFeature[],
|
pruneFeatureValues,
|
||||||
): Record<string, unknown> {
|
resolveFeatureValues,
|
||||||
const allowedFeatureIds = new Set(features.map((feature) => feature.id));
|
} from "./feature-preferences";
|
||||||
let changed = false;
|
|
||||||
const next: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
for (const [featureId, value] of Object.entries(featureValues)) {
|
|
||||||
if (!allowedFeatureIds.has(featureId)) {
|
|
||||||
changed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
next[featureId] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed ? next : featureValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyFeatureValues(
|
|
||||||
features: AgentFeature[],
|
|
||||||
featureValues: Record<string, unknown>,
|
|
||||||
): AgentFeature[] {
|
|
||||||
if (Object.keys(featureValues).length === 0) {
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
|
|
||||||
return features.map((feature) => {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(featureValues, feature.id)) {
|
|
||||||
return feature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...feature,
|
|
||||||
value: featureValues[feature.id],
|
|
||||||
} as AgentFeature;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
type DraftFeatureConfig = Pick<
|
type DraftFeatureConfig = Pick<
|
||||||
AgentSessionConfig,
|
AgentSessionConfig,
|
||||||
@@ -60,10 +26,15 @@ export function useDraftAgentFeatures(input: {
|
|||||||
thinkingOptionId: string | null | undefined;
|
thinkingOptionId: string | null | undefined;
|
||||||
}) {
|
}) {
|
||||||
const { serverId, provider, cwd, modeId, modelId, thinkingOptionId } = input;
|
const { serverId, provider, cwd, modeId, modelId, thinkingOptionId } = input;
|
||||||
const [featureValues, setFeatureValues] = useState<Record<string, unknown>>({});
|
const [localFeatureValues, setLocalFeatureValues] = useState<Record<string, unknown>>({});
|
||||||
const client = useHostRuntimeClient(serverId ?? "");
|
const client = useHostRuntimeClient(serverId ?? "");
|
||||||
const isConnected = useHostRuntimeIsConnected(serverId ?? "");
|
const isConnected = useHostRuntimeIsConnected(serverId ?? "");
|
||||||
|
const { preferences, updatePreferences } = useFormPreferences();
|
||||||
const normalizedCwd = cwd?.trim() || "";
|
const normalizedCwd = cwd?.trim() || "";
|
||||||
|
const persistedFeatureValues = useMemo(
|
||||||
|
() => preferences.providerPreferences?.[provider]?.featureValues ?? {},
|
||||||
|
[preferences.providerPreferences, provider],
|
||||||
|
);
|
||||||
|
|
||||||
const draftConfig = useMemo<DraftFeatureConfig | null>(() => {
|
const draftConfig = useMemo<DraftFeatureConfig | null>(() => {
|
||||||
if (!normalizedCwd) {
|
if (!normalizedCwd) {
|
||||||
@@ -102,28 +73,56 @@ export function useDraftAgentFeatures(input: {
|
|||||||
return payload.features ?? [];
|
return payload.features ?? [];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const availableFeatures = featuresQuery.data ?? [];
|
||||||
|
const featureValues = useMemo(
|
||||||
|
() =>
|
||||||
|
resolveFeatureValues({
|
||||||
|
features: availableFeatures,
|
||||||
|
persistedFeatureValues,
|
||||||
|
localFeatureValues,
|
||||||
|
}),
|
||||||
|
[availableFeatures, localFeatureValues, persistedFeatureValues],
|
||||||
|
);
|
||||||
|
|
||||||
const features = useMemo(() => {
|
const features = useMemo(() => {
|
||||||
return applyFeatureValues(featuresQuery.data ?? [], featureValues);
|
return applyFeatureValues(availableFeatures, featureValues);
|
||||||
}, [featureValues, featuresQuery.data]);
|
}, [availableFeatures, featureValues]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const next = pruneFeatureValues(featureValues, features);
|
setLocalFeatureValues({});
|
||||||
if (next !== featureValues) {
|
}, [provider]);
|
||||||
setFeatureValues(next);
|
|
||||||
|
useEffect(() => {
|
||||||
|
const next = pruneFeatureValues(localFeatureValues, availableFeatures);
|
||||||
|
if (next !== localFeatureValues) {
|
||||||
|
setLocalFeatureValues(next);
|
||||||
}
|
}
|
||||||
}, [featureValues, features]);
|
}, [availableFeatures, localFeatureValues]);
|
||||||
|
|
||||||
const effectiveFeatureValues = Object.keys(featureValues).length > 0 ? featureValues : undefined;
|
const effectiveFeatureValues = Object.keys(featureValues).length > 0 ? featureValues : undefined;
|
||||||
const setFeatureValue = useCallback((featureId: string, value: unknown) => {
|
const setFeatureValue = useCallback((featureId: string, value: unknown) => {
|
||||||
setFeatureValues((current) => {
|
setLocalFeatureValues((current) => {
|
||||||
if (Object.is(current[featureId], value)) {
|
if (Object.is(current[featureId], value)) {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...current, [featureId]: value };
|
return { ...current, [featureId]: value };
|
||||||
});
|
});
|
||||||
}, []);
|
void updatePreferences(
|
||||||
|
(current) =>
|
||||||
|
mergeProviderPreferences({
|
||||||
|
preferences: current,
|
||||||
|
provider,
|
||||||
|
updates: {
|
||||||
|
featureValues: {
|
||||||
|
[featureId]: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).catch((error) => {
|
||||||
|
console.warn("[useDraftAgentFeatures] persist feature preference failed", error);
|
||||||
|
});
|
||||||
|
}, [provider, updatePreferences]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
features,
|
features,
|
||||||
|
|||||||
@@ -59,6 +59,41 @@ describe("mergeProviderPreferences", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("merges feature values without dropping existing entries", () => {
|
||||||
|
expect(
|
||||||
|
mergeProviderPreferences({
|
||||||
|
preferences: {
|
||||||
|
provider: "codex",
|
||||||
|
providerPreferences: {
|
||||||
|
codex: {
|
||||||
|
model: "gpt-5.4",
|
||||||
|
featureValues: {
|
||||||
|
fast_mode: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provider: "codex",
|
||||||
|
updates: {
|
||||||
|
featureValues: {
|
||||||
|
plan_mode: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
provider: "codex",
|
||||||
|
providerPreferences: {
|
||||||
|
codex: {
|
||||||
|
model: "gpt-5.4",
|
||||||
|
featureValues: {
|
||||||
|
fast_mode: true,
|
||||||
|
plan_mode: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("favorite model preferences", () => {
|
describe("favorite model preferences", () => {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const providerPreferencesSchema = z.object({
|
|||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
mode: z.string().optional(),
|
mode: z.string().optional(),
|
||||||
thinkingByModel: z.record(z.string()).optional(),
|
thinkingByModel: z.record(z.string()).optional(),
|
||||||
|
featureValues: z.record(z.unknown()).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const formPreferencesSchema = z.object({
|
const formPreferencesSchema = z.object({
|
||||||
@@ -53,7 +54,9 @@ async function loadFormPreferences(): Promise<FormPreferences> {
|
|||||||
export interface UseFormPreferencesReturn {
|
export interface UseFormPreferencesReturn {
|
||||||
preferences: FormPreferences;
|
preferences: FormPreferences;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
updatePreferences: (updates: Partial<FormPreferences>) => Promise<void>;
|
updatePreferences: (
|
||||||
|
updates: Partial<FormPreferences> | ((current: FormPreferences) => FormPreferences),
|
||||||
|
) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeProviderPreferences(args: {
|
export function mergeProviderPreferences(args: {
|
||||||
@@ -71,6 +74,13 @@ export function mergeProviderPreferences(args: {
|
|||||||
...existing.thinkingByModel,
|
...existing.thinkingByModel,
|
||||||
...updates.thinkingByModel,
|
...updates.thinkingByModel,
|
||||||
};
|
};
|
||||||
|
const nextFeatureValues =
|
||||||
|
updates.featureValues === undefined
|
||||||
|
? existing.featureValues
|
||||||
|
: {
|
||||||
|
...existing.featureValues,
|
||||||
|
...updates.featureValues,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...preferences,
|
...preferences,
|
||||||
@@ -81,6 +91,7 @@ export function mergeProviderPreferences(args: {
|
|||||||
...existing,
|
...existing,
|
||||||
...updates,
|
...updates,
|
||||||
...(nextThinkingByModel ? { thinkingByModel: nextThinkingByModel } : {}),
|
...(nextThinkingByModel ? { thinkingByModel: nextThinkingByModel } : {}),
|
||||||
|
...(nextFeatureValues ? { featureValues: nextFeatureValues } : {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -133,11 +144,12 @@ export function useFormPreferences(): UseFormPreferencesReturn {
|
|||||||
const preferences = data ?? DEFAULT_FORM_PREFERENCES;
|
const preferences = data ?? DEFAULT_FORM_PREFERENCES;
|
||||||
|
|
||||||
const updatePreferences = useCallback(
|
const updatePreferences = useCallback(
|
||||||
async (updates: Partial<FormPreferences>) => {
|
async (updates: Partial<FormPreferences> | ((current: FormPreferences) => FormPreferences)) => {
|
||||||
const prev =
|
const prev =
|
||||||
queryClient.getQueryData<FormPreferences>(FORM_PREFERENCES_QUERY_KEY) ??
|
queryClient.getQueryData<FormPreferences>(FORM_PREFERENCES_QUERY_KEY) ??
|
||||||
DEFAULT_FORM_PREFERENCES;
|
DEFAULT_FORM_PREFERENCES;
|
||||||
const next = { ...prev, ...updates };
|
const next =
|
||||||
|
typeof updates === "function" ? updates(prev) : { ...prev, ...updates };
|
||||||
queryClient.setQueryData<FormPreferences>(FORM_PREFERENCES_QUERY_KEY, next);
|
queryClient.setQueryData<FormPreferences>(FORM_PREFERENCES_QUERY_KEY, next);
|
||||||
await AsyncStorage.setItem(FORM_PREFERENCES_STORAGE_KEY, JSON.stringify(next));
|
await AsyncStorage.setItem(FORM_PREFERENCES_STORAGE_KEY, JSON.stringify(next));
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user