import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { createToolFlow } from "@app/components/tools/shared/createToolFlow";
import MergeSettings from "@app/components/tools/merge/MergeSettings";
import MergeFileSorter from "@app/components/tools/merge/MergeFileSorter";
import { useMergeParameters } from "@app/hooks/tools/merge/useMergeParameters";
import { useMergeOperation } from "@app/hooks/tools/merge/useMergeOperation";
import { useBaseTool } from "@app/hooks/tools/shared/useBaseTool";
import { BaseToolProps, ToolComponent } from "@app/types/tool";
import { useMergeTips } from "@app/components/tooltips/useMergeTips";
import { useFileManagement, useSelectedFiles, useAllFiles } from "@app/contexts/FileContext";
const Merge = (props: BaseToolProps) => {
const { t } = useTranslation();
const mergeTips = useMergeTips();
// File selection hooks for custom sorting
const { fileIds } = useAllFiles();
const { selectedFileStubs } = useSelectedFiles();
const { reorderFiles } = useFileManagement();
const base = useBaseTool(
'merge',
useMergeParameters,
useMergeOperation,
props,
{ minFiles: 2 }
);
const naturalCompare = useCallback((a: string, b: string): number => {
const isDigit = (char: string) => char >= '0' && char <= '9';
const getChunk = (s: string, length: number, marker: number): { chunk: string; newMarker: number } => {
let chunk = '';
const c = s.charAt(marker);
chunk += c;
marker++;
if (isDigit(c)) {
while (marker < length && isDigit(s.charAt(marker))) {
chunk += s.charAt(marker);
marker++;
}
} else {
while (marker < length && !isDigit(s.charAt(marker))) {
chunk += s.charAt(marker);
marker++;
}
}
return { chunk, newMarker: marker };
};
const len1 = a.length;
const len2 = b.length;
let marker1 = 0;
let marker2 = 0;
while (marker1 < len1 && marker2 < len2) {
const { chunk: chunk1, newMarker: newMarker1 } = getChunk(a, len1, marker1);
marker1 = newMarker1;
const { chunk: chunk2, newMarker: newMarker2 } = getChunk(b, len2, marker2);
marker2 = newMarker2;
let result: number;
if (isDigit(chunk1.charAt(0)) && isDigit(chunk2.charAt(0))) {
const num1 = parseInt(chunk1, 10);
const num2 = parseInt(chunk2, 10);
result = num1 - num2;
} else {
result = chunk1.localeCompare(chunk2);
}
if (result !== 0) {
return result;
}
}
return len1 - len2;
}, []);
// Custom file sorting logic for merge tool
const sortFiles = useCallback((sortType: 'filename' | 'dateModified', ascending: boolean = true) => {
const sortedStubs = [...selectedFileStubs].sort((stubA, stubB) => {
let comparison = 0;
switch (sortType) {
case 'filename':
comparison = naturalCompare(stubA.name, stubB.name);
break;
case 'dateModified':
comparison = stubA.lastModified - stubB.lastModified;
break;
}
return ascending ? comparison : -comparison;
});
const selectedIds = sortedStubs.map(record => record.id);
const deselectedIds = fileIds.filter(id => !selectedIds.includes(id));
reorderFiles([...selectedIds, ...deselectedIds]);
}, [selectedFileStubs, fileIds, reorderFiles, naturalCompare]);
return createToolFlow({
files: {
selectedFiles: base.selectedFiles,
isCollapsed: base.hasResults,
minFiles: 2,
},
steps: [
{
title: "Sort Files",
isCollapsed: base.settingsCollapsed,
content: (
),
},
{
title: "Settings",
isCollapsed: base.settingsCollapsed,
onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset : undefined,
tooltip: mergeTips,
content: (
),
},
],
executeButton: {
text: t("merge.submit", "Merge PDFs"),
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
},
review: {
isVisible: base.hasResults,
operation: base.operation,
title: t("merge.title", "Merge Results"),
onFileClick: base.handleThumbnailClick,
onUndo: base.handleUndo,
},
});
};
export default Merge as ToolComponent;