PHP Classes

File: templates/whos_online.twig

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   templates/whos_online.twig   Download  
File: templates/whos_online.twig
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 7,136 bytes
 

Contents

Class file image Download
{% extends "base.twig" %} {% block title %}{{ t('ui.whos_online.page_title', {}, locale, ['common']) }} - {{ system_name }}{% endblock %} {% block content %} <div class="container mt-4"> <div class="d-flex justify-content-between align-items-center mb-3"> <h1><i class="fas fa-user-friends"></i> {{ t('ui.whos_online.heading', {}, locale, ['common']) }}</h1> <small class="text-muted">{{ t('ui.whos_online.active_last_minutes', {'minutes': online_minutes}, locale, ['common']) }}</small> </div> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h6 class="mb-0">{{ t('ui.whos_online.online_users', {}, locale, ['common']) }}</h6> <span class="badge bg-secondary" id="onlineCount">{{ online_user_count|default(online_users|length) }}</span> </div> <div class="card-body p-0"> <div id="onlineTableWrap" class="{{ online_users|length > 0 ? '' : 'd-none' }}"> <div class="table-responsive"> <table class="table table-striped align-middle mb-0"> <thead> <tr> <th>{{ t('ui.common.username', {}, locale, ['common']) }}</th> <th>{{ t('ui.common.location', {}, locale, ['common']) }}</th> {% if current_user and current_user.is_admin %} <th>{{ t('ui.whos_online.service', {}, locale, ['common']) }}</th> <th>{{ t('ui.whos_online.activity', {}, locale, ['common']) }}</th> <th>{{ t('ui.whos_online.idle', {}, locale, ['common']) }}</th> {% endif %} </tr> </thead> <tbody id="onlineUsersBody"> {% for user in online_users %} <tr> <td> <a href="/profile/{{ user.username|url_encode }}" class="text-decoration-none">{{ user.username }}</a> </td> <td>{{ user.location ?: t('ui.common.unknown', {}, locale, ['common']) }}</td> {% if current_user and current_user.is_admin %} <td><span class="badge bg-secondary">{{ user.service ?: 'web' }}</span></td> <td>{{ user.activity ?: '-' }}</td> <td class="idle-time" data-last-activity-ts="{{ user.last_activity|date('U') }}">-</td> {% endif %} </tr> {% endfor %} </tbody> </table> </div> </div> <div class="p-3 {{ online_users|length > 0 ? 'd-none' : '' }}" id="onlineEmptyState"> <div class="alert alert-secondary mb-0"> <i class="fas fa-user-slash me-2"></i> {{ t('ui.whos_online.no_users_online', {}, locale, ['common']) }} </div> </div> </div> </div> </div> {% endblock %} {% block scripts %} <script> function uiT(key, fallback, params = {}) { if (window.t) { return window.t(key, params, fallback); } return fallback; } const onlineMinutesLabel = document.querySelector('.text-muted'); const onlineCount = document.getElementById('onlineCount'); const onlineTableWrap = document.getElementById('onlineTableWrap'); const onlineUsersBody = document.getElementById('onlineUsersBody'); const onlineEmptyState = document.getElementById('onlineEmptyState'); const isAdmin = !!(window.currentUserIsAdmin && window.currentUserIsAdmin === true); function formatIdle(ts) { if (!ts) return '-'; const diffSec = Math.floor(Date.now() / 1000 - ts); if (diffSec < 0) return '-'; if (diffSec < 60) return diffSec + 's'; const diffMin = Math.floor(diffSec / 60); if (diffMin < 60) return diffMin + 'm'; const diffHr = Math.floor(diffMin / 60); const remMin = diffMin % 60; return remMin ? `${diffHr}h ${remMin}m` : `${diffHr}h`; } function refreshIdleTimes() { document.querySelectorAll('.idle-time[data-last-activity-ts]').forEach(el => { el.textContent = formatIdle(parseInt(el.dataset.lastActivityTs, 10)); }); } function updateOnlineView(payload) { if (!payload || !Array.isArray(payload.users)) { return; } const users = payload.users; const onlineUserCountValue = payload.online_user_count ?? users.length; if (onlineCount) { onlineCount.textContent = onlineUserCountValue; } if (onlineMinutesLabel && payload.online_minutes) { onlineMinutesLabel.textContent = uiT( 'ui.whos_online.active_last_minutes', `Active in last ${payload.online_minutes} minutes`, { minutes: payload.online_minutes } ); } if (users.length === 0) { if (onlineTableWrap) onlineTableWrap.classList.add('d-none'); if (onlineEmptyState) onlineEmptyState.classList.remove('d-none'); return; } if (onlineTableWrap) onlineTableWrap.classList.remove('d-none'); if (onlineEmptyState) onlineEmptyState.classList.add('d-none'); if (onlineUsersBody) { onlineUsersBody.innerHTML = users.map(user => { const location = user.location && user.location !== '' ? user.location : uiT('ui.common.unknown', 'Unknown'); const username = escapeHtml(user.username || ''); const usernameCell = `<a href="/profile/${encodeURIComponent(user.username)}" class="text-decoration-none">${username}</a>`; const ts = user.last_activity_ts || null; const serviceCell = isAdmin ? `<td><span class="badge bg-secondary">${escapeHtml(user.service || 'web')}</span></td>` : ''; const activityCell = isAdmin ? `<td>${user.activity && user.activity !== '' ? escapeHtml(user.activity) : '-'}</td>` : ''; const idleCell = isAdmin ? `<td class="idle-time" data-last-activity-ts="${ts}">${formatIdle(ts)}</td>` : ''; return `<tr> <td>${usernameCell}</td> <td>${escapeHtml(location)}</td> ${serviceCell} ${activityCell} ${idleCell} </tr>`; }).join(''); } } function refreshOnline() { fetch('/api/whosonline') .then(res => res.json()) .then(updateOnlineView) .catch(() => {}); } refreshIdleTimes(); setInterval(refreshIdleTimes, 10000); setInterval(refreshOnline, 60000); </script> {% endblock %}