actionExport.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import React from "react";
  2. import { EVENT_CHANGE, EVENT_IO, trackEvent } from "../analytics";
  3. import { load, save, saveAs } from "../components/icons";
  4. import { ProjectName } from "../components/ProjectName";
  5. import { ToolButton } from "../components/ToolButton";
  6. import { loadFromJSON, saveAsJSON } from "../data";
  7. import { t } from "../i18n";
  8. import useIsMobile from "../is-mobile";
  9. import { KEYS } from "../keys";
  10. import { muteFSAbortError } from "../utils";
  11. import { register } from "./register";
  12. export const actionChangeProjectName = register({
  13. name: "changeProjectName",
  14. perform: (_elements, appState, value) => {
  15. trackEvent(EVENT_CHANGE, "title");
  16. return { appState: { ...appState, name: value }, commitToHistory: false };
  17. },
  18. PanelComponent: ({ appState, updateData }) => (
  19. <ProjectName
  20. label={t("labels.fileTitle")}
  21. value={appState.name || "Unnamed"}
  22. onChange={(name: string) => updateData(name)}
  23. />
  24. ),
  25. });
  26. export const actionChangeExportBackground = register({
  27. name: "changeExportBackground",
  28. perform: (_elements, appState, value) => {
  29. return {
  30. appState: { ...appState, exportBackground: value },
  31. commitToHistory: false,
  32. };
  33. },
  34. PanelComponent: ({ appState, updateData }) => (
  35. <label>
  36. <input
  37. type="checkbox"
  38. checked={appState.exportBackground}
  39. onChange={(event) => updateData(event.target.checked)}
  40. />{" "}
  41. {t("labels.withBackground")}
  42. </label>
  43. ),
  44. });
  45. export const actionChangeExportEmbedScene = register({
  46. name: "changeExportEmbedScene",
  47. perform: (_elements, appState, value) => {
  48. return {
  49. appState: { ...appState, exportEmbedScene: value },
  50. commitToHistory: false,
  51. };
  52. },
  53. PanelComponent: ({ appState, updateData }) => (
  54. <label title={t("labels.exportEmbedScene_details")}>
  55. <input
  56. type="checkbox"
  57. checked={appState.exportEmbedScene}
  58. onChange={(event) => updateData(event.target.checked)}
  59. />{" "}
  60. {t("labels.exportEmbedScene")}
  61. </label>
  62. ),
  63. });
  64. export const actionChangeShouldAddWatermark = register({
  65. name: "changeShouldAddWatermark",
  66. perform: (_elements, appState, value) => {
  67. return {
  68. appState: { ...appState, shouldAddWatermark: value },
  69. commitToHistory: false,
  70. };
  71. },
  72. PanelComponent: ({ appState, updateData }) => (
  73. <label>
  74. <input
  75. type="checkbox"
  76. checked={appState.shouldAddWatermark}
  77. onChange={(event) => updateData(event.target.checked)}
  78. />{" "}
  79. {t("labels.addWatermark")}
  80. </label>
  81. ),
  82. });
  83. export const actionSaveScene = register({
  84. name: "saveScene",
  85. perform: async (elements, appState, value) => {
  86. try {
  87. const { fileHandle } = await saveAsJSON(elements, appState);
  88. trackEvent(EVENT_IO, "save");
  89. return { commitToHistory: false, appState: { ...appState, fileHandle } };
  90. } catch (error) {
  91. if (error?.name !== "AbortError") {
  92. console.error(error);
  93. }
  94. return { commitToHistory: false };
  95. }
  96. },
  97. keyTest: (event) =>
  98. event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
  99. PanelComponent: ({ updateData }) => (
  100. <ToolButton
  101. type="button"
  102. icon={save}
  103. title={t("buttons.save")}
  104. aria-label={t("buttons.save")}
  105. showAriaLabel={useIsMobile()}
  106. onClick={() => updateData(null)}
  107. />
  108. ),
  109. });
  110. export const actionSaveAsScene = register({
  111. name: "saveAsScene",
  112. perform: async (elements, appState, value) => {
  113. try {
  114. const { fileHandle } = await saveAsJSON(elements, {
  115. ...appState,
  116. fileHandle: null,
  117. });
  118. trackEvent(EVENT_IO, "save as");
  119. return { commitToHistory: false, appState: { ...appState, fileHandle } };
  120. } catch (error) {
  121. if (error?.name !== "AbortError") {
  122. console.error(error);
  123. }
  124. return { commitToHistory: false };
  125. }
  126. },
  127. keyTest: (event) =>
  128. event.key === KEYS.S && event.shiftKey && event[KEYS.CTRL_OR_CMD],
  129. PanelComponent: ({ updateData }) => (
  130. <ToolButton
  131. type="button"
  132. icon={saveAs}
  133. title={t("buttons.saveAs")}
  134. aria-label={t("buttons.saveAs")}
  135. showAriaLabel={useIsMobile()}
  136. hidden={
  137. !("chooseFileSystemEntries" in window || "showOpenFilePicker" in window)
  138. }
  139. onClick={() => updateData(null)}
  140. />
  141. ),
  142. });
  143. export const actionLoadScene = register({
  144. name: "loadScene",
  145. perform: (
  146. elements,
  147. appState,
  148. { elements: loadedElements, appState: loadedAppState, error },
  149. ) => ({
  150. elements: loadedElements,
  151. appState: {
  152. ...loadedAppState,
  153. errorMessage: error,
  154. },
  155. commitToHistory: true,
  156. }),
  157. PanelComponent: ({ updateData, appState }) => (
  158. <ToolButton
  159. type="button"
  160. icon={load}
  161. title={t("buttons.load")}
  162. aria-label={t("buttons.load")}
  163. showAriaLabel={useIsMobile()}
  164. onClick={() => {
  165. loadFromJSON(appState)
  166. .then(({ elements, appState }) => {
  167. updateData({ elements, appState });
  168. })
  169. .catch(muteFSAbortError)
  170. .catch((error) => {
  171. updateData({ error: error.message });
  172. });
  173. }}
  174. />
  175. ),
  176. });