Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + React + Typescript</title>
<title>PictoPy - AI-Powered Photo Manager</title>
</head>

<body>
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/components/Media/ChronologicalGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { groupImagesByYearMonthFromMetadata } from '@/utils/dateUtils';
import { setCurrentViewIndex } from '@/features/imageSlice';
import { MediaView } from './MediaView';
import { selectIsImageViewOpen } from '@/features/imageSelectors';
import { useSidebar } from '@/components/ui/sidebar';

export type MonthMarker = {
offset: number;
Expand Down Expand Up @@ -34,6 +35,11 @@ export const ChronologicalGallery = ({
const monthHeaderRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());
const galleryRef = useRef<HTMLDivElement>(null);
const isImageViewOpen = useSelector(selectIsImageViewOpen);
const { open: sidebarOpen } = useSidebar();

// Adjust grid minmax based on sidebar state
// When sidebar is collapsed, reduce min size to fit more images per row
const gridMinSize = sidebarOpen ? '224px' : '200px';

// Optimized grouping with proper date handling
const grouped = useMemo(
Expand Down Expand Up @@ -157,7 +163,12 @@ export const ChronologicalGallery = ({
</div>

{/* Images Grid */}
<div className="grid grid-cols-[repeat(auto-fill,_minmax(224px,_1fr))] gap-4 p-2">
<div
className="grid gap-4 p-2 transition-all duration-300"
style={{
gridTemplateColumns: `repeat(auto-fill, minmax(${gridMinSize}, 1fr))`,
}}
>
{imgs.map((img) => {
const chronologicalIndex =
imageIndexMap.get(img.id) ?? -1;
Expand Down
54 changes: 52 additions & 2 deletions frontend/src/components/Navigation/Sidebar/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
SidebarSeparator,
useSidebar,
} from '@/components/ui/sidebar';
import {
Bolt,
Expand All @@ -16,15 +17,23 @@ import {
Video,
BookImage,
ClockFading,
ChevronLeft,
} from 'lucide-react';
import { useLocation, Link } from 'react-router';
import { ROUTES } from '@/constants/routes';
import { getVersion } from '@tauri-apps/api/app';
import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';

export function AppSidebar() {
const location = useLocation();
const [version, setVersion] = useState<string>('1.0.0');
const { open, toggleSidebar } = useSidebar();

useEffect(() => {
getVersion().then((version) => {
Expand Down Expand Up @@ -59,7 +68,7 @@ export function AppSidebar() {
<Sidebar
variant="sidebar"
collapsible="icon"
className="border-border/40 border-r shadow-sm"
className="border-border/40 border-r shadow-sm transition-all duration-300 ease-in-out"
>
<SidebarHeader className="flex justify-center py-3">
<div className="text-lg font-semibold">.</div>
Expand All @@ -73,7 +82,7 @@ export function AppSidebar() {
asChild
isActive={isActive(item.path)}
tooltip={item.name}
className="rounded-sm"
className="rounded-sm transition-all duration-200"
>
<Link to={item.path} className="flex items-center gap-3">
<item.icon className="h-5 w-5" />
Expand All @@ -85,9 +94,50 @@ export function AppSidebar() {
</SidebarMenu>
</SidebarContent>
<SidebarFooter className="border-border/40 mt-auto border-t py-4">
<<<<<<< sidebar-toggle
<div className="space-y-3">
<div
className={`text-muted-foreground space-y-1 px-4 text-xs transition-all duration-300 ${
open
? 'translate-x-0 opacity-100'
: 'pointer-events-none absolute -translate-x-2 opacity-0'
}`}
>
<div className="font-medium whitespace-nowrap">
PictoPy v{version}
</div>
<div className="whitespace-nowrap">
© {new Date().getFullYear()} PictoPy
</div>
</div>
<div
className={`flex transition-all duration-300 ${open ? 'justify-end px-4' : 'justify-center'}`}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={toggleSidebar}
className="hover:bg-accent h-8 w-8 transition-transform duration-300 ease-in-out"
>
<ChevronLeft
className={`h-5 w-5 transition-transform duration-300 ease-in-out ${
!open ? 'rotate-180' : ''
}`}
/>
</Button>
</TooltipTrigger>
<TooltipContent side="right">
<p>{open ? 'Collapse sidebar' : 'Expand sidebar'}</p>
</TooltipContent>
</Tooltip>
</div>
=======
<div className="text-muted-foreground space-y-1 px-4 text-xs">
<div className="font-medium">PictoPy v{version}</div>
<div>© {new Date().getFullYear()} PictoPy</div>
>>>>>>> main
</div>
</SidebarFooter>
</Sidebar>
Expand Down
30 changes: 22 additions & 8 deletions frontend/src/components/ui/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,24 @@ function SidebarProvider({
const [openMobile, setOpenMobile] = React.useState(false);

// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen);
const open = openProp ?? _open;

// Restore sidebar state from cookie on mount
React.useEffect(() => {
try {
const cookies = document.cookie
.split('; ')
.find((row) => row.startsWith(SIDEBAR_COOKIE_NAME + '='));
if (cookies) {
const savedState = cookies.split('=')[1] === 'true';
_setOpen(savedState);
}
} catch {
// Ignore cookie errors
}
}, []);

const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === 'function' ? value(open) : value;
Expand All @@ -81,7 +96,9 @@ function SidebarProvider({
}

// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
try {
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
} catch {}
},
[setOpenProp, open],
);
Expand All @@ -107,8 +124,6 @@ function SidebarProvider({
return () => window.removeEventListener('keydown', handleKeyDown);
}, [toggleSidebar]);

// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? 'expanded' : 'collapsed';

const contextValue = React.useMemo<SidebarContextProps>(
Expand Down Expand Up @@ -231,7 +246,7 @@ function Sidebar({
side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
// Adjust the padding for floating and inset variants.

variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
Expand Down Expand Up @@ -425,7 +440,7 @@ function SidebarGroupAction({
data-sidebar="group-action"
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.

'after:absolute after:-inset-2 md:after:hidden',
'group-data-[collapsible=icon]:hidden',
className,
Expand Down Expand Up @@ -560,7 +575,7 @@ function SidebarMenuAction({
data-sidebar="menu-action"
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.

'after:absolute after:-inset-2 md:after:hidden',
'peer-data-[size=sm]/menu-button:top-1',
'peer-data-[size=default]/menu-button:top-1.5',
Expand Down Expand Up @@ -604,7 +619,6 @@ function SidebarMenuSkeleton({
}: React.ComponentProps<'div'> & {
showIcon?: boolean;
}) {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`;
}, []);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Home/MyFav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const MyFav = () => {
}, [data, isSuccess, dispatch, isSearchActive]);

const favouriteImages = useMemo(
() => images.filter((image) => image.isFavourite === true),
() => images.filter((image) => image.isFavourite),
[images],
);

Expand Down
2 changes: 1 addition & 1 deletion landing-page/src/Pages/Landing page/Home1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const shuffle = (array: (typeof squareData)[0][]) => {
let currentIndex = array.length,
randomIndex;

while (currentIndex != 0) {
while (currentIndex !== 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;

Expand Down
Loading