feat(profile): let users design their public profile

This commit is contained in:
lobo committed 2026-09-28 14:20:14 +02:00
1 parent a5458b3f86
commit b55fcfec92
20 files changed
+674 -138

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@ export const DEFAULT_APPEARANCE = {
accent: 'violet', corners: 'default', wideLayout: false, backgroundAppid: null, bannerAppid: null, showBanner: true,
};
const appid = (value) => (Number.isInteger(value) && value > 0 ? value : null);
export const appid = (value) => (Number.isInteger(value) && value > 0 ? value : null);
const STORAGE_KEY = 'tbp_appearance';
const REQUEST_TIMEOUT_MS = 10000;
+37
View File
@@ -0,0 +1,37 @@
import {apiFetch} from './api.js';
import {ACCENTS, CORNERS, DEFAULT_APPEARANCE, appid} from './appearance.js';
// Auth accepts only these names (ProfileStyleRequest); Core sends no data for a section that is not listed.
export const PROFILE_SECTIONS = ['SUMMARY', 'STATUS', 'PLAYING', 'FINISHED', 'STATS'];
export const DEFAULT_PROFILE_STYLE = {
accent: DEFAULT_APPEARANCE.accent, corners: DEFAULT_APPEARANCE.corners, bannerAppid: null, backgroundAppid: null,
sections: PROFILE_SECTIONS,
};
export const normalizeProfileStyle = (value) => ({
accent: ACCENTS.includes(value?.accent) ? value.accent : DEFAULT_PROFILE_STYLE.accent,
corners: CORNERS.includes(value?.corners) ? value.corners : DEFAULT_PROFILE_STYLE.corners,
bannerAppid: appid(value?.bannerAppid),
backgroundAppid: appid(value?.backgroundAppid),
sections: Array.isArray(value?.sections)
? value.sections.filter((s, i) => PROFILE_SECTIONS.includes(s) && value.sections.indexOf(s) === i)
: PROFILE_SECTIONS,
});
/**
* The stored style from GET /api/users/me; null when there is no answer to use.
*/
export const loadProfileStyle = async () => {
const response = await apiFetch(`${import.meta.env.VITE_API_URL}/api/users/me?v=1`);
if (!response.ok) return null;
const data = await response.json();
return normalizeProfileStyle(data.settings?.profileStyle);
};
export const saveProfileStyle = (style) =>
apiFetch(`${import.meta.env.VITE_API_URL}/api/users/settings/profile-style`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(style),
});
+18
View File
@@ -0,0 +1,18 @@
import {describe, expect, it} from 'vitest';
import {DEFAULT_PROFILE_STYLE, normalizeProfileStyle} from './profileStyle.js';
describe('normalizeProfileStyle', () => {
it('is the default without a style, e.g. from an older Auth', () => {
expect(normalizeProfileStyle(undefined)).toEqual(DEFAULT_PROFILE_STYLE);
});
it('keeps the order, drops unknown and repeated sections and bad values', () => {
expect(normalizeProfileStyle({accent: 'hotpink', corners: 'round', bannerAppid: -5, backgroundAppid: 620,
sections: ['STATS', 'NOTES', 'PLAYING', 'STATS']}))
.toEqual({accent: 'violet', corners: 'round', bannerAppid: null, backgroundAppid: 620, sections: ['STATS', 'PLAYING']});
});
it('keeps an empty list: every section hidden', () => {
expect(normalizeProfileStyle({sections: []}).sections).toEqual([]);
});
});