Fix current match killfeed visible limit and polling stability
This commit is contained in:
@@ -29,6 +29,7 @@ Preservar la identidad HLL Vietnam: militar, Vietnam, tactica y sobria. No tocar
|
|||||||
4. Limitar en frontend el numero de eventos renderizados para mostrar solo filas completas.
|
4. Limitar en frontend el numero de eventos renderizados para mostrar solo filas completas.
|
||||||
5. Mantener dos columnas solo con ancho suficiente y pasar a una columna en ancho medio/mobile.
|
5. Mantener dos columnas solo con ancho suficiente y pasar a una columna en ancho medio/mobile.
|
||||||
6. Validar visualmente las vistas de partida actual para `comunidad-hispana-01` y `comunidad-hispana-02`.
|
6. Validar visualmente las vistas de partida actual para `comunidad-hispana-01` y `comunidad-hispana-02`.
|
||||||
|
7. Restaurar ventana reciente reemplazada para el killfeed, con deduplicacion defensiva y sin cache/TTL adicional.
|
||||||
|
|
||||||
## Files to Read First
|
## Files to Read First
|
||||||
|
|
||||||
@@ -45,9 +46,9 @@ Preservar la identidad HLL Vietnam: militar, Vietnam, tactica y sobria. No tocar
|
|||||||
|
|
||||||
- `frontend/assets/css/historico.css`
|
- `frontend/assets/css/historico.css`
|
||||||
- `frontend/assets/js/partida-actual.js`
|
- `frontend/assets/js/partida-actual.js`
|
||||||
- `ai/tasks/done/TASK-261-improve-current-match-killfeed-layout-for-normalized-weapon-icons.md`
|
- `ai/tasks/in-progress/TASK-261-improve-current-match-killfeed-layout-for-normalized-weapon-icons.md`
|
||||||
|
|
||||||
`frontend/assets/js/partida-actual.js` se modifica para limitar los eventos visibles por breakpoint.
|
`frontend/assets/js/partida-actual.js` se modifica para limitar los eventos visibles por breakpoint y para reemplazar la ventana reciente del feed cuando llega una respuesta valida.
|
||||||
|
|
||||||
## Constraints
|
## Constraints
|
||||||
|
|
||||||
@@ -81,10 +82,12 @@ Diagnostico corregido:
|
|||||||
- La correccion anterior quitaba cortes, pero convertia `#current-match-feed-list.current-match-killfeed` en nodo scrolleable con `max-height: 620px`, `overflow-y: auto`, `scroll-padding-bottom` y `scrollbar-gutter`.
|
- La correccion anterior quitaba cortes, pero convertia `#current-match-feed-list.current-match-killfeed` en nodo scrolleable con `max-height: 620px`, `overflow-y: auto`, `scroll-padding-bottom` y `scrollbar-gutter`.
|
||||||
- El comportamiento correcto es renderizar solo las bajas recientes que caben completas y ocultar eventos mas antiguos cuando hay demasiados.
|
- El comportamiento correcto es renderizar solo las bajas recientes que caben completas y ocultar eventos mas antiguos cuando hay demasiados.
|
||||||
- Analisis de parpadeo posterior:
|
- Analisis de parpadeo posterior:
|
||||||
- El comportamiento anterior correcto del feed era pedir la lista completa con `/api/current-match/kills?...&limit=18` en cada ciclo y renderizar sobre el estado acumulado.
|
- El comportamiento anterior correcto del feed era pedir la lista completa con `/api/current-match/kills?...&limit=18` en cada ciclo y reemplazar la ventana reciente renderizable.
|
||||||
- El cambio que altera el flujo no viene del limite visual de TASK-261; viene de `d558ac8 feat: refine current match live feed`, que anadio `since_event_id` y limpiaba `state.byId` cuando el endpoint respondia `scope: no-current-match-events`.
|
- `d558ac8 feat: refine current match live feed` introdujo `since_event_id` y limpiaba `state.byId` cuando el endpoint respondia `scope: no-current-match-events`.
|
||||||
- En produccion se observo el ciclo exacto: request sin cursor devuelve 18 bajas; request siguiente con `since_event_id` devuelve `no-current-match-events` con `items: 0`; el JS borra `state.byId`; el siguiente ciclo vuelve a pedir sin cursor y reaparece la lista.
|
- En produccion se observo el ciclo exacto: request sin cursor devuelve 18 bajas; request siguiente con `since_event_id` devuelve `no-current-match-events` con `items: 0`; el JS borra `state.byId`; el siguiente ciclo vuelve a pedir sin cursor y reaparece la lista.
|
||||||
- Por eso el feed alternaba entre lista visible y estado vacio.
|
- Por eso el feed alternaba entre lista visible y estado vacio.
|
||||||
|
- La version posterior ya elimino `since_event_id`, pero seguia usando `state.byId` como acumulador local indefinido por `event_id`.
|
||||||
|
- Ese acumulador podia conservar eventos viejos o incompletos, duplicar bajas si `event_id` cambiaba, y mezclar metadata `unknown` con metadata correcta para el mismo jugador.
|
||||||
|
|
||||||
Cambios aplicados:
|
Cambios aplicados:
|
||||||
|
|
||||||
@@ -102,13 +105,33 @@ Cambios aplicados:
|
|||||||
- Si hay mas eventos almacenados que visibles, el estado muestra: `Mostrando las últimas N bajas detectadas.`
|
- Si hay mas eventos almacenados que visibles, el estado muestra: `Mostrando las últimas N bajas detectadas.`
|
||||||
- Se restauro el patron de peticion anterior eliminando `since_event_id` del request de killfeed.
|
- Se restauro el patron de peticion anterior eliminando `since_event_id` del request de killfeed.
|
||||||
- Se ajusto la limpieza de `no-current-match-events`: solo limpia cuando no hay bajas validas en memoria. Una respuesta vacia transitoria ya no borra una lista valida.
|
- Se ajusto la limpieza de `no-current-match-events`: solo limpia cuando no hay bajas validas en memoria. Una respuesta vacia transitoria ya no borra una lista valida.
|
||||||
|
- Reapertura actual:
|
||||||
|
- `loadKillFeed()` mantiene la request sin `since_event_id`.
|
||||||
|
- Cuando `data.items` trae eventos validos, `renderKillFeed()` construye una ventana nueva desde esa respuesta y reemplaza `state.byId` con la ventana deduplicada.
|
||||||
|
- La deduplicacion usa `event_id` cuando existe y clave semantica defensiva con `server_time`, `event_timestamp`, `killer_name`, `victim_name` y `weapon` para detectar duplicados con distinto id.
|
||||||
|
- Si dos filas representan la misma baja, se conserva la version con mejor metadata: equipos conocidos sobre `unknown` y nombres reales sobre placeholders.
|
||||||
|
- Si `data.items` llega vacio, el feed visible se conserva salvo que no hubiera eventos o exista cambio claro de contexto por servidor/partida/mapa.
|
||||||
- No se anadio cache/TTL porque el problema era el cursor incremental borrando estado valido, no la ausencia de una politica de cache.
|
- No se anadio cache/TTL porque el problema era el cursor incremental borrando estado valido, no la ausencia de una politica de cache.
|
||||||
- Se mantienen las mejoras previas: filas de `min-height: 74px`, columna de arma `128px`, mobile `104px`, ellipsis en nombres/labels y una columna desde `max-width: 1280px`.
|
- Se mantienen las mejoras previas: filas de `min-height: 74px`, columna de arma `128px`, mobile `104px`, ellipsis en nombres/labels y una columna desde `max-width: 1280px`.
|
||||||
|
|
||||||
Validacion ejecutada:
|
Validacion ejecutada:
|
||||||
|
|
||||||
- `node --check frontend/assets/js/partida-actual.js`: OK.
|
- `node --check frontend/assets/js/partida-actual.js`: OK.
|
||||||
- Browser plugin: no se pudo usar porque la herramienta requerida `node_repl js` no esta expuesta; se uso Playwright local como fallback.
|
- Reapertura actual:
|
||||||
|
- `node --check frontend/assets/js/partida-actual.js`: OK.
|
||||||
|
- Fixture Node/DOM simulado: OK para respuesta con 20 kills -> 12/6/5, respuesta vacia posterior preservada, metadata `unknown` reemplazada por equipos conocidos, duplicado semantico con distinto `event_id` renderizado una sola vez, y ventana nueva reemplazando la anterior.
|
||||||
|
- Fixture Node/DOM simulado para `comunidad-hispana-01` y `comunidad-hispana-02`: OK sin `TypeError` sincronico con `#current-match-history` ausente.
|
||||||
|
- Chrome headless local con frontend real y APIs simuladas: OK para desktop 1440px con 12 filas, 1280px con 6 filas y 480px con 5 filas; en los tres casos `clippedRows: 0`, `overflow-y: visible` y `scrollHeight == clientHeight`.
|
||||||
|
- Chrome headless local con secuencia real de polling simulada: OK para 20 kills -> respuesta vacia -> metadata mejorada -> duplicado semantico -> kills nuevas; no bajo a 0 filas tras vacio, conservo metadata de equipo conocido, renderizo un solo duplicado y actualizo a la ventana nueva.
|
||||||
|
- Chrome headless local contra backend publico de produccion durante varios ciclos: OK para `comunidad-hispana-01` y `comunidad-hispana-02`; 10 requests de killfeed por servidor, muestras estables `[0,0,0,0,0,0,0,0]`, sin alternancia lista/vacio, sin duplicados semanticos y sin `TypeError`.
|
||||||
|
- Nota de produccion: durante esta validacion publica ambos servidores devolvieron 0 bajas recientes, por lo que duplicados y mejora de metadata se validaron con fixture visual en navegador real.
|
||||||
|
- Browser plugin: no se pudo usar porque `node_repl js` no esta expuesto por tool discovery.
|
||||||
|
- Playwright Python local: no usable por `ModuleNotFoundError: No module named 'greenlet._greenlet'`.
|
||||||
|
- Playwright .NET CLI local: no usable porque no encuentra proyecto Playwright en el repositorio.
|
||||||
|
- Endpoints publicos con `GET`: `200` en `/api/current-match`, `/api/current-match/kills?limit=18` y `/api/current-match/players` para `comunidad-hispana-01` y `comunidad-hispana-02`.
|
||||||
|
- `HEAD` en esos endpoints responde `501`; se descarta para esta validacion porque el contrato real es `GET`.
|
||||||
|
- `git diff --name-only` revisado: mis cambios de TASK-261 son el JS y el movimiento/documentacion de la task; persisten cambios previos no relacionados en `ai/system-metrics.md`, assets de clans/mapas/weapons, TASK-204/TASK-242 y `tmp/`.
|
||||||
|
- Validacion previa: Browser plugin no se pudo usar porque la herramienta requerida `node_repl js` no estaba expuesta; en esa ejecucion se uso Playwright local como fallback.
|
||||||
- Fixture larga de 20 kills interceptando las APIs de la pagina real:
|
- Fixture larga de 20 kills interceptando las APIs de la pagina real:
|
||||||
- desktop `1440px`: renderiza 12 kills, 2 columnas, `overflow-y: visible`, `scrollHeight == clientHeight`, sin scrollbar interno, `clippedRows: 0`, copy `Mostrando las últimas 12 bajas detectadas.`
|
- desktop `1440px`: renderiza 12 kills, 2 columnas, `overflow-y: visible`, `scrollHeight == clientHeight`, sin scrollbar interno, `clippedRows: 0`, copy `Mostrando las últimas 12 bajas detectadas.`
|
||||||
- medio `1100px`: renderiza 6 kills, 1 columna, `overflow-y: visible`, `scrollHeight == clientHeight`, sin scrollbar interno, `clippedRows: 0`, copy `Mostrando las últimas 6 bajas detectadas.`
|
- medio `1100px`: renderiza 6 kills, 1 columna, `overflow-y: visible`, `scrollHeight == clientHeight`, sin scrollbar interno, `clippedRows: 0`, copy `Mostrando las últimas 6 bajas detectadas.`
|
||||||
@@ -135,7 +158,7 @@ Validacion ejecutada:
|
|||||||
|
|
||||||
Alcance:
|
Alcance:
|
||||||
|
|
||||||
- TASK-261 queda en `ai/tasks/done` para cierre y commit aislado.
|
- TASK-261 queda cerrada en `ai/tasks/done` tras validacion visual/manual de estabilidad.
|
||||||
- No se tocaron assets fisicos, SVGs, PNGs, `frontend/assets/img/weapons/`, backend, scheduler, RCON, TeamKills, Elo/MMR, mapas, clans, brands, configuracion de servidores, `27001`, `ai/system-metrics.md` ni TASK-204.
|
- No se tocaron assets fisicos, SVGs, PNGs, `frontend/assets/img/weapons/`, backend, scheduler, RCON, TeamKills, Elo/MMR, mapas, clans, brands, configuracion de servidores, `27001`, `ai/system-metrics.md` ni TASK-204.
|
||||||
|
|
||||||
## Change Budget
|
## Change Budget
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ function initializeKillFeed(nodes) {
|
|||||||
return {
|
return {
|
||||||
byId: new Map(),
|
byId: new Map(),
|
||||||
latestEventId: "",
|
latestEventId: "",
|
||||||
|
contextSignature: "",
|
||||||
visibleSignature: "",
|
visibleSignature: "",
|
||||||
visibleCount: 0,
|
visibleCount: 0,
|
||||||
};
|
};
|
||||||
@@ -221,22 +222,51 @@ function renderKillFeed(data, nodes, state) {
|
|||||||
if (!nodes.feedList || !nodes.feedState) {
|
if (!nodes.feedList || !nodes.feedState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const incoming = Array.isArray(data.items) ? data.items : [];
|
const incoming = Array.isArray(data.items)
|
||||||
const hasVisibleEvents = state.byId.size > 0;
|
? data.items.filter(isValidKillFeedEvent)
|
||||||
if (data.scope === "no-current-match-events" && !hasVisibleEvents) {
|
: [];
|
||||||
state.byId.clear();
|
const contextSignature = getKillFeedContextSignature(data);
|
||||||
state.latestEventId = "";
|
const hasStoredEvents = state.byId.size > 0;
|
||||||
|
const contextChanged =
|
||||||
|
Boolean(contextSignature) &&
|
||||||
|
Boolean(state.contextSignature) &&
|
||||||
|
contextSignature !== state.contextSignature;
|
||||||
|
if (incoming.length > 0) {
|
||||||
|
const events = buildKillFeedWindow(incoming)
|
||||||
|
.sort(compareKillFeedEvents)
|
||||||
|
.slice(-CURRENT_MATCH_KILL_FEED_LIMIT);
|
||||||
|
replaceKillFeedWindow(state, events, contextSignature);
|
||||||
|
renderKillFeedEvents({
|
||||||
|
data,
|
||||||
|
events,
|
||||||
|
nodes,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasStoredEvents || contextChanged) {
|
||||||
|
replaceKillFeedWindow(state, [], contextSignature || state.contextSignature);
|
||||||
|
renderKillFeedEvents({
|
||||||
|
data,
|
||||||
|
events: [],
|
||||||
|
nodes,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
incoming.forEach((event) => {
|
|
||||||
if (event?.event_id) {
|
|
||||||
state.byId.set(event.event_id, event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const events = [...state.byId.values()]
|
const events = [...state.byId.values()]
|
||||||
.sort(compareKillFeedEvents)
|
.sort(compareKillFeedEvents)
|
||||||
.slice(-CURRENT_MATCH_KILL_FEED_LIMIT);
|
.slice(-CURRENT_MATCH_KILL_FEED_LIMIT);
|
||||||
state.byId = new Map(events.map((event) => [event.event_id, event]));
|
replaceKillFeedWindow(state, events, state.contextSignature);
|
||||||
state.latestEventId = events[events.length - 1]?.event_id || state.latestEventId;
|
renderKillFeedEvents({
|
||||||
|
data: { ...data, scope: "preserved-recent-window" },
|
||||||
|
events,
|
||||||
|
nodes,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderKillFeedEvents({ data, events, nodes, state }) {
|
||||||
if (events.length === 0) {
|
if (events.length === 0) {
|
||||||
nodes.feedList.innerHTML = "";
|
nodes.feedList.innerHTML = "";
|
||||||
state.visibleSignature = "";
|
state.visibleSignature = "";
|
||||||
@@ -246,9 +276,7 @@ function renderKillFeed(data, nodes, state) {
|
|||||||
}
|
}
|
||||||
const visibleLimit = getKillFeedVisibleLimit();
|
const visibleLimit = getKillFeedVisibleLimit();
|
||||||
const visualEvents = events.slice(-visibleLimit);
|
const visualEvents = events.slice(-visibleLimit);
|
||||||
const visibleSignature = `${visibleLimit}:${visualEvents
|
const visibleSignature = getKillFeedVisibleSignature(visualEvents, visibleLimit);
|
||||||
.map((event) => event.event_id)
|
|
||||||
.join("|")}`;
|
|
||||||
if (visibleSignature !== state.visibleSignature) {
|
if (visibleSignature !== state.visibleSignature) {
|
||||||
nodes.feedList.innerHTML = renderKillFeedColumns(visualEvents);
|
nodes.feedList.innerHTML = renderKillFeedColumns(visualEvents);
|
||||||
state.visibleSignature = visibleSignature;
|
state.visibleSignature = visibleSignature;
|
||||||
@@ -262,6 +290,146 @@ function renderKillFeed(data, nodes, state) {
|
|||||||
nodes.feedState.classList.remove("historical-state--error");
|
nodes.feedState.classList.remove("historical-state--error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function replaceKillFeedWindow(state, events, contextSignature) {
|
||||||
|
state.byId = new Map(
|
||||||
|
events.map((event) => [getKillFeedWindowKey(event), event]),
|
||||||
|
);
|
||||||
|
state.latestEventId = events[events.length - 1]?.event_id || "";
|
||||||
|
state.contextSignature = contextSignature || state.contextSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKillFeedWindow(events) {
|
||||||
|
const deduped = [];
|
||||||
|
const indexByEventId = new Map();
|
||||||
|
const indexBySemanticKey = new Map();
|
||||||
|
events.forEach((event) => {
|
||||||
|
const eventIdKey = getKillFeedEventIdKey(event);
|
||||||
|
const semanticKey = getKillFeedSemanticKey(event);
|
||||||
|
const existingIndex =
|
||||||
|
(eventIdKey ? indexByEventId.get(eventIdKey) : undefined) ??
|
||||||
|
(semanticKey ? indexBySemanticKey.get(semanticKey) : undefined);
|
||||||
|
if (existingIndex !== undefined) {
|
||||||
|
deduped[existingIndex] = pickBetterKillFeedEvent(
|
||||||
|
deduped[existingIndex],
|
||||||
|
event,
|
||||||
|
);
|
||||||
|
if (eventIdKey) {
|
||||||
|
indexByEventId.set(eventIdKey, existingIndex);
|
||||||
|
}
|
||||||
|
if (semanticKey) {
|
||||||
|
indexBySemanticKey.set(semanticKey, existingIndex);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextIndex = deduped.length;
|
||||||
|
deduped.push(event);
|
||||||
|
if (eventIdKey) {
|
||||||
|
indexByEventId.set(eventIdKey, nextIndex);
|
||||||
|
}
|
||||||
|
if (semanticKey) {
|
||||||
|
indexBySemanticKey.set(semanticKey, nextIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return deduped;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickBetterKillFeedEvent(current, candidate) {
|
||||||
|
if (!current) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return getKillFeedMetadataScore(candidate) > getKillFeedMetadataScore(current)
|
||||||
|
? candidate
|
||||||
|
: current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedMetadataScore(event) {
|
||||||
|
return [
|
||||||
|
isKnownKillFeedTeam(event?.killer_team),
|
||||||
|
isKnownKillFeedTeam(event?.victim_team),
|
||||||
|
isAvailableKillFeedName(event?.killer_name, "Jugador no disponible"),
|
||||||
|
isAvailableKillFeedName(event?.victim_name, "Objetivo no disponible"),
|
||||||
|
Boolean(String(event?.weapon || "").trim()),
|
||||||
|
Boolean(String(event?.event_timestamp || "").trim()),
|
||||||
|
Number.isFinite(Number(event?.server_time)),
|
||||||
|
Boolean(String(event?.event_id || "").trim()),
|
||||||
|
].filter(Boolean).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidKillFeedEvent(event) {
|
||||||
|
return Boolean(
|
||||||
|
event &&
|
||||||
|
typeof event === "object" &&
|
||||||
|
(getKillFeedEventIdKey(event) || getKillFeedSemanticKey(event)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedWindowKey(event) {
|
||||||
|
return getKillFeedSemanticKey(event) || getKillFeedEventIdKey(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedEventIdKey(event) {
|
||||||
|
const eventId = String(event?.event_id || "").trim();
|
||||||
|
return eventId ? `event:${eventId}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedSemanticKey(event) {
|
||||||
|
const parts = [
|
||||||
|
event?.server_time,
|
||||||
|
event?.event_timestamp,
|
||||||
|
event?.killer_name,
|
||||||
|
event?.victim_name,
|
||||||
|
event?.weapon,
|
||||||
|
].map((value) => normalizeKillFeedDedupeValue(value));
|
||||||
|
return parts.every(Boolean) ? `semantic:${parts.join("|")}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedVisibleSignature(events, visibleLimit) {
|
||||||
|
return `${visibleLimit}:${events
|
||||||
|
.map((event) =>
|
||||||
|
[
|
||||||
|
getKillFeedWindowKey(event),
|
||||||
|
normalizeKillFeedDedupeValue(event?.killer_name),
|
||||||
|
normalizeKillFeedDedupeValue(event?.victim_name),
|
||||||
|
normalizeKillFeedDedupeValue(event?.killer_team),
|
||||||
|
normalizeKillFeedDedupeValue(event?.victim_team),
|
||||||
|
normalizeKillFeedDedupeValue(event?.weapon),
|
||||||
|
event?.is_teamkill ? "1" : "0",
|
||||||
|
].join(":"),
|
||||||
|
)
|
||||||
|
.join("|")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKillFeedContextSignature(data) {
|
||||||
|
return [
|
||||||
|
data?.server_slug,
|
||||||
|
data?.match_id,
|
||||||
|
data?.map,
|
||||||
|
data?.map_id,
|
||||||
|
data?.layer_id,
|
||||||
|
data?.map_pretty_name,
|
||||||
|
]
|
||||||
|
.map((value) => normalizeKillFeedDedupeValue(value))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeKillFeedDedupeValue(value) {
|
||||||
|
return String(value ?? "").trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isKnownKillFeedTeam(value) {
|
||||||
|
return getPlayerTeamDisplay(value).key !== "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAvailableKillFeedName(value, fallback) {
|
||||||
|
const normalized = normalizeLookupText(value);
|
||||||
|
return Boolean(
|
||||||
|
normalized &&
|
||||||
|
normalized !== normalizeLookupText(fallback) &&
|
||||||
|
normalized !== normalizeLookupText("No disponible"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getKillFeedVisibleLimit() {
|
function getKillFeedVisibleLimit() {
|
||||||
if (window.matchMedia("(max-width: 480px)").matches) {
|
if (window.matchMedia("(max-width: 480px)").matches) {
|
||||||
return CURRENT_MATCH_KILL_FEED_VISIBLE_LIMIT_MOBILE;
|
return CURRENT_MATCH_KILL_FEED_VISIBLE_LIMIT_MOBILE;
|
||||||
@@ -712,7 +880,10 @@ function formatDuration(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatKillFeedCoverage(scope, visibleCount = 0, totalCount = 0) {
|
function formatKillFeedCoverage(scope, visibleCount = 0, totalCount = 0) {
|
||||||
if (visibleCount > 0 && totalCount > visibleCount) {
|
if (
|
||||||
|
visibleCount > 0 &&
|
||||||
|
(totalCount > visibleCount || scope === "preserved-recent-window")
|
||||||
|
) {
|
||||||
return `Mostrando las últimas ${visibleCount} bajas detectadas.`;
|
return `Mostrando las últimas ${visibleCount} bajas detectadas.`;
|
||||||
}
|
}
|
||||||
if (scope === "open-admin-log-match-window") {
|
if (scope === "open-admin-log-match-window") {
|
||||||
|
|||||||
Reference in New Issue
Block a user