Message: Quote highlighting (#3994)
This commit is contained in:
parent
b09f9bd255
commit
e77506cc54
@ -28,6 +28,7 @@ interface OwnProps {
|
|||||||
shouldRenderAsHtml?: boolean;
|
shouldRenderAsHtml?: boolean;
|
||||||
inChatList?: boolean;
|
inChatList?: boolean;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
|
focusedQuote?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIN_CUSTOM_EMOJIS_FOR_SHARED_CANVAS = 3;
|
const MIN_CUSTOM_EMOJIS_FOR_SHARED_CANVAS = 3;
|
||||||
@ -47,6 +48,7 @@ function MessageText({
|
|||||||
shouldRenderAsHtml,
|
shouldRenderAsHtml,
|
||||||
inChatList,
|
inChatList,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
focusedQuote,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
|
const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
@ -101,6 +103,7 @@ function MessageText({
|
|||||||
sharedCanvasHqRef,
|
sharedCanvasHqRef,
|
||||||
cacheBuster: textCacheBusterRef.current.toString(),
|
cacheBuster: textCacheBusterRef.current.toString(),
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
focusedQuote,
|
||||||
}),
|
}),
|
||||||
].flat().filter(Boolean)}
|
].flat().filter(Boolean)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import SafeLink from '../SafeLink';
|
|||||||
|
|
||||||
export type TextFilter = (
|
export type TextFilter = (
|
||||||
'escape_html' | 'hq_emoji' | 'emoji' | 'emoji_html' | 'br' | 'br_html' | 'highlight' | 'links' |
|
'escape_html' | 'hq_emoji' | 'emoji' | 'emoji_html' | 'br' | 'br_html' | 'highlight' | 'links' |
|
||||||
'simple_markdown' | 'simple_markdown_html'
|
'simple_markdown' | 'simple_markdown_html' | 'quote'
|
||||||
);
|
);
|
||||||
|
|
||||||
const SIMPLE_MARKDOWN_REGEX = /(\*\*|__).+?\1/g;
|
const SIMPLE_MARKDOWN_REGEX = /(\*\*|__).+?\1/g;
|
||||||
@ -30,7 +30,7 @@ const SIMPLE_MARKDOWN_REGEX = /(\*\*|__).+?\1/g;
|
|||||||
export default function renderText(
|
export default function renderText(
|
||||||
part: TextPart,
|
part: TextPart,
|
||||||
filters: Array<TextFilter> = ['emoji'],
|
filters: Array<TextFilter> = ['emoji'],
|
||||||
params?: { highlight: string | undefined },
|
params?: { highlight?: string; quote?: string },
|
||||||
): TeactNode[] {
|
): TeactNode[] {
|
||||||
if (typeof part !== 'string') {
|
if (typeof part !== 'string') {
|
||||||
return [part];
|
return [part];
|
||||||
@ -62,6 +62,9 @@ export default function renderText(
|
|||||||
case 'highlight':
|
case 'highlight':
|
||||||
return addHighlight(text, params!.highlight);
|
return addHighlight(text, params!.highlight);
|
||||||
|
|
||||||
|
case 'quote':
|
||||||
|
return addHighlight(text, params!.quote, true);
|
||||||
|
|
||||||
case 'links':
|
case 'links':
|
||||||
return addLinks(text);
|
return addLinks(text);
|
||||||
|
|
||||||
@ -183,7 +186,7 @@ function addLineBreaks(textParts: TextPart[], type: 'jsx' | 'html'): TextPart[]
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addHighlight(textParts: TextPart[], highlight: string | undefined): TextPart[] {
|
function addHighlight(textParts: TextPart[], highlight: string | undefined, isQuote?: true): TextPart[] {
|
||||||
return textParts.reduce<TextPart[]>((result, part) => {
|
return textParts.reduce<TextPart[]>((result, part) => {
|
||||||
if (typeof part !== 'string' || !highlight) {
|
if (typeof part !== 'string' || !highlight) {
|
||||||
result.push(part);
|
result.push(part);
|
||||||
@ -200,7 +203,7 @@ function addHighlight(textParts: TextPart[], highlight: string | undefined): Tex
|
|||||||
const newParts: TextPart[] = [];
|
const newParts: TextPart[] = [];
|
||||||
newParts.push(part.substring(0, queryPosition));
|
newParts.push(part.substring(0, queryPosition));
|
||||||
newParts.push(
|
newParts.push(
|
||||||
<span className="matching-text-highlight">
|
<span className={buildClassName('matching-text-highlight', isQuote && 'is-quote')}>
|
||||||
{part.substring(queryPosition, queryPosition + highlight.length)}
|
{part.substring(queryPosition, queryPosition + highlight.length)}
|
||||||
</span>,
|
</span>,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,6 +24,7 @@ interface IOrganizedEntity {
|
|||||||
organizedIndexes: Set<number>;
|
organizedIndexes: Set<number>;
|
||||||
nestedEntities: IOrganizedEntity[];
|
nestedEntities: IOrganizedEntity[];
|
||||||
}
|
}
|
||||||
|
type RenderTextParams = Parameters<typeof renderText>[2];
|
||||||
|
|
||||||
const HQ_EMOJI_THRESHOLD = 64;
|
const HQ_EMOJI_THRESHOLD = 64;
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ export function renderTextWithEntities({
|
|||||||
sharedCanvasHqRef,
|
sharedCanvasHqRef,
|
||||||
cacheBuster,
|
cacheBuster,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
focusedQuote,
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
entities?: ApiMessageEntity[];
|
entities?: ApiMessageEntity[];
|
||||||
@ -59,9 +61,10 @@ export function renderTextWithEntities({
|
|||||||
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>;
|
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>;
|
||||||
cacheBuster?: string;
|
cacheBuster?: string;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
|
focusedQuote?: string;
|
||||||
}) {
|
}) {
|
||||||
if (!entities?.length) {
|
if (!entities?.length) {
|
||||||
return renderMessagePart(text, highlight, emojiSize, shouldRenderAsHtml, isSimple);
|
return renderMessagePart(text, highlight, focusedQuote, emojiSize, shouldRenderAsHtml, isSimple);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: TextPart[] = [];
|
const result: TextPart[] = [];
|
||||||
@ -90,7 +93,7 @@ export function renderTextWithEntities({
|
|||||||
}
|
}
|
||||||
if (textBefore) {
|
if (textBefore) {
|
||||||
renderResult.push(...renderMessagePart(
|
renderResult.push(...renderMessagePart(
|
||||||
textBefore, highlight, emojiSize, shouldRenderAsHtml, isSimple,
|
textBefore, highlight, focusedQuote, emojiSize, shouldRenderAsHtml, isSimple,
|
||||||
) as TextPart[]);
|
) as TextPart[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +169,7 @@ export function renderTextWithEntities({
|
|||||||
}
|
}
|
||||||
if (textAfter) {
|
if (textAfter) {
|
||||||
renderResult.push(...renderMessagePart(
|
renderResult.push(...renderMessagePart(
|
||||||
textAfter, highlight, emojiSize, shouldRenderAsHtml, isSimple,
|
textAfter, highlight, focusedQuote, emojiSize, shouldRenderAsHtml, isSimple,
|
||||||
) as TextPart[]);
|
) as TextPart[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,6 +220,7 @@ export function getTextWithEntitiesAsHtml(formattedText?: ApiFormattedText) {
|
|||||||
function renderMessagePart(
|
function renderMessagePart(
|
||||||
content: TextPart | TextPart[],
|
content: TextPart | TextPart[],
|
||||||
highlight?: string,
|
highlight?: string,
|
||||||
|
focusedQuote?: string,
|
||||||
emojiSize?: number,
|
emojiSize?: number,
|
||||||
shouldRenderAsHtml?: boolean,
|
shouldRenderAsHtml?: boolean,
|
||||||
isSimple?: boolean,
|
isSimple?: boolean,
|
||||||
@ -225,7 +229,7 @@ function renderMessagePart(
|
|||||||
const result: TextPart[] = [];
|
const result: TextPart[] = [];
|
||||||
|
|
||||||
content.forEach((c) => {
|
content.forEach((c) => {
|
||||||
result.push(...renderMessagePart(c, highlight, emojiSize, shouldRenderAsHtml, isSimple));
|
result.push(...renderMessagePart(c, highlight, focusedQuote, emojiSize, shouldRenderAsHtml, isSimple));
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -238,15 +242,21 @@ function renderMessagePart(
|
|||||||
const emojiFilter = emojiSize && emojiSize > HQ_EMOJI_THRESHOLD ? 'hq_emoji' : 'emoji';
|
const emojiFilter = emojiSize && emojiSize > HQ_EMOJI_THRESHOLD ? 'hq_emoji' : 'emoji';
|
||||||
|
|
||||||
const filters: TextFilter[] = [emojiFilter];
|
const filters: TextFilter[] = [emojiFilter];
|
||||||
|
const params: RenderTextParams = {};
|
||||||
if (!isSimple) {
|
if (!isSimple) {
|
||||||
filters.push('br');
|
filters.push('br');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (highlight) {
|
if (highlight) {
|
||||||
return renderText(content, filters.concat('highlight'), { highlight });
|
filters.push('highlight');
|
||||||
} else {
|
params.highlight = highlight;
|
||||||
return renderText(content, filters);
|
|
||||||
}
|
}
|
||||||
|
if (focusedQuote) {
|
||||||
|
filters.push('quote');
|
||||||
|
params.quote = focusedQuote;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderText(content, filters, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organize entities in a tree-like structure to better represent how the text will be displayed
|
// Organize entities in a tree-like structure to better represent how the text will be displayed
|
||||||
|
|||||||
@ -225,6 +225,7 @@ type StateProps = {
|
|||||||
isChatProtected?: boolean;
|
isChatProtected?: boolean;
|
||||||
isFocused?: boolean;
|
isFocused?: boolean;
|
||||||
focusDirection?: FocusDirection;
|
focusDirection?: FocusDirection;
|
||||||
|
focusedQuote?: string;
|
||||||
noFocusHighlight?: boolean;
|
noFocusHighlight?: boolean;
|
||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
isForwarding?: boolean;
|
isForwarding?: boolean;
|
||||||
@ -337,6 +338,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
isChatProtected,
|
isChatProtected,
|
||||||
isFocused,
|
isFocused,
|
||||||
focusDirection,
|
focusDirection,
|
||||||
|
focusedQuote,
|
||||||
noFocusHighlight,
|
noFocusHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
isForwarding,
|
isForwarding,
|
||||||
@ -727,7 +729,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useFocusMessage(
|
useFocusMessage(
|
||||||
ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isJustAdded,
|
ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isJustAdded, Boolean(focusedQuote),
|
||||||
);
|
);
|
||||||
|
|
||||||
const signature = (isChannel && message.postAuthorTitle)
|
const signature = (isChannel && message.postAuthorTitle)
|
||||||
@ -868,6 +870,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
messageOrStory={message}
|
messageOrStory={message}
|
||||||
translatedText={requestedTranslationLanguage ? currentTranslatedText : undefined}
|
translatedText={requestedTranslationLanguage ? currentTranslatedText : undefined}
|
||||||
isForAnimation={isForAnimation}
|
isForAnimation={isForAnimation}
|
||||||
|
focusedQuote={focusedQuote}
|
||||||
emojiSize={emojiSize}
|
emojiSize={emojiSize}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
isProtected={isProtected}
|
isProtected={isProtected}
|
||||||
@ -1488,7 +1491,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer,
|
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer, quote: focusedQuote,
|
||||||
} = (isFocused && focusedMessage) || {};
|
} = (isFocused && focusedMessage) || {};
|
||||||
|
|
||||||
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
||||||
@ -1610,6 +1613,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
focusDirection,
|
focusDirection,
|
||||||
noFocusHighlight,
|
noFocusHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
|
focusedQuote,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -237,7 +237,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.matching-text-highlight {
|
.matching-text-highlight:not(.is-quote) {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
background: #cae3f7;
|
background: #cae3f7;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
@ -248,6 +248,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.matching-text-highlight.is-quote {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
&.animate {
|
||||||
|
color: var(--color-text);
|
||||||
|
animation: quote-highlight 0.5s;
|
||||||
|
animation-delay: 1.5s;
|
||||||
|
background-color: #cae3f7;
|
||||||
|
|
||||||
|
.theme-dark & {
|
||||||
|
animation-name: quote-highlight-dark;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.message-title {
|
.message-title {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -990,3 +1007,25 @@ blockquote, .blockquote {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes quote-highlight {
|
||||||
|
0% {
|
||||||
|
background-color: #cae3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes quote-highlight-dark {
|
||||||
|
0% {
|
||||||
|
background-color: #cae3f7;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import { useLayoutEffect, useRef } from '../../../../lib/teact/teact';
|
import { useLayoutEffect, useRef } from '../../../../lib/teact/teact';
|
||||||
|
import { addExtraClass } from '../../../../lib/teact/teact-dom';
|
||||||
|
|
||||||
import type { FocusDirection } from '../../../../types';
|
import type { FocusDirection } from '../../../../types';
|
||||||
|
|
||||||
import { requestForcedReflow, requestMeasure, requestMutation } from '../../../../lib/fasterdom/fasterdom';
|
import {
|
||||||
|
requestForcedReflow, requestMeasure, requestMutation,
|
||||||
|
} from '../../../../lib/fasterdom/fasterdom';
|
||||||
import animateScroll from '../../../../util/animateScroll';
|
import animateScroll from '../../../../util/animateScroll';
|
||||||
|
|
||||||
// This is used when the viewport was replaced.
|
// This is used when the viewport was replaced.
|
||||||
@ -18,6 +21,7 @@ export default function useFocusMessage(
|
|||||||
noFocusHighlight?: boolean,
|
noFocusHighlight?: boolean,
|
||||||
isResizingContainer?: boolean,
|
isResizingContainer?: boolean,
|
||||||
isJustAdded?: boolean,
|
isJustAdded?: boolean,
|
||||||
|
isQuote?: boolean,
|
||||||
) {
|
) {
|
||||||
const isRelocatedRef = useRef(!isJustAdded);
|
const isRelocatedRef = useRef(!isJustAdded);
|
||||||
|
|
||||||
@ -30,17 +34,30 @@ export default function useFocusMessage(
|
|||||||
// `noFocusHighlight` is always called with “scroll-to-bottom” buttons
|
// `noFocusHighlight` is always called with “scroll-to-bottom” buttons
|
||||||
const isToBottom = noFocusHighlight;
|
const isToBottom = noFocusHighlight;
|
||||||
|
|
||||||
const exec = () => animateScroll(
|
const exec = () => {
|
||||||
messagesContainer,
|
const result = animateScroll(
|
||||||
elementRef.current!,
|
messagesContainer,
|
||||||
isToBottom ? 'end' : 'centerOrTop',
|
elementRef.current!,
|
||||||
FOCUS_MARGIN,
|
isToBottom ? 'end' : 'centerOrTop',
|
||||||
focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined,
|
FOCUS_MARGIN,
|
||||||
focusDirection,
|
focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined,
|
||||||
undefined,
|
focusDirection,
|
||||||
isResizingContainer,
|
undefined,
|
||||||
true,
|
isResizingContainer,
|
||||||
);
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isQuote) {
|
||||||
|
const firstQuote = elementRef.current!.querySelector<HTMLSpanElement>('.is-quote');
|
||||||
|
if (firstQuote) {
|
||||||
|
requestMutation(() => {
|
||||||
|
addExtraClass(firstQuote, 'animate');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
if (isRelocated) {
|
if (isRelocated) {
|
||||||
// We need this to override scroll setting from Message List layout effect
|
// We need this to override scroll setting from Message List layout effect
|
||||||
@ -52,6 +69,6 @@ export default function useFocusMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer,
|
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isQuote,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export default function useInnerHandlers(
|
|||||||
} = message;
|
} = message;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
replyToMsgId, replyToPeerId, replyToTopId, isQuote,
|
replyToMsgId, replyToPeerId, replyToTopId, isQuote, quoteText,
|
||||||
} = getMessageReplyInfo(message) || {};
|
} = getMessageReplyInfo(message) || {};
|
||||||
|
|
||||||
const handleAvatarClick = useLastCallback(() => {
|
const handleAvatarClick = useLastCallback(() => {
|
||||||
@ -90,6 +90,7 @@ export default function useInnerHandlers(
|
|||||||
messageId: replyToMsgId,
|
messageId: replyToMsgId,
|
||||||
replyMessageId: replyToPeerId ? undefined : messageId,
|
replyMessageId: replyToPeerId ? undefined : messageId,
|
||||||
noForumTopicPanel: !replyToPeerId, // Open topic panel for cross-chat replies
|
noForumTopicPanel: !replyToPeerId, // Open topic panel for cross-chat replies
|
||||||
|
...(isQuote && { quote: quoteText?.text }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -386,7 +386,7 @@ addActionHandler('focusNextReply', (global, actions, payload): ActionReturnType
|
|||||||
addActionHandler('focusMessage', (global, actions, payload): ActionReturnType => {
|
addActionHandler('focusMessage', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
|
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
|
||||||
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel,
|
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel, quote,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@ -412,12 +412,20 @@ addActionHandler('focusMessage', (global, actions, payload): ActionReturnType =>
|
|||||||
}
|
}
|
||||||
blurTimeout = window.setTimeout(() => {
|
blurTimeout = window.setTimeout(() => {
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
global = updateFocusedMessage(global, undefined, undefined, undefined, undefined, undefined, tabId);
|
global = updateFocusedMessage({ global }, tabId);
|
||||||
global = updateFocusDirection(global, undefined, tabId);
|
global = updateFocusDirection(global, undefined, tabId);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
}, noHighlight ? FOCUS_NO_HIGHLIGHT_DURATION : FOCUS_DURATION);
|
}, noHighlight ? FOCUS_NO_HIGHLIGHT_DURATION : FOCUS_DURATION);
|
||||||
|
|
||||||
global = updateFocusedMessage(global, chatId, messageId, threadId, noHighlight, isResizingContainer, tabId);
|
global = updateFocusedMessage({
|
||||||
|
global,
|
||||||
|
chatId,
|
||||||
|
messageId,
|
||||||
|
threadId,
|
||||||
|
noHighlight,
|
||||||
|
isResizingContainer,
|
||||||
|
quote,
|
||||||
|
}, tabId);
|
||||||
global = updateFocusDirection(global, undefined, tabId);
|
global = updateFocusDirection(global, undefined, tabId);
|
||||||
|
|
||||||
if (replyMessageId) {
|
if (replyMessageId) {
|
||||||
|
|||||||
@ -527,11 +527,24 @@ function updateScheduledMessages<T extends GlobalState>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateFocusedMessage<T extends GlobalState>(
|
export function updateFocusedMessage<T extends GlobalState>({
|
||||||
global: T, chatId?: string, messageId?: number, threadId = MAIN_THREAD_ID, noHighlight = false,
|
global,
|
||||||
|
chatId,
|
||||||
|
messageId,
|
||||||
|
threadId = MAIN_THREAD_ID,
|
||||||
|
noHighlight = false,
|
||||||
isResizingContainer = false,
|
isResizingContainer = false,
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
quote,
|
||||||
): T {
|
}: {
|
||||||
|
global: T;
|
||||||
|
chatId?: string;
|
||||||
|
messageId?: number;
|
||||||
|
threadId?: number;
|
||||||
|
noHighlight?: boolean;
|
||||||
|
isResizingContainer?: boolean;
|
||||||
|
quote?: string;
|
||||||
|
},
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>): T {
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
focusedMessage: {
|
focusedMessage: {
|
||||||
...selectTabState(global, tabId).focusedMessage,
|
...selectTabState(global, tabId).focusedMessage,
|
||||||
@ -540,6 +553,7 @@ export function updateFocusedMessage<T extends GlobalState>(
|
|||||||
messageId,
|
messageId,
|
||||||
noHighlight,
|
noHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
|
quote,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -254,6 +254,7 @@ export type TabState = {
|
|||||||
direction?: FocusDirection;
|
direction?: FocusDirection;
|
||||||
noHighlight?: boolean;
|
noHighlight?: boolean;
|
||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
|
quote?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
selectedMessages?: {
|
selectedMessages?: {
|
||||||
@ -1654,6 +1655,7 @@ export interface ActionPayloads {
|
|||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
shouldReplaceHistory?: boolean;
|
shouldReplaceHistory?: boolean;
|
||||||
noForumTopicPanel?: boolean;
|
noForumTopicPanel?: boolean;
|
||||||
|
quote?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
|
||||||
focusLastMessage: WithTabId | undefined;
|
focusLastMessage: WithTabId | undefined;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user