diora-web/docs/refactoring.md

2.9 KiB

Refactoring (started 2026-10-14)

diora grew fast across four code bases that share one web front end: diora-web (server + app.js and friends), diora-android (Kotlin + web-native/*.js), diora-desktop (Electron + desktop.js) and the vendored copies between them. This is the plan for tidying it up without breaking anything.

Rules

  • No behaviour changes in a refactoring step. Features and fixes go in their own commits.
  • Tests before moving code. A step only starts when what it touches is covered.
  • Small steps, each one releasable, each one checked on web, desktop and Android.
  • Whoever owns a file does its refactoring (web-native/* → the Android session; app.js, home.js, templates, desktop → the desktop session); shared contracts live in docs/.

The numbers (2026-10-14, before step 2; app.js is now static/js/app/)

file lines
static/js/app.js 6 864, about 275 functions, about 120 globals, 45 sections
static/podcasts/native-podcast.js (copy from diora-android) 4 554
static/css/app.css 2 010
static/js/podcasts-web.js 1 899
podcasts/views.py 2 505
web-native/native.css 2 150

Web JavaScript has no unit tests and four Playwright smoke tests that run nowhere. The reading-position rules exist four times (books/views.py, app.js, desktop ops.js, Android Ops.kt), each tested separately or not at all.

Steps

  1. Safety net (in progress)
    • Playwright tests that run locally and in CI against a throwaway database: log in, open a book (an EPUB fixture), read, the position is saved and restored, a highlight, a note in the margin, the theme switch, radio and podcasts tabs load.
    • Shared test vectors for the reading-position rules (docs/progress-cases.json), run by the Django tests, desktop's node tests and Android's OpsTest — one contract, checked in all four places.
  2. Split app.js (done: 656520f, sw v106 — static/js/app/NN-*.js, 33 files; the template lists them in order) by its sections into plain scripts loaded in order (radio, focus, ambient, crypto, epub, books, reading position, reader, tts, highlights, margin, …). They stay classic scripts sharing globals, so the native layers that wrap window.* keep working unchanged; template, sw.js SHELL and Android's build_web list them. No code changes inside the moved functions.
  3. Dead code: features and helpers nothing calls any more (found by the split and by coverage of the tests), removed one at a time.
  4. Server: podcasts/views.py into modules by area (feeds, episodes, queue, player sync, import); shared helpers (_require_auth, JSON body parsing) in one place.
  5. CSS: the remaining fixed colours to the theme tokens (docs/theme.md); app.css split like app.js.
  6. Copies between repositories: one sync script per direction with a check in CI that the copy matches its source revision (today: sync_native.sh, sync_podcast_ui.py).