feat: add create command modal
This commit is contained in:
parent
dbaa7a768c
commit
9a59326afa
@ -14,6 +14,7 @@
|
|||||||
href="./src/lib/bootstrap-icons-1.11.3/font/bootstrap-icons.css"
|
href="./src/lib/bootstrap-icons-1.11.3/font/bootstrap-icons.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
|
<script src="./lib/popper.min.js"></script>
|
||||||
<script src="./lib/bootstrap-5.3.3-dist/js/bootstrap.bundle.min.js"></script>
|
<script src="./lib/bootstrap-5.3.3-dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<title>Playback Device Server</title>
|
<title>Playback Device Server</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
function Command({ id, protocol, command, device, command_type, title } = {}) {
|
function Command({
|
||||||
|
id,
|
||||||
|
protocol,
|
||||||
|
command: commandNumber,
|
||||||
|
device,
|
||||||
|
commandType,
|
||||||
|
title,
|
||||||
|
} = {}) {
|
||||||
let _id = id;
|
let _id = id;
|
||||||
let _protocol = protocol;
|
let _protocol = protocol;
|
||||||
let _command = command;
|
let _commandNumber = commandNumber;
|
||||||
let _device = device;
|
let _device = device;
|
||||||
let _command_type = command_type;
|
let _commandType = commandType;
|
||||||
let _title = title;
|
let _title = title;
|
||||||
|
|
||||||
function getId() {
|
function getId() {
|
||||||
@ -22,12 +29,12 @@ function Command({ id, protocol, command, device, command_type, title } = {}) {
|
|||||||
_protocol = protocol;
|
_protocol = protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCommand() {
|
function getCommandNumber() {
|
||||||
return _command;
|
return _commandNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCommand(command) {
|
function setCommandNumber(commandNumber) {
|
||||||
_command = command;
|
_commandNumber = commandNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDevice() {
|
function getDevice() {
|
||||||
@ -39,11 +46,11 @@ function Command({ id, protocol, command, device, command_type, title } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCommandType() {
|
function getCommandType() {
|
||||||
return _command_type;
|
return _commandType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCommandType(command_type) {
|
function setCommandType(commandType) {
|
||||||
_command_type = command_type;
|
_commandType = commandType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTitle() {
|
function getTitle() {
|
||||||
@ -59,8 +66,8 @@ function Command({ id, protocol, command, device, command_type, title } = {}) {
|
|||||||
setId,
|
setId,
|
||||||
getProtocol,
|
getProtocol,
|
||||||
setProtocol,
|
setProtocol,
|
||||||
getCommand,
|
getCommandNumber,
|
||||||
setCommand,
|
setCommandNumber,
|
||||||
getDevice,
|
getDevice,
|
||||||
setDevice,
|
setDevice,
|
||||||
getCommandType,
|
getCommandType,
|
||||||
@ -70,4 +77,73 @@ function Command({ id, protocol, command, device, command_type, title } = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Command.Protocols = {
|
||||||
|
samsung: "Samsung",
|
||||||
|
nec: "NEC",
|
||||||
|
onkyo: "Onkyo",
|
||||||
|
apple: "Apple",
|
||||||
|
denon: "Denon",
|
||||||
|
sharp: "Sharp",
|
||||||
|
panasonic: "Panasonic",
|
||||||
|
kaseikyo: "Kaseikyo",
|
||||||
|
jvc: "JVC",
|
||||||
|
lg: "LG",
|
||||||
|
sony: "Sony",
|
||||||
|
rc5: "RC5",
|
||||||
|
rc6: "RC6",
|
||||||
|
universal_pulse_distance: "Universal Pulse Distance",
|
||||||
|
universal_pulse_width: "Universal Pulse Width",
|
||||||
|
universal_pulse_distance_width: "Universal Pulse Distance Width",
|
||||||
|
hash: "Hash",
|
||||||
|
pronto: "Pronto",
|
||||||
|
bose_wave: "BoseWave",
|
||||||
|
bang_olufsen: "Bang & Olufsen",
|
||||||
|
lego: "Lego",
|
||||||
|
fast: "FAST",
|
||||||
|
whynter: "Whynter",
|
||||||
|
magiquest: "MagiQuest",
|
||||||
|
}
|
||||||
|
|
||||||
|
Command.CommandTypes = {
|
||||||
|
power: "Power",
|
||||||
|
input: "Input",
|
||||||
|
one: "1",
|
||||||
|
two: "2",
|
||||||
|
three: "3",
|
||||||
|
four: "4",
|
||||||
|
five: "5",
|
||||||
|
six: "6",
|
||||||
|
seven: "7",
|
||||||
|
eight: "8",
|
||||||
|
nine: "9",
|
||||||
|
zero: "0",
|
||||||
|
volume_up: "Volume Up",
|
||||||
|
volume_down: "Volume Down",
|
||||||
|
mute: "Mute",
|
||||||
|
channel_up: "Channel Up",
|
||||||
|
channel_down: "Channel Down",
|
||||||
|
menu: "Menu",
|
||||||
|
home: "Home",
|
||||||
|
settings: "Settings",
|
||||||
|
options: "Options",
|
||||||
|
up_arrow: "Up Arrow",
|
||||||
|
down_arrow: "Down Arrow",
|
||||||
|
left_arrow: "Left Arrow",
|
||||||
|
right_arrow: "Right Arrow",
|
||||||
|
select: "Select",
|
||||||
|
info: "Info",
|
||||||
|
back: "Back",
|
||||||
|
exit: "Exit",
|
||||||
|
red: "Red",
|
||||||
|
green: "Green",
|
||||||
|
yellow: "Yellow",
|
||||||
|
blue: "Blue",
|
||||||
|
rewind: "Rewind",
|
||||||
|
play: "Play",
|
||||||
|
pause: "Pause",
|
||||||
|
stop: "Stop",
|
||||||
|
forward: "Forward",
|
||||||
|
other: "Other",
|
||||||
|
}
|
||||||
|
|
||||||
export default Command;
|
export default Command;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import PlaybackDevice from "./playback-device.js";
|
import PlaybackDevice from "./playback-device.js";
|
||||||
import User from "./user.js";
|
import User from "./user.js";
|
||||||
import Integration from "./integration.js";
|
import Integration from "./integration.js";
|
||||||
|
import Command from "./command.js";
|
||||||
|
|
||||||
const Serializer = (function () {
|
const Serializer = (function () {
|
||||||
function deserializeUser(object) {
|
function deserializeUser(object) {
|
||||||
@ -29,6 +30,15 @@ const Serializer = (function () {
|
|||||||
if (!objects) return [];
|
if (!objects) return [];
|
||||||
return objects.map((object) => deserializeIntegration(object));
|
return objects.map((object) => deserializeIntegration(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deserializeCommand(object) {
|
||||||
|
return new Command(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deserializeCommands(objects) {
|
||||||
|
if (!objects) return [];
|
||||||
|
return objects.map((object) => deserializeCommand(object));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deserializeUser,
|
deserializeUser,
|
||||||
@ -37,6 +47,8 @@ const Serializer = (function () {
|
|||||||
deserializeDevices,
|
deserializeDevices,
|
||||||
deserializeIntegration,
|
deserializeIntegration,
|
||||||
deserializeIntegrations,
|
deserializeIntegrations,
|
||||||
|
deserializeCommand,
|
||||||
|
deserializeCommands,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
6
www/src/lib/popper.min.js
vendored
Normal file
6
www/src/lib/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
215
www/src/modals/create-command-modal.jsx
Normal file
215
www/src/modals/create-command-modal.jsx
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import { createEffect, createMemo, createSignal } from "solid-js";
|
||||||
|
import ValidatedTextInput from "../modules/validated-text-input.jsx";
|
||||||
|
import EventEmitter from "../tools/event-emitter.js";
|
||||||
|
import ModalHandler from "./modal-handler.js";
|
||||||
|
import Modal from "./modal.jsx";
|
||||||
|
import RemotesService from "../services/remotes-service.js";
|
||||||
|
import Command from "../data/command.js";
|
||||||
|
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
const COMMAND_CREATED_EVENT = "success";
|
||||||
|
const MIN_TITLE_LENGTH = 3;
|
||||||
|
|
||||||
|
function CreateCommandModal(props) {
|
||||||
|
const [protocol, setProtocol] = createSignal("");
|
||||||
|
const [commandNumber, setCommandNumber] = createSignal("");
|
||||||
|
const [device, setDevice] = createSignal("");
|
||||||
|
const [commandType, setCommandType] = createSignal("");
|
||||||
|
const [title, setTitle] = createSignal("");
|
||||||
|
const [error, setError] = createSignal("");
|
||||||
|
|
||||||
|
const isProtocolValid = createMemo(() => protocol() !== "");
|
||||||
|
const isCommandNumberValid = createMemo(
|
||||||
|
() => commandNumber() !== "" && !isNaN(commandNumber())
|
||||||
|
);
|
||||||
|
const isDeviceValid = createMemo(() => device() !== "" && !isNaN(device()));
|
||||||
|
const isCommandTypeValid = createMemo(() => commandType() !== "");
|
||||||
|
const isTitleValid = createMemo(() => title().length >= MIN_TITLE_LENGTH);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
let commandString = commandType() ? Command.CommandTypes[commandType()] : "";
|
||||||
|
let protocolString = protocol() ? Command.Protocols[protocol()] : "";
|
||||||
|
setTitle(commandString + " " + protocolString);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isFormValid = createMemo(
|
||||||
|
() =>
|
||||||
|
isProtocolValid() &&
|
||||||
|
isCommandNumberValid() &&
|
||||||
|
isDeviceValid() &&
|
||||||
|
isCommandTypeValid() &&
|
||||||
|
isTitleValid()
|
||||||
|
);
|
||||||
|
|
||||||
|
async function handleCreateCommand() {
|
||||||
|
let command;
|
||||||
|
try {
|
||||||
|
command = await RemotesService.createCommand({
|
||||||
|
protocol: protocol(),
|
||||||
|
commandNumber: commandNumber(),
|
||||||
|
device: device(),
|
||||||
|
commandType: commandType(),
|
||||||
|
title: title(),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setError(e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resetFields();
|
||||||
|
CreateCommandModal.Handler.hide();
|
||||||
|
eventEmitter.dispatchEvent(COMMAND_CREATED_EVENT, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFields() {
|
||||||
|
setProtocol("");
|
||||||
|
setCommandNumber("");
|
||||||
|
setDevice("");
|
||||||
|
setCommandType("");
|
||||||
|
setTitle("");
|
||||||
|
setError("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
ref={props.ref}
|
||||||
|
id="createCommandModal"
|
||||||
|
modalTitle="New Command"
|
||||||
|
centered={true}
|
||||||
|
>
|
||||||
|
<div class="modal-body" style="overflow-y:inherit !important;">
|
||||||
|
<Show when={error() !== ""}>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{error()}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label for="new_command_protocol" class="col-form-label col-sm-3">
|
||||||
|
Protocol
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary dropdown-toggle"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
{protocol() ? Command.Protocols[protocol()] : "Please select"}
|
||||||
|
</button>
|
||||||
|
<ul
|
||||||
|
class="dropdown-menu"
|
||||||
|
style="max-height: 10em; overflow-y: auto;"
|
||||||
|
>
|
||||||
|
{Object.keys(Command.Protocols).map((protocol) => (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class="dropdown-item pe-auto"
|
||||||
|
onClick={() => setProtocol(protocol)}
|
||||||
|
>
|
||||||
|
{Command.Protocols[protocol]}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label for="new_command_number" class="col-form-label col-sm-3">
|
||||||
|
Command Number
|
||||||
|
</label>
|
||||||
|
<ValidatedTextInput
|
||||||
|
class="col-sm-9"
|
||||||
|
id="new_command_number"
|
||||||
|
valid={isCommandNumberValid()}
|
||||||
|
value={commandNumber()}
|
||||||
|
onInput={(e) => setCommandNumber(e.target.value)}
|
||||||
|
errorText={"Command number must be a number"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label
|
||||||
|
for="new_command_device_number"
|
||||||
|
class="col-form-label col-sm-3"
|
||||||
|
>
|
||||||
|
Device Number
|
||||||
|
</label>
|
||||||
|
<ValidatedTextInput
|
||||||
|
class="col-sm-9"
|
||||||
|
id="new_command_device_number"
|
||||||
|
valid={isDeviceValid()}
|
||||||
|
value={device()}
|
||||||
|
onInput={(e) => setDevice(e.target.value)}
|
||||||
|
errorText={"Device number must be a number"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label for="new_command_protocol" class="col-form-label col-sm-3">
|
||||||
|
Command Type
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary dropdown-toggle"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
{commandType()
|
||||||
|
? Command.CommandTypes[commandType()]
|
||||||
|
: "Please select"}
|
||||||
|
</button>
|
||||||
|
<ul
|
||||||
|
class="dropdown-menu"
|
||||||
|
style="max-height: 10em; overflow-y: auto;"
|
||||||
|
>
|
||||||
|
{Object.keys(Command.CommandTypes).map((commandType) => (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class="dropdown-item pe-auto"
|
||||||
|
onClick={() => setCommandType(commandType)}
|
||||||
|
>
|
||||||
|
{Command.CommandTypes[commandType]}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label for="new_command_title" class="col-form-label col-sm-3">
|
||||||
|
Title
|
||||||
|
</label>
|
||||||
|
<ValidatedTextInput
|
||||||
|
class="col-sm-9"
|
||||||
|
id="new_command_title"
|
||||||
|
valid={isTitleValid()}
|
||||||
|
value={title()}
|
||||||
|
onInput={(e) => setTitle(e.target.value)}
|
||||||
|
errorText={`Title must be at least ${MIN_TITLE_LENGTH} characters long`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleCreateCommand}
|
||||||
|
class="btn btn-primary"
|
||||||
|
disabled={!isFormValid()}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateCommandModal.Handler = new ModalHandler();
|
||||||
|
CreateCommandModal.onCommandCreated = (callback) =>
|
||||||
|
eventEmitter.on(COMMAND_CREATED_EVENT, callback);
|
||||||
|
|
||||||
|
export default CreateCommandModal;
|
||||||
@ -6,6 +6,7 @@ import UserSettingsModal from "./user-settings-modal.jsx";
|
|||||||
import CreateDeviceModal from "./create-device-modal.jsx";
|
import CreateDeviceModal from "./create-device-modal.jsx";
|
||||||
import ShowRegistrationCodeModal from "./show-registration-code-modal.jsx";
|
import ShowRegistrationCodeModal from "./show-registration-code-modal.jsx";
|
||||||
import DeleteIntegrationModal from "./delete-integration-modal.jsx";
|
import DeleteIntegrationModal from "./delete-integration-modal.jsx";
|
||||||
|
import CreateCommandModal from "./create-command-modal.jsx";
|
||||||
|
|
||||||
const ModalRegistry = (function () {
|
const ModalRegistry = (function () {
|
||||||
const modals = [
|
const modals = [
|
||||||
@ -39,6 +40,11 @@ const ModalRegistry = (function () {
|
|||||||
component: DeleteIntegrationModal,
|
component: DeleteIntegrationModal,
|
||||||
ref: null,
|
ref: null,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "newCommandModal",
|
||||||
|
component: CreateCommandModal,
|
||||||
|
ref: null,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function getModals(props) {
|
function getModals(props) {
|
||||||
|
|||||||
@ -1,15 +1,26 @@
|
|||||||
|
import Serializer from "../data/serializer";
|
||||||
|
|
||||||
function RemotesService() {
|
function RemotesService() {
|
||||||
|
let commands = [];
|
||||||
|
|
||||||
async function getRemotes() {
|
async function getRemotes() {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCommands() {
|
async function getCommands() {
|
||||||
return [];
|
return [].concat(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createCommand(commandObject) {
|
||||||
|
let command = Serializer.deserializeCommand(commandObject);
|
||||||
|
commands.push(command);
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getRemotes,
|
getRemotes,
|
||||||
getCommands,
|
getCommands,
|
||||||
|
createCommand,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { createResource, onMount } from "solid-js";
|
import { createResource, onMount } from "solid-js";
|
||||||
import List from "../../modules/list";
|
import List from "../../modules/list";
|
||||||
import RemotesService from "../../services/remotes-service";
|
import RemotesService from "../../services/remotes-service";
|
||||||
|
import CreateCommandModal from "../../modals/create-command-modal";
|
||||||
|
|
||||||
function CommandsList(props) {
|
function CommandsList(props) {
|
||||||
const [commands, { refetch: refetchCommands }] = createResource(
|
const [commands, { refetch: refetchCommands }] = createResource(
|
||||||
@ -11,7 +12,18 @@ function CommandsList(props) {
|
|||||||
refetchCommands();
|
refetchCommands();
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleNewCommand() {}
|
CreateCommandModal.onCommandCreated(() => {
|
||||||
|
console.log("CREATED");
|
||||||
|
refetchCommands();
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleNewCommand() {
|
||||||
|
refetchCommands();
|
||||||
|
CreateCommandModal.Handler.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteCommand(command) {
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user