[ENG-1452] Default to Home (#1801)

* use React.children for SeeMore

* use Home dir as default route when available
This commit is contained in:
Brendan Allan
2023-11-21 16:22:52 +11:00
committed by GitHub
parent bbc01294f6
commit a827fa382d
7 changed files with 112 additions and 130 deletions

View File

@@ -1,15 +1,14 @@
import { EjectSimple } from '@phosphor-icons/react';
import { useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import { useBridgeQuery, useLibraryQuery } from '@sd/client';
import { Button, toast, tw } from '@sd/ui';
import { Icon, IconName } from '~/components';
import { usePlatform } from '~/util/Platform';
import { useHomeDir } from '~/hooks/useHomeDir';
import SidebarLink from './Link';
import Section from './Section';
import SeeMore from './SeeMore';
import { SeeMore } from './SeeMore';
const Name = tw.span`truncate`;
@@ -29,24 +28,14 @@ const SidebarIcon = ({ name }: { name: IconName }) => {
};
export const EphemeralSection = () => {
const platform = usePlatform();
const homeDir = useQuery(['userDirs', 'home'], () => {
if (platform.userHomeDir) return platform.userHomeDir();
else return null;
});
const locations = useLibraryQuery(['locations.list']);
const homeDir = useHomeDir();
const volumes = useBridgeQuery(['volumes.list']);
// this will return an array of location ids that are also volumes
// { "/Mount/Point": 1, "/Mount/Point2": 2"}
type LocationIdsMap = {
[key: string]: number;
};
const locationIdsForVolumes = useMemo<LocationIdsMap>(() => {
const locationIdsForVolumes = useMemo(() => {
if (!locations.data || !volumes.data) return {};
const volumePaths = volumes.data.map((volume) => volume.mount_points[0] ?? null);
@@ -55,106 +44,82 @@ export const EphemeralSection = () => {
volumePaths.includes(location.path)
);
const locationIdsMap = matchedLocations.reduce((acc, location) => {
if (location.path) {
acc[location.path] = location.id;
const locationIdsMap = matchedLocations.reduce(
(acc, location) => {
if (location.path) {
acc[location.path] = location.id;
}
return acc;
},
{} as {
[key: string]: number;
}
return acc;
}, {} as LocationIdsMap);
);
return locationIdsMap;
}, [locations.data, volumes.data]);
const items = [
{ type: 'network' },
homeDir.data ? { type: 'home', path: homeDir.data } : null,
...(volumes.data || []).flatMap((volume, volumeIndex) =>
volume.mount_points.map((mountPoint, index) =>
mountPoint !== homeDir.data
? { type: 'volume', volume, mountPoint, volumeIndex, index }
: null
)
const mountPoints = (volumes.data || []).flatMap((volume, volumeIndex) =>
volume.mount_points.map((mountPoint, index) =>
mountPoint !== homeDir.data
? { type: 'volume', volume, mountPoint, volumeIndex, index }
: null
)
].filter(Boolean) as Array<{
type: string;
path?: string;
volume?: any;
mountPoint?: string;
volumeIndex?: number;
index?: number;
}>;
);
return (
<Section name="Local">
<SeeMore
items={items}
renderItem={(item, index) => {
<SeeMore>
<SidebarLink className="group relative w-full" to="network">
<SidebarIcon name="Globe" />
<Name>Network</Name>
</SidebarLink>
{homeDir.data && (
<SidebarLink
to={`ephemeral/0?path=${homeDir.data}`}
className="group relative w-full border border-transparent"
>
<SidebarIcon name="Home" />
<Name>Home</Name>
</SidebarLink>
)}
{mountPoints.map((item) => {
if (!item) return;
const locationId = locationIdsForVolumes[item.mountPoint ?? ''];
if (item?.type === 'network') {
return (
<SidebarLink
className="group relative w-full"
to="./network"
key={index}
>
<SidebarIcon name="Globe" />
<Name>Network</Name>
</SidebarLink>
);
}
if (item?.type === 'home') {
return (
<SidebarLink
to={`ephemeral/0?path=${item.path}`}
className="group relative w-full border border-transparent"
key={index}
>
<SidebarIcon name="Home" />
<Name>Home</Name>
</SidebarLink>
);
}
if (item?.type === 'volume') {
const key = `${item.volumeIndex}-${item.index}`;
const name =
item.mountPoint === '/'
? 'Root'
: item.index === 0
? item.volume.name
: item.mountPoint;
const toPath =
locationId !== undefined
? `location/${locationId}`
: `ephemeral/${key}?path=${item.mountPoint}`;
return (
<SidebarLink
to={toPath}
key={key}
className="group relative w-full border border-transparent"
>
<SidebarIcon
name={
item.volume.file_system === 'exfat'
? 'SD'
: item.volume.name === 'Macintosh HD'
? 'HDD'
: 'Drive'
}
/>
<Name>{name}</Name>
{item.volume.disk_type === 'Removable' && <EjectButton />}
</SidebarLink>
);
}
return null; // This should never be reached, but is here to satisfy TypeScript
}}
/>
const key = `${item.volumeIndex}-${item.index}`;
const name =
item.mountPoint === '/'
? 'Root'
: item.index === 0
? item.volume.name
: item.mountPoint;
const toPath =
locationId !== undefined
? `location/${locationId}`
: `ephemeral/${key}?path=${item.mountPoint}`;
return (
<SidebarLink
to={toPath}
key={key}
className="group relative w-full border border-transparent"
>
<SidebarIcon
name={
item.volume.file_system === 'exfat'
? 'SD'
: item.volume.name === 'Macintosh HD'
? 'HDD'
: 'Drive'
}
/>
<Name>{name}</Name>
{item.volume.disk_type === 'Removable' && <EjectButton />}
</SidebarLink>
);
})}
</SeeMore>
</Section>
);
};

View File

@@ -17,7 +17,7 @@ import { useSavedSearches } from '../../Explorer/Search/SavedSearches';
import SidebarLink from './Link';
import LocationsContextMenu from './LocationsContextMenu';
import Section from './Section';
import SeeMore from './SeeMore';
import { SeeMore } from './SeeMore';
import TagsContextMenu from './TagsContextMenu';
type SidebarGroup = {
@@ -142,9 +142,8 @@ export const LibrarySection = () => {
</Link>
}
>
<SeeMore
items={locationsQuery.data || []}
renderItem={(location, index) => (
<SeeMore>
{locationsQuery.data?.map((location) => (
<LocationsContextMenu key={location.id} locationId={location.id}>
<SidebarLink
onContextMenu={() =>
@@ -179,8 +178,8 @@ export const LibrarySection = () => {
<span className="truncate">{location.name}</span>
</SidebarLink>
</LocationsContextMenu>
)}
/>
))}
</SeeMore>
<AddLocationButton className="mt-1" />
</Section>
{!!tags.data?.length && (
@@ -192,9 +191,8 @@ export const LibrarySection = () => {
</NavLink>
}
>
<SeeMore
items={tags.data}
renderItem={(tag, index) => (
<SeeMore>
{tags.data?.map((tag, index) => (
<TagsContextMenu tagId={tag.id} key={tag.id}>
<SidebarLink
onContextMenu={() =>
@@ -219,8 +217,8 @@ export const LibrarySection = () => {
<span className="ml-1.5 truncate text-sm">{tag.name}</span>
</SidebarLink>
</TagsContextMenu>
)}
/>
))}
</SeeMore>
</Section>
)}
</>

View File

@@ -1,22 +1,20 @@
import { ReactNode, useState } from 'react';
import { Children, PropsWithChildren, useState } from 'react';
export const SEE_MORE_COUNT = 5;
interface SeeMoreProps<T> {
items: T[];
renderItem: (item: T, index: number) => ReactNode;
interface Props extends PropsWithChildren {
limit?: number;
}
const SeeMore = <T,>({ items, renderItem, limit = SEE_MORE_COUNT }: SeeMoreProps<T>) => {
export function SeeMore({ children, limit = SEE_MORE_COUNT }: Props) {
const [seeMore, setSeeMore] = useState(false);
const displayedItems = seeMore ? items : items.slice(0, limit);
const childrenArray = Children.toArray(children);
return (
<>
{displayedItems.map((item, index) => renderItem(item, index))}
{items.length > limit && (
{childrenArray.map((child, index) => (seeMore || index < limit ? child : null))}
{childrenArray.length > limit && (
<div
onClick={() => setSeeMore(!seeMore)}
className="mb-1 ml-2 mt-0.5 cursor-pointer text-center text-tiny font-semibold text-ink-faint/50 transition hover:text-accent"
@@ -26,6 +24,4 @@ const SeeMore = <T,>({ items, renderItem, limit = SEE_MORE_COUNT }: SeeMoreProps
)}
</>
);
};
export default SeeMore;
}

View File

@@ -1,6 +1,6 @@
import clsx from 'clsx';
import { Suspense, useEffect, useMemo, useRef } from 'react';
import { Navigate, Outlet, useNavigate } from 'react-router-dom';
import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
import {
ClientContextProvider,
initPlausible,
@@ -48,7 +48,7 @@ const Layout = () => {
const firstLibrary = libraries.data[0];
if (firstLibrary) return <Navigate to={`/${firstLibrary.uuid}`} replace />;
else return <Navigate to="./network" replace />;
else return <Navigate to="./" replace />;
}
return (

View File

@@ -1,5 +1,6 @@
import type { RouteObject } from 'react-router-dom';
import { Navigate } from 'react-router-dom';
import { useHomeDir } from '~/hooks/useHomeDir';
import settingsRoutes from './settings';
@@ -37,6 +38,13 @@ export default [
{
index: true,
Component: () => {
const homeDir = useHomeDir();
if (homeDir.data)
return (
<Navigate to={`ephemeral/0?${new URLSearchParams({ path: homeDir.data })}`} />
);
return <Navigate to="network" />;
}
},

View File

@@ -21,7 +21,7 @@ const Index = () => {
const libraryId = currentLibrary ? currentLibrary.uuid : libraries.data[0]?.uuid;
return <Navigate to={`${libraryId}/network`} replace />;
return <Navigate to={`${libraryId}`} replace />;
};
const Wrapper = () => {

View File

@@ -0,0 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import { usePlatform } from '~/util/Platform';
export function useHomeDir() {
const platform = usePlatform();
return useQuery(
['userDirs', 'home'],
() => {
if (platform.userHomeDir) return platform.userHomeDir();
else return null;
},
{ suspense: true }
);
}