mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(posting): add scoped options and qst formatting (#1135)
* feat(posting): add scoped options and qst formatting * fix(posting): address post options review feedback * fix(posting): sync option content before publish * fix(posting): handle final review nits
This commit is contained in:
@@ -137,4 +137,22 @@ describe('usePublishPost', () => {
|
||||
expect(latestValue.publishPostError).toBe('blocked:unresolved');
|
||||
expect(testState.publishCommentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('publishes after synchronizing one-shot publish options', async () => {
|
||||
await act(async () => {
|
||||
latestValue.setPublishPostOptions({
|
||||
content: 'Old body',
|
||||
} as never);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
latestValue.publishPost({
|
||||
content: 'Fresh body',
|
||||
} as never);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(testState.lastPublishOptions?.content).toBe('Fresh body');
|
||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,6 +167,25 @@ describe('usePublishReply', () => {
|
||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('publishes after synchronizing one-shot reply options', async () => {
|
||||
await act(async () => {
|
||||
latestValue.setPublishReplyOptions({
|
||||
content: 'Old reply',
|
||||
} as never);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await latestValue.publishReply({
|
||||
content: 'Fresh reply',
|
||||
} as never);
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
|
||||
expect(testState.lastPublishOptions?.content).toBe('Fresh reply');
|
||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('blocks publish when a same-board external quote cannot be resolved', async () => {
|
||||
testState.resolveExternalQuoteTargetMock.mockResolvedValue(null);
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
||||
const addChallenge = useChallengesStore((state) => state.addChallenge);
|
||||
const abandonPublishRef = useRef<(() => Promise<void>) | undefined>();
|
||||
const [publishPostError, setPublishPostError] = useState<string | null>(null);
|
||||
const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0);
|
||||
const startedPublishRequestIdRef = useRef(0);
|
||||
const { blockedReason } = usePublishAuthorDomainGuard();
|
||||
const abandonCurrentPublish = useCallback(async () => {
|
||||
await abandonPublishRef.current?.();
|
||||
@@ -90,7 +92,7 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
||||
setPublishPostError(null);
|
||||
}, [author?.displayName, blockedReason, communityAddress, content, link, spoiler, title]);
|
||||
|
||||
const publishPost = useCallback(() => {
|
||||
const startPublishPost = useCallback(() => {
|
||||
if (blockedReason) {
|
||||
setPublishPostError(getPublishAuthorDomainErrorMessage(blockedReason));
|
||||
return;
|
||||
@@ -100,6 +102,28 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => {
|
||||
return publishComment();
|
||||
}, [blockedReason, publishComment]);
|
||||
|
||||
useEffect(() => {
|
||||
if (pendingPublishRequestId === 0 || pendingPublishRequestId === startedPublishRequestIdRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
startedPublishRequestIdRef.current = pendingPublishRequestId;
|
||||
startPublishPost();
|
||||
}, [pendingPublishRequestId, startPublishPost]);
|
||||
|
||||
const publishPost = useCallback(
|
||||
(options?: Partial<Comment>) => {
|
||||
if (options) {
|
||||
setPublishPostOptions(options);
|
||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
return startPublishPost();
|
||||
},
|
||||
[setPublishPostOptions, startPublishPost],
|
||||
);
|
||||
|
||||
return {
|
||||
setPublishPostOptions,
|
||||
resetPublishPostOptions,
|
||||
|
||||
@@ -41,6 +41,8 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
||||
const startedPublishRequestIdRef = useRef(0);
|
||||
const [resolvedExternalQuotedCids, setResolvedExternalQuotedCids] = useState<string[] | undefined>();
|
||||
const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0);
|
||||
const [pendingSyncedPublishRequestId, setPendingSyncedPublishRequestId] = useState(0);
|
||||
const startedSyncedPublishRequestIdRef = useRef(0);
|
||||
const [isResolvingExternalQuotes, setIsResolvingExternalQuotes] = useState(false);
|
||||
const [publishReplyError, setPublishReplyError] = useState<string | null>(null);
|
||||
const [publishReplyStateMessage, setPublishReplyStateMessage] = useState<string | null>(null);
|
||||
@@ -151,64 +153,82 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti
|
||||
publishComment();
|
||||
}, [pendingPublishRequestId, publishComment]);
|
||||
|
||||
const publishReply = useCallback(async () => {
|
||||
setPublishReplyError(null);
|
||||
|
||||
if (blockedReason) {
|
||||
setPublishReplyStateMessage(null);
|
||||
setPublishReplyError(getPublishAuthorDomainErrorMessage(blockedReason));
|
||||
return;
|
||||
}
|
||||
|
||||
if (publishResolvableQuoteReferences.length === 0) {
|
||||
setResolvedExternalQuotedCids(undefined);
|
||||
setPublishReplyStateMessage(null);
|
||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account?.id) {
|
||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsResolvingExternalQuotes(true);
|
||||
|
||||
try {
|
||||
const resolvedCids = new Set<string>();
|
||||
|
||||
for (const reference of publishResolvableQuoteReferences) {
|
||||
const resolvedTarget = await resolveExternalQuoteTarget({
|
||||
account,
|
||||
directories,
|
||||
onStatus: (status) => {
|
||||
setPublishReplyStateMessage(getExternalQuoteStatusMessage(t, status));
|
||||
},
|
||||
reference,
|
||||
});
|
||||
|
||||
if (!resolvedTarget?.cid) {
|
||||
setPublishReplyError(
|
||||
t('external_quote_publish_missing', {
|
||||
interpolation: { escapeValue: false },
|
||||
quote: reference.raw,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
resolvedCids.add(resolvedTarget.cid);
|
||||
const publishReply = useCallback(
|
||||
async (options?: Partial<Comment>) => {
|
||||
if (options) {
|
||||
setPublishReplyOptions(options);
|
||||
setPendingSyncedPublishRequestId((requestId) => requestId + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
setResolvedExternalQuotedCids(resolvedCids.size > 0 ? [...resolvedCids] : undefined);
|
||||
setPublishReplyStateMessage(null);
|
||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
||||
} catch {
|
||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||
} finally {
|
||||
setIsResolvingExternalQuotes(false);
|
||||
setPublishReplyError(null);
|
||||
|
||||
if (blockedReason) {
|
||||
setPublishReplyStateMessage(null);
|
||||
setPublishReplyError(getPublishAuthorDomainErrorMessage(blockedReason));
|
||||
return;
|
||||
}
|
||||
|
||||
if (publishResolvableQuoteReferences.length === 0) {
|
||||
setResolvedExternalQuotedCids(undefined);
|
||||
setPublishReplyStateMessage(null);
|
||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account?.id) {
|
||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsResolvingExternalQuotes(true);
|
||||
|
||||
try {
|
||||
const resolvedCids = new Set<string>();
|
||||
|
||||
for (const reference of publishResolvableQuoteReferences) {
|
||||
const resolvedTarget = await resolveExternalQuoteTarget({
|
||||
account,
|
||||
directories,
|
||||
onStatus: (status) => {
|
||||
setPublishReplyStateMessage(getExternalQuoteStatusMessage(t, status));
|
||||
},
|
||||
reference,
|
||||
});
|
||||
|
||||
if (!resolvedTarget?.cid) {
|
||||
setPublishReplyError(
|
||||
t('external_quote_publish_missing', {
|
||||
interpolation: { escapeValue: false },
|
||||
quote: reference.raw,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
resolvedCids.add(resolvedTarget.cid);
|
||||
}
|
||||
|
||||
setResolvedExternalQuotedCids(resolvedCids.size > 0 ? [...resolvedCids] : undefined);
|
||||
setPublishReplyStateMessage(null);
|
||||
setPendingPublishRequestId((requestId) => requestId + 1);
|
||||
} catch {
|
||||
setPublishReplyError(t('external_quote_resolution_unavailable'));
|
||||
} finally {
|
||||
setIsResolvingExternalQuotes(false);
|
||||
}
|
||||
},
|
||||
[account, blockedReason, directories, publishResolvableQuoteReferences, setPublishReplyOptions, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (pendingSyncedPublishRequestId === 0 || pendingSyncedPublishRequestId === startedSyncedPublishRequestIdRef.current) {
|
||||
return;
|
||||
}
|
||||
}, [account, blockedReason, directories, publishResolvableQuoteReferences, t]);
|
||||
|
||||
startedSyncedPublishRequestIdRef.current = pendingSyncedPublishRequestId;
|
||||
publishReply();
|
||||
}, [pendingSyncedPublishRequestId, publishReply]);
|
||||
|
||||
return {
|
||||
isResolvingExternalQuotes,
|
||||
|
||||
Reference in New Issue
Block a user