mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-12 03:21:58 +00:00
Migrate most of frontend callServerMethod usage over to websocket
This commit is contained in:
@@ -66,6 +66,21 @@ class Utilities:
|
|||||||
context.ws.add_route("utilities/ping", self.ping)
|
context.ws.add_route("utilities/ping", self.ping)
|
||||||
context.ws.add_route("utilities/settings/get", self.get_setting)
|
context.ws.add_route("utilities/settings/get", self.get_setting)
|
||||||
context.ws.add_route("utilities/settings/set", self.set_setting)
|
context.ws.add_route("utilities/settings/set", self.set_setting)
|
||||||
|
context.ws.add_route("utilities/install_plugin", self.install_plugin)
|
||||||
|
context.ws.add_route("utilities/install_plugins", self.install_plugins)
|
||||||
|
context.ws.add_route("utilities/cancel_plugin_install", self.cancel_plugin_install)
|
||||||
|
context.ws.add_route("utilities/confirm_plugin_install", self.confirm_plugin_install)
|
||||||
|
context.ws.add_route("utilities/uninstall_plugin", self.uninstall_plugin)
|
||||||
|
context.ws.add_route("utilities/execute_in_tab", self.execute_in_tab)
|
||||||
|
context.ws.add_route("utilities/inject_css_into_tab", self.inject_css_into_tab)
|
||||||
|
context.ws.add_route("utilities/remove_css_from_tab", self.remove_css_from_tab)
|
||||||
|
context.ws.add_route("utilities/allow_remote_debugging", self.allow_remote_debugging)
|
||||||
|
context.ws.add_route("utilities/disallow_remote_debugging", self.disallow_remote_debugging)
|
||||||
|
context.ws.add_route("utilities/filepicker_ls", self.filepicker_ls)
|
||||||
|
context.ws.add_route("utilities/disable_rdt", self.disable_rdt)
|
||||||
|
context.ws.add_route("utilities/enable_rdt", self.enable_rdt)
|
||||||
|
context.ws.add_route("utilities/get_tab_id", self.get_tab_id)
|
||||||
|
context.ws.add_route("utilities/get_user_info", self.get_user_info)
|
||||||
|
|
||||||
async def _handle_server_method_call(self, request):
|
async def _handle_server_method_call(self, request):
|
||||||
method_name = request.match_info["method_name"]
|
method_name = request.match_info["method_name"]
|
||||||
@@ -139,8 +154,7 @@ class Utilities:
|
|||||||
"result": e
|
"result": e
|
||||||
}
|
}
|
||||||
|
|
||||||
async def inject_css_into_tab(self, tab: str, style: str):
|
async def inject_css_into_tab(self, tab: str, style: str) -> str:
|
||||||
try:
|
|
||||||
css_id = str(uuid.uuid4())
|
css_id = str(uuid.uuid4())
|
||||||
|
|
||||||
result = await inject_to_tab(tab,
|
result = await inject_to_tab(tab,
|
||||||
@@ -153,24 +167,12 @@ class Utilities:
|
|||||||
}})()
|
}})()
|
||||||
""", False)
|
""", False)
|
||||||
|
|
||||||
if result and "exceptionDetails" in result["result"]:
|
if "exceptionDetails" in result["result"]:
|
||||||
return {
|
raise result["result"]["exceptionDetails"]
|
||||||
"success": False,
|
|
||||||
"result": result["result"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return css_id
|
||||||
"success": True,
|
|
||||||
"result": css_id
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"result": e
|
|
||||||
}
|
|
||||||
|
|
||||||
async def remove_css_from_tab(self, tab: str, css_id: str):
|
async def remove_css_from_tab(self, tab: str, css_id: str):
|
||||||
try:
|
|
||||||
result = await inject_to_tab(tab,
|
result = await inject_to_tab(tab,
|
||||||
f"""
|
f"""
|
||||||
(function() {{
|
(function() {{
|
||||||
@@ -181,20 +183,10 @@ class Utilities:
|
|||||||
}})()
|
}})()
|
||||||
""", False)
|
""", False)
|
||||||
|
|
||||||
if result and "exceptionDetails" in result["result"]:
|
if "exceptionDetails" in result["result"]:
|
||||||
return {
|
raise result["result"]["exceptionDetails"]
|
||||||
"success": False,
|
|
||||||
"result": result
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return
|
||||||
"success": True
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"result": e
|
|
||||||
}
|
|
||||||
|
|
||||||
async def get_setting(self, key: str, default: Any):
|
async def get_setting(self, key: str, default: Any):
|
||||||
return self.context.settings.getSetting(key, default)
|
return self.context.settings.getSetting(key, default)
|
||||||
@@ -211,12 +203,12 @@ class Utilities:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
async def filepicker_ls(self,
|
async def filepicker_ls(self,
|
||||||
path : str | None = None,
|
path: str | None = None,
|
||||||
include_files: bool = True,
|
include_files: bool = True,
|
||||||
include_folders: bool = True,
|
include_folders: bool = True,
|
||||||
include_ext: list[str] = [],
|
include_ext: list[str] | None = None,
|
||||||
include_hidden: bool = False,
|
include_hidden: bool = False,
|
||||||
order_by: str = "name_asc",
|
order_by: str = "name_desc",
|
||||||
filter_for: str | None = None,
|
filter_for: str | None = None,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
max: int = 1000):
|
max: int = 1000):
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class WSRouter:
|
|||||||
match data["type"]:
|
match data["type"]:
|
||||||
case MessageType.CALL.value:
|
case MessageType.CALL.value:
|
||||||
# do stuff with the message
|
# do stuff with the message
|
||||||
if self.routes[data["route"]]:
|
if data["route"] in self.routes:
|
||||||
try:
|
try:
|
||||||
res = await self.routes[data["route"]](*data["args"])
|
res = await self.routes[data["route"]](*data["args"])
|
||||||
await self.write({"type": MessageType.REPLY.value, "id": data["id"], "result": res})
|
await self.write({"type": MessageType.REPLY.value, "id": data["id"], "result": res})
|
||||||
@@ -87,7 +87,8 @@ class WSRouter:
|
|||||||
except:
|
except:
|
||||||
await self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": format_exc()})
|
await self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": format_exc()})
|
||||||
else:
|
else:
|
||||||
await self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": "Route does not exist."})
|
# Dunno why but fstring doesnt work here
|
||||||
|
await self.write({"type": MessageType.ERROR.value, "id": data["id"], "error": "Route " + data["route"] + " does not exist."})
|
||||||
case MessageType.REPLY.value:
|
case MessageType.REPLY.value:
|
||||||
if self.running_calls[data["id"]]:
|
if self.running_calls[data["id"]]:
|
||||||
self.running_calls[data["id"]].set_result(data["result"])
|
self.running_calls[data["id"]].set_result(data["result"])
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ConfirmModal } from 'decky-frontend-lib';
|
import { ConfirmModal } from 'decky-frontend-lib';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
|
import { uninstallPlugin } from '../../plugin';
|
||||||
|
|
||||||
interface PluginUninstallModalProps {
|
interface PluginUninstallModalProps {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -14,7 +16,7 @@ const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, butt
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
closeModal={closeModal}
|
closeModal={closeModal}
|
||||||
onOK={async () => {
|
onOK={async () => {
|
||||||
await window.DeckyPluginLoader.callServerMethod('uninstall_plugin', { name });
|
await uninstallPlugin(name);
|
||||||
// uninstalling a plugin resets the hidden setting for it server-side
|
// uninstalling a plugin resets the hidden setting for it server-side
|
||||||
// we invalidate here so if you re-install it, you won't have an out-of-date hidden filter
|
// we invalidate here so if you re-install it, you won't have an out-of-date hidden filter
|
||||||
await window.DeckyPluginLoader.hiddenPluginsService.invalidate();
|
await window.DeckyPluginLoader.hiddenPluginsService.invalidate();
|
||||||
|
|||||||
@@ -95,29 +95,20 @@ const sortOptions = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function getList(
|
const getList = window.DeckyBackend.callable<
|
||||||
|
[
|
||||||
path: string,
|
path: string,
|
||||||
includeFiles: boolean,
|
includeFiles?: boolean,
|
||||||
includeFolders: boolean = true,
|
includeFolders?: boolean,
|
||||||
includeExt: string[] | null = null,
|
includeExt?: string[] | null,
|
||||||
includeHidden: boolean = false,
|
includeHidden?: boolean,
|
||||||
orderBy: SortOptions = SortOptions.name_desc,
|
orderBy?: SortOptions,
|
||||||
filterFor: RegExp | ((file: File) => boolean) | null = null,
|
filterFor?: RegExp | ((file: File) => boolean) | null,
|
||||||
pageNumber: number = 1,
|
pageNumber?: number,
|
||||||
max: number = 1000,
|
max?: number,
|
||||||
): Promise<{ result: FileListing | string; success: boolean }> {
|
],
|
||||||
return window.DeckyPluginLoader.callServerMethod('filepicker_ls', {
|
FileListing
|
||||||
path,
|
>('utilities/filepicker_ls');
|
||||||
include_files: includeFiles,
|
|
||||||
include_folders: includeFolders,
|
|
||||||
include_ext: includeExt ? includeExt : [],
|
|
||||||
include_hidden: includeHidden,
|
|
||||||
order_by: orderBy,
|
|
||||||
filter_for: filterFor,
|
|
||||||
page: pageNumber,
|
|
||||||
max: max,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconStyles = {
|
const iconStyles = {
|
||||||
paddingRight: '10px',
|
paddingRight: '10px',
|
||||||
@@ -126,20 +117,20 @@ const iconStyles = {
|
|||||||
|
|
||||||
const FilePicker: FunctionComponent<FilePickerProps> = ({
|
const FilePicker: FunctionComponent<FilePickerProps> = ({
|
||||||
startPath,
|
startPath,
|
||||||
//What are we allowing to show in the file picker
|
// What are we allowing to show in the file picker
|
||||||
includeFiles = true,
|
includeFiles = true,
|
||||||
includeFolders = true,
|
includeFolders = true,
|
||||||
//Parameter for specifying a specific filename match
|
// Parameter for specifying a specific filename match
|
||||||
filter = undefined,
|
filter = undefined,
|
||||||
//Filter for specific extensions as an array
|
// Filter for specific extensions as an array
|
||||||
validFileExtensions = undefined,
|
validFileExtensions = undefined,
|
||||||
//Allow to override the fixed extension above
|
// Allow to override the fixed extension above
|
||||||
allowAllFiles = true,
|
allowAllFiles = true,
|
||||||
//If we need to show hidden files and folders (both Win and Linux should work)
|
// If we need to show hidden files and folders (both Win and Linux should work)
|
||||||
defaultHidden = false, // false by default makes sense for most users
|
defaultHidden = false, // false by default makes sense for most users
|
||||||
//How much files per page to show, default 1000
|
// How many files per page to show, default 1000
|
||||||
max = 1000,
|
max = 1000,
|
||||||
//Which picking option to select by default
|
// Which picking option to select by default
|
||||||
fileSelType = FileSelectionType.FOLDER,
|
fileSelType = FileSelectionType.FOLDER,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
closeModal,
|
closeModal,
|
||||||
@@ -190,6 +181,7 @@ const FilePicker: FunctionComponent<FilePickerProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
try {
|
||||||
const listing = await getList(
|
const listing = await getList(
|
||||||
path,
|
path,
|
||||||
includeFiles,
|
includeFiles,
|
||||||
@@ -201,10 +193,15 @@ const FilePicker: FunctionComponent<FilePickerProps> = ({
|
|||||||
page,
|
page,
|
||||||
max,
|
max,
|
||||||
);
|
);
|
||||||
if (!listing.success) {
|
setRawError(null);
|
||||||
|
setError(FileErrorTypes.None);
|
||||||
|
setFiles(listing.files);
|
||||||
|
setLoading(false);
|
||||||
|
setListing(listing);
|
||||||
|
logger.log('reloaded', path, listing);
|
||||||
|
} catch (theError: any) {
|
||||||
setListing({ files: [], realpath: path, total: 0 });
|
setListing({ files: [], realpath: path, total: 0 });
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
const theError = listing.result as string;
|
|
||||||
switch (theError) {
|
switch (theError) {
|
||||||
case theError.match(/\[Errno\s2.*/i)?.input:
|
case theError.match(/\[Errno\s2.*/i)?.input:
|
||||||
case theError.match(/\[WinError\s3.*/i)?.input:
|
case theError.match(/\[WinError\s3.*/i)?.input:
|
||||||
@@ -220,14 +217,7 @@ const FilePicker: FunctionComponent<FilePickerProps> = ({
|
|||||||
}
|
}
|
||||||
logger.debug(theError);
|
logger.debug(theError);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
setRawError(null);
|
|
||||||
setError(FileErrorTypes.None);
|
|
||||||
setFiles((listing.result as FileListing).files);
|
|
||||||
}
|
}
|
||||||
setLoading(false);
|
|
||||||
setListing(listing.result as FileListing);
|
|
||||||
logger.log('reloaded', path, listing);
|
|
||||||
})();
|
})();
|
||||||
}, [error, path, includeFiles, includeFolders, showHidden, sort, selectedExts, page]);
|
}, [error, path, includeFiles, includeFolders, showHidden, sort, selectedExts, page]);
|
||||||
|
|
||||||
|
|||||||
@@ -91,13 +91,16 @@ export default function DeveloperSettings() {
|
|||||||
>
|
>
|
||||||
<DialogButton
|
<DialogButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
let res = await window.DeckyPluginLoader.callServerMethod('get_tab_id', { name: 'SharedJSContext' });
|
try {
|
||||||
if (res.success) {
|
let tabId = await window.DeckyBackend.call<[name: string], string>(
|
||||||
Navigation.NavigateToExternalWeb(
|
'utilities/get_tab_id',
|
||||||
'localhost:8080/devtools/inspector.html?ws=localhost:8080/devtools/page/' + res.result,
|
'SharedJSContext',
|
||||||
);
|
);
|
||||||
} else {
|
Navigation.NavigateToExternalWeb(
|
||||||
console.error('Unable to find ID for SharedJSContext tab ', res.result);
|
'localhost:8080/devtools/inspector.html?ws=localhost:8080/devtools/page/' + tabId,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Unable to find ID for SharedJSContext tab ', e);
|
||||||
Navigation.NavigateToExternalWeb('localhost:8080');
|
Navigation.NavigateToExternalWeb('localhost:8080');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ export default function RemoteDebuggingSettings() {
|
|||||||
value={allowRemoteDebugging || false}
|
value={allowRemoteDebugging || false}
|
||||||
onChange={(toggleValue) => {
|
onChange={(toggleValue) => {
|
||||||
setAllowRemoteDebugging(toggleValue);
|
setAllowRemoteDebugging(toggleValue);
|
||||||
if (toggleValue) window.DeckyPluginLoader.callServerMethod('allow_remote_debugging');
|
if (toggleValue) window.DeckyBackend.call('allow_remote_debugging');
|
||||||
else window.DeckyPluginLoader.callServerMethod('disallow_remote_debugging');
|
else window.DeckyBackend.call('disallow_remote_debugging');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ export async function setShouldConnectToReactDevTools(enable: boolean) {
|
|||||||
icon: <FaReact />,
|
icon: <FaReact />,
|
||||||
});
|
});
|
||||||
await sleep(5000);
|
await sleep(5000);
|
||||||
return enable
|
return enable ? window.DeckyBackend.call('utilities/enable_rdt') : window.DeckyBackend.call('utilities/disable_rdt');
|
||||||
? window.DeckyPluginLoader.callServerMethod('enable_rdt')
|
|
||||||
: window.DeckyPluginLoader.callServerMethod('disable_rdt');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startup() {
|
export async function startup() {
|
||||||
|
|||||||
@@ -2,5 +2,11 @@
|
|||||||
(async () => {
|
(async () => {
|
||||||
console.debug('Setting up decky-frontend-lib...');
|
console.debug('Setting up decky-frontend-lib...');
|
||||||
window.DFL = await import('decky-frontend-lib');
|
window.DFL = await import('decky-frontend-lib');
|
||||||
|
console.debug('Authenticating to Decky backend...');
|
||||||
|
window.deckyAuthToken = await fetch('http://127.0.0.1:1337/auth/token').then((r) => r.text());
|
||||||
|
console.debug('Connecting to Decky backend...');
|
||||||
|
window.DeckyBackend = new (await import('./wsrouter')).WSRouter();
|
||||||
|
await window.DeckyBackend.connect();
|
||||||
|
console.debug('Starting Decky!');
|
||||||
await import('./start');
|
await import('./start');
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import Toaster from './toaster';
|
|||||||
import { VerInfo, callUpdaterMethod } from './updater';
|
import { VerInfo, callUpdaterMethod } from './updater';
|
||||||
import { getSetting, setSetting } from './utils/settings';
|
import { getSetting, setSetting } from './utils/settings';
|
||||||
import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
|
import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
|
||||||
import { WSRouter } from './wsrouter';
|
|
||||||
|
|
||||||
const StorePage = lazy(() => import('./components/store/Store'));
|
const StorePage = lazy(() => import('./components/store/Store'));
|
||||||
const SettingsPage = lazy(() => import('./components/settings'));
|
const SettingsPage = lazy(() => import('./components/settings'));
|
||||||
@@ -49,8 +48,6 @@ class PluginLoader extends Logger {
|
|||||||
public toaster: Toaster = new Toaster();
|
public toaster: Toaster = new Toaster();
|
||||||
private deckyState: DeckyState = new DeckyState();
|
private deckyState: DeckyState = new DeckyState();
|
||||||
|
|
||||||
public ws: WSRouter = new WSRouter();
|
|
||||||
|
|
||||||
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
||||||
public notificationService = new NotificationService(this.deckyState);
|
public notificationService = new NotificationService(this.deckyState);
|
||||||
|
|
||||||
@@ -105,15 +102,13 @@ class PluginLoader extends Logger {
|
|||||||
|
|
||||||
initFilepickerPatches();
|
initFilepickerPatches();
|
||||||
|
|
||||||
this.ws.connect().then(() => {
|
|
||||||
this.getUserInfo();
|
this.getUserInfo();
|
||||||
|
|
||||||
this.updateVersion();
|
this.updateVersion();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUserInfo() {
|
public async getUserInfo() {
|
||||||
const userInfo = (await this.callServerMethod('get_user_info')).result as UserInfo;
|
const userInfo = await window.DeckyBackend.call<[], UserInfo>('utilities/get_user_info');
|
||||||
setSetting('user_info.user_name', userInfo.username);
|
setSetting('user_info.user_name', userInfo.username);
|
||||||
setSetting('user_info.user_home', userInfo.path);
|
setSetting('user_info.user_home', userInfo.path);
|
||||||
}
|
}
|
||||||
@@ -183,8 +178,8 @@ class PluginLoader extends Logger {
|
|||||||
version={version}
|
version={version}
|
||||||
hash={hash}
|
hash={hash}
|
||||||
installType={install_type}
|
installType={install_type}
|
||||||
onOK={() => this.callServerMethod('confirm_plugin_install', { request_id })}
|
onOK={() => window.DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
|
||||||
onCancel={() => this.callServerMethod('cancel_plugin_install', { request_id })}
|
onCancel={() => window.DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -196,8 +191,8 @@ class PluginLoader extends Logger {
|
|||||||
showModal(
|
showModal(
|
||||||
<MultiplePluginsInstallModal
|
<MultiplePluginsInstallModal
|
||||||
requests={requests}
|
requests={requests}
|
||||||
onOK={() => this.callServerMethod('confirm_plugin_install', { request_id })}
|
onOK={() => window.DeckyBackend.call<[string]>('utilities/confirm_plugin_install', request_id)}
|
||||||
onCancel={() => this.callServerMethod('cancel_plugin_install', { request_id })}
|
onCancel={() => window.DeckyBackend.call<[string]>('utilities/cancel_plugin_install', request_id)}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -360,6 +355,7 @@ class PluginLoader extends Logger {
|
|||||||
selectFiles?: boolean,
|
selectFiles?: boolean,
|
||||||
regex?: RegExp,
|
regex?: RegExp,
|
||||||
): Promise<{ path: string; realpath: string }> {
|
): Promise<{ path: string; realpath: string }> {
|
||||||
|
console.warn('openFilePicker is deprecated and will be removed. Please migrate to openFilePickerV2');
|
||||||
if (selectFiles) {
|
if (selectFiles) {
|
||||||
return this.openFilePickerV2(FileSelectionType.FILE, startPath, true, true, regex);
|
return this.openFilePickerV2(FileSelectionType.FILE, startPath, true, true, regex);
|
||||||
} else {
|
} else {
|
||||||
@@ -443,18 +439,10 @@ class PluginLoader extends Logger {
|
|||||||
code,
|
code,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
injectCssIntoTab(tab: string, style: string) {
|
injectCssIntoTab: window.DeckyBackend.callable<[tab: string, style: string], string>(
|
||||||
return this.callServerMethod('inject_css_into_tab', {
|
'utilities/inject_css_into_tab',
|
||||||
tab,
|
),
|
||||||
style,
|
removeCssFromTab: window.DeckyBackend.callable<[tab: string, cssId: string]>('utilities/remove_css_from_tab'),
|
||||||
});
|
|
||||||
},
|
|
||||||
removeCssFromTab(tab: string, cssId: any) {
|
|
||||||
return this.callServerMethod('remove_css_from_tab', {
|
|
||||||
tab,
|
|
||||||
css_id: cssId,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,27 @@ export enum InstallType {
|
|||||||
REINSTALL,
|
REINSTALL,
|
||||||
UPDATE,
|
UPDATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type installPluginArgs = [
|
||||||
|
artifact: string,
|
||||||
|
name?: string,
|
||||||
|
version?: string,
|
||||||
|
hash?: string | boolean,
|
||||||
|
installType?: InstallType,
|
||||||
|
];
|
||||||
|
|
||||||
|
export let installPlugin = window.DeckyBackend.callable<installPluginArgs>('utilities/install_plugin');
|
||||||
|
|
||||||
|
type installPluginsArgs = [
|
||||||
|
requests: {
|
||||||
|
artifact: string;
|
||||||
|
name?: string;
|
||||||
|
version?: string;
|
||||||
|
hash?: string | boolean;
|
||||||
|
installType?: InstallType;
|
||||||
|
}[],
|
||||||
|
];
|
||||||
|
|
||||||
|
export let installPlugins = window.DeckyBackend.callable<installPluginsArgs>('utilities/install_plugins');
|
||||||
|
|
||||||
|
export let uninstallPlugin = window.DeckyBackend.callable<[name: string]>('utilities/uninstall_plugin');
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
window.deckyAuthToken = await fetch('http://127.0.0.1:1337/auth/token').then((r) => r.text());
|
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(Backend)
|
.use(Backend)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
|
|||||||
+6
-15
@@ -1,4 +1,4 @@
|
|||||||
import { InstallType, Plugin } from './plugin';
|
import { InstallType, Plugin, installPlugin, installPlugins } from './plugin';
|
||||||
import { getSetting, setSetting } from './utils/settings';
|
import { getSetting, setSetting } from './utils/settings';
|
||||||
|
|
||||||
export enum Store {
|
export enum Store {
|
||||||
@@ -92,33 +92,24 @@ export async function getPluginList(): Promise<StorePlugin[]> {
|
|||||||
|
|
||||||
export async function installFromURL(url: string) {
|
export async function installFromURL(url: string) {
|
||||||
const splitURL = url.split('/');
|
const splitURL = url.split('/');
|
||||||
await window.DeckyPluginLoader.callServerMethod('install_plugin', {
|
await installPlugin(url, splitURL[splitURL.length - 1].replace('.zip', ''));
|
||||||
name: splitURL[splitURL.length - 1].replace('.zip', ''),
|
|
||||||
artifact: url,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion, installType: InstallType) {
|
export async function requestPluginInstall(plugin: string, selectedVer: StorePluginVersion, installType: InstallType) {
|
||||||
const artifactUrl = selectedVer.artifact ?? pluginUrl(selectedVer.hash);
|
const artifactUrl = selectedVer.artifact ?? pluginUrl(selectedVer.hash);
|
||||||
await window.DeckyPluginLoader.callServerMethod('install_plugin', {
|
await installPlugin(artifactUrl, plugin, selectedVer.name, selectedVer.hash, installType);
|
||||||
name: plugin,
|
|
||||||
artifact: artifactUrl,
|
|
||||||
version: selectedVer.name,
|
|
||||||
hash: selectedVer.hash,
|
|
||||||
install_type: installType,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestMultiplePluginInstalls(requests: PluginInstallRequest[]) {
|
export async function requestMultiplePluginInstalls(requests: PluginInstallRequest[]) {
|
||||||
await window.DeckyPluginLoader.callServerMethod('install_plugins', {
|
await installPlugins(
|
||||||
requests: requests.map(({ plugin, installType, selectedVer }) => ({
|
requests.map(({ plugin, installType, selectedVer }) => ({
|
||||||
name: plugin,
|
name: plugin,
|
||||||
artifact: selectedVer.artifact ?? pluginUrl(selectedVer.hash),
|
artifact: selectedVer.artifact ?? pluginUrl(selectedVer.hash),
|
||||||
version: selectedVer.name,
|
version: selectedVer.name,
|
||||||
hash: selectedVer.hash,
|
hash: selectedVer.hash,
|
||||||
install_type: installType,
|
install_type: installType,
|
||||||
})),
|
})),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkForUpdates(plugins: Plugin[]): Promise<PluginUpdateMapping> {
|
export async function checkForUpdates(plugins: Plugin[]): Promise<PluginUpdateMapping> {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export async function getSetting<T>(key: string, def: T): Promise<T> {
|
export async function getSetting<T>(key: string, def: T): Promise<T> {
|
||||||
const res = await window.DeckyPluginLoader.ws.call<[string, T], T>('utilities/settings/get', key, def);
|
const res = await window.DeckyBackend.call<[string, T], T>('utilities/settings/get', key, def);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setSetting<T>(key: string, value: T): Promise<void> {
|
export async function setSetting<T>(key: string, value: T): Promise<void> {
|
||||||
await window.DeckyPluginLoader.ws.call<[string, T], void>('utilities/settings/set', key, value);
|
await window.DeckyBackend.call<[string, T], void>('utilities/settings/set', key, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
DeckyBackend: WSRouter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
// Call-reply
|
// Call-reply
|
||||||
CALL,
|
CALL,
|
||||||
@@ -94,7 +100,6 @@ export class WSRouter extends Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onMessage(msg: MessageEvent) {
|
async onMessage(msg: MessageEvent) {
|
||||||
this.debug('WS Message', msg);
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(msg.data) as Message;
|
const data = JSON.parse(msg.data) as Message;
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
@@ -108,7 +113,7 @@ export class WSRouter extends Logger {
|
|||||||
await this.write({ type: MessageType.ERROR, id: data.id, error: (e as Error)?.stack || e });
|
await this.write({ type: MessageType.ERROR, id: data.id, error: (e as Error)?.stack || e });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.write({ type: MessageType.ERROR, id: data.id, error: 'Route does not exist.' });
|
await this.write({ type: MessageType.ERROR, id: data.id, error: `Route ${data.route} does not exist.` });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -152,6 +157,10 @@ export class WSRouter extends Logger {
|
|||||||
return resolver.promise;
|
return resolver.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callable<Args extends any[] = any[], Return = void>(route: string): (...args: Args) => Promise<Return> {
|
||||||
|
return (...args) => this.call<Args, Return>(route, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
async onError(error: any) {
|
async onError(error: any) {
|
||||||
this.error('WS DISCONNECTED', error);
|
this.error('WS DISCONNECTED', error);
|
||||||
await this.connect();
|
await this.connect();
|
||||||
|
|||||||
Reference in New Issue
Block a user