mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(post options): support nonoko board redirects (#1137)
This commit is contained in:
@@ -23,6 +23,7 @@ const testState = vi.hoisted(() => ({
|
||||
isMobile: false,
|
||||
isResolvingExternalQuotes: false,
|
||||
isUploading: false,
|
||||
navigateMock: vi.fn(),
|
||||
offlineTitle: '' as string | false,
|
||||
offlineStates: {} as Record<string, { isOffline: boolean; isOnlineStatusLoading: boolean; offlineTitle: string | false }>,
|
||||
offlineStatusLoading: false,
|
||||
@@ -74,6 +75,14 @@ vi.mock('react-i18next', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => testState.navigateMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
setAccount: (account: unknown) => testState.setAccountMock(account),
|
||||
useAccount: () => testState.account,
|
||||
@@ -333,6 +342,7 @@ describe('ReplyModal', () => {
|
||||
testState.isMobile = false;
|
||||
testState.isResolvingExternalQuotes = false;
|
||||
testState.isUploading = false;
|
||||
testState.navigateMock.mockReset();
|
||||
testState.offlineTitle = '';
|
||||
testState.offlineStates = {};
|
||||
testState.offlineStatusLoading = false;
|
||||
@@ -686,6 +696,29 @@ describe('ReplyModal', () => {
|
||||
expect(testState.closeModalMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('redirects to the board index after a reply when nonoko is used', async () => {
|
||||
testState.openEmpty = true;
|
||||
testState.selectedText = '';
|
||||
testState.publishReplyMock.mockImplementation(() => {
|
||||
testState.replyIndex = 3;
|
||||
});
|
||||
|
||||
await renderReplyModal('/mu/thread/post-1');
|
||||
|
||||
const optionsInput = container.querySelectorAll<HTMLInputElement>('input[type="text"]')[1];
|
||||
const textarea = container.querySelector<HTMLTextAreaElement>('textarea');
|
||||
|
||||
await dispatchInput(optionsInput, 'nonoko');
|
||||
await dispatchInput(textarea as HTMLTextAreaElement, 'reply body');
|
||||
await clickButtonByText('post');
|
||||
await rerenderReplyModal('/mu/thread/post-1');
|
||||
|
||||
expect(testState.publishReplyMock).toHaveBeenCalledTimes(1);
|
||||
expect(testState.resetPublishReplyOptionsMock).toHaveBeenCalledTimes(1);
|
||||
expect(testState.closeModalMock).toHaveBeenCalledTimes(1);
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/mu');
|
||||
});
|
||||
|
||||
it('inserts quote requests only once and keeps the textarea content stable across rerenders', async () => {
|
||||
testState.isMobile = true;
|
||||
testState.openEmpty = true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { setAccount, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
getContentWithPostOptionState as getContentWithOptions,
|
||||
getPostOptionsDirectoryCode,
|
||||
getUnsupportedPostOptionsMessage,
|
||||
hasNonokoOption,
|
||||
isUnsupportedPostOptionsMessage,
|
||||
} from '../../lib/utils/post-options-utils';
|
||||
import { getPublishURLFilename, isValidPublishURL } from '../../lib/utils/url-utils';
|
||||
@@ -50,6 +51,7 @@ interface ReplyModalProps {
|
||||
const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threadNumber, postCid, scrollY, communityAddress }: ReplyModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInModView = isModView(location.pathname);
|
||||
@@ -86,6 +88,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
||||
const optionsRef = useRef<HTMLInputElement>(null);
|
||||
const fortuneEntryRef = useRef<FortuneEntry | null>(null);
|
||||
const diceRollRef = useRef<DiceRoll | null>(null);
|
||||
const nonokoRedirectPathRef = useRef<string | null>(null);
|
||||
const lastSelectionStartRef = useRef(0);
|
||||
const lastSelectionEndRef = useRef(0);
|
||||
const lastProcessedQuoteInsertRequestIdRef = useRef(0);
|
||||
@@ -133,6 +136,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
||||
checkContentLengthRef.current.cancel();
|
||||
checkPostOptionsRef.current.cancel();
|
||||
setLengthError(null);
|
||||
nonokoRedirectPathRef.current = null;
|
||||
|
||||
if (currentOptionsError) {
|
||||
setError(currentOptionsError);
|
||||
@@ -160,15 +164,21 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
||||
}
|
||||
|
||||
setError(null);
|
||||
nonokoRedirectPathRef.current = hasNonokoOption(currentOptions) ? `/${postOptionsDirectoryCode || params.boardIdentifier || communityAddress}` : null;
|
||||
publishReply({ content: publishContent });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof replyIndex === 'number') {
|
||||
const nonokoRedirectPath = nonokoRedirectPathRef.current;
|
||||
nonokoRedirectPathRef.current = null;
|
||||
resetPublishReplyOptions();
|
||||
closeModal();
|
||||
if (nonokoRedirectPath) {
|
||||
navigate(nonokoRedirectPath);
|
||||
}
|
||||
}
|
||||
}, [replyIndex, resetPublishReplyOptions, closeModal]);
|
||||
}, [replyIndex, resetPublishReplyOptions, closeModal, navigate]);
|
||||
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
Reference in New Issue
Block a user