mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(reply modal): restore multiline quote insertion
This commit is contained in:
@@ -50,6 +50,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
const { selectedText } = useSelectedTextStore();
|
const { selectedText } = useSelectedTextStore();
|
||||||
const quoteInsertRequestId = useReplyModalStore((state) => state.quoteInsertRequestId);
|
const quoteInsertRequestId = useReplyModalStore((state) => state.quoteInsertRequestId);
|
||||||
const quoteInsertNumber = useReplyModalStore((state) => state.quoteInsertNumber);
|
const quoteInsertNumber = useReplyModalStore((state) => state.quoteInsertNumber);
|
||||||
|
const quoteInsertSelectedText = useReplyModalStore((state) => state.quoteInsertSelectedText);
|
||||||
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [lengthError, setLengthError] = useState<string | null>(null);
|
const [lengthError, setLengthError] = useState<string | null>(null);
|
||||||
@@ -239,16 +240,24 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
lastProcessedQuoteInsertRequestIdRef.current = quoteInsertRequestId;
|
lastProcessedQuoteInsertRequestIdRef.current = quoteInsertRequestId;
|
||||||
|
|
||||||
const quote = `>>${quoteInsertNumber ?? '?'}`;
|
const quote = `>>${quoteInsertNumber ?? '?'}`;
|
||||||
|
const selectedQuote = quoteInsertSelectedText?.trimEnd() || '';
|
||||||
const isFocused = document.activeElement === textarea;
|
const isFocused = document.activeElement === textarea;
|
||||||
const rawStart = isFocused ? (textarea.selectionStart ?? textarea.value.length) : lastSelectionStartRef.current;
|
const rawStart = isFocused ? (textarea.selectionStart ?? textarea.value.length) : lastSelectionStartRef.current;
|
||||||
const selectionEnd = isFocused ? (textarea.selectionEnd ?? rawStart) : lastSelectionEndRef.current;
|
const selectionEnd = isFocused ? (textarea.selectionEnd ?? rawStart) : lastSelectionEndRef.current;
|
||||||
const minStart = contentPrefix.length;
|
const minStart = contentPrefix.length;
|
||||||
const start = Math.max(rawStart, minStart);
|
const start = Math.max(rawStart, minStart);
|
||||||
const end = Math.max(selectionEnd, minStart);
|
const end = Math.max(selectionEnd, minStart);
|
||||||
const nextValue = `${textarea.value.slice(0, start)}${quote}${textarea.value.slice(end)}`;
|
const before = textarea.value.slice(0, start);
|
||||||
|
const after = textarea.value.slice(end);
|
||||||
|
const needsLeadingNewline = before.length > minStart && !before.endsWith('\n');
|
||||||
|
let insertion = `${needsLeadingNewline ? '\n' : ''}${quote}\n`;
|
||||||
|
if (selectedQuote) {
|
||||||
|
insertion += `${selectedQuote}\n`;
|
||||||
|
}
|
||||||
|
const nextValue = `${before}${insertion}${after}`;
|
||||||
|
|
||||||
textarea.value = nextValue;
|
textarea.value = nextValue;
|
||||||
const nextCursor = start + quote.length;
|
const nextCursor = before.length + insertion.length;
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
textarea.setSelectionRange(nextCursor, nextCursor);
|
textarea.setSelectionRange(nextCursor, nextCursor);
|
||||||
lastSelectionStartRef.current = nextCursor;
|
lastSelectionStartRef.current = nextCursor;
|
||||||
@@ -258,7 +267,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
|
|||||||
const formattedContent = formatMarkdown(contentWithoutPrefix);
|
const formattedContent = formatMarkdown(contentWithoutPrefix);
|
||||||
setPublishReplyOptions({ content: formattedContent });
|
setPublishReplyOptions({ content: formattedContent });
|
||||||
checkContentLength(formattedContent, t);
|
checkContentLength(formattedContent, t);
|
||||||
}, [showReplyModal, quoteInsertRequestId, quoteInsertNumber, contentPrefix, setPublishReplyOptions, checkContentLength, t]);
|
}, [showReplyModal, quoteInsertRequestId, quoteInsertNumber, quoteInsertSelectedText, contentPrefix, setPublishReplyOptions, checkContentLength, t]);
|
||||||
|
|
||||||
// on android, auto upload file to image hosting sites with open api
|
// on android, auto upload file to image hosting sites with open api
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
|
|||||||
@@ -11,10 +11,26 @@ interface ReplyModalState {
|
|||||||
scrollY: number;
|
scrollY: number;
|
||||||
quoteInsertRequestId: number;
|
quoteInsertRequestId: number;
|
||||||
quoteInsertNumber: number | null;
|
quoteInsertNumber: number | null;
|
||||||
|
quoteInsertSelectedText: string | null;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
openReplyModal: (parentCid: string, parentNumber: number | undefined, postCid: string, threadNumber: number | undefined, subplebbitAddress: string) => void;
|
openReplyModal: (parentCid: string, parentNumber: number | undefined, postCid: string, threadNumber: number | undefined, subplebbitAddress: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getQuotedSelection = () => {
|
||||||
|
const text = document.getSelection()?.toString();
|
||||||
|
if (!text) return '';
|
||||||
|
|
||||||
|
// Keep each selected line as 5chan greentext and normalize newlines.
|
||||||
|
const normalizedText = text.replace(/\r\n/g, '\n').replace(/\n+$/g, '');
|
||||||
|
|
||||||
|
if (!normalizedText) return '';
|
||||||
|
|
||||||
|
return normalizedText
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => `>${line}`)
|
||||||
|
.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
||||||
showReplyModal: false,
|
showReplyModal: false,
|
||||||
activeCid: null,
|
activeCid: null,
|
||||||
@@ -25,6 +41,7 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
|||||||
scrollY: 0,
|
scrollY: 0,
|
||||||
quoteInsertRequestId: 0,
|
quoteInsertRequestId: 0,
|
||||||
quoteInsertNumber: null,
|
quoteInsertNumber: null,
|
||||||
|
quoteInsertSelectedText: null,
|
||||||
|
|
||||||
closeModal: () => {
|
closeModal: () => {
|
||||||
// Reset selected text if you're using that store
|
// Reset selected text if you're using that store
|
||||||
@@ -35,23 +52,25 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
|||||||
parentNumber: null,
|
parentNumber: null,
|
||||||
threadNumber: null,
|
threadNumber: null,
|
||||||
quoteInsertNumber: null,
|
quoteInsertNumber: null,
|
||||||
|
quoteInsertSelectedText: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
openReplyModal: (parentCid, parentNumber, postCid, threadNumber, subplebbitAddress) => {
|
openReplyModal: (parentCid, parentNumber, postCid, threadNumber, subplebbitAddress) => {
|
||||||
|
const quotedSelection = getQuotedSelection();
|
||||||
|
|
||||||
// If the reply modal is already open, insert this quote in the current textarea at caret.
|
// If the reply modal is already open, insert this quote in the current textarea at caret.
|
||||||
if (get().showReplyModal) {
|
if (get().showReplyModal) {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
quoteInsertRequestId: state.quoteInsertRequestId + 1,
|
quoteInsertRequestId: state.quoteInsertRequestId + 1,
|
||||||
quoteInsertNumber: parentNumber ?? null,
|
quoteInsertNumber: parentNumber ?? null,
|
||||||
|
quoteInsertSelectedText: quotedSelection || null,
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get selected text
|
if (quotedSelection) {
|
||||||
const text = document.getSelection()?.toString();
|
useSelectedTextStore.getState().setSelectedText(`${quotedSelection}\n`);
|
||||||
if (text) {
|
|
||||||
useSelectedTextStore.getState().setSelectedText(`>${text}\n`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle mobile scrollY
|
// Handle mobile scrollY
|
||||||
|
|||||||
Reference in New Issue
Block a user