Skip to content
1,444 changes: 1,002 additions & 442 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.60",
"@types/react": "^18.2.36",
"@types/react-dom": "^18.2.14",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"husky": "^8.0.3",
"lint-staged": "^15.0.2",
Expand Down
71 changes: 71 additions & 0 deletions src/api/albumApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { Album, AlbumsResponse } from "models/Album";
import { IGetObjects } from "types/types";

import axiosClient from "./axios";

export async function addAlbum({
name,
isSystem
}: {
name: string;
isSystem: boolean;
}): Promise<Album> {
const res = await axiosClient.post('album', {
name,
isSystem
});

return res.data;
}

export const getAlbums = () =>
useQuery({
queryKey: ['getAlbums'],
queryFn: async (): Promise<Album[]> => {
const res = await axiosClient.get<AlbumsResponse>('album');
return res.data.albums;
},
});

export const deleteAlbum = () =>
useMutation({
mutationFn: async (id: string) => {
await axiosClient.delete(`album/${id}`);
},
});

export async function addObjectsToAlbum(params: { albumId: string; objectIds: string[] }) {
const { albumId, objectIds } = params;
const { data } = await axiosClient.post(
`album/${encodeURIComponent(albumId)}/objects`, objectIds
);
return data;
}

export const getAlbumItems = async ({ albumId }: { albumId: string; }): Promise<IGetObjects> => {
const res = await axiosClient.get(`/album/${albumId}/100`);
return res.data;
};

export const getAlbumById = async ({ albumId }: { albumId: string; }): Promise<Album> => {
const res = await axiosClient.get(`/album/${albumId}`);
return res.data;
};

export async function updateAlbum({ id, name }: {
id: string
name: string;
}): Promise<void> {
const res = await axiosClient.put('album', { id, name });
return res.data;
}

export async function bulkRemoveObjectsFromAlbum(params: { albumId: string; objectIds: string[] }) {
const { albumId, objectIds } = params;
const { data } = await axiosClient.post(
`/albums/${encodeURIComponent(albumId)}/objects:bulk-delete`,
objectIds
);
return data;
}
6 changes: 3 additions & 3 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
import { UploadImageResponse } from 'models/UploadImageResponse';

import {
BASE_URL,
Expand Down Expand Up @@ -165,7 +166,7 @@ export async function uploadImage({
}: {
file: File;
objectHash: string;
}): Promise<void> {
}): Promise<UploadImageResponse> {
const res = await axiosClient.post(
'object',
{
Expand All @@ -180,7 +181,6 @@ export async function uploadImage({
},
}
);

return res.data;
}

Expand Down Expand Up @@ -401,4 +401,4 @@ export const fetchFavoritesIds = async ({
params: { lastId: pageParam, PageSize: NUMBER_OF_OBJECTS_PER_PAGE },
});
return res.data;
};
};
91 changes: 91 additions & 0 deletions src/components/Albums/AddToAlbumDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import FolderIcon from '@mui/icons-material/Folder';
import {
CircularProgress, Dialog, DialogContent, DialogTitle, List, ListItemButton,
ListItemIcon, ListItemText, Typography
} from '@mui/material';
import { getAlbums } from 'api/albumApi';
import { Album } from 'models/Album';
import * as React from 'react';

type Props = {
open: boolean;
onClose: () => void;
onSelect: (albumId: string) => void;
title?: string;
hideSystemAlbums?: boolean;
};

export const AddToAlbumDialog: React.FC<Props> = ({
open,
onClose,
onSelect,
title = 'Choose album',
hideSystemAlbums = false,
}) => {
const { data: albums, isLoading, isError, error } = getAlbums();

const visibleAlbums = React.useMemo<Album[]>(() => {
const list = albums ?? [];
return hideSystemAlbums ? list.filter(a => !a.isSystem) : list;
}, [albums, hideSystemAlbums]);

const handlePick = (id: string) => {
onSelect(id);
onClose();
};

return (
<Dialog
open={open}
onClose={onClose}
fullWidth
maxWidth="xs"
aria-labelledby="select-album-title"
>
<DialogTitle id="select-album-title">{title}</DialogTitle>

<DialogContent dividers>
{isLoading && <CircularProgress size={24} />}

{isError && (
<Typography color="error">
Error loading albums {(error as Error)?.message ?? 'Unknown error'}
</Typography>
)}

{!isLoading && !isError && visibleAlbums.length === 0 && (
<Typography color="text.secondary">There are no albums to pick</Typography>
)}

{!isLoading && !isError && visibleAlbums.length > 0 && (
<List dense disablePadding>
{visibleAlbums.map((album) => (
<ListItemButton
key={album.id}
onClick={() => handlePick(album.id)}
sx={{
borderRadius: 1,
mb: 0.5,
'&:hover': { bgcolor: 'action.hover' },
}}
>
<ListItemIcon>
<FolderIcon />
</ListItemIcon>

<ListItemText
primary={album.name}
secondary={new Date(album.dateCreated).toLocaleDateString()}
primaryTypographyProps={{ noWrap: true, fontWeight: 600 }}
secondaryTypographyProps={{ noWrap: true }}
/>
</ListItemButton>
))}
</List>
)}
</DialogContent>
</Dialog>
);
};

export default AddToAlbumDialog;
Loading
Loading