Refactor file handling in Explorer component

- Updated file type checks from `file.kind.type` to `file.kind` for consistency across various components in the Explorer views.
- Enhanced the `Thumb` component to conditionally hide the icon based on thumbnail loading status.
- Adjusted the `HeroStats` component for improved readability and structure.
- Added a new `iconScale` prop to the `FileInspector` component's thumbnail for better visual scaling.
This commit is contained in:
Jamie Pine 2025-11-21 08:01:37 -08:00
parent 553fadd2d4
commit 008d05414a
23 changed files with 154 additions and 134 deletions

View File

@ -20,3 +20,4 @@

View File

@ -70,3 +70,4 @@ enum VideoMediaData {

View File

@ -10,3 +10,4 @@ pub use output::LibraryOpenOutput;

View File

@ -124,3 +124,4 @@ mod tests {

View File

@ -16,3 +16,4 @@ pub struct VolumeTrackInput {

View File

@ -13,3 +13,4 @@ pub struct VolumeUntrackInput {

View File

@ -7,3 +7,4 @@ analysis.md

View File

@ -41,3 +41,4 @@ required-features = ["cli"]

View File

@ -52,3 +52,4 @@ fn main() -> Result<()> {

View File

@ -19,3 +19,4 @@ pub fn export_json(templates: &[Template], groups: &[LogGroup]) -> Result<String

View File

@ -241,3 +241,4 @@ mod tests {

View File

@ -106,3 +106,4 @@ mod tests {

View File

@ -147,7 +147,8 @@ export const Thumb = memo(function Thumb({
alt=""
className={clsx(
"object-contain transition-opacity",
thumbLoaded && "opacity-0",
// Only hide icon if we actually have a thumbnail that loaded
thumbLoaded && thumbnailSrc && "opacity-0",
)}
style={{
width: iconSize,
@ -191,16 +192,21 @@ export function Icon({
size?: number;
className?: string;
}) {
const kindCapitalized = file.content_identity?.kind
? file.content_identity.kind.charAt(0).toUpperCase() +
file.content_identity.kind.slice(1)
: "Document";
// This is jank and has to be done in several places. Ideally a util function.
const fileKind =
file?.content_identity?.kind && file.content_identity.kind !== "unknown"
? file.content_identity.kind
: file.kind === "File"
? file.extension || "File"
: file.kind;
// this too
const kindCapitalized = fileKind.charAt(0).toUpperCase() + fileKind.slice(1);
const icon = getIcon(
kindCapitalized,
true, // Dark theme
file.kind.type === "File" ? file.kind.data?.extension : undefined,
file.kind.type === "Directory",
file.extension,
file.kind === "Directory",
);
return (

View File

@ -67,7 +67,7 @@ export function Column({ path, isActive, onNavigate }: ColumnProps) {
icon: FolderOpen,
label: "Open",
onClick: (file: File) => {
if (file.kind.type === "Directory") {
if (file.kind === "Directory") {
onNavigate(file.sd_path);
}
},

View File

@ -40,7 +40,7 @@ export function ColumnItem({
>
<FileComponent.Thumb file={file} size={20} />
<span className="text-sm truncate flex-1">{file.name}</span>
{file.kind.type === "Directory" && (
{file.kind === "Directory" && (
<svg
className="size-3 text-ink-dull"
fill="none"

View File

@ -70,7 +70,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele
icon: FolderOpen,
label: "Open",
onClick: () => {
if (file.kind.type === "Directory") {
if (file.kind === "Directory") {
setCurrentPath(file.sd_path);
} else {
console.log("Open file:", file.name);
@ -78,7 +78,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele
}
},
keybind: "⌘O",
condition: () => file.kind.type === "Directory" || file.kind.type === "File",
condition: () => file.kind === "Directory" || file.kind === "File",
},
{
icon: MagnifyingGlass,
@ -308,7 +308,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele
type: "submenu",
icon: FileText,
label: "Document Processing",
condition: () => ["pdf", "doc", "docx"].includes(file.kind.type === "File" ? file.kind.data?.extension || "" : ""),
condition: () => file.kind === "File" && ["pdf", "doc", "docx"].includes(file.extension || ""),
submenu: [
{
icon: TextAa,
@ -425,7 +425,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele
};
const handleDoubleClick = () => {
if (file.kind.type === "Directory") {
if (file.kind === "Directory") {
setCurrentPath(file.sd_path);
}
};

View File

@ -24,7 +24,7 @@ export function FileRow({ file, fileIndex, allFiles }: FileRowProps) {
};
const handleDoubleClick = () => {
if (file.kind.type === "Directory") {
if (file.kind === "Directory") {
setCurrentPath(file.sd_path);
}
};
@ -60,7 +60,7 @@ export function FileRow({ file, fileIndex, allFiles }: FileRowProps) {
{formatRelativeTime(file.modified_at)}
</div>
<div className="w-24 text-sm text-ink-dull">
{file.kind.type === "File" ? file.kind.data?.extension || "—" : "Folder"}
{file.kind === "File" ? file.extension || "—" : "Folder"}
</div>
</div>
);

View File

@ -11,7 +11,7 @@ interface SizeCircleProps {
// Get file extension or type
function getFileType(file: File): string {
if (file.kind.type === "Directory") return "Folder";
if (file.kind === "Directory") return "Folder";
const name = file.name;
const lastDot = name.lastIndexOf(".");
@ -22,7 +22,7 @@ function getFileType(file: File): string {
// Get color based on file type
function getFileColor(file: File): string {
if (file.kind.type === "Directory") return "bg-blue-500";
if (file.kind === "Directory") return "bg-blue-500";
const ext = file.name.split(".").pop()?.toLowerCase() || "";

View File

@ -29,7 +29,7 @@ function getTailwindColor(className: string): string {
}
function getFileColorClass(file: File): string {
if (file.kind.type === "Directory") return "bg-accent";
if (file.kind === "Directory") return "bg-accent";
const ext = file.name.split(".").pop()?.toLowerCase() || "";
@ -71,7 +71,7 @@ function getFileColor(file: File): string {
}
function getFileType(file: File): string {
if (file.kind.type === "Directory") return "Folder";
if (file.kind === "Directory") return "Folder";
const name = file.name;
const lastDot = name.lastIndexOf(".");
@ -348,7 +348,7 @@ export function SizeView() {
}
// Navigate if directory
if (d.data.file.kind.type === "Directory") {
if (d.data.file.kind === "Directory") {
setCurrentPathRef.current(d.data.file.sd_path);
}
})

View File

@ -59,3 +59,4 @@ export function useJobDispatch() {

View File

@ -137,6 +137,7 @@ function OverviewTab({ file }: { file: File }) {
<FileComponent.Thumb
file={file}
size={200}
iconScale={0.6}
className="w-full max-w-full"
/>
</div>

View File

@ -28,15 +28,15 @@ export function HeroStats({
deviceCount,
uniqueContentCount,
}: HeroStatsProps) {
const usagePercent = totalStorage > 0 ? (usedStorage / totalStorage) * 100 : 0;
const usagePercent =
totalStorage > 0 ? (usedStorage / totalStorage) * 100 : 0;
return (
<motion.div
<div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="bg-app-box border border-app-line rounded-2xl p-8"
>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8">
{/* Total Storage */}
<StatCard
@ -45,7 +45,8 @@ export function HeroStats({
value={formatBytes(totalStorage)}
subtitle={
<>
<span className="text-accent">{formatBytes(usedStorage)}</span> used
<span className="text-accent">{formatBytes(usedStorage)}</span>{" "}
used
</>
}
progress={usagePercent}
@ -80,8 +81,7 @@ export function HeroStats({
badge="PREVIEW"
/>
</div>
</motion.div>
</div>
);
}
@ -133,7 +133,6 @@ function StatCard({
<div className="text-xs text-ink-dull mb-1">{label}</div>
<div className="text-xs text-ink-faint">{subtitle}</div>
</div>
</div>
</div>
);