Implementar avatar estilo Windows Live Messenger con sistema de presencia
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 55s

- Agregar componente MsnAvatar con marco SVG de grosor variable
- Implementar degradados radiales estilo Frutiger Aero
- Agregar composable usePresence con detección de inactividad
- Incluir selector de estados: Online, Away, Busy, Offline
- Actualizar UserHeader para usar el nuevo avatar
This commit is contained in:
2025-10-17 13:56:59 -06:00
parent fa19845b8e
commit 6b87902119
3 changed files with 478 additions and 10 deletions

View File

@@ -0,0 +1,124 @@
/**
* Composable para manejar el estado de presencia del usuario
* Inspirado en Windows Live Messenger
*/
export type PresenceStatus = 'online' | 'away' | 'busy' | 'offline'
interface PresenceConfig {
light: string // Color para el highlight (top-left)
medium: string // Color medio
dark: string // Color para la sombra (bottom-right)
}
export const PRESENCE_COLORS: Record<PresenceStatus, PresenceConfig> = {
online: {
light: '#CCFF99',
medium: '#66CC33',
dark: '#339900'
},
away: {
light: '#FFE699',
medium: '#FFCC33',
dark: '#FF9900'
},
busy: {
light: '#FF9999',
medium: '#FF3333',
dark: '#CC0000'
},
offline: {
light: '#CCCCCC',
medium: '#999999',
dark: '#666666'
}
}
export const usePresence = () => {
const status = useState<PresenceStatus>('userPresenceStatus', () => 'online')
const lastActivity = useState<number>('lastActivity', () => Date.now())
// Configuración de tiempos (en milisegundos)
const AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutos de inactividad = Away
let inactivityTimer: NodeJS.Timeout | null = null
// Actualizar última actividad
const updateActivity = () => {
lastActivity.value = Date.now()
// Si estaba away y el usuario vuelve a estar activo, ponerlo online
if (status.value === 'away') {
status.value = 'online'
}
resetInactivityTimer()
}
// Resetear el timer de inactividad
const resetInactivityTimer = () => {
if (inactivityTimer) {
clearTimeout(inactivityTimer)
}
// Solo en el cliente
if (import.meta.client) {
inactivityTimer = setTimeout(() => {
// Solo cambiar a away si está online
if (status.value === 'online') {
status.value = 'away'
}
}, AWAY_TIMEOUT)
}
}
// Cambiar estado manualmente
const setStatus = (newStatus: PresenceStatus) => {
status.value = newStatus
// Si se cambia manualmente, resetear el timer
if (newStatus === 'online') {
resetInactivityTimer()
} else if (inactivityTimer) {
// Si se pone busy o offline manualmente, cancelar el timer de away
clearTimeout(inactivityTimer)
inactivityTimer = null
}
}
// Obtener los colores del estado actual
const colors = computed(() => PRESENCE_COLORS[status.value])
// Inicializar listeners de actividad (solo en cliente)
const initActivityListeners = () => {
if (import.meta.client) {
const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click']
events.forEach(event => {
document.addEventListener(event, updateActivity, { passive: true })
})
// Iniciar el timer
resetInactivityTimer()
// Cleanup cuando se desmonte
onUnmounted(() => {
events.forEach(event => {
document.removeEventListener(event, updateActivity)
})
if (inactivityTimer) {
clearTimeout(inactivityTimer)
}
})
}
}
return {
status: readonly(status),
colors,
setStatus,
updateActivity,
initActivityListeners,
PRESENCE_COLORS
}
}