fix: more robust cookie management for plugins requiring launch
Instead of storing a cookie for every plugin that requires launch, we create a single cookie with '+' separated value. We make use of the cookieStore native API (available everywhere since June 2025) to access cookie data. The variables are renamed to be more explicit. We now use class-based SCSS for styling, instead of manually setting style.display attribute.
This commit is contained in:
parent
070f3503c5
commit
097be3e3fe
@ -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
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block warning %}
|
||||
<div id="warning-main">
|
||||
<div id="warning-launch-required-main">
|
||||
<img src="{{ url_for('static', filename='img/Featured icon.svg')}}" alt="">
|
||||
<span>Changes have been made to some plugins that will only take effect after running launch platform.</span>
|
||||
</div>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
{% from '_switch.html' import switch %}
|
||||
|
||||
{% for plugin in plugins %}
|
||||
<div class="installed-plugin" hx-get="{{ url_for('plugin', name=plugin.name) }}" hx-push-url="true">
|
||||
<div class="installed-plugin" hx-get="{{ url_for('plugin', name=plugin.name) }}" hx-push-url="true" data-plugin="{{ plugin.name }}">
|
||||
<div class="details">
|
||||
<div class="name"><a href="{{ url_for('plugin', name=plugin.name) }}">{{ plugin.name }}</a></div>
|
||||
<div class="author">By {{ plugin.author }}</div>
|
||||
<div class="description">{{ plugin.description|safe }}</div>
|
||||
</div>
|
||||
<div class="warning" hx-preserve="true" id="warning-cookie-{{plugin.name}}">
|
||||
<div class="warning-launch-required" hx-preserve="true">
|
||||
<img src="{{ url_for('static', filename='img/Featured icon.svg')}}" alt="" title="Run launch platform for changes to this plugin to take effect">
|
||||
</div>
|
||||
|
||||
|
||||
@ -22,17 +22,6 @@ View all your installed plugins in one place.
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Sets the warning that launch platform needs to be executed for plugins to take effect
|
||||
function SetWarning(){
|
||||
const warningElements = document.querySelectorAll('[id^="warning-cookie-"]');
|
||||
const warningMain = document.getElementById('warning-main');
|
||||
warningElements.forEach(function(warningElement) {
|
||||
if (document.cookie.includes(warningElement.id)) {
|
||||
warningElement.style.display = 'flex';
|
||||
warningMain.style.display = 'flex';
|
||||
}
|
||||
});
|
||||
}
|
||||
document.body.addEventListener('htmx:afterOnLoad', function(event) {
|
||||
let toggleSwitches = document.querySelectorAll(".switch");
|
||||
toggleSwitches.forEach(toggleSwitch => {
|
||||
@ -42,7 +31,6 @@ View all your installed plugins in one place.
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
SetWarning();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user