307 lines
7.0 KiB
Svelte
307 lines
7.0 KiB
Svelte
<script lang="ts">
|
|
import { getContext } from "svelte";
|
|
import type { ChatService } from "../../services/chat.svelte";
|
|
import type * as Types from "../../../module_bindings/types";
|
|
|
|
let {
|
|
localName = $bindable(),
|
|
biography = $bindable(),
|
|
avatarPreview = $bindable(),
|
|
newAvatarFile = $bindable(),
|
|
bannerPreview = $bindable(),
|
|
newBannerFile = $bindable(),
|
|
currentUser,
|
|
}: {
|
|
localName: string;
|
|
biography: string;
|
|
avatarPreview: string | null;
|
|
newAvatarFile: File | null;
|
|
bannerPreview: string | null;
|
|
newBannerFile: File | null;
|
|
currentUser: Types.User | undefined;
|
|
} = $props();
|
|
|
|
const chat = getContext<ChatService>("chat");
|
|
|
|
async function handleAvatarChange(e: Event) {
|
|
const file = (e.target as HTMLInputElement).files?.[0];
|
|
if (file) {
|
|
newAvatarFile = file;
|
|
avatarPreview = URL.createObjectURL(file);
|
|
}
|
|
}
|
|
|
|
async function handleBannerChange(e: Event) {
|
|
const file = (e.target as HTMLInputElement).files?.[0];
|
|
if (file) {
|
|
newBannerFile = file;
|
|
bannerPreview = URL.createObjectURL(file);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="account-profile-card">
|
|
<div class="profile-banner" style="background-image: {bannerPreview ? `url(${bannerPreview})` : 'none'}; background-color: {bannerPreview ? 'transparent' : 'var(--background-tertiary)'};">
|
|
<label class="banner-upload-overlay">
|
|
<i class="fas fa-camera"></i>
|
|
<span>Change Banner</span>
|
|
<input type="file" accept="image/*" onchange={handleBannerChange} style="display: none;" />
|
|
</label>
|
|
</div>
|
|
<div class="profile-info-section">
|
|
<div class="profile-avatar-wrapper">
|
|
<div class="avatar-preview-large">
|
|
{#if avatarPreview}
|
|
<img src={avatarPreview} alt="Avatar Preview" />
|
|
{:else}
|
|
<div class="avatar-placeholder">
|
|
{currentUser?.name?.[0]?.toUpperCase() || "?"}
|
|
</div>
|
|
{/if}
|
|
<label class="avatar-upload-overlay">
|
|
<i class="fas fa-camera"></i>
|
|
<span>Change</span>
|
|
<input type="file" accept="image/*" onchange={handleAvatarChange} style="display: none;" />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="profile-name-section">
|
|
<span class="profile-display-name">{currentUser?.name || "Unknown"}</span>
|
|
<span class="profile-tag">#{chat.identity?.toHexString().substring(0, 4)}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="account-edit-box">
|
|
<div class="form-group">
|
|
<label for="display-name">Display Name</label>
|
|
<div class="input-wrapper">
|
|
<input
|
|
id="display-name"
|
|
type="text"
|
|
bind:value={localName}
|
|
placeholder="Enter your display name"
|
|
/>
|
|
<button class="btn-clear" onclick={() => localName = ""} aria-label="Clear name">
|
|
<i class="fas fa-times-circle"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="biography">Biography</label>
|
|
<textarea
|
|
id="biography"
|
|
bind:value={biography}
|
|
placeholder="Tell us about yourself..."
|
|
rows="4"
|
|
maxlength="200"
|
|
></textarea>
|
|
<div class="textarea-footer">{biography.length}/200</div>
|
|
</div>
|
|
|
|
<div class="avatar-actions">
|
|
<button class="btn-secondary small" onclick={() => {
|
|
avatarPreview = null;
|
|
newAvatarFile = null;
|
|
}}>Remove Avatar</button>
|
|
<button class="btn-secondary small" onclick={() => {
|
|
bannerPreview = null;
|
|
newBannerFile = null;
|
|
}}>Remove Banner</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* Account Card Style */
|
|
.account-profile-card {
|
|
background-color: var(--background-secondary);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.profile-banner {
|
|
height: 140px;
|
|
background-size: cover;
|
|
background-position: center;
|
|
position: relative;
|
|
}
|
|
|
|
.banner-upload-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.3);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
cursor: pointer;
|
|
transition: opacity 0.2s;
|
|
color: white;
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
gap: 4px;
|
|
}
|
|
|
|
.banner-upload-overlay:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.profile-info-section {
|
|
padding: 16px;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 16px;
|
|
margin-top: -45px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.profile-avatar-wrapper {
|
|
padding: 6px;
|
|
background-color: var(--background-secondary);
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.avatar-preview-large {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
position: relative;
|
|
background-color: var(--background-tertiary);
|
|
}
|
|
|
|
.avatar-preview-large img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.avatar-placeholder {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
color: white;
|
|
background-color: var(--brand);
|
|
}
|
|
|
|
.avatar-upload-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
cursor: pointer;
|
|
transition: opacity 0.2s;
|
|
color: white;
|
|
font-size: 0.7rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.avatar-upload-overlay:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.profile-name-section {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.profile-display-name {
|
|
font-size: 1.25rem;
|
|
font-weight: 700;
|
|
color: var(--header-primary);
|
|
}
|
|
|
|
.profile-tag {
|
|
font-size: 1rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.account-edit-box {
|
|
background-color: var(--background-tertiary);
|
|
margin: 0 16px 16px 16px;
|
|
padding: 16px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.form-group label {
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
color: var(--text-muted);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.input-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: var(--background-primary);
|
|
border-radius: 4px;
|
|
padding-right: 8px;
|
|
}
|
|
|
|
.form-group input[type="text"], .form-group textarea {
|
|
padding: 10px;
|
|
background-color: var(--background-primary);
|
|
color: var(--text-normal);
|
|
border: none;
|
|
font-size: 1rem;
|
|
outline: none;
|
|
border-radius: 4px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.form-group textarea {
|
|
resize: none;
|
|
}
|
|
|
|
.textarea-footer {
|
|
text-align: right;
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.avatar-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.btn-clear {
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
font-size: 1.1rem;
|
|
opacity: 0.6;
|
|
transition: opacity 0.1s;
|
|
}
|
|
|
|
.btn-clear:hover {
|
|
opacity: 1;
|
|
color: var(--text-normal);
|
|
}
|
|
</style>
|