From eb94e21b9fc42102089dfde1aa9217d428400de6 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 11 Nov 2024 12:21:44 +0200 Subject: [PATCH 01/32] chore: publish button --- static/js/NavSidebar.jsx | 1 - static/js/sheets/SheetOptions.jsx | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index d2f8e2d92e..6a04201018 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -671,7 +671,6 @@ const GetStartedButton = () => { return ; } const CreateSheetsButton = () => { - // #sheetsButton return } const CreateASheet = () => ( diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index ad29ac4444..3b9fc76a30 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -8,6 +8,12 @@ import $ from "../sefaria/sefariaJquery"; import {SignUpModalKind} from "../sefaria/signupModalContent"; import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; +import Button from "../shared/Button"; + +const PublishButton = () => { + return +} + const modifyHistoryObjectForSheetOptions = (historyObject) => { // we want the 'ref' property to be for the sheet itself and not its segments, as in "Sheet 3" not "Sheet 3:4" let newHistoryObject = Object.assign({}, historyObject); @@ -71,6 +77,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable}) => return setDeleting(false)} sheetID={sheetID}/>; } return ( + <> } + ); } From 8e7372e5e7a80648ae4f48a6e7358b4c83d0ece3 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 11 Nov 2024 13:10:56 +0200 Subject: [PATCH 02/32] chore: start implementing publish button --- static/js/sheets/SheetOptions.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 3b9fc76a30..128fd95abb 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -10,8 +10,18 @@ import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; import Button from "../shared/Button"; +const togglePublish = async (sheetID, shouldPublish) => { + const newPublishState = shouldPublish ? "unlisted" : "public"; + let updatedSheet = await (new Promise((resolve, reject) => Sefaria.sheets.loadSheetByID(sheetID, sheet => resolve(sheet)))); + updatedSheet.status = newPublishState; + updatedSheet.lastModified = lastModified; + delete updatedSheet._id; + const postJSON = JSON.stringify(updatedSheet); + postSheet(postJSON); +} + const PublishButton = () => { - return + return } const modifyHistoryObjectForSheetOptions = (historyObject) => { From c589db15a6bb5f63ce16262e53ffde7f501aa57c Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 11 Nov 2024 16:09:54 +0200 Subject: [PATCH 03/32] chore: publish button and unpublish button when appropriate --- static/icons/unpublish.svg | 3 +++ static/js/Sheet.jsx | 2 +- static/js/sheets/SheetOptions.jsx | 34 +++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 static/icons/unpublish.svg diff --git a/static/icons/unpublish.svg b/static/icons/unpublish.svg new file mode 100644 index 0000000000..ce5e0c49de --- /dev/null +++ b/static/icons/unpublish.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/js/Sheet.jsx b/static/js/Sheet.jsx index 822bb3c45b..242b2a6554 100644 --- a/static/js/Sheet.jsx +++ b/static/js/Sheet.jsx @@ -100,7 +100,7 @@ class Sheet extends Component { } else { const sheetOptions = ; diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 6669101f1f..817ad7c268 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -21,7 +21,7 @@ const togglePublish = async (sheetID, shouldPublish) => { } const PublishButton = () => { - return + return } const modifyHistoryObjectForSheetOptions = (historyObject) => { @@ -36,7 +36,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, authorUrl}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, authorUrl}) => { // `editable` -- whether the sheet belongs to the current user const [isSharing, setSharing] = useState(false); // Share Modal open or closed const [isCollectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -44,6 +44,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, auth const [isSaving, setSaving] = useState(false); const [isExporting, setExporting] = useState(getExportingStatus()); const [isDeleting, setDeleting] = useState(false); + const [isPublished, setIsPublished] = useState(sheet.status === "public"); const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { if (isSaving) { @@ -69,25 +70,26 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, auth } }, [isCollectionsMode, isSaving, isCopying, isExporting]); if (isSharing) { - return setSharing(false)}/>; + return setSharing(false)}/>; } else if (isCollectionsMode) { - return setCollectionsMode(false)} sheetID={sheetID}/>; + return setCollectionsMode(false)} sheetID={sheet.id}/>; } else if (isCopying) { - return setCopying(false)} sheetID={sheetID}/>; + return setCopying(false)} sheetID={sheet.id}/>; } else if (isSaving) { return setSaving(false)}/>; } else if (isExporting) { - return setExporting(false)} sheetID={sheetID}/>; + return setExporting(false)} sheetID={sheet.id}/>; } else if (isDeleting) { - return setDeleting(false)} sheetID={sheetID} authorUrl={authorUrl}/>; + return setDeleting(false)} sheetID={sheet.id} authorUrl={authorUrl}/>; } return ( <> + {editable && !isPublished && } - setExporting(true)}/> + setExporting(true)}/> setSharing(true)}/> + {editable && isPublished && <> + + + togglePublish(sheet.id, false)}/> + + + } {editable && <> @@ -142,6 +151,15 @@ const DeleteButton = ({onClick}) => { onClick={handleClick}/> } +const UnpublishButton = ({onClick}) => { + return +} + const CollectionsButton = ({setCollectionsMode, editable}) => { const label = editable ? "Edit Collections" : "Add to Collection"; return Date: Mon, 11 Nov 2024 16:22:10 +0200 Subject: [PATCH 04/32] chore: publish button css --- static/css/s2.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 354b30771b..4562aab420 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -8070,7 +8070,14 @@ a .button:hover { width: -moz-fit-content; width: fit-content; } - +.button.small.publish { + color: #666666; + background-color: white; + font-weight: 600; + min-height: 31px; + height: 31px; + padding: 0 10px; +} .headerWithAdminButtonsContainer{ display: flex; justify-content: space-between; @@ -8495,6 +8502,7 @@ a.sheetAuthorName:hover { flex: 1 0 95%; text-align: initial; } + .sheetMetaDataBox .title:empty:before { content: "Title"; color: #999; From 77875871c63e12a0d870cc314e99fbf5ef391209 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 12 Nov 2024 10:11:20 +0200 Subject: [PATCH 05/32] chore: start PublishModal --- static/js/sheets/SheetOptions.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 817ad7c268..5125ab7fe3 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -44,7 +44,8 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, author const [isSaving, setSaving] = useState(false); const [isExporting, setExporting] = useState(getExportingStatus()); const [isDeleting, setDeleting] = useState(false); - const [isPublished, setIsPublished] = useState(sheet.status === "public"); + const [isPublishing, setIsPublishing] = useState(false); + const [sheetIsPublished, setSheetIsPublished] = useState(sheet.status === "public"); const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { if (isSaving) { @@ -87,9 +88,12 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, author else if (isDeleting) { return setDeleting(false)} sheetID={sheet.id} authorUrl={authorUrl}/>; } + else if (isPublishing) { + return setIsPublishing(false)} sheet={sheet}/>; + } return ( <> - {editable && !isPublished && } + {editable && !sheetIsPublished && } setSharing(true)}/> - {editable && isPublished && <> + {editable && sheetIsPublished && <> togglePublish(sheet.id, false)}/> From a7a1772c071265bf77d502113ccd572dff9b063d Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 12 Nov 2024 15:18:49 +0200 Subject: [PATCH 06/32] chore: togglePublish not working due to lastModified dateModified issue --- static/js/Editor.jsx | 2 +- static/js/Sheet.jsx | 2 +- static/js/sheets/SheetOptions.jsx | 57 +++++++++++++++++++------------ 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index c18627861e..ee0beecd51 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2547,7 +2547,7 @@ const BlockButton = ({format, icon}) => { } const SefariaEditor = (props) => { - const editorContainer = useRef(); + const editorContainer = useRef(null); const [sheet, setSheet] = useState(props.data); const initValue = [{type: "sheet", children: [{text: ""}]}]; const renderElement = useCallback(props => , []); diff --git a/static/js/Sheet.jsx b/static/js/Sheet.jsx index 242b2a6554..822bb3c45b 100644 --- a/static/js/Sheet.jsx +++ b/static/js/Sheet.jsx @@ -100,7 +100,7 @@ class Sheet extends Component { } else { const sheetOptions = ; diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 1ca72cbf9a..bc0b6f46fc 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -1,6 +1,6 @@ import React, {useEffect, useState} from "react"; import {DropdownMenu, DropdownMenuItem, DropdownMenuItemWithIcon, DropdownMenuSeparator} from "../common/DropdownMenu"; -import {InterfaceText, SaveButtonWithText} from "../Misc"; +import {InterfaceText, SaveButtonWithText, TitleVariants} from "../Misc"; import Modal from "../shared/modal"; import {ShareBox} from "../ConnectionsPanel"; import Sefaria from "../sefaria/sefaria"; @@ -10,18 +10,19 @@ import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; import Button from "../shared/Button"; -const togglePublish = async (sheetID, shouldPublish) => { - const newPublishState = shouldPublish ? "unlisted" : "public"; - let updatedSheet = await (new Promise((resolve, reject) => Sefaria.sheets.loadSheetByID(sheetID, sheet => resolve(sheet)))); - updatedSheet.status = newPublishState; - updatedSheet.lastModified = lastModified; - delete updatedSheet._id; - const postJSON = JSON.stringify(updatedSheet); - postSheet(postJSON); +const togglePublish = async (sheet, shouldPublish) => { + sheet.status = shouldPublish ? "public" : "unlisted"; + sheet.lastModified = sheet.dateModified; + delete sheet._id; + Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { + if (data.id) { + Sefaria.sheets._loadSheetByID[data.id] = data; + } + }) } -const PublishButton = () => { - return +const PublishButton = ({onClick}) => { + return } const modifyHistoryObjectForSheetOptions = (historyObject) => { @@ -36,7 +37,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, authorUrl}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, authorUrl}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -45,6 +46,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, author const [exportingMode, setExportingMode] = useState(getExportingStatus()); const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); + let sheet = Sefaria.sheets.loadSheetByID(sheetID); const [sheetIsPublished, setSheetIsPublished] = useState(sheet.status === "public"); const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { @@ -71,29 +73,29 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, author } }, [collectionsMode, savingMode, copyingMode, exportingMode]); if (sharingMode) { - return setSharingMode(false)}/>; + return setSharingMode(false)}/>; } else if (collectionsMode) { - return setCollectionsMode(false)} sheetID={sheet.id}/>; + return setCollectionsMode(false)} sheetID={sheetID}/>; } else if (copyingMode) { - return setCopyingMode(false)} sheetID={sheet.id}/>; + return setCopyingMode(false)} sheetID={sheetID}/>; } else if (savingMode) { return setSavingMode(false)}/>; } else if (exportingMode) { - return setExportingMode(false)} sheetID={sheet.id}/>; + return setExportingMode(false)} sheetID={sheetID}/>; } else if (deletingMode) { - return setDeletingMode(false)} sheetID={sheet.id} authorUrl={authorUrl}/>; + return setDeletingMode(false)} sheetID={sheetID} authorUrl={authorUrl}/>; } else if (publishingMode) { return setPublishingMode(false)} sheet={sheet}/>; } return ( <> - {editable && !sheetIsPublished && } + {editable && !sheetIsPublished && togglePublish(sheet,true)}/>} - setExportingMode(true)}/> + setExportingMode(true)}/> setSharingMode(true)}/> @@ -116,7 +118,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, editable, author {editable && sheetIsPublished && <> - togglePublish(sheet.id, false)}/> + togglePublish(sheetID, false)}/> } @@ -282,6 +284,19 @@ const GenericSheetModal = ({title, message, close}) => { ; } +const PublishModal = ({sheet, close}) => { + const [topics, setTopics] = useState(sheet.map((item, i) =>({["name"]: item, ["id"]: i}))); + return +
Publish
+ + + + + + +
; +} + const SaveModal = ({historyObject, close}) => { const isSaved = !!Sefaria.getSavedItem(historyObject); const savingMessage = "Saving..."; @@ -324,7 +339,7 @@ const GoogleDocExportModal = ({ sheetID, close }) => { history.replaceState("", document.title, window.location.pathname + window.location.search); // remove exportToDrive hash once it's used to trigger export $.ajax({ type: "POST", - url: "/api/sheets/" + sheet.id + "/export_to_drive", + url: "/api/sheets/" + sheetID + "/export_to_drive", success: function (data) { if ("error" in data) { console.log(data.error.message); From a0a5b0dc5461956bb29047b43df876027aca944a Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 13 Nov 2024 11:55:48 +0200 Subject: [PATCH 07/32] chore: lastModified passed to SheetOptions, dont pass SheetOptions down from Sheet --- static/js/Editor.jsx | 21 +++++++++------ static/js/Sheet.jsx | 44 ++++++++++++------------------- static/js/sheets/SheetContent.jsx | 9 +++++-- static/js/sheets/SheetOptions.jsx | 4 +-- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index ee0beecd51..fad078dea1 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -10,9 +10,7 @@ import * as sheetsUtils from './sefaria/sheetsUtils' import { SheetMetaDataBox, - SheetAuthorStatement, SheetTitle, - CollectionStatement, ProfilePic, InterfaceText, Autocompleter, @@ -21,6 +19,7 @@ import { import classNames from 'classnames'; import $ from "./sefaria/sefariaJquery"; import ReactDOM from "react-dom"; +import {SheetOptions} from "./sheets/SheetOptions"; // Mapping from Sheet doc format source types to Slate block element types const sheet_item_els = { @@ -2558,6 +2557,12 @@ const SefariaEditor = (props) => { const [canUseDOM, setCanUseDOM] = useState(false); const [lastSelection, setLastSelection] = useState(null) const [readyForNormalize, setReadyForNormalize] = useState(false); + const sheetOptions = ; useEffect( () => { @@ -2949,14 +2954,14 @@ const SefariaEditor = (props) => { return (
- saveDocument(currentDocument)} - sheetOptions={props.sheetOptions}/> + sheetOptions={sheetOptions}/> { /* debugger */ diff --git a/static/js/Sheet.jsx b/static/js/Sheet.jsx index 822bb3c45b..68796426ab 100644 --- a/static/js/Sheet.jsx +++ b/static/js/Sheet.jsx @@ -99,11 +99,6 @@ class Sheet extends Component { editor = (); } else { - const sheetOptions = ; const sidebar =
{sidebar} @@ -135,7 +127,6 @@ class Sheet extends Component { content = (
{sidebar}
diff --git a/static/js/sheets/SheetContent.jsx b/static/js/sheets/SheetContent.jsx index 1cf9875ffa..20214d77b0 100644 --- a/static/js/sheets/SheetContent.jsx +++ b/static/js/sheets/SheetContent.jsx @@ -2,7 +2,7 @@ import Component from "react-class"; import $ from "../sefaria/sefariaJquery"; import ReactDOM from "react-dom"; import Sefaria from "../sefaria/sefaria"; -import {AddToSourceSheetModal} from "./SheetOptions"; +import {AddToSourceSheetModal, SheetOptions} from "./SheetOptions"; import { SheetComment, SheetHeader, @@ -231,13 +231,18 @@ class SheetContent extends Component { } render() { const sources = this.getSources(); + const sheetOptions = ; return (
+ sheetOptions={sheetOptions}/>
{sources}
diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index bc0b6f46fc..b9ea20f847 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -37,7 +37,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, authorUrl}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, authorUrl, lastModified}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -95,7 +95,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, auth } return ( <> - {editable && !sheetIsPublished && togglePublish(sheet,true)}/>} + {editable && !sheetIsPublished && togglePublish(sheet,true, lastModified)}/>} Date: Wed, 13 Nov 2024 13:09:23 +0200 Subject: [PATCH 08/32] chore: publish button does not open modal but does publish --- static/js/Editor.jsx | 11 ++++++----- static/js/Sheet.jsx | 3 +-- static/js/sheets/SheetOptions.jsx | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index fad078dea1..c8d23a38e1 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2558,11 +2558,12 @@ const SefariaEditor = (props) => { const [lastSelection, setLastSelection] = useState(null) const [readyForNormalize, setReadyForNormalize] = useState(false); const sheetOptions = ; + sheetID={props.data.id} + historyObject={props.historyObject} + editable={props.editable} + authorUrl={props.data.ownerProfileUrl} + lastModified={lastModified} + sheet={sheet}/>; useEffect( () => { diff --git a/static/js/Sheet.jsx b/static/js/Sheet.jsx index 68796426ab..c2a735b771 100644 --- a/static/js/Sheet.jsx +++ b/static/js/Sheet.jsx @@ -9,9 +9,8 @@ import Sefaria from './sefaria/sefaria'; import SefariaEditor from './Editor'; import { InterfaceText, - LoadingMessage, SheetMetaDataBox, + LoadingMessage, } from './Misc'; -import {SheetOptions} from "./sheets/SheetOptions"; import SheetContentSidebar from "./sheets/SheetContentSidebar"; import SheetContent from "./sheets/SheetContent"; diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index b9ea20f847..25a79c508e 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -10,9 +10,10 @@ import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; import Button from "../shared/Button"; -const togglePublish = async (sheet, shouldPublish) => { +const togglePublish = async (sheet, shouldPublish, lastModified) => { sheet.status = shouldPublish ? "public" : "unlisted"; - sheet.lastModified = sheet.dateModified; + sheet.lastModified = lastModified; + console.log(lastModified, sheet.dateModified); delete sheet._id; Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { if (data.id) { @@ -37,7 +38,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, authorUrl, lastModified}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorUrl, editable, lastModified}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -46,8 +47,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, editable, auth const [exportingMode, setExportingMode] = useState(getExportingStatus()); const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); - let sheet = Sefaria.sheets.loadSheetByID(sheetID); - const [sheetIsPublished, setSheetIsPublished] = useState(sheet.status === "public"); + const [sheetIsPublished, setSheetIsPublished] = useState(sheet?.status === "public"); const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { if (savingMode) { From d42812b6d6fd215400db346095c43cbe95749780 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 13 Nov 2024 17:59:54 +0200 Subject: [PATCH 09/32] chore: sheetIsPublished doesnt need to be state --- static/js/sheets/SheetOptions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 25a79c508e..c502406f47 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -47,7 +47,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorU const [exportingMode, setExportingMode] = useState(getExportingStatus()); const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); - const [sheetIsPublished, setSheetIsPublished] = useState(sheet?.status === "public"); + const sheetIsPublished = sheet?.status === "public"; const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { if (savingMode) { From 7140f9612b59d7a39c9a68cbe1c9ee63503ab280 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Thu, 14 Nov 2024 12:09:09 +0200 Subject: [PATCH 10/32] chore: start publishmodal --- static/js/sheets/SheetOptions.jsx | 46 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index c502406f47..8f9b79c5e1 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -22,10 +22,6 @@ const togglePublish = async (sheet, shouldPublish, lastModified) => { }) } -const PublishButton = ({onClick}) => { - return -} - const modifyHistoryObjectForSheetOptions = (historyObject) => { // we want the 'ref' property to be for the sheet itself and not its segments, as in "Sheet 3" not "Sheet 3:4" let newHistoryObject = Object.assign({}, historyObject); @@ -91,11 +87,12 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorU return setDeletingMode(false)} sheetID={sheetID} authorUrl={authorUrl}/>; } else if (publishingMode) { - return setPublishingMode(false)} sheet={sheet}/>; + return setPublishingMode(false)} sheet={sheet} togglePublish={togglePublish} lastModified={lastModified}/>; } + const openPublishModalButton = ; return ( <> - {editable && !sheetIsPublished && togglePublish(sheet,true, lastModified)}/>} + {editable && !sheetIsPublished && openPublishModalButton} - togglePublish(sheetID, false)}/> + togglePublish(sheet, false, lastModified)}/> } @@ -284,17 +281,34 @@ const GenericSheetModal = ({title, message, close}) => { ; } -const PublishModal = ({sheet, close}) => { - const [topics, setTopics] = useState(sheet.map((item, i) =>({["name"]: item, ["id"]: i}))); +const PublishModal = ({sheet, close, togglePublish, lastModified}) => { + const [topics, setTopics] = useState(sheet.topics.map((item, i) =>({["name"]: item, ["id"]: i}))); + const [title, setTitle] = useState(sheet.title || ""); + const [summary, setSummary] = useState(sheet.summary || ""); + console.log("hello", sheet); + const handlePublish = () => { + sheet.title = title; + sheet.summary = summary; + sheet.topics = topics; + togglePublish(sheet, true, lastModified); + } + return
Publish
- - - - - - -
; +
+ Title + setTitle(e.target.value)}> +
+
+ Description (max 140 characters) + setSummary(e.target.value)}> +
+
+ Add topics related to your sheet + + +
+ ; } const SaveModal = ({historyObject, close}) => { From 36a9e71e107325458cbbb888f0a30233d5a39d48 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Thu, 14 Nov 2024 14:44:27 +0200 Subject: [PATCH 11/32] chore: use ReactTags in PublishModal --- static/js/sheets/SheetOptions.jsx | 81 +++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 8f9b79c5e1..c09e9daacb 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -9,6 +9,7 @@ import {SignUpModalKind} from "../sefaria/signupModalContent"; import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; import Button from "../shared/Button"; +import ReactTags from "react-tag-autocomplete"; const togglePublish = async (sheet, shouldPublish, lastModified) => { sheet.status = shouldPublish ? "public" : "unlisted"; @@ -282,14 +283,73 @@ const GenericSheetModal = ({title, message, close}) => { } const PublishModal = ({sheet, close, togglePublish, lastModified}) => { + const reactTags = React.createRef(); const [topics, setTopics] = useState(sheet.topics.map((item, i) =>({["name"]: item, ["id"]: i}))); - const [title, setTitle] = useState(sheet.title || ""); + const [title, setTitle] = useState(sheet.title.stripHtmlConvertLineBreaks() || ""); const [summary, setSummary] = useState(sheet.summary || ""); - console.log("hello", sheet); + const [suggestions, setSuggestions] = useState([]); + const [validation, setValidation] = useState({ + validationMsg: "", + validationFailed: "none" + }); + const [tags, setTags] = useState( + sheet.topics.map((topic, i) => ({ + id: i, + name: topic["asTyped"], + slug: topic["slug"], + }) + ) + ) + const updateSuggestedTags = (input) => { + if (input == "") return + Sefaria.getName(input, false, 0).then(d => { + const topics = d.completion_objects + .filter(obj => obj.type === "Topic") + .map((filteredObj, index) => ({ + id: index, + name: filteredObj.title, + slug: filteredObj.key + }) + ) + return topics + }).then(topics => setSuggestions(topics)) + } + const onTagDelete = (i) => { + const newTags = tags.slice(0); + newTags.splice(i, 1); + setTags(newTags); + } + const onTagAddition = (tag) => { + const newTags = [].concat(tags, tag); + setTags(newTags); + } + const onTagValidate = (tag) => { + return tags.every((item) => item.name !== tag.name) + } + const handleSummaryChange = (event) => { + const newSummary = event.target.value; + if (event.target.value.length > 280) { + setValidation({ + validationMsg: Sefaria._("The summary description is limited to 280 characters."), + validationFailed: "summary" + }); + } + else { + setValidation({ + validationMsg: "", + validationFailed: "none" + }); + } + setSummary(newSummary); + } const handlePublish = () => { sheet.title = title; sheet.summary = summary; - sheet.topics = topics; + sheet.topics = tags.map(tag => ({ + asTyped: tag.name, + slug: tag.slug, + }) + ); togglePublish(sheet, true, lastModified); } @@ -301,11 +361,22 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => {
Description (max 140 characters) - setSummary(e.target.value)}> +
Add topics related to your sheet - +
; From 8e33419bf0ae111e16f7a55f79601ab6c30b5407 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Thu, 14 Nov 2024 15:47:55 +0200 Subject: [PATCH 12/32] chore: use textarea for summary --- static/js/sheets/SheetOptions.jsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index c09e9daacb..8ff94d3cfa 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -46,6 +46,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorU const [publishingMode, setPublishingMode] = useState(false); const sheetIsPublished = sheet?.status === "public"; const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); + console.log("SheetOptions", sheet); const getSignUpModalKind = () => { if (savingMode) { return SignUpModalKind.Save; @@ -361,11 +362,16 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => {
Description (max 140 characters) - +
Add topics related to your sheet - +
+
+ { onValidate={onTagValidate} onInput={updateSuggestedTags} /> +
+
+
; From 02d1ad1c9710f4b942a419602151f7214f86c16a Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 17 Nov 2024 09:24:34 +0200 Subject: [PATCH 13/32] chore: dont need to pass sheet --- static/js/sheets/SheetOptions.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 8ff94d3cfa..08920f45b5 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -35,7 +35,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorUrl, editable, lastModified}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -44,6 +44,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheet, sheetID, authorU const [exportingMode, setExportingMode] = useState(getExportingStatus()); const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); + const sheet = Sefaria.sheets.loadSheetByID(sheetID); const sheetIsPublished = sheet?.status === "public"; const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); console.log("SheetOptions", sheet); From 2afcf7401e4ca997117de1c867eeadf4e4549e0e Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 19 Nov 2024 10:47:52 +0200 Subject: [PATCH 14/32] chore: add validation from aboutsheet --- static/js/Misc.jsx | 2 +- static/js/sheets/SheetOptions.jsx | 137 +++++++++++++++++++----------- 2 files changed, 89 insertions(+), 50 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 779ef61b10..a213e9c03f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3065,7 +3065,7 @@ const SheetMetaDataBox = ({title, summary, authorUrl, authorStatement, authorIma {sheetOptions} - {summary || editable && } + {(summary || editable) && }
{ sheet.status = shouldPublish ? "public" : "unlisted"; sheet.lastModified = lastModified; - console.log(lastModified, sheet.dateModified); delete sheet._id; Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { if (data.id) { @@ -286,7 +285,6 @@ const GenericSheetModal = ({title, message, close}) => { const PublishModal = ({sheet, close, togglePublish, lastModified}) => { const reactTags = React.createRef(); - const [topics, setTopics] = useState(sheet.topics.map((item, i) =>({["name"]: item, ["id"]: i}))); const [title, setTitle] = useState(sheet.title.stripHtmlConvertLineBreaks() || ""); const [summary, setSummary] = useState(sheet.summary || ""); const [suggestions, setSuggestions] = useState([]); @@ -302,19 +300,51 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => { }) ) ) - const updateSuggestedTags = (input) => { - if (input == "") return - Sefaria.getName(input, false, 0).then(d => { - const topics = d.completion_objects - .filter(obj => obj.type === "Topic") - .map((filteredObj, index) => ({ - id: index, - name: filteredObj.title, - slug: filteredObj.key - }) - ) - return topics - }).then(topics => setSuggestions(topics)) + const isFormValidated = () => { + if ((!summary || summary.trim() === '') && tags.length === 0) { + setValidation({ + validationMsg: Sefaria._("Please add a description and topics to publish your sheet."), + validationFailed: "both" + }); + return false + } + else if (!summary || summary.trim() === '') { + setValidation({ + validationMsg: Sefaria._("Please add a description to publish your sheet."), + validationFailed: "summary" + }); + return false + } + + else if (tags.length === 0) { + setValidation({ + validationMsg: Sefaria._("Please add topics to publish your sheet."), + validationFailed: "topics" + }); + return false; + } + + else { + setValidation({ + validationMsg: "", + validationFailed: "none" + }); + return true; + } + } + const updateSuggestedTags = (input) => { + if (input === "") return + Sefaria.getName(input, false, 0).then(d => { + const topics = d.completion_objects + .filter(obj => obj.type === "Topic") + .map((filteredObj, index) => ({ + id: index, + name: filteredObj.title, + slug: filteredObj.key + }) + ) + return topics; + }).then(topics => setSuggestions(topics)) } const onTagDelete = (i) => { const newTags = tags.slice(0); @@ -352,48 +382,57 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => { slug: tag.slug, }) ); - togglePublish(sheet, true, lastModified); + if ((isFormValidated())) { + togglePublish(sheet, true, lastModified); + } } return -
Publish
-
- Title - setTitle(e.target.value)}> -
-
- Description (max 140 characters) - -
-
- Add topics related to your sheet - - -
-
-
- +
Publish
+
+
+
+ Title + setTitle(e.target.value)}> +
+
+ Description (max 140 characters) + +
+
+ Add topics related to your sheet +
+ +
+ {validation.validationFailed !== "none" && +

{validation.validationMsg}

} +
-
-
- -
+
+
; } const SaveModal = ({historyObject, close}) => { - const isSaved = !!Sefaria.getSavedItem(historyObject); + const isSaved = !!Sefaria.getSavedItem(historyObject); const savingMessage = "Saving..."; const [message, setMessage] = useState(savingMessage); const savedMessage = isSaved ? "Sheet no longer saved." : "Saved sheet."; From 51a719fcde16a2a2bdbc3ec357ebe11d654b4540 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 19 Nov 2024 14:38:08 +0200 Subject: [PATCH 15/32] chore: start css styling of publish box --- static/css/s2.css | 65 ++++++++++++++++++++---------- static/js/sheets/SheetOptions.jsx | 67 +++++++++++++++---------------- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 4562aab420..3868739a56 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -8522,7 +8522,6 @@ a.sheetAuthorName:hover { } .publishBox { text-align: start; - background-color: #EDEDEC; border-radius: 6px; padding: 10px 20px; max-width: 540px; @@ -8530,6 +8529,17 @@ a.sheetAuthorName:hover { font-size: 16px; color: #666; } +.publishBox .publishLabel { + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); + font-size: 14px; + font-weight: 400; + line-height: 18px; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: var(--dark-grey); + margin-bottom: 10px; +} div.transparentBackground.publishBox { background-color: inherit; @@ -8551,6 +8561,39 @@ div.transparentBackground.publishBox { .publishBox textarea.error { border: 1px solid red; } +.sheetMetaDataBox .publishBox .react-tags__selected { + display: inline; +} +.sheetMetaDataBox .publishBox .react-tags__selected-tag { + display: inline-block; + box-sizing: border-box; + margin: 0 6px 6px 0; + padding: 6px 8px; + border: none; + border-radius: 6px; + background: #FFFFFF; + color: #000; + /* match the font styles */ + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +.sheetMetaDataBox .publishBox textarea, .sheetMetaDataBox .publishBox input, .sheetMetaDataBox .publishBox .react-tags { + border-radius: 6px; + border: none; + max-width: 200px; + width: 95%; + padding: 7px; + background: #EDEDEC; + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); + font-size: 16px; + font-weight: 400; + line-height: 18.75px; + text-underline-position: from-font; + text-decoration-skip-ink: none; + margin-bottom: 10px; +} .publishBox p strong { color: black; font-weight: 400; @@ -8574,26 +8617,6 @@ div.transparentBackground.publishBox { .publishBox .smallText { color: #666666 } -.publishBox textarea { - width: 100%; - height: 100px; - resize: none; - box-sizing: border-box; - font-size: 16px; - color: #000; - border: none; - border-radius: 6px; - box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); - font-style: normal; - padding: 10px 10px 4px 10px; - margin-bottom: 0px; -} -.publishBox textarea::placeholder { - font-size: 16px; - color: #666; - font-style: normal; - font-family: var(--english-sans-serif-font-family); -} .publishBox .react-tags { position: relative; padding: 10px 10px 4px 10px; diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 489e6a46b4..2776932036 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -390,43 +390,42 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => { return
Publish
-
-
- Title +
+
+ Title +
setTitle(e.target.value)}> -
-
- Description (max 140 characters) +
+ Description (max 140 characters) +
-
-
- Add topics related to your sheet -
- + className={validation.validationFailed === "both" || validation.validationFailed === "summary" ? "error" : ""} + rows="3" + maxLength="281" + placeholder={Sefaria._("Write a short description of your sheet...")} + value={summary} + onChange={handleSummaryChange}> +
+ Add topics related to your sheet
- {validation.validationFailed !== "none" && -

{validation.validationMsg}

} - -
-
+
+ +
+ {validation.validationFailed !== "none" && +

{validation.validationMsg}

} + +
; } From 0bf5ab18b8e5317c2b43d81c5c5a73e192d6533a Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 20 Nov 2024 12:11:25 +0200 Subject: [PATCH 16/32] chore: PublishModal contains PublishMenu --- static/js/sheets/SheetOptions.jsx | 247 +++++++++++++++++------------- 1 file changed, 142 insertions(+), 105 deletions(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 2776932036..a757c0846c 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -10,18 +10,6 @@ import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; import Button from "../shared/Button"; import ReactTags from "react-tag-autocomplete"; - -const togglePublish = async (sheet, shouldPublish, lastModified) => { - sheet.status = shouldPublish ? "public" : "unlisted"; - sheet.lastModified = lastModified; - delete sheet._id; - Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { - if (data.id) { - Sefaria.sheets._loadSheetByID[data.id] = data; - } - }) -} - const modifyHistoryObjectForSheetOptions = (historyObject) => { // we want the 'ref' property to be for the sheet itself and not its segments, as in "Sheet 3" not "Sheet 3:4" let newHistoryObject = Object.assign({}, historyObject); @@ -44,9 +32,8 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); const sheet = Sefaria.sheets.loadSheetByID(sheetID); - const sheetIsPublished = sheet?.status === "public"; + const isPublic = sheet?.status === "public"; const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); - console.log("SheetOptions", sheet); const getSignUpModalKind = () => { if (savingMode) { return SignUpModalKind.Save; @@ -89,12 +76,12 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi return setDeletingMode(false)} sheetID={sheetID} authorUrl={authorUrl}/>; } else if (publishingMode) { - return setPublishingMode(false)} sheet={sheet} togglePublish={togglePublish} lastModified={lastModified}/>; + return setPublishingMode(false)} sheet={sheet} isPublic={isPublic} lastModified={lastModified}/>; } - const openPublishModalButton = ; + const publishModalButton = ; return ( <> - {editable && !sheetIsPublished && openPublishModalButton} + {editable && !isPublic && publishModalButton} setSharingMode(true)}/> - {editable && sheetIsPublished && <> + {editable && isPublic && <> - togglePublish(sheet, false, lastModified)}/> + setPublishingMode(true)}/> } @@ -283,7 +270,59 @@ const GenericSheetModal = ({title, message, close}) => { ; } -const PublishModal = ({sheet, close, togglePublish, lastModified}) => { +const PublishModal = ({sheet, close, lastModified, isPublic}) => { + // `isPublic` is a boolean indicating whether the sheet is already published/public; + // if false, the sheet's 'status' is 'unlisted'. If `isPublic` is true, we just want to unpublish it + // so this modal simply posts the new status. If `isPublic` is false, we want to give the user the PublishMenu component + // allowing them to specify title, summary, and tags and from there the user can choose to post it. + const publishState = { + notPosting: "", + posting: "Updating sheet...", + posted: "Success!", + } + + // if it's not yet public, show PublishMenu; if it's public, start unpublishing it + const initState = !isPublic ? publishState.notPosting : publishState.posting; + const [publishText, setPublishText] = useState(initState); + const handleClose = () => { + if (publishText !== publishText.posting) { + // don't allow user to close modal while posting is taking place + close(); + } + } + const togglePublishStatus = async () => { + setPublishText(publishState.posting); + sheet.status = isPublic ? "unlisted" : "public"; + sheet.lastModified = lastModified; + delete sheet._id; + Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { + if (data.id) { + Sefaria.sheets._loadSheetByID[data.id] = data; + setPublishText(publishState.posted); + } + }).catch(error => { + setPublishText(error.message); + }) + } + useEffect(async () => { + if (publishText === publishState.posting) { + await togglePublishStatus(); + } + }, [publishText]) + let contents; + if (publishText === publishState.notPosting) { + contents = setPublishText(publishState.posting)}/>; + } + else { + contents =
{publishText}
; + } + return +
Publish
+ {contents} +
; +} + +const PublishMenu = ({sheet, publishCallback}) => { const reactTags = React.createRef(); const [title, setTitle] = useState(sheet.title.stripHtmlConvertLineBreaks() || ""); const [summary, setSummary] = useState(sheet.summary || ""); @@ -332,7 +371,7 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => { return true; } } - const updateSuggestedTags = (input) => { + const updateSuggestedTags = (input) => { if (input === "") return Sefaria.getName(input, false, 0).then(d => { const topics = d.completion_objects @@ -383,102 +422,100 @@ const PublishModal = ({sheet, close, togglePublish, lastModified}) => { }) ); if ((isFormValidated())) { - togglePublish(sheet, true, lastModified); + publishCallback(true); } } - - return -
Publish
-
-
-
- Title -
- setTitle(e.target.value)}> -
- Description (max 140 characters) -
- -
- Add topics related to your sheet -
-
- -
- {validation.validationFailed !== "none" && -

{validation.validationMsg}

} - -
-
-
; + return
+
+
+ Title +
+ setTitle(e.target.value)}> +
+ Description (max 140 characters) +
+ +
+ Add topics related to your sheet +
+
+ +
+ {validation.validationFailed !== "none" && +

{validation.validationMsg}

} + +
+
} const SaveModal = ({historyObject, close}) => { const isSaved = !!Sefaria.getSavedItem(historyObject); - const savingMessage = "Saving..."; - const [message, setMessage] = useState(savingMessage); - const savedMessage = isSaved ? "Sheet no longer saved." : "Saved sheet."; - useEffect(() => { - if (message === savingMessage) { - Sefaria.toggleSavedItem(historyObject) - .finally(() => { - setMessage(savedMessage); - }); - } - }); - return Save} message={{message}} close={close}/>; + const savingMessage = "Saving..."; + const [message, setMessage] = useState(savingMessage); + const savedMessage = isSaved ? "Sheet no longer saved." : "Saved sheet."; + useEffect(() => { + if (message === savingMessage) { + Sefaria.toggleSavedItem(historyObject) + .finally(() => { + setMessage(savedMessage); + }); + } + }); + return Save} + message={{message}} close={close}/>; } -const GoogleDocExportButton = ({ onClick }) => { - const googleDriveText = { en: "Export to Google Docs", he: "ייצוא לגוגל דוקס" }; - return onClick()} />; +const GoogleDocExportButton = ({onClick}) => { + const googleDriveText = {en: "Export to Google Docs", he: "ייצוא לגוגל דוקס"}; + return onClick()}/>; } -const GoogleDocExportModal = ({ sheetID, close }) => { - const googleDriveState = { - exporting: {en: "Exporting to Google Docs...", he: "מייצא לגוגל דוקס..."}, - exportComplete: { en: "Success!", he: "ייצוא הסתיים"} - } - const [googleDriveText, setGoogleDriveText] = useState(googleDriveState.exporting); +const GoogleDocExportModal = ({sheetID, close}) => { + const googleDriveState = { + exporting: {en: "Exporting to Google Docs...", he: "מייצא לגוגל דוקס..."}, + exportComplete: {en: "Success!", he: "ייצוא הסתיים"} + } + const [googleDriveText, setGoogleDriveText] = useState(googleDriveState.exporting); - const [googleDriveLink, setGoogleDriveLink] = useState(""); - const sheet = Sefaria.sheets.loadSheetByID(sheetID); + const [googleDriveLink, setGoogleDriveLink] = useState(""); + const sheet = Sefaria.sheets.loadSheetByID(sheetID); - useEffect(() => { - if (googleDriveText.en === googleDriveState.exporting.en) { - history.replaceState("", document.title, window.location.pathname + window.location.search); // remove exportToDrive hash once it's used to trigger export - $.ajax({ - type: "POST", - url: "/api/sheets/" + sheetID + "/export_to_drive", - success: function (data) { - if ("error" in data) { - console.log(data.error.message); - // Export Failed - } else { - // Export succeeded + useEffect(() => { + if (googleDriveText.en === googleDriveState.exporting.en) { + history.replaceState("", document.title, window.location.pathname + window.location.search); // remove exportToDrive hash once it's used to trigger export + $.ajax({ + type: "POST", + url: "/api/sheets/" + sheetID + "/export_to_drive", + success: function (data) { + if ("error" in data) { + console.log(data.error.message); + // Export Failed + } else { + // Export succeeded setGoogleDriveLink(data.webViewLink); setGoogleDriveText(googleDriveState.exportComplete) } From 252c8d8ee6128156f99632c600b93f30a9d791cd Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 20 Nov 2024 13:21:24 +0200 Subject: [PATCH 17/32] fix: published sheets once modified do not get unpublished --- static/js/Editor.jsx | 43 +++++++++++++++---------------- static/js/sheets/SheetOptions.jsx | 27 +++++++++++-------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index c8d23a38e1..9aeeeab439 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2557,13 +2557,6 @@ const SefariaEditor = (props) => { const [canUseDOM, setCanUseDOM] = useState(false); const [lastSelection, setLastSelection] = useState(null) const [readyForNormalize, setReadyForNormalize] = useState(false); - const sheetOptions = ; useEffect( () => { @@ -2783,9 +2776,7 @@ const SefariaEditor = (props) => { sources: sources.filter(x => !!x), nextNode: doc.nextNode, }; - - return JSON.stringify(sheet); - + return sheet; } @@ -2795,14 +2786,15 @@ const SefariaEditor = (props) => { return } // console.log('saving...'); + postSheet(json, doc[0].id); + } - $.post("/api/sheets/", {"json": json}, res => { - setlastModified(res.dateModified); - // console.log("saved at: "+ res.dateModified); + const postSheet = (sheet, id) => { + return Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { + setlastModified(data.dateModified); setUnsavedChanges(false); - - const updatedSheet = {...Sefaria.sheets._loadSheetByID[doc[0].id], ...res}; - Sefaria.sheets._loadSheetByID[doc[0].id] = updatedSheet + const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; + Sefaria.sheets._loadSheetByID[id] = updatedSheet; }); } @@ -2952,14 +2944,21 @@ const SefariaEditor = (props) => { () => withTables(withSefariaSheet(withLinks(withHistory(withReact(createEditor()))))), [] ); - + const sheetOptions = ; return (
- saveDocument(currentDocument)} sheetOptions={sheetOptions}/> diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index a757c0846c..6fe8538200 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -22,7 +22,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified, postSheet}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -76,7 +76,11 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi return setDeletingMode(false)} sheetID={sheetID} authorUrl={authorUrl}/>; } else if (publishingMode) { - return setPublishingMode(false)} sheet={sheet} isPublic={isPublic} lastModified={lastModified}/>; + return setPublishingMode(false)} + sheet={sheet} + isPublic={isPublic} + postSheet={postSheet} + lastModified={lastModified}/>; } const publishModalButton = ; return ( @@ -270,7 +274,7 @@ const GenericSheetModal = ({title, message, close}) => { ; } -const PublishModal = ({sheet, close, lastModified, isPublic}) => { +const PublishModal = ({sheet, close, lastModified, isPublic, postSheet}) => { // `isPublic` is a boolean indicating whether the sheet is already published/public; // if false, the sheet's 'status' is 'unlisted'. If `isPublic` is true, we just want to unpublish it // so this modal simply posts the new status. If `isPublic` is false, we want to give the user the PublishMenu component @@ -291,23 +295,24 @@ const PublishModal = ({sheet, close, lastModified, isPublic}) => { } } const togglePublishStatus = async () => { - setPublishText(publishState.posting); sheet.status = isPublic ? "unlisted" : "public"; sheet.lastModified = lastModified; delete sheet._id; - Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { + postSheet(sheet, sheet.id).then(data => { if (data.id) { - Sefaria.sheets._loadSheetByID[data.id] = data; - setPublishText(publishState.posted); + setPublishText(publishState.posted); } }).catch(error => { - setPublishText(error.message); + setPublishText(error.message); }) } - useEffect(async () => { - if (publishText === publishState.posting) { - await togglePublishStatus(); + useEffect( () => { + const toggle = async () => { + if (publishText === publishState.posting) { + await togglePublishStatus(); + } } + toggle(); }, [publishText]) let contents; if (publishText === publishState.notPosting) { From d9bfb3c6d2bdf71ac6aaa124ceb8bf06b3460d68 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 20 Nov 2024 16:11:55 +0200 Subject: [PATCH 18/32] chore: set publish status on sefaria editor level --- static/js/Editor.jsx | 8 +++++-- static/js/sheets/SheetOptions.jsx | 39 +++++++++++++++---------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 9aeeeab439..b32aa96fc6 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -597,7 +597,7 @@ function transformSheetJsonToSlate(sheet) { let initValue = [ { type: 'Sheet', - status: sheet.status, + status: status, views: sheet.views, tags: sheet.tags || [], includedRefs: sheet.includedRefs, @@ -2548,6 +2548,7 @@ const BlockButton = ({format, icon}) => { const SefariaEditor = (props) => { const editorContainer = useRef(null); const [sheet, setSheet] = useState(props.data); + const [status, setStatus] = useState(sheet?.status || "unlisted"); const initValue = [{type: "sheet", children: [{text: ""}]}]; const renderElement = useCallback(props => , []); const [value, setValue] = useState(initValue); @@ -2764,7 +2765,7 @@ const SefariaEditor = (props) => { }); let sheet = { - status: doc.status, + status: status, id: doc.id, promptedToPublish: doc.promptedToPublish, lastModified: lastModified, @@ -2792,6 +2793,7 @@ const SefariaEditor = (props) => { const postSheet = (sheet, id) => { return Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { setlastModified(data.dateModified); + setStatus(data.status); setUnsavedChanges(false); const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; Sefaria.sheets._loadSheetByID[id] = updatedSheet; @@ -2799,6 +2801,7 @@ const SefariaEditor = (props) => { } function onChange(value) { + console.log("onChange", value); if (currentDocument !== value) { setCurrentDocument(value); } @@ -2951,6 +2954,7 @@ const SefariaEditor = (props) => { editable={props.editable} authorUrl={sheet.ownerProfileUrl} lastModified={lastModified} + status={status} />; return (
diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 6fe8538200..aab4069e98 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -22,7 +22,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified, postSheet}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified, postSheet, status}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -31,8 +31,6 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi const [exportingMode, setExportingMode] = useState(getExportingStatus()); const [deletingMode, setDeletingMode] = useState(false); const [publishingMode, setPublishingMode] = useState(false); - const sheet = Sefaria.sheets.loadSheetByID(sheetID); - const isPublic = sheet?.status === "public"; const historyObjectForSheet = modifyHistoryObjectForSheetOptions(historyObject); const getSignUpModalKind = () => { if (savingMode) { @@ -77,15 +75,15 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi } else if (publishingMode) { return setPublishingMode(false)} - sheet={sheet} - isPublic={isPublic} + sheetID={sheetID} + status={status} postSheet={postSheet} lastModified={lastModified}/>; } const publishModalButton = ; return ( <> - {editable && !isPublic && publishModalButton} + {editable && status === 'unlisted' && publishModalButton} setSharingMode(true)}/> - {editable && isPublic && <> - - - setPublishingMode(true)}/> - - + {editable && status === 'public' && <> + + + setPublishingMode(true)}/> + + } {editable && <> @@ -274,20 +272,21 @@ const GenericSheetModal = ({title, message, close}) => { ; } -const PublishModal = ({sheet, close, lastModified, isPublic, postSheet}) => { - // `isPublic` is a boolean indicating whether the sheet is already published/public; - // if false, the sheet's 'status' is 'unlisted'. If `isPublic` is true, we just want to unpublish it - // so this modal simply posts the new status. If `isPublic` is false, we want to give the user the PublishMenu component - // allowing them to specify title, summary, and tags and from there the user can choose to post it. +const PublishModal = ({close, lastModified, status, sheetID, postSheet}) => { + // `status` is 'public' or 'unlisted'. we are going to toggle the status. if it's 'public' we want to unlist it + // so this modal simply posts the new status. If it's 'unlisted', we want to give the user the PublishMenu component + // allowing them to specify title, summary, and tags and from there the user can choose to make the sheet public + const sheet = Sefaria.sheets.loadSheetByID(sheetID); const publishState = { notPosting: "", posting: "Updating sheet...", posted: "Success!", } - // if it's not yet public, show PublishMenu; if it's public, start unpublishing it - const initState = !isPublic ? publishState.notPosting : publishState.posting; + // if it's not yet public, show PublishMenu and don't yet post it; if it's public, start posting it + const initState = status === 'unlisted' ? publishState.notPosting : publishState.posting; const [publishText, setPublishText] = useState(initState); + const handleClose = () => { if (publishText !== publishText.posting) { // don't allow user to close modal while posting is taking place @@ -295,7 +294,7 @@ const PublishModal = ({sheet, close, lastModified, isPublic, postSheet}) => { } } const togglePublishStatus = async () => { - sheet.status = isPublic ? "unlisted" : "public"; + sheet.status = status === 'public' ? "unlisted" : "public"; sheet.lastModified = lastModified; delete sheet._id; postSheet(sheet, sheet.id).then(data => { From 6fd6f0f87da26d22565eeabaa5e3f8a8b2fb7688 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Thu, 21 Nov 2024 13:22:16 +0200 Subject: [PATCH 19/32] chore: title and summary implemented using react state --- static/js/Editor.jsx | 23 +++++++++++++---------- static/js/Misc.jsx | 39 +++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index b32aa96fc6..a45b965868 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -593,7 +593,6 @@ function transformSheetJsonToSlate(sheet) { children: [{text: ""}] }) } - let initValue = [ { type: 'Sheet', @@ -613,7 +612,7 @@ function transformSheetJsonToSlate(sheet) { authorUrl: sheet.ownerProfileUrl, authorStatement: sheet.ownerName, authorImage: sheet.ownerImageUrl, - title: sheet.title, + title: sheetTitle, displayedCollection: sheet.displayedCollection || "", collectionName: sheet.collectionName || "", collectionImage: sheet.collectionImage || "", @@ -2558,6 +2557,12 @@ const SefariaEditor = (props) => { const [canUseDOM, setCanUseDOM] = useState(false); const [lastSelection, setLastSelection] = useState(null) const [readyForNormalize, setReadyForNormalize] = useState(false); + const [summary, setSummary] = useState(sheet.summary || ""); + const [title, setTitle] = useState(sheet.title || ""); + + useEffect(() => { + saveDocument(currentDocument); + }, [title, summary]); useEffect( () => { @@ -2669,8 +2674,6 @@ const SefariaEditor = (props) => { }, [canUseDOM]) function saveSheetContent(doc, lastModified) { - const sheetTitle = editorContainer.current.querySelector(".sheetContent .sheetMetaDataBox .title") ? editorContainer.current.querySelector(".sheetContent .sheetMetaDataBox .title").textContent : "Untitled"; - const sheetSummary = editorContainer.current.querySelector(".sheetContent .sheetMetaDataBox .summary") ? editorContainer.current.querySelector(".sheetContent .sheetMetaDataBox .summary").textContent : ""; const docContent = doc.children.find(el => el.type == "SheetContent") if (!docContent) { return false @@ -2769,11 +2772,11 @@ const SefariaEditor = (props) => { id: doc.id, promptedToPublish: doc.promptedToPublish, lastModified: lastModified, - summary: sheetSummary, + summary: summary, options: { ...doc.options, divineNames: props.divineNameReplacement }, tags: doc.tags, displayedCollection: doc.displayedCollection, - title: sheetTitle === "" ? "Untitled" : sheetTitle, + title: title === "" ? "Untitled" : title, sources: sources.filter(x => !!x), nextNode: doc.nextNode, }; @@ -2801,7 +2804,6 @@ const SefariaEditor = (props) => { } function onChange(value) { - console.log("onChange", value); if (currentDocument !== value) { setCurrentDocument(value); } @@ -2961,10 +2963,11 @@ const SefariaEditor = (props) => { saveDocument(currentDocument)} + titleCallback={(newTitle) => setTitle(newTitle)} + summaryCallback={(newSummary) => setSummary(newSummary)} sheetOptions={sheetOptions}/> { /* debugger */ diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index a213e9c03f..2b24b3727a 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2895,21 +2895,24 @@ SheetTitle.propTypes = { title: PropTypes.string, }; -const SheetMetaDataBoxSegment = (props) => ( -
{ + const handleBlur = (e) => { + const content = e.target.textContent; + if (props.blurCallback) { + props.blurCallback(content); + } + } + return
- {props.text ? props.text.stripHtmlConvertLineBreaks() : ""} + {props.text ? props.text.stripHtmlConvertLineBreaks() : ""}
-); -SheetMetaDataBoxSegment.propTypes = { - title: PropTypes.string, -}; +} const SheetAuthorStatement = (props) => ( @@ -3059,13 +3062,17 @@ const TitleVariants = function({titles, update, options}) { />
} -const SheetMetaDataBox = ({title, summary, authorUrl, authorStatement, authorImage, sheetOptions, editable, blurCallback}) => { +const SheetMetaDataBox = ({title, summary, authorUrl, authorStatement, authorImage, sheetOptions, editable, titleCallback, + summaryCallback}) => { return
- + {sheetOptions}
- {(summary || editable) && } + {(summary || editable) && }
Date: Thu, 21 Nov 2024 14:46:00 +0200 Subject: [PATCH 20/32] chore: title and summary update but sheet gets saved twice upon initial load --- static/js/Editor.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index a45b965868..580abf2e60 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2561,6 +2561,9 @@ const SefariaEditor = (props) => { const [title, setTitle] = useState(sheet.title || ""); useEffect(() => { + if (!canUseDOM) { + return + } saveDocument(currentDocument); }, [title, summary]); @@ -2797,6 +2800,8 @@ const SefariaEditor = (props) => { return Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { setlastModified(data.dateModified); setStatus(data.status); + setTitle(data.title); + setSummary(data.summary); setUnsavedChanges(false); const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; Sefaria.sheets._loadSheetByID[id] = updatedSheet; From 8f348be16e254b428e4ea43020dcd7273b09205b Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 24 Nov 2024 14:23:49 +0200 Subject: [PATCH 21/32] chore: use Untitled placeholder --- static/js/sheets/SheetOptions.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index aab4069e98..66ff7dd800 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -434,7 +434,10 @@ const PublishMenu = ({sheet, publishCallback}) => {
Title
- setTitle(e.target.value)}> + setTitle(e.target.value)}>
Description (max 140 characters)
From 4ab45e96a087e98cda120b76ade4f98dc64b8734 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 24 Nov 2024 15:04:56 +0200 Subject: [PATCH 22/32] chore: default to Untitled in post --- static/js/sheets/SheetOptions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 66ff7dd800..26454be5cc 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -418,7 +418,7 @@ const PublishMenu = ({sheet, publishCallback}) => { setSummary(newSummary); } const handlePublish = () => { - sheet.title = title; + sheet.title = title === "" ? "Untitled" : title; sheet.summary = summary; sheet.topics = tags.map(tag => ({ asTyped: tag.name, From b664125a3da347a62f561255fb95392d23ef8393 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Tue, 26 Nov 2024 11:54:58 +0200 Subject: [PATCH 23/32] chore: postSheet should be synchronous --- static/js/Editor.jsx | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 9c2dd48864..61fa80d3ce 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2560,10 +2560,6 @@ const SefariaEditor = (props) => { const [summary, setSummary] = useState(sheet.summary || ""); const [title, setTitle] = useState(sheet.title || ""); - useEffect(() => { - saveDocument(currentDocument); - }, [title, summary]); - useEffect( () => { if (!canUseDOM) { @@ -2583,7 +2579,7 @@ const SefariaEditor = (props) => { clearTimeout(handler); }; }, - [currentDocument[0].children[0]] // Only re-call effect if value or delay changes + [currentDocument[0].children[0], title, summary] // Only re-call effect if value or delay changes ); useEffect( @@ -2599,6 +2595,8 @@ const SefariaEditor = (props) => { //TODO: Check that we still need/want this temporary analytics tracking code try {hj('event', 'using_new_editor');} catch {console.error('hj failed')} + + return () => saveDocument(currentDocument); }, [] ) @@ -2793,16 +2791,15 @@ const SefariaEditor = (props) => { postSheet(json, doc[0].id); } - const postSheet = (sheet, id) => { - return Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST").then(data => { - setlastModified(data.dateModified); - setStatus(data.status); - setTitle(data.title); - setSummary(data.summary); - setUnsavedChanges(false); - const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; - Sefaria.sheets._loadSheetByID[id] = updatedSheet; - }); + const postSheet = async (sheet, id) => { + const data = await Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST"); + setlastModified(data.dateModified); + setStatus(data.status); + setTitle(data.title); + setSummary(data.summary); + setUnsavedChanges(false); + const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; + Sefaria.sheets._loadSheetByID[id] = updatedSheet; } function onChange(value) { From 1457050408bc0a759fdac839015611ca941f5815 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Wed, 27 Nov 2024 13:59:54 +0200 Subject: [PATCH 24/32] chore: lastModified doesnt need to be state. it can be derived from cache --- static/js/Editor.jsx | 18 ++++++++---------- static/js/sheets/SheetOptions.jsx | 9 ++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 61fa80d3ce..6f8f13076a 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2553,7 +2553,6 @@ const SefariaEditor = (props) => { const [value, setValue] = useState(initValue); const [currentDocument, setCurrentDocument] = useState(initValue); const [unsavedChanges, setUnsavedChanges] = useState(false); - const [lastModified, setlastModified] = useState(props.data.dateModified); const [canUseDOM, setCanUseDOM] = useState(false); const [lastSelection, setLastSelection] = useState(null) const [readyForNormalize, setReadyForNormalize] = useState(false); @@ -2596,7 +2595,6 @@ const SefariaEditor = (props) => { //TODO: Check that we still need/want this temporary analytics tracking code try {hj('event', 'using_new_editor');} catch {console.error('hj failed')} - return () => saveDocument(currentDocument); }, [] ) @@ -2781,25 +2779,26 @@ const SefariaEditor = (props) => { return sheet; } - + function getLastModified() { + return Sefaria.sheets._loadSheetByID[props.data.id]?.dateModified || props.data.dateModified; + } function saveDocument(doc) { + const lastModified = getLastModified(); const json = saveSheetContent(doc[0], lastModified); if (!json) { return } // console.log('saving...'); - postSheet(json, doc[0].id); + postSheet(json); } - const postSheet = async (sheet, id) => { + const postSheet = async (sheet) => { const data = await Sefaria.apiRequestWithBody("/api/sheets/", null, sheet, "POST"); - setlastModified(data.dateModified); setStatus(data.status); setTitle(data.title); setSummary(data.summary); - setUnsavedChanges(false); - const updatedSheet = {...Sefaria.sheets._loadSheetByID[id], ...data}; - Sefaria.sheets._loadSheetByID[id] = updatedSheet; + const updatedSheet = {...Sefaria.sheets._loadSheetByID[props.data.id], ...data}; + Sefaria.sheets._loadSheetByID[props.data.id] = updatedSheet; } function onChange(value) { @@ -2954,7 +2953,6 @@ const SefariaEditor = (props) => { historyObject={props.historyObject} editable={props.editable} authorUrl={sheet.ownerProfileUrl} - lastModified={lastModified} status={status} />; return ( diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 26454be5cc..fc9133538c 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -22,7 +22,7 @@ const getExportingStatus = () => { return urlHashObject === "exportToDrive"; } -const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, lastModified, postSheet, status}) => { +const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, editable, postSheet, status}) => { // `editable` -- whether the sheet belongs to the current user const [sharingMode, setSharingMode] = useState(false); // Share Modal open or closed const [collectionsMode, setCollectionsMode] = useState(false); // Collections Modal open or closed @@ -77,8 +77,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi return setPublishingMode(false)} sheetID={sheetID} status={status} - postSheet={postSheet} - lastModified={lastModified}/>; + postSheet={postSheet}/>; } const publishModalButton = ; return ( @@ -272,7 +271,7 @@ const GenericSheetModal = ({title, message, close}) => { ; } -const PublishModal = ({close, lastModified, status, sheetID, postSheet}) => { +const PublishModal = ({close, status, sheetID, postSheet}) => { // `status` is 'public' or 'unlisted'. we are going to toggle the status. if it's 'public' we want to unlist it // so this modal simply posts the new status. If it's 'unlisted', we want to give the user the PublishMenu component // allowing them to specify title, summary, and tags and from there the user can choose to make the sheet public @@ -295,7 +294,7 @@ const PublishModal = ({close, lastModified, status, sheetID, postSheet}) => { } const togglePublishStatus = async () => { sheet.status = status === 'public' ? "unlisted" : "public"; - sheet.lastModified = lastModified; + sheet.lastModified = sheet.dateModified; delete sheet._id; postSheet(sheet, sheet.id).then(data => { if (data.id) { From 923eeb990ff8d7e1bf0e441d6489034520f38287 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 1 Dec 2024 09:36:21 +0200 Subject: [PATCH 25/32] chore: shared -> common directory --- static/js/sheets/SheetOptions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 0fed917081..2d90257882 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -8,7 +8,7 @@ import $ from "../sefaria/sefariaJquery"; import {SignUpModalKind} from "../sefaria/signupModalContent"; import {AddToSourceSheetBox} from "../AddToSourceSheet"; import {CollectionsWidget} from "../CollectionsWidget"; -import Button from "../shared/Button"; +import Button from "../common/Button"; import ReactTags from "react-tag-autocomplete"; const modifyHistoryObjectForSheetOptions = (historyObject) => { // we want the 'ref' property to be for the sheet itself and not its segments, as in "Sheet 3" not "Sheet 3:4" From d307f74dbb3ebdbc606501ad3291cb16166288bb Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 1 Dec 2024 10:12:26 +0200 Subject: [PATCH 26/32] chore: css adjustments for publish modal --- static/css/s2.css | 4 ++-- static/js/sheets/SheetOptions.jsx | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 2121b87117..d4c0e01b63 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -8550,6 +8550,7 @@ a.sheetAuthorName:hover { border-radius: 6px; padding: 10px 20px; max-width: 540px; + width: 500px; margin: 20px -20px; font-size: 16px; color: #666; @@ -8606,8 +8607,7 @@ div.transparentBackground.publishBox { .sheetMetaDataBox .publishBox textarea, .sheetMetaDataBox .publishBox input, .sheetMetaDataBox .publishBox .react-tags { border-radius: 6px; border: none; - max-width: 200px; - width: 95%; + width: 98%; padding: 7px; background: #EDEDEC; --english-font: var(--english-sans-serif-font-family); diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 2d90257882..73a25ccca6 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -296,10 +296,8 @@ const PublishModal = ({close, status, sheetID, postSheet}) => { sheet.status = status === 'public' ? "unlisted" : "public"; sheet.lastModified = sheet.dateModified; delete sheet._id; - postSheet(sheet, sheet.id).then(data => { - if (data.id) { - setPublishText(publishState.posted); - } + postSheet(sheet, sheet.id).then(() => { + setPublishText(publishState.posted); }).catch(error => { setPublishText(error.message); }) @@ -402,9 +400,9 @@ const PublishMenu = ({sheet, publishCallback}) => { } const handleSummaryChange = (event) => { const newSummary = event.target.value; - if (event.target.value.length > 280) { + if (event.target.value.length > 140) { setValidation({ - validationMsg: Sefaria._("The summary description is limited to 280 characters."), + validationMsg: Sefaria._("The summary description is limited to 140 characters."), validationFailed: "summary" }); } @@ -428,7 +426,7 @@ const PublishMenu = ({sheet, publishCallback}) => { publishCallback(true); } } - return
+ return
Title @@ -443,7 +441,7 @@ const PublishMenu = ({sheet, publishCallback}) => { From 0acedb6938e1e27796759e6c8ffea226be3441e0 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 2 Dec 2024 10:18:58 +0200 Subject: [PATCH 27/32] chore: catch error in calling postSheet --- static/js/Editor.jsx | 16 ++++++++++------ static/js/sheets/SheetOptions.jsx | 11 ++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 6f8f13076a..ecb61b15c5 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2567,7 +2567,7 @@ const SefariaEditor = (props) => { setUnsavedChanges(true); // Update debounced value after delay - const handler = setTimeout(() => { + const handler = setTimeout( () => { saveDocument(currentDocument); }, 500); @@ -2629,7 +2629,7 @@ const SefariaEditor = (props) => { if (node.text && props.divineNameReplacement) { const newStr = replaceDivineNames(node.text, props.divineNameReplacement) if (newStr != node.text) { - Transforms.insertText(editor, newStr, { at: path }) + Transforms.insertText(editor, newStr, {at: path}) } } } @@ -2640,8 +2640,8 @@ const SefariaEditor = (props) => { const temp_select = editor.selection Transforms.select(editor, { - anchor: {path: [0, 0], offset: 0}, - focus: {path: [0, 0], offset: 0}, + anchor: {path: [0, 0], offset: 0}, + focus: {path: [0, 0], offset: 0}, }); Transforms.select(editor, temp_select) @@ -2782,14 +2782,18 @@ const SefariaEditor = (props) => { function getLastModified() { return Sefaria.sheets._loadSheetByID[props.data.id]?.dateModified || props.data.dateModified; } - function saveDocument(doc) { + async function saveDocument(doc) { const lastModified = getLastModified(); const json = saveSheetContent(doc[0], lastModified); if (!json) { return } // console.log('saving...'); - postSheet(json); + try { + await postSheet(json); + } catch(error) { + console.log(`Error: ${error.message}`) + } } const postSheet = async (sheet) => { diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 73a25ccca6..ed1f010a30 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -296,11 +296,12 @@ const PublishModal = ({close, status, sheetID, postSheet}) => { sheet.status = status === 'public' ? "unlisted" : "public"; sheet.lastModified = sheet.dateModified; delete sheet._id; - postSheet(sheet, sheet.id).then(() => { - setPublishText(publishState.posted); - }).catch(error => { - setPublishText(error.message); - }) + try { + await postSheet(sheet); + setPublishText(publishState.posted); + } catch (error) { + setPublishText(`Error: ${error.message}`); + } } useEffect( () => { const toggle = async () => { From 57ae1dcb7ee8d99d7e7cffed907a252e2221e90f Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 15 Dec 2024 15:52:55 +0200 Subject: [PATCH 28/32] chore: pass handleCollectionsChange from Sheet to Editor to SheetOptions --- static/js/Editor.jsx | 3 ++- static/js/sheets/Sheet.jsx | 18 ++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/static/js/Editor.jsx b/static/js/Editor.jsx index 8092585098..d001e72d0d 100644 --- a/static/js/Editor.jsx +++ b/static/js/Editor.jsx @@ -2955,9 +2955,10 @@ const SefariaEditor = (props) => { sheetID={sheet.id} postSheet={postSheet} historyObject={props.historyObject} - editable={props.editable} + editable={true} authorUrl={sheet.ownerProfileUrl} status={status} + handleCollectionsChange={props.handleCollectionsChange} />; return (
diff --git a/static/js/sheets/Sheet.jsx b/static/js/sheets/Sheet.jsx index beae7026da..b7e5023cda 100644 --- a/static/js/sheets/Sheet.jsx +++ b/static/js/sheets/Sheet.jsx @@ -74,17 +74,10 @@ class Sheet extends Component { } } handleCollectionsChange() { - // when editing a sheet and user selects through SheetOptions to change the status of the collections for the sheet, - // update the user's collections and sheet cache. need to forceUpdate because sheet is stored not in this component's state - // but rather in Sefaria module's cache - Promise.all([ - Sefaria.getUserCollections(Sefaria._uid), - Sefaria.getUserCollectionsForSheet(this.props.id) - ]) - .then(() => { - Sefaria.sheets._loadSheetByID[this.props.id].collections = Sefaria.getUserCollectionsForSheetFromCache(this.props.id); - this.forceUpdate(); - }); + // when editing a sheet and user makes (through SheetOptions) a change in sheet's collections data we need to forceUpdate because + // sheet is stored not in this component's state but rather in Sefaria module's cache + Sefaria.sheets._loadSheetByID[this.props.id].collections = Sefaria.getUserCollectionsForSheetFromCache(this.props.id); + this.forceUpdate(); } render() { @@ -117,7 +110,8 @@ class Sheet extends Component { divineNameReplacement={this.props.divineNameReplacement} toggleSignUpModal={this.props.toggleSignUpModal} historyObject={this.props.historyObject} - editable={editable} + editable={true} + handleCollectionsChange={this.handleCollectionsChange} />
{sidebar} From 9512706ff9d814339f3a2e69f3aab071a25c02fd Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 15 Dec 2024 16:01:00 +0200 Subject: [PATCH 29/32] chore: css fix --- static/css/s2.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 6ea14a5db5..2d5231777f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -8092,7 +8092,7 @@ a .button:hover { .button.small.publish { color: #666666; background-color: white; - font-weight: 600; + font-weight: 500; min-height: 31px; height: 31px; padding: 0 10px; @@ -10531,6 +10531,8 @@ span.purim-emoji img{ } .sheetContent .spacer:only-of-type.empty { line-height: inherit; + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); } .sheetItem:only-of-type.empty .SheetOutsideText:before, .sheetContent .spacer:only-of-type.empty:before { @@ -13885,8 +13887,8 @@ span.ref-link-color-3 {color: blue} } .sheetsInPanel .dropdownLinks { min-width: 50%; - margin-top: 10px; - color: #666; + margin-top: 7px; + color: var(--dark-grey); cursor: pointer; position: relative; -webkit-margin-start: 20px; From 88e543f82434d18bb147bb4996a4696b983e3e47 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Thu, 19 Dec 2024 15:30:24 +0200 Subject: [PATCH 30/32] chore: add comment --- static/js/UserProfile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/UserProfile.jsx b/static/js/UserProfile.jsx index 6658045390..2e1e07562d 100644 --- a/static/js/UserProfile.jsx +++ b/static/js/UserProfile.jsx @@ -574,7 +574,7 @@ const UserBackground = ({profile: p, showBio}) => { ); } - const aboutMe =
; + const aboutMe =
; // aboutMe is the only field in profile with HTML const subTitle =
{p.position} {p.position && p.organization ? {Sefaria._(" at ")} : null} From 8c73eb89c5278e00a64c5f18a7f3e95d828cc609 Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Sun, 22 Dec 2024 09:28:25 +0200 Subject: [PATCH 31/32] chore: clarify comment --- static/js/UserProfile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/UserProfile.jsx b/static/js/UserProfile.jsx index 2e1e07562d..2254586278 100644 --- a/static/js/UserProfile.jsx +++ b/static/js/UserProfile.jsx @@ -574,7 +574,7 @@ const UserBackground = ({profile: p, showBio}) => { ); } - const aboutMe =
; // aboutMe is the only field in profile with HTML + const aboutMe =
; // Bio/"About Me" is the only field in profile with HTML const subTitle =
{p.position} {p.position && p.organization ? {Sefaria._(" at ")} : null} From feae1110f8e7a24e2b9650953fc26e04ef794fce Mon Sep 17 00:00:00 2001 From: stevekaplan123 Date: Mon, 30 Dec 2024 12:04:59 +0200 Subject: [PATCH 32/32] chore: menuIcon to button --- static/js/sheets/SheetOptions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sheets/SheetOptions.jsx b/static/js/sheets/SheetOptions.jsx index 109ac8b000..e14f81bc3c 100644 --- a/static/js/sheets/SheetOptions.jsx +++ b/static/js/sheets/SheetOptions.jsx @@ -87,7 +87,7 @@ const SheetOptions = ({historyObject, toggleSignUpModal, sheetID, authorUrl, edi return ( <> {editable && status === 'unlisted' && publishModalButton} - }> + }>