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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ interface SizeCircleProps {
// Get file extension or type // Get file extension or type
function getFileType(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 name = file.name;
const lastDot = name.lastIndexOf("."); const lastDot = name.lastIndexOf(".");
@ -22,7 +22,7 @@ function getFileType(file: File): string {
// Get color based on file type // Get color based on file type
function getFileColor(file: File): string { 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() || ""; const ext = file.name.split(".").pop()?.toLowerCase() || "";

View File

@ -29,7 +29,7 @@ function getTailwindColor(className: string): string {
} }
function getFileColorClass(file: File): 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() || ""; const ext = file.name.split(".").pop()?.toLowerCase() || "";
@ -71,7 +71,7 @@ function getFileColor(file: File): string {
} }
function getFileType(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 name = file.name;
const lastDot = name.lastIndexOf("."); const lastDot = name.lastIndexOf(".");
@ -348,7 +348,7 @@ export function SizeView() {
} }
// Navigate if directory // Navigate if directory
if (d.data.file.kind.type === "Directory") { if (d.data.file.kind === "Directory") {
setCurrentPathRef.current(d.data.file.sd_path); 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 <FileComponent.Thumb
file={file} file={file}
size={200} size={200}
iconScale={0.6}
className="w-full max-w-full" className="w-full max-w-full"
/> />
</div> </div>

View File

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