From 16112d4f1d8d2d97b135966ad0ae1267906fff0a Mon Sep 17 00:00:00 2001
From: Raymond <101374892+raymonable@users.noreply.github.com>
Date: Thu, 16 Jan 2025 19:56:44 -0500
Subject: [PATCH] feat: :heavy_plus_sign: pfp cropper + enhancements
---
AquaNet/package.json | 1 +
AquaNet/src/App.svelte | 2 +-
.../settings/GeneralGameSettings.svelte | 4 +-
AquaNet/src/libs/i18n/en_ref.ts | 4 +-
AquaNet/src/pages/User/Settings.svelte | 85 +++++++++++++++++--
5 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/AquaNet/package.json b/AquaNet/package.json
index a50f876e..55f89bb1 100644
--- a/AquaNet/package.json
+++ b/AquaNet/package.json
@@ -39,6 +39,7 @@
"lxgw-wenkai-lite-webfont": "^1.7.0",
"modern-normalize": "^3.0.1",
"moment": "^2.30.1",
+ "svelte-easy-crop": "^4.0.0",
"svelte5-router": "^3.0.1"
},
"packageManager": "pnpm@9.7.0+sha512.dc09430156b427f5ecfc79888899e1c39d2d690f004be70e05230b72cb173d96839587545d09429b55ac3c429c801b4dc3c0e002f653830a420fa2dd4e3cf9cf"
diff --git a/AquaNet/src/App.svelte b/AquaNet/src/App.svelte
index 6a320657..760f0ba8 100644
--- a/AquaNet/src/App.svelte
+++ b/AquaNet/src/App.svelte
@@ -75,7 +75,7 @@
img
width: 1.5rem
height: 1.5rem
- border-radius: 50%
+ border-radius: vars.$border-radius
object-fit: cover
.pfp
diff --git a/AquaNet/src/components/settings/GeneralGameSettings.svelte b/AquaNet/src/components/settings/GeneralGameSettings.svelte
index 65c0a033..6fbbb64a 100644
--- a/AquaNet/src/components/settings/GeneralGameSettings.svelte
+++ b/AquaNet/src/components/settings/GeneralGameSettings.svelte
@@ -2,7 +2,7 @@
import { fade } from "svelte/transition";
import { FADE_IN, FADE_OUT } from "../../libs/config";
import GameSettingFields from "./GameSettingFields.svelte";
- import { ts } from "../../libs/i18n";
+ import { t, ts } from "../../libs/i18n";
import useLocalStorage from "../../libs/hooks/useLocalStorage.svelte";
const rounding = useLocalStorage("rounding", true);
@@ -10,7 +10,7 @@
- These settings affect Mai and Wacca.
+ {ts("settings.gameNotice")}
diff --git a/AquaNet/src/libs/i18n/en_ref.ts b/AquaNet/src/libs/i18n/en_ref.ts
index 80dc168a..658d0b7c 100644
--- a/AquaNet/src/libs/i18n/en_ref.ts
+++ b/AquaNet/src/libs/i18n/en_ref.ts
@@ -158,6 +158,7 @@ export const EN_REF_SETTINGS = {
'settings.mai2.name': 'Player Name',
'settings.profile.picture': 'Profile Picture',
'settings.profile.upload-new': 'Upload New',
+ 'settings.profile.bad-format': 'Invalid image format. Supported types are PNG, JPG, JPEG, WEBP & GIF.',
'settings.profile.save': 'Save',
'settings.profile.name': 'Display Name',
'settings.profile.username': 'Username',
@@ -167,7 +168,8 @@ export const EN_REF_SETTINGS = {
'settings.profile.unset': 'Unset',
'settings.profile.unchanged': 'Unchanged',
'settings.export': 'Export Player Data',
- 'settings.cabNotice': "Note: These settings will only affect your own cab/setup. If you're playing on someone else's setup, please contact them to change these settings."
+ 'settings.cabNotice': "Note: These settings will only affect your own cab/setup. If you're playing on someone else's setup, please contact them to change these settings.",
+ 'settings.gameNotice': "These only apply to Mai and Wacca."
}
export const EN_REF_USERBOX = {
diff --git a/AquaNet/src/pages/User/Settings.svelte b/AquaNet/src/pages/User/Settings.svelte
index ca4f8e1b..02c8a7ec 100644
--- a/AquaNet/src/pages/User/Settings.svelte
+++ b/AquaNet/src/pages/User/Settings.svelte
@@ -9,6 +9,7 @@
import { pfp } from "../../libs/ui";
import { t, ts } from "../../libs/i18n";
import { FADE_IN, FADE_OUT } from "../../libs/config";
+ import Cropper from "svelte-easy-crop";
import UserBox from "../../components/settings/ChuniSettings.svelte";
import Mai2Settings from "../../components/settings/Mai2Settings.svelte";
import WaccaSettings from "../../components/settings/WaccaSettings.svelte";
@@ -32,6 +33,11 @@
// Fetch user data
const getMe = () => USER.me().then((m) => {
+ if (pfpCropURL != null) {
+ URL.revokeObjectURL(pfpCropURL);
+ pfpField.value = "";
+ pfpCropURL = null;
+ }
me = m
CARD.userGames(m.username).then(games => {
@@ -50,6 +56,8 @@
let changed: string[] = []
let pfpField: HTMLInputElement
+ let pfpCropURL: string | null = null;
+ let pfpCrop = { width: 0, height: 0, x: 0, y: 0 };
function submit(field: string, value: string) {
if (submitting) return
@@ -60,16 +68,53 @@
}).catch(e => error = e.message).finally(() => submitting = "")
}
- function uploadPfp(file: File) {
+ function uploadPfp() {
if (submitting) return
- submitting = 'profilePicture'
-
- USER.uploadPfp(file).then(() => {
- me.profilePicture = file.name
- // reload
- getMe()
- }).catch(e => error = e.message).finally(() => submitting = "")
+ // Don't know why this isn't just a part of the cropper module. Have to do this myself.. What a shame
+ let canvas = document.createElement("canvas");
+ let ctx = canvas.getContext("2d");
+ canvas.width = 256;
+ canvas.height = 256;
+ let img = document.createElement("img");
+ img.onload = () => {
+ ctx?.drawImage(img, pfpCrop.x, pfpCrop.y, pfpCrop.width, pfpCrop.height, 0, 0, 256, 256);
+ canvas.toBlob(blob => {
+ if (!blob) return;
+ submitting = 'profilePicture'
+ USER.uploadPfp(blob as File).then(() => {
+ me.profilePicture = me.username
+ // reload
+ // this doesn't work btw
+ setTimeout(getMe, 200);
+ }).catch(e => error = e.message).finally(() => submitting = "")
+ });
+ }
+ img.src = pfpCropURL ?? "";
}
+ function handlePfpUpload(e: Event & { target: HTMLInputElement }) {
+ if (!e.target) return;
+ let files = e?.target?.files;
+ if (!files || files.length <= 0) return;
+ let file = files[0];
+ console.log(me.username, me);
+ switch (file.type) {
+ case "image/gif":
+ USER.uploadPfp(file).then(() => {
+ me.profilePicture = me.username
+ // reload
+ setTimeout(getMe, 200);
+ }).catch(e => error = e.message).finally(() => submitting = "")
+ break;
+ case "image/png":
+ case "image/jpg":
+ case "image/jpeg":
+ case "image/webp":
+ pfpCropURL = URL.createObjectURL(file);
+ break;
+ default:
+ error = t("settings.profile.bad-format");
+ }
+ };
const passwordAction = (node: HTMLInputElement, whether: boolean) => {
if (whether) node.type = 'password'
@@ -107,8 +152,9 @@
{/if}
+
pfpField.files && uploadPfp(pfpField.files[0])} />
+ on:change={handlePfpUpload} />
{#each profileFields as [field, name], i (field)}
@@ -155,6 +201,22 @@