mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(publishing): keep submit guard active until publish settles
Keep one-shot post and reply submissions guarded until the effect-driven publish settles, so duplicate clicks reuse the in-flight request instead of creating a second signed publish. Adds regression tests for post and reply duplicate-submit paths.
This commit is contained in:
@@ -806,6 +806,39 @@ describe('PostForm', () => {
|
|||||||
expect(testState.publishedPostOptions?.content).toBe(`${youtubeLink}\nVideo body`);
|
expect(testState.publishedPostOptions?.content).toBe(`${youtubeLink}\nVideo body`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ignores duplicate post clicks while publish is pending', async () => {
|
||||||
|
let resolvePublish: () => void = () => {};
|
||||||
|
const publishPromise = new Promise<void>((resolve) => {
|
||||||
|
resolvePublish = resolve;
|
||||||
|
});
|
||||||
|
testState.publishPostMock.mockReturnValue(publishPromise);
|
||||||
|
|
||||||
|
await renderPostForm('/all');
|
||||||
|
await clickByText(container, 'start_new_thread');
|
||||||
|
|
||||||
|
const table = container.querySelector('table') as HTMLTableElement;
|
||||||
|
const select = table.querySelector('select') as HTMLSelectElement;
|
||||||
|
const textarea = table.querySelector('textarea') as HTMLTextAreaElement;
|
||||||
|
const postButton = Array.from(table.querySelectorAll('button')).find((button) => button.textContent === 'post') as HTMLButtonElement;
|
||||||
|
|
||||||
|
await dispatchChange(select, 'music-posting.eth');
|
||||||
|
await dispatchInput(textarea, 'Thread body');
|
||||||
|
|
||||||
|
await clickByText(table, 'post');
|
||||||
|
expect(postButton.disabled).toBe(true);
|
||||||
|
|
||||||
|
await clickByText(table, 'post');
|
||||||
|
expect(testState.publishPostMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
resolvePublish();
|
||||||
|
await publishPromise;
|
||||||
|
});
|
||||||
|
await flushEffects();
|
||||||
|
|
||||||
|
expect(postButton.disabled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('publishes known twimg query-format post links with a path extension without editing the field', async () => {
|
it('publishes known twimg query-format post links with a path extension without editing the field', async () => {
|
||||||
const twimgLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium';
|
const twimgLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium';
|
||||||
const publishLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg';
|
const publishLink = 'https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg';
|
||||||
|
|||||||
@@ -713,7 +713,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
|||||||
};
|
};
|
||||||
|
|
||||||
nonokoRedirectPathRef.current = hasNonokoOption(currentOptions) ? getBoardIndexPath() : null;
|
nonokoRedirectPathRef.current = hasNonokoOption(currentOptions) ? getBoardIndexPath() : null;
|
||||||
publishPost({ content: publishContent, ...getPublishLinkOptions(currentUrl, appliedYouTubeConversion), ...publishOptions });
|
await publishPost({ content: publishContent, ...getPublishLinkOptions(currentUrl, appliedYouTubeConversion), ...publishOptions });
|
||||||
});
|
});
|
||||||
|
|
||||||
// redirect to pending page when pending comment is created
|
// redirect to pending page when pending comment is created
|
||||||
|
|||||||
@@ -156,6 +156,50 @@ describe('usePublishPost', () => {
|
|||||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps a one-shot publish pending and ignores duplicate publish calls until it settles', async () => {
|
||||||
|
let resolvePublish: () => void = () => {};
|
||||||
|
const publishPromise = new Promise<void>((resolve) => {
|
||||||
|
resolvePublish = resolve;
|
||||||
|
});
|
||||||
|
testState.publishCommentMock.mockReturnValue(publishPromise);
|
||||||
|
|
||||||
|
let firstPublish: Promise<void> | undefined;
|
||||||
|
let secondPublish: Promise<void> | undefined;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstPublish = latestValue.publishPost({
|
||||||
|
content: 'Fresh body',
|
||||||
|
} as never);
|
||||||
|
secondPublish = latestValue.publishPost({
|
||||||
|
content: 'Duplicate body',
|
||||||
|
} as never);
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(firstPublish).toBe(secondPublish);
|
||||||
|
expect(testState.lastPublishOptions?.content).toBe('Fresh body');
|
||||||
|
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
let isSettled = false;
|
||||||
|
firstPublish?.then(() => {
|
||||||
|
isSettled = true;
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(isSettled).toBe(false);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
resolvePublish();
|
||||||
|
await publishPromise;
|
||||||
|
await firstPublish;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(isSettled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('clears stale flag data from one-shot publish options', async () => {
|
it('clears stale flag data from one-shot publish options', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
latestValue.setPublishPostOptions({
|
latestValue.setPublishPostOptions({
|
||||||
|
|||||||
@@ -179,7 +179,9 @@ describe('usePublishReply', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply();
|
latestValue.publishReply();
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
});
|
});
|
||||||
@@ -197,7 +199,9 @@ describe('usePublishReply', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply();
|
latestValue.publishReply();
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
});
|
});
|
||||||
@@ -215,17 +219,65 @@ describe('usePublishReply', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply({
|
latestValue.publishReply({
|
||||||
content: 'Fresh reply',
|
content: 'Fresh reply',
|
||||||
} as never);
|
} as never);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(testState.lastPublishOptions?.content).toBe('Fresh reply');
|
expect(testState.lastPublishOptions?.content).toBe('Fresh reply');
|
||||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps a one-shot reply pending and ignores duplicate publish calls until it settles', async () => {
|
||||||
|
let resolvePublish: () => void = () => {};
|
||||||
|
const publishPromise = new Promise<void>((resolve) => {
|
||||||
|
resolvePublish = resolve;
|
||||||
|
});
|
||||||
|
testState.publishCommentMock.mockReturnValue(publishPromise);
|
||||||
|
|
||||||
|
let firstPublish: Promise<void> | undefined;
|
||||||
|
let secondPublish: Promise<void> | undefined;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstPublish = latestValue.publishReply({
|
||||||
|
content: 'Fresh reply',
|
||||||
|
} as never);
|
||||||
|
secondPublish = latestValue.publishReply({
|
||||||
|
content: 'Duplicate reply',
|
||||||
|
} as never);
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(firstPublish).toBe(secondPublish);
|
||||||
|
expect(testState.lastPublishOptions?.content).toBe('Fresh reply');
|
||||||
|
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
let isSettled = false;
|
||||||
|
firstPublish?.then(() => {
|
||||||
|
isSettled = true;
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(isSettled).toBe(false);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
resolvePublish();
|
||||||
|
await publishPromise;
|
||||||
|
await firstPublish;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(isSettled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('clears stale flag data from one-shot reply options', async () => {
|
it('clears stale flag data from one-shot reply options', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
latestValue.setPublishReplyOptions({
|
latestValue.setPublishReplyOptions({
|
||||||
@@ -238,13 +290,15 @@ describe('usePublishReply', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply({
|
latestValue.publishReply({
|
||||||
content: 'Plain reply',
|
content: 'Plain reply',
|
||||||
challengeRequest: undefined,
|
challengeRequest: undefined,
|
||||||
flairs: undefined,
|
flairs: undefined,
|
||||||
} as never);
|
} as never);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(testState.lastPublishOptions?.content).toBe('Plain reply');
|
expect(testState.lastPublishOptions?.content).toBe('Plain reply');
|
||||||
@@ -263,8 +317,9 @@ describe('usePublishReply', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply();
|
latestValue.publishReply();
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(latestValue.publishReplyError).toContain('external_quote_publish_missing');
|
expect(latestValue.publishReplyError).toContain('external_quote_publish_missing');
|
||||||
@@ -276,7 +331,8 @@ describe('usePublishReply', () => {
|
|||||||
renderHook();
|
renderHook();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await latestValue.publishReply();
|
latestValue.publishReply();
|
||||||
|
await Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(latestValue.publishReplyError).toBe('blocked:unresolved');
|
expect(latestValue.publishReplyError).toBe('blocked:unresolved');
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
|
type PendingPublishRequest = {
|
||||||
|
id: number;
|
||||||
|
promise: Promise<void>;
|
||||||
|
resolve: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePendingPublishRequest = () => {
|
||||||
|
const nextRequestIdRef = useRef(0);
|
||||||
|
const pendingRequestRef = useRef<PendingPublishRequest | null>(null);
|
||||||
|
|
||||||
|
const startPendingPublishRequest = useCallback(() => {
|
||||||
|
if (pendingRequestRef.current) {
|
||||||
|
return { request: pendingRequestRef.current, started: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = nextRequestIdRef.current + 1;
|
||||||
|
nextRequestIdRef.current = id;
|
||||||
|
|
||||||
|
let resolveRequest: () => void = () => {};
|
||||||
|
const promise = new Promise<void>((resolve) => {
|
||||||
|
resolveRequest = resolve;
|
||||||
|
});
|
||||||
|
const request = { id, promise, resolve: resolveRequest };
|
||||||
|
pendingRequestRef.current = request;
|
||||||
|
|
||||||
|
return { request, started: true };
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const finishPendingPublishRequest = useCallback((requestId: number) => {
|
||||||
|
const request = pendingRequestRef.current;
|
||||||
|
if (!request || request.id !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingRequestRef.current = null;
|
||||||
|
request.resolve();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { finishPendingPublishRequest, startPendingPublishRequest };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default usePendingPublishRequest;
|
||||||
@@ -4,6 +4,7 @@ import { useShallow } from 'zustand/react/shallow';
|
|||||||
import usePublishPostStore from '../stores/use-publish-post-store';
|
import usePublishPostStore from '../stores/use-publish-post-store';
|
||||||
import useChallengesStore from '../stores/use-challenges-store';
|
import useChallengesStore from '../stores/use-challenges-store';
|
||||||
import usePublishAuthorDomainGuard, { getPublishAuthorDomainErrorMessage } from './use-publish-author-domain-guard';
|
import usePublishAuthorDomainGuard, { getPublishAuthorDomainErrorMessage } from './use-publish-author-domain-guard';
|
||||||
|
import usePendingPublishRequest from './use-pending-publish-request';
|
||||||
|
|
||||||
type UsePublishPostOptions = {
|
type UsePublishPostOptions = {
|
||||||
communityAddress?: string;
|
communityAddress?: string;
|
||||||
@@ -30,6 +31,7 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
|||||||
const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0);
|
const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0);
|
||||||
const startedPublishRequestIdRef = useRef(0);
|
const startedPublishRequestIdRef = useRef(0);
|
||||||
const { blockedReason } = usePublishAuthorDomainGuard();
|
const { blockedReason } = usePublishAuthorDomainGuard();
|
||||||
|
const { finishPendingPublishRequest, startPendingPublishRequest } = usePendingPublishRequest();
|
||||||
const abandonCurrentPublish = useCallback(async () => {
|
const abandonCurrentPublish = useCallback(async () => {
|
||||||
await abandonPublishRef.current?.();
|
await abandonPublishRef.current?.();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -95,14 +97,14 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
|||||||
setPublishPostError(null);
|
setPublishPostError(null);
|
||||||
}, [author?.displayName, blockedReason, communityAddress, content, flairs, link, spoiler, title]);
|
}, [author?.displayName, blockedReason, communityAddress, content, flairs, link, spoiler, title]);
|
||||||
|
|
||||||
const startPublishPost = useCallback(() => {
|
const startPublishPost = useCallback(async () => {
|
||||||
if (blockedReason) {
|
if (blockedReason) {
|
||||||
setPublishPostError(getPublishAuthorDomainErrorMessage(blockedReason));
|
setPublishPostError(getPublishAuthorDomainErrorMessage(blockedReason));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPublishPostError(null);
|
setPublishPostError(null);
|
||||||
return publishComment();
|
await publishComment();
|
||||||
}, [blockedReason, publishComment]);
|
}, [blockedReason, publishComment]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -110,21 +112,32 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startedPublishRequestIdRef.current = pendingPublishRequestId;
|
const requestId = pendingPublishRequestId;
|
||||||
startPublishPost();
|
startedPublishRequestIdRef.current = requestId;
|
||||||
}, [pendingPublishRequestId, startPublishPost]);
|
void startPublishPost()
|
||||||
|
.catch(() => undefined)
|
||||||
|
.finally(() => finishPendingPublishRequest(requestId));
|
||||||
|
}, [finishPendingPublishRequest, pendingPublishRequestId, startPublishPost]);
|
||||||
|
|
||||||
const publishPost = useCallback(
|
const publishPost = useCallback(
|
||||||
(options?: Partial<Comment>) => {
|
(options?: Partial<Comment>) => {
|
||||||
if (options) {
|
const pendingRequest = startPendingPublishRequest();
|
||||||
setPublishPostOptions(options);
|
if (!pendingRequest.started) {
|
||||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
return pendingRequest.request.promise;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return startPublishPost();
|
if (options) {
|
||||||
|
setPublishPostOptions(options);
|
||||||
|
setPendingPublishRequestId(pendingRequest.request.id);
|
||||||
|
return pendingRequest.request.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startPublishPost()
|
||||||
|
.catch(() => undefined)
|
||||||
|
.finally(() => finishPendingPublishRequest(pendingRequest.request.id));
|
||||||
|
return pendingRequest.request.promise;
|
||||||
},
|
},
|
||||||
[setPublishPostOptions, startPublishPost],
|
[finishPendingPublishRequest, setPublishPostOptions, startPendingPublishRequest, startPublishPost],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { extractUnresolvedExternalQuoteReferences, getExternalQuoteStatusMessage
|
|||||||
import { resolveExternalQuoteTarget } from '../lib/utils/external-quote-resolver';
|
import { resolveExternalQuoteTarget } from '../lib/utils/external-quote-resolver';
|
||||||
import useChallengesStore from '../stores/use-challenges-store';
|
import useChallengesStore from '../stores/use-challenges-store';
|
||||||
import usePublishAuthorDomainGuard, { getPublishAuthorDomainErrorMessage } from './use-publish-author-domain-guard';
|
import usePublishAuthorDomainGuard, { getPublishAuthorDomainErrorMessage } from './use-publish-author-domain-guard';
|
||||||
|
import usePendingPublishRequest from './use-pending-publish-request';
|
||||||
|
|
||||||
type UsePublishReplyOptions = {
|
type UsePublishReplyOptions = {
|
||||||
cid: string;
|
cid: string;
|
||||||
@@ -47,6 +48,7 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
|||||||
const [isResolvingExternalQuotes, setIsResolvingExternalQuotes] = useState(false);
|
const [isResolvingExternalQuotes, setIsResolvingExternalQuotes] = useState(false);
|
||||||
const [publishReplyError, setPublishReplyError] = useState<string | null>(null);
|
const [publishReplyError, setPublishReplyError] = useState<string | null>(null);
|
||||||
const [publishReplyStateMessage, setPublishReplyStateMessage] = useState<string | null>(null);
|
const [publishReplyStateMessage, setPublishReplyStateMessage] = useState<string | null>(null);
|
||||||
|
const { finishPendingPublishRequest, startPendingPublishRequest } = usePendingPublishRequest();
|
||||||
const abandonCurrentPublish = useCallback(async () => {
|
const abandonCurrentPublish = useCallback(async () => {
|
||||||
await abandonPublishRef.current?.();
|
await abandonPublishRef.current?.();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -166,35 +168,35 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startedPublishRequestIdRef.current = pendingPublishRequestId;
|
const requestId = pendingPublishRequestId;
|
||||||
publishComment();
|
startedPublishRequestIdRef.current = requestId;
|
||||||
}, [pendingPublishRequestId, publishComment]);
|
void Promise.resolve()
|
||||||
|
.then(() => publishComment())
|
||||||
const publishReply = useCallback(
|
.catch(() => undefined)
|
||||||
async (options?: Partial<Comment>) => {
|
.finally(() => finishPendingPublishRequest(requestId));
|
||||||
if (options) {
|
}, [finishPendingPublishRequest, pendingPublishRequestId, publishComment]);
|
||||||
setPublishReplyOptions(options);
|
|
||||||
setPendingSyncedPublishRequestId((requestId) => requestId + 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const preparePublishReply = useCallback(
|
||||||
|
async (requestId: number) => {
|
||||||
setPublishReplyError(null);
|
setPublishReplyError(null);
|
||||||
|
|
||||||
if (blockedReason) {
|
if (blockedReason) {
|
||||||
setPublishReplyStateMessage(null);
|
setPublishReplyStateMessage(null);
|
||||||
setPublishReplyError(getPublishAuthorDomainErrorMessage(blockedReason));
|
setPublishReplyError(getPublishAuthorDomainErrorMessage(blockedReason));
|
||||||
|
finishPendingPublishRequest(requestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publishResolvableQuoteReferences.length === 0) {
|
if (publishResolvableQuoteReferences.length === 0) {
|
||||||
setResolvedExternalQuotedCids(undefined);
|
setResolvedExternalQuotedCids(undefined);
|
||||||
setPublishReplyStateMessage(null);
|
setPublishReplyStateMessage(null);
|
||||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
setPendingPublishRequestId(requestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!account?.id) {
|
if (!account?.id) {
|
||||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||||
|
finishPendingPublishRequest(requestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +222,7 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
|||||||
quote: reference.raw,
|
quote: reference.raw,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
finishPendingPublishRequest(requestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,14 +231,34 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
|||||||
|
|
||||||
setResolvedExternalQuotedCids(resolvedCids.size > 0 ? [...resolvedCids] : undefined);
|
setResolvedExternalQuotedCids(resolvedCids.size > 0 ? [...resolvedCids] : undefined);
|
||||||
setPublishReplyStateMessage(null);
|
setPublishReplyStateMessage(null);
|
||||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
setPendingPublishRequestId(requestId);
|
||||||
} catch {
|
} catch {
|
||||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||||
|
finishPendingPublishRequest(requestId);
|
||||||
} finally {
|
} finally {
|
||||||
setIsResolvingExternalQuotes(false);
|
setIsResolvingExternalQuotes(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[account, blockedReason, directories, publishResolvableQuoteReferences, setPublishReplyOptions, t],
|
[account, blockedReason, directories, finishPendingPublishRequest, publishResolvableQuoteReferences, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const publishReply = useCallback(
|
||||||
|
(options?: Partial<Comment>) => {
|
||||||
|
const pendingRequest = startPendingPublishRequest();
|
||||||
|
if (!pendingRequest.started) {
|
||||||
|
return pendingRequest.request.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
setPublishReplyOptions(options);
|
||||||
|
setPendingSyncedPublishRequestId(pendingRequest.request.id);
|
||||||
|
return pendingRequest.request.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
void preparePublishReply(pendingRequest.request.id).catch(() => finishPendingPublishRequest(pendingRequest.request.id));
|
||||||
|
return pendingRequest.request.promise;
|
||||||
|
},
|
||||||
|
[finishPendingPublishRequest, preparePublishReply, setPublishReplyOptions, startPendingPublishRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -243,9 +266,10 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startedSyncedPublishRequestIdRef.current = pendingSyncedPublishRequestId;
|
const requestId = pendingSyncedPublishRequestId;
|
||||||
publishReply();
|
startedSyncedPublishRequestIdRef.current = requestId;
|
||||||
}, [pendingSyncedPublishRequestId, publishReply]);
|
void preparePublishReply(requestId).catch(() => finishPendingPublishRequest(requestId));
|
||||||
|
}, [finishPendingPublishRequest, pendingSyncedPublishRequestId, preparePublishReply]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isResolvingExternalQuotes,
|
isResolvingExternalQuotes,
|
||||||
|
|||||||
Reference in New Issue
Block a user