feat: rebuild the forum index and post-login pages into the mobile view

The list view already handled every table row shape a forum page uses,
but the switch only ever routed viewforum.php and search.php into it;
index.php arrived untouched, so the very first page a visitor lands on
stayed in the desktop layout. It matches the same category/forum row
structure a viewforum.php category page already produces, so no new
building logic was needed, only the routing.

Submitting the login form reveals a second gap: it re-renders the same
URL with a generic confirmation page instead of the form, which the
existing form-lookup silently ignored, leaving that page unstyled too
right before phpBB's own redirect fires. It shares its shape with any
other message phpBB might render there (a wrong password, for example),
so the fix recognises it structurally rather than by its wording, and
carries the original message cell over unchanged the same way the login
form itself is reused rather than rebuilt.
This commit is contained in:
Andre
2026-08-02 16:28:13 +02:00
parent 27551e6937
commit aec883e7c9
3 changed files with 124 additions and 32 deletions
+41 -12
View File
@@ -10,7 +10,9 @@ die `.user.js`-Datei ist das Produkt.
| `dev/rin-mobile.dev.user.js` | zusaetzlich `localhost:*` | Loader, bindet per `@require file:///` das echte Script ein | | `dev/rin-mobile.dev.user.js` | zusaetzlich `localhost:*` | Loader, bindet per `@require file:///` das echte Script ein |
| `dev/viewtopic.php` | — | Fixture: gespeicherter Thread, 1114 Seiten | | `dev/viewtopic.php` | — | Fixture: gespeicherter Thread, 1114 Seiten |
| `dev/viewforum.php` | — | Fixture: Main Forum, 610 Seiten, mit Unterforen, Ankuendigungen und Stickies | | `dev/viewforum.php` | — | Fixture: Main Forum, 610 Seiten, mit Unterforen, Ankuendigungen und Stickies |
| `dev/index.php` | — | Fixture: Forumwurzel, zwei Kategorien mit Forenzeilen, keine Paginierung |
| `dev/ucp.php` | — | Fixture: Login-Formular (`mode=login`), nur ausgeloggter Zustand | | `dev/ucp.php` | — | Fixture: Login-Formular (`mode=login`), nur ausgeloggter Zustand |
| `dev/after_login/ucp.php` | — | Fixture: dieselbe URL nach erfolgreichem Login, "Information"-Vorlage statt Formular |
Die Fixtures sind ueber `.gitignore` (`dev/*.php`) ausgeschlossen. Nach einem Die Fixtures sind ueber `.gitignore` (`dev/*.php`) ausgeschlossen. Nach einem
frischen Klon sind sie nicht da und muessen nach der Anleitung in der README neu frischen Klon sind sie nicht da und muessen nach der Anleitung in der README neu
@@ -115,13 +117,31 @@ klammert das `overflow` dagegen nicht, weil der Root keinen Containing Block
aufspannt. Die Freihaltung oben haengt an `body.rm-crumbed`, damit ohne aufspannt. Die Freihaltung oben haengt an `body.rm-crumbed`, damit ohne
Brotkrumen im Markup kein leerer Streifen bleibt. Brotkrumen im Markup kein leerer Streifen bleibt.
**Die Login-Erkennung ist ungetestet gegen eine echte Sitzung.** `loginState()` **Die Login-Erkennung ist inzwischen gegen eine echte Sitzung geprueft.**
unterscheidet `a[href*="mode=login"]` von `a[href*="mode=logout"]` im `loginState()` unterscheidet `a[href*="mode=login"]` von
Menuebalken und liest den Namen aus dem vermuteten Muster `Logout [ Name ]` im `a[href*="mode=logout"]` im Menuebalken und liest den Namen aus dem Muster
Linktext. Dem Repository liegt keine eingeloggte Fixture vor — passt das `Logout [ Name ]` im Linktext. `dev/index.php` und `dev/after_login/ucp.php`
Namensmuster nicht, bleibt die Pille bei „Logout" ohne Namen stehen, statt eine sind eingeloggt aufgenommen (`Logout [ <Benutzername> ]`) und bestaetigen das Muster.
Ausnahme zu werfen. Vor einer Aenderung an `loginState()` erst mit einer echten Der Fallback bleibt trotzdem stehen: passt das Muster einmal nicht — andere
Sitzung pruefen, wie die Zeile tatsaechlich aussieht. Sprache, anderer Klammertyp — zeigt die Pille „Logout" ohne Namen, statt eine
Ausnahme zu werfen.
**`ucp.php?mode=login` rendert nach dem Absenden dieselbe URL neu, diesmal
ohne Formular.** phpBB fuellt die Zwischenmeldung („You have been successfully
logged in.") in dieselbe generische „Information"-Vorlage, die auch fuer
andere Meldungen auf dieser Seite dient (vermutlich auch Login-Fehler, dafuer
liegt aber keine Fixture vor). `buildLoginInfo()` erkennt die Vorlage deshalb
an ihrer Form — eine Kopfzeile mit genau einer `th`-Zelle, gefolgt von einer
`td.row1`-Nachrichtenzelle — nicht am Wortlaut, und uebernimmt die Zelle
unveraendert wie das Formular selbst. Findet `buildLogin()` weder Formular
noch diese Form, baut es nichts; die Originalseite bleibt sichtbar.
**`index.php` wird ueber `location.pathname` erkannt, nicht ueber
`here`.** Der Login-Redirect haengt "index.php" gelegentlich als Wert eines
Query-Parameters an `ucp.php` (`redirect=index.php`, siehe `dev/ucp.php`).
Ein Test auf `location.pathname + location.search` wie bei den uebrigen
Weichen wuerde eine solche Login-URL faelschlich als Liste einordnen, noch
bevor die Pruefung auf `ucp.php` ueberhaupt greift.
**Ist eine Seite nicht laenger als der Viewport, kann Safari nicht wegscrollen, **Ist eine Seite nicht laenger als der Viewport, kann Safari nicht wegscrollen,
um seine eigene Werkzeugleiste unten freizugeben.** Bei Liste und Thread faellt um seine eigene Werkzeugleiste unten freizugeben.** Bei Liste und Thread faellt
@@ -217,11 +237,20 @@ die Zahlenspalten dasselbe Ergebnis liefern.
Fuer den Login genuegt `dev/ucp.php` unveraendert: geprueft werden das Fuer den Login genuegt `dev/ucp.php` unveraendert: geprueft werden das
uebernommene Formular (Username-/Passwortfeld, `sid`, `redirect`, Submit) und uebernommene Formular (Username-/Passwortfeld, `sid`, `redirect`, Submit) und
die Pille in beiden Zustaenden — Letzteres per Textersetzung von die Pille im ausgeloggten Zustand. Fuer den eingeloggten Zustand der Pille
`mode=login` zu `mode=logout&sid=…Logout [ Name ]…` in der eingelesenen `dev/index.php` oder `dev/after_login/ucp.php` laden — beide sind eingeloggt
Fixture, da eine echte eingeloggte Seite fehlt. Das Namensmuster bleibt damit aufgenommen und tragen das echte `Logout [ Name ]`-Markup.
nur gegen die eigene Annahme getestet, nicht gegen echtes Markup — siehe die
Falle oben. Fuer die Meldung nach dem Login genuegt `dev/after_login/ucp.php`
unveraendert, URL `ucp.php?mode=login`: geprueft werden Titel, uebernommene
Nachricht samt Link und die Pille. Zu pruefen ist ausserdem, dass `dev/ucp.php`
(das Formular, gleiche URL) weiterhin die Formular-Zweig traf und nicht in die
Meldungs-Erkennung faellt.
Fuer die Forumwurzel genuegt `dev/index.php`, URL `index.php?sid=…`: zwei
Kategorien mit zusammen 16 Forenkarten, keine Paginierung. Zu pruefen ist
zusaetzlich eine `ucp.php?mode=login&redirect=index.php`-URL — sie muss trotz
des `index.php`-Substrings im Login-Zweig landen, nicht im Listen-Zweig.
Am Geraet zu pruefen bleibt, was Blink nicht zeigt: `env(safe-area-inset-*)`, Am Geraet zu pruefen bleibt, was Blink nicht zeigt: `env(safe-area-inset-*)`,
fixierte Leisten gegen Safaris ein- und ausfahrende Toolbar, fixierte Leisten gegen Safaris ein- und ausfahrende Toolbar,
+30 -17
View File
@@ -15,9 +15,9 @@ page type:
| Page | View | | Page | View |
|---|---| |---|---|
| `viewforum.php`, `search.php`, forum root | Topic and forum list as cards | | `viewforum.php`, `search.php`, `index.php`, forum root | Topic and forum list as cards |
| `viewtopic.php` | Thread as a post stream with a fixed bottom navigation bar | | `viewtopic.php` | Thread as a post stream with a fixed bottom navigation bar |
| `ucp.php?mode=login` | Login form, restyled in place | | `ucp.php?mode=login` | Login form, restyled in place; on the same URL after a successful submit, the resulting information page instead |
| anything else | Untouched — no styles are injected | | anything else | Untouched — no styles are injected |
A `viewforum.php` page is not necessarily a list of topics: a category page A `viewforum.php` page is not necessarily a list of topics: a category page
@@ -61,19 +61,16 @@ Both views carry a fixed breadcrumb bar at the top, taken from the forum's own
`Board index » … » Main Forum` trail. Without it there is no way back out of a `Board index » … » Main Forum` trail. Without it there is no way back out of a
thread, because the original page is hidden and phpBB links the parent forum thread, because the original page is hidden and phpBB links the parent forum
nowhere else. The bar scrolls horizontally if the trail is too long for the nowhere else. The bar scrolls horizontally if the trail is too long for the
screen, and is left out entirely on pages that do not carry a trail. Note that screen, and is left out entirely on pages that do not carry a trail.
`Board index` leads to `index.php`, which the script does not rebuild — that
page arrives in its desktop layout.
The breadcrumb bar also carries a login pill, built on every page the script The breadcrumb bar also carries a login pill, built on every page the script
touches, not only the login form itself. Session state is read from the touches, not only the login form itself. Session state is read from the
forum's own menu bar: a `mode=login` link means logged out, a `mode=logout` forum's own menu bar: a `mode=login` link means logged out, a `mode=logout`
link means logged in, and the pill switches between a "Login" link and the link means logged in, and the pill switches between a "Login" link and the
account name accordingly. The name is parsed from the link text with a account name accordingly. The name is parsed from the link text with a
`Logout [ Name ]` pattern; that pattern is unverified against a real logged-in `Logout [ Name ]` pattern, confirmed against a real logged-in session's markup;
session, since the repository's fixtures are all captured while logged out — if it ever fails to match — a different language, a different bracket style —
if the pattern does not match, the pill still shows "Logout", just without a the pill still shows "Logout", just without a name, rather than breaking.
name, rather than breaking.
The login form itself is not rebuilt from scratch: the original `<form>` The login form itself is not rebuilt from scratch: the original `<form>`
element is moved into the new layout and restyled, table rows collapsed to a element is moved into the new layout and restyled, table rows collapsed to a
@@ -82,6 +79,14 @@ hidden `sid` and `redirect` fields, the `autologin` / `viewonline` checkboxes
and the plain server-side POST intact — the script never talks to the forum's and the plain server-side POST intact — the script never talks to the forum's
session handling directly, it only changes how the existing form looks. session handling directly, it only changes how the existing form looks.
Submitting the form re-renders the same URL with a generic "Information"
template instead of the form — a short confirmation ("You have been
successfully logged in.") before phpBB's own `<meta refresh>` moves on to the
forum index. The script recognises this template by its shape (a single-cell
header followed by a message cell), not by its wording, and restyles it the
same way as the form: the original cell — message and link included — is
reused in place rather than rebuilt.
The script runs at `document-start`, injects a mobile viewport meta tag, and The script runs at `document-start`, injects a mobile viewport meta tag, and
carries a `DEBUG` flag. While `DEBUG` is `true`, a green diagnostic overlay is carries a `DEBUG` flag. While `DEBUG` is `true`, a green diagnostic overlay is
prepended to the page showing which view was chosen and how many rows were prepended to the page showing which view was chosen and how many rows were
@@ -225,9 +230,9 @@ anhand des Seitentyps:
| Seite | Ansicht | | Seite | Ansicht |
|---|---| |---|---|
| `viewforum.php`, `search.php`, Forumwurzel | Themen- und Forenliste als Karten | | `viewforum.php`, `search.php`, `index.php`, Forumwurzel | Themen- und Forenliste als Karten |
| `viewtopic.php` | Thread als Beitragsstrom mit fixierter Navigationsleiste | | `viewtopic.php` | Thread als Beitragsstrom mit fixierter Navigationsleiste |
| `ucp.php?mode=login` | Login-Formular, an Ort und Stelle umgestylt | | `ucp.php?mode=login` | Login-Formular, an Ort und Stelle umgestylt; auf derselben URL nach erfolgreichem Absenden stattdessen die Informationsseite |
| alles andere | Unverändert — es werden keine Stile injiziert | | alles andere | Unverändert — es werden keine Stile injiziert |
Eine `viewforum.php`-Seite ist nicht zwangsläufig eine Themenliste: eine Eine `viewforum.php`-Seite ist nicht zwangsläufig eine Themenliste: eine
@@ -274,18 +279,17 @@ Beide Ansichten haben oben eine fixierte Brotkrumenleiste, übernommen aus dem
kein Weg zurück, weil die Originalseite ausgeblendet ist und phpBB das kein Weg zurück, weil die Originalseite ausgeblendet ist und phpBB das
Elternforum nirgends sonst verlinkt. Die Leiste lässt sich waagerecht schieben, Elternforum nirgends sonst verlinkt. Die Leiste lässt sich waagerecht schieben,
wenn der Pfad breiter ist als der Bildschirm, und entfällt auf Seiten ohne wenn der Pfad breiter ist als der Bildschirm, und entfällt auf Seiten ohne
Pfadangabe. `Board index` führt auf `index.php` — diese Seite baut das Script Pfadangabe.
nicht um, sie erscheint im Desktop-Layout.
Die Brotkrumenleiste trägt außerdem eine Login-Pille, gebaut auf jeder Seite, Die Brotkrumenleiste trägt außerdem eine Login-Pille, gebaut auf jeder Seite,
die das Script anfasst — nicht nur auf dem Login-Formular selbst. Der die das Script anfasst — nicht nur auf dem Login-Formular selbst. Der
Sitzungszustand wird aus dem Menübalken der Originalseite gelesen: ein Sitzungszustand wird aus dem Menübalken der Originalseite gelesen: ein
`mode=login`-Link bedeutet ausgeloggt, ein `mode=logout`-Link eingeloggt, die `mode=login`-Link bedeutet ausgeloggt, ein `mode=logout`-Link eingeloggt, die
Pille wechselt entsprechend zwischen einem „Login"-Link und dem Kontonamen. Pille wechselt entsprechend zwischen einem „Login"-Link und dem Kontonamen.
Der Name wird über das Muster `Logout [ Name ]` aus dem Linktext gelesen; das Der Name wird über das Muster `Logout [ Name ]` aus dem Linktext gelesen, das
Muster ist gegen keine echte eingeloggte Sitzung geprüft, da alle Fixtures des gegen das Markup einer echten eingeloggten Sitzung bestätigt ist; greift es
Repositories ausgeloggt aufgenommen wurden — greift es nicht, bleibt die einmal nicht — andere Sprache, andere Klammerform — bleibt die Pille bei
Pille bei „Logout" stehen, nur ohne Namen, statt zu reißen. „Logout" stehen, nur ohne Namen, statt zu reißen.
Das Login-Formular selbst wird nicht neu gebaut: das Original-`<form>`-Element Das Login-Formular selbst wird nicht neu gebaut: das Original-`<form>`-Element
wandert unverändert in das neue Layout und wird umgestylt, Tabellenzeilen wandert unverändert in das neue Layout und wird umgestylt, Tabellenzeilen
@@ -295,6 +299,15 @@ Checkboxen `autologin` / `viewonline` und den schlichten serverseitigen POST —
das Script spricht nie selbst mit der Sitzungsverwaltung des Forums, es ändert das Script spricht nie selbst mit der Sitzungsverwaltung des Forums, es ändert
nur, wie das vorhandene Formular aussieht. nur, wie das vorhandene Formular aussieht.
Nach dem Absenden liefert dieselbe URL statt des Formulars eine allgemeine
„Information"-Vorlage aus — eine kurze Bestätigung („You have been
successfully logged in."), bevor phpBBs eigenes `<meta refresh>` zum
Forenindex weiterleitet. Das Script erkennt diese Vorlage an ihrer Form, nicht
am Wortlaut (eine Kopfzeile mit einer einzelnen Zelle, gefolgt von einer
Nachrichtenzelle), und stylt sie wie das Formular um: die Original-Zelle —
Nachricht und Link inklusive — wandert unverändert an ihren Platz statt neu
gebaut zu werden.
Das Script läuft bei `document-start`, setzt ein mobiles Viewport-Meta-Tag und Das Script läuft bei `document-start`, setzt ein mobiles Viewport-Meta-Tag und
hat ein `DEBUG`-Flag. Solange `DEBUG` auf `true` steht, wird ein grünes hat ein `DEBUG`-Flag. Solange `DEBUG` auf `true` steht, wird ein grünes
Diagnose-Overlay über die Seite gelegt, das zeigt, welche Ansicht gewählt wurde Diagnose-Overlay über die Seite gelegt, das zeigt, welche Ansicht gewählt wurde
+53 -3
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.6 // @version 3.7
// @match *://cs.rin.ru/forum/* // @match *://cs.rin.ru/forum/*
// @run-at document-start // @run-at document-start
// ==/UserScript== // ==/UserScript==
@@ -23,8 +23,13 @@
// die Ladereihenfolge entschied bei gleich benannten Regeln ueber das // die Ladereihenfolge entschied bei gleich benannten Regeln ueber das
// Layout. // Layout.
var here = location.pathname + location.search; var here = location.pathname + location.search;
// index.php wird ueber den Pfad geprueft, nicht ueber "here": der
// Login-Redirect haengt "index.php" gelegentlich als Wert eines
// Query-Parameters an ucp.php, ueber "here" faellt das faelschlich hier
// hinein statt in den Login-Zweig.
var view = /viewtopic\.php/.test(here) ? 'thread' var view = /viewtopic\.php/.test(here) ? 'thread'
: /viewforum\.php|search\.php/.test(here) || : /viewforum\.php|search\.php/.test(here) ||
/\/index\.php$/.test(location.pathname) ||
location.pathname === '/forum/' ? 'liste' location.pathname === '/forum/' ? 'liste'
: /ucp\.php/.test(here) && /mode=login/.test(here) ? 'login' : /ucp\.php/.test(here) && /mode=login/.test(here) ? 'login'
: ''; : '';
@@ -216,6 +221,16 @@
.rm-login-note summary { cursor:pointer; font-weight:650; opacity:.9; } .rm-login-note summary { cursor:pointer; font-weight:650; opacity:.9; }
.rm-login-note[open] summary { margin-bottom:8px; } .rm-login-note[open] summary { margin-bottom:8px; }
/* Die verschobene td.row1 wird per Klasse zum Block, wie form/table/tr
ueber .rm-login form weiter unten. */
.rm-login-msg { display:block !important; box-sizing:border-box;
padding:22px 14px calc(60px + env(safe-area-inset-bottom)) !important;
text-align:center; font-size:16px; line-height:1.6; }
.rm-login-msg a { display:inline-block; margin-top:8px; padding:12px 24px;
font-size:16px; font-weight:650; border-radius:10px;
color:#fff !important; text-decoration:none !important;
background:rgba(120,170,255,.85); }
/* Das Original-Formular ist tabellenbasiert und wird fuer den gestapelten /* Das Original-Formular ist tabellenbasiert und wird fuer den gestapelten
Kartenstil komplett auf Block-Elemente umgelegt — derselbe Trick wie bei Kartenstil komplett auf Block-Elemente umgelegt — derselbe Trick wie bei
.rm-body table im Thread. Ohne Paginierung fehlt hier die .rm-nav, die .rm-body table im Thread. Ohne Paginierung fehlt hier die .rm-nav, die
@@ -1181,7 +1196,7 @@
function buildLogin() { function buildLogin() {
var userInput = document.querySelector('input[name="username"]'); var userInput = document.querySelector('input[name="username"]');
var form = userInput && userInput.closest('form'); var form = userInput && userInput.closest('form');
if (!form) { log.push('login: kein Formular'); return; } if (!form) { buildLoginInfo(); return; }
var root = document.createElement('div'); var root = document.createElement('div');
root.className = 'rm-root rm-login'; root.className = 'rm-root rm-login';
@@ -1218,6 +1233,41 @@
log.push('login=formular'); log.push('login=formular');
} }
// ucp.php?mode=login liefert ausser dem Formular auch Zwischenseiten ueber
// dieselbe generische "Information"-Vorlage aus — zum Beispiel die
// Bestaetigung nach erfolgreichem Login, vermutlich aber auch andere
// Meldungen wie ein falsches Passwort, fuer die keine Fixture vorliegt.
// Erkannt wird die Vorlage an der Form, nicht am Text: eine Kopfzeile mit
// genau einer th-Zelle traegt in diesem View sonst nirgends eine einzelne
// Nachricht samt Link. Wie beim Formular wandert die Original-Zelle
// unveraendert in die neue Ansicht, damit der Link darin (zurueck zum Index
// oder zu einem anderen Ziel) unangetastet bleibt.
function buildLoginInfo() {
var th = null;
document.querySelectorAll('table.tablebg th').forEach(function (t) {
if (th) return;
var row = t.closest('tr');
if (row && row.children.length === 1) th = t;
});
var msgCell = th && th.closest('table').querySelector('td.row1');
if (!th || !msgCell) { log.push('login: kein Formular, keine Meldung'); return; }
var root = document.createElement('div');
root.className = 'rm-root rm-login';
var title = document.createElement('div');
title.className = 'rm-login-title';
title.textContent = txt(th) || 'Information';
root.appendChild(title);
msgCell.classList.add('rm-login-msg');
root.appendChild(msgCell);
replaceBody(root);
addCrumbs();
log.push('login=meldung');
}
// ========================================================================== // ==========================================================================
function run() { function run() {
@@ -1230,7 +1280,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.6\n' + log.join('\n'); d.textContent = 'RIN Mobile v3.7\n' + log.join('\n');
document.body.insertBefore(d, document.body.firstChild); document.body.insertBefore(d, document.body.firstChild);
} }
} }