Skip to content

Commit

Permalink
feat: add thumbnails mutations and hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
pyphilia committed Dec 1, 2021
1 parent a9c7efe commit 527183d
Show file tree
Hide file tree
Showing 21 changed files with 856 additions and 21 deletions.
62 changes: 61 additions & 1 deletion src/api/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
buildDeleteItemRoute,
buildDeleteItemsRoute,
buildDownloadFilesRoute,
buildDownloadItemThumbnailRoute,
buildDownloadPublicItemThumbnailRoute,
buildEditItemRoute,
buildGetChildrenRoute,
buildGetItemRoute,
Expand All @@ -24,6 +26,7 @@ import {
buildRestoreItemsRoute,
buildS3FileUrl,
buildS3UploadFileRoute,
buildUploadItemThumbnailRoute,
GET_OWN_ITEMS_ROUTE,
GET_RECYCLED_ITEMS_ROUTE,
SHARE_ITEM_WITH_ROUTE,
Expand All @@ -37,7 +40,10 @@ import {
} from './utils';
import { getParentsIdsFromPath } from '../utils/item';
import { ExtendedItem, Item, QueryClientConfig, UUID } from '../types';
import { FALLBACK_TO_PUBLIC_FOR_STATUS_CODES } from '../config/constants';
import {
DEFAULT_THUMBNAIL_SIZES,
FALLBACK_TO_PUBLIC_FOR_STATUS_CODES,
} from '../config/constants';

export const getItem = (
id: UUID,
Expand Down Expand Up @@ -316,6 +322,33 @@ export const uploadItemToS3 = async (
return response.json();
};

export const uploadItemThumbnailToS3 = async (
{
itemId,
filename,
contentType,
}: { itemId: UUID; filename: string; contentType: string },
{ API_HOST }: QueryClientConfig,
) => {
const response = await fetch(
`${API_HOST}/${buildUploadItemThumbnailRoute(itemId)}`,
{
// Send and receive JSON.
...DEFAULT_POST,
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
filename,
contentType,
}),
},
).then(failOnError);

return response.json();
};

export const getS3FileUrl = async (
{ id }: { id: UUID },
{ API_HOST, S3_FILES_HOST }: QueryClientConfig,
Expand Down Expand Up @@ -395,3 +428,30 @@ export const restoreItems = async (
}).then(failOnError);
return res.ok;
};

export const downloadItemThumbnail = async (
{ id, size = DEFAULT_THUMBNAIL_SIZES }: { id: UUID; size?: string },
{ API_HOST }: QueryClientConfig,
) => {
let res = await fetch(
`${API_HOST}/${buildDownloadItemThumbnailRoute({ id, size })}`,
{
...DEFAULT_GET,
headers: {},
},
)

// try to fetch public items if cannot access privately
if (FALLBACK_TO_PUBLIC_FOR_STATUS_CODES.includes(res.status)) {
res = await fetch(
`${API_HOST}/${buildDownloadPublicItemThumbnailRoute({ id, size })}`,
DEFAULT_GET,
).then(failOnError);
}

if (!res.ok) {
throw new Error(res.statusText);
}

return res;
};
64 changes: 63 additions & 1 deletion src/api/member.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StatusCodes } from 'http-status-codes';
import axios from 'axios';
import { failOnError, DEFAULT_GET, DEFAULT_PATCH } from './utils';
import { failOnError, DEFAULT_GET, DEFAULT_PATCH, DEFAULT_POST } from './utils';
import {
buildGetMemberBy,
buildGetMember,
Expand All @@ -9,9 +9,13 @@ import {
buildGetMembersRoute,
buildGetPublicMembersRoute,
buildGetPublicMember,
buildUploadAvatarRoute,
buildDownloadAvatarRoute,
buildDownloadPublicAvatarRoute,
} from './routes';
import { Member, QueryClientConfig, UUID } from '../types';
import {
DEFAULT_THUMBNAIL_SIZES,
FALLBACK_TO_PUBLIC_FOR_STATUS_CODES,
SIGNED_OUT_USER,
} from '../config/constants';
Expand Down Expand Up @@ -107,3 +111,61 @@ export const editMember = async (

return res.json();
};

export const uploadAvatar = async (
{
itemId,
filename,
contentType,
}: { itemId: UUID; filename: string; contentType: string },
{ API_HOST }: QueryClientConfig,
) => {
const response = await fetch(
`${API_HOST}/${buildUploadAvatarRoute(itemId)}`,
{
// Send and receive JSON.
...DEFAULT_POST,
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
filename,
contentType,
}),
},
).then(failOnError);

return response.json();
};

export const downloadAvatar = async (
{ id, size = DEFAULT_THUMBNAIL_SIZES }: { id: UUID; size?: string },
{ API_HOST }: QueryClientConfig,
) => {
let res = await fetch(
`${API_HOST}/${buildDownloadAvatarRoute({ id, size })}`,
{
...DEFAULT_GET,
headers: {},
},
)

if (FALLBACK_TO_PUBLIC_FOR_STATUS_CODES.includes(res.status)) {
res = await fetch(
`${API_HOST}/${buildDownloadPublicAvatarRoute({ id, size })}`,
{
...DEFAULT_GET,
headers: {},
},
).then(failOnError);
}

if (!res.ok) {
// TODO: wrong way to pass error
// should use axios
throw new Error(res.statusText)
}

return res;
};
55 changes: 52 additions & 3 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import qs from 'qs';
import { DEFAULT_THUMBNAIL_SIZES } from '../config/constants';
import { UUID } from '../types';

