1
0
Fork 0

Perf fixes

Turns out, adding an object to states.statuses proxyMap object, re-render ALL statuses
This commit is contained in:
Lim Chee Aun 2023-01-07 20:26:23 +08:00
parent 862107f2e6
commit 41df88e625
6 changed files with 71 additions and 73 deletions

View file

@ -302,23 +302,23 @@ async function startStream() {
});
}
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
if (status.reblog) {
states.statuses.set(status.reblog.id, status.reblog);
states.statuses[status.reblog.id] = status.reblog;
}
}, 5000);
stream.on('update', handleNewStatus);
stream.on('status.update', (status) => {
console.log('STATUS.UPDATE', status);
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
if (status.reblog) {
states.statuses.set(status.reblog.id, status.reblog);
states.statuses[status.reblog.id] = status.reblog;
}
});
stream.on('delete', (statusID) => {
console.log('DELETE', statusID);
// states.statuses.delete(statusID);
const s = states.statuses.get(statusID);
// delete states.statuses[statusID];
const s = states.statuses[statusID];
if (s) s._deleted = true;
});
stream.on('notification', (notification) => {
@ -334,16 +334,14 @@ async function startStream() {
states.notificationsNew.unshift(notification);
}
if (notification.status && !states.statuses.has(notification.status.id)) {
states.statuses.set(notification.status.id, notification.status);
if (notification.status && !states.statuses[notification.status.id]) {
states.statuses[notification.status.id] = notification.status;
if (
notification.status.reblog &&
!states.statuses.has(notification.status.reblog.id)
!states.statuses[notification.status.reblog.id]
) {
states.statuses.set(
notification.status.reblog.id,
notification.status.reblog,
);
states.statuses[notification.status.reblog.id] =
notification.status.reblog;
}
}
});
@ -397,9 +395,9 @@ function startVisibility() {
newStatuses[0].id !== states.home[0].id
) {
states.homeNew = newStatuses.map((status) => {
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
if (status.reblog) {
states.statuses.set(status.reblog.id, status.reblog);
states.statuses[status.reblog.id] = status.reblog;
}
return {
id: status.id,
@ -424,20 +422,15 @@ function startVisibility() {
if (
notification.status &&
!states.statuses.has(notification.status.id)
!states.statuses[notification.status.id]
) {
states.statuses.set(
notification.status.id,
notification.status,
);
states.statuses[notification.status.id] = notification.status;
if (
notification.status.reblog &&
!states.statuses.has(notification.status.reblog.id)
!states.statuses[notification.status.reblog.id]
) {
states.statuses.set(
notification.status.reblog.id,
notification.status.reblog,
);
states.statuses[notification.status.reblog.id] =
notification.status.reblog;
}
}
}

View file

@ -2,6 +2,7 @@ import './status.css';
import { getBlurHashAverageColor } from 'fast-blurhash';
import mem from 'mem';
import { memo } from 'preact/compat';
import {
useEffect,
useLayoutEffect,
@ -61,7 +62,7 @@ function Status({
const snapStates = useSnapshot(states);
if (!status) {
status = snapStates.statuses.get(statusID);
status = snapStates.statuses[statusID];
}
if (!status) {
return null;
@ -106,6 +107,8 @@ function Status({
_deleted,
} = status;
console.debug('RENDER Status', id, status?.account.displayName);
const createdAtDate = new Date(createdAt);
const editedAtDate = new Date(editedAt);
@ -122,20 +125,20 @@ function Status({
}
const [inReplyToAccount, setInReplyToAccount] = useState(inReplyToAccountRef);
if (!withinContext && !inReplyToAccount && inReplyToAccountId) {
const account = states.accounts.get(inReplyToAccountId);
const account = states.accounts[inReplyToAccountId];
if (account) {
setInReplyToAccount(account);
} else {
memFetchAccount(inReplyToAccountId)
.then((account) => {
setInReplyToAccount(account);
states.accounts.set(account.id, account);
states.accounts[account.id] = account;
})
.catch((e) => {});
}
}
const showSpoiler = snapStates.spoilers.has(id) || false;
const showSpoiler = !!snapStates.spoilers[id] || false;
const debugHover = (e) => {
if (e.shiftKey) {
@ -321,9 +324,9 @@ function Status({
e.preventDefault();
e.stopPropagation();
if (showSpoiler) {
states.spoilers.delete(id);
delete states.spoilers[id];
} else {
states.spoilers.set(id, true);
states.spoilers[id] = true;
}
}}
>
@ -388,7 +391,7 @@ function Status({
poll={poll}
readOnly={readOnly}
onUpdate={(newPoll) => {
states.statuses.get(id).poll = newPoll;
states.statuses[id].poll = newPoll;
}}
/>
)}
@ -400,9 +403,9 @@ function Status({
e.preventDefault();
e.stopPropagation();
if (showSpoiler) {
states.spoilers.delete(id);
delete states.spoilers[id];
} else {
states.spoilers.set(id, true);
states.spoilers[id] = true;
}
}}
>
@ -519,28 +522,26 @@ function Status({
}
}
// Optimistic
states.statuses.set(id, {
states.statuses[id] = {
...status,
reblogged: !reblogged,
reblogsCount: reblogsCount + (reblogged ? -1 : 1),
});
};
if (reblogged) {
const newStatus = await masto.v1.statuses.unreblog(
id,
);
states.statuses.set(newStatus.id, newStatus);
states.statuses[newStatus.id] = newStatus;
} else {
const newStatus = await masto.v1.statuses.reblog(id);
states.statuses.set(newStatus.id, newStatus);
states.statuses.set(
newStatus.reblog.id,
newStatus.reblog,
);
states.statuses[newStatus.id] = newStatus;
states.statuses[newStatus.reblog.id] =
newStatus.reblog;
}
} catch (e) {
console.error(e);
// Revert optimistism
states.statuses.set(id, status);
states.statuses[id] = status;
}
}}
/>
@ -557,25 +558,25 @@ function Status({
onClick={async () => {
try {
// Optimistic
states.statuses.set(statusID, {
states.statuses[statusID] = {
...status,
favourited: !favourited,
favouritesCount:
favouritesCount + (favourited ? -1 : 1),
});
};
if (favourited) {
const newStatus = await masto.v1.statuses.unfavourite(
id,
);
states.statuses.set(newStatus.id, newStatus);
states.statuses[newStatus.id] = newStatus;
} else {
const newStatus = await masto.v1.statuses.favourite(id);
states.statuses.set(newStatus.id, newStatus);
states.statuses[newStatus.id] = newStatus;
}
} catch (e) {
console.error(e);
// Revert optimistism
states.statuses.set(statusID, status);
states.statuses[statusID] = status;
}
}}
/>
@ -590,23 +591,23 @@ function Status({
onClick={async () => {
try {
// Optimistic
states.statuses.set(statusID, {
states.statuses[statusID] = {
...status,
bookmarked: !bookmarked,
});
};
if (bookmarked) {
const newStatus = await masto.v1.statuses.unbookmark(
id,
);
states.statuses.set(newStatus.id, newStatus);
states.statuses[newStatus.id] = newStatus;
} else {
const newStatus = await masto.v1.statuses.bookmark(id);
states.statuses.set(newStatus.id, newStatus);
states.statuses[newStatus.id] = newStatus;
}
} catch (e) {
console.error(e);
// Revert optimistism
states.statuses.set(statusID, status);
states.statuses[statusID] = status;
}
}}
/>
@ -1437,4 +1438,4 @@ function formatDuration(time) {
}
}
export default Status;
export default memo(Status);

View file

@ -18,6 +18,8 @@ function Home({ hidden }) {
const [uiState, setUIState] = useState('default');
const [showMore, setShowMore] = useState(false);
console.debug('RENDER Home');
const homeIterator = useRef(
masto.v1.timelines.listHome({
limit: LIMIT,
@ -36,9 +38,9 @@ function Home({ hidden }) {
return { done: true };
}
const homeValues = allStatuses.value.map((status) => {
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
if (status.reblog) {
states.statuses.set(status.reblog.id, status.reblog);
states.statuses[status.reblog.id] = status.reblog;
}
return {
id: status.id,

View file

@ -1,6 +1,7 @@
import './notifications.css';
import { Link } from 'preact-router/match';
import { memo } from 'preact/compat';
import { useEffect, useRef, useState } from 'preact/hooks';
import { useSnapshot } from 'valtio';
@ -205,6 +206,8 @@ function Notifications() {
const [showMore, setShowMore] = useState(false);
const [onlyMentions, setOnlyMentions] = useState(false);
console.debug('RENDER Notifications');
const notificationsIterator = useRef(
masto.v1.notifications.list({
limit: LIMIT,
@ -224,7 +227,7 @@ function Notifications() {
}
const notificationsValues = allNotifications.value.map((notification) => {
if (notification.status) {
states.statuses.set(notification.status.id, notification.status);
states.statuses[notification.status.id] = notification.status;
}
return notification;
});
@ -411,4 +414,4 @@ function Notifications() {
);
}
export default Notifications;
export default memo(Notifications);

View file

@ -44,7 +44,7 @@ function StatusPage({ id }) {
// console.log('onScroll');
if (!scrollableRef.current) return;
const { scrollTop } = scrollableRef.current;
states.scrollPositions.set(id, scrollTop);
states.scrollPositions[id] = scrollTop;
}, 100);
scrollableRef.current.addEventListener('scroll', onScroll, {
passive: true,
@ -63,7 +63,7 @@ function StatusPage({ id }) {
if (cachedStatuses) {
// Case 1: It's cached, let's restore them to make it snappy
const reallyCachedStatuses = cachedStatuses.filter(
(s) => states.statuses.has(s.id),
(s) => states.statuses[s.id],
// Some are not cached in the global state, so we need to filter them out
);
setStatuses(reallyCachedStatuses);
@ -83,15 +83,15 @@ function StatusPage({ id }) {
const heroFetch = () => masto.v1.statuses.fetch(id);
const contextFetch = masto.v1.statuses.fetchContext(id);
const hasStatus = snapStates.statuses.has(id);
let heroStatus = snapStates.statuses.get(id);
const hasStatus = !!snapStates.statuses[id];
let heroStatus = snapStates.statuses[id];
if (hasStatus) {
console.log('Hero status is cached');
// NOTE: This might conflict if the user interacts with the status before the fetch is done, e.g. favouriting it
// heroTimer = setTimeout(async () => {
// try {
// heroStatus = await heroFetch();
// states.statuses.set(id, heroStatus);
// states.statuses[id] = heroStatus;
// } catch (e) {
// // Silent fail if status is cached
// console.error(e);
@ -100,7 +100,7 @@ function StatusPage({ id }) {
} else {
try {
heroStatus = await heroFetch();
states.statuses.set(id, heroStatus);
states.statuses[id] = heroStatus;
} catch (e) {
console.error(e);
setUIState('error');
@ -113,11 +113,11 @@ function StatusPage({ id }) {
const { ancestors, descendants } = context;
ancestors.forEach((status) => {
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
});
const nestedDescendants = [];
descendants.forEach((status) => {
states.statuses.set(status.id, status);
states.statuses[status.id] = status;
if (status.inReplyToAccountId === status.account.id) {
// If replying to self, it's part of the thread, level 1
nestedDescendants.push(status);
@ -225,7 +225,7 @@ function StatusPage({ id }) {
}
}
} else {
const scrollPosition = states.scrollPositions.get(id);
const scrollPosition = states.scrollPositions[id];
if (scrollPosition && scrollableRef.current) {
// Case 3: Not user initiated (e.g. back/forward button), restore to saved scroll position
console.log('Case 3');
@ -247,7 +247,7 @@ function StatusPage({ id }) {
}
}, [statuses, uiState]);
const heroStatus = snapStates.statuses.get(id);
const heroStatus = snapStates.statuses[id];
const heroDisplayName = useMemo(() => {
// Remove shortcodes from display name
if (!heroStatus) return '';

View file

@ -1,19 +1,18 @@
import { proxy } from 'valtio';
import { proxyMap } from 'valtio/utils';
export default proxy({
history: [],
statuses: proxyMap([]),
statuses: {},
home: [],
homeNew: [],
homeLastFetchTime: null,
notifications: [],
notificationsNew: [],
notificationsLastFetchTime: null,
accounts: new Map(),
accounts: {},
reloadStatusPage: 0,
spoilers: proxyMap([]),
scrollPositions: new Map(),
spoilers: {},
scrollPositions: {},
// Modals
showCompose: false,
showSettings: false,