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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
## @popperjs/core
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
|
@ -1952,30 +1974,6 @@ Signal Desktop makes use of the following open source projects.
|
||||||
|
|
||||||
License: MIT
|
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
|
## qrcode-generator
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/fast-memoize": "1.2.6",
|
"@formatjs/fast-memoize": "1.2.6",
|
||||||
"@indutny/frameless-titlebar": "2.3.5",
|
"@indutny/frameless-titlebar": "2.3.5",
|
||||||
|
"@indutny/sneequals": "1.0.1",
|
||||||
"@popperjs/core": "2.11.6",
|
"@popperjs/core": "2.11.6",
|
||||||
"@react-spring/web": "9.5.5",
|
"@react-spring/web": "9.5.5",
|
||||||
"@signalapp/better-sqlite3": "8.0.4",
|
"@signalapp/better-sqlite3": "8.0.4",
|
||||||
|
@ -143,7 +144,6 @@
|
||||||
"pino": "8.6.1",
|
"pino": "8.6.1",
|
||||||
"protobufjs": "6.11.3",
|
"protobufjs": "6.11.3",
|
||||||
"proxy-agent": "5.0.0",
|
"proxy-agent": "5.0.0",
|
||||||
"proxy-compare": "2.3.0",
|
|
||||||
"qrcode-generator": "1.4.4",
|
"qrcode-generator": "1.4.4",
|
||||||
"quill": "1.3.7",
|
"quill": "1.3.7",
|
||||||
"quill-delta": "4.0.1",
|
"quill-delta": "4.0.1",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import filesize from 'filesize';
|
||||||
import getDirection from 'direction';
|
import getDirection from 'direction';
|
||||||
import emojiRegex from 'emoji-regex';
|
import emojiRegex from 'emoji-regex';
|
||||||
import LinkifyIt from 'linkify-it';
|
import LinkifyIt from 'linkify-it';
|
||||||
|
import { memoize } from '@indutny/sneequals';
|
||||||
|
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import type {
|
import type {
|
||||||
|
@ -56,7 +57,6 @@ import { isVoiceMessage, canBeDownloaded } from '../../types/Attachment';
|
||||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||||
|
|
||||||
import type { CallingNotificationType } from '../../util/callingNotification';
|
import type { CallingNotificationType } from '../../util/callingNotification';
|
||||||
import { proxyMemoize } from '../../util/proxyMemoize';
|
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { getRecipients } from '../../util/getRecipients';
|
import { getRecipients } from '../../util/getRecipients';
|
||||||
import { getOwn } from '../../util/getOwn';
|
import { getOwn } from '../../util/getOwn';
|
||||||
|
@ -270,7 +270,7 @@ export function getConversation(
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
|
|
||||||
export const getAttachmentsForMessage = proxyMemoize(
|
export const getAttachmentsForMessage = memoize(
|
||||||
({
|
({
|
||||||
sticker,
|
sticker,
|
||||||
attachments = [],
|
attachments = [],
|
||||||
|
@ -299,13 +299,10 @@ export const getAttachmentsForMessage = proxyMemoize(
|
||||||
.filter(attachment => !attachment.error || canBeDownloaded(attachment))
|
.filter(attachment => !attachment.error || canBeDownloaded(attachment))
|
||||||
.map(attachment => getPropsForAttachment(attachment))
|
.map(attachment => getPropsForAttachment(attachment))
|
||||||
.filter(isNotNil);
|
.filter(isNotNil);
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'getAttachmentsForMessage',
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const processBodyRanges = proxyMemoize(
|
export const processBodyRanges = memoize(
|
||||||
(
|
(
|
||||||
{ bodyRanges }: Pick<MessageWithUIFieldsType, 'bodyRanges'>,
|
{ bodyRanges }: Pick<MessageWithUIFieldsType, 'bodyRanges'>,
|
||||||
options: { conversationSelector: GetConversationByIdType }
|
options: { conversationSelector: GetConversationByIdType }
|
||||||
|
@ -327,9 +324,6 @@ export const processBodyRanges = proxyMemoize(
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => b.start - a.start);
|
.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<
|
message: Pick<
|
||||||
MessageWithUIFieldsType,
|
MessageWithUIFieldsType,
|
||||||
|
@ -555,9 +549,6 @@ export const getPropsForQuote = proxyMemoize(
|
||||||
sentAt: Number(sentAt),
|
sentAt: Number(sentAt),
|
||||||
text,
|
text,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'getPropsForQuote',
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -642,7 +633,7 @@ function getTextDirection(body?: string): TextDirection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPropsForMessage = proxyMemoize(
|
export const getPropsForMessage = memoize(
|
||||||
(
|
(
|
||||||
message: MessageWithUIFieldsType,
|
message: MessageWithUIFieldsType,
|
||||||
options: GetPropsForMessageOptions
|
options: GetPropsForMessageOptions
|
||||||
|
@ -747,9 +738,6 @@ export const getPropsForMessage = proxyMemoize(
|
||||||
textDirection: getTextDirection(message.body),
|
textDirection: getTextDirection(message.body),
|
||||||
timestamp: message.sent_at,
|
timestamp: message.sent_at,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'getPropsForMessage',
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2021-2022 Signal Messenger, LLC
|
// Copyright 2021-2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { memoize } from '@indutny/sneequals';
|
||||||
import type { TimelineItemType } from '../../components/conversation/TimelineItem';
|
import type { TimelineItemType } from '../../components/conversation/TimelineItem';
|
||||||
import { proxyMemoize } from '../../util/proxyMemoize';
|
|
||||||
|
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import type { MessageWithUIFieldsType } from '../ducks/conversations';
|
import type { MessageWithUIFieldsType } from '../ducks/conversations';
|
||||||
|
@ -23,7 +23,7 @@ import {
|
||||||
import { getActiveCall, getCallSelector } from './calling';
|
import { getActiveCall, getCallSelector } from './calling';
|
||||||
import { getPropsForBubble } from './message';
|
import { getPropsForBubble } from './message';
|
||||||
|
|
||||||
const getTimelineItemInner = proxyMemoize(
|
const getTimelineItemInner = memoize(
|
||||||
(message: MessageWithUIFieldsType, state: StateType): TimelineItemType => {
|
(message: MessageWithUIFieldsType, state: StateType): TimelineItemType => {
|
||||||
const selectedMessage = getSelectedMessage(state);
|
const selectedMessage = getSelectedMessage(state);
|
||||||
const conversationSelector = getConversationSelector(state);
|
const conversationSelector = getConversationSelector(state);
|
||||||
|
@ -51,9 +51,6 @@ const getTimelineItemInner = proxyMemoize(
|
||||||
activeCall,
|
activeCall,
|
||||||
accountSelector,
|
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"
|
classnames "^2.2.6"
|
||||||
deepmerge "^4.2.2"
|
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":
|
"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
|
||||||
version "0.1.3"
|
version "0.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
|
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"
|
proxy-from-env "^1.0.0"
|
||||||
socks-proxy-agent "^5.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:
|
proxy-from-env@^1.0.0, proxy-from-env@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue