Auditing async usage in frontend code (#1402)

I found a lot of places where asyncs weren't being properly wrapped or
awaited
This commit is contained in:
Evan Simkowitz 2024-12-05 21:09:54 -05:00 committed by GitHub
parent 297e2b627f
commit 7a61f25331
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 194 additions and 181 deletions

View File

@ -165,7 +165,7 @@ export class WaveBrowserWindow extends BaseWindow {
}
focusedWaveWindow = this;
console.log("focus win", this.waveWindowId);
fireAndForget(async () => await ClientService.FocusWindow(this.waveWindowId));
fireAndForget(() => ClientService.FocusWindow(this.waveWindowId));
setWasInFg(true);
setWasActive(true);
});
@ -235,7 +235,7 @@ export class WaveBrowserWindow extends BaseWindow {
}
if (this.deleteAllowed) {
console.log("win removing window from backend DB", this.waveWindowId);
fireAndForget(async () => await WindowService.CloseWindow(this.waveWindowId, true));
fireAndForget(() => WindowService.CloseWindow(this.waveWindowId, true));
}
for (const tabView of this.allTabViews.values()) {
tabView?.destroy();

View File

@ -368,7 +368,7 @@ electron.ipcMain.on("quicklook", (event, filePath: string) => {
electron.ipcMain.on("open-native-path", (event, filePath: string) => {
console.log("open-native-path", filePath);
fireAndForget(async () =>
fireAndForget(() =>
electron.shell.openPath(filePath).then((excuse) => {
if (excuse) console.error(`Failed to open ${filePath} in native application: ${excuse}`);
})

View File

@ -96,7 +96,7 @@ export class Updater {
body: "A new version of Wave Terminal is ready to install.",
});
updateNotification.on("click", () => {
fireAndForget(() => this.promptToInstallUpdate());
fireAndForget(this.promptToInstallUpdate.bind(this));
});
updateNotification.show();
});
@ -188,7 +188,7 @@ export class Updater {
if (allWindows.length > 0) {
await dialog.showMessageBox(focusedWaveWindow ?? allWindows[0], dialogOpts).then(({ response }) => {
if (response === 0) {
fireAndForget(async () => this.installUpdate());
fireAndForget(this.installUpdate.bind(this));
}
});
}
@ -210,7 +210,7 @@ export function getResolvedUpdateChannel(): string {
return isDev() ? "dev" : (autoUpdater.channel ?? "latest");
}
ipcMain.on("install-app-update", () => fireAndForget(() => updater?.promptToInstallUpdate()));
ipcMain.on("install-app-update", () => fireAndForget(updater?.promptToInstallUpdate.bind(updater)));
ipcMain.on("get-app-update-status", (event) => {
event.returnValue = updater?.status;
});

View File

@ -12,6 +12,7 @@ import { FlexiModal } from "./modal";
import { QuickTips } from "@/app/element/quicktips";
import { atoms, getApi } from "@/app/store/global";
import { modalsModel } from "@/app/store/modalmodel";
import { fireAndForget } from "@/util/util";
import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
import "./tos.scss";
@ -20,25 +21,22 @@ const pageNumAtom: PrimitiveAtom<number> = atom<number>(1);
const ModalPage1 = () => {
const settings = useAtomValue(atoms.settingsAtom);
const clientData = useAtomValue(atoms.client);
const [tosOpen, setTosOpen] = useAtom(modalsModel.tosOpen);
const [telemetryEnabled, setTelemetryEnabled] = useState<boolean>(!!settings["telemetry:enabled"]);
const setPageNum = useSetAtom(pageNumAtom);
const acceptTos = () => {
if (!clientData.tosagreed) {
services.ClientService.AgreeTos();
fireAndForget(services.ClientService.AgreeTos);
}
setPageNum(2);
};
const setTelemetry = (value: boolean) => {
services.ClientService.TelemetryUpdate(value)
.then(() => {
fireAndForget(() =>
services.ClientService.TelemetryUpdate(value).then(() => {
setTelemetryEnabled(value);
})
.catch((error) => {
console.error("failed to set telemetry:", error);
});
);
};
const label = telemetryEnabled ? "Telemetry Enabled" : "Telemetry Disabled";

View File

@ -5,9 +5,9 @@ import { Modal } from "@/app/modals/modal";
import { Markdown } from "@/element/markdown";
import { modalsModel } from "@/store/modalmodel";
import * as keyutil from "@/util/keyutil";
import { UserInputService } from "../store/services";
import { fireAndForget } from "@/util/util";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { UserInputService } from "../store/services";
import "./userinputmodal.scss";
const UserInputModal = (userInputRequest: UserInputRequest) => {
@ -16,33 +16,39 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
const checkboxRef = useRef<HTMLInputElement>();
const handleSendErrResponse = useCallback(() => {
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
errormsg: "Canceled by the user",
});
fireAndForget(() =>
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
errormsg: "Canceled by the user",
})
);
modalsModel.popModal();
}, [responseText, userInputRequest]);
const handleSendText = useCallback(() => {
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
text: responseText,
checkboxstat: checkboxRef?.current?.checked ?? false,
});
fireAndForget(() =>
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
text: responseText,
checkboxstat: checkboxRef?.current?.checked ?? false,
})
);
modalsModel.popModal();
}, [responseText, userInputRequest]);
console.log("bar");
const handleSendConfirm = useCallback(
(response: boolean) => {
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
confirm: response,
checkboxstat: checkboxRef?.current?.checked ?? false,
});
fireAndForget(() =>
UserInputService.SendUserInputResponse({
type: "userinputresp",
requestid: userInputRequest.requestid,
confirm: response,
checkboxstat: checkboxRef?.current?.checked ?? false,
})
);
modalsModel.popModal();
},
[userInputRequest]

