Skip to content

Commit 1f597a5

Browse files
committed
feat: memoize content grid items
1 parent 9af5166 commit 1f597a5

File tree

1 file changed

+153
-147
lines changed

1 file changed

+153
-147
lines changed

electron/renderer/pages/grid.tsx

+153-147
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { css } from '@emotion/react';
55
import isEmpty from 'lodash-es/isEmpty.js';
66
import { useObservable, useSubscription } from 'observable-hooks';
77
import type { KeyboardEventHandler, ReactNode } from 'react';
8-
import { useCallback, useEffect, useRef, useState } from 'react';
8+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
99
import * as rxjs from 'rxjs';
1010
import { v4 as uuid } from 'uuid';
1111
import { getExperienceMindState } from '../../common/game/get-experience-mindstate.js';
@@ -347,167 +347,173 @@ const GridPage: React.FC = (): ReactNode => {
347347
const [gridWidthRef, { width: gridWidth }] = useMeasure<HTMLDivElement>();
348348
const gridHeight = windowSize.height - bottomBarSize.height - 40;
349349

350-
// TODO define a default config set
351-
// TODO allow users to customize the set and add/remove items
352-
// TODO IPC handler to get/save the user's config set
353-
const configGridItems: Array<GridItemConfig> = [];
350+
const contentGridItems = useMemo<Array<GridItemContent>>(() => {
351+
// TODO define a default config set
352+
// TODO allow users to customize the set and add/remove items
353+
// TODO IPC handler to get/save the user's config set
354+
const configGridItems: Array<GridItemConfig> = [];
354355

355-
configGridItems.push({
356-
gameItemInfo: getGameItemInfo(GameItemId.ROOM),
357-
whenVisibleStreamToItemIds: [GameItemId.ROOM],
358-
whenHiddenStreamToItemIds: [],
359-
});
356+
configGridItems.push({
357+
gameItemInfo: getGameItemInfo(GameItemId.ROOM),
358+
whenVisibleStreamToItemIds: [GameItemId.ROOM],
359+
whenHiddenStreamToItemIds: [],
360+
});
360361

361-
configGridItems.push({
362-
gameItemInfo: getGameItemInfo(GameItemId.EXPERIENCE),
363-
whenVisibleStreamToItemIds: [GameItemId.EXPERIENCE],
364-
whenHiddenStreamToItemIds: [],
365-
});
362+
configGridItems.push({
363+
gameItemInfo: getGameItemInfo(GameItemId.EXPERIENCE),
364+
whenVisibleStreamToItemIds: [GameItemId.EXPERIENCE],
365+
whenHiddenStreamToItemIds: [],
366+
});
366367

367-
configGridItems.push({
368-
gameItemInfo: getGameItemInfo(GameItemId.MAIN),
369-
whenVisibleStreamToItemIds: [GameItemId.MAIN],
370-
whenHiddenStreamToItemIds: [],
371-
});
368+
configGridItems.push({
369+
gameItemInfo: getGameItemInfo(GameItemId.MAIN),
370+
whenVisibleStreamToItemIds: [GameItemId.MAIN],
371+
whenHiddenStreamToItemIds: [],
372+
});
372373

373-
configGridItems.push({
374-
gameItemInfo: getGameItemInfo(GameItemId.SPELLS),
375-
whenVisibleStreamToItemIds: [GameItemId.SPELLS],
376-
whenHiddenStreamToItemIds: [],
377-
});
374+
configGridItems.push({
375+
gameItemInfo: getGameItemInfo(GameItemId.SPELLS),
376+
whenVisibleStreamToItemIds: [GameItemId.SPELLS],
377+
whenHiddenStreamToItemIds: [],
378+
});
378379

379-
const configItemsMap: Record<string, GridItemConfig> = {};
380-
const configItemIds: Array<string> = [];
381-
configGridItems.forEach((configItem) => {
382-
const itemId = configItem.gameItemInfo.itemId;
383-
configItemsMap[itemId] = configItem;
384-
configItemIds.push(itemId);
385-
});
380+
const configItemsMap: Record<string, GridItemConfig> = {};
381+
const configItemIds: Array<string> = [];
382+
configGridItems.forEach((configItem) => {
383+
const itemId = configItem.gameItemInfo.itemId;
384+
configItemsMap[itemId] = configItem;
385+
configItemIds.push(itemId);
386+
});
386387

387-
// TODO define a default layout
388-
// TODO IPC handler to get/save a layout
389-
// TODO allow user to assign layouts to characters
390-
let layoutGridItems = new Array<GridItemInfo>();
391-
392-
layoutGridItems.push({
393-
itemId: 'room',
394-
itemTitle: 'Room',
395-
isFocused: false,
396-
layout: {
397-
x: 0,
398-
y: 0,
399-
width: 828,
400-
height: 200,
401-
},
402-
});
388+
// TODO define a default layout
389+
// TODO IPC handler to get/save a layout
390+
// TODO allow user to assign layouts to characters
391+
let layoutGridItems = new Array<GridItemInfo>();
392+
393+
layoutGridItems.push({
394+
itemId: 'room',
395+
itemTitle: 'Room',
396+
isFocused: false,
397+
layout: {
398+
x: 0,
399+
y: 0,
400+
width: 828,
401+
height: 200,
402+
},
403+
});
403404

404-
layoutGridItems.push({
405-
itemId: 'experience',
406-
itemTitle: 'Experience',
407-
isFocused: false,
408-
layout: {
409-
x: 828,
410-
y: 0,
411-
width: 306,
412-
height: 392,
413-
},
414-
});
405+
layoutGridItems.push({
406+
itemId: 'experience',
407+
itemTitle: 'Experience',
408+
isFocused: false,
409+
layout: {
410+
x: 828,
411+
y: 0,
412+
width: 306,
413+
height: 392,
414+
},
415+
});
415416

416-
layoutGridItems.push({
417-
itemId: 'spells',
418-
itemTitle: 'Spells',
419-
isFocused: false,
420-
layout: {
421-
x: 828,
422-
y: 390,
423-
width: 306,
424-
height: 310,
425-
},
426-
});
417+
layoutGridItems.push({
418+
itemId: 'spells',
419+
itemTitle: 'Spells',
420+
isFocused: false,
421+
layout: {
422+
x: 828,
423+
y: 390,
424+
width: 306,
425+
height: 310,
426+
},
427+
});
427428

428-
layoutGridItems.push({
429-
itemId: 'main',
430-
itemTitle: 'Main',
431-
isFocused: true,
432-
layout: {
433-
x: 0,
434-
y: 200,
435-
width: 828,
436-
height: 500,
437-
},
438-
});
429+
layoutGridItems.push({
430+
itemId: 'main',
431+
itemTitle: 'Main',
432+
isFocused: true,
433+
layout: {
434+
x: 0,
435+
y: 200,
436+
width: 828,
437+
height: 500,
438+
},
439+
});
439440

440-
// Drop any items that no longer have a matching config item.
441-
layoutGridItems = layoutGridItems.filter((layoutItem) => {
442-
return configItemIds.includes(layoutItem.itemId);
443-
});
441+
// Drop any items that no longer have a matching config item.
442+
layoutGridItems = layoutGridItems.filter((layoutItem) => {
443+
return configItemIds.includes(layoutItem.itemId);
444+
});
444445

445-
const layoutItemsMap: Record<string, GridItemInfo> = {};
446-
const layoutItemIds: Array<string> = [];
447-
layoutGridItems.forEach((layoutItem) => {
448-
const itemId = layoutItem.itemId;
449-
layoutItemsMap[itemId] = layoutItem;
450-
layoutItemIds.push(itemId);
451-
});
446+
const layoutItemsMap: Record<string, GridItemInfo> = {};
447+
const layoutItemIds: Array<string> = [];
448+
layoutGridItems.forEach((layoutItem) => {
449+
const itemId = layoutItem.itemId;
450+
layoutItemsMap[itemId] = layoutItem;
451+
layoutItemIds.push(itemId);
452+
});
452453

453-
// Map of item ids to the item ids that should stream to it.
454-
// The key is the item id that should receive the stream(s).
455-
// The values are the items redirecting their stream to the key item.
456-
const itemStreamMapping: Record<string, Array<string>> = {};
457-
458-
// If layout includes the config item then stream to its visible items.
459-
// If layout does not include the config item then stream to its hidden items.
460-
configGridItems.forEach((configItem) => {
461-
const itemId = configItem.gameItemInfo.itemId;
462-
463-
const streamToItemIds = layoutItemsMap[itemId]
464-
? configItem.whenVisibleStreamToItemIds
465-
: configItem.whenHiddenStreamToItemIds;
466-
467-
// TODO rename this method and move it out the for-each loop
468-
// If an item is hidden and redirects elsewhere, follow the chain
469-
// until we find an item that is visible to truly redirect to.
470-
// This is necessary because the layout may not include all items.
471-
const funcX = (streamToItemIds: Array<string>, itemId: string) => {
472-
streamToItemIds.forEach((streamToItemId) => {
473-
if (layoutItemsMap[streamToItemId]) {
474-
// We're in luck. We found a visible item to stream to.
475-
itemStreamMapping[streamToItemId] ||= [];
476-
itemStreamMapping[streamToItemId].push(itemId);
477-
} else {
478-
// Well, where the hidden item wanted to redirect to
479-
// also is hidden so we need to keep looking for a visible item.
480-
funcX(
481-
configItemsMap[streamToItemId].whenHiddenStreamToItemIds,
482-
itemId
483-
);
484-
}
485-
});
486-
};
454+
// Map of item ids to the item ids that should stream to it.
455+
// The key is the item id that should receive the stream(s).
456+
// The values are the items redirecting their stream to the key item.
457+
const itemStreamMapping: Record<string, Array<string>> = {};
458+
459+
// If layout includes the config item then stream to its visible items.
460+
// If layout does not include the config item then stream to its hidden items.
461+
configGridItems.forEach((configItem) => {
462+
const itemId = configItem.gameItemInfo.itemId;
463+
464+
const streamToItemIds = layoutItemsMap[itemId]
465+
? configItem.whenVisibleStreamToItemIds
466+
: configItem.whenHiddenStreamToItemIds;
467+
468+
// TODO rename this method and move it out the for-each loop
469+
// If an item is hidden and redirects elsewhere, follow the chain
470+
// until we find an item that is visible to truly redirect to.
471+
// This is necessary because the layout may not include all items.
472+
const funcX = (streamToItemIds: Array<string>, itemId: string) => {
473+
streamToItemIds.forEach((streamToItemId) => {
474+
if (layoutItemsMap[streamToItemId]) {
475+
// We're in luck. We found a visible item to stream to.
476+
itemStreamMapping[streamToItemId] ||= [];
477+
itemStreamMapping[streamToItemId].push(itemId);
478+
} else {
479+
// Well, where the hidden item wanted to redirect to
480+
// also is hidden so we need to keep looking for a visible item.
481+
funcX(
482+
configItemsMap[streamToItemId].whenHiddenStreamToItemIds,
483+
itemId
484+
);
485+
}
486+
});
487+
};
487488

488-
funcX(streamToItemIds, itemId);
489-
});
489+
funcX(streamToItemIds, itemId);
490+
});
490491

491-
const contentGridItems: Array<GridItemContent> = [];
492-
493-
layoutGridItems.forEach((layoutItem) => {
494-
const configItem = configItemsMap[layoutItem.itemId];
495-
496-
contentGridItems.push({
497-
itemId: layoutItem.itemId,
498-
itemTitle: configItem.gameItemInfo.itemTitle ?? layoutItem.itemTitle,
499-
isFocused: layoutItem.isFocused,
500-
layout: layoutItem.layout,
501-
content: (
502-
<GameStream
503-
gameStreamIds={itemStreamMapping[layoutItem.itemId].map((itemId) => {
504-
return configItemsMap[itemId].gameItemInfo.streamId;
505-
})}
506-
stream$={gameLogLineSubject$}
507-
/>
508-
),
492+
const contentGridItems: Array<GridItemContent> = [];
493+
494+
layoutGridItems.forEach((layoutItem) => {
495+
const configItem = configItemsMap[layoutItem.itemId];
496+
497+
contentGridItems.push({
498+
itemId: layoutItem.itemId,
499+
itemTitle: configItem.gameItemInfo.itemTitle ?? layoutItem.itemTitle,
500+
isFocused: layoutItem.isFocused,
501+
layout: layoutItem.layout,
502+
content: (
503+
<GameStream
504+
gameStreamIds={itemStreamMapping[layoutItem.itemId].map(
505+
(itemId) => {
506+
return configItemsMap[itemId].gameItemInfo.streamId;
507+
}
508+
)}
509+
stream$={gameLogLineSubject$}
510+
/>
511+
),
512+
});
509513
});
510-
});
514+
515+
return contentGridItems;
516+
}, [gameLogLineSubject$]);
511517

512518
return (
513519
<EuiPageTemplate

0 commit comments

Comments
 (0)