export const APPS_ROUTE = 'app-items';
Expand Down Expand Up @@ -77,6 +78,48 @@ export const buildUploadFilesRoute = (parentId: UUID) =>
: `${ITEMS_ROUTE}/upload`;
export const buildDownloadFilesRoute = (id: UUID) =>
`${ITEMS_ROUTE}/${id}/download`;
export const buildUploadAvatarRoute = (id: UUID) =>
`${MEMBERS_ROUTE}/avatars/${id}`;
export const buildDownloadAvatarRoute = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id: UUID;
size?: string;
}) =>
`${MEMBERS_ROUTE}/avatars/${id}${qs.stringify(
{ size },
{ addQueryPrefix: true },
)}`;
export const buildDownloadPublicAvatarRoute = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id: UUID;
size?: string;
}) =>
`p/${buildDownloadAvatarRoute({ id, size })}`;
export const buildUploadItemThumbnailRoute = (id: UUID) =>
`${ITEMS_ROUTE}/thumbnails/${id}`;
export const buildDownloadItemThumbnailRoute = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id: UUID;
size?: string;
}) =>
`${ITEMS_ROUTE}/thumbnails/${id}${qs.stringify(
{ size },
{ addQueryPrefix: true },
)}`;
export const buildDownloadPublicItemThumbnailRoute = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id: UUID;
size?: string;
}) =>
`p/${buildDownloadItemThumbnailRoute({ id, size })}`;
export const buildPublicDownloadFilesRoute = (id: UUID) =>
`p/${buildDownloadFilesRoute(id)}`;
export const buildS3UploadFileRoute = (parentId: UUID) =>
Expand Down Expand Up @@ -141,21 +184,21 @@ export const buildRestoreItemsRoute = (ids: UUID[]) =>
)}`;

export const GET_CATEGORY_TYPES_ROUTE = `${ITEMS_ROUTE}/category-types`
export const buildGetCategoriesRoute = (ids?: UUID[]) =>
export const buildGetCategoriesRoute = (ids?: UUID[]) =>
`${CATEGORIES_ROUTE}?${qs.stringify(
{ type: ids },
{ arrayFormat: 'repeat' }
)}`;
export const buildGetCategoryInfoRoute = (id: UUID) => `${CATEGORIES_ROUTE}/${id}`;
export const buildGetItemCategoriesRoute = (id: UUID) => `${ITEMS_ROUTE}/${id}/categories`;
export const buildGetItemsInCategoryRoute = (ids: UUID[]) =>
export const buildGetItemsInCategoryRoute = (ids: UUID[]) =>
`${ITEMS_ROUTE}/withCategories?${qs.stringify(
{ category: ids },
{ arrayFormat: 'repeat' }
)}`;
export const buildPostItemCategoryRoute = (id: UUID) =>
`${ITEMS_ROUTE}/${id}/categories`;
export const buildDeleteItemCategoryRoute = (id: UUID) =>
export const buildDeleteItemCategoryRoute = (id: UUID) =>
`${ITEMS_ROUTE}/item-category/${id}`;

export const API_ROUTES = {
Expand Down Expand Up @@ -217,4 +260,10 @@ export const API_ROUTES = {
buildGetItemCategoriesRoute,
buildPostItemCategoryRoute,
buildDeleteItemCategoryRoute,
buildUploadItemThumbnailRoute,
buildDownloadItemThumbnailRoute,
buildDownloadPublicItemThumbnailRoute,
buildUploadAvatarRoute,
buildDownloadAvatarRoute,
buildDownloadPublicAvatarRoute,
};
8 changes: 8 additions & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ export const FALLBACK_TO_PUBLIC_FOR_STATUS_CODES = [
StatusCodes.UNAUTHORIZED,
StatusCodes.FORBIDDEN,
];

export const THUMBNAIL_SIZES = {
SMALL: 'small',
MEDIUM: 'medium',
LARGE: 'large',
ORIGINAL: 'original',
};
export const DEFAULT_THUMBNAIL_SIZES = THUMBNAIL_SIZES.SMALL;
17 changes: 17 additions & 0 deletions src/config/keys.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { UUID } from '../types';
import { hashItemsIds } from '../utils/item';
import { DEFAULT_THUMBNAIL_SIZES } from './constants';

export const APPS_KEY = 'apps';
export const ITEMS_KEY = 'items';
Expand Down Expand Up @@ -59,6 +60,20 @@ export const buildPublicItemsWithTagKey = (id?: UUID) => [
id,
];
export const RECYCLED_ITEMS_KEY = 'recycledItems';
export const buildItemThumbnailKey = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id?: UUID;
size?: string;
}) => [ITEMS_KEY, id, 'thumbnails', size];
export const buildAvatarKey = ({
id,
size = DEFAULT_THUMBNAIL_SIZES,
}: {
id?: UUID;
size?: string;
}) => [MEMBERS_KEY, id, 'avatars', size];

export const MUTATION_KEYS = {
POST_ITEM: 'postItem',
Expand Down Expand Up @@ -88,4 +103,6 @@ export const MUTATION_KEYS = {
RESTORE_ITEMS: 'restoreItems',
POST_ITEM_CATEGORY: 'postItemCategory',
DELETE_ITEM_CATEGORY: 'deleteItemCategory',
UPLOAD_ITEM_THUMBNAIL: 'uploadItemThumbnail',
UPLOAD_AVATAR: 'uploadAvatar',
};
Loading

0 comments on commit 527183d

Please sign in to comment.