Migrate most of frontend callServerMethod usage over to websocket

This commit is contained in:
AAGaming
2023-08-05 01:11:43 -04:00
committed by marios8543
parent cfb6fe69e3
commit 34d1a34b10
14 changed files with 162 additions and 160 deletions
+25 -33
View File
@@ -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):
+3 -2
View File
@@ -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>
+1 -3
View File
@@ -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() {
+6
View File
@@ -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');
})(); })();
+10 -22
View File
@@ -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,
});
},
}; };
} }
} }
+24
View File
@@ -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');
-2
View File
@@ -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
View File
@@ -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> {
+2 -2
View File
@@ -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);
} }
+11 -2
View File
@@ -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();