Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 49 additions & 5 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,44 @@ export function AppSidebar() {
</SidebarMenu>
</SidebarContent>
<SidebarFooter className="border-border/40 mt-auto border-t py-4">
<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>
<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>
</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