(() => { const self = reg_ns("me", ["streams"]); self.LOGIN_ACCOUNT_TOKENS = JSON.parse( window.localStorage.getItem("atto:login_account_tokens") || "{}", ); self.define("logout", async () => { if ( !(await trigger("atto::confirm", [ "Are you sure you would like to do this?", ])) ) { return; } fetch("/api/v1/auth/logout", { method: "POST", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); if (res.ok) { delete self.LOGIN_ACCOUNT_TOKENS[res.payload]; self.set_login_account_tokens(self.LOGIN_ACCOUNT_TOKENS); const next = Object.entries(self.LOGIN_ACCOUNT_TOKENS)[0]; if (next) { self.login(next[0]); } else { setTimeout(() => { window.location.href = "/"; }, 150); } } }); }); self.define("remove_post", async (_, id) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/posts/${id}`, { method: "DELETE", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("purge_post", async (_, id) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this? This cannot be undone.", ])) ) { return; } fetch(`/api/v1/posts/${id}/purge`, { method: "DELETE", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("restore_post", async (_, id) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/posts/${id}/restore`, { method: "POST", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("vote", async (_, id, option) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/posts/${id}/poll_vote`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ option, }), }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); if (res.ok) { window.location.href = `/post/${id}`; } }); }); self.define("update_open", async (_, id, status) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/posts/${id}/open`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ open: status }), }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("react", async (_, element, asset, asset_type, is_like) => { await trigger("atto::debounce", ["reactions::toggle"]); fetch("/api/v1/reactions", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ asset, asset_type, is_like, }), }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); if (res.ok) { const like = element.parentElement.querySelector( '[hook_element="reaction.like"]', ); const dislike = element.parentElement.querySelector( '[hook_element="reaction.dislike"]', ); if (is_like) { like.classList.add("green"); like.querySelector("svg").classList.add("filled"); dislike.classList.remove("red"); } else { dislike.classList.add("red"); like.classList.remove("green"); like.querySelector("svg").classList.remove("filled"); } } }); }); self.define("remove_notification", (_, id) => { fetch(`/api/v1/notifications/${id}`, { method: "DELETE", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("update_notification_read_status", (_, id, read) => { fetch(`/api/v1/notifications/${id}/read_status`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ read, }), }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("clear_notifs", async () => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch("/api/v1/notifications/my", { method: "DELETE", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define( "repost", (_, id, content, community, do_not_redirect = false) => { return new Promise((resolve, _) => { fetch(`/api/v1/posts/${id}/repost`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ content, community, }), }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); if (res.ok) { if (!do_not_redirect) { setTimeout(() => { window.location.href = `/post/${res.payload}`; }, 100); } resolve(res.payload); } }); }); }, ); self.define("report", (_, asset, asset_type) => { window.open( `/mod_panel/file_report?asset=${asset}&asset_type=${asset_type}`, ); }); self.define("seen", () => { fetch("/api/v1/auth/user/me/seen", { method: "POST", }) .then((res) => res.json()) .then((res) => { if (!res.ok) { trigger("atto::toast", ["error", res.message]); } }); }); self.define("remove_question", async (_, id) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/questions/${id}`, { method: "DELETE", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("ip_block_question", async (_, id) => { if ( !(await trigger("atto::confirm", [ "Are you sure you want to do this?", ])) ) { return; } fetch(`/api/v1/questions/${id}/block_ip`, { method: "POST", }) .then((res) => res.json()) .then((res) => { trigger("atto::toast", [ res.ok ? "success" : "error", res.message, ]); }); }); self.define("notifications_stream", ({ _, streams }) => { const element = document.getElementById("notifications_span"); streams.subscribe("notifs"); streams.event("notifs", "message", (data) => { if (!data.method.Packet) { console.warn("notifications stream cannot read this message"); return; } const inner_data = JSON.parse(data.data); if (data.method.Packet.Crud === "Create") { const current = Number.parseInt(element.innerText || "0"); if (current <= 0) { element.classList.remove("hidden"); } element.innerText = current + 1; // check if we're already connected const connected = window.sessionStorage.getItem("atto:connected/notifs") === "true"; if (connected) { return; } window.sessionStorage.setItem("atto:connected/notifs", "true"); // send notification const enabled = window.localStorage.getItem("atto:notifs_enabled") === "true"; if (Notification.permission === "granted" && enabled) { // try to pull notification user const matches = /\/api\/v1\/auth\/user\/find\/(\d*)/.exec( inner_data.content, ); // ... new Notification(inner_data.title, { body: inner_data.content, icon: matches[1] ? `/api/v1/auth/user/${matches[1]}/avatar?selector_type=id` : "/public/favicon.svg", lang: "en-US", }); console.info("notification created"); } } else if (data.method.Packet.Crud === "Delete") { const current = Number.parseInt(element.innerText || "0"); if (current - 1 <= 0) { element.classList.add("hidden"); } element.innerText = current - 1; } else { console.warn("correct packet type but with wrong data"); } }); }); self.define("notifications_button", (_, element) => { if (Notification.permission === "granted") { let enabled = window.localStorage.getItem("atto:notifs_enabled") === "true"; function text() { if (!enabled) { element.innerText = "Enable notifications"; } else { element.innerText = "Disable notifications"; } } element.addEventListener("click", () => { enabled = !enabled; window.localStorage.setItem("atto:notifs_enabled", enabled); text(); }); text(); } else if (Notification.permission !== "denied") { element.innerText = "Enable notifications"; element.addEventListener("click", () => { Notification.requestPermission().then((permission) => { if (permission === "granted") { window.localStorage.setItem( "atto:notifs_enabled", "true", ); window.location.reload(); } else { alert( "Permission denied! You must allow this permission for browser notifications.", ); } }); }); } }); self.define("emojis", async () => { const payload = (await (await fetch("/api/v1/my_emojis")).json()) .payload; const out = []; for (const [community, [category, emojis]] of Object.entries(payload)) { for (const emoji of emojis) { out.push({ category, name: emoji.name, shortcodes: [`${community}.${emoji.name}`], url: `/api/v1/communities/${community}/emojis/${emoji.name}`, }); } } return out; }); // token switcher self.define("append_associations", (_, tokens) => { fetch("/api/v1/auth/user/me/append_associations", { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ tokens, }), }) .then((res) => res.json()) .then((res) => { if (res.ok) { console.log("associations sent"); } else { console.warn(res.message); } }); }); self.define( "set_login_account_tokens", ({ $ }, value) => { $.LOGIN_ACCOUNT_TOKENS = value; window.localStorage.setItem( "atto:login_account_tokens", JSON.stringify(value), ); }, ["object"], ); self.define("login", (_, username) => { const token = self.LOGIN_ACCOUNT_TOKENS[username]; if (!token) { return; } self.append_associations([token]); setTimeout(() => { window.location.href = `/api/v1/auth/token?token=${token}`; }, 150); }); self.define("remove_token", async (_, username) => { if ( !(await trigger("atto::confirm", [ "Are you sure you would like to do this?", ])) ) { return; } delete self.LOGIN_ACCOUNT_TOKENS[username]; self.set_login_account_tokens(self.LOGIN_ACCOUNT_TOKENS); trigger("atto::toast", ["success", "Token removed"]); }); self.define("render_token_picker", ({ $ }, element) => { element.innerHTML = ""; for (const token of Object.entries($.LOGIN_ACCOUNT_TOKENS)) { element.innerHTML += `