feat: implement right-click context menu for style customization and enhance real-time stats tracking
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<div class="message-item">
|
||||
<!-- Auteur + horodatage -->
|
||||
<div class="message-meta">
|
||||
<span class="ip-wrap">
|
||||
<span class="ip-wrap" @contextmenu.prevent="openIpMenu($event, message.authorIp)" title="Clic droit pour personnaliser">
|
||||
<span v-if="petsLeft(message)" class="pet">{{ petsLeft(message) }}</span>
|
||||
<span class="ip" :style="ipStyle(message)">{{ message.authorIp }}</span>
|
||||
<span v-if="petsRight(message)" class="pet">{{ petsRight(message) }}</span>
|
||||
@@ -30,7 +30,7 @@
|
||||
:key="reply.id"
|
||||
class="reply"
|
||||
>
|
||||
<span class="ip-wrap">
|
||||
<span class="ip-wrap" @contextmenu.prevent="openIpMenu($event, reply.authorIp)" title="Clic droit pour personnaliser">
|
||||
<span v-if="petsLeft(reply)" class="pet pet--sm">{{ petsLeft(reply) }}</span>
|
||||
<span class="ip reply-ip" :style="ipStyle(reply)">{{ reply.authorIp }}</span>
|
||||
<span v-if="petsRight(reply)" class="pet pet--sm">{{ petsRight(reply) }}</span>
|
||||
@@ -52,8 +52,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Message, Reply } from '@/composables/useMessages';
|
||||
import { getIpColorWithPerks, getIpGlowWithPerks } from '@/composables/ipColor';
|
||||
import { getIpColorWithPerks, getIpGlowWithPerks, getIpColor, getIpGlow } from '@/composables/ipColor';
|
||||
import { usePerks } from '@/composables/usePerks';
|
||||
import { openContextMenu } from '@/composables/useContextMenu';
|
||||
import { useCustomStyles, IP_COLOR_OPTIONS, PET_OPTIONS } from '@/composables/useCustomStyles';
|
||||
import RichContent from './RichContent.vue';
|
||||
import MessageAttachments from './MessageAttachments.vue';
|
||||
|
||||
@@ -61,21 +63,28 @@ defineProps<{ message: Message }>();
|
||||
defineEmits<{ reply: [payload: { id: string; authorIp: string }] }>();
|
||||
|
||||
const { perksFor } = usePerks();
|
||||
const { prefs } = useCustomStyles();
|
||||
|
||||
/** Perks for an author: prefer the perks embedded in the payload, else the store. */
|
||||
function perksOf(m: Reply): any {
|
||||
return m.authorPerks ?? perksFor(m.authorIp);
|
||||
}
|
||||
|
||||
function ipStyle(m: Reply) {
|
||||
const ip = m.authorIp;
|
||||
const colorOverride = prefs.ipColors[ip];
|
||||
if (colorOverride && colorOverride !== 'auto') {
|
||||
return { color: colorOverride, textShadow: getIpGlow(colorOverride) };
|
||||
}
|
||||
const p = perksOf(m);
|
||||
return {
|
||||
color: getIpColorWithPerks(m.authorIp, p),
|
||||
textShadow: getIpGlowWithPerks(m.authorIp, p),
|
||||
color: getIpColorWithPerks(ip, p),
|
||||
textShadow: getIpGlowWithPerks(ip, p),
|
||||
};
|
||||
}
|
||||
|
||||
function petsLeft(m: Reply): string {
|
||||
const ip = m.authorIp;
|
||||
if (ip in prefs.ipPets) return prefs.ipPets[ip]; // '' = aucun pet
|
||||
const pets = perksOf(m)?.pets ?? [];
|
||||
return pets
|
||||
.filter((x: any) => x.position === 'left' || x.position === 'both')
|
||||
@@ -83,6 +92,8 @@ function petsLeft(m: Reply): string {
|
||||
.join('');
|
||||
}
|
||||
function petsRight(m: Reply): string {
|
||||
const ip = m.authorIp;
|
||||
if (ip in prefs.ipPets) return ''; // override = pet gauche uniquement
|
||||
const pets = perksOf(m)?.pets ?? [];
|
||||
return pets
|
||||
.filter((x: any) => x.position === 'right' || x.position === 'both')
|
||||
@@ -90,6 +101,36 @@ function petsRight(m: Reply): string {
|
||||
.join('');
|
||||
}
|
||||
|
||||
function openIpMenu(e: MouseEvent, ip: string): void {
|
||||
const currentColor = prefs.ipColors[ip] ?? 'auto';
|
||||
const currentPet = ip in prefs.ipPets ? prefs.ipPets[ip] : '__inherit__';
|
||||
openContextMenu({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
title: ip,
|
||||
items: [
|
||||
{ value: '__h_color', label: 'Couleur', isHeader: true },
|
||||
...IP_COLOR_OPTIONS.map((o) => ({ value: `color:${o.value}`, label: o.label, swatch: o.swatch })),
|
||||
{ value: '__h_pet', label: 'Pet', isHeader: true },
|
||||
{ value: 'pet:__inherit__', label: '↩ défaut (perk)' },
|
||||
...PET_OPTIONS.map((o) => ({ value: `pet:${o.value}`, label: o.label })),
|
||||
],
|
||||
current: currentColor !== 'auto' ? `color:${currentColor}` : `pet:${currentPet}`,
|
||||
onSelect: (v) => {
|
||||
if (v.startsWith('color:')) {
|
||||
prefs.ipColors[ip] = v.slice(6);
|
||||
} else if (v.startsWith('pet:')) {
|
||||
const pet = v.slice(4);
|
||||
if (pet === '__inherit__') {
|
||||
delete prefs.ipPets[ip];
|
||||
} else {
|
||||
prefs.ipPets[ip] = pet;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function fmt(date: string): string {
|
||||
return new Date(date).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user