diff --git a/platform/website/src/assets/styles/global.scss b/platform/website/src/assets/styles/global.scss index 20171bb5..579b0ba1 100644 --- a/platform/website/src/assets/styles/global.scss +++ b/platform/website/src/assets/styles/global.scss @@ -161,6 +161,60 @@ input[type="password"] { } } +// Style the range input +input[type="range"] { + appearance: none; + -webkit-appearance: none; + background: transparent; + + &:focus { + outline: none; + } + + &::-moz-range-thumb { + appearance: none; + width: 1rem; + height: 1rem; + border-radius: 50%; + background: white; + cursor: pointer; + } + + &::-ms-thumb { + width: 1rem; + height: 1rem; + border-radius: 50%; + background: white; + cursor: pointer; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + margin-top: -0.25rem; + width: 1rem; + height: 1rem; + border-radius: 50%; + background: white; + cursor: pointer; + } + + &::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + cursor: pointer; + background: rgba($textColor, 0.25); + border-radius: 0.25rem; + } + + &::-moz-range-track { + width: 100%; + height: 0.5rem; + cursor: pointer; + background: rgba($textColor, 0.25); + border-radius: 0.25rem; + } +} + // Screen reader only // Hide but still render .sr-only:not(:focus):not(:active) { diff --git a/platform/website/src/components/player.svelte b/platform/website/src/components/player.svelte index ff888174..6327cb70 100644 --- a/platform/website/src/components/player.svelte +++ b/platform/website/src/components/player.svelte @@ -611,56 +611,6 @@ } .volume { - appearance: none; - -webkit-appearance: none; width: 8rem; - background: transparent; - } - - .volume:focus { - outline: none; - } - - .volume::-moz-range-thumb { - appearance: none; - width: 1rem; - height: 1rem; - border-radius: 50%; - background: white; - cursor: pointer; - } - - .volume::-ms-thumb { - width: 1rem; - height: 1rem; - border-radius: 50%; - background: white; - cursor: pointer; - } - - .volume::-webkit-slider-thumb { - -webkit-appearance: none; - margin-top: -0.25rem; - width: 1rem; - height: 1rem; - border-radius: 50%; - background: white; - cursor: pointer; - } - - .volume::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - cursor: pointer; - background: rgba($textColor, 0.25); - border-radius: 0.25rem; - } - - .volume::-moz-range-track { - width: 100%; - height: 0.5rem; - cursor: pointer; - background: rgba($textColor, 0.25); - border-radius: 0.25rem; } diff --git a/platform/website/src/components/settings/image-editor-dialog.svelte b/platform/website/src/components/settings/image-editor-dialog.svelte new file mode 100644 index 00000000..0011ae1b --- /dev/null +++ b/platform/website/src/components/settings/image-editor-dialog.svelte @@ -0,0 +1,110 @@ + + + +
+
+ +
+ + + +
+
+
+ + +
+
+
+ + diff --git a/platform/website/src/components/settings/image-editor.svelte b/platform/website/src/components/settings/image-editor.svelte index 2c7a1363..52abe290 100644 --- a/platform/website/src/components/settings/image-editor.svelte +++ b/platform/website/src/components/settings/image-editor.svelte @@ -22,8 +22,10 @@ export let overlay = true; export let gridOverlay = false; export let size = 40 * 16; - export let minScale = 1; - export let maxScale = 2; + // between minScale and maxScale + export let scale = 1; + export let minScale: number; + export let maxScale: number; export let src: string; let mouseStartX: number; @@ -36,9 +38,6 @@ let x = 0; let y = 0; - // between minScale and maxScale - let scale = 1; - $: applyLimits(), scale; // If the image is currently grabbed @@ -125,7 +124,7 @@ scale = Math.min(Math.max(minScale, scale), maxScale); } - export function calculateResult() { + export function calculateResult(callback: BlobCallback) { if (!moveable) return; const canvas = document.createElement('canvas'); @@ -155,7 +154,7 @@ ctx.drawImage(moveable, xrl, yrt, rw, rh, ox, oy, rw, rh); - return canvas.toDataURL('image/png'); + canvas.toBlob(callback, "image/png"); } function updateAspectRatio() { @@ -167,52 +166,30 @@ -
-
-
- - 1} class:high={aspectRatio < 1} style="--scale: {scale}; --x: {x * size}px; --y: {y * size}px" alt="upload a file" /> - {#if overlay} -
- {/if} - {#if gridOverlay} -
-
-
-
-
-
-
-
-
-
-
- {/if} +
+ + 1} class:high={aspectRatio < 1} style="--scale: {scale}; --x: {x * size}px; --y: {y * size}px" alt="upload a file" /> + {#if overlay} +
+ {/if} + {#if gridOverlay} +
+
+
+
+
+
+
+
+
+
-
+ {/if}
diff --git a/platform/website/src/routes/(app)/settings/profile/+page.svelte b/platform/website/src/routes/(app)/settings/profile/+page.svelte index 24e6a25c..2618be6f 100644 --- a/platform/website/src/routes/(app)/settings/profile/+page.svelte +++ b/platform/website/src/routes/(app)/settings/profile/+page.svelte @@ -18,6 +18,9 @@ import { FileStatus } from "$/gql/graphql"; import ErrorDialog from "$/components/error-dialog.svelte"; import { colorToStyle, rgbHexToHsl } from "$/lib/colors"; + import ImageEditorDialog from "$/components/settings/image-editor-dialog.svelte"; + + const imageEditorTypes = ["image/png", "image/jls", "image/jpeg", "image/jxl", "image/bmp"]; const recommendedColors = ["#ff7a00", "#ffe457", "#57ff86", "#00ffd1", "#5786ff", "#8357ff"]; @@ -37,7 +40,8 @@ let displayColorRgb = $user?.displayColor.rgb; let displayColorInput: HTMLInputElement; - let avatarFiles: FileList; + let avatarFiles: FileList | null = null; + let avatarSrc: string | null = null; let avatarInput: HTMLInputElement; $: status = @@ -168,12 +172,13 @@ let turnstileToken: string | null = null; - function uploadProfilePicture() { - if (turnstileToken) { + function uploadProfilePicture(blob: Blob | null) { + if (turnstileToken && blob) { + status = Status.Saving; uploadFile( `${PUBLIC_UPLOAD_ENDPOINT}/profile-picture`, { set_active: true }, - avatarFiles[0], + blob, turnstileToken, ) .then((res) => res.json()) @@ -189,6 +194,7 @@ fileError = err; status = Status.Unchanged; }); + resetAvatarFile(); } } @@ -220,10 +226,24 @@ $: { if (avatarFiles && avatarFiles[0]) { - status = Status.Saving; - uploadProfilePicture(); + if (imageEditorTypes.includes(avatarFiles[0].type)) { + const reader = new FileReader(); + reader.onload = (e) => { + if (!e.target) return; + avatarSrc = e.target.result as string; + }; + + reader.readAsDataURL(avatarFiles[0]); + } else { + uploadProfilePicture(avatarFiles[0]); + } } } + + function resetAvatarFile() { + avatarFiles = null; + avatarSrc = null; + } {#if $user} @@ -234,6 +254,9 @@ on:close={() => (fileError = null)} /> {/if} + {#if avatarSrc} + uploadProfilePicture(e.detail.blob)} /> + {/if}