Skip to content

Releases: ernestdefoe/cascade

Cascade 0.3.3 — fixes the blank discussion page

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 20:43

If a discussion page renders blank with Cannot read properties of null (reading 'toISOString') in the console, this is the fix. Update.

What was wrong

Cascade eager-loads firstPost and lastPost on the discussion index to build the excerpt and the reply preview, and it narrowed that load to four columns: id, discussion_id, type, content.

That is harmless while nothing serialises those relations — which is why it was invisible on every forum I could test. But the eager load is shared. The moment any other extension includes firstPost or lastPost on the index, those posts are serialised from the models Cascade narrowed, and they arrive in the browser with:

createdAt: null
number: null

Flarum's store then holds a half-loaded Post, and core's PostStream does post.createdAt().toISOString() with no guard. Opening any discussion throws, and the stream renders nothing.

Why it looked like somebody else's bug

The stack trace goes through the other extension's PostStream wrapper, and Flarum's error banner names Cascade only because its extender is in the chain. On a forum without such an extension — mine, and every test install — it cannot be reproduced at all. That combination pointed everywhere except at the actual cause.

The fix

The relations load the whole row. The subset saved almost nothing: content is by far the largest column and was always selected, so everything dropped was a handful of scalars on a row already being read.

🚨 The rule it leaves behind: never narrow the columns of a relation other code can ask for. A private query may select what it likes; a shared one has to load a model that is safe to serialise.

Verified: ?include=firstPost returns real createdAt and number, the excerpt and image fields are unchanged, and all 15 preset/width combinations still pass.

Cascade 0.3.2 — supersedes 0.3.1, which was missing six fixes

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 20:38

If you are on 0.3.0 or 0.3.1, update. Those two tags carry older code than v0.2.6 despite the higher version number, so a fresh install picked up a build missing six fixes. This release is cut from main and contains everything.

Nothing here is new work — it is the 0.2.1–0.2.6 fixes, which 0.3.0/0.3.1 were tagged behind:

  • Avatar initials no longer translate into words. A user called Ernest renders as E; with Chrome translating a Spanish page, e is a conjunction and every avatar became a circle reading AND. Marked notranslate.
  • The left rail has a gutter on the column presets. X-style and Bluesky-style set the column gap to 0 on purpose, which left the nav and sidebar widgets flush against the feed — measured at 0px.
  • The rail no longer clips its own dropdowns. Pinning it added overflow-y, and a non-visible value on one axis forces the other from visible to auto — so it had quietly become a clipping box on both axes.
  • Touch targets are 44px on phones and tablets. The action row measured 104×26; both Apple and Android put the floor at 44pt/48dp.
  • The sidebar sits below the feed on mobile. Its widgets measured 247px on a 390px phone, putting the first discussion 412px down — half the opening screen of a feed theme was everything except the feed.

Why the numbers went backwards

0.3.0 and 0.3.1 were tagged from a point in history that the 0.2.x fixes were later committed on top of. Composer resolves by version number, not by date, so the highest number was the oldest tree. 0.3.2 is tagged from main, so the highest number is the newest code again.

v0.3.1

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:11

The admin settings page is translatable

locale/en.yml promised that nothing in Cascade is hardcoded English in a
component. That was true of the forum and false of the admin panel: all
eight settings carried their label, help text and select options as English
literals, so an admin running Cascade in another language got a settings page in
English and no way to change it.

All 25 strings are now translator keys under admin.settings, and Spanish
ships for every one of them
.

The three preset names — Facebook-style, X-style, Bluesky-style — are shown on
both frontends (an admin picks the forum default, a member picks their own), so
they now have a single definition under lib. rather than being translated
twice in two places that could drift.

For translators

If you have translated Cascade, three keys moved:

forum.preset.wall      →  lib.preset.wall
forum.preset.timeline  →  lib.preset.timeline
forum.preset.stream    →  lib.preset.stream

Everything else under forum. is unchanged, and admin.settings.* is new.

v0.3.0

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:03

Two defects reported from a Spanish-language forum, both reproduced on a clean
2.0.0-rc.8 install with Cascade and nothing else — neither turned out to be a
conflict with anything.

The avatar sat on top of every post

On the discussion page the avatar overlapped the author name and the first two
lines of the post: 27px of it, at every width.

--avatar-column-width: 0px collapses core's avatar column. That is right on
the feed, where the markup is Cascade's own. On the discussion page the markup
is core's.Post-container is a grid, and core renders .Post-side into
that first column regardless of how wide it is. A 64px avatar in a 0px column
does not disappear; it overflows, onto .Post-body, which starts at the same x.

Cascade now finishes what the variable starts: the side column is clipped, and
the inline avatar core hides at desktop — the one this theme has always said it
uses — is shown instead. The header sizes to it and the body clears it.

"Trending this week", three times

The rail printed the window above every row, under a heading that already
said "Trending":

Trending
  Trending this week / Literatura / 4 discussions
  Trending this week / Poesía     / 2 discussions
  Trending this week / General    / 2 discussions

The per-row label was guarding a case the server cannot produce: the endpoint
widens its window until it finds something and returns one window for the
whole response, so rows can never disagree. The window now appears once, under
the title.

It was invisible on a forum with a single trending tag, which is why it lasted:
it only looks wrong from two rows up.

Spanish

Cascade shipped only en.yml, so on a Spanish forum it placed ~40 English
strings into an otherwise Spanish interface — the composer placeholder, every
row action, the whole trending rail.

locale/es.yml now ships. The vocabulary is taken from flarum-lang/spanish
(informal/tuteo) rather than invented, so Cascade does not introduce a second
word for something core already names: Discusiones, Publicaciones,
Responder, Crear discusión, Cargando.

Corrections from native speakers are welcome — open an issue or a PR.

Cascade 0.2.6 — sidebar below the feed on mobile

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:59

On a phone the nav links are hoisted into the header dropdown, so what is left in the sidebar is the widgets. Measured at 247px on a 390px phone, which put the first discussion 412px down the page — half the opening screen of a feed theme was everything except the feed.

The sidebar now sits below the discussion list on phones and tablets.

Three things had to change together, and the last two were only found by asserting no horizontal overflow:

  • The container is display: block at this width, so order alone does nothing — it has to be a flex column first.
  • The desktop rule sets align-items: flex-start, which in a column shrinks every child to its content width rather than the screen.
  • 🚨 The sidebar also carries align-self: flex-start from the pinned rail, and align-self beats the container's align-items. So it kept sizing itself to the mobile nav strip — 1842px inside a 768px page, scrolling the whole document sideways. min-width: 0 did not help, because the item was never being asked to shrink; it was being told to size to its content.

Verified at 390, 430 and 768: sidebar below the entire list, no horizontal overflow anywhere, feed full width.

Cascade 0.2.5 — touch targets on tablets too

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:49

Follow-up to 0.2.4. The 44px action row was scoped to phones, which left tablets — touch devices with no pointer at all — on the 26px bar.

That is the one place the mistake is least likely to be caught, because anyone checking does so on a desktop. Now applied to phone and tablet alike.

Cascade 0.2.4 — phone touch targets

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:48

React / Reply / Share are the primary actions on a phone, and they measured 104x26. Apple and Android both put the floor for a touch target at 44pt/48dp — 26px is reachable with a fingertip only if you aim for it.

The action row now has a min-height of 44px on phones. It grows into a proper target without shifting the label against its icon, and desktop keeps its tighter bar, where a pointer does not need the margin.

Cascade 0.2.3 — the column presets get a gap

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:44

On X-style and Bluesky-style the left rail sat flush against the feed — a Calendar link, a widget caption and a leaderboard count all measuring exactly 0px from the edge. Text touching a boundary reads as clipped even when nothing is cut.

Those two presets set the column gap to 0 on purpose: their feed column is drawn with borders rather than whitespace. But that also removed the only thing keeping the rail off it. They now use an 18px gap.

🚨 Padding could never have fixed this, and it is worth knowing why. Both .IndexPage-nav and its ul compute a fixed width pinned to the sidebar variable rather than filling their parent — so they sit straight over any padding their container is given. Two attempts died there before measuring the ul. Separation between fixed-width columns has to be a gap.

Two gaps cost 36px, taken off the feed at every band, so the totals are unchanged: 1180 / 1352 / 1448 / 1576 exactly as in 0.2.2. The feed still runs 682 → 1008 across the ladder, and there is now 18px either side of it.

Also reverts the max-height / overflow-y from 0.2.0's pinned rail, which had quietly made the sidebar a clipping container on both axes and would have cut off any dropdown opening inside it. The rail is still sticky.

Cascade 0.2.2 — left rail gutter and clipping fix

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:40

Two faults in the left rail, both introduced in 0.2.x and both obvious once a column preset is switched on.

The rail had no gutter

Facebook-style separates its columns with a gap, so the rail has air to its right. X-style and Bluesky-style set that gap to 0 deliberately — their feed column is defined by its own borders rather than by whitespace — which left the nav and the sidebar widgets running hard against the feed edge. Measured at exactly 0px for a Calendar link, a widget caption and a leaderboard count. Text touching a boundary reads as clipped even when nothing is actually cut, and 0.2.0's narrower rail made it worse.

Fixed with padding on the rail, so the shell's column arithmetic and the width ladder are untouched.

The rail was clipping its own dropdowns

Pinning the rail in 0.2.0 added a max-height and overflow-y: auto so a nav taller than the window could scroll inside itself.

🚨 Per spec, a non-visible value on one axis forces the other from visible to auto — so the rail quietly became a clipping container on both axes, and the sidebar holds dropdowns, which open outside it.

A nav whose last item sits below the fold is a far smaller problem than a menu that cannot be seen, and it is how the rail behaved before it was pinned at all. Both properties are gone; the rail is still sticky.

Cascade 0.2.1 — avatars vs browser translation

Choose a tag to compare

@ernestdefoe ernestdefoe released this 12 Sep 00:25

A user called Ernest renders as a circle containing E. Read a Spanish forum with Chrome's translation switched on and e is a Spanish conjunction meaning and — so every avatar on the page became a circle containing the word AND, overflowing its own border.

It gets reported as broken avatars, because that is exactly what it looks like. Any single letter is a word in some language: a, y, o, e, i. An initial is identity, not prose.

Avatars are now marked notranslate, so translators leave them alone.

🚨 Worth knowing if you hit this elsewhere: the opt-out has to be the class, not translate="no". HTMLElement.translate is a boolean property and Mithril sets known properties directly rather than writing attributes, so assigning the string 'no' assigns something truthy and the element comes out as translate="yes" — the exact opposite, silently. Measured across 70 avatars before the fix: every one of them translate=yes.