diff --git a/js/avatar.js b/js/avatar.js new file mode 100644 index 0000000..2e473f3 --- /dev/null +++ b/js/avatar.js @@ -0,0 +1,38 @@ +import * as DB from "./db.js"; +{ + const DEFAULT_AVATAR_PATHS = [ + + ].map(s => `"/data/img/avatars/"${s}`); + + /** @param {string} id The user's UID */ + function getDefaultAvatarPath(id) { + let counter = 0; + for(let i = 0; i < id.length; i++) { + counter += id.charCodeAt(i); + } + const index = counter % DEFAULT_AVATAR_PATHS.length; + return DEFAULT_AVATAR_PATHS[index]; + } + + /** @param {string} id The user's UID */ + async function getDefaultAvatar(id) { + const path = getDefaultAvatarPath(id); + const response = await fetch(path); + if(!response.ok) { + console.error("Failed to fetch default profile picture: ", response); + return null; + } + return await response.blob(); + } + + /** @param {string} id The user's UID */ + async function getAvatar(id) { + try { + const avatar = await DB.getAvatarById(id); + if(avatar) return avatar; + } catch (err) { + console.warn(`Failed to fetch avatar for ${id}: ${err}`); + } + return await getDefaultAvatar(id); + } +} \ No newline at end of file diff --git a/js/db.js b/js/db.js index 80bae27..b894b14 100644 --- a/js/db.js +++ b/js/db.js @@ -26,13 +26,13 @@ window.SUPABASE = SUPABASE; * @property {string} submitted_by * @property {SubmissionValidity} validity * @property {string?} proof - * @property {string | undefined} replay_data - Base64 encoded replay data, may be undefined if not requested + * @property {string | undefined} replay_data Base64 encoded replay data, may be undefined if not requested */ /** * @typedef {Object} ReplayData * @property {string} submission_id - * @property {string} replay_data - Base64 encoded replay data + * @property {string} replay_data Base64 encoded replay data */ /** @@ -73,6 +73,10 @@ export const TABLES = { REPLAYS: "replays", } +export const STORAGES = { + AVATARS: "avatars" +} + // #region Profile table functions /** * @param {number} limit @@ -308,6 +312,42 @@ export async function deleteReplay(submissionId) { return data; } // #endregion +// #region Avatars/Profile Pictures +// Profile pictures are stored in Supabase Storage, and the name of the file is the user's ID. + +/** + * @param {string} id The user's ID + * @returns {Promise} The user's profile picture + * @throws {Error} + */ +export async function getAvatarById(id = SUPABASE.auth.getUser().data.user.id) { + const { data, error } = await SUPABASE.storage.from(STORAGES.AVATARS).download(id); + if(error) throw new Error(error.message); + return data; +} + +/** + * @param {File} file + * @param {string} id The user's ID + * @returns {Promise<{path: string}>} + * @throws {Error} + */ +export async function updateAvatar(file, id = SUPABASE.auth.getUser().data.user.id) { + const { data, error } = await SUPABASE.storage.from(STORAGES.AVATARS).upload(id, file, {upsert: true}); + if(error) throw new Error(error.message); + return data; +} + +/** + * @param {string} id The user's ID + * @returns {Promise} + */ +export async function deleteAvatar(id = SUPABASE.auth.getUser().data.user.id) { + const { error } = await SUPABASE.storage.from(STORAGES.AVATARS).remove([id]); + if(error) throw new Error(error.message); + return []; // Supabase returns an empty array on success +} +// #endregion // #region Download all (table) data // This is an open-source project, why not let users backup everything in case something terrible happens? // Note: profile pictures will not be scraped, as they are stored in a different location. diff --git a/js/profile-pic.js b/js/profile-pic.js deleted file mode 100644 index 923a340..0000000 --- a/js/profile-pic.js +++ /dev/null @@ -1,5 +0,0 @@ -{ - const DEFAULT_PROFILE_PICTURE_PATHS = [ - - ].map(s => `"/data/img/default-pfps/"${s}`); -} \ No newline at end of file