View File

@ -19,6 +19,7 @@ import {
} from "@/layout/index";
import { getLayoutModelForStaticTab } from "@/layout/lib/layoutModelHooks";
import * as keyutil from "@/util/keyutil";
import { fireAndForget } from "@/util/util";
import * as jotai from "jotai";
const simpleControlShiftAtom = jotai.atom(false);
@ -83,7 +84,7 @@ function genericClose(tabId: string) {
return;
}
const layoutModel = getLayoutModelForTab(tabAtom);
layoutModel.closeFocusedNode();
fireAndForget(layoutModel.closeFocusedNode.bind(layoutModel));
}
function switchBlockByBlockNum(index: number) {

View File

@ -6,6 +6,7 @@
import { waveEventSubscribe } from "@/app/store/wps";
import { getWebServerEndpoint } from "@/util/endpoints";
import { fetch } from "@/util/fetchutil";
import { fireAndForget } from "@/util/util";
import { atom, Atom, Getter, PrimitiveAtom, Setter, useAtomValue } from "jotai";
import { useEffect } from "react";
import { globalStore } from "./jotaiStore";
@ -301,7 +302,7 @@ function setObjectValue<T extends WaveObj>(value: T, setFn?: Setter, pushToServe
}
setFn(wov.dataAtom, { value: value, loading: false });
if (pushToServer) {
ObjectService.UpdateObject(value, false);
fireAndForget(() => ObjectService.UpdateObject(value, false));
}
}

View File

@ -6,6 +6,7 @@ import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";
import { Button } from "@/element/button";
import { ContextMenuModel } from "@/store/contextmenu";
import { fireAndForget } from "@/util/util";
import { clsx } from "clsx";
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
import { ObjectService } from "../store/services";
@ -72,14 +73,21 @@ const Tab = memo(
};
}, []);
const handleRenameTab = (event) => {
const selectEditableText = useCallback(() => {
if (editableRef.current) {
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(editableRef.current);
selection.removeAllRanges();
selection.addRange(range);
}
}, []);
const handleRenameTab: React.MouseEventHandler<HTMLDivElement> = (event) => {
event?.stopPropagation();
setIsEditable(true);
editableTimeoutRef.current = setTimeout(() => {
if (editableRef.current) {
editableRef.current.focus();
document.execCommand("selectAll", false);
}
selectEditableText();
}, 0);
};
@ -88,20 +96,14 @@ const Tab = memo(
newText = newText || originalName;
editableRef.current.innerText = newText;
setIsEditable(false);
ObjectService.UpdateTabName(id, newText);
fireAndForget(() => ObjectService.UpdateTabName(id, newText));
setTimeout(() => refocusNode(null), 10);
};
const handleKeyDown = (event) => {
const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
if ((event.metaKey || event.ctrlKey) && event.key === "a") {
event.preventDefault();
if (editableRef.current) {
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(editableRef.current);
selection.removeAllRanges();
selection.addRange(range);
}
selectEditableText();
return;
}
// this counts glyphs, not characters
@ -150,7 +152,10 @@ const Tab = memo(
let menu: ContextMenuItem[] = [
{ label: isPinned ? "Unpin Tab" : "Pin Tab", click: () => onPinChange() },
{ label: "Rename Tab", click: () => handleRenameTab(null) },
{ label: "Copy TabId", click: () => navigator.clipboard.writeText(id) },
{
label: "Copy TabId",
click: () => fireAndForget(() => navigator.clipboard.writeText(id)),
},
{ type: "separator" },
];
const fullConfig = globalStore.get(atoms.fullConfigAtom);
@ -175,10 +180,11 @@ const Tab = memo(
}
submenu.push({
label: preset["display:name"] ?? presetName,
click: () => {
ObjectService.UpdateObjectMeta(oref, preset);
RpcApi.ActivityCommand(TabRpcClient, { settabtheme: 1 });
},
click: () =>
fireAndForget(async () => {
await ObjectService.UpdateObjectMeta(oref, preset);
await RpcApi.ActivityCommand(TabRpcClient, { settabtheme: 1 });
}),
});
}
menu.push({ label: "Backgrounds", type: "submenu", submenu }, { type: "separator" });

View File

@ -467,13 +467,12 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
// Reset dragging state
setDraggingTab(null);
// Update workspace tab ids
fireAndForget(
async () =>
await WorkspaceService.UpdateTabIds(
workspace.oid,
tabIds.slice(pinnedTabCount),
tabIds.slice(0, pinnedTabCount)
)
fireAndForget(() =>
WorkspaceService.UpdateTabIds(
workspace.oid,
tabIds.slice(pinnedTabCount),
tabIds.slice(0, pinnedTabCount)
)
);
}),
[]
@ -579,9 +578,7 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
const handlePinChange = useCallback(
(tabId: string, pinned: boolean) => {
console.log("handlePinChange", tabId, pinned);
fireAndForget(async () => {
await WorkspaceService.ChangeTabPinning(workspace.oid, tabId, pinned);
});
fireAndForget(() => WorkspaceService.ChangeTabPinning(workspace.oid, tabId, pinned));
},
[workspace]
);

View File

@ -189,12 +189,10 @@ const WorkspaceSwitcher = () => {
}, []);
const onDeleteWorkspace = useCallback((workspaceId: string) => {
fireAndForget(async () => {
getApi().deleteWorkspace(workspaceId);
setTimeout(() => {
fireAndForget(updateWorkspaceList);
}, 10);
});
getApi().deleteWorkspace(workspaceId);
setTimeout(() => {
fireAndForget(updateWorkspaceList);
}, 10);
}, []);
const isActiveWorkspaceSaved = !!(activeWorkspace.name && activeWorkspace.icon);
@ -267,12 +265,10 @@ const WorkspaceSwitcherItem = ({
const isCurrentWorkspace = activeWorkspace.oid === workspace.oid;
const setWorkspace = useCallback((newWorkspace: Workspace) => {
fireAndForget(async () => {
if (newWorkspace.name != "") {
setObjectValue({ ...newWorkspace, otype: "workspace" }, undefined, true);
}
setWorkspaceEntry({ ...workspaceEntry, workspace: newWorkspace });
});
if (newWorkspace.name != "") {
setObjectValue({ ...newWorkspace, otype: "workspace" }, undefined, true);
}
setWorkspaceEntry({ ...workspaceEntry, workspace: newWorkspace });
}, []);
const isActive = !!workspaceEntry.windowId;

View File

@ -543,26 +543,26 @@ function TableBody({
},
{
label: "Copy File Name",
click: () => navigator.clipboard.writeText(fileName),
click: () => fireAndForget(() => navigator.clipboard.writeText(fileName)),
},
{
label: "Copy Full File Name",
click: () => navigator.clipboard.writeText(finfo.path),
click: () => fireAndForget(() => navigator.clipboard.writeText(finfo.path)),
},
{
label: "Copy File Name (Shell Quoted)",
click: () => navigator.clipboard.writeText(shellQuote([fileName])),
click: () => fireAndForget(() => navigator.clipboard.writeText(shellQuote([fileName]))),
},
{
label: "Copy Full File Name (Shell Quoted)",
click: () => navigator.clipboard.writeText(shellQuote([finfo.path])),
click: () => fireAndForget(() => navigator.clipboard.writeText(shellQuote([finfo.path]))),
},
{
type: "separator",
},
{
label: "Download File",
click: async () => {
click: () => {
getApi().downloadFile(normPath);
},
},
@ -572,7 +572,7 @@ function TableBody({
// TODO: Only show this option for local files, resolve correct host path if connection is WSL
{
label: openNativeLabel,
click: async () => {
click: () => {
getApi().openNativePath(normPath);
},
},
@ -581,30 +581,32 @@ function TableBody({
},
{
label: "Open Preview in New Block",
click: async () => {
const blockDef: BlockDef = {
meta: {
view: "preview",
file: finfo.path,
},
};
await createBlock(blockDef);
},
click: () =>
fireAndForget(async () => {
const blockDef: BlockDef = {
meta: {
view: "preview",
file: finfo.path,
},
};
await createBlock(blockDef);
}),
},
];
if (finfo.mimetype == "directory") {
menu.push({
label: "Open Terminal in New Block",
click: async () => {
const termBlockDef: BlockDef = {
meta: {
controller: "shell",
view: "term",
"cmd:cwd": finfo.path,
},
};
await createBlock(termBlockDef);
},
click: () =>
fireAndForget(async () => {
const termBlockDef: BlockDef = {
meta: {
controller: "shell",
view: "term",
"cmd:cwd": finfo.path,
},
};
await createBlock(termBlockDef);
}),
});
}
menu.push(
@ -613,9 +615,11 @@ function TableBody({
},
{
label: "Delete",
click: async () => {
await FileService.DeleteFile(conn, finfo.path).catch((e) => console.log(e));
setRefreshVersion((current) => current + 1);
click: () => {
fireAndForget(async () => {
await FileService.DeleteFile(conn, finfo.path).catch((e) => console.log(e));
setRefreshVersion((current) => current + 1);
});
},
}
);

View File

@ -24,7 +24,7 @@ import * as WOS from "@/store/wos";
import { getWebServerEndpoint } from "@/util/endpoints";
import { goHistory, goHistoryBack, goHistoryForward } from "@/util/historyutil";
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed, keydownWrapper } from "@/util/keyutil";
import { base64ToString, isBlank, jotaiLoadableValue, makeConnRoute, stringToBase64 } from "@/util/util";
import { base64ToString, fireAndForget, isBlank, jotaiLoadableValue, makeConnRoute, stringToBase64 } from "@/util/util";
import { Monaco } from "@monaco-editor/react";
import clsx from "clsx";
import { Atom, atom, Getter, PrimitiveAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
@ -257,7 +257,7 @@ export class PreviewModel implements ViewModel {
className: clsx(
`${saveClassName} warning border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500`
),
onClick: this.handleFileSave.bind(this),
onClick: () => fireAndForget(this.handleFileSave.bind(this)),
});
if (get(this.canPreview)) {
viewTextChildren.push({
@ -265,7 +265,7 @@ export class PreviewModel implements ViewModel {
text: "Preview",
className:
"grey border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500",
onClick: () => this.setEditMode(false),
onClick: () => fireAndForget(() => this.setEditMode(false)),
});
}
} else if (get(this.canPreview)) {
@ -274,7 +274,7 @@ export class PreviewModel implements ViewModel {
text: "Edit",
className:
"grey border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500",
onClick: () => this.setEditMode(true),
onClick: () => fireAndForget(() => this.setEditMode(true)),
});
}
return [
@ -497,7 +497,7 @@ export class PreviewModel implements ViewModel {
return;
}
const blockOref = WOS.makeORef("block", this.blockId);
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
await services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
// Clear the saved file buffers
globalStore.set(this.fileContentSaved, null);
@ -538,7 +538,7 @@ export class PreviewModel implements ViewModel {
}
console.log(newFileInfo.path);
this.updateOpenFileModalAndError(false);
this.goHistory(newFileInfo.path);
await this.goHistory(newFileInfo.path);
refocusNode(this.blockId);
} catch (e) {
globalStore.set(this.openFileError, e.message);
@ -546,7 +546,7 @@ export class PreviewModel implements ViewModel {
}
}
goHistoryBack() {
async goHistoryBack() {
const blockMeta = globalStore.get(this.blockAtom)?.meta;
const curPath = globalStore.get(this.metaFilePath);
const updateMeta = goHistoryBack("file", curPath, blockMeta, true);
@ -555,10 +555,10 @@ export class PreviewModel implements ViewModel {
}
updateMeta.edit = false;
const blockOref = WOS.makeORef("block", this.blockId);
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
await services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
}
goHistoryForward() {
async goHistoryForward() {
const blockMeta = globalStore.get(this.blockAtom)?.meta;
const curPath = globalStore.get(this.metaFilePath);
const updateMeta = goHistoryForward("file", curPath, blockMeta);
@ -567,13 +567,13 @@ export class PreviewModel implements ViewModel {
}
updateMeta.edit = false;
const blockOref = WOS.makeORef("block", this.blockId);
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
await services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
}
setEditMode(edit: boolean) {
async setEditMode(edit: boolean) {
const blockMeta = globalStore.get(this.blockAtom)?.meta;
const blockOref = WOS.makeORef("block", this.blockId);
services.ObjectService.UpdateObjectMeta(blockOref, { ...blockMeta, edit });
await services.ObjectService.UpdateObjectMeta(blockOref, { ...blockMeta, edit });
}
async handleFileSave() {
@ -588,7 +588,7 @@ export class PreviewModel implements ViewModel {
}
const conn = globalStore.get(this.connection) ?? "";
try {
services.FileService.SaveFile(conn, filePath, stringToBase64(newFileContent));
await services.FileService.SaveFile(conn, filePath, stringToBase64(newFileContent));
globalStore.set(this.fileContent, newFileContent);
globalStore.set(this.newFileContent, null);
console.log("saved file", filePath);
@ -630,42 +630,44 @@ export class PreviewModel implements ViewModel {
getSettingsMenuItems(): ContextMenuItem[] {
const menuItems: ContextMenuItem[] = [];
const blockData = globalStore.get(this.blockAtom);
menuItems.push({
label: "Copy Full Path",
click: async () => {
const filePath = await globalStore.get(this.normFilePath);
if (filePath == null) {
return;
}
navigator.clipboard.writeText(filePath);
},
click: () =>
fireAndForget(async () => {
const filePath = await globalStore.get(this.normFilePath);
if (filePath == null) {
return;
}
await navigator.clipboard.writeText(filePath);
}),
});
menuItems.push({
label: "Copy File Name",
click: async () => {
const fileInfo = await globalStore.get(this.statFile);
if (fileInfo == null || fileInfo.name == null) {
return;
}
navigator.clipboard.writeText(fileInfo.name);
},
click: () =>
fireAndForget(async () => {
const fileInfo = await globalStore.get(this.statFile);
if (fileInfo == null || fileInfo.name == null) {
return;
}
await navigator.clipboard.writeText(fileInfo.name);
}),
});
const mimeType = jotaiLoadableValue(globalStore.get(this.fileMimeTypeLoadable), "");
if (mimeType == "directory") {
menuItems.push({
label: "Open Terminal in New Block",
click: async () => {
const fileInfo = await globalStore.get(this.statFile);
const termBlockDef: BlockDef = {
meta: {
view: "term",
controller: "shell",
"cmd:cwd": fileInfo.dir,
},
};
await createBlock(termBlockDef);
},
click: () =>
fireAndForget(async () => {
const fileInfo = await globalStore.get(this.statFile);
const termBlockDef: BlockDef = {
meta: {
view: "term",
controller: "shell",
"cmd:cwd": fileInfo.dir,
},
};
await createBlock(termBlockDef);
}),
});
}
const loadableSV = globalStore.get(this.loadableSpecializedView);
@ -677,11 +679,11 @@ export class PreviewModel implements ViewModel {
menuItems.push({ type: "separator" });
menuItems.push({
label: "Save File",
click: this.handleFileSave.bind(this),
click: () => fireAndForget(this.handleFileSave.bind(this)),
});
menuItems.push({
label: "Revert File",
click: this.handleFileRevert.bind(this),
click: () => fireAndForget(this.handleFileRevert.bind(this)),
});
}
menuItems.push({ type: "separator" });
@ -689,12 +691,13 @@ export class PreviewModel implements ViewModel {
label: "Word Wrap",
type: "checkbox",
checked: wordWrap,
click: () => {
const blockOref = WOS.makeORef("block", this.blockId);
services.ObjectService.UpdateObjectMeta(blockOref, {
"editor:wordwrap": !wordWrap,
});
},
click: () =>
fireAndForget(async () => {
const blockOref = WOS.makeORef("block", this.blockId);
await services.ObjectService.UpdateObjectMeta(blockOref, {
"editor:wordwrap": !wordWrap,
});
}),
});
}
}
@ -716,16 +719,16 @@ export class PreviewModel implements ViewModel {
keyDownHandler(e: WaveKeyboardEvent): boolean {
if (checkKeyPressed(e, "Cmd:ArrowLeft")) {
this.goHistoryBack();
fireAndForget(this.goHistoryBack.bind(this));
return true;
}
if (checkKeyPressed(e, "Cmd:ArrowRight")) {
this.goHistoryForward();
fireAndForget(this.goHistoryForward.bind(this));
return true;
}
if (checkKeyPressed(e, "Cmd:ArrowUp")) {
// handle up directory
this.goParentDirectory({});
fireAndForget(() => this.goParentDirectory({}));
return true;
}
const openModalOpen = globalStore.get(this.openFileModal);
@ -739,7 +742,7 @@ export class PreviewModel implements ViewModel {
if (canPreview) {
if (checkKeyPressed(e, "Cmd:e")) {
const editMode = globalStore.get(this.editMode);
this.setEditMode(!editMode);
fireAndForget(() => this.setEditMode(!editMode));
return true;
}
}
@ -833,15 +836,15 @@ function CodeEditPreview({ model }: SpecializedViewProps) {
function codeEditKeyDownHandler(e: WaveKeyboardEvent): boolean {
if (checkKeyPressed(e, "Cmd:e")) {
model.setEditMode(false);
fireAndForget(() => model.setEditMode(false));
return true;
}
if (checkKeyPressed(e, "Cmd:s") || checkKeyPressed(e, "Ctrl:s")) {
model.handleFileSave();
fireAndForget(model.handleFileSave.bind(model));
return true;
}
if (checkKeyPressed(e, "Cmd:r")) {
model.handleFileRevert();
fireAndForget(model.handleFileRevert.bind(model));
return true;
}
return false;
@ -990,7 +993,7 @@ const OpenFileModal = memo(
const handleCommandOperations = async () => {
if (checkKeyPressed(waveEvent, "Enter")) {
model.handleOpenFile(filePath);
await model.handleOpenFile(filePath);
return true;
}
return false;

View File

@ -119,7 +119,11 @@ export class TermWrap {
data = data.substring(nextSlashIdx);
}
setTimeout(() => {
services.ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), { "cmd:cwd": data });
fireAndForget(() =>
services.ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), {
"cmd:cwd": data,
})
);
}, 0);
return true;
});
@ -284,7 +288,9 @@ export class TermWrap {
const serializedOutput = this.serializeAddon.serialize();
const termSize: TermSize = { rows: this.terminal.rows, cols: this.terminal.cols };
console.log("idle timeout term", this.dataBytesProcessed, serializedOutput.length, termSize);
services.BlockService.SaveTerminalState(this.blockId, serializedOutput, "full", this.ptyOffset, termSize);
fireAndForget(() =>
services.BlockService.SaveTerminalState(this.blockId, serializedOutput, "full", this.ptyOffset, termSize)
);
this.dataBytesProcessed = 0;
}

View File

@ -221,12 +221,12 @@ export class WaveAiModel implements ViewModel {
({
label: preset[1]["display:name"],
onClick: () =>
fireAndForget(async () => {
await ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), {
fireAndForget(() =>
ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), {
...preset[1],
"ai:preset": preset[0],
});
}),
})
),
}) as MenuItem
);
dropdownItems.push({
@ -386,7 +386,7 @@ export class WaveAiModel implements ViewModel {
this.setLocked(false);
this.cancel = false;
};
handleAiStreamingResponse();
fireAndForget(handleAiStreamingResponse);
}
useWaveAi() {
@ -404,14 +404,14 @@ export class WaveAiModel implements ViewModel {
keyDownHandler(waveEvent: WaveKeyboardEvent): boolean {
if (checkKeyPressed(waveEvent, "Cmd:l")) {
this.clearMessages();
fireAndForget(this.clearMessages.bind(this));
return true;
}
return false;
}
}
function makeWaveAiViewModel(blockId): WaveAiModel {
function makeWaveAiViewModel(blockId: string): WaveAiModel {
const waveAiModel = new WaveAiModel(blockId);
return waveAiModel;
}
@ -634,7 +634,7 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
// a weird workaround to initialize ansynchronously
useEffect(() => {
model.populateMessages();
fireAndForget(model.populateMessages.bind(model));
}, []);
const handleTextAreaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {

View File

@ -293,7 +293,7 @@ export class WebViewModel implements ViewModel {
* @param url The URL that has been navigated to.
*/
handleNavigate(url: string) {
ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), { url });
fireAndForget(() => ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), { url }));
globalStore.set(this.url, url);
}
@ -432,22 +432,18 @@ export class WebViewModel implements ViewModel {
return [
{
label: "Set Block Homepage",
click: async () => {
await this.setHomepageUrl(this.getUrl(), "block");
},
click: () => fireAndForget(() => this.setHomepageUrl(this.getUrl(), "block")),
},
{
label: "Set Default Homepage",
click: async () => {
await this.setHomepageUrl(this.getUrl(), "global");
},
click: () => fireAndForget(() => this.setHomepageUrl(this.getUrl(), "global")),
},
{
type: "separator",
},
{
label: this.webviewRef.current?.isDevToolsOpened() ? "Close DevTools" : "Open DevTools",
click: async () => {
click: () => {
if (this.webviewRef.current) {
if (this.webviewRef.current.isDevToolsOpened()) {
this.webviewRef.current.closeDevTools();

View File

@ -58,7 +58,6 @@ function TileLayoutComponent({ tabAtom, contents, getCursorPoint }: TileLayoutPr
const setActiveDrag = useSetAtom(layoutModel.activeDrag);
const setReady = useSetAtom(layoutModel.ready);
const isResizing = useAtomValue(layoutModel.isResizing);
const ephemeralNode = useAtomValue(layoutModel.ephemeralNode);
const { activeDrag, dragClientOffset } = useDragLayer((monitor) => ({
activeDrag: monitor.isDragging(),

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { getSettingsKeyAtom } from "@/app/store/global";
import { atomWithThrottle, boundNumber } from "@/util/util";
import { atomWithThrottle, boundNumber, fireAndForget } from "@/util/util";
import { Atom, atom, Getter, PrimitiveAtom, Setter } from "jotai";
import { splitAtom } from "jotai/utils";
import { createRef, CSSProperties } from "react";
@ -852,7 +852,7 @@ export class LayoutModel {
animationTimeS: this.animationTimeS,
ready: this.ready,
disablePointerEvents: this.activeDrag,
onClose: async () => await this.closeNode(nodeid),
onClose: () => fireAndForget(() => this.closeNode(nodeid)),
toggleMagnify: () => this.magnifyNodeToggle(nodeid),
focusNode: () => this.focusNode(nodeid),
dragHandleRef: createRef(),

View File

@ -24,7 +24,7 @@ export function getLayoutModelForTab(tabAtom: Atom<Tab>): LayoutModel {
}
const layoutTreeStateAtom = withLayoutTreeStateAtomFromTab(tabAtom);
const layoutModel = new LayoutModel(layoutTreeStateAtom, globalStore.get, globalStore.set);
globalStore.sub(layoutTreeStateAtom, () => fireAndForget(async () => layoutModel.onTreeStateAtomUpdated()));
globalStore.sub(layoutTreeStateAtom, () => fireAndForget(layoutModel.onTreeStateAtomUpdated.bind(layoutModel)));
layoutModelMap.set(tabId, layoutModel);
return layoutModel;
}
@ -56,7 +56,7 @@ export function useTileLayout(tabAtom: Atom<Tab>, tileContent: TileLayoutContent
useOnResize(layoutModel?.displayContainerRef, layoutModel?.onContainerResize);
// Once the TileLayout is mounted, re-run the state update to get all the nodes to flow in the layout.
useEffect(() => fireAndForget(async () => layoutModel.onTreeStateAtomUpdated(true)), []);
useEffect(() => fireAndForget(() => layoutModel.onTreeStateAtomUpdated(true)), []);
useEffect(() => layoutModel.registerTileLayout(tileContent), [tileContent]);
return layoutModel;