Skip to content

Commit d724273

Browse files
committed
fix: Some extension icons being in the wrong place
feat: Expose new installExtension from filepath function to backend ext refactor: Improve RUN_COMMAND usage feat: Use live index for manager
1 parent 36df582 commit d724273

File tree

27 files changed

+357
-146
lines changed

27 files changed

+357
-146
lines changed

extensions/core-controller/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "Enables individual game mapping of controller to keyboard inputs",
55
"version": "1.0.0",
66
"main": "./dist/extension.js",
7+
"icon": "icon.svg",
78
"contributes": {
89
"moduleFederation": [
910
{

extensions/core-controller/rslib.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default defineConfig({
4242
assetPrefix: 'auto',
4343
minify: false,
4444
cleanDistPath: {
45-
keep: [/assets*/, /\.css/],
45+
keep: [/assets*/, /\.css/, /\.png/, /\.svg/],
4646
}
4747
},
4848
}

extensions/core-controller/src/components/Initializer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function Initializer() {
1717
name: 'core_controller/ControllerSupport'
1818
}));
1919
};
20-
});
20+
}, [dispatch]);
2121

2222
return <></>;
2323
}
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DownloadExtCommand = 'core-manager.download-extension';
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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+
}

extensions/core-manager/src/components/Initializer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function Initializer() {
2222
return () => {
2323
dispatch(removeCustomRoute(customRoute));
2424
};
25-
});
25+
}, [dispatch]);
2626

2727
return <></>;
2828
}

extensions/core-manager/src/components/ManagerPage.tsx

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { LeftSidebarItem } from 'flashpoint-launcher-renderer';
2-
import { CheckBox, LeftSidebar, SimpleButton, StateWrapper } from 'flashpoint-launcher-renderer-ext/components';
3-
import { useAppSelector } from 'flashpoint-launcher-renderer-ext/hooks';
4-
import { setExtensionEnabled } from 'flashpoint-launcher-renderer-ext/utils';
5-
import { useEffect, useState } from 'react';
6-
import { List, RowComponentProps, useDynamicRowHeight } from 'react-window';
7-
import { loadExtRepoRaw, ManagerExtensionInfo } from '../extensionLoader';
2+
import { LeftSidebar, StateWrapper } from 'flashpoint-launcher-renderer-ext/components';
3+
import { useState } from 'react';
4+
import { ExtensionSubsection } from './ExtensionSubsection';
85

96
type ManagerPageSubsection = 'extensions' | 'support-packs' | 'utilities';
107

@@ -39,7 +36,7 @@ export default function ManagerPage() {
3936
</div>
4037
<div className='manager-page-subsection-wrapper'>
4138
<StateWrapper show={subsection === 'extensions'}>
42-
<SubsectionExtensions/>
39+
<ExtensionSubsection/>
4340
</StateWrapper>
4441
<StateWrapper show={subsection === 'support-packs'}>
4542
<SubsectionSupportPacks/>
@@ -52,84 +49,6 @@ export default function ManagerPage() {
5249
);
5350
}
5451

55-
function SubsectionExtensions() {
56-
const [availableExtensions, setAvailableExtensions] = useState<ManagerExtensionInfo[]>([]);
57-
const installedExtensions = useAppSelector(state => state.main.extensions);
58-
const disabledExtensions = useAppSelector(state => state.preferences.disabledExtensions);
59-
60-
useEffect(() => {
61-
loadExtRepoRaw('')
62-
.then((data) => {
63-
setAvailableExtensions(data);
64-
});
65-
});
66-
67-
const extensionList: ManagerExtensionInfo[] = installedExtensions.map(ext => {
68-
return {
69-
id: ext.id,
70-
title: ext.displayName || ext.name,
71-
description: ext.description || 'No Description',
72-
installed: true
73-
} satisfies ManagerExtensionInfo;
74-
});
75-
for (const ext of availableExtensions) {
76-
const existingIdx = installedExtensions.findIndex(e => e.id === ext.id);
77-
if (existingIdx === -1) {
78-
extensionList.push(ext);
79-
}
80-
}
81-
82-
extensionList.sort((a, b) => a.title.localeCompare(b.title));
83-
84-
const rowHeight = useDynamicRowHeight({
85-
defaultRowHeight: 24
86-
});
87-
88-
return <div className='manager-page-subsection'>
89-
<div className='manager-page-subsection-header'>Extensions</div>
90-
{ extensionList !== undefined ? (
91-
<List<ExtensionRowProps>
92-
className='simple-scroll'
93-
rowComponent={ExtensionRow}
94-
rowCount={extensionList.length}
95-
rowProps={{
96-
items: extensionList,
97-
disabledExtensions,
98-
}}
99-
rowHeight={rowHeight}/>
100-
) : (
101-
<div>Loading...</div>
102-
)}
103-
</div>;
104-
}
105-
106-
type ExtensionRowProps = {
107-
items: ManagerExtensionInfo[];
108-
disabledExtensions: string[];
109-
}
110-
111-
function ExtensionRow({ items, disabledExtensions, index, style }: RowComponentProps<ExtensionRowProps>) {
112-
const { id, title, description, installed } = items[index];
113-
const enabled = !disabledExtensions.includes(id);
114-
let rowClassName = 'manager-extension-row';
115-
if (index % 2 === 0) { rowClassName += ' manager-extension-row--even'; }
116-
117-
return <div className={rowClassName} style={style}>
118-
<div className='manager-extension-row-top'>
119-
<div className='manager-extension-row-title'>{title} - {`${id}`}</div>
120-
{ installed && (
121-
<CheckBox
122-
onToggle={() => setExtensionEnabled(id, !enabled)}
123-
checked={enabled}/>
124-
)}
125-
</div>
126-
<div className='manager-extension-row-inner'>
127-
<div>{description}</div>
128-
<SimpleButton value={installed ? 'Remove' : 'Install'}/>
129-
</div>
130-
</div>;
131-
}
132-
13352
function SubsectionSupportPacks() {
13453
return <div>Support Packs</div>;
13554

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
1-
import { ExtensionContext, log } from 'flashpoint-launcher';
1+
import axios from 'axios';
2+
import { commands, Disposable, ExtensionContext, installExtension, log, registerDisposable } from 'flashpoint-launcher';
3+
import fs from 'node:fs';
4+
import os from 'node:os';
5+
import path from 'node:path';
6+
import { DownloadExtCommand } from './commands';
27

38
export async function activate(context: ExtensionContext): Promise<void> {
4-
log.info('Hello from the manager');
9+
const register = (disp: Disposable) => {
10+
registerDisposable(context.subscriptions, disp);
11+
};
12+
13+
register(
14+
commands.registerCommand(DownloadExtCommand, async (url: string) => {
15+
const tempPath = os.tmpdir();
16+
const tempFilepath = path.join(tempPath, `extension-${Date.now()}.zip`);
17+
const writer = fs.createWriteStream(tempFilepath);
18+
19+
const res = await axios.get(url, {
20+
responseType: 'stream'
21+
});
22+
23+
res.data.pipe(writer);
24+
25+
await new Promise((resolve, reject) => {
26+
writer.on('finish', resolve);
27+
writer.on('error', reject);
28+
});
29+
30+
log.info('Downloaded Extension Archive from ' + url);
31+
32+
await installExtension(tempFilepath);
33+
})
34+
);
535
}

0 commit comments

Comments
 (0)