feat: create frontend for user input requests

This is part of a change to allow the backend to request user input from
the frontend. This adds a component specifically for handling this
logic. It is only a starting point, and does not work perfectly yet.
This commit is contained in:
Sylvia Crowe 2024-01-30 22:50:40 -08:00
parent 98f6b84d3e
commit f781e358e5
8 changed files with 124 additions and 15 deletions

View File

@ -8,6 +8,7 @@ export const SESSION_SETTINGS = "sessionSettings";
export const LINE_SETTINGS = "lineSettings";
export const CLIENT_SETTINGS = "clientSettings";
export const TAB_SWITCHER = "tabSwitcher";
export const USER_INPUT = "userInput";
export const LineContainer_Main = "main";
export const LineContainer_History = "history";

View File

@ -6,3 +6,4 @@ export { CreateRemoteConnModal } from "./createremoteconn";
export { ViewRemoteConnDetailModal } from "./viewremoteconndetail";
export { EditRemoteConnModal } from "./editremoteconn";
export { TabSwitcherModal } from "./tabswitcher";
export { UserInputModal } from "./userinput";

View File

@ -64,7 +64,7 @@ class ModalsProvider extends React.Component {
for (let i = 0; i < store.length; i++) {
let entry = store[i];
let Comp = entry.component;
rtn.push(<Comp key={entry.uniqueKey} />);
rtn.push(<Comp key={entry.uniqueKey} {...entry.props} />);
}
return <>{rtn}</>;
}

View File

@ -9,20 +9,22 @@ import {
ViewRemoteConnDetailModal,
EditRemoteConnModal,
TabSwitcherModal,
UserInputModal,
} from "../modals";
import { ScreenSettingsModal, SessionSettingsModal, LineSettingsModal } from "./settings";
import * as constants from "../../appconst";
const modalsRegistry: { [key: string]: () => React.ReactElement } = {
[constants.ABOUT]: () => <AboutModal />,
[constants.CREATE_REMOTE]: () => <CreateRemoteConnModal />,
[constants.VIEW_REMOTE]: () => <ViewRemoteConnDetailModal />,
[constants.EDIT_REMOTE]: () => <EditRemoteConnModal />,
[constants.ALERT]: () => <AlertModal />,
[constants.SCREEN_SETTINGS]: () => <ScreenSettingsModal />,
[constants.SESSION_SETTINGS]: () => <SessionSettingsModal />,
[constants.LINE_SETTINGS]: () => <LineSettingsModal />,
[constants.TAB_SWITCHER]: () => <TabSwitcherModal />,
const modalsRegistry: { [key: string]: React.ComponentType } = {
[constants.ABOUT]: AboutModal,
[constants.CREATE_REMOTE]: CreateRemoteConnModal,
[constants.VIEW_REMOTE]: ViewRemoteConnDetailModal,
[constants.EDIT_REMOTE]: EditRemoteConnModal,
[constants.ALERT]: AlertModal,
[constants.SCREEN_SETTINGS]: ScreenSettingsModal,
[constants.SESSION_SETTINGS]: SessionSettingsModal,
[constants.LINE_SETTINGS]: LineSettingsModal,
[constants.TAB_SWITCHER]: TabSwitcherModal,
[constants.USER_INPUT]: UserInputModal,
};
export { modalsRegistry };

View File

@ -0,0 +1,11 @@
@import "../../../app/common/themes/themes.less";
.userinput-modal {
width: 500px;
.wave-modal-content {
.wave-modal-body {
padding: 40px 20px;
}
}
}

View File

@ -0,0 +1,88 @@
import * as React from "react";
import { GlobalModel } from "../../../model/model";
import { Choose, When, If } from "tsx-control-statements";
import { Modal, PasswordField, Markdown } from "../common";
import { GlobalCommandRunner } from "../../../model/model";
import { UserInputRequest, UserInputResponse, UserInputResponsePacket } from "../../../types/types";
import "./userinput.less";
export const UserInputModal = (userInputRequest: UserInputRequest) => {
const [responseText, setResponseText] = React.useState(null);
const closeModal = React.useCallback(() => {
const userInputResponse: UserInputResponse = {
type: userInputRequest.responsetype,
};
GlobalCommandRunner.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
response: userInputResponse,
});
GlobalModel.remotesModel.closeModal();
}, [responseText, userInputRequest]);
const handleSendText = React.useCallback(() => {
const userInputResponse: UserInputResponse = {
type: userInputRequest.responsetype,
text: responseText,
};
GlobalCommandRunner.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
response: userInputResponse,
});
GlobalModel.remotesModel.closeModal();
}, [responseText, userInputRequest]);
const handleSendConfirm = React.useCallback(
(response: boolean) => {
const userInputResponse: UserInputResponse = {
type: userInputRequest.responsetype,
confirm: response,
};
GlobalCommandRunner.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
response: userInputResponse,
});
GlobalModel.remotesModel.closeModal();
},
[userInputRequest]
);
return (
<Modal className="userinput-modal">
<Modal.Header onClose={closeModal} title={"title"} />
<div className="wave-modal-body">
<If condition={false}>
<Markdown text={userInputRequest.querytext} />
</If>
<If condition={!false}>{userInputRequest.querytext}</If>
<Choose>
<When condition={(userInputRequest.responsetype = "string")}>
<PasswordField
placeholder="password"
onChange={setResponseText}
value={responseText}
maxLength={400}
/>
</When>
</Choose>
</div>
<Choose>
<When condition={(userInputRequest.responsetype = "string")}>
<Modal.Footer onCancel={closeModal} onOk={handleSendText} okLabel="Connect" />
</When>
<When condition={(userInputRequest.responsetype = "bool")}>
<Modal.Footer
onCancel={() => handleSendConfirm(false)}
onOk={() => handleSendConfirm(true)}
okLabel="Yes"
cancelLabel="No"
/>
</When>
</Choose>
</Modal>
);
};

View File

@ -3307,14 +3307,14 @@ class RemotesModel {
}
class ModalsModel {
store: OArr<T.ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store" });
store: OArr<T.ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store", deep: false });
pushModal(modalId: string) {
pushModal(modalId: string, props?: any) {
const modalFactory = modalsRegistry[modalId];
if (modalFactory && !this.store.some((modal) => modal.id === modalId)) {
mobx.action(() => {
this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4() });
this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4(), props: props });
})();
}
}
@ -4124,7 +4124,8 @@ class Model {
requestid: userInputRequest.requestid,
response: userInputResponse,
};
this.ws.pushMessage(userInputResponsePacket);
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
//this.ws.pushMessage(userInputResponsePacket);
}
}
@ -5048,6 +5049,10 @@ class CommandRunner {
}
GlobalModel.submitCommand("sidebar", "open", null, kwargs, false);
}
sendUserInput(userInputResponsePacket: UserInputResponsePacket) {
GlobalModel.sendInputPacket(userInputResponsePacket);
}
}
function cmdPacketString(pk: FeCmdPacketType): string {

View File

@ -740,6 +740,7 @@ type ModalStoreEntry = {
id: string;
component: React.ComponentType;
uniqueKey: string;
props?: any;
};
type StrWithPos = {