219 lines
6.0 KiB
JavaScript
219 lines
6.0 KiB
JavaScript
const loginLink = document.querySelector("#login-link");
|
|
const logoutButton = document.querySelector("#logout-button");
|
|
const userChip = document.querySelector("#user-chip");
|
|
const loggedOutPanel = document.querySelector("#logged-out-panel");
|
|
const appPanel = document.querySelector("#app-panel");
|
|
const linksSection = document.querySelector("#links-section");
|
|
const form = document.querySelector("#shorten-form");
|
|
const input = document.querySelector("#url");
|
|
const button = document.querySelector("#submit-button");
|
|
const alertBox = document.querySelector("#alert");
|
|
const result = document.querySelector("#result");
|
|
const shortUrl = document.querySelector("#short-url");
|
|
const longUrl = document.querySelector("#long-url");
|
|
const copyButton = document.querySelector("#copy-button");
|
|
const refreshButton = document.querySelector("#refresh-button");
|
|
const linksList = document.querySelector("#links-list");
|
|
const emptyState = document.querySelector("#empty-state");
|
|
const template = document.querySelector("#link-template");
|
|
|
|
let currentUser = null;
|
|
|
|
async function api(path, options = {}) {
|
|
const response = await fetch(path, {
|
|
...options,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(options.headers || {}),
|
|
},
|
|
});
|
|
|
|
if (response.status === 204) {
|
|
return null;
|
|
}
|
|
|
|
const payload = await response.json().catch(() => ({}));
|
|
|
|
if (!response.ok) {
|
|
throw new Error(payload.error || "Die Anfrage ist fehlgeschlagen.");
|
|
}
|
|
|
|
return payload;
|
|
}
|
|
|
|
function showError(message) {
|
|
alertBox.textContent = message;
|
|
alertBox.hidden = false;
|
|
}
|
|
|
|
function clearError() {
|
|
alertBox.textContent = "";
|
|
alertBox.hidden = true;
|
|
}
|
|
|
|
function setLoading(isLoading) {
|
|
button.disabled = isLoading;
|
|
button.textContent = isLoading ? "Kürze..." : "Kürzen";
|
|
}
|
|
|
|
function setAuthenticated(user) {
|
|
currentUser = user.authenticated ? user : null;
|
|
|
|
if (currentUser) {
|
|
userChip.textContent = currentUser.email
|
|
? `${currentUser.username} · ${currentUser.email}`
|
|
: currentUser.username;
|
|
userChip.hidden = false;
|
|
logoutButton.hidden = false;
|
|
loginLink.hidden = true;
|
|
loggedOutPanel.hidden = true;
|
|
appPanel.hidden = false;
|
|
linksSection.hidden = false;
|
|
loadLinks();
|
|
} else {
|
|
userChip.hidden = true;
|
|
logoutButton.hidden = true;
|
|
loginLink.hidden = false;
|
|
loggedOutPanel.hidden = false;
|
|
appPanel.hidden = true;
|
|
linksSection.hidden = true;
|
|
}
|
|
}
|
|
|
|
async function loadMe() {
|
|
const user = await api("/api/me");
|
|
setAuthenticated(user);
|
|
}
|
|
|
|
async function loadLinks() {
|
|
if (!currentUser) return;
|
|
|
|
linksList.innerHTML = "";
|
|
emptyState.hidden = true;
|
|
|
|
try {
|
|
const links = await api("/api/links");
|
|
emptyState.hidden = links.length !== 0;
|
|
links.forEach(renderLink);
|
|
} catch (error) {
|
|
showError(error.message);
|
|
}
|
|
}
|
|
|
|
function renderLink(link) {
|
|
const node = template.content.cloneNode(true);
|
|
const card = node.querySelector(".link-card");
|
|
const short = node.querySelector(".link-short");
|
|
const long = node.querySelector(".link-long");
|
|
const form = node.querySelector(".edit-form");
|
|
const editInput = node.querySelector(".edit-input");
|
|
const saveButton = node.querySelector(".save-button");
|
|
const copyButton = node.querySelector(".copy-link-button");
|
|
const deleteButton = node.querySelector(".delete-button");
|
|
|
|
short.href = link.short_url;
|
|
short.textContent = link.short_url;
|
|
long.textContent = link.long_url;
|
|
editInput.value = link.long_url;
|
|
|
|
form.addEventListener("submit", async (event) => {
|
|
event.preventDefault();
|
|
clearError();
|
|
saveButton.disabled = true;
|
|
saveButton.textContent = "Speichere...";
|
|
|
|
try {
|
|
const updated = await api(`/api/links/${encodeURIComponent(link.code)}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ url: editInput.value.trim() }),
|
|
});
|
|
|
|
long.textContent = updated.long_url;
|
|
editInput.value = updated.long_url;
|
|
saveButton.textContent = "Gespeichert";
|
|
setTimeout(() => {
|
|
saveButton.textContent = "Speichern";
|
|
}, 1200);
|
|
} catch (error) {
|
|
showError(error.message);
|
|
saveButton.textContent = "Speichern";
|
|
} finally {
|
|
saveButton.disabled = false;
|
|
}
|
|
});
|
|
|
|
copyButton.addEventListener("click", async () => {
|
|
await copyToClipboard(link.short_url, copyButton);
|
|
});
|
|
|
|
deleteButton.addEventListener("click", async () => {
|
|
const ok = confirm("Diesen Kurzlink wirklich löschen?");
|
|
if (!ok) return;
|
|
|
|
clearError();
|
|
try {
|
|
await api(`/api/links/${encodeURIComponent(link.code)}`, { method: "DELETE" });
|
|
card.remove();
|
|
if (!linksList.children.length) emptyState.hidden = false;
|
|
} catch (error) {
|
|
showError(error.message);
|
|
}
|
|
});
|
|
|
|
linksList.appendChild(node);
|
|
}
|
|
|
|
async function copyToClipboard(value, targetButton) {
|
|
try {
|
|
await navigator.clipboard.writeText(value);
|
|
const old = targetButton.textContent;
|
|
targetButton.textContent = "Kopiert!";
|
|
setTimeout(() => {
|
|
targetButton.textContent = old;
|
|
}, 1500);
|
|
} catch {
|
|
showError("Kopieren ist fehlgeschlagen. Bitte kopiere den Link manuell.");
|
|
}
|
|
}
|
|
|
|
form.addEventListener("submit", async (event) => {
|
|
event.preventDefault();
|
|
clearError();
|
|
result.hidden = true;
|
|
setLoading(true);
|
|
|
|
try {
|
|
const payload = await api("/api/shorten", {
|
|
method: "POST",
|
|
body: JSON.stringify({ url: input.value.trim() }),
|
|
});
|
|
|
|
shortUrl.textContent = payload.short_url;
|
|
shortUrl.href = payload.short_url;
|
|
longUrl.textContent = payload.long_url;
|
|
result.hidden = false;
|
|
copyButton.textContent = "Kopieren";
|
|
input.value = "";
|
|
await loadLinks();
|
|
} catch (error) {
|
|
showError(error.message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
});
|
|
|
|
copyButton.addEventListener("click", async () => {
|
|
await copyToClipboard(shortUrl.href, copyButton);
|
|
});
|
|
|
|
refreshButton.addEventListener("click", loadLinks);
|
|
|
|
logoutButton.addEventListener("click", async () => {
|
|
await fetch("/auth/logout", { method: "POST" });
|
|
window.location.href = "/";
|
|
});
|
|
|
|
loadMe().catch((error) => {
|
|
console.error(error);
|
|
});
|