DEV: Migrate to local storage
Setting cookies means that they're sent in the request headers for every HTTP request. This will have a (tiny) impact on performance, plus it can raise privacy concerns. Using localStorage is more appropriate for this use case. This commit includes migration logic for any previously-saved values. Previously the cookies were set to last for the 'session'. localStorage doesn't have an expiration mechanism, so this commit implements a 7-day expiration on the values.
This commit is contained in:
parent
6c43321b28
commit
468cf81fd2
|
@ -6,6 +6,9 @@ import cookie, { removeCookie } from "discourse/lib/cookie";
|
||||||
const VALID_TAGS =
|
const VALID_TAGS =
|
||||||
"h1, h2, h3, h4, h5, h6, p, code, blockquote, .md-table, li p";
|
"h1, h2, h3, h4, h5, h6, p, code, blockquote, .md-table, li p";
|
||||||
const DELIMITER = "=";
|
const DELIMITER = "=";
|
||||||
|
const EXPIRE_AFTER_DAYS = 7;
|
||||||
|
const EXPIRE_AFTER_SECONDS = EXPIRE_AFTER_DAYS * 24 * 60 * 60;
|
||||||
|
const STORAGE_PREFIX = "d-placeholder-";
|
||||||
|
|
||||||
function buildInput(key, placeholder) {
|
function buildInput(key, placeholder) {
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
|
@ -63,17 +66,77 @@ function buildSelect(key, placeholder) {
|
||||||
export default {
|
export default {
|
||||||
name: "discourse-placeholder-theme-component",
|
name: "discourse-placeholder-theme-component",
|
||||||
|
|
||||||
initialize() {
|
// TODO: Remove once this change has been live for a few months
|
||||||
|
migrateCookiesToKeyValueStore() {
|
||||||
|
const cookies = document.cookie.split("; ");
|
||||||
|
const oldPlaceholderCookies = [];
|
||||||
|
|
||||||
|
for (let i = 0, l = cookies.length; i < l; i++) {
|
||||||
|
let parts = cookies[i].split("=");
|
||||||
|
if (parts[0].startsWith(STORAGE_PREFIX)) {
|
||||||
|
oldPlaceholderCookies.push(parts[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of oldPlaceholderCookies) {
|
||||||
|
const value = cookie(key);
|
||||||
|
|
||||||
|
this.setValue(key, value);
|
||||||
|
removeCookie(key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
expireOldValues() {
|
||||||
|
const now = Date.now();
|
||||||
|
Object.keys(window.localStorage)
|
||||||
|
.filter((k) => k.startsWith(STORAGE_PREFIX))
|
||||||
|
.forEach((k) => {
|
||||||
|
const data = this.keyValueStore.getObject(k);
|
||||||
|
if (!data?.expires || data.expires < now) {
|
||||||
|
this.removeValue(k);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue(key) {
|
||||||
|
const data = this.keyValueStore.getObject(`${STORAGE_PREFIX}${key}`);
|
||||||
|
if (data) {
|
||||||
|
data.expires = Date.now() + EXPIRE_AFTER_SECONDS;
|
||||||
|
this.keyValueStore.setObject(`${STORAGE_PREFIX}${key}`, data);
|
||||||
|
return data.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
this.keyValueStore.setObject({
|
||||||
|
key: `${STORAGE_PREFIX}${key}`,
|
||||||
|
value: {
|
||||||
|
expires: Date.now() + EXPIRE_AFTER_SECONDS,
|
||||||
|
value: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeValue(key) {
|
||||||
|
this.keyValueStore.remove(`${STORAGE_PREFIX}${key}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize(container) {
|
||||||
|
this.keyValueStore = container.lookup("key-value-store:main");
|
||||||
|
|
||||||
|
this.migrateCookiesToKeyValueStore();
|
||||||
|
this.expireOldValues();
|
||||||
|
|
||||||
withPluginApi("0.8.7", (api) => {
|
withPluginApi("0.8.7", (api) => {
|
||||||
api.decorateCooked(
|
api.decorateCooked(
|
||||||
($cooked, postWidget) => {
|
($cooked, postWidget) => {
|
||||||
if (!postWidget) return;
|
if (!postWidget) return;
|
||||||
|
|
||||||
const postIdentifier = `d-placeholder-${postWidget.widget.attrs.topicId}-${postWidget.widget.attrs.id}-`;
|
const postIdentifier = `${postWidget.widget.attrs.topicId}-${postWidget.widget.attrs.id}-`;
|
||||||
const mappings = [];
|
const mappings = [];
|
||||||
const placeholders = {};
|
const placeholders = {};
|
||||||
|
|
||||||
function processChange(inputEvent) {
|
const processChange = (inputEvent) => {
|
||||||
const value = inputEvent.target.value;
|
const value = inputEvent.target.value;
|
||||||
const key = inputEvent.target.dataset.key;
|
const key = inputEvent.target.dataset.key;
|
||||||
const placeholder = placeholders[inputEvent.target.dataset.key];
|
const placeholder = placeholders[inputEvent.target.dataset.key];
|
||||||
|
@ -81,10 +144,10 @@ export default {
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
if (value !== placeholder.default) {
|
if (value !== placeholder.default) {
|
||||||
cookie(placeholderIdentifier, value);
|
this.setValue(placeholderIdentifier, value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
removeCookie(placeholderIdentifier);
|
this.removeValue(placeholderIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
let newValue;
|
let newValue;
|
||||||
|
@ -129,7 +192,7 @@ export default {
|
||||||
|
|
||||||
if (replaced) elem.innerHTML = newInnnerHTML;
|
if (replaced) elem.innerHTML = newInnnerHTML;
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function processPlaceholders() {
|
function processPlaceholders() {
|
||||||
mappings.length = 0;
|
mappings.length = 0;
|
||||||
|
@ -158,7 +221,7 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _fillPlaceholders() {
|
const _fillPlaceholders = () => {
|
||||||
if (Object.keys(placeholders).length > 0) {
|
if (Object.keys(placeholders).length > 0) {
|
||||||
processPlaceholders(placeholders, $cooked, mappings);
|
processPlaceholders(placeholders, $cooked, mappings);
|
||||||
|
|
||||||
|
@ -167,7 +230,7 @@ export default {
|
||||||
const placeholder = placeholders[placeholderKey];
|
const placeholder = placeholders[placeholderKey];
|
||||||
const placeholderIdentifier = `${postIdentifier}${placeholderKey}`;
|
const placeholderIdentifier = `${postIdentifier}${placeholderKey}`;
|
||||||
const value =
|
const value =
|
||||||
cookie(placeholderIdentifier) || placeholder.default;
|
this.getValue(placeholderIdentifier) || placeholder.default;
|
||||||
|
|
||||||
processChange({
|
processChange({
|
||||||
target: {
|
target: {
|
||||||
|
@ -180,7 +243,7 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const placeholderNodes = $cooked[0].querySelectorAll(
|
const placeholderNodes = $cooked[0].querySelectorAll(
|
||||||
".d-wrap[data-wrap=placeholder]:not(.placeholdered)"
|
".d-wrap[data-wrap=placeholder]:not(.placeholdered)"
|
||||||
|
@ -192,13 +255,13 @@ export default {
|
||||||
if (!dataKey) return;
|
if (!dataKey) return;
|
||||||
|
|
||||||
const placeholderIdentifier = `${postIdentifier}${dataKey}`;
|
const placeholderIdentifier = `${postIdentifier}${dataKey}`;
|
||||||
const valueFromCookie = cookie(placeholderIdentifier);
|
const valueFromStore = this.getValue(placeholderIdentifier);
|
||||||
const defaultValues = (elem.dataset.defaults || "")
|
const defaultValues = (elem.dataset.defaults || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
placeholders[dataKey] = {
|
placeholders[dataKey] = {
|
||||||
default: valueFromCookie || elem.dataset.default,
|
default: valueFromStore || elem.dataset.default,
|
||||||
defaults: defaultValues,
|
defaults: defaultValues,
|
||||||
delimiter: elem.dataset.delimiter || DELIMITER,
|
delimiter: elem.dataset.delimiter || DELIMITER,
|
||||||
description: elem.dataset.description,
|
description: elem.dataset.description,
|
||||||
|
|
Loading…
Reference in New Issue