quiver icon indicating copy to clipboard operation
quiver copied to clipboard

Auto-save setting

Open enjoysmath opened this issue 3 years ago • 12 comments

I'm new to web dev here.

Please give a brief sketch of how I can add this feature to Quiver or alternatively put this feature in my own app in which I have Quiver embedded:

image

enjoysmath avatar Jan 08 '21 23:01 enjoysmath

Look at the code invoked by the "Save" button. https://github.com/varkor/quiver/blob/c567eb61a3dca41e8b95d50009f67f0d2da37192/src/ui.js#L5166-L5176 This saves the current diagram to the URL. You could save the diagram every time the diagram changes: I would probably do this every time an action takes place as recorded by the history (i.e. undo/redo) system, e.g. before or after these lines. https://github.com/varkor/quiver/blob/c567eb61a3dca41e8b95d50009f67f0d2da37192/src/ui.js#L3274-L3277 If you didn't want to update the URL, you could just as easily save it to localStorage instead.

varkor avatar Jan 09 '21 00:01 varkor

Regarding adding this feature to quiver itself, I am open to this feature, but would need to work out what the best UI would be, e.g. a setting to enable/disable automatic saving (as I personally prefer to manually save diagrams, which gives me finer control). So there aren't any global editor settings, so I'm not immediate sure where it would go, but I'm open to suggestions.

varkor avatar Jan 09 '21 00:01 varkor

@varkor I would put it as a checkbox on the bottom button control widget "Auto-save [/]" and when the user unchecks it, it turns into a button + a checkbox. The button is for manually saving. Reason to put in the bottom button panel is the top one is perfectly filled already, and it's the bottom one that has the vacancies.

Do you mind if I try adding it in my version of quiver and then notifying you here when it's ready? Then you can show me the process to work on quiver with you and I can learn some more webdev skills. Your code is very well organized in my experience of reading others' code. It's also very well commented. Not huge overbearing comments, but ones that perfectly describe what the code is doing and are concise.

enjoysmath avatar Jan 09 '21 01:01 enjoysmath

You're very welcome to experiment with this so that we can see how it works in practice and figure out the best user experience. Let me know if you have any questions about the implementation — I'm glad you find the comments helpful: hopefully they'll be enough to get you started.

varkor avatar Jan 09 '21 01:01 varkor

cc https://github.com/varkor/quiver/issues/13, which essentially also requested this feature, but was closed after manual saving was introduced.

varkor avatar Jan 09 '21 01:01 varkor

@varkor, do you recommend raw javascript cookies as in: https://www.w3schools.com/js/js_cookies.asp

Or should I use something more sophisticated, and what would that be?

enjoysmath avatar Jan 09 '21 01:01 enjoysmath

For quiver itself, I think the URL itself should be updated, just like the save button does currently. I.e. when the auto-save option is enabled, it should act like the save button is clicked every time the user makes a change.

varkor avatar Jan 09 '21 01:01 varkor

@varkor, Here's the code mods:

        const add_action = (name, combinations, action, type="button") => {
            const shortcut_name = Shortcuts.name(combinations);

            var input_type = type;

            if (type == "checkbox")
                input_type = "input";
            
            const button = new DOM.Element(input_type, { class: "action", "data-name": name })
                .add(new DOM.Element("span", { class: "symbol" }).add(
                    new DOM.Element("img", { src: `icons/${
                        name.toLowerCase().replace(/ /g, "-")
                    }.svg` })
                ))
                .add(new DOM.Element("span", { class: "name" }).add(name))
                .add(new DOM.Element("span", { class: "shortcut" }).add(shortcut_name))
                .listen(pointer_event("down"), (event) => {
                    if (event.button === 0) {
                        event.stopImmediatePropagation();
                    }
                });
            
            if (type == "checkbox")
                button.set_attributes({"type": "checkbox"});

            const trigger_action_and_update_toolbar = (event) => {
                action.call(button, event);
                ui.toolbar.update(ui);
            };

            if (type == "checkbox")
                button.listen("onclick", trigger_action_and_update_toolbar);
            else
                button.listen("click", trigger_action_and_update_toolbar);

            ui.shortcuts.add(combinations, trigger_action_and_update_toolbar, button);

            this.element.add(button);
            return button;
        };

        // Add all of the toolbar buttons.

        // "Saving" updates the URL to reflect the current diagram.
        add_action(
            "Auto-Save",
            [{key: "T", modifier: true, context: Shortcuts.SHORTCUT_PRIORITY.Always }],
            () => {
                autosave_check = self.element.querySelectorAll('[data-name="Auto-Save"]');
                save_action = self.element.querySelectorAll('[data-name="Save"]');

                if (autosave_check.checked)                     
                    save_action.disabled = true;
                else 
                    save_action.disabled = false;
            },
            "checkbox"
        );

where I tried to re-use your add_action function as much as possible. However, I'm not getting the action code to execute when the checkbox is toggled. Any idea what to pass in instead of "onclick"? I've also tried "checked".

enjoysmath avatar Jan 09 '21 02:01 enjoysmath

Checkboxes should respond to the "change" event.

varkor avatar Jan 09 '21 02:01 varkor

@varkor I'm aware of some issues with above code, I'm fixing them now.

enjoysmath avatar Jan 09 '21 03:01 enjoysmath

@varkor

Here is the updated code:


        const add_action = (name, combinations, action, type) => {
            const shortcut_name = Shortcuts.name(combinations);

            var elem_type = type;

            if (type === "checkbox")
                elem_type = "input";
            
            const button = new DOM.Element(elem_type, { class: "action", "data-name": name })
                .add(new DOM.Element("span", { class: "symbol" }).add(
                    new DOM.Element("img", { src: `icons/${
                        name.toLowerCase().replace(/ /g, "-")
                    }.svg` })
                ))
                .add(new DOM.Element("span", { class: "name" }).add(name))
                .add(new DOM.Element("span", { class: "shortcut" }).add(shortcut_name))
                .listen(pointer_event("down"), (event) => {
                    if (event.button === 0) {
                        event.stopImmediatePropagation();
                    }
                });
            
            if (type == "checkbox")
                button.setAttribute("type", "checkbox");

            const trigger_action_and_update_toolbar = (event) => {
                action.call(button, event);
                ui.toolbar.update(ui);
            };

            var event_name = null;

            if (type === "checkbox") 
                event_name = "change";                
            else
                event_name = "click";

            button.listen(event_name, trigger_action_and_update_toolbar);

            ui.shortcuts.add(combinations, trigger_action_and_update_toolbar, button);

            this.element.add(button);
            return button;
        };

        // Add all of the toolbar buttons.

        // "Saving" updates the URL to reflect the current diagram.
        this.save_button = add_action(
            "Save",
            [{ key: "S", modifier: true, context: Shortcuts.SHORTCUT_PRIORITY.Always }],
            () => {
                // For now, we do not include macro information in the URL.
                const { data } = ui.quiver.export("base64");
                // `data` is the new URL.
                history.pushState({}, "", data);
            },
        );

        // Perform the above saving action every change in the edit history automatically
        add_action(
            "Auto-Save",
            [{key: "T", modifier: true, context: Shortcuts.SHORTCUT_PRIORITY.Always }],
            (button, event) => {
                this.save_button.disabled = button.checked;
            },
            "checkbox"
        );



dom.js:122 Uncaught TypeError: Cannot read property 'setAttribute' of undefined
    at DOM.Element.set_attributes (dom.js:122)
    at new DOM.Element (dom.js:37)
    at add_action (ui.js:5027)
    at Toolbar.initialise (ui.js:5067)
    at UI.initialise (ui.js:658)
    at HTMLDocument.<anonymous> (ui.js:5975)

But I'm having the above issue in Chrome. I'm unable to use setAttribute. How, in this modern framework should I be making an "input" DOM.Element into a checkbox?

enjoysmath avatar Jan 09 '21 03:01 enjoysmath

quiver has a custom API for DOM elements, which you can find in dom.js. To set an attribute, you would write button.set_attributes({ type: "checkbox" });. While experimenting, you may find it easier to access the underlying element and call the usual methods on that, e.g. button.element.setAttribute("type", "checkbox");.

varkor avatar Jan 09 '21 18:01 varkor

I'm going to close this issue, as I think manual saving is sufficient.

varkor avatar Jul 29 '23 17:07 varkor