diff --git a/tutordeck/server/app.py b/tutordeck/server/app.py index 47332eb..7570e2c 100644 --- a/tutordeck/server/app.py +++ b/tutordeck/server/app.py @@ -181,13 +181,9 @@ async def plugin_toggle(name: str) -> Response: ), ) if enable_plugin: - response.set_cookie( - f"{constants.WARNING_COOKIE_PREFIX}-{name}", - "requires launch", - max_age=constants.ONE_MONTH, - ) + update_plugins_requiring_launch(response, add=name) else: - response.delete_cookie(f"{constants.WARNING_COOKIE_PREFIX}-{name}") + update_plugins_requiring_launch(response, remove=name) return response @@ -354,3 +350,41 @@ async def command() -> WerkzeugResponse: command_args = command_string.split() tutorclient.CliPool.run_parallel(app, command_args) return redirect(url_for("advanced")) + + +def update_plugins_requiring_launch( + response: Response, add: str | None = None, remove: str | None = None +) -> None: + """ + Store the list of plugins for which a recent set of changes require running "local launch". + + This list is stored as a "+"-separated string in a cookie. Note that flask will automatically put the content in quotes. + """ + # Note that comma, colon and semi-colon are not supported in cookie values + separator = "+" + + # Get current plugins + names = set( + [ + cookie + for cookie in request.cookies.get( + constants.PLUGINS_REQUIRE_LAUNCH_COOKIE_NAME, "" + ).split(separator) + if cookie + ] + ) + + # Add new plugins + if add: + names.add(add) + + # Remove plugins + if remove: + names.discard(remove) + + # Update the response + response.set_cookie( + constants.PLUGINS_REQUIRE_LAUNCH_COOKIE_NAME, + separator.join(sorted(names)), + max_age=60 * 60 * 24 * 30, # 1 month + ) diff --git a/tutordeck/server/constants.py b/tutordeck/server/constants.py index df876bb..3960906 100644 --- a/tutordeck/server/constants.py +++ b/tutordeck/server/constants.py @@ -1,4 +1,3 @@ SHORT_SLEEP_SECONDS = 0.1 -ONE_MONTH = 60 * 60 * 24 * 30 -WARNING_COOKIE_PREFIX = "warning-cookie" +PLUGINS_REQUIRE_LAUNCH_COOKIE_NAME = "plugins-require-launch" ITEMS_PER_PAGE = 100 diff --git a/tutordeck/server/static/js/deck.js b/tutordeck/server/static/js/deck.js index 2e77cce..8b0dd17 100644 --- a/tutordeck/server/static/js/deck.js +++ b/tutordeck/server/static/js/deck.js @@ -1,26 +1,20 @@ -function setCookie(name, value, days) { - let expires = ""; - if (days) { - let date = new Date(); - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); - expires = "; expires=" + date.toUTCString(); +// Handle plugins requiring launch based on the values in the corresponding cookie +const pluginsRequireLaunchCookieName = "plugins-require-launch"; +async function displayPluginsRequireLaunchWarning() { + const cookie = await cookieStore.get(pluginsRequireLaunchCookieName); + if (cookie && cookie.value) { + const cookieValue = cookie.value.slice(1, -1); // remove quotes + cookieValue.split('+').map(s => s.trim()).forEach(plugin => { + document.querySelectorAll(`[data-plugin="${plugin}"] .warning-launch-required`).forEach(element => { + element.classList.add("visible"); + document.getElementById('warning-launch-required-main').classList.add("visible"); + }); + }); } - document.cookie = `${name}=${value || ""}${expires}; path=/`; -} -function getCookie(name) { - let nameEQ = name + "="; - return ( - document.cookie - .split(";") - .map((cookie) => cookie.trim()) - .find((cookie) => cookie.startsWith(nameEQ)) - ?.slice(nameEQ.length) || null - ); -} -function eraseCookie(name) { - document.cookie = - name + "=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; } +document.body.addEventListener('htmx:afterOnLoad', function(event) { + displayPluginsRequireLaunchWarning(); +}); // Handle modal const modalContainer = document.getElementById("modal_container"); @@ -43,15 +37,11 @@ closeToastButtons.forEach((button) => { hideToast(toast); }); }); -function showToast() { +function showLaunchSuccessfulToast() { + // TODO this is very brittle because it relies on static variables and string values. if (toast) { if (toastTitle === "Launch platform was successfully executed") { - document.cookie.split(";").forEach((cookie) => { - let name = cookie.split("=")[0].trim(); - if (name.startsWith("warning-cookie")) { - eraseCookie(name); - } - }); + cookieStore.delete(pluginsRequireLaunchCookieName); } toast.style.display = "flex"; setTimeout(() => { diff --git a/tutordeck/server/static/js/logs.js b/tutordeck/server/static/js/logs.js index 8cc4a39..bd1a5b4 100644 --- a/tutordeck/server/static/js/logs.js +++ b/tutordeck/server/static/js/logs.js @@ -51,10 +51,11 @@ htmx.on("htmx:sseBeforeMessage", function (evt) { activateInputs(); // There are certain commands for which we do not show the toast message // Only show the toast if it was set in the `setToastContent` function and if the command ran successfully + // TODO this is brittle because it relies on a hard-coded "Success!" string that is sent from the backend. if (data.stdout.includes("Success!")) { setToastContent(command); if (toastTitle.textContent.trim()) { - showToast("info"); + showLaunchSuccessfulToast(); } } if (onPluginPage) { diff --git a/tutordeck/server/static/scss/deck.scss b/tutordeck/server/static/scss/deck.scss index 59e1836..cdef3e5 100644 --- a/tutordeck/server/static/scss/deck.scss +++ b/tutordeck/server/static/scss/deck.scss @@ -343,8 +343,11 @@ main { } } } - #warning-main { + #warning-launch-required-main { display: none; + &.visible { + display: flex; + } border: 1px solid $gray-2; border-radius: 0.5em; align-items: center; @@ -445,8 +448,11 @@ main { } } } - .warning { + .warning-launch-required { display: none; + &.visible { + display: flex; + } margin-right: 2em; img { width: 2.5em; diff --git a/tutordeck/server/templates/_base_header.html b/tutordeck/server/templates/_base_header.html index 3d9a611..707b36d 100644 --- a/tutordeck/server/templates/_base_header.html +++ b/tutordeck/server/templates/_base_header.html @@ -14,12 +14,12 @@
{% endblock %} {% block warning %} -
+
Changes have been made to some plugins that will only take effect after running launch platform.
diff --git a/tutordeck/server/templates/_plugin_installed_list.html b/tutordeck/server/templates/_plugin_installed_list.html index a47896e..5d7ed49 100644 --- a/tutordeck/server/templates/_plugin_installed_list.html +++ b/tutordeck/server/templates/_plugin_installed_list.html @@ -1,13 +1,13 @@ {% from '_switch.html' import switch %} {% for plugin in plugins %} -
+
By {{ plugin.author }}
{{ plugin.description|safe }}
-