132 lines
4.5 KiB
TypeScript
132 lines
4.5 KiB
TypeScript
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
import { useSearchParams } from "react-router-dom";
|
|
import { useDeltaWatermarks } from "@govoplan/core-webui";
|
|
import type { ApiSettings } from "../../../types";
|
|
import {
|
|
getCampaignWorkspaceDelta,
|
|
type CampaignWorkspaceDeltaResponse
|
|
} from "../../../api/campaigns";
|
|
import type { CampaignWorkspaceData } from "../utils/campaignView";
|
|
|
|
const initialData: CampaignWorkspaceData = {
|
|
campaign: null,
|
|
versions: [],
|
|
currentVersion: null,
|
|
summary: null
|
|
};
|
|
|
|
type UseCampaignWorkspaceDataOptions = {
|
|
includeCurrentVersion?: boolean;
|
|
includeSummary?: boolean;
|
|
includeVersions?: boolean;
|
|
};
|
|
|
|
export function useCampaignWorkspaceData(
|
|
settings: ApiSettings,
|
|
campaignId: string,
|
|
options: UseCampaignWorkspaceDataOptions = {}
|
|
) {
|
|
const {
|
|
includeCurrentVersion = true,
|
|
includeSummary = false,
|
|
includeVersions = false
|
|
} = options;
|
|
const [searchParams] = useSearchParams();
|
|
const selectedVersionId = searchParams.get("version");
|
|
const [data, setData] = useState<CampaignWorkspaceData>(initialData);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState("");
|
|
const dataRef = useRef<CampaignWorkspaceData>(initialData);
|
|
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
|
|
const queryKey = useMemo(
|
|
() => JSON.stringify({
|
|
campaignId,
|
|
selectedVersionId,
|
|
includeCurrentVersion,
|
|
includeSummary,
|
|
includeVersions,
|
|
apiBaseUrl: settings.apiBaseUrl,
|
|
apiKey: settings.apiKey,
|
|
accessToken: settings.accessToken
|
|
}),
|
|
[campaignId, selectedVersionId, includeCurrentVersion, includeSummary, includeVersions, settings.apiBaseUrl, settings.apiKey, settings.accessToken]
|
|
);
|
|
|
|
const reload = useCallback(async () => {
|
|
if (!campaignId) return;
|
|
setLoading(true);
|
|
setError("");
|
|
try {
|
|
const shouldLoadVersions = includeCurrentVersion || includeVersions;
|
|
let nextWatermark = getDeltaWatermark(queryKey);
|
|
let merged: CampaignWorkspaceData = dataRef.current;
|
|
let hasMore = false;
|
|
do {
|
|
const response = await getCampaignWorkspaceDelta(settings, campaignId, {
|
|
versionId: selectedVersionId,
|
|
includeCurrentVersion,
|
|
includeSummary,
|
|
includeVersions: shouldLoadVersions,
|
|
since: nextWatermark,
|
|
});
|
|
merged = mergeWorkspaceDelta(merged, response);
|
|
nextWatermark = response.watermark ?? null;
|
|
hasMore = response.has_more;
|
|
} while (hasMore);
|
|
setDeltaWatermark(queryKey, nextWatermark);
|
|
dataRef.current = merged;
|
|
setData(merged);
|
|
} catch (err) {
|
|
dataRef.current = initialData;
|
|
setData(initialData);
|
|
resetDeltaWatermark(queryKey);
|
|
setError(err instanceof Error ? err.message : String(err));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [settings, campaignId, includeCurrentVersion, includeSummary, includeVersions, selectedVersionId, queryKey, getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark]);
|
|
|
|
useEffect(() => {
|
|
resetDeltaWatermark(queryKey);
|
|
dataRef.current = initialData;
|
|
setData(initialData);
|
|
}, [queryKey, resetDeltaWatermark]);
|
|
|
|
useEffect(() => {
|
|
reload();
|
|
}, [reload]);
|
|
|
|
return { data, loading, error, reload, setError };
|
|
}
|
|
|
|
function mergeWorkspaceDelta(current: CampaignWorkspaceData, response: CampaignWorkspaceDeltaResponse): CampaignWorkspaceData {
|
|
if (response.full) {
|
|
return {
|
|
campaign: response.campaign,
|
|
versions: response.versions,
|
|
currentVersion: response.current_version,
|
|
summary: response.summary
|
|
};
|
|
}
|
|
|
|
const deletedVersionIds = new Set(
|
|
response.deleted
|
|
.filter((item) => item.resource_type === "campaign_version")
|
|
.map((item) => item.id)
|
|
);
|
|
const versionMap = new Map(current.versions.map((version) => [version.id, version]));
|
|
for (const version of response.versions) {
|
|
versionMap.set(version.id, version);
|
|
}
|
|
for (const versionId of deletedVersionIds) {
|
|
versionMap.delete(versionId);
|
|
}
|
|
const currentVersionDeleted = current.currentVersion ? deletedVersionIds.has(current.currentVersion.id) : false;
|
|
return {
|
|
campaign: response.campaign ?? current.campaign,
|
|
versions: Array.from(versionMap.values()).sort((left, right) => (right.version_number ?? 0) - (left.version_number ?? 0)),
|
|
currentVersion: response.current_version ?? (currentVersionDeleted ? null : current.currentVersion),
|
|
summary: response.summary ?? current.summary
|
|
};
|
|
}
|