import type { FormEvent } from 'react' import { useEffect, useRef, useState } from 'react' import { useHotkeys } from 'react-hotkeys-hook' import { Link } from 'react-router-dom' import { ActionButton } from '@src/components/ActionButton' import { DeleteConfirmationDialog } from '@src/components/ProjectCard/DeleteProjectDialog' import { ProjectCardRenameForm } from '@src/components/ProjectCard/ProjectCardRenameForm' import Tooltip from '@src/components/Tooltip' import { FILE_EXT, PROJECT_IMAGE_NAME } from '@src/lib/constants' import { PATHS } from '@src/lib/paths' import type { Project } from '@src/lib/project' import { reportRejection } from '@src/lib/trap' import { toSync } from '@src/lib/utils' function ProjectCard({ project, handleRenameProject, handleDeleteProject, ...props }: { project: Project handleRenameProject: ( e: FormEvent, f: Project ) => Promise handleDeleteProject: (f: Project) => Promise }) { useHotkeys('esc', () => setIsEditing(false)) const [isEditing, setIsEditing] = useState(false) const [isConfirmingDelete, setIsConfirmingDelete] = useState(false) const [numberOfFiles, setNumberOfFiles] = useState(1) const [numberOfFolders, setNumberOfFolders] = useState(0) const [imageUrl, setImageUrl] = useState('') let inputRef = useRef(null) function handleSave(e: FormEvent) { e.preventDefault() handleRenameProject(e, project) .then(() => setIsEditing(false)) .catch(reportRejection) } function getDisplayedTime(dateTimeMs: number) { const date = new Date(dateTimeMs) const startOfToday = new Date() startOfToday.setHours(0, 0, 0, 0) return date.getTime() < startOfToday.getTime() ? date.toLocaleDateString() : date.toLocaleTimeString() } useEffect(() => { async function getNumberOfFiles() { setNumberOfFiles(project.kcl_file_count) setNumberOfFolders(project.directory_count) } async function setupImageUrl() { const projectImagePath = window.electron.path.join( project.path, PROJECT_IMAGE_NAME ) if (await window.electron.exists(projectImagePath)) { const imageData = await window.electron.readFile(projectImagePath) const blob = new Blob([imageData], { type: 'image/png' }) const imageUrl = URL.createObjectURL(blob) if (blob.size > 0) { /** * Off chance that a thumbnail.png is cancelled writing and ends up writing 0 bytes * We do not want to load a 0 byte image */ setImageUrl(imageUrl) } } } void getNumberOfFiles() void setupImageUrl() }, [project.kcl_file_count, project.directory_count]) useEffect(() => { if (inputRef.current && isEditing) { inputRef.current.focus() inputRef.current.select() } }, [isEditing, inputRef.current]) return (
  • {imageUrl && ( )}
    {isEditing ? ( e.stopPropagation()} project={project} onDismiss={() => setIsEditing(false)} ref={inputRef} /> ) : (

    {project.name?.replace(FILE_EXT, '')}

    )} {project.readWriteAccess && ( {numberOfFiles} file {numberOfFiles === 1 ? '' : 's'}{' '} {numberOfFolders > 0 && ( <> {'/ '} {numberOfFolders} {' '} folder{numberOfFolders === 1 ? '' : 's'} )} )} Edited{' '} {project.metadata && project.metadata.modified ? getDisplayedTime(parseInt(project.metadata.modified)) : 'never'}
    {!isEditing && (
    { e.stopPropagation() e.nativeEvent.stopPropagation() setIsEditing(true) }} className="!p-0" > Rename project { e.stopPropagation() e.nativeEvent.stopPropagation() setIsConfirmingDelete(true) }} > Delete project
    )} {isConfirmingDelete && ( { await handleDeleteProject(project) setIsConfirmingDelete(false) }, reportRejection)} onDismiss={() => setIsConfirmingDelete(false)} >

    This will permanently delete "{project.name || 'this file'} ".

    Are you sure you want to delete "{project.name || 'this file'} "? This action cannot be undone.

    )}
  • ) } export default ProjectCard