mirror of
https://github.com/danog/telegram-tt.git
synced 2024-11-27 12:55:11 +01:00
[Perf] Teact: Optimize updating attributes and support dangerouslySetInnerHTML
This commit is contained in:
parent
c2d76a9990
commit
528137daff
@ -3,7 +3,6 @@ import React, {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
@ -168,9 +167,9 @@ type DispatchProps = Pick<GlobalActions, 'toggleMessageSelection' | 'clickInline
|
||||
const NBSP = '\u00A0';
|
||||
const GROUP_MESSAGE_HOVER_ATTRIBUTE = 'data-is-document-group-hover';
|
||||
// eslint-disable-next-line max-len
|
||||
const APPENDIX_OWN = '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#a)"/><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#EEFFDE" class="corner"/></g></svg>';
|
||||
const APPENDIX_OWN = { __html: '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#a)"/><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#EEFFDE" class="corner"/></g></svg>' };
|
||||
// eslint-disable-next-line max-len
|
||||
const APPENDIX_NOT_OWN = '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#000" filter="url(#a)"/><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#FFF" class="corner"/></g></svg>';
|
||||
const APPENDIX_NOT_OWN = { __html: '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#000" filter="url(#a)"/><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#FFF" class="corner"/></g></svg>' };
|
||||
const APPEARANCE_DELAY = 10;
|
||||
const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
||||
|
||||
@ -233,8 +232,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const bottomMarkerRef = useRef<HTMLDivElement>(null);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const appendixRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
|
||||
@ -405,13 +403,6 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
message.id,
|
||||
);
|
||||
useFocusMessage(ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer);
|
||||
useLayoutEffect(() => {
|
||||
if (!appendixRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
appendixRef.current.innerHTML = isOwn ? APPENDIX_OWN : APPENDIX_NOT_OWN;
|
||||
}, [isOwn, withAppendix]);
|
||||
|
||||
let style = '';
|
||||
let calculatedWidth;
|
||||
@ -766,7 +757,9 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
</Button>
|
||||
) : undefined}
|
||||
{withCommentButton && <CommentButton message={message} disabled={noComments} />}
|
||||
{withAppendix && <div className="svg-appendix" ref={appendixRef} />}
|
||||
{withAppendix && (
|
||||
<div className="svg-appendix" dangerouslySetInnerHTML={isOwn ? APPENDIX_OWN : APPENDIX_NOT_OWN} />
|
||||
)}
|
||||
</div>
|
||||
{message.inlineButtons && (
|
||||
<InlineButtons message={message} onClick={clickInlineButton} />
|
||||
|
@ -223,7 +223,9 @@ function createNode($element: VirtualElement): Node {
|
||||
}
|
||||
|
||||
Object.keys(props).forEach((key) => {
|
||||
addAttribute(element, key, props[key]);
|
||||
if (props[key] !== undefined) {
|
||||
setAttribute(element, key, props[key]);
|
||||
}
|
||||
});
|
||||
|
||||
$element.children = children.map(($child, i) => (
|
||||
@ -395,31 +397,31 @@ function updateAttributes($current: VirtualRealElement, $new: VirtualRealElement
|
||||
const newKeys = Object.keys($new.props);
|
||||
|
||||
currentKeys.forEach((key) => {
|
||||
if ($current.props[key] !== undefined && $new.props[key] === undefined) {
|
||||
removeAttribute(element, key, $current.props[key]);
|
||||
const currentValue = $current.props[key];
|
||||
const newValue = $new.props[key];
|
||||
|
||||
if (
|
||||
currentValue !== undefined
|
||||
&& (
|
||||
newValue === undefined
|
||||
|| (currentValue !== newValue && key.startsWith('on'))
|
||||
)
|
||||
) {
|
||||
removeAttribute(element, key, currentValue);
|
||||
}
|
||||
});
|
||||
|
||||
newKeys.forEach((key) => {
|
||||
if ($new.props[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
const currentValue = $current.props[key];
|
||||
const newValue = $new.props[key];
|
||||
|
||||
if ($current.props[key] !== $new.props[key]) {
|
||||
if ($current.props[key] === undefined) {
|
||||
addAttribute(element, key, $new.props[key]);
|
||||
} else {
|
||||
updateAttribute(element, key, $current.props[key], $new.props[key]);
|
||||
}
|
||||
if (newValue !== undefined && newValue !== currentValue) {
|
||||
setAttribute(element, key, newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addAttribute(element: HTMLElement, key: string, value: any) {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
function setAttribute(element: HTMLElement, key: string, value: any) {
|
||||
// An optimization attempt
|
||||
if (key === 'className') {
|
||||
element.className = value;
|
||||
@ -428,6 +430,9 @@ function addAttribute(element: HTMLElement, key: string, value: any) {
|
||||
(element as HTMLInputElement).value = value;
|
||||
} else if (key === 'style') {
|
||||
element.style.cssText = value;
|
||||
} else if (key === 'dangerouslySetInnerHTML') {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
element.innerHTML = value.__html;
|
||||
} else if (key.startsWith('on')) {
|
||||
addEventListener(element, key, value, key.endsWith('Capture'));
|
||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||
@ -444,6 +449,8 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
||||
(element as HTMLInputElement).value = '';
|
||||
} else if (key === 'style') {
|
||||
element.style.cssText = '';
|
||||
} else if (key === 'dangerouslySetInnerHTML') {
|
||||
element.innerHTML = '';
|
||||
} else if (key.startsWith('on')) {
|
||||
removeEventListener(element, key, value, key.endsWith('Capture'));
|
||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||
@ -453,16 +460,6 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateAttribute(element: HTMLElement, key: string, oldValue: any, newValue: any) {
|
||||
if (key === 'value') {
|
||||
// Removing and adding value causes a cursor jump
|
||||
(element as HTMLInputElement).value = newValue !== undefined ? newValue : '';
|
||||
} else {
|
||||
removeAttribute(element, key, oldValue);
|
||||
addAttribute(element, key, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
function DEBUG_addToVirtualTreeSize($current: VirtualRealElement | VirtualDomHead) {
|
||||
DEBUG_virtualTreeSize += $current.children.length;
|
||||
|
Loading…
Reference in New Issue
Block a user