|
| 1 | +import { CheckBox, SimpleButton } from 'flashpoint-launcher-renderer-ext/components'; |
| 2 | +import { useAppSelector } from 'flashpoint-launcher-renderer-ext/hooks'; |
| 3 | +import { getExtensionFileURL, runCommand, setExtensionEnabled } from 'flashpoint-launcher-renderer-ext/utils'; |
| 4 | +import { useEffect, useState } from 'react'; |
| 5 | +import { List, RowComponentProps, useDynamicRowHeight } from 'react-window'; |
| 6 | +import { DownloadExtCommand } from '../commands'; |
| 7 | +import { loadExtIndexUrl, ManagerExtensionInfo } from '../extensionLoader'; |
| 8 | + |
| 9 | +export type ExtensionRowProps = { |
| 10 | + items: ManagerExtensionInfo[]; |
| 11 | + disabledExtensions: string[]; |
| 12 | +} |
| 13 | + |
| 14 | +export function ExtensionSubsection() { |
| 15 | + const [availableExtensions, setAvailableExtensions] = useState<ManagerExtensionInfo[]>([]); |
| 16 | + const installedExtensions = useAppSelector(state => state.main.extensions); |
| 17 | + const disabledExtensions = useAppSelector(state => state.preferences.disabledExtensions); |
| 18 | + |
| 19 | + useEffect(() => { |
| 20 | + console.log('loading ext'); |
| 21 | + loadExtIndexUrl('https://raw.githubusercontent.com/FlashpointProject/FlashpointExtensionIndex/refs/heads/main/extindex.json') |
| 22 | + .then((data) => { |
| 23 | + setAvailableExtensions(data); |
| 24 | + }); |
| 25 | + }, []); |
| 26 | + |
| 27 | + const extensionList: ManagerExtensionInfo[] = installedExtensions.map(ext => { |
| 28 | + return { |
| 29 | + id: ext.id, |
| 30 | + title: ext.displayName || ext.name, |
| 31 | + description: ext.description || 'No Description', |
| 32 | + newestVersion: ext.version, |
| 33 | + iconUrl: ext.icon ? getExtensionFileURL(ext.id, ext.icon) : undefined, |
| 34 | + installed: true |
| 35 | + } satisfies ManagerExtensionInfo; |
| 36 | + }); |
| 37 | + for (const ext of availableExtensions) { |
| 38 | + const existingIdx = installedExtensions.findIndex(e => e.id === ext.id); |
| 39 | + if (existingIdx === -1) { |
| 40 | + extensionList.push(ext); |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + extensionList.sort((a, b) => a.title.localeCompare(b.title)); |
| 45 | + |
| 46 | + const rowHeight = useDynamicRowHeight({ |
| 47 | + defaultRowHeight: 24 |
| 48 | + }); |
| 49 | + |
| 50 | + return <div className='manager-page-subsection'> |
| 51 | + <div className='manager-page-subsection-header'>Extensions</div> |
| 52 | + { extensionList !== undefined ? ( |
| 53 | + <List<ExtensionRowProps> |
| 54 | + className='simple-scroll' |
| 55 | + rowComponent={ExtensionRow} |
| 56 | + rowCount={extensionList.length} |
| 57 | + rowProps={{ |
| 58 | + items: extensionList, |
| 59 | + disabledExtensions, |
| 60 | + }} |
| 61 | + rowHeight={rowHeight}/> |
| 62 | + ) : ( |
| 63 | + <div>Loading...</div> |
| 64 | + )} |
| 65 | + </div>; |
| 66 | +} |
| 67 | + |
| 68 | +export function ExtensionRow({ items, disabledExtensions, index, style }: RowComponentProps<ExtensionRowProps>) { |
| 69 | + const { id, title, description, installed, newestVersion, iconUrl, getDownloadUrl, } = items[index]; |
| 70 | + const [busy, setBusy] = useState(false); |
| 71 | + const canInstall = getDownloadUrl !== undefined; |
| 72 | + const enabled = !disabledExtensions.includes(id); |
| 73 | + let rowClassName = 'manager-extension-row'; |
| 74 | + if (index % 2 === 0) { rowClassName += ' manager-extension-row--even'; } |
| 75 | + |
| 76 | + return <div className={rowClassName} style={style}> |
| 77 | + <div className='manager-extension-row-icon'> |
| 78 | + { iconUrl && ( |
| 79 | + <img src={iconUrl}/> |
| 80 | + ) } |
| 81 | + </div> |
| 82 | + <div className='manager-extension-row-content'> |
| 83 | + <div className='manager-extension-row-top'> |
| 84 | + <div className='manager-extension-row-title'>{title} - {`${id}`}</div> |
| 85 | + { !busy && ( |
| 86 | + <> |
| 87 | + { installed && ( |
| 88 | + <CheckBox |
| 89 | + onToggle={() => setExtensionEnabled(id, !enabled)} |
| 90 | + checked={enabled}/> |
| 91 | + )} |
| 92 | + </> |
| 93 | + )} |
| 94 | + </div> |
| 95 | + <div className='manager-extension-row-inner'> |
| 96 | + <div>{description}</div> |
| 97 | + { !busy ? ( |
| 98 | + <> |
| 99 | + { installed && ( |
| 100 | + <SimpleButton value={'Remove'}/> |
| 101 | + )} |
| 102 | + { canInstall && ( |
| 103 | + <SimpleButton value={'Install'} onClick={() => { |
| 104 | + setBusy(true); |
| 105 | + runCommand(DownloadExtCommand, getDownloadUrl(newestVersion)) |
| 106 | + .catch((error) => { |
| 107 | + const errorString = `Failed to download and install extension: ${error}`; |
| 108 | + alert(errorString); |
| 109 | + log.error('Manager', errorString); |
| 110 | + }) |
| 111 | + .finally(() => setBusy(false)); |
| 112 | + }}/> |
| 113 | + )} |
| 114 | + </> |
| 115 | + ) : <div>Busy...</div> } |
| 116 | + </div> |
| 117 | + </div> |
| 118 | + </div>; |
| 119 | +} |
0 commit comments