Files
zep/src/chat/components/settings/AccountSettings.svelte
T
2026-04-05 02:08:33 -04:00

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>