Files
XIP/frontend/src/components/shop/MesPersos.vue

314 lines
9.9 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Mes Personnalisations visible depuis le shop, onglet "Mes Persos" -->
<template>
<div class="persos">
<!-- Image de fond du chat -->
<section class="section">
<h2 class="section-title">🖼 Fond du chat</h2>
<p class="section-sub">URL d'une image (jpg, png, gif, webp…) ou laisse vide pour le fond par défaut.</p>
<div class="bg-row">
<input
v-model="bgDraft"
class="bg-input"
type="text"
placeholder="https://example.com/image.jpg"
@keydown.enter="applyBg"
/>
<button class="btn-apply" @click="applyBg" type="button">Appliquer</button>
<button v-if="prefs.chatBgUrl" class="btn-reset" @click="resetBg" type="button">✕ Retirer</button>
</div>
<div v-if="prefs.chatBgUrl" class="bg-preview" :style="{ backgroundImage: `url(${prefs.chatBgUrl})` }" />
</section>
<!-- ── Bouton d'envoi -->
<section class="section" :class="{ locked: !myPerks.elementSkin }">
<h2 class="section-title">
Bouton d'envoi
<span v-if="!myPerks.elementSkin" class="lock-badge">🔒 Skin d'éléments requis</span>
</h2>
<div class="style-grid">
<button
v-for="[k, p] in Object.entries(SEND_BUTTON_PRESETS)"
:key="k"
class="style-tile"
:class="{ 'style-tile--active': prefs.sendButton === k }"
:disabled="!myPerks.elementSkin"
@click="prefs.sendButton = k as SendButtonKey"
type="button"
>
<span class="style-swatch" :style="{ background: p.bg, color: p.color, borderRadius: p.radius }"></span>
<span class="style-label">{{ p.label }}</span>
</button>
</div>
</section>
<!-- Couleur de l'IP ────────────────────────────────────────── -->
<section class="section" :class="{ locked: !myPerks.elementSkin }">
<h2 class="section-title">
🎨 Couleur de mon IP
<span v-if="!myPerks.elementSkin" class="lock-badge">🔒 Skin d'éléments requis</span>
</h2>
<p v-if="myIp" class="section-sub">IP&nbsp;: <code class="code-ip" :style="ipPreviewStyle">{{ myIp }}</code></p>
<div class="style-grid">
<button
v-for="opt in IP_COLOR_OPTIONS"
:key="opt.value"
class="style-tile"
:class="{ 'style-tile--active': currentIpColor === opt.value }"
:disabled="!myPerks.elementSkin"
@click="setIpColor(opt.value)"
type="button"
>
<span v-if="opt.swatch" class="color-dot" :style="{ background: opt.swatch }" />
<span v-else class="color-dot color-dot--auto" />
<span class="style-label">{{ opt.label }}</span>
</button>
</div>
</section>
<!-- Pets -->
<section class="section" :class="{ locked: !hasPets }">
<h2 class="section-title">
Mes pets
<span v-if="!hasPets" class="lock-badge">Achetez un Pet dans le shop</span>
</h2>
<template v-if="hasPets">
<div class="pet-grid">
<button
v-for="pet in ownedPets"
:key="pet.char"
class="pet-cell"
:class="{ 'pet-cell--active': activePet === pet.char }"
@click="togglePet(pet.char)"
type="button"
>{{ pet.char }}</button>
<button
class="pet-cell pet-cell--none"
:class="{ 'pet-cell--active': activePet === '' }"
@click="togglePet('')"
type="button"
> Aucun</button>
</div>
<p class="section-sub" style="margin-top:6px">
Actif&nbsp;: <strong>{{ activePet || 'aucun' }}</strong>
s'affiche à gauche de ton IP dans le chat.
</p>
</template>
<p v-else class="section-sub">Aucun pet possédé pour l'instant.</p>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { useCustomStyles, SEND_BUTTON_PRESETS, IP_COLOR_OPTIONS, type SendButtonKey } from '@/composables/useCustomStyles';
import { useMyPerks } from '@/composables/useMessages';
import { getIpColorWithPerks, getIpGlow } from '@/composables/ipColor';
import { useWallet } from '@/composables/useWallet';
const { prefs } = useCustomStyles();
const { myPerks } = useMyPerks();
const { ip: myIp } = useWallet();
// ── Background ──────────────────────────────────────────────────────────────
const bgDraft = ref(prefs.chatBgUrl);
watch(() => prefs.chatBgUrl, (v) => { bgDraft.value = v; });
function applyBg(): void { prefs.chatBgUrl = bgDraft.value.trim(); }
function resetBg(): void { prefs.chatBgUrl = ''; bgDraft.value = ''; }
// ── IP color ────────────────────────────────────────────────────────────────
const currentIpColor = computed(() => prefs.ipColors[myIp.value] ?? 'auto');
function setIpColor(value: string): void {
if (!myIp.value) return;
prefs.ipColors[myIp.value] = value;
}
const ipPreviewStyle = computed(() => {
if (!myIp.value) return {};
const color = currentIpColor.value === 'auto'
? getIpColorWithPerks(myIp.value, myPerks.value)
: currentIpColor.value;
return { color, textShadow: getIpGlow(color) };
});
// ── Pets ────────────────────────────────────────────────────────────────────
const ownedPets = computed(() => {
const seen = new Set<string>();
return (myPerks.value.pets ?? []).filter((p) => {
if (seen.has(p.char)) return false;
seen.add(p.char);
return true;
});
});
const hasPets = computed(() => ownedPets.value.length > 0);
const activePet = computed(() =>
myIp.value && myIp.value in prefs.ipPets ? prefs.ipPets[myIp.value] : (ownedPets.value[0]?.char ?? '')
);
function togglePet(char: string): void {
if (!myIp.value) return;
prefs.ipPets[myIp.value] = char;
}
</script>
<style scoped>
.persos {
display: flex;
flex-direction: column;
gap: 24px;
padding: 4px 0;
max-width: 640px;
}
.section {
background: #101018;
border: 1px solid #20203a;
border-radius: 10px;
padding: 18px 20px;
}
.section.locked {
opacity: 0.6;
}
.section-title {
font-size: 14px;
font-weight: bold;
color: #ccccee;
margin: 0 0 6px;
display: flex;
align-items: center;
gap: 10px;
}
.section-sub {
font-size: 11px;
color: #5a5a80;
margin: 0 0 12px;
}
.lock-badge {
font-size: 10px;
font-weight: normal;
color: #886644;
background: #1a1408;
border: 1px solid #44330066;
border-radius: 8px;
padding: 2px 8px;
}
/* Background */
.bg-row {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
}
.bg-input {
flex: 1;
background: #141420;
border: 1px solid #222234;
border-radius: 6px;
color: #aaaacc;
font-family: Arial, sans-serif;
font-size: 12px;
padding: 8px 12px;
outline: none;
}
.bg-input:focus { border-color: #333355; }
.btn-apply {
background: #1a2a1a; border: 1px solid #33aa55; color: #44dd77;
font-size: 12px; font-weight: bold; padding: 7px 14px; border-radius: 14px; cursor: pointer;
}
.btn-apply:hover { background: #234a23; }
.btn-reset {
background: #2a1010; border: 1px solid #882222; color: #ff6655;
font-size: 11px; padding: 7px 12px; border-radius: 14px; cursor: pointer;
}
.btn-reset:hover { background: #3a1818; }
.bg-preview {
width: 100%;
height: 80px;
background-size: cover;
background-position: center;
border-radius: 6px;
border: 1px solid #222234;
}
/* Style tiles */
.style-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.style-tile {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
background: #141420;
border: 1px solid #222234;
border-radius: 8px;
padding: 10px 14px;
cursor: pointer;
transition: border-color 0.1s, background 0.1s;
}
.style-tile:hover:not(:disabled) { background: #1a1a2e; border-color: #333355; }
.style-tile--active { border-color: #00ddff; background: #0a1a20; }
.style-tile:disabled { cursor: not-allowed; opacity: 0.5; }
.style-swatch {
width: 34px; height: 34px;
border-radius: inherit;
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: bold;
border: 1px solid #ffffff10;
}
.style-label {
font-size: 10px;
color: #8888aa;
white-space: nowrap;
}
.style-tile--active .style-label { color: #00ddff; }
/* Color dots */
.color-dot {
width: 20px; height: 20px;
border-radius: 50%;
border: 1px solid #ffffff22;
}
.color-dot--auto {
background: conic-gradient(#00ddff, #ff00cc, #00ee77, #ffdd44, #00ddff);
}
/* IP code */
.code-ip {
font-family: 'Courier New', monospace;
font-size: 12px;
font-weight: bold;
}
/* Pet grid */
.pet-grid {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.pet-cell {
width: 42px; height: 42px;
background: #141420;
border: 1px solid #222234;
border-radius: 8px;
font-size: 20px;
cursor: pointer;
transition: border-color 0.1s, background 0.1s;
display: flex; align-items: center; justify-content: center;
}
.pet-cell:hover { background: #1a1a2e; border-color: #333355; }
.pet-cell--active { border-color: #00ddff; background: #0a1a20; }
.pet-cell--none { font-size: 11px; color: #666; width: auto; padding: 0 10px; }
.pet-cell--none.pet-cell--active { color: #00ddff; }
</style>