This commit is contained in:
2025-11-14 19:46:18 +01:00
parent a3c5494bf7
commit bd3eab8b40
10 changed files with 53 additions and 53 deletions
+21 -22
View File
@@ -2,6 +2,8 @@ import type { AxiosInstance } from 'axios';
export type Identifier = string | number;
type Nullable<T> = T | null;
export interface AssetObject {
ordinal?: number;
url?: string | null;
@@ -40,12 +42,12 @@ export type EnsureAssetUrl = (
options?: { start?: number | null; limit?: number | null; force?: boolean; [key: string]: unknown },
) => Promise<unknown>;
export type GetAsset = (document: DocumentLike, assetType: string) => AssetLike | null | undefined;
export type GetAsset = (document: DocumentLike, assetType: string) => Nullable<AssetLike>;
export const getAssetFromGroup = (
assets: AssetLike[] | Record<string, AssetLike> | null | undefined,
assetType: string,
): AssetLike | null => {
assets?: AssetLike[] | Record<string, AssetLike> | null,
assetType: string = '',
): Nullable<AssetLike> => {
if (!assetType || !assets) {
return null;
}
@@ -57,11 +59,11 @@ export const getAssetFromGroup = (
return assets?.[assetType] || null;
};
export const getAssetFromVersion = (currentVersion, assetType) => {
export const getAssetFromVersion = (currentVersion: Nullable<DocumentVersionLike>, assetType: string) => {
if (!currentVersion) {
return null;
}
return getAssetFromGroup(currentVersion.assets, assetType);
return getAssetFromGroup(currentVersion.assets ?? null, assetType);
};
const normalizeAssetObjects = (objects?: AssetObject[] | null): AssetObject[] => {
@@ -182,7 +184,7 @@ export class AssetView {
export const createAssetView = (asset?: AssetLike | null): AssetView => new AssetView(asset);
export const resolveDocumentAssetUrl = (
doc: DocumentLike | null | undefined,
doc: Nullable<DocumentLike>,
type: string,
{
ensureAssetUrl,
@@ -195,7 +197,7 @@ export const resolveDocumentAssetUrl = (
ensureOptions?: { start?: number; limit?: number; [key: string]: unknown };
objectOrdinal?: number;
} = {},
): string | null => {
): Nullable<string> => {
if (!doc || !type) {
return null;
}
@@ -251,13 +253,13 @@ class AssetManager {
this.api = api;
}
rememberAsset(entry: AssetLike | null | undefined) {
rememberAsset(entry?: Nullable<AssetLike>) {
if (entry?.id) {
this.assetCache.set(entry.id, entry);
}
}
hydrateAsset(asset: AssetLike | null | undefined): AssetLike | null | undefined {
hydrateAsset(asset?: Nullable<AssetLike>): Nullable<AssetLike> {
if (!asset || !asset.id) {
return asset;
}
@@ -287,7 +289,7 @@ class AssetManager {
return merged;
}
hydrateDocument(document: DocumentLike | null | undefined): DocumentLike | null | undefined {
hydrateDocument(document?: Nullable<DocumentLike>): Nullable<DocumentLike> {
if (!document) {
return document;
}
@@ -330,14 +332,14 @@ class AssetManager {
return { ...document, current_version: nextCurrentVersion };
}
hydrateDocuments(documents: DocumentLike[] | null | undefined) {
hydrateDocuments(documents?: DocumentLike[] | null) {
if (!Array.isArray(documents)) {
return documents;
return documents ?? [];
}
return documents.map((doc) => this.hydrateDocument(doc));
}
hydrateDetail(detail: { document?: DocumentLike; assets?: AssetLike[] } | null | undefined) {
hydrateDetail(detail?: { document?: DocumentLike; assets?: AssetLike[] } | null) {
if (!detail) {
return detail;
}
@@ -366,10 +368,7 @@ class AssetManager {
return changed ? next : detail;
}
hydrateFolderContents(contents: {
documents?: DocumentLike[];
document?: DocumentLike;
} | null | undefined) {
hydrateFolderContents(contents?: { documents?: DocumentLike[]; document?: DocumentLike } | null) {
if (!contents) {
return contents;
}
@@ -384,12 +383,12 @@ class AssetManager {
}
ensureAsset(
documentId: Identifier | null | undefined,
asset: AssetLike | null | undefined,
documentId?: Identifier | null,
asset?: Nullable<AssetLike>,
{ force = false, start = null, limit = null }: { force?: boolean; start?: number | null; limit?: number | null } = {},
): Promise<AssetLike | null> {
): Promise<Nullable<AssetLike>> {
if (!documentId || !asset?.id) {
return Promise.resolve(asset || null);
return Promise.resolve(asset ?? null);
}
const requestedStart = Number.isInteger(start) && start > 0 ? start : 1;
@@ -476,7 +476,7 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
}, [correspondents, document?.correspondents]);
const metaRows = useMemo(() => {
const rows: { key: string; label: string; value: string | null | undefined }[] = [];
const rows: { key: string; label: string; value: string | null }[] = [];
const currentVersionNumber = document?.current_version?.version_number;
if (Number.isFinite(currentVersionNumber)) {
rows.push({
@@ -17,7 +17,7 @@ const DEFAULT_THUMBNAIL_SIZE = 48;
// Detect when an element becomes visible within a scroll container so we can delay loading.
const useLazyVisibility = (
rootRef: MutableRefObject<Element | null> | null,
resetKey: string | number | null | undefined,
resetKey?: string | number | null,
) => {
const targetRef = useRef<HTMLDivElement | null>(null);
const [isVisible, setIsVisible] = useState(false);
@@ -66,10 +66,10 @@ const useLazyVisibility = (
return { ref: targetRef, isVisible };
};
const getPageCount = (doc: DocumentLike | null | undefined) =>
Number.isFinite(doc?.current_version?.metadata?.page_count)
? (doc?.current_version?.metadata?.page_count as number)
: null;
const getPageCount = (doc?: DocumentLike | null) => {
const count = doc?.current_version?.metadata?.page_count;
return Number.isFinite(count) ? Number(count) : null;
};
type DocumentLike = AssetManagerDocumentLike;
type AssetLike = AssetManagerAssetLike;
+1 -1
View File
@@ -80,7 +80,7 @@ interface DocumentsGridProps {
onTagClick?: (tagId?: Identifier | null) => void;
scrollRef?: RefObject<HTMLElement | null>;
onCorrespondentClick?: (correspondentId?: Identifier | null) => void;
activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null;
activeCorrespondentIdSet?: Set<Identifier | null> | null;
onDocumentRename?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
}
+1 -1
View File
@@ -84,7 +84,7 @@ export interface DocumentsListProps {
tagLookupById?: Map<Identifier, DocumentTag> | null;
onTagClick?: (tagId?: Identifier | null) => void;
onCorrespondentClick?: (correspondentId?: Identifier | null) => void;
activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null;
activeCorrespondentIdSet?: Set<Identifier | null> | null;
scrollRef?: RefObject<HTMLElement | null>;
}
@@ -16,9 +16,9 @@ import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
const ROOT_FOLDER_LABEL = 'Documents';
type DocumentId = string | number;
type NullableDocumentId = DocumentId | null | undefined;
type NullableDocumentId = DocumentId | null;
type SelectedIdList = Array<NullableDocumentId> | null | undefined;
type SelectedIdList = NullableDocumentId[] | null;
type FolderTreeNode = {
id?: DocumentId;
@@ -137,8 +137,8 @@ const buildFolderTreeOptions = (tree?: FolderTreeNode[] | null): SelectionAssign
const buildTagAssignments = (
selectedDocuments: DocumentLike[],
tagLookupById: Map<DocumentId, TagOption> | null | undefined,
tags: TagOption[] | null | undefined,
tagLookupById: Map<DocumentId, TagOption> | null,
tags: TagOption[] | null,
total: number,
): SelectionAssignmentMenuItem[] => {
if (!total) {
@@ -202,7 +202,7 @@ const buildTagAssignments = (
const buildCorrespondentAssignments = (
selectedDocuments: DocumentLike[],
correspondents: CorrespondentOption[] | null | undefined,
correspondents: CorrespondentOption[] | null,
total: number,
): SelectionAssignmentMenuItem[] => {
if (!total) {
@@ -493,7 +493,7 @@ const SelectionFloatingActions: React.FC<SelectionFloatingActionsProps> = ({
if (!documentIdList.length || !onMoveDocumentsToFolder) {
return;
}
const candidate = option as { id?: DocumentId; value?: DocumentId } | DocumentId | null | undefined;
const candidate = option as { id?: DocumentId; value?: DocumentId } | DocumentId | null;
const value = isRecord(candidate)
? (candidate?.id ?? candidate?.value ?? null)
: candidate;
+5 -5
View File
@@ -20,7 +20,7 @@ export interface DocumentLike {
export interface DocumentMetadataItem {
label: string;
value: string | null | undefined;
value: string | null;
}
export const buildDocumentMetadataItems = (document?: DocumentLike | null): DocumentMetadataItem[] => {
@@ -35,19 +35,19 @@ export const buildDocumentMetadataItems = (document?: DocumentLike | null): Docu
{ label: 'Updated at', value: formatDateTime(document.updated_at) },
{
label: 'Filename',
value: document.filename,
value: document.filename ?? null,
},
{
label: 'Original filename',
value: document.original_name || '—',
value: document.original_name ?? null,
},
{
label: 'SHA-256 checksum',
value: metadata.checksum || '—',
value: metadata.checksum ?? null,
},
{
label: 'Content type',
value: document.content_type || '—',
value: document.content_type ?? null,
},
];
};
+9 -8
View File
@@ -38,12 +38,12 @@ interface DescribeSummaryOptions {
export interface DocumentSummaryRow {
key: string;
label: string;
value: string | null | undefined;
value: string | null;
}
export interface DocumentSummary {
title: string | undefined;
originalName: string | null | undefined;
originalName: string | null;
mimeTypeLabel: string;
sizeLabel: string;
createdAtLabel: string;
@@ -51,7 +51,7 @@ export interface DocumentSummary {
updatedAtLabel: string;
pageCount: number | null;
pageCountLabel: string;
folderLabel: string | null | undefined;
folderLabel: string | null;
tags: TagEntry[];
correspondents: CorrespondentEntry[];
tagsSummary: string;
@@ -59,7 +59,7 @@ export interface DocumentSummary {
summaryRows: DocumentSummaryRow[];
}
const coercePageCount = (metadata: DocumentMetadata | null | undefined): number | null => {
const coercePageCount = (metadata?: DocumentMetadata | null): number | null => {
const raw = metadata?.page_count;
if (raw == null || raw === '') {
return null;
@@ -68,7 +68,7 @@ const coercePageCount = (metadata: DocumentMetadata | null | undefined): number
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
};
const sanitizeArray = <T>(entries: (T | null | undefined)[] | null | undefined): T[] =>
const sanitizeArray = <T>(entries?: Array<T | null> | null): T[] =>
Array.isArray(entries) ? entries.filter(Boolean) as T[] : [];
export const describeDocumentSummary = (document?: SummaryDocument | null, options: DescribeSummaryOptions = {}): DocumentSummary => {
@@ -79,7 +79,7 @@ export const describeDocumentSummary = (document?: SummaryDocument | null, optio
if (!document) {
return {
title: '',
originalName: '',
originalName: null,
mimeTypeLabel: '—',
sizeLabel: '—',
createdAtLabel: '—',
@@ -110,7 +110,8 @@ export const describeDocumentSummary = (document?: SummaryDocument | null, optio
const issuedLabel = formatDateTime(document.issued_at);
const updatedAtLabel = formatDateTime(document.updated_at);
const folderLabel = document.folder_path;
const folderLabel = document.folder_path ?? null;
const displayFolderLabel = folderLabel ?? 'Documents';
const tags = sanitizeArray<TagEntry>(document.tags);
const correspondents = sanitizeArray<CorrespondentEntry>(document.correspondents);
@@ -130,7 +131,7 @@ export const describeDocumentSummary = (document?: SummaryDocument | null, optio
{ key: 'issued', label: 'Issued', value: issuedLabel },
{ key: 'pages', label: 'Pages', value: pageCountLabel },
{ key: 'updated', label: 'Updated', value: updatedAtLabel },
{ key: 'folder', label: 'Folder', value: folderLabel },
{ key: 'folder', label: 'Folder', value: displayFolderLabel },
{ key: 'tags', label: 'Tags', value: tagsSummary },
{ key: 'correspondents', label: 'Correspondents', value: correspondentsSummary },
];
+2 -2
View File
@@ -21,7 +21,7 @@ const serializePayload = (payload: TagPayload): string | null => {
}
};
export const createTagTransferPayload = (tag: TagLike | null | undefined, sourceDocId: string | number | null = null): TagPayload | null => {
export const createTagTransferPayload = (tag?: TagLike | null, sourceDocId: string | number | null = null): TagPayload | null => {
if (!tag || tag.id == null) {
return null;
}
@@ -58,7 +58,7 @@ export const writeTagTransferData = (dataTransfer: DataTransfer | null, tag: Tag
}
};
export const readTagTransferData = (dataTransfer: DataTransfer | null | undefined): string | null => {
export const readTagTransferData = (dataTransfer?: DataTransfer | null): string | null => {
if (!dataTransfer) {
return null;
}
+2 -2
View File
@@ -26,8 +26,8 @@ export interface WorkspaceEntry {
}
interface UseEntryPointerOptions {
resolveDocumentRowKey?: (id: string | number) => string | null | undefined;
resolveFolderRowKey?: (id: string | number) => string | null | undefined;
resolveDocumentRowKey?: (id: string | number) => string | null;
resolveFolderRowKey?: (id: string | number) => string | null;
onSelectEntry?: (entry: WorkspaceEntry, event?: PointerEventLike | null, metadata?: EntryPointerMetadata) => void;
onInspectDocument?: (id: string | number, metadata?: EntryPointerMetadata) => void;
}