Skip to content

Commit a85980b

Browse files
committed
refactor: Move LogsPage and BrowsePage to functional components
fix: Various state fixes for Logs page fix: Fixed logs page source dropdown
1 parent 4dbce71 commit a85980b

File tree

18 files changed

+736
-791
lines changed

18 files changed

+736
-791
lines changed

extensions/core-nga/src/components/components.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ export function NgCredits(props: GameComponentProps) {
6868
return (
6969
<div className='browse-right-sidebar__row'>
7070
<p>Credits: </p>
71-
{ credits.map((credit, idx) => {
71+
{credits.map((credit, idx) => {
7272
return (
7373
<div className='nga-credit' key={idx}>
74-
{ credit.roles?.map((role, idx) => {
74+
{credit.roles?.map((role, idx) => {
7575
if (role in ROLE_ICONS) {
7676
const Component = ROLE_ICONS[role];
7777
return (
@@ -91,10 +91,16 @@ export function NgCredits(props: GameComponentProps) {
9191
);
9292
}
9393
})}
94-
<div className='nga-credt-name'>{credit.name}</div>
94+
<div
95+
className='nga-credt-name browse-right-sidebar__searchable'
96+
onClick={() => {
97+
props.doSearch(`developer:"${credit.name}"`);
98+
}}>
99+
{credit.name}
100+
</div>
95101
</div>
96102
);
97-
}) }
103+
})}
98104
</div>
99105
);
100106
}

src/back/index.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1+
import { BackIn, BackInit, BackInitArgs, BackOut, BackResParams, ComponentState, ComponentStatus, DownloadDetails } from '@shared/back/types';
2+
import { getContentFolderByKey, getCurationFolder } from '@shared/curate/util';
3+
import { ILogoSet, LogoSet } from '@shared/extensions/interfaces';
4+
import { IBackProcessInfo, RecursivePartial } from '@shared/interfaces';
5+
import { LangFileContent, getDefaultLocalization } from '@shared/lang';
16
import { ILogEntry, LogLevel } from '@shared/Log/interface';
7+
import { PreferencesFile } from '@shared/preferences/PreferencesFile';
8+
import { defaultPreferencesData } from '@shared/preferences/util';
29
import { Theme } from '@shared/ThemeFile';
310
import {
411
createErrorProxy, deepCopy,
512
removeFileExtension,
613
stringifyArray
714
} from '@shared/Util';
8-
import * as os from 'os';
9-
import { BackIn, BackInit, BackInitArgs, BackOut, BackResParams, ComponentState, ComponentStatus, DownloadDetails, FpfssUser } from '@shared/back/types';
10-
import { getContentFolderByKey, getCurationFolder } from '@shared/curate/util';
11-
import { ILogoSet, LogoSet } from '@shared/extensions/interfaces';
12-
import { IBackProcessInfo, RecursivePartial } from '@shared/interfaces';
13-
import { LangFileContent, getDefaultLocalization } from '@shared/lang';
14-
import { PreferencesFile } from '@shared/preferences/PreferencesFile';
15-
import { defaultPreferencesData } from '@shared/preferences/util';
1615
import { validateSemiUUID } from '@shared/utils/uuid';
1716
import { FPA_VERSION, VERSION } from '@shared/version';
1817
import * as child_process from 'child_process';
@@ -22,32 +21,31 @@ import * as fs from 'fs-extra';
2221
import * as http from 'http';
2322
import * as mime from 'mime';
2423
import { Progress, add, extractFull } from 'node-7z';
24+
import * as os from 'os';
2525
import * as path from 'path';
2626
import 'reflect-metadata';
2727
import { genCurationWarnings, loadCurationFolder } from './curate/util';
2828
// Required for the DB Models to function
29+
import { FlashpointArchive, enableDebug, loggerSusbcribe } from '@fparchive/flashpoint-archive';
2930
import {
3031
CURATIONS_FOLDER_EXPORTED,
3132
CURATIONS_FOLDER_EXTRACTING,
3233
CURATIONS_FOLDER_TEMP,
3334
CURATIONS_FOLDER_WORKING, CURATION_META_FILENAMES
3435
} from '@shared/constants';
35-
import { FlashpointArchive, enableDebug, loggerSusbcribe } from '@fparchive/flashpoint-archive';
36+
import { formatString } from '@shared/utils/StringFormatter';
3637
import { Tail } from 'tail';
3738
import { ConfigFile } from './ConfigFile';
38-
import { loadExecMappingsFile } from './Execs';
39-
import { ExtConfigFile } from './ExtConfigFile';
40-
import { InstancedAbortController } from './InstancedAbortController';
41-
import { ManagedChildProcess } from './ManagedChildProcess';
42-
import { PlaylistFile } from './PlaylistFile';
43-
import { ServicesFile } from './ServicesFile';
44-
import { SocketServer } from './SocketServer';
45-
import { newThemeWatcher } from './Themes';
4639
import { CONFIG_FILENAME, DISCORD_LINK, EXT_CONFIG_FILENAME, PREFERENCES_FILENAME, SERVICES_SOURCE, WIKI_AV_TROUBLESHOOTING } from './constants';
40+
import { saveCurationFpfssInfo } from './curate/fpfss';
4741
import { loadCurationIndexImage } from './curate/parse';
4842
import { readCurationMeta } from './curate/read';
4943
import { onFileServerRequestCurationFileFactory, onFileServerRequestPostCuration } from './curate/util';
44+
import { axios } from './dns';
5045
import { downloadGameData } from './download';
46+
import { Downloader } from './Downloader';
47+
import { loadExecMappingsFile } from './Execs';
48+
import { ExtConfigFile } from './ExtConfigFile';
5149
import { ApiEmitter } from './extensions/ApiEmitter';
5250
import { ExtensionService } from './extensions/ExtensionService';
5351
import {
@@ -57,24 +55,26 @@ import {
5755
registerInterceptor
5856
} from './extensions/NodeInterceptor';
5957
import { Command, RegisteredMiddleware } from './extensions/types';
58+
import { InstancedAbortController } from './InstancedAbortController';
59+
import { ManagedChildProcess } from './ManagedChildProcess';
6060
import { SystemEnvMiddleware } from './middleware';
61+
import { PlaylistFile } from './PlaylistFile';
6162
import { registerRequestCallbacks } from './responses';
6263
import { genContentTree } from './rust';
64+
import { ServicesFile } from './ServicesFile';
65+
import { SocketServer } from './SocketServer';
66+
import { newThemeWatcher } from './Themes';
6367
import { BackState, ImageDownloadItem } from './types';
68+
import { awaitDialog } from './util/dialog';
6469
import { EventQueue } from './util/EventQueue';
70+
import { onDidInstallGameData, onDidRemoveGame, onDidRemovePlaylistGame, onDidUninstallGameData, onDidUpdateGame, onDidUpdatePlaylist, onDidUpdatePlaylistGame, onServiceChange, onWillImportCuration, onWillUninstallGameData } from './util/events';
6571
import { FileServer, serveFile } from './util/FileServer';
6672
import { FolderWatcher } from './util/FolderWatcher';
73+
import { dispose } from './util/lifecycle';
6774
import { LogFile } from './util/LogFile';
6875
import { logFactory } from './util/logging';
6976
import { createContainer, exit, getMacPATH, promiseSleep, runService } from './util/misc';
7077
import { uuid } from './util/uuid';
71-
import { onDidInstallGameData, onDidRemoveGame, onDidRemovePlaylistGame, onDidUninstallGameData, onDidUpdateGame, onDidUpdatePlaylist, onDidUpdatePlaylistGame, onServiceChange, onWillImportCuration, onWillUninstallGameData } from './util/events';
72-
import { dispose } from './util/lifecycle';
73-
import { formatString } from '@shared/utils/StringFormatter';
74-
import { awaitDialog } from './util/dialog';
75-
import { saveCurationFpfssInfo } from './curate/fpfss';
76-
import { axios } from './dns';
77-
import { Downloader } from './Downloader';
7878

7979
export const VERBOSE = {
8080
enabled: false
@@ -1461,7 +1461,6 @@ async function updateFileServerDownloadQueue() {
14611461
})
14621462
.catch((err) => {
14631463
item.res.writeHead(404);
1464-
log.error('Launcher', 'Failure downloading image on demand: ' + err);
14651464
})
14661465
.finally(async () => {
14671466
removeFileServerDownloadItem(item);
@@ -1643,4 +1642,4 @@ export async function databaseReady(): Promise<FlashpointArchive> {
16431642
};
16441643
waitForDb();
16451644
});
1646-
}
1645+
}

src/main/MainWindowPreload.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ window.Shared = {
8484

8585
customVersion: undefined,
8686

87+
initialLogEntries: createErrorProxy('initialLogEntries'),
8788
initialLang: createErrorProxy('initialLang'),
8889
initialLangList: createErrorProxy('initialLangList'),
8990
initialThemes: createErrorProxy('initialThemes'),
@@ -126,7 +127,7 @@ const onInit = (async () => {
126127
fullJsonFolderPath: path.resolve(data.config.flashpointPath, data.preferences.jsonFolderPath),
127128
};
128129
window.Shared.fileServerPort = data.fileServerPort;
129-
window.Shared.log.entries = data.log;
130+
window.Shared.initialLogEntries = data.log;
130131
// window.Shared.initialServices = data.services;
131132
window.Shared.customVersion = data.customVersion;
132133
window.Shared.initialLang = data.language;

src/renderer/components/Dropdown.tsx

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
2-
import { useCallback, useEffect, useRef, useState } from 'react';
3-
import { checkIfAncestor } from '../Util';
2+
import { useRef, useState } from 'react';
43

54
export type DropdownProps = {
65
/** Extra class name to add to dropdown frame */
@@ -17,45 +16,51 @@ export type DropdownProps = {
1716
export function Dropdown(props: DropdownProps) {
1817
// Hooks
1918
const [expanded, setExpanded] = useState<boolean>(false);
20-
const contentRef = useRef<HTMLDivElement>(null);
21-
useEffect(() => { // ("Hide" the drop-downs content if the user clicks outside the content element)
22-
if (expanded) {
23-
const onGlobalMouseDown = (event: MouseEvent) => {
24-
if (!event.defaultPrevented) {
25-
if (!checkIfAncestor(event.target as HTMLElement | null, contentRef.current)) {
26-
setExpanded(false);
27-
}
28-
}
29-
};
30-
document.addEventListener('mousedown', onGlobalMouseDown);
31-
return () => { document.removeEventListener('mousedown', onGlobalMouseDown); };
32-
}
33-
}, [expanded, contentRef]);
34-
const onMouseDown = useCallback((event: React.MouseEvent) => {
35-
if (event.button === 0) { // (Left mouse button)
36-
setExpanded(!expanded);
19+
const dropdownRef = useRef<HTMLDivElement>(null);
20+
21+
const onToggleExpanded = () => {
22+
setExpanded(!expanded);
23+
};
24+
25+
// Close dropdown when clicking outside of it
26+
const handleClickOutside = (event: any) => {
27+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
28+
setExpanded(false);
3729
}
38-
}, [expanded]);
30+
};
31+
32+
React.useEffect(() => {
33+
// Add event listener to handle clicks outside the dropdown
34+
document.addEventListener('mousedown', handleClickOutside);
35+
36+
// Cleanup the event listener on component unmount
37+
return () => {
38+
document.removeEventListener('mousedown', handleClickOutside);
39+
};
40+
});
3941

4042
const baseClass = props.form ? 'simple-dropdown-form' : 'simple-dropdown';
4143

4244
// Render
4345
return (
44-
<div className={`${baseClass} ${props.className}`}>
46+
<div
47+
className={`${baseClass} ${props.className}`}
48+
onClick={onToggleExpanded}>
4549
<div
4650
className={`${baseClass}__select-box ${props.headerClassName}`}
47-
onMouseDown={onMouseDown}
4851
tabIndex={0}>
4952
<div className={`${baseClass}__select-text`}>
50-
{ props.text }
53+
{props.text}
5154
</div>
5255
<div className={`${baseClass}__select-icon`} />
5356
</div>
5457
<div
5558
className={`${baseClass}__content` + (expanded ? '' : ` ${baseClass}__content--hidden`)}
56-
onMouseUp={() => setExpanded(false)}
57-
ref={contentRef}>
58-
{ props.children }
59+
ref={dropdownRef}
60+
onClick={(e) => e.stopPropagation()}>
61+
{expanded && (
62+
props.children
63+
)}
5964
</div>
6065
</div>
6166
);

src/renderer/components/GameGrid.tsx

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import { GameLaunchOverride, TagFilter } from 'flashpoint-launcher';
1313

1414
const RENDERER_OVERSCAN = 5;
1515

16-
/** A function that receives an HTML element. */
17-
type RefFunc<T extends HTMLElement> = (instance: T | null) => void;
18-
1916
type ColumnsRows = {
2017
columns: number;
2118
rows: number;
@@ -54,8 +51,6 @@ export type GameGridProps = {
5451
/** Moves a game at the specified index above the other game at the destination index, inside the playlist */
5552
onMovePlaylistGame: (sourceGameId: string, destGameId: string) => void;
5653
updateView: UpdateView;
57-
/** Function for getting a reference to grid element. Called whenever the reference could change. */
58-
gridRef?: RefFunc<HTMLDivElement>;
5954
/** Updates to clear platform icon cache */
6055
logoVersion: number;
6156
/** Screenshot Preview Mode */
@@ -106,7 +101,6 @@ export class GameGrid extends React.Component<GameGridProps, GameGridState> {
106101
componentDidMount(): void {
107102
window.Shared.back.registerAny(this.onResponse);
108103
this.updateCssVars();
109-
this.updatePropRefs();
110104
}
111105

112106
componentDidUpdate(prevProps: GameGridProps): void {
@@ -116,7 +110,6 @@ export class GameGrid extends React.Component<GameGridProps, GameGridState> {
116110
});
117111
}
118112
this.updateCssVars();
119-
this.updatePropRefs();
120113

121114
// Clear forced scrollTop after use
122115
if (this.state.forceScrollTop !== undefined) {
@@ -417,26 +410,6 @@ export class GameGrid extends React.Component<GameGridProps, GameGridState> {
417410
}
418411
}
419412

420-
/**
421-
* Call the "ref" property functions.
422-
* Do this whenever there's a possibility that the referenced elements has been replaced.
423-
*/
424-
updatePropRefs(): void {
425-
if (this.props.gridRef) {
426-
// Find the grid element
427-
let ref: HTMLDivElement | null = null;
428-
if (this.wrapperRef.current) {
429-
const inner = this.wrapperRef.current.querySelector('.game-grid');
430-
if (inner) { ref = inner as HTMLDivElement; }
431-
}
432-
// Call callback
433-
if (ref !== this.prevWrapperRef) {
434-
this.prevWrapperRef = ref;
435-
this.props.gridRef(ref);
436-
}
437-
}
438-
}
439-
440413
calculateSize(resultsTotal: number, width: number): ColumnsRows {
441414
// Calculate and set column/row count
442415
// (16 is the width of a scroll-bar in pixels - at least on windows)

src/renderer/components/GameList.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ import { GameListItem } from './GameListItem';
1111
import { GameDragData, GameDragEventData } from './pages/BrowsePage';
1212
import { GameLaunchOverride, TagFilter } from 'flashpoint-launcher';
1313

14-
/** A function that receives an HTML element. */
15-
type RefFunc<T extends HTMLElement> = (instance: T | null) => void;
16-
1714
const RENDERER_OVERSCAN = 15;
1815

1916
export type OwnProps = {
@@ -51,8 +48,6 @@ export type OwnProps = {
5148
/** Moves a game at the specified index above the other game at the destination index, inside the playlist */
5249
onMovePlaylistGame: (sourceGameId: string, destGameId: string) => void;
5350
updateView: UpdateView;
54-
/** Function for getting a reference to grid element. Called whenever the reference could change. */
55-
listRef?: RefFunc<HTMLDivElement>;
5651
/** Updates to clear platform icon cache */
5752
logoVersion: number;
5853
/** View id */

src/renderer/components/LeftBrowseSidebar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type OwnProps = {
1616
selectedPlaylistID?: string;
1717
isEditing: boolean;
1818
isNewPlaylist: boolean;
19-
currentPlaylist?: Playlist;
19+
currentPlaylist: Playlist | null;
2020
playlistIconCache: Record<string, string>;
2121
onDelete: () => void;
2222
onSave: () => void;
@@ -100,7 +100,7 @@ export class LeftBrowseSidebar extends React.Component<LeftBrowseSidebarProps> {
100100
renderPlaylistsMemo = memoizeOne((
101101
playlists: Playlist[],
102102
playlistIconCache: Record<string, string>,
103-
currentPlaylist: Playlist | undefined,
103+
currentPlaylist: Playlist | null,
104104
editingDisabled: boolean,
105105
editingExtremeDisabled: boolean,
106106
isEditing: boolean,
@@ -109,7 +109,7 @@ export class LeftBrowseSidebar extends React.Component<LeftBrowseSidebarProps> {
109109
) => {
110110
const renderItem = (playlist: Playlist, isNew: boolean): void => {
111111
const isSelected = isNew || playlist.id === selectedPlaylistID;
112-
const p = (isSelected && currentPlaylist) ? currentPlaylist : playlist;
112+
const p = (isSelected && currentPlaylist !== null) ? currentPlaylist : playlist;
113113
const key = isNew ? '?new' : playlist.id;
114114
elements.push(
115115
<PlaylistItem

0 commit comments

Comments
 (0)