mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
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:
@@ -4,6 +4,7 @@ import { createRoot, type Root } from 'react-dom/client';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { DesktopBoardButtons, MobileBoardButtons } from '../board-buttons';
|
||||
import useThreadLiveUpdatesStore from '../../../stores/use-thread-live-updates-store';
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||
@@ -237,6 +238,7 @@ describe('BoardButtons', () => {
|
||||
testState.sortType = 'active';
|
||||
testState.subscribed = false;
|
||||
testState.viewMode = 'compact';
|
||||
useThreadLiveUpdatesStore.getState().resetState();
|
||||
Object.defineProperty(globalThis, 'alert', {
|
||||
configurable: true,
|
||||
value: vi.fn(),
|
||||
@@ -321,7 +323,7 @@ describe('BoardButtons', () => {
|
||||
expect(testState.resetMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('renders thread actions and post stats, then updates, auto-alerts, and scrolls to the bottom', async () => {
|
||||
it('renders thread actions and post stats, then requests refreshes, toggles auto updates, and scrolls to the bottom', async () => {
|
||||
testState.commentsByCid = {
|
||||
'comment-1': {
|
||||
cid: 'comment-1',
|
||||
@@ -345,11 +347,36 @@ describe('BoardButtons', () => {
|
||||
|
||||
await clickButton('bottom');
|
||||
await clickButton('update');
|
||||
await clickButton('Auto');
|
||||
const autoCheckbox = container.querySelector<HTMLInputElement>('input[type="checkbox"]');
|
||||
expect(autoCheckbox?.checked).toBe(false);
|
||||
await act(async () => {
|
||||
autoCheckbox?.click();
|
||||
});
|
||||
|
||||
expect(window.scrollTo).toHaveBeenCalledWith({ behavior: 'instant', top: 2400 });
|
||||
expect(testState.resetMock).toHaveBeenCalledTimes(1);
|
||||
expect(globalThis.alert).toHaveBeenCalledWith('posts_auto_update_info');
|
||||
expect(useThreadLiveUpdatesStore.getState().updateRequestId).toBe(1);
|
||||
expect(useThreadLiveUpdatesStore.getState().enabled).toBe(true);
|
||||
expect(autoCheckbox?.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the update button enabled while a manual thread refresh is in progress', async () => {
|
||||
testState.commentsByCid = {
|
||||
'comment-1': {
|
||||
cid: 'comment-1',
|
||||
postCid: 'comment-1',
|
||||
replyCount: 9,
|
||||
},
|
||||
};
|
||||
useThreadLiveUpdatesStore.getState().startUpdate();
|
||||
|
||||
await renderWithRoute(createElement(DesktopBoardButtons), '/mu/thread/comment-1');
|
||||
|
||||
const updateButton = Array.from(container.querySelectorAll('button')).find((candidate) => candidate.textContent === 'update');
|
||||
expect(updateButton?.hasAttribute('disabled')).toBe(false);
|
||||
|
||||
await clickButton('update');
|
||||
|
||||
expect(useThreadLiveUpdatesStore.getState().updateRequestId).toBe(1);
|
||||
});
|
||||
|
||||
it('renders mobile mod-queue controls and clamps alert threshold updates', async () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import useSortingStore from '../../stores/use-sorting-store';
|
||||
import useAllFeedFilterStore from '../../stores/use-all-feed-filter-store';
|
||||
import useModQueueStore from '../../stores/use-mod-queue-store';
|
||||
import useFeedViewSettingsStore from '../../stores/use-feed-view-settings-store';
|
||||
import useThreadLiveUpdatesStore from '../../stores/use-thread-live-updates-store';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import CatalogFilters from '../catalog-filters';
|
||||
@@ -35,7 +36,6 @@ interface BoardButtonsProps {
|
||||
|
||||
export const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const directories = useDirectories();
|
||||
|
||||
const createCatalogLink = () => {
|
||||
@@ -158,9 +158,9 @@ export const RefreshButton = () => {
|
||||
|
||||
export const UpdateButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const reset = useFeedResetStore((state) => state.reset);
|
||||
const requestUpdate = useThreadLiveUpdatesStore((state) => state.requestUpdate);
|
||||
return (
|
||||
<button className='button' onClick={() => reset?.()}>
|
||||
<button className='button' onClick={() => requestUpdate()}>
|
||||
{t('update')}
|
||||
</button>
|
||||
);
|
||||
@@ -169,26 +169,19 @@ export const UpdateButton = () => {
|
||||
export const AutoButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = useIsMobile();
|
||||
const handleAutoClick = () => {
|
||||
window.alert(t('posts_auto_update_info'));
|
||||
};
|
||||
|
||||
const autoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.enabled);
|
||||
const setAutoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.setEnabled);
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<button className='button' onClick={handleAutoClick}>
|
||||
<label>
|
||||
<input type='checkbox' className={styles.autoCheckbox} checked disabled />
|
||||
{t('Auto')}
|
||||
</label>
|
||||
</button>
|
||||
) : (
|
||||
<label onClick={handleAutoClick}>
|
||||
{' '}
|
||||
<input type='checkbox' className={styles.autoCheckbox} checked disabled /> {t('Auto')}
|
||||
</label>
|
||||
)}
|
||||
</>
|
||||
<label className={isMobile ? 'button' : undefined}>
|
||||
<input
|
||||
type='checkbox'
|
||||
aria-label={t('Auto')}
|
||||
className={styles.autoCheckbox}
|
||||
checked={autoUpdateEnabled}
|
||||
onChange={(event) => setAutoUpdateEnabled(event.target.checked)}
|
||||
/>{' '}
|
||||
{t('Auto')}
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -495,14 +488,15 @@ export const PostPageStats = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const autoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.enabled);
|
||||
const commentCid = params?.commentCid as string | undefined;
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
const comment = useComment({ commentCid });
|
||||
const comment = useComment({ commentCid, autoUpdate: autoUpdateEnabled });
|
||||
const postCid = comment?.postCid ?? commentCid;
|
||||
const post = useComment({ commentCid: postCid });
|
||||
const post = useComment({ commentCid: postCid, autoUpdate: autoUpdateEnabled });
|
||||
|
||||
const archived = isCommentArchived(post);
|
||||
const { closed, pinned, replyCount } = post || {};
|
||||
|
||||
Reference in New Issue
Block a user