Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: persist gallery draft #326

Merged
merged 51 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7f59dc7
wip
shahin-hq Oct 27, 2023
beba28b
wip
shahin-hq Oct 30, 2023
2b2b96a
wip
shahin-hq Oct 30, 2023
694e033
wip
shahin-hq Oct 30, 2023
1f92817
wip
shahin-hq Oct 30, 2023
8cc7418
wip
shahin-hq Oct 30, 2023
b03b234
wip
shahin-hq Oct 30, 2023
2d110aa
Merge branch 'develop' into feat/persist-gallery-drafts
shahin-hq Oct 30, 2023
3bcd7fc
wip
shahin-hq Oct 31, 2023
bdc3a87
Merge remote-tracking branch 'origin/feat/persist-gallery-drafts' int…
shahin-hq Oct 31, 2023
4a49115
wip
shahin-hq Oct 31, 2023
ea516a6
Merge branch 'develop' into feat/persist-gallery-drafts
shahin-hq Oct 31, 2023
b0aef6f
wip
shahin-hq Oct 31, 2023
07cd85c
Merge remote-tracking branch 'origin/feat/persist-gallery-drafts' int…
shahin-hq Oct 31, 2023
63bc5a9
wip
shahin-hq Oct 31, 2023
6a0d962
Merge branch 'develop' into feat/persist-gallery-drafts
alfonsobries Oct 31, 2023
48f6b47
Merge branch 'develop' into feat/persist-gallery-drafts
goga-m Nov 1, 2023
df01afd
wip
shahin-hq Nov 1, 2023
69e3f55
wip
shahin-hq Nov 1, 2023
08f07f4
Merge branch 'develop' into feat/persist-gallery-drafts
shahin-hq Nov 1, 2023
26d5e80
Merge remote-tracking branch 'origin/feat/persist-gallery-drafts' int…
shahin-hq Nov 1, 2023
228ea59
wip
shahin-hq Nov 1, 2023
79f5d9b
wip
shahin-hq Nov 1, 2023
19b5d54
wip
shahin-hq Nov 1, 2023
a6dce57
Merge branch 'feat/persist-gallery-drafts' into feat/save-gallery-drafts
shahin-hq Nov 1, 2023
05ed6d4
wip
shahin-hq Nov 1, 2023
0c373ce
wip
shahin-hq Nov 1, 2023
72c8e27
Merge branch 'feat/persist-gallery-drafts' into feat/save-gallery-drafts
shahin-hq Nov 1, 2023
11be9a8
wip
shahin-hq Nov 1, 2023
6513069
wip
shahin-hq Nov 1, 2023
bce9e62
Optimize 1 SVG(s)
shahin-hq Nov 1, 2023
1e12f9b
Optimize 1 SVG(s)
ItsANameToo Nov 1, 2023
223a153
wip
shahin-hq Nov 1, 2023
e7e0778
Merge branch 'feat/gallery-drafts' into feat/persist-gallery-drafts
shahin-hq Nov 2, 2023
00561a1
Merge branch 'feat/gallery-drafts' into feat/persist-gallery-drafts
shahin-hq Nov 2, 2023
5975eee
Merge remote-tracking branch 'origin/feat/save-gallery-drafts' into f…
shahin-hq Nov 2, 2023
d013354
fix tests
shahin-hq Nov 2, 2023
cc12a80
fix tests
shahin-hq Nov 2, 2023
55cf480
fix tests
shahin-hq Nov 2, 2023
a54a9f1
fix tests
shahin-hq Nov 2, 2023
39491b1
fix tests
shahin-hq Nov 2, 2023
e89e4bb
fix tests
shahin-hq Nov 2, 2023
cc1c30c
fix tests
shahin-hq Nov 2, 2023
f4475eb
Merge branch 'feat/gallery-drafts' into feat/save-gallery-drafts
shahin-hq Nov 2, 2023
c66fb97
fix tests
shahin-hq Nov 2, 2023
84e8755
Merge branch 'feat/persist-gallery-drafts' into feat/save-gallery-drafts
shahin-hq Nov 2, 2023
02b39f4
fix tests
shahin-hq Nov 2, 2023
cde31a2
mip
shahin-hq Nov 2, 2023
267323d
wip
shahin-hq Nov 2, 2023
cd569ac
Merge branch 'feat/gallery-drafts' into feat/save-gallery-drafts
shahin-hq Nov 2, 2023
cdda34b
feat: handle gallery drafts toolbar actions (#346)
shahin-hq Nov 3, 2023
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: 2 additions & 0 deletions lang/en/pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@
'can_purchase' => 'You can purchase NFTs with these top NFT Marketplaces:',
'must_own_one_nft' => 'You must own at least one (1) NFT in order to create a gallery.',
'back_to_galleries' => 'Back to Galleries',
'draft_saved' => 'Draft Saved',
'saving_to_draft' => 'Saving to draft',
],
'delete_modal' => [
'title' => 'Delete Gallery',
Expand Down
1 change: 1 addition & 0 deletions resources/icons/fat-double-check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ describe("GalleryActionToolbar", () => {
expect(screen.getByTestId("GalleryActionToolbar__publish")).toBeDisabled();
});

it("should show saving to draft icon", () => {
render(
<GalleryActionToolbar
galleryCoverUrl="/test"
isProcessing
isSavingDraft={true}
/>,
);

expect(screen.getByTestId("Icon_SavingDraft")).toBeInTheDocument();
});

it("should show draft saved icon", () => {
render(
<GalleryActionToolbar
galleryCoverUrl="/test"
isProcessing
draftId={1}
isSavingDraft={false}
/>,
);

expect(screen.getByTestId("Icon_DraftSaved")).toBeInTheDocument();
});

it.each(allBreakpoints)("should render without delete button in %s screen", (breakpoint) => {
render(<GalleryActionToolbar showDelete={false} />, { breakpoint });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { type FormEvent, type MouseEvent, useLayoutEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button, IconButton } from "@/Components/Buttons";
import { GalleryDraftStatus } from "@/Components/Galleries/GalleryPage/GalleryActionToolbar/GalleryDraftStatus";
import { Icon } from "@/Components/Icon";
import { Img } from "@/Components/Image";
import { isTruthy } from "@/Utils/is-truthy";

Expand All @@ -13,6 +15,8 @@ export const GalleryActionToolbar = ({
onCancel,
isProcessing = false,
showDelete = true,
draftId,
isSavingDraft,
}: {
onCoverClick?: (event: MouseEvent<HTMLButtonElement>) => void;
onTemplateClick?: (event: MouseEvent<HTMLButtonElement>) => void;
Expand All @@ -22,6 +26,8 @@ export const GalleryActionToolbar = ({
galleryCoverUrl?: string;
isProcessing?: boolean;
showDelete?: boolean;
draftId?: number;
isSavingDraft?: boolean;
}): JSX.Element => {
const { t } = useTranslation();
const [scrollbarWidth, setScrollbarWidth] = useState(0);
Expand Down Expand Up @@ -85,17 +91,48 @@ export const GalleryActionToolbar = ({
)}
</div>

<div className="xs:hidden">
<div className="flex items-center">
{isTruthy(draftId) && isSavingDraft === false && (
<Icon
size="lg"
name="FatDoubleCheck"
data-testid="Icon_DraftSaved"
className="text-theme-secondary-700"
/>
)}

{isSavingDraft === true && (
<Icon
size="lg"
name="Refresh"
data-testid="Icon_SavingDraft"
className="text-theme-secondary-700 dark:text-theme-dark-200"
/>
)}
</div>
</div>

<div className="flex space-x-3 sm:hidden">
<IconButton
icon="DoorExit"
className="rotate-180"
onClick={onCancel}
/>
<IconButton
icon="CheckSmall"
variant="primary"
onClick={onPublish}
/>
<div className="hidden xs:flex">
<GalleryDraftStatus
isSavingDraft={isSavingDraft}
draftId={draftId}
/>
</div>

<div className="flex space-x-3 sm:hidden">
<IconButton
icon="DoorExit"
className="rotate-180"
onClick={onCancel}
/>
<IconButton
icon="CheckSmall"
variant="primary"
onClick={onPublish}
/>
</div>
</div>

<div className="hidden w-auto flex-nowrap space-x-3 lg:flex">
Expand Down Expand Up @@ -158,6 +195,11 @@ export const GalleryActionToolbar = ({
</div>

<div className="hidden space-x-3 sm:flex">
<GalleryDraftStatus
isSavingDraft={isSavingDraft}
draftId={draftId}
/>

{showDelete && (
<IconButton
data-testid="GalleryActionToolbar__delete"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect } from "vitest";
import { GalleryDraftStatus } from "@/Components/Galleries/GalleryPage/GalleryActionToolbar/GalleryDraftStatus";
import { render, screen } from "@/Tests/testing-library";

describe("GalleryDraftStatus", () => {
it("should show draft saved status", () => {
render(
<GalleryDraftStatus
draftId={1}
isSavingDraft={false}
/>,
);

expect(screen.getByText(/Draft Saved/)).toBeInTheDocument();
});

it("should show saving status", () => {
render(
<GalleryDraftStatus
draftId={1}
isSavingDraft={true}
/>,
);

expect(screen.getByText(/Saving to draft/)).toBeInTheDocument();
});

it("should not render any status message", () => {
render(<GalleryDraftStatus />);

expect(screen.queryByText(/Saving to draft/)).not.toBeInTheDocument();
expect(screen.queryByText(/Draft Saved/)).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useTranslation } from "react-i18next";
import { Icon } from "@/Components/Icon";
import { isTruthy } from "@/Utils/is-truthy";

export const GalleryDraftStatus = ({
draftId,
isSavingDraft,
}: {
draftId?: number;
isSavingDraft?: boolean;
}): JSX.Element => {
const { t } = useTranslation();

if (isTruthy(draftId) && isSavingDraft === false) {
return (
<div className="flex items-center">
<Icon
size="lg"
name="FatDoubleCheck"
className="text-theme-secondary-700"
/>
<div className="ml-2 hidden text-sm font-medium leading-5.5 text-theme-secondary-700 xs:block">
{t("pages.galleries.create.draft_saved")}
</div>
</div>
);
}

if (isSavingDraft === true) {
return (
<div className="flex items-center">
<Icon
size="lg"
name="Refresh"
className="text-theme-secondary-700 dark:text-theme-dark-200"
/>
<div className="ml-2 hidden text-sm font-medium leading-5.5 text-theme-secondary-700 dark:text-theme-dark-200 xs:block">
{t("pages.galleries.create.saving_to_draft")}
</div>
</div>
);
}

return <></>;
};
2 changes: 1 addition & 1 deletion resources/js/I18n/Locales/en.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { isTruthy } from "@/Utils/is-truthy";
export const GalleryNameInput = ({
name,
onChange,
onBlur,
error,
maxLength = 50,
}: {
maxLength?: number;
name: string;
onChange?: (name: string) => void;
onBlur?: () => void;
error?: string;
}): JSX.Element => {
const { t } = useTranslation();
Expand Down Expand Up @@ -48,6 +50,9 @@ export const GalleryNameInput = ({
type="text"
maxLength={maxLength + 1}
value={name}
onBlur={() => {
onBlur?.();
}}
onChange={(event) => {
onChange?.(event.target.value);
}}
Expand Down
29 changes: 28 additions & 1 deletion resources/js/Pages/Galleries/MyGalleries/Create.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type PageProps, type VisitOptions } from "@inertiajs/core";
import { Head, router, usePage } from "@inertiajs/react";
import uniqBy from "lodash/uniqBy";
import { type FormEvent, type MouseEvent, useCallback, useMemo, useState } from "react";
import { type FormEvent, type MouseEvent, useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { ConfirmDeletionDialog } from "@/Components/ConfirmDeletionDialog";
import { FeaturedCollectionsBanner } from "@/Components/FeaturedCollectionsBanner";
Expand All @@ -17,9 +17,12 @@ import { NoNftsOverlay } from "@/Components/Layout/NoNftsOverlay";
import { useMetaMaskContext } from "@/Contexts/MetaMaskContext";
import { useAuthorizedAction } from "@/Hooks/useAuthorizedAction";
import { GalleryNameInput } from "@/Pages/Galleries/Components/GalleryNameInput";
import { useGalleryDrafts } from "@/Pages/Galleries/hooks/useGalleryDrafts";
import { useGalleryForm } from "@/Pages/Galleries/hooks/useGalleryForm";
import { assertUser, assertWallet } from "@/Utils/assertions";
import { getQueryParameters } from "@/Utils/get-query-parameters";
import { isTruthy } from "@/Utils/is-truthy";
import { replaceUrlQuery } from "@/Utils/replace-url-query";

interface Properties {
auth: PageProps["auth"];
Expand Down Expand Up @@ -57,8 +60,22 @@ const Create = ({
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [busy, setBusy] = useState(false);

const { draftId } = getQueryParameters();

const { setDraftCover, setDraftNfts, setDraftTitle, draft, isSaving } = useGalleryDrafts(
isTruthy(draftId) ? Number(draftId) : undefined,
isTruthy(gallery?.slug),
);

useEffect(() => {
if (draft.id !== null) {
replaceUrlQuery({ draftId: draft.id.toString() });
}
}, [draft.id]);

const { selectedNfts, data, setData, errors, submit, updateSelectedNfts, processing } = useGalleryForm({
gallery,
setDraftNfts,
});

const totalValue = 0;
Expand Down Expand Up @@ -119,6 +136,9 @@ const Create = ({
onChange={(name) => {
setData("name", name);
}}
onBlur={() => {
setDraftTitle(data.name);
}}
/>

<EditableGalleryHook
Expand Down Expand Up @@ -168,6 +188,8 @@ const Create = ({
showDelete={isTruthy(gallery)}
isProcessing={processing}
galleryCoverUrl={galleryCoverImageUrl}
isSavingDraft={isSaving}
draftId={draft.id ?? undefined}
onCoverClick={({ currentTarget }: MouseEvent<HTMLButtonElement>) => {
currentTarget.blur();
setGallerySliderActiveTab(GalleryFormSliderTabs.Cover);
Expand Down Expand Up @@ -198,8 +220,13 @@ const Create = ({
setGalleryCoverImageUrl(imageDataURI);
if (blob === undefined) {
setData("coverImage", null);
setDraftCover(null, null);
} else {
setData("coverImage", new File([blob], blob.name, { type: blob.type }));
// eslint-disable-next-line promise/prefer-await-to-then
void blob.arrayBuffer().then((buf) => {
setDraftCover(buf, blob.type);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to make this function async that is why used then

}
setIsGalleryFormSliderOpen(false);
}}
Expand Down
17 changes: 16 additions & 1 deletion resources/js/Pages/Galleries/hooks/useGalleryDrafts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe("useGalleryDrafts", () => {
});
});

it("should try to create a new row if draft hasn't been created yet", async () => {
it("should try to create a new record if draft hasn't been created yet", async () => {
mocks.useIndexedDB().add.mockResolvedValue(2);

const { result } = renderHook(() => useGalleryDrafts());
Expand All @@ -112,6 +112,20 @@ describe("useGalleryDrafts", () => {
});
});

it("should not create a new draft if disabled", async () => {
mocks.useIndexedDB().add.mockResolvedValue(2);

const { result } = renderHook(() => useGalleryDrafts(undefined, true));

act(() => {
result.current.setDraftTitle("hello");
});

await waitFor(() => {
expect(result.current.draft.id).toBe(null);
});
});

it("should try to update the row if draft is present", async () => {
const givenDraftId = 2;
const updateMock = vi.fn();
Expand Down Expand Up @@ -188,6 +202,7 @@ describe("useGalleryDrafts", () => {
await waitFor(() => {
expect(addMock).not.toHaveBeenCalled();
expect(result.current.reachedLimit).toBe(true);
expect(result.current.isSaving).toBe(false);
});
});

Expand Down
Loading
Loading