Initial LinkLog implementation

This commit is contained in:
Olaf
2026-08-24 14:30:30 +02:00
commit 1c827956c4
50 changed files with 3436 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
(() => {
const profileForm = document.querySelector('#profile-form');
const mastodonForm = document.querySelector('#mastodon-form');
const profileLogoutButton = document.querySelector('#logout-button');
const accessToken = localStorage.getItem('linklogAccessToken');
const defaultAvatarUrl = 'https://example.com/avatar.png';
const defaultMastodonInstance = 'mastodon.social';
const defaultPostPrefix = 'From my #LinkLog: "';
function authHeaders(includeJson = false) {
return {
...(includeJson ? {'Content-Type': 'application/json'} : {}),
...(accessToken ? {Authorization: `Bearer ${accessToken}`} : {}),
};
}
function setStatus(selector, message, isError = false) {
const status = document.querySelector(selector);
status.textContent = message;
status.style.color = isError ? '#b91c1c' : '#166534';
}
async function loadProfile() {
const response = await fetch('/api/user/me', {headers: authHeaders()});
if (!response.ok) throw new Error('Could not load profile');
const profile = await response.json();
document.querySelector('#username').textContent = profile.username || '';
document.querySelector('#email').value = profile.email || '';
document.querySelector('#bio').value = profile.bio || '';
document.querySelector('#avatar-url').value = profile.avatar_url || defaultAvatarUrl;
if (profile.is_admin) {
document.querySelector('#admin-link').classList.remove('hidden');
}
profileLogoutButton.classList.remove('hidden');
}
async function loadMastodonConfig() {
const response = await fetch('/api/user/plugins/mastodon', {headers: authHeaders()});
if (!response.ok) throw new Error('Could not load Mastodon settings');
const config = await response.json();
document.querySelector('#mastodon-instance').value = config.instance || defaultMastodonInstance;
document.querySelector('#mastodon-access-token').value = config.access_token || '';
document.querySelector('#mastodon-post-prefix').value = config.post_prefix || defaultPostPrefix;
}
profileForm.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = new FormData(profileForm);
const response = await fetch('/api/user/me', {
method: 'PUT',
headers: authHeaders(true),
body: JSON.stringify(Object.fromEntries(formData)),
});
setStatus('#profile-status', response.ok ? 'Profile saved.' : 'Could not save profile.', !response.ok);
});
mastodonForm.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = new FormData(mastodonForm);
const response = await fetch('/api/user/plugins/mastodon', {
method: 'PUT',
headers: authHeaders(true),
body: JSON.stringify(Object.fromEntries(formData)),
});
setStatus('#mastodon-status', response.ok ? 'Mastodon settings saved.' : 'Could not save Mastodon settings.', !response.ok);
});
Promise.all([loadProfile(), loadMastodonConfig()]).catch((error) => {
setStatus('#profile-status', accessToken ? error.message : 'Please sign in first.', true);
});
})();