djust 0.5.4rc1

Pre-releaseReleased

Fixed

  • PresenceMixin + NotificationMixin — side-effect replay on WS state restoration (#893, #894) — Sibling bugs to #889, found via audit after the UploadMixin fix shipped in #891. Both issues share the same root cause: a mixin's mount()-called method has a process-wide side effect beyond setting instance attrs, and the WS consumer's state-restoration path (which skips mount()) never re-issues the side effect. #893 (Presence): track_presence() calls PresenceManager.join_presence(...) as a per-process singleton registration; after restore, the restored user's presence is invisible to other users and handle_presence_join doesn't fire for the user's own join. #894 (Notifications): listen(channel) calls PostgresNotifyListener.instance().ensure_listening(channel) which issues the Postgres LISTEN channel SQL statement on the current process; after a cross-process restore (server restart between HTTP and WS, sticky-session LB routing WS to a different worker, worker reshuffle under load) the destination process's listener has no subscriptions, so NOTIFYs never reach the restored view. Fix (mirrors PR #891): PresenceMixin._restore_presence() replays join_presence when _presence_tracked=True; NotificationMixin._restore_listen_channels() replays ensure_listening per channel (both convergent under replay — PresenceManager.join_presence overwrites the existing record with identical data so repeated calls are a no-op in effect; ensure_listening explicitly early-returns on known channels); WS consumer's state-restoration path calls both right after _restore_private_state(), alongside the existing _restore_upload_configs() call. All three methods are defensive: missing attributes / backend errors / per-item failures are logged at WARNING and swallowed — restoration must never kill the WebSocket. 11 regression cases in tests/unit/test_mixin_restoration_893_894.py cover both mixins' happy paths, no-op guards (not-tracked, missing user_id, empty channel set, missing attribute), exception handling (backend exception, per-channel failure, postgres unavailable), and an end-to-end session-round-trip test. (python/djust/presence.py, python/djust/mixins/notifications.py, python/djust/websocket.py)

  • UploadMixin — uploads broken after HTTP→WS state restoration (#889) — Production-critical bug affecting every app using UploadMixin with the default pre-rendered HTTP→WS flow. The WS consumer's state-restoration path (websocket.py:1540-1572) skips mount() when pre-rendered session state exists, and the live UploadManager instance isn't JSON-serializable — so _upload_manager silently dropped by _get_private_state(), never restored, and any upload request hit _handle_upload_register with "No uploads configured for this view". Fix: allow_upload() now also records each call as a JSON-serializable dict in self._upload_configs_saved (list of kwarg dicts with primitive values); the new UploadMixin._restore_upload_configs() method replays the saved calls; the WS consumer calls it at the end of the state-restoration path (right after _restore_private_state). Result: restored views behave identically to fresh-mount views. Caveat: allow_upload(writer=CustomWriterClass) — the writer class itself still can't round-trip through JSON; a warning is logged at replay time and the config falls back to the default buffered writer. Apps that rely on custom writers with session restoration need a follow-up design (out of scope for this fix). 10 regression cases in tests/unit/test_upload_restoration_889.py cover: call-list recording, writer-marker flag, multi-slot tracking, JSON round-trip survival, manager rebuild from the list, no-op on empty / missing list, idempotency across repeated restores, writer-fallback warning, and a full HTTP→session→WS-restore end-to-end scenario. (python/djust/uploads.py, python/djust/websocket.py)

Added

  • dj-transition — declarative CSS enter/leave transitions (v0.6.0) — Phoenix JS.transition parity. Three-phase class orchestration so template authors can drive CSS transitions without writing a dj-hook. Attribute value is three space-separated class tokens — phase 1 (start) applied synchronously, phases 2 (active) + 3 (end) applied on the next animation frame so the browser commits the start layout before the transition begins. transitionend removes the active class (phase 3 stays as the final-state). 600 ms fallback timeout covers the display: none / zero-duration corner cases where transitionend never fires. Any attribute-value change re-runs the sequence so authors can retrigger from JS. New static/djust/src/41-dj-transition.js (~120 LOC); document-level MutationObserver matches the dj-dialog / dj-mutation / dj-sticky-scroll registration pattern. 7 JSDOM cases in tests/js/dj_transition.test.js cover spec parsing, phase-1 synchronous application, next-frame phase-2/3 application, transitionend cleanup, fallback-timeout cleanup, global export, and re-trigger-on-attribute-change. This is phase 1 of the v0.6.0 Animations & transitions work; FLIP, dj-remove, dj-transition-group, and skeleton components will ship as separate follow-ups. (python/djust/static/djust/src/41-dj-transition.js)

    See docs/website/guides/declarative-ux-attrs.md.

All releases · Atom feed