Skip to content

Commit 7b89eab

Browse files
committed
Keep SaveButton generic while preserving the Scratch save path
1 parent c0ab971 commit 7b89eab

6 files changed

Lines changed: 71 additions & 31 deletions

File tree

src/components/Menus/Sidebar/DownloadPanel/DownloadPanel.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ import {
1515
} from "../../../../events/WebComponentCustomEvents";
1616
import "../../../../assets/stylesheets/DownloadPanel.scss";
1717
import UploadButton from "../../../UploadButton/UploadButton";
18+
import { useScratchSaveButtonProps } from "../../../../hooks/useScratchSave";
1819

1920
export const DownloadPanel = () => {
2021
const { t } = useTranslation();
2122
const user = useSelector((state) => state.auth.user);
2223
const project = useSelector((state) => state.editor.project);
2324
const projectOwner = isOwner(user, project);
25+
const scratchSaveButtonProps = useScratchSaveButtonProps();
2426

2527
const handleLogIn = () => {
2628
if (window.plausible) {
@@ -81,7 +83,7 @@ export const DownloadPanel = () => {
8183
)}
8284
</div>
8385
</div>
84-
{user && !projectOwner && <SaveButton fill />}
86+
{user && !projectOwner && <SaveButton fill {...scratchSaveButtonProps} />}
8587
</SidebarPanel>
8688
);
8789
};

src/components/Menus/Sidebar/ProjectsPanel/ProjectsPanel.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,25 @@ import { useMediaQuery } from "react-responsive";
1414
import SaveStatus from "../../../SaveStatus/SaveStatus";
1515
import { navigateToProjectsPageEvent } from "../../../../events/WebComponentCustomEvents";
1616
import DesignSystemButton from "../../../DesignSystemButton/DesignSystemButton";
17+
import { useScratchSaveButtonProps } from "../../../../hooks/useScratchSave";
1718

1819
const ProjectsPanel = () => {
1920
const { t } = useTranslation();
2021

2122
const isLoggedIn = useSelector((state) => state?.auth?.user);
2223
const isMobile = useMediaQuery({ query: MOBILE_MEDIA_QUERY });
2324
const readOnly = useSelector((state) => state.editor.readOnly);
25+
const scratchSaveButtonProps = useScratchSaveButtonProps({
26+
enabled: !readOnly,
27+
});
2428

2529
const saveOptions = (
2630
<>
2731
<div className="projects-panel__save">
28-
<SaveButton className="projects-panel__save-button" />
32+
<SaveButton
33+
className="projects-panel__save-button"
34+
{...scratchSaveButtonProps}
35+
/>
2936
</div>
3037
<div className="projects-panel__save-status">
3138
<SaveStatus isMobile={isMobile} />

src/components/ProjectBar/ProjectBar.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import SaveButton from "../SaveButton/SaveButton";
99

1010
import "../../assets/stylesheets/ProjectBar.scss";
1111
import { isOwner } from "../../utils/projectHelpers";
12+
import { useScratchSaveButtonProps } from "../../hooks/useScratchSave";
1213

1314
const ProjectBar = ({ nameEditable = true }) => {
1415
const { t } = useTranslation();
@@ -19,6 +20,9 @@ const ProjectBar = ({ nameEditable = true }) => {
1920
const lastSavedTime = useSelector((state) => state.editor.lastSavedTime);
2021
const projectOwner = isOwner(user, project);
2122
const readOnly = useSelector((state) => state.editor.readOnly);
23+
const scratchSaveButtonProps = useScratchSaveButtonProps({
24+
enabled: !readOnly,
25+
});
2226

2327
if (loading !== "success") {
2428
return null;
@@ -38,7 +42,10 @@ const ProjectBar = ({ nameEditable = true }) => {
3842
</div>
3943
{!projectOwner && !readOnly && (
4044
<div className="project-bar__btn-wrapper">
41-
<SaveButton className="project-bar__btn btn--save" />
45+
<SaveButton
46+
className="project-bar__btn btn--save"
47+
{...scratchSaveButtonProps}
48+
/>
4249
</div>
4350
)}
4451
{lastSavedTime && user && !readOnly && (

src/components/SaveButton/SaveButton.jsx

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@ import React, { useEffect, useState, useCallback } from "react";
22
import { useSelector, useDispatch } from "react-redux";
33
import { useTranslation } from "react-i18next";
44
import classNames from "classnames";
5+
import { isOwner } from "../../utils/projectHelpers";
56

67
import { logInEvent } from "../../events/WebComponentCustomEvents";
78

89
import DesignSystemButton from "../DesignSystemButton/DesignSystemButton";
910
import SaveIcon from "../../assets/icons/save.svg";
1011
import { triggerSave } from "../../redux/EditorSlice";
11-
import { useScratchSave } from "../../hooks/useScratchSave";
1212

13-
const SaveButton = ({ className, type, fill = false }) => {
13+
const SaveButton = ({
14+
className,
15+
type,
16+
fill = false,
17+
onSave = null,
18+
isSaving = false,
19+
saveLabelKey = null,
20+
}) => {
1421
const dispatch = useDispatch();
1522
const { t } = useTranslation();
1623

1724
const [buttonType, setButtonType] = useState(type);
1825
const webComponent = useSelector((state) => state.editor.webComponent);
19-
const {
20-
enableScratchSaveState,
21-
isScratchSaving,
22-
loading,
23-
projectOwner,
24-
saveScratchProject,
25-
scratchSaveLabelKey,
26-
shouldRemixOnSave,
27-
user,
28-
} = useScratchSave();
26+
const loading = useSelector((state) => state.editor.loading);
27+
const user = useSelector((state) => state.auth.user);
28+
const project = useSelector((state) => state.editor.project);
29+
const projectOwner = isOwner(user, project);
30+
const usesCustomSave = Boolean(onSave);
2931

3032
useEffect(() => {
3133
if (!type) {
@@ -38,19 +40,15 @@ const SaveButton = ({ className, type, fill = false }) => {
3840
window.plausible("Save button");
3941
}
4042
document.dispatchEvent(logInEvent);
41-
if (enableScratchSaveState) {
42-
saveScratchProject({ shouldRemixOnSave });
43+
if (usesCustomSave) {
44+
onSave();
4345
return;
4446
}
4547
dispatch(triggerSave());
46-
}, [dispatch, enableScratchSaveState, saveScratchProject, shouldRemixOnSave]);
48+
}, [dispatch, onSave, usesCustomSave]);
4749

4850
const buttonText = t(
49-
enableScratchSaveState
50-
? scratchSaveLabelKey
51-
: user
52-
? "header.save"
53-
: "header.loginToSave",
51+
usesCustomSave ? saveLabelKey : user ? "header.save" : "header.loginToSave",
5452
);
5553

5654
return (
@@ -69,7 +67,7 @@ const SaveButton = ({ className, type, fill = false }) => {
6967
icon={<SaveIcon />}
7068
type={buttonType}
7169
fill={fill}
72-
disabled={isScratchSaving}
70+
disabled={isSaving}
7371
/>
7472
)
7573
);

src/components/SaveButton/SaveButton.test.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,9 @@ describe("When project is loaded", () => {
8585
editor: {
8686
loading: "success",
8787
webComponent: true,
88-
scratchIframeProjectIdentifier: "teacher-project",
8988
project: {
9089
identifier: "teacher-project",
9190
user_id: "teacher-id",
92-
project_type: "code_editor_scratch",
9391
},
9492
},
9593
auth: {
@@ -103,7 +101,15 @@ describe("When project is loaded", () => {
103101

104102
render(
105103
<Provider store={scratchStore}>
106-
<SaveButton />
104+
<SaveButton
105+
isSaving={false}
106+
saveLabelKey="header.save"
107+
onSave={() =>
108+
postMessageToScratchIframe({
109+
type: "scratch-gui-remix",
110+
})
111+
}
112+
/>
107113
</Provider>,
108114
);
109115

src/hooks/useScratchSave.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { shouldRemixScratchProjectOnSave } from "../utils/scratchIframe";
44
import { useScratchSaveState } from "./useScratchSaveState";
55

66
export const useScratchSave = ({ enabled = true } = {}) => {
7-
const loading = useSelector((state) => state.editor.loading);
8-
const user = useSelector((state) => state.auth.user);
9-
const project = useSelector((state) => state.editor.project);
7+
const loading = useSelector((state) => state.editor?.loading);
8+
const user = useSelector((state) => state.auth?.user);
9+
const project = useSelector((state) => state.editor?.project);
1010
const scratchIframeProjectIdentifier = useSelector(
11-
(state) => state.editor.scratchIframeProjectIdentifier,
11+
(state) => state.editor?.scratchIframeProjectIdentifier,
1212
);
1313

1414
const projectOwner = isOwner(user, project);
@@ -38,3 +38,23 @@ export const useScratchSave = ({ enabled = true } = {}) => {
3838
user,
3939
};
4040
};
41+
42+
export const useScratchSaveButtonProps = ({ enabled = true } = {}) => {
43+
const {
44+
enableScratchSaveState,
45+
isScratchSaving,
46+
saveScratchProject,
47+
scratchSaveLabelKey,
48+
shouldRemixOnSave,
49+
} = useScratchSave({ enabled });
50+
51+
if (!enableScratchSaveState) {
52+
return {};
53+
}
54+
55+
return {
56+
isSaving: isScratchSaving,
57+
saveLabelKey: scratchSaveLabelKey,
58+
onSave: () => saveScratchProject({ shouldRemixOnSave }),
59+
};
60+
};

0 commit comments

Comments
 (0)