Use @indutny/sneequals for memoization
This commit is contained in:
parent
2ffea11bdb
commit
41d144bc98
6 changed files with 35 additions and 130 deletions
|
@ -33,6 +33,28 @@ Signal Desktop makes use of the following open source projects.
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
## @indutny/sneequals
|
||||
|
||||
Copyright Fedor Indutny, 2022.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
## @popperjs/core
|
||||
|
||||
License: MIT
|
||||
|
@ -1952,30 +1974,6 @@ Signal Desktop makes use of the following open source projects.
|
|||
|
||||
License: MIT
|
||||
|
||||
## proxy-compare
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020-2022 Daishi Kato
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
## qrcode-generator
|
||||
|
||||
License: MIT
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "1.2.6",
|
||||
"@indutny/frameless-titlebar": "2.3.5",
|
||||
"@indutny/sneequals": "1.0.1",
|
||||
"@popperjs/core": "2.11.6",
|
||||
"@react-spring/web": "9.5.5",
|
||||
"@signalapp/better-sqlite3": "8.0.4",
|
||||
|
@ -143,7 +144,6 @@
|
|||
"pino": "8.6.1",
|
||||
"protobufjs": "6.11.3",
|
||||
"proxy-agent": "5.0.0",
|
||||
"proxy-compare": "2.3.0",
|
||||
"qrcode-generator": "1.4.4",
|
||||
"quill": "1.3.7",
|
||||
"quill-delta": "4.0.1",
|
||||
|
|
|
@ -7,6 +7,7 @@ import filesize from 'filesize';
|
|||
import getDirection from 'direction';
|
||||
import emojiRegex from 'emoji-regex';
|
||||
import LinkifyIt from 'linkify-it';
|
||||
import { memoize } from '@indutny/sneequals';
|
||||
|
||||
import type { StateType } from '../reducer';
|
||||
import type {
|
||||
|
@ -56,7 +57,6 @@ import { isVoiceMessage, canBeDownloaded } from '../../types/Attachment';
|
|||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
|
||||
import type { CallingNotificationType } from '../../util/callingNotification';
|
||||
import { proxyMemoize } from '../../util/proxyMemoize';
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import { getRecipients } from '../../util/getRecipients';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
|
@ -270,7 +270,7 @@ export function getConversation(
|
|||
|
||||
// Message
|
||||
|
||||
export const getAttachmentsForMessage = proxyMemoize(
|
||||
export const getAttachmentsForMessage = memoize(
|
||||
({
|
||||
sticker,
|
||||
attachments = [],
|
||||
|
@ -299,13 +299,10 @@ export const getAttachmentsForMessage = proxyMemoize(
|
|||
.filter(attachment => !attachment.error || canBeDownloaded(attachment))
|
||||
.map(attachment => getPropsForAttachment(attachment))
|
||||
.filter(isNotNil);
|
||||
},
|
||||
{
|
||||
name: 'getAttachmentsForMessage',
|
||||
}
|
||||
);
|
||||
|
||||
export const processBodyRanges = proxyMemoize(
|
||||
export const processBodyRanges = memoize(
|
||||
(
|
||||
{ bodyRanges }: Pick<MessageWithUIFieldsType, 'bodyRanges'>,
|
||||
options: { conversationSelector: GetConversationByIdType }
|
||||
|
@ -327,9 +324,6 @@ export const processBodyRanges = proxyMemoize(
|
|||
};
|
||||
})
|
||||
.sort((a, b) => b.start - a.start);
|
||||
},
|
||||
{
|
||||
name: 'processBodyRanges',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -489,7 +483,7 @@ const getPropsForStoryReplyContext = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getPropsForQuote = proxyMemoize(
|
||||
export const getPropsForQuote = memoize(
|
||||
(
|
||||
message: Pick<
|
||||
MessageWithUIFieldsType,
|
||||
|
@ -555,9 +549,6 @@ export const getPropsForQuote = proxyMemoize(
|
|||
sentAt: Number(sentAt),
|
||||
text,
|
||||
};
|
||||
},
|
||||
{
|
||||
name: 'getPropsForQuote',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -642,7 +633,7 @@ function getTextDirection(body?: string): TextDirection {
|
|||
}
|
||||
}
|
||||
|
||||
export const getPropsForMessage = proxyMemoize(
|
||||
export const getPropsForMessage = memoize(
|
||||
(
|
||||
message: MessageWithUIFieldsType,
|
||||
options: GetPropsForMessageOptions
|
||||
|
@ -747,9 +738,6 @@ export const getPropsForMessage = proxyMemoize(
|
|||
textDirection: getTextDirection(message.body),
|
||||
timestamp: message.sent_at,
|
||||
};
|
||||
},
|
||||
{
|
||||
name: 'getPropsForMessage',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { memoize } from '@indutny/sneequals';
|
||||
import type { TimelineItemType } from '../../components/conversation/TimelineItem';
|
||||
import { proxyMemoize } from '../../util/proxyMemoize';
|
||||
|
||||
import type { StateType } from '../reducer';
|
||||
import type { MessageWithUIFieldsType } from '../ducks/conversations';
|
||||
|
@ -23,7 +23,7 @@ import {
|
|||
import { getActiveCall, getCallSelector } from './calling';
|
||||
import { getPropsForBubble } from './message';
|
||||
|
||||
const getTimelineItemInner = proxyMemoize(
|
||||
const getTimelineItemInner = memoize(
|
||||
(message: MessageWithUIFieldsType, state: StateType): TimelineItemType => {
|
||||
const selectedMessage = getSelectedMessage(state);
|
||||
const conversationSelector = getConversationSelector(state);
|
||||
|
@ -51,9 +51,6 @@ const getTimelineItemInner = proxyMemoize(
|
|||
activeCall,
|
||||
accountSelector,
|
||||
});
|
||||
},
|
||||
{
|
||||
name: 'getTimelineItemInner',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { createProxy, getUntracked, isChanged } from 'proxy-compare';
|
||||
|
||||
export type ExcludeNull<O> = Exclude<O, null>;
|
||||
|
||||
export type ProxyMemoizeOptions<Result> = Readonly<{
|
||||
// For debugging
|
||||
name: string;
|
||||
|
||||
equalityFn?: (prev: Result, next: Result) => boolean;
|
||||
}>;
|
||||
|
||||
export function proxyMemoize<Params extends ReadonlyArray<object>, Result>(
|
||||
fn: (...params: Params) => ExcludeNull<Result>,
|
||||
{ equalityFn }: ProxyMemoizeOptions<ExcludeNull<Result>>
|
||||
): (...param: Params) => ExcludeNull<Result> {
|
||||
type CacheEntryType = Readonly<{
|
||||
params: Params;
|
||||
result: ExcludeNull<Result>;
|
||||
}>;
|
||||
|
||||
const cache = new WeakMap<object, CacheEntryType>();
|
||||
const affected = new WeakMap<object, unknown>();
|
||||
const proxyCache = new WeakMap<object, unknown>();
|
||||
const changedCache = new WeakMap<object, unknown>();
|
||||
|
||||
return (...params: Params): ExcludeNull<Result> => {
|
||||
if (params.length < 1) {
|
||||
throw new Error('At least one parameter is required');
|
||||
}
|
||||
|
||||
const cacheKey = params[0];
|
||||
|
||||
const entry = cache.get(cacheKey);
|
||||
|
||||
if (entry && entry.params.length === params.length) {
|
||||
let isValid = true;
|
||||
for (const [i, cachedParam] of entry.params.entries()) {
|
||||
// Proxy wasn't even touched - we are good to go.
|
||||
const wasUsed = affected.has(cachedParam);
|
||||
if (!wasUsed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isChanged(cachedParam, params[i], affected, changedCache)) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid) {
|
||||
return entry.result;
|
||||
}
|
||||
}
|
||||
|
||||
const proxies = params.map(param =>
|
||||
createProxy(param, affected, proxyCache)
|
||||
) as unknown as Params;
|
||||
|
||||
const trackedResult = fn(...proxies);
|
||||
const untrackedResult = getUntracked(trackedResult);
|
||||
|
||||
// eslint-disable-next-line eqeqeq
|
||||
let result = untrackedResult === null ? trackedResult : untrackedResult;
|
||||
|
||||
// Try to reuse result if custom equality check is configured.
|
||||
if (entry && equalityFn && equalityFn(entry.result, result)) {
|
||||
({ result } = entry);
|
||||
}
|
||||
|
||||
cache.set(cacheKey, {
|
||||
params,
|
||||
result,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
10
yarn.lock
10
yarn.lock
|
@ -1607,6 +1607,11 @@
|
|||
classnames "^2.2.6"
|
||||
deepmerge "^4.2.2"
|
||||
|
||||
"@indutny/sneequals@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@indutny/sneequals/-/sneequals-1.0.1.tgz#9e0fa9ab68038f640105b837ae8f9ee453ba0fac"
|
||||
integrity sha512-UBWmN5kI4VDvV8/ratSqUGodYWiEhu2KlJqIqB5ga43CRhNXlrzhFg9ejM3PeoZaB9PP5hf8s2ZOOagAefYNhw==
|
||||
|
||||
"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
|
||||
|
@ -14369,11 +14374,6 @@ proxy-agent@5.0.0:
|
|||
proxy-from-env "^1.0.0"
|
||||
socks-proxy-agent "^5.0.0"
|
||||
|
||||
proxy-compare@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.3.0.tgz#ac9633ae52918ff9c9fcc54dfe6316c7a02d20ee"
|
||||
integrity sha512-c3L2CcAi7f7pvlD0D7xsF+2CQIW8C3HaYx2Pfgq8eA4HAl3GAH6/dVYsyBbYF/0XJs2ziGLrzmz5fmzPm6A0pQ==
|
||||
|
||||
proxy-from-env@^1.0.0, proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
|
|
Loading…
Reference in a new issue