etherpad-lite/src
John McLear 3ef99bb03a
fix: flush pending changesets immediately after reconnect (#7458)
* fix: flush pending changesets immediately after reconnect

After reconnecting, setUpSocket() did not call handleUserChanges(),
so any edits made while disconnected were not sent to the server until
the user made another change. This caused divergent pad state between
users.

Now calls handleUserChanges() after reconnect to immediately transmit
any pending local changesets.

Fixes #5108

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: defer handleUserChanges on connect to avoid editor init race

Calling handleUserChanges() synchronously in setUpSocket() caused
"Cannot read properties of null (reading 'changeset')" because the
editor isn't fully initialized on first connect. Deferred with
setTimeout(500ms) to allow initialization to complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add test for pending changeset flush after reconnect

Verifies that edits made while disconnected are transmitted to the
server immediately upon reconnection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: flush pending changesets on actual reconnect, not just initial connect

setUpSocket() only runs during initialization. Move handleUserChanges()
to the reconnect code path so pending edits are flushed when the
connection is re-established.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: flush pending changes when isPendingRevision clears after reconnect

The existing fix in setChannelState('CONNECTED') calls handleUserChanges(),
but at that point isPendingRevision is still true so changes are blocked.
The real trigger must be in setIsPendingRevision(): when it transitions
from true to false (after all CLIENT_RECONNECT messages are processed),
call handleUserChanges() to flush any locally-queued edits.

Also adds a targeted regression test that simulates the exact reconnect
state transitions and verifies pending edits reach the server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: rewrite reconnect flush test for reliability

Replaced the fragile offline/online simulation with a direct test that
uses separate browser contexts. Simplified to a single test that
exercises the exact setIsPendingRevision(false) -> handleUserChanges()
codepath and verifies the flushed text is visible from another client.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: remove Playwright reconnect test (not feasible)

The reconnect test requires access to pad.collabClient internals which
are not exposed on window in the browser context. Playwright cannot
call setStateIdle/setIsPendingRevision/setChannelState. The backend
tests adequately cover the code fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: don't flush in setChannelState to avoid editor init race

Calling handleUserChanges() in setChannelState('CONNECTED') fires
synchronously on first connect before the editor is fully initialized,
breaking chat/user_name tests. The setIsPendingRevision(false) trigger
is sufficient for the reconnect path, and the existing
setTimeout(handleUserChanges, 500) in setUpSocket() already handles
the initial connect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove setTimeout flush in setUpSocket — rely on setIsPendingRevision trigger

The setTimeout(handleUserChanges, 500) in setUpSocket was a timing hack
that:
- Only fires on initial connect (setUpSocket is called once at the end
  of getCollabClient; reconnects go through pad.ts:248 which calls
  setChannelState('CONNECTED') directly, bypassing setUpSocket).
- Doesn't actually fix issue #5108 (the reconnect-flush bug). That's
  fixed deterministically by the wasPending && !value trigger in
  setIsPendingRevision, which fires whenever the server's CLIENT_RECONNECT
  message lands (both for noChanges and after replaying revisions).
- Introduced a 500ms race window on initial pad load.

The reconnect path now relies entirely on the deterministic event-based
trigger (setIsPendingRevision), with no timing assumptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:23:42 +01:00
..
locales Localisation updates from https://translatewiki.net. 2026-04-06 14:04:17 +02:00
node fix: capture head revision atomically with atext to prevent mismatched apply (#7480) 2026-04-07 18:30:08 +01:00
static fix: flush pending changesets immediately after reconnect (#7458) 2026-04-16 15:23:42 +01:00
templates feat: make cookie names configurable with prefix setting (#7450) 2026-04-05 01:58:29 +01:00
tests fix(tests): stabilize flaky list_wrap_indent frontend test (#7522) 2026-04-16 11:24:58 +01:00
.eslintrc.cjs Feat/admin react (#6211) 2024-03-09 23:07:09 +01:00
ep.json 7139 let user maintain a single session across multiple browsers (#7228) 2025-11-18 12:23:55 +01:00
etherpad_icon.svg Change favicon to be closer to new style 2020-04-19 18:20:21 +02:00
package.json fix: explicitly ignore scarf build scripts to fix bin install CI (#7523) 2026-04-16 10:27:40 +01:00
playwright.config.ts chore: show individual test names in CI Playwright output (#7462) 2026-04-05 01:57:49 +01:00
README.md remove one less warning during install by having a readme placeholder 2013-02-10 03:04:08 +00:00
tsconfig.json build(deps-dev): bump the dev-dependencies group with 8 updates (#7429) 2026-04-01 21:33:09 +01:00
vitest.config.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
web.config Added typescript to etherpad 2024-02-05 21:13:02 +01:00

Ignore this file and see the file in the base installation folder