DevPlace CI / test (push) Failing after 2m7s
- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications - Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py` - Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views - Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES` - Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
91 lines
3.1 KiB
JavaScript
91 lines
3.1 KiB
JavaScript
// retoor <retoor@molodetz.nl>
|
|
|
|
export class ProfileEditor {
|
|
constructor() {
|
|
this.initProfileEdit();
|
|
this.initPlatformTags();
|
|
}
|
|
|
|
initProfileEdit() {
|
|
document.querySelectorAll("[data-edit-field]").forEach((btn) => {
|
|
btn.addEventListener("click", () => {
|
|
const field = btn.dataset.editField;
|
|
const display = document.getElementById(`display-${field}`);
|
|
const input = document.getElementById(`input-${field}`);
|
|
if (!display || !input) {
|
|
return;
|
|
}
|
|
display.classList.toggle("hidden");
|
|
input.classList.toggle("hidden");
|
|
if (!input.classList.contains("hidden")) {
|
|
input.focus();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
initPlatformTags() {
|
|
const platformsInput = document.getElementById("platforms-input");
|
|
const hiddenInput = document.getElementById("platforms");
|
|
const tagsContainer = document.getElementById("platforms-tags");
|
|
if (!platformsInput || !hiddenInput || !tagsContainer) return;
|
|
|
|
const addPlatform = (val) => {
|
|
val = val.trim();
|
|
if (!val) return;
|
|
const tag = document.createElement("span");
|
|
tag.className = "platform-tag";
|
|
tag.textContent = val;
|
|
tag.dataset.value = val;
|
|
const remove = document.createElement("button");
|
|
remove.type = "button";
|
|
remove.textContent = "x";
|
|
remove.setAttribute("aria-label", `Remove ${val}`);
|
|
remove.style.marginLeft = "4px";
|
|
remove.style.fontSize = "0.75rem";
|
|
remove.style.padding = "0";
|
|
remove.style.background = "none";
|
|
remove.style.border = "none";
|
|
remove.style.color = "inherit";
|
|
remove.style.cursor = "pointer";
|
|
remove.addEventListener("click", () => {
|
|
tag.remove();
|
|
updatePlatforms();
|
|
});
|
|
tag.appendChild(remove);
|
|
tagsContainer.appendChild(tag);
|
|
platformsInput.value = "";
|
|
updatePlatforms();
|
|
};
|
|
|
|
const updatePlatforms = () => {
|
|
const values = [];
|
|
tagsContainer.querySelectorAll(".platform-tag").forEach((t) => {
|
|
values.push(t.dataset.value);
|
|
});
|
|
hiddenInput.value = values.join(",");
|
|
};
|
|
|
|
const seeded = hiddenInput.value;
|
|
hiddenInput.value = "";
|
|
seeded
|
|
.split(",")
|
|
.map((value) => value.trim())
|
|
.filter(Boolean)
|
|
.forEach(addPlatform);
|
|
|
|
platformsInput.addEventListener("keydown", (e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
addPlatform(platformsInput.value);
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll(".platform-preset").forEach((btn) => {
|
|
btn.addEventListener("click", () => {
|
|
addPlatform(btn.dataset.platform);
|
|
});
|
|
});
|
|
}
|
|
}
|