import { useState, useCallback, useEffect } from "react";
import {
listFileVersions,
getFileVersion,
restoreFileVersion,
type FileVersionSummary,
type FileVersion,
type FileDetail,
VersionConflictError,
} from "../lib/api";
export interface UseVersionHistoryOptions {
fileId: string | null;
currentVersion: number;
}
export interface VersionHistoryState {
versions: FileVersionSummary[];
loading: boolean;
error: string | null;
selectedVersion: FileVersion | null;
loadingVersion: boolean;
}
export function useVersionHistory(options: UseVersionHistoryOptions) {
const { fileId, currentVersion } = options;
const [versions, setVersions] = useState<FileVersionSummary[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [selectedVersion, setSelectedVersion] = useState<FileVersion | null>(null);
const [loadingVersion, setLoadingVersion] = useState(false);
const [restoring, setRestoring] = useState(false);
const [conflict, setConflict] = useState<{ expected: number; actual: number } | null>(null);
// Fetch version list
const fetchVersions = useCallback(async () => {
if (!fileId) return;
setLoading(true);
setError(null);
try {
const response = await listFileVersions(fileId);
setVersions(response.versions);
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to fetch versions");
} finally {
setLoading(false);
}
}, [fileId]);
// Fetch a specific version's content
const fetchVersion = useCallback(
async (version: number): Promise<FileVersion | null> => {
if (!fileId) return null;
setLoadingVersion(true);
setError(null);
try {
const versionData = await getFileVersion(fileId, version);
setSelectedVersion(versionData);
return versionData;
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to fetch version");
return null;
} finally {
setLoadingVersion(false);
}
},
[fileId]
);
// Restore to a specific version (creates a new version with that content)
const restoreToVersion = useCallback(
async (targetVersion: number): Promise<FileDetail | null> => {
if (!fileId) return null;
setRestoring(true);
setError(null);
setConflict(null);
try {
const result = await restoreFileVersion(fileId, targetVersion, currentVersion);
// Refresh version list after restore
await fetchVersions();
setSelectedVersion(null);
return result;
} catch (e) {
if (e instanceof VersionConflictError) {
setConflict({
expected: e.expectedVersion,
actual: e.actualVersion,
});
return null;
}
setError(e instanceof Error ? e.message : "Failed to restore version");
return null;
} finally {
setRestoring(false);
}
},
[fileId, currentVersion, fetchVersions]
);
// Clear selected version
const clearSelectedVersion = useCallback(() => {
setSelectedVersion(null);
}, []);
// Clear conflict
const clearConflict = useCallback(() => {
setConflict(null);
}, []);
// Fetch versions when fileId changes
useEffect(() => {
if (fileId) {
fetchVersions();
} else {
setVersions([]);
setSelectedVersion(null);
}
}, [fileId, fetchVersions]);
return {
versions,
loading,
error,
selectedVersion,
loadingVersion,
restoring,
conflict,
fetchVersions,
fetchVersion,
restoreToVersion,
clearSelectedVersion,
clearConflict,
};
}