diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index e38dd7a94..2e750defa 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -45,6 +45,7 @@ import { ResolvablePromise, resolvablePromise, isRunningInIframe, + getDateTime, } from "../packages/excalidraw/utils"; import { FIREBASE_STORAGE_PREFIXES, @@ -775,6 +776,7 @@ const ExcalidrawWrapper = () => { excalidrawAPI.getSceneElements(), excalidrawAPI.getAppState(), excalidrawAPI.getFiles(), + `${t("labels.untitled")}-${getDateTime()}`, ); }} > diff --git a/excalidraw-app/components/ExportToExcalidrawPlus.tsx b/excalidraw-app/components/ExportToExcalidrawPlus.tsx index 4c566950b..5127ac7f4 100644 --- a/excalidraw-app/components/ExportToExcalidrawPlus.tsx +++ b/excalidraw-app/components/ExportToExcalidrawPlus.tsx @@ -23,13 +23,14 @@ import { FILE_UPLOAD_MAX_BYTES } from "../app_constants"; import { encodeFilesForUpload } from "../data/FileManager"; import { MIME_TYPES } from "../../packages/excalidraw/constants"; import { trackEvent } from "../../packages/excalidraw/analytics"; -import { getFrame } from "../../packages/excalidraw/utils"; +import { getDateTime, getFrame } from "../../packages/excalidraw/utils"; import { ExcalidrawLogo } from "../../packages/excalidraw/components/ExcalidrawLogo"; export const exportToExcalidrawPlus = async ( elements: readonly NonDeletedExcalidrawElement[], appState: Partial, files: BinaryFiles, + name: string, ) => { const firebase = await loadFirebaseStorage(); @@ -53,7 +54,7 @@ export const exportToExcalidrawPlus = async ( .ref(`/migrations/scenes/${id}`) .put(blob, { customMetadata: { - data: JSON.stringify({ version: 2, name: appState.name }), + data: JSON.stringify({ version: 2, name }), created: Date.now().toString(), }, }); @@ -117,7 +118,12 @@ export const ExportToExcalidrawPlus: React.FC<{ onClick={async () => { try { trackEvent("export", "eplus", `ui (${getFrame()})`); - await exportToExcalidrawPlus(elements, appState, files); + await exportToExcalidrawPlus( + elements, + appState, + files, + `${t("labels.untitled")}-${getDateTime()}`, + ); onSuccess(); } catch (error: any) { console.error(error); diff --git a/packages/excalidraw/actions/actionClipboard.tsx b/packages/excalidraw/actions/actionClipboard.tsx index dadc61013..2ed936e65 100644 --- a/packages/excalidraw/actions/actionClipboard.tsx +++ b/packages/excalidraw/actions/actionClipboard.tsx @@ -13,6 +13,7 @@ import { exportCanvas, prepareElementsForExport } from "../data/index"; import { isTextElement } from "../element"; import { t } from "../i18n"; import { isFirefox } from "../constants"; +import { getDateTime } from "../utils"; export const actionCopy = register({ name: "copy", @@ -138,6 +139,7 @@ export const actionCopyAsSvg = register({ { ...appState, exportingFrame, + name: app.props.name || `${t("labels.untitled")}-${getDateTime()}`, }, ); return { @@ -184,6 +186,7 @@ export const actionCopyAsPng = register({ await exportCanvas("clipboard", exportedElements, appState, app.files, { ...appState, exportingFrame, + name: app.props.name || `${t("labels.untitled")}-${getDateTime()}`, }); return { appState: { diff --git a/packages/excalidraw/actions/actionExport.tsx b/packages/excalidraw/actions/actionExport.tsx index 74dff34c8..7b07c1f73 100644 --- a/packages/excalidraw/actions/actionExport.tsx +++ b/packages/excalidraw/actions/actionExport.tsx @@ -19,6 +19,7 @@ import { nativeFileSystemSupported } from "../data/filesystem"; import { Theme } from "../element/types"; import "../components/ToolIcon.scss"; +import { getDateTime } from "../utils"; export const actionChangeProjectName = register({ name: "changeProjectName", @@ -29,7 +30,7 @@ export const actionChangeProjectName = register({ PanelComponent: ({ appState, updateData, appProps, data }) => ( updateData(name)} isNameEditable={ typeof appProps.name === "undefined" && !appState.viewModeEnabled @@ -144,8 +145,18 @@ export const actionSaveToActiveFile = register({ try { const { fileHandle } = isImageFileHandle(appState.fileHandle) - ? await resaveAsImageWithScene(elements, appState, app.files) - : await saveAsJSON(elements, appState, app.files); + ? await resaveAsImageWithScene( + elements, + appState, + app.files, + app.props.name || `${t("labels.untitled")}-${getDateTime()}`, + ) + : await saveAsJSON( + elements, + appState, + app.files, + app.props.name || `${t("labels.untitled")}-${getDateTime()}`, + ); return { commitToHistory: false, @@ -190,6 +201,7 @@ export const actionSaveFileToDisk = register({ fileHandle: null, }, app.files, + app.props.name || `${t("labels.untitled")}-${getDateTime()}`, ); return { commitToHistory: false, diff --git a/packages/excalidraw/appState.ts b/packages/excalidraw/appState.ts index 4dec9a790..1e90a36ae 100644 --- a/packages/excalidraw/appState.ts +++ b/packages/excalidraw/appState.ts @@ -65,7 +65,6 @@ export const getDefaultAppState = (): Omit< isRotating: false, lastPointerDownWith: "mouse", multiElement: null, - name: `${t("labels.untitled")}-${getDateTime()}`, contextMenu: null, openMenu: null, openPopup: null, @@ -175,7 +174,6 @@ const APP_STATE_STORAGE_CONF = (< isRotating: { browser: false, export: false, server: false }, lastPointerDownWith: { browser: true, export: false, server: false }, multiElement: { browser: false, export: false, server: false }, - name: { browser: true, export: false, server: false }, offsetLeft: { browser: false, export: false, server: false }, offsetTop: { browser: false, export: false, server: false }, contextMenu: { browser: false, export: false, server: false }, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index f965a7679..e99366239 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -270,6 +270,7 @@ import { updateStable, addEventListener, normalizeEOL, + getDateTime, } from "../utils"; import { createSrcDoc, @@ -619,7 +620,6 @@ class App extends React.Component { gridModeEnabled = false, objectsSnapModeEnabled = false, theme = defaultAppState.theme, - name = defaultAppState.name, } = props; this.state = { ...defaultAppState, @@ -630,7 +630,6 @@ class App extends React.Component { zenModeEnabled, objectsSnapModeEnabled, gridSize: gridModeEnabled ? GRID_SIZE : null, - name, width: window.innerWidth, height: window.innerHeight, }; @@ -1725,7 +1724,7 @@ class App extends React.Component { this.files, { exportBackground: this.state.exportBackground, - name: this.state.name, + name: this.props?.name || `${t("labels.untitled")}-${getDateTime()}`, viewBackgroundColor: this.state.viewBackgroundColor, exportingFrame: opts.exportingFrame, }, @@ -2124,7 +2123,7 @@ class App extends React.Component { let gridSize = actionResult?.appState?.gridSize || null; const theme = actionResult?.appState?.theme || this.props.theme || THEME.LIGHT; - let name = actionResult?.appState?.name ?? this.state.name; + const errorMessage = actionResult?.appState?.errorMessage ?? this.state.errorMessage; if (typeof this.props.viewModeEnabled !== "undefined") { @@ -2139,10 +2138,6 @@ class App extends React.Component { gridSize = this.props.gridModeEnabled ? GRID_SIZE : null; } - if (typeof this.props.name !== "undefined") { - name = this.props.name; - } - editingElement = editingElement || actionResult.appState?.editingElement || null; @@ -2165,7 +2160,6 @@ class App extends React.Component { zenModeEnabled, gridSize, theme, - name, errorMessage, }); }, @@ -2699,12 +2693,6 @@ class App extends React.Component { }); } - if (this.props.name && prevProps.name !== this.props.name) { - this.setState({ - name: this.props.name, - }); - } - this.excalidrawContainerRef.current?.classList.toggle( "theme--dark", this.state.theme === "dark", diff --git a/packages/excalidraw/components/ImageExportDialog.tsx b/packages/excalidraw/components/ImageExportDialog.tsx index 7ca54e985..533adf648 100644 --- a/packages/excalidraw/components/ImageExportDialog.tsx +++ b/packages/excalidraw/components/ImageExportDialog.tsx @@ -34,7 +34,7 @@ import { Tooltip } from "./Tooltip"; import "./ImageExportDialog.scss"; import { useAppProps } from "./App"; import { FilledButton } from "./FilledButton"; -import { cloneJSON } from "../utils"; +import { cloneJSON, getDateTime } from "../utils"; import { prepareElementsForExport } from "../data"; const supportsContextFilters = @@ -73,7 +73,9 @@ const ImageExportModal = ({ ); const appProps = useAppProps(); - const [projectName, setProjectName] = useState(appStateSnapshot.name); + const [projectName, setProjectName] = useState( + appProps.name || `${t("labels.untitled")}-${getDateTime()}`, + ); const [exportSelectionOnly, setExportSelectionOnly] = useState(hasSelection); const [exportWithBackground, setExportWithBackground] = useState( appStateSnapshot.exportBackground, @@ -109,7 +111,6 @@ const ImageExportModal = ({ elements: exportedElements, appState: { ...appStateSnapshot, - name: projectName, exportBackground: exportWithBackground, exportWithDarkMode: exportDarkMode, exportScale, diff --git a/packages/excalidraw/components/ProjectName.tsx b/packages/excalidraw/components/ProjectName.tsx index 69ff33527..8d50630a8 100644 --- a/packages/excalidraw/components/ProjectName.tsx +++ b/packages/excalidraw/components/ProjectName.tsx @@ -1,14 +1,15 @@ import "./TextInput.scss"; import React, { useState } from "react"; -import { focusNearestParent } from "../utils"; +import { focusNearestParent, getDateTime } from "../utils"; import "./ProjectName.scss"; import { useExcalidrawContainer } from "./App"; import { KEYS } from "../keys"; +import { t } from "../i18n"; type Props = { - value: string; + value?: string; onChange: (value: string) => void; label: string; isNameEditable: boolean; @@ -17,7 +18,9 @@ type Props = { export const ProjectName = (props: Props) => { const { id } = useExcalidrawContainer(); - const [fileName, setFileName] = useState(props.value); + const [fileName, setFileName] = useState( + props.value || `${t("labels.untitled")}-${getDateTime()}`, + ); const handleBlur = (event: any) => { if (!props.ignoreFocus) { diff --git a/packages/excalidraw/data/json.ts b/packages/excalidraw/data/json.ts index 037c5ca18..174011d66 100644 --- a/packages/excalidraw/data/json.ts +++ b/packages/excalidraw/data/json.ts @@ -71,6 +71,7 @@ export const saveAsJSON = async ( elements: readonly ExcalidrawElement[], appState: AppState, files: BinaryFiles, + name: string, ) => { const serialized = serializeAsJSON(elements, appState, files, "local"); const blob = new Blob([serialized], { @@ -78,7 +79,7 @@ export const saveAsJSON = async ( }); const fileHandle = await fileSave(blob, { - name: appState.name, + name, extension: "excalidraw", description: "Excalidraw file", fileHandle: isImageFileHandle(appState.fileHandle) diff --git a/packages/excalidraw/data/resave.ts b/packages/excalidraw/data/resave.ts index 0998fd3c7..c73890e22 100644 --- a/packages/excalidraw/data/resave.ts +++ b/packages/excalidraw/data/resave.ts @@ -7,8 +7,9 @@ export const resaveAsImageWithScene = async ( elements: readonly ExcalidrawElement[], appState: AppState, files: BinaryFiles, + name: string, ) => { - const { exportBackground, viewBackgroundColor, name, fileHandle } = appState; + const { exportBackground, viewBackgroundColor, fileHandle } = appState; const fileHandleType = getFileHandleType(fileHandle); diff --git a/packages/excalidraw/history.ts b/packages/excalidraw/history.ts index d102a7ecc..2704f1392 100644 --- a/packages/excalidraw/history.ts +++ b/packages/excalidraw/history.ts @@ -26,7 +26,6 @@ const clearAppStatePropertiesForHistory = (appState: AppState) => { viewBackgroundColor: appState.viewBackgroundColor, editingLinearElement: appState.editingLinearElement, editingGroupId: appState.editingGroupId, - name: appState.name, }; }; diff --git a/packages/excalidraw/types.ts b/packages/excalidraw/types.ts index 89b121b2f..c752916b2 100644 --- a/packages/excalidraw/types.ts +++ b/packages/excalidraw/types.ts @@ -247,7 +247,6 @@ export interface AppState { scrollY: number; cursorButton: "up" | "down"; scrolledOutside: boolean; - name: string; isResizing: boolean; isRotating: boolean; zoom: Zoom;