1- import React from "react" ;
2- import { useSelector } from "react-redux" ;
1+ import React , { useEffect } from "react" ;
2+ import { useDispatch , useSelector } from "react-redux" ;
33import { useTranslation } from "react-i18next" ;
44import DownloadIcon from "../../assets/icons/download.svg" ;
55import UploadIcon from "../../assets/icons/upload.svg" ;
@@ -8,26 +8,53 @@ import ProjectName from "../ProjectName/ProjectName";
88import DownloadButton from "../DownloadButton/DownloadButton" ;
99import UploadButton from "../UploadButton/UploadButton" ;
1010import DesignSystemButton from "../DesignSystemButton/DesignSystemButton" ;
11+ import SaveStatus from "../SaveStatus/SaveStatus" ;
1112
1213import "../../assets/stylesheets/ProjectBar.scss" ;
14+ import { setScratchLastSavedTime } from "../../redux/EditorSlice" ;
1315import { useScratchSave } from "../../hooks/useScratchSave" ;
1416
17+ const getProjectLastSavedTime = ( updatedAt ) => {
18+ const timestamp = Date . parse ( updatedAt || "" ) ;
19+ return Number . isNaN ( timestamp ) ? null : timestamp ;
20+ } ;
21+
1522const ScratchProjectBar = ( { nameEditable = true } ) => {
1623 const { t } = useTranslation ( ) ;
24+ const dispatch = useDispatch ( ) ;
1725
1826 const user = useSelector ( ( state ) => state . auth . user ) ;
1927 const loading = useSelector ( ( state ) => state . editor . loading ) ;
28+ const project = useSelector ( ( state ) => state . editor . project ) ;
2029 const readOnly = useSelector ( ( state ) => state . editor . readOnly ) ;
21- const showScratchSaveButton = Boolean ( user && ! readOnly ) ;
22- const {
23- isScratchSaving,
24- saveScratchProject,
25- scratchSaveLabelKey,
26- shouldRemixOnSave,
27- } = useScratchSave ( {
28- enabled : showScratchSaveButton ,
30+ const saving = useSelector ( ( state ) => state . editor . saving ) ;
31+ const lastSavedTime = useSelector ( ( state ) => state . editor . lastSavedTime ) ;
32+ const canSave = Boolean ( user && ! readOnly ) ;
33+ const { saveScratchProject, shouldRemixOnSave } = useScratchSave ( {
34+ enabled : canSave ,
2935 } ) ;
30- const scratchSaveLabel = t ( scratchSaveLabelKey ) ;
36+
37+ const projectIdentifier = project ?. identifier ;
38+ const isScratchSaving = saving === "pending" ;
39+ const isScratchSaveFailed = saving === "failed" ;
40+ const isNewProject = ! projectIdentifier ;
41+ const canAutoSave = Boolean ( projectIdentifier && ! shouldRemixOnSave ) ;
42+ const showSaveButton = canSave && ( isNewProject || shouldRemixOnSave ) ;
43+ const showSaveStatus =
44+ canSave && canAutoSave && Boolean ( lastSavedTime ) && ! isScratchSaveFailed ;
45+ const projectLastSavedTime = getProjectLastSavedTime ( project ?. updated_at ) ;
46+
47+ useEffect ( ( ) => {
48+ if ( ! canSave || ! canAutoSave || lastSavedTime || ! projectLastSavedTime ) {
49+ return ;
50+ }
51+
52+ dispatch (
53+ setScratchLastSavedTime ( {
54+ lastSavedTime : projectLastSavedTime ,
55+ } ) ,
56+ ) ;
57+ } , [ dispatch , canAutoSave , canSave , lastSavedTime , projectLastSavedTime ] ) ;
3158
3259 if ( loading !== "success" ) {
3360 return null ;
@@ -55,19 +82,20 @@ const ScratchProjectBar = ({ nameEditable = true }) => {
5582 type = "tertiary"
5683 />
5784 </ div >
58- { showScratchSaveButton && (
85+ { showSaveButton && (
5986 < div className = "project-bar__btn-wrapper" >
6087 < DesignSystemButton
6188 className = "project-bar__btn btn--save btn--primary"
6289 onClick = { ( ) => saveScratchProject ( { shouldRemixOnSave } ) }
63- text = { scratchSaveLabel }
90+ text = { t ( "header.save" ) }
6491 textAlways
6592 icon = { < SaveIcon /> }
6693 type = "primary"
6794 disabled = { isScratchSaving }
6895 />
6996 </ div >
7097 ) }
98+ { showSaveStatus && < SaveStatus /> }
7199 </ div >
72100 </ div >
73101 ) ;
0 commit comments