mirror of
https://github.com/Andre-Bzk/rin-mobile-userscript.git
synced 2026-11-15 09:33:08 +01:00
The script previously had no way to log in or tell whether a session was already active, since ucp.php fell outside the view switch and rendered in the desktop layout. Session state is only visible in the forum's own menu bar link (mode=login vs mode=logout), so every view now surfaces it as a pill in the breadcrumb bar instead of leaving the user to dig for it. The login form itself is moved into the new layout rather than rebuilt from scratch, keeping the hidden sid/redirect fields and the autologin/viewonline checkboxes intact without the script having to reimplement the session handshake. As the first short, unscrollable view in the project, it also exposed a WebKit quirk that longer views never triggered: without enough scroll headroom, Safari's own partially-retracted toolbar has nothing to scroll away and permanently covers the submit button. The generous bottom padding on the form works around that, separately from the safe-area inset that only accounts for the home indicator.
243 lines
13 KiB
Markdown
243 lines
13 KiB
Markdown
# CLAUDE.md
|
|
|
|
Ein Userscript, das die phpBB-Seiten von `cs.rin.ru` fuer Safari auf iOS in eine
|
|
mobile Ansicht umbaut. Kein Build, keine Abhaengigkeiten, kein `package.json` —
|
|
die `.user.js`-Datei ist das Produkt.
|
|
|
|
| Datei | Greift auf | Zweck |
|
|
|---|---|---|
|
|
| `rin-mobile.user.js` | `*://cs.rin.ru/forum/*` | Beide Ansichten, Weiche nach Seitentyp |
|
|
| `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/viewforum.php` | — | Fixture: Main Forum, 610 Seiten, mit Unterforen, Ankuendigungen und Stickies |
|
|
| `dev/ucp.php` | — | Fixture: Login-Formular (`mode=login`), nur ausgeloggter Zustand |
|
|
|
|
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
|
|
gespeichert werden — die Seite verlangt einen Login.
|
|
|
|
## Aufbau
|
|
|
|
Ein IIFE, streng der Reihe nach:
|
|
|
|
1. `DEBUG`-Flag und `log`-Array
|
|
2. Weiche: `view` ist `'thread'`, `'liste'`, `'login'` oder `''` — abgeleitet
|
|
aus `location.pathname + location.search`
|
|
3. Nur bei gesetztem `view`: Viewport-Meta (idempotent ueber `[data-rm]`) und
|
|
der komplette Style-Block als Template-Literal
|
|
4. `replaceBody(root)` blendet alle bisherigen `body`-Kinder aus und haengt die
|
|
neue Ansicht an
|
|
5. `pageState()`, `pageSheet()` und `pageNav()` — die Paginierung, von Liste
|
|
und Thread benutzt, nicht vom Login
|
|
6. `buildList()`, `buildThread()` beziehungsweise `buildLogin()` lesen das
|
|
Original-DOM und bauen ein `div.rm-root`; alle drei haengen zum Schluss
|
|
ueber `addCrumbs()` die fixierte Brotkrumenleiste an, die zugleich ueber
|
|
`loginState()` die Login/Logout-Pille traegt
|
|
7. `run()` waehlt die Ansicht und haengt bei `DEBUG` das gruene Overlay an
|
|
8. Start ueber `DOMContentLoaded`, falls `readyState === 'loading'`
|
|
|
|
Auf Seiten ausserhalb der Weiche (`posting.php`, Profile, …) passiert nichts,
|
|
insbesondere wird kein Stylesheet injiziert.
|
|
|
|
## Fallen, die nicht aus dem Code hervorgehen
|
|
|
|
**Beide Ansichten teilen sich einen Klassennamensraum.** Das war der Grund fuer
|
|
die Zusammenlegung: als getrennte Scripts matchte `rin-mobile` auch
|
|
`viewtopic.php` und injizierte dort sein Stylesheet, obwohl es nichts baute.
|
|
Gleich benannte Regeln kollidierten mit identischer Spezifitaet, und die
|
|
Ladereihenfolge der Userscripts entschied ueber das Layout.
|
|
|
|
- Geteilt: `.rm-root`, `.rm-hidden`, `.rm-dbg`, die globale `img`-Regel
|
|
- Geteilt sind ausserdem die Leiste und ihr Sheet: `.rm-nav`, `.rm-sheet*`,
|
|
`.rm-goto`, `.rm-grid`
|
|
- Unterschiede zwischen den Ansichten ueber einen Modifier am Root loesen,
|
|
nicht ueber zwei Regeln mit demselben Selektor. `.rm-root.rm-paged` haelt
|
|
so die 64px hinter der fixierten Leiste frei — gesetzt von `pageNav()`
|
|
selbst, weil es die Leiste nicht auf jeder Liste gibt.
|
|
- Namen sind ansichtsspezifisch zu halten: `.rm-jump` und `.rm-group` gehoeren
|
|
der Liste, `.rm-post` und `.rm-sig` dem Thread.
|
|
- `.rm-av` braucht `!important` auf Breite und Hoehe, sonst gewinnt die globale
|
|
`img { height:auto !important }`-Regel gegen die Avatarmasse.
|
|
|
|
**Was nach `body` soll, muss nach `replaceBody()` angehaengt werden.**
|
|
Die Funktion markiert *alle* vorhandenen `body`-Kinder mit `.rm-hidden`. Das
|
|
Sheet wird deshalb erst beim ersten Antippen gebaut — das loest das Problem
|
|
nebenbei mit.
|
|
|
|
**In der Liste entscheidet die Dokumentreihenfolge ueber den Zeilentyp.**
|
|
`buildList()` sammelt Links auf `viewforum.php` und `viewtopic.php` in einem
|
|
Rutsch ein und ordnet jede `tr` nach ihrem ersten verwertbaren Link zu. Eine
|
|
Forenzeile verlinkt in der Spalte „letzter Beitrag" ebenfalls `viewtopic.php` —
|
|
zaehlte man Themenlinks zuerst, faellt die Zeile als Thema an, der Forenname
|
|
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,
|
|
stehen aber in Zeilen ohne Forenmerkmale und werden verworfen.
|
|
|
|
**Der Typ einer Themenzeile steht im Symbol, nicht am Titel.** `rinDark`
|
|
unterscheidet `global_read.png`, `announce_read.png`, `sticky_read.png` und
|
|
`topic_read.png` in der ersten Spalte, jeweils auch als `_locked`-Variante. Die
|
|
Trennzeilen der Seite (`<td class="row3" colspan="6"><b>Stickies</b>`) tragen
|
|
keinen Link und fallen beim Einsammeln durch; ihr Text ist trotzdem die beste
|
|
Beschriftung und wird ueber die Vorgaengerzeilen nachgeschlagen. Achtung beim
|
|
Zaehlen im Fixture: dieselben Symbole stehen unten noch einmal in der Legende,
|
|
ein Grep zaehlt also mehr, als es Zeilen gibt.
|
|
|
|
**Der Menuebalken der Seite verlinkt Themen.** „Forum rules" und die FAQ stehen
|
|
in einer Layoutzeile ohne Tabelle oder Script darin, die Bremse in `rowKind()`
|
|
greift dort also nicht. Ohne das Merkmal `a.topictitle` beziehungsweise zwei
|
|
Zahlenspalten wurde daraus eine Geisterkarte am Kopf jeder Liste — mit
|
|
Gruppenkoepfen faellt sie als eigener Abschnitt „Themen 1" auf.
|
|
|
|
**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
|
|
einen Link ausserhalb einer Listenzeile die Layoutzeile, die die ganze Seite
|
|
enthaelt. Ohne Bremse wurde daraus eine Karte mit dem kompletten Seitentext —
|
|
Kopfzeile, Stylesheet und Inline-Scripts inklusive — als Forenbeschreibung.
|
|
Zeilen mit `table`, `script`, `style` oder `form` darin sind deshalb keine
|
|
Kandidaten, und die Beschreibung wird nur aus den direkten Textknoten der
|
|
Titelzelle gebildet, nie aus deren gesamtem Text.
|
|
|
|
**Das Theme ist `rinDark`, nicht subsilver2.** Fuer die Forenliste waren die
|
|
Klassennamen aus der phpBB-Dokumentation (`forumlink`, `forumdesc`) deshalb
|
|
zunaechst nur eine Vermutung; `dev/viewforum.php` bestaetigt sie inzwischen. Der
|
|
Rueckfall auf die Form der Zeile bleibt trotzdem stehen — er kostet nichts und
|
|
traegt die Erkennung, wenn das Theme wechselt.
|
|
|
|
**Die Brotkrumenleiste ist fixiert, nicht sticky.** `.rm-root` traegt
|
|
`overflow:hidden` und wuerde damit zum Scrollport einer `position:sticky`
|
|
-Leiste, in dem nie gescrollt wird — sie klebte nie. Fixierte Nachfahren
|
|
klammert das `overflow` dagegen nicht, weil der Root keinen Containing Block
|
|
aufspannt. Die Freihaltung oben haengt an `body.rm-crumbed`, damit ohne
|
|
Brotkrumen im Markup kein leerer Streifen bleibt.
|
|
|
|
**Die Login-Erkennung ist ungetestet gegen eine echte Sitzung.** `loginState()`
|
|
unterscheidet `a[href*="mode=login"]` von `a[href*="mode=logout"]` im
|
|
Menuebalken und liest den Namen aus dem vermuteten Muster `Logout [ Name ]` im
|
|
Linktext. Dem Repository liegt keine eingeloggte Fixture vor — passt das
|
|
Namensmuster nicht, bleibt die Pille bei „Logout" ohne Namen stehen, statt eine
|
|
Ausnahme zu werfen. Vor einer Aenderung an `loginState()` erst mit einer echten
|
|
Sitzung pruefen, wie die Zeile tatsaechlich aussieht.
|
|
|
|
**Ist eine Seite nicht laenger als der Viewport, kann Safari nicht wegscrollen,
|
|
um seine eigene Werkzeugleiste unten freizugeben.** Bei Liste und Thread faellt
|
|
das nicht auf, weil Karten beziehungsweise Beitraege fast immer laenger sind
|
|
als der Bildschirm. Der Login hat kein `.rm-nav`, das sonst Platz reserviert,
|
|
und war als kurze Formularseite der erste Fall, bei dem die Seite genau so
|
|
hoch wie der Viewport enden konnte — die teilweise eingefahrene Leiste blieb
|
|
dann stehen und verdeckte den Submit-Button, ohne dass noch etwas zu scrollen
|
|
war, um sie wegzuschieben. `env(safe-area-inset-bottom)` allein loest das
|
|
nicht, das federt nur den Home-Indicator ab. Der Bodensatz unter dem Formular
|
|
in `.rm-login form` ist deshalb absichtlich groesser als noetig: er erzwingt
|
|
Scrollhoehe ueber den Viewport hinaus.
|
|
|
|
**`buildLogin()` baut das Formular nicht nach, sondern verschiebt es.** Wie der
|
|
Beitragstext im Thread (`bodyWrap.appendChild(pb)`) wandert das
|
|
Original-`<form>` unveraendert in `.rm-root` und wird nur per CSS auf
|
|
Block-Elemente umgelegt. Nachbauen wuerde bedeuten, `sid`, das doppelte
|
|
`redirect`-Feld und `autologin`/`viewonline` einzeln nachzuziehen — verliert
|
|
man dabei eines, scheitert der Login stumm oder landet nicht dort, wo er soll.
|
|
Aus demselben Grund faengt `buildLogin()` keine Fehlermeldung eines
|
|
gescheiterten Logins ab: ohne Fixture eines fehlgeschlagenen Versuchs waere die
|
|
Erkennung geraten. Fehlt das Formular (`input[name="username"]`), baut die
|
|
Funktion nichts und die Originalseite bleibt sichtbar.
|
|
|
|
**`Page X of Y` steht als `Page <strong>1</strong> of <strong>1114</strong>`
|
|
im Markup.** Ein Grep im Roh-HTML nach `Page 1 of` findet nichts, die Regex auf
|
|
`txt(document.body)` greift trotzdem. Nicht vorschnell fuer kaputt halten.
|
|
|
|
**Seitenlinks stehen nicht nur im Paginierungsblock.** Jede Themenzeile mit
|
|
mehr als einer Seite bringt ihr eigenes „Go to page: 1 … 42, 43" mit, und diese
|
|
Links tragen ebenfalls `start=`. Die Liste baute ihre Seitenleiste bis 3.5 aus
|
|
allen `a[href*="start="]` der Seite und kam so auf 103 Eintraege, von denen die
|
|
meisten mitten in einen fremden Thread fuehrten. `pageState()` macht es richtig
|
|
und liest nur den Block um den `jumpto()`-Link.
|
|
|
|
**`per_page` kommt aus der `jumpto()`-Funktion der Seite**
|
|
(`var per_page = '15';`). Nur damit lassen sich Seiten-URLs als
|
|
`start = (Seite - 1) * per_page` selbst bauen. Die gerenderten Seitenlinks
|
|
taugen nicht als Quelle — phpBB zeigt bei 1114 Seiten nur `1, 2, 3, 4, 5 … 1114`.
|
|
Ohne `per_page` degradiert die Leiste auf erste Seite plus die
|
|
Original-Zurueck/Weiter-Links, das Sheet verschwindet.
|
|
|
|
**`postRow()` steigt bewusst mehrere `tr` hoch.** Der Beitragstext liegt in
|
|
einer verschachtelten Tabelle, `closest('tr')` trifft die innere Zeile ohne
|
|
Autor- und Profilspalte.
|
|
|
|
**`DEBUG` steht auf `true`** und muss vor dem Ausliefern auf `false`. Die
|
|
Versionsnummer steht doppelt: im `@version`-Header und im Text des
|
|
Debug-Overlays.
|
|
|
|
## Testen
|
|
|
|
Es gibt keine Testsuite. Bewaehrt hat sich ein Wegwerf-Harness mit jsdom im
|
|
Scratchpad (nicht im Repo ablegen — das Projekt soll abhaengigkeitsfrei
|
|
bleiben):
|
|
|
|
```js
|
|
const dom = new JSDOM(html, { url: 'https://cs.rin.ru/forum/viewtopic.php?f=10&t=68689&start=45' });
|
|
```
|
|
|
|
Drei Stolpersteine dabei:
|
|
|
|
- JSDOM parst asynchron fertig. Nach `eval(src)` rund 60 ms warten, sonst hat
|
|
das Script noch auf `DOMContentLoaded` gewartet und nichts gebaut.
|
|
- `location.href` ist in JSDOM nicht ueberschreibbar. Fuer Navigationstests im
|
|
eingelesenen Quelltext `location.href = u;` durch einen Hook ersetzen.
|
|
- `requestAnimationFrame` und `scrollIntoView` fehlen und muessen gestubbt
|
|
werden.
|
|
|
|
Sinnvolle Faelle: erste Seite, zweite Seite, Mitte, letzte Seite, dazu die
|
|
Degradation ohne `per_page` und ein Thread ohne Paginierung. Fixture-Eckdaten:
|
|
`f=10&t=68689`, `per_page = 15`, 1114 Seiten, letzte Seite `start=16695`.
|
|
|
|
Dieselben Faelle gelten seit 3.5 fuer die Liste, die sich die Leiste mit dem
|
|
Thread teilt. Eckdaten von `dev/viewforum.php`: `f=10`, `per_page = 100`, 610
|
|
Seiten, letzte Seite `start=60900`, dazu 4 Unterforen, 7 globale
|
|
Ankuendigungen, 2 Ankuendigungen, 8 Stickies und 92 Themen. Achtung beim
|
|
Nachbauen einer einseitigen Liste: die Seitenlinks zu entfernen genuegt nicht,
|
|
`Page 1 of 610` steht auch im Text und traegt die Gesamtzahl allein.
|
|
|
|
Fuer die Weiche und die Listenansicht genuegt eine handgeschriebene Tabelle mit
|
|
`a.topictitle`-Zeilen. Zu pruefen ist dabei auch eine Seite ausserhalb der
|
|
Weiche wie `posting.php`: dort darf weder eine Ansicht gebaut noch ein
|
|
`<style>` injiziert werden.
|
|
|
|
Fuer die Forenliste braucht es eine zweite Tabelle im subsilver2-Schnitt:
|
|
`td.cat > h4 > a` als Kategoriezeile, darunter Zeilen mit `a.forumlink`, drei
|
|
`p.forumdesc` (Beschreibung, Moderatoren, Unterforen), zwei Zahlenspalten und
|
|
einer Spalte „letzter Beitrag" mit Link auf `viewtopic.php`. Vier Faelle sind
|
|
sinnvoll: reine Forenliste, reine Themenliste, beides auf einer Seite, und
|
|
dieselbe Forenliste ohne die Klassen `forumlink`/`forumdesc` — die muss ueber
|
|
die Zahlenspalten dasselbe Ergebnis liefern.
|
|
|
|
Fuer den Login genuegt `dev/ucp.php` unveraendert: geprueft werden das
|
|
uebernommene Formular (Username-/Passwortfeld, `sid`, `redirect`, Submit) und
|
|
die Pille in beiden Zustaenden — Letzteres per Textersetzung von
|
|
`mode=login` zu `mode=logout&sid=…Logout [ Name ]…` in der eingelesenen
|
|
Fixture, da eine echte eingeloggte Seite fehlt. Das Namensmuster bleibt damit
|
|
nur gegen die eigene Annahme getestet, nicht gegen echtes Markup — siehe die
|
|
Falle oben.
|
|
|
|
Am Geraet zu pruefen bleibt, was Blink nicht zeigt: `env(safe-area-inset-*)`,
|
|
fixierte Leisten gegen Safaris ein- und ausfahrende Toolbar,
|
|
`-webkit-backdrop-filter`, Schriftskalierung. Details und die
|
|
Tampermonkey-Einrichtung stehen in der README.
|
|
|
|
## Konventionen
|
|
|
|
- **ES5-Stil im Script-Code:** `var`, `function`, keine Arrow Functions, kein
|
|
`let`/`const`. Template-Literals und `Set` sind in Gebrauch. Keine
|
|
Transpilation, kein Linter.
|
|
- **Kommentare deutsch, Umlaute als `ae`/`oe`/`ue` transliteriert.** Echte
|
|
Umlaute nur in Strings und Regexes, die im UI landen.
|
|
- Kommentare begruenden, warum etwas so gebaut ist (welche DOM-Eigenheit von
|
|
phpBB dahintersteckt), nicht was der Code tut.
|
|
- CSS-Klassen durchgaengig mit `rm-` praefixiert.
|
|
- Neue nutzersichtbare Funktionen in der README dokumentieren — zweisprachig,
|
|
siehe globale Anweisung.
|