fix(thread-page): make thread auto updates opt-in (#1115)

* fix(thread-page): make thread auto updates opt-in

Wire `Auto` and `Update` to the same manual refresh path and cover the thread flow with an e2e harness.

* fix(thread-page): address PR review findings
This commit is contained in:
Tommaso Casaburi
2026-03-20 16:48:57 +08:00
committed by GitHub
parent ce2ad82c8c
commit 816281c607
13 changed files with 630 additions and 49 deletions
@@ -8,6 +8,7 @@ import usePostNumberStore from '../use-post-number-store';
import useReplyModalStore from '../use-reply-modal-store';
import useSelectedTextStore from '../use-selected-text-store';
import useSortingStore from '../use-sorting-store';
import useThreadLiveUpdatesStore from '../use-thread-live-updates-store';
const resetReplyModalStore = () => {
useReplyModalStore.setState({
@@ -41,6 +42,7 @@ describe('interaction stores', () => {
usePostNumberStore.setState({ numberToCid: {}, cidToNumber: {} });
useSelectedTextStore.getState().resetSelectedText();
useSortingStore.getState().setSortType('active');
useThreadLiveUpdatesStore.getState().resetState();
resetReplyModalStore();
Object.defineProperty(window, 'innerWidth', {
@@ -86,6 +88,61 @@ describe('interaction stores', () => {
expect(useSortingStore.getState().sortType).toBe('replyCount');
});
it('tracks thread live update toggle state and queues manual refresh requests', () => {
const store = useThreadLiveUpdatesStore.getState();
expect(store.enabled).toBe(false);
expect(store.isUpdating).toBe(false);
expect(store.updateRequestId).toBe(0);
expect(store.repliesResetRequestId).toBe(0);
store.setEnabled(true);
expect(useThreadLiveUpdatesStore.getState().enabled).toBe(true);
store.toggleEnabled();
expect(useThreadLiveUpdatesStore.getState().enabled).toBe(false);
store.requestUpdate();
expect(useThreadLiveUpdatesStore.getState().updateRequestId).toBe(1);
store.startUpdate();
expect(useThreadLiveUpdatesStore.getState().isUpdating).toBe(true);
store.finishUpdate(1);
expect(useThreadLiveUpdatesStore.getState()).toMatchObject({
isUpdating: false,
repliesResetRequestId: 1,
});
store.requestUpdate();
expect(useThreadLiveUpdatesStore.getState().updateRequestId).toBe(2);
store.startUpdate();
store.finishUpdate(2, false);
expect(useThreadLiveUpdatesStore.getState()).toMatchObject({
isUpdating: false,
repliesResetRequestId: 1,
});
store.requestUpdate();
store.startUpdate();
store.requestUpdate();
store.startUpdate();
store.finishUpdate(3);
expect(useThreadLiveUpdatesStore.getState()).toMatchObject({
isUpdating: true,
repliesResetRequestId: 3,
updateRequestId: 4,
});
store.finishUpdate(4);
expect(useThreadLiveUpdatesStore.getState()).toMatchObject({
isUpdating: false,
repliesResetRequestId: 4,
updateRequestId: 4,
});
});
it('queues challenges, abandons the current one, and logs abandon failures', async () => {
const abandonMock = vi.fn().mockResolvedValue(undefined);
const failingAbandonMock = vi.fn().mockRejectedValue(new Error('stop failed'));