feat: mark the boundary between forums and topics in the list view

A forum page carries its subforums and its topics in tables of their own,
each with a header. Rebuilding the page into one stream of cards dissolves
that separation: both kinds render identically, so where the subforums end
and the topics begin was no longer visible, and a single card gave no hint
whether tapping it opens a forum or a thread.

A header naming the kind and its count is therefore inserted wherever the
kind changes, and forum cards are tinted and edged. Directly below a
category heading the header is suppressed — on the forum root that heading
already introduces the forums under it, and repeating the kind there adds
a strip per category for nothing.

Counting requires knowing how many cards a block actually yields, which is
not the number of rows: rows that turn out to be layout or fail conversion
drop out. The cards are therefore built completely before any of them is
emitted.
This commit is contained in:
Andre
2026-08-02 15:11:47 +02:00
parent 3609d9e151
commit 4ce48b3875
3 changed files with 75 additions and 10 deletions
+6
View File
@@ -65,6 +65,12 @@ verschwindet und nur die Beschreibung bleibt als `.rm-last` uebrig. Genau so
war es vor 3.2. Brotkrumen und „Mark forums read" verlinken ebenfalls Foren, war es vor 3.2. Brotkrumen und „Mark forums read" verlinken ebenfalls Foren,
stehen aber in Zeilen ohne Forenmerkmale und werden verworfen. stehen aber in Zeilen ohne Forenmerkmale und werden verworfen.
**Der Gruppenkopf entfaellt hinter einer Kategoriezeile.** Sonst stuende auf
der Forenwurzel unter jeder Kategorie noch ein „FOREN n" — die Kategorie leitet
die Foren darunter bereits ein. Die Karten werden deshalb erst vollstaendig
gebaut und dann ausgegeben: die Anzahl im Kopf steht erst fest, wenn feststeht,
welche Zeilen ueberhaupt eine Karte hergeben.
**Das Seitengeruest ist selbst eine Tabelle.** `closest('tr')` liefert fuer **Das Seitengeruest ist selbst eine Tabelle.** `closest('tr')` liefert fuer
einen Link ausserhalb einer Listenzeile die Layoutzeile, die die ganze Seite einen Link ausserhalb einer Listenzeile die Layoutzeile, die die ganze Seite
enthaelt. Ohne Bremse wurde daraus eine Karte mit dem kompletten Seitentext — enthaelt. Ohne Bremse wurde daraus eine Karte mit dem kompletten Seitentext —
+14
View File
@@ -27,6 +27,13 @@ card; category headers become section headings. Rows are recognised by phpBB's
own `forumlink` / `forumdesc` classes, and failing that by their shape — two own `forumlink` / `forumdesc` classes, and failing that by their shape — two
purely numeric columns are only ever a forum's topic and post count. purely numeric columns are only ever a forum's topic and post count.
Where forums and topics end up in the same stream of cards, a header strip
naming the kind and its count (`FOREN 4`, `THEMEN 3`) is inserted wherever the
kind changes, and forum cards additionally carry a coloured left edge. The
original page separates the two by putting them in tables of their own; that
boundary is otherwise lost. The strip is left out directly below
a category heading, which already introduces the forums under it.
The thread navigation bar holds first / previous / next / last and a page The thread navigation bar holds first / previous / next / last and a page
indicator. Tapping the indicator opens a sheet with a number field and a grid of indicator. Tapping the indicator opens a sheet with a number field and a grid of
all pages. Targets are computed as `start = (page - 1) × per_page` rather than all pages. Targets are computed as `start = (page - 1) × per_page` rather than
@@ -199,6 +206,13 @@ Zwischenüberschriften. Erkannt werden die Zeilen an phpBBs eigenen Klassen
`forumlink` / `forumdesc` und ersatzweise an ihrer Form — zwei rein numerische `forumlink` / `forumdesc` und ersatzweise an ihrer Form — zwei rein numerische
Spalten hat nur eine Forenzeile mit Themen- und Beitragszahl. Spalten hat nur eine Forenzeile mit Themen- und Beitragszahl.
Stehen Foren und Themen im selben Kartenstrom, wird bei jedem Wechsel ein
Gruppenkopf mit Art und Anzahl eingezogen (`FOREN 4`, `THEMEN 3`); Forenkarten
tragen zusätzlich eine farbige linke Kante. Die Originalseite trennt beide über
eigene Tabellen mit eigenem Kopf — diese Grenze geht sonst verloren.
Direkt unter einer Kategorieüberschrift entfällt der
Gruppenkopf, weil die Überschrift die Foren darunter schon einleitet.
Die Navigationsleiste im Thread enthält erste / vorherige / nächste / letzte Die Navigationsleiste im Thread enthält erste / vorherige / nächste / letzte
Seite und eine Positionsanzeige. Ein Tipp darauf öffnet ein Sheet mit Seite und eine Positionsanzeige. Ein Tipp darauf öffnet ein Sheet mit
Zahlenfeld und einem Raster aller Seiten. Die Ziele werden als Zahlenfeld und einem Raster aller Seiten. Die Ziele werden als
+55 -10
View File
@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name RIN Mobile // @name RIN Mobile
// @description Mobile Ansicht fuer cs.rin.ru: Themenlisten als Karten, Threads als Beitragsstrom // @description Mobile Ansicht fuer cs.rin.ru: Themenlisten als Karten, Threads als Beitragsstrom
// @version 3.2 // @version 3.3
// @match *://cs.rin.ru/forum/* // @match *://cs.rin.ru/forum/*
// @run-at document-start // @run-at document-start
// ==/UserScript== // ==/UserScript==
@@ -98,6 +98,16 @@
border:1px solid rgba(128,128,128,.4); border:1px solid rgba(128,128,128,.4);
text-decoration:none !important; color:inherit !important; } text-decoration:none !important; color:inherit !important; }
/* Foren und Themen stehen auf einer Forenseite in derselben Tabelle
untereinander. Im Kartenstrom unterscheiden sie sich sonst nur am Text,
der Uebergang von den Unterforen zu den Themen ist nicht mehr zu sehen. */
.rm-group { display:flex; align-items:baseline; justify-content:space-between;
gap:10px; padding:11px 14px; font-size:12px; font-weight:700;
text-transform:uppercase; letter-spacing:.07em; opacity:.75;
background:rgba(128,128,128,.16);
border-bottom:1px solid rgba(128,128,128,.28); }
.rm-group .rm-count { font-weight:600; letter-spacing:0; opacity:.7; }
.rm-card { box-sizing:border-box !important; .rm-card { box-sizing:border-box !important;
display:block !important; display:block !important;
width:100% !important; max-width:100% !important; width:100% !important; max-width:100% !important;
@@ -105,6 +115,8 @@
padding:14px 14px 15px !important; padding:14px 14px 15px !important;
border-bottom:1px solid rgba(128,128,128,.28) !important; } border-bottom:1px solid rgba(128,128,128,.28) !important; }
.rm-card:active { background:rgba(128,128,128,.14); } .rm-card:active { background:rgba(128,128,128,.14); }
.rm-card.rm-forum { border-left:3px solid rgba(120,170,255,.7);
background:rgba(120,170,255,.07); }
.rm-main { box-sizing:border-box !important; .rm-main { box-sizing:border-box !important;
display:block !important; display:block !important;
width:100% !important; max-width:100% !important; width:100% !important; max-width:100% !important;
@@ -303,9 +315,9 @@
// Karten teilen sich Titel, Zahlenzeile und Fusszeile; nur die Quellen // Karten teilen sich Titel, Zahlenzeile und Fusszeile; nur die Quellen
// dafuer unterscheiden sich zwischen Thema und Forum. // dafuer unterscheiden sich zwischen Thema und Forum.
function card(titleA, badgeable) { function card(titleA, badgeable, kind) {
var el = document.createElement('div'); var el = document.createElement('div');
el.className = 'rm-card'; el.className = 'rm-card' + (kind === 'forum' ? ' rm-forum' : '');
var main = document.createElement('a'); var main = document.createElement('a');
main.className = 'rm-main'; main.className = 'rm-main';
@@ -375,7 +387,7 @@
} }
}); });
var el = card(titleA, true); var el = card(titleA, true, 'thema');
var meta = metaRow([author, var meta = metaRow([author,
nums[0] ? nums[0] + ' Antworten' : '', nums[0] ? nums[0] + ' Antworten' : '',
nums[1] ? nums[1] + ' Views' : '']); nums[1] ? nums[1] + ' Views' : '']);
@@ -403,7 +415,7 @@
} }
}); });
var el = card(titleA, false); var el = card(titleA, false, 'forum');
// Beschreibung, Moderatoren und Unterforen benutzen denselben Absatztyp. // Beschreibung, Moderatoren und Unterforen benutzen denselben Absatztyp.
// Die beiden letzten sind nur an ihrer Einleitung zu erkennen. // Die beiden letzten sind nur an ihrer Einleitung zu erkennen.
@@ -481,6 +493,22 @@
return el; return el;
} }
// Kopf ueber einem Block gleichartiger Karten. Die Originalseite trennt Foren
// und Themen ueber zwei Tabellen mit eigenem Kopf; im Kartenstrom geht diese
// Grenze verloren.
function groupHead(kind, n) {
var el = document.createElement('div');
el.className = 'rm-group';
var t = document.createElement('span');
t.textContent = kind === 'forum' ? 'Foren' : 'Themen';
el.appendChild(t);
var c = document.createElement('span');
c.className = 'rm-count';
c.textContent = n;
el.appendChild(c);
return el;
}
// Welche Karte eine Zeile ergibt, entscheidet der erste verwertbare Link // Welche Karte eine Zeile ergibt, entscheidet der erste verwertbare Link
// darin. Beiwerk wie die Brotkrumen oder "Mark topics read" verlinkt // darin. Beiwerk wie die Brotkrumen oder "Mark topics read" verlinkt
// ebenfalls auf viewforum.php, steht aber in keiner Zeile, die eine Karte // ebenfalls auf viewforum.php, steht aber in keiner Zeile, die eine Karte
@@ -533,17 +561,34 @@
var root = document.createElement('div'); var root = document.createElement('div');
root.className = 'rm-root'; root.className = 'rm-root';
var built = 0; // Erst alle Karten bauen, dann ausgeben: der Gruppenkopf nennt die Zahl der
// Karten unter sich, und die steht erst fest, wenn die Zeilen durch sind,
// von denen manche keine Karte hergeben.
var items = [];
rows.forEach(function (r) { rows.forEach(function (r) {
try { try {
var c = r.kind === 'thema' ? convertTopic(r.tr) var c = r.kind === 'thema' ? convertTopic(r.tr)
: r.kind === 'forum' ? convertForum(r.tr) : r.kind === 'forum' ? convertForum(r.tr)
: section(r.a); : section(r.a);
if (!c) return; if (c) items.push({ kind: r.kind, el: c });
root.appendChild(c);
if (r.kind !== 'sektion') built++;
} catch (e) { log.push('convert: ' + e.message); } } catch (e) { log.push('convert: ' + e.message); }
}); });
var built = 0, last = '';
items.forEach(function (it, i) {
// Eine Kategoriezeile leitet die Foren darunter bereits ein — auf der
// Forenwurzel wuerde ein Gruppenkopf sie nur doppeln.
if (it.kind !== 'sektion') {
if (it.kind !== last && last !== 'sektion') {
var n = 0;
while (i + n < items.length && items[i + n].kind === it.kind) n++;
root.appendChild(groupHead(it.kind, n));
}
built++;
}
last = it.kind;
root.appendChild(it.el);
});
log.push('karten=' + built); log.push('karten=' + built);
if (!built) return; if (!built) return;
@@ -946,7 +991,7 @@
if (DEBUG) { if (DEBUG) {
var d = document.createElement('div'); var d = document.createElement('div');
d.className = 'rm-dbg'; d.className = 'rm-dbg';
d.textContent = 'RIN Mobile v3.2\n' + log.join('\n'); d.textContent = 'RIN Mobile v3.3\n' + log.join('\n');
document.body.insertBefore(d, document.body.firstChild); document.body.insertBefore(d, document.body.firstChild);
} }
} }