Added chat window on the browser side.

This commit is contained in:
Jared Goodwin 2020-02-23 13:48:52 -08:00
parent 29f01e1925
commit d474a786e8
14 changed files with 243 additions and 9 deletions

View File

@ -21,6 +21,7 @@
<ItemGroup>
<Content Remove="wwwroot\scripts\BrowserSockets.ts" />
<Content Remove="wwwroot\scripts\Chat.ts" />
<Content Remove="wwwroot\scripts\CommandCompletion.ts" />
<Content Remove="wwwroot\scripts\CommandProcessor.ts" />
<Content Remove="wwwroot\scripts\Commands\BashCommands.ts" />
@ -136,6 +137,7 @@
<TypeScriptCompile Include="wwwroot\lib\%40types\jquery\legacy.d.ts" />
<TypeScriptCompile Include="wwwroot\lib\%40types\jquery\misc.d.ts" />
<TypeScriptCompile Include="wwwroot\lib\%40types\sizzle\index.d.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Chat.ts" />
<TypeScriptCompile Include="wwwroot\scripts\CommandCompletion.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Commands\BashCommands.ts" />
<TypeScriptCompile Include="wwwroot\scripts\Commands\WinPSCommands.ts" />

View File

@ -50,7 +50,7 @@ namespace Remotely.Server.Services
public Task Chat(string message, string senderConnectionID)
{
return BrowserHub.Clients.Client(senderConnectionID).SendAsync("Chat", Device.DeviceName, message);
return BrowserHub.Clients.Client(senderConnectionID).SendAsync("Chat", Device.ID, Device.DeviceName, message);
}
public Task CMDResultViaAjax(string commandID)

View File

@ -119,4 +119,16 @@ button.navbar-toggler:hover {
span.label.code {
background: rgb(60, 60, 60);
color: white;
}
.chat-window {
background-color: rgb(25,25,25);
border: 1px solid dimgray;
}
.chat-header {
color: white;
background-color: #555;
}
.chat-input {
background-color: lightgray;
}

View File

@ -114,4 +114,13 @@ button.navbar-toggler:hover {
span.label.code {
background: rgb(245, 245, 245);
color: black;
}
.chat-window {
background-color: white;
border: 1px solid dimgray;
}
.chat-header {
color: white;
background-color: #008cba;
}

View File

@ -220,6 +220,32 @@ span.label.code {
.middle-aligned {
vertical-align: middle !important;
}
.chat-window {
position: fixed;
right: 20px;
bottom: 20px;
width: 250px;
}
.chat-header {
user-select: none;
cursor: move;
padding: 5px 10px;
}
.chat-messages {
overflow-y: auto;
height: 200px;
padding: 5px 10px;
}
.chat-input {
width: 100%;
resize: none;
margin-bottom: -5px;
}
.float-message {
position: fixed;
top: 20px;

View File

@ -4,6 +4,7 @@ import { CreateCommandHarness, AddCommandResultsHarness, AddPSCoreResultsHarness
import { Store } from "./Store.js";
import { Main } from "./Main.js";
import { AddConsoleOutput, AddConsoleHTML } from "./Console.js";
import { ReceiveChatText } from "./Chat.js";
export var Connection;
export var ServiceID;
export var Connected;
@ -32,8 +33,11 @@ export function Connect() {
}
;
function applyMessageHandlers(hubConnection) {
hubConnection.on("Chat", (deviceName, message) => {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
hubConnection.on("Chat", (deviceID, deviceName, message) => {
if (message) {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
ReceiveChatText(deviceID, deviceName, message);
}
});
hubConnection.on("UserOptions", (options) => {
Main.UserSettings.CommandModeShortcuts.Web = options.CommandModeShortcutWeb;

File diff suppressed because one or more lines are too long

View File

@ -9,6 +9,7 @@ import { Store } from "./Store.js";
import { UserOptions } from "./Models/UserOptions.js";
import { Main } from "./Main.js";
import { AddConsoleOutput, AddConsoleHTML } from "./Console.js";
import { ReceiveChatText } from "./Chat.js";
export var Connection: any;
@ -44,8 +45,11 @@ export function Connect() {
};
function applyMessageHandlers(hubConnection) {
hubConnection.on("Chat", (deviceName: string, message: string) => {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
hubConnection.on("Chat", (deviceID: string, deviceName: string, message: string) => {
if (message) {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
ReceiveChatText(deviceID, deviceName, message);
}
});
hubConnection.on("UserOptions", (options: UserOptions) => {
Main.UserSettings.CommandModeShortcuts.Web = options.CommandModeShortcutWeb;

View File

@ -0,0 +1,76 @@
import * as BrowserSockets from "./BrowserSockets.js";
export function CreateChatWindow(deviceID, deviceName) {
var chatWindow = document.getElementById("chat-" + deviceID);
if (!chatWindow) {
var windowHtml = `
<div class="chat-header">
<div class="text-right">
<i class="fas fa-window-close close-button pointer"></i>
</div>
<h6>Chat with ${deviceName}</h6>
</div>
<div class="chat-messages">
</div>
<textarea class="chat-input" value=""></textarea>
`;
chatWindow = document.createElement("div");
chatWindow.classList.add("chat-window");
chatWindow.style.right = "20px";
chatWindow.style.bottom = "20px";
chatWindow.setAttribute("id", "chat-" + deviceID);
chatWindow.innerHTML = windowHtml;
document.body.appendChild(chatWindow);
chatWindow.querySelector(".close-button").onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
document.body.removeChild(chatWindow);
};
chatWindow.querySelector(".chat-header").onmousedown = (ev) => {
ev.preventDefault();
ev.stopPropagation();
chatWindow.removeEventListener("mousemove", moveChatWindow);
chatWindow.removeEventListener("mouseup", stopMovingChatWindow);
chatWindow.removeEventListener("mouseleave", stopMovingChatWindow);
chatWindow.addEventListener("mousemove", moveChatWindow);
chatWindow.addEventListener("mouseup", stopMovingChatWindow);
chatWindow.addEventListener("mouseleave", stopMovingChatWindow);
};
chatWindow.querySelector(".chat-input").onkeypress = (ev) => {
if (ev.key.toLowerCase() == "enter") {
ev.preventDefault();
ev.stopPropagation();
var inputText = ev.currentTarget.value;
chatWindow.querySelector(".chat-messages").innerHTML += `
<div>
<span class="text-primary">You: </span>
<span>${inputText}</span>
</div>
`;
ev.currentTarget.value = "";
BrowserSockets.Connection.invoke("Chat", inputText, [deviceID]);
}
};
}
}
export function ReceiveChatText(deviceID, deviceName, message) {
CreateChatWindow(deviceID, deviceName);
var chatWindow = document.getElementById("chat-" + deviceID);
var chatMessages = chatWindow.querySelector(".chat-messages");
chatMessages.innerHTML += `
<div>
<span class="text-primary">${deviceName}: </span>
<span>${message}</span>
</div>
`;
}
function moveChatWindow(ev) {
var chatWindow = ev.currentTarget;
chatWindow.style.right = String(parseInt(chatWindow.style.right || "0") - ev.movementX) + "px";
chatWindow.style.bottom = String(parseInt(chatWindow.style.bottom || "0") - ev.movementY) + "px";
}
function stopMovingChatWindow(ev) {
ev.currentTarget.removeEventListener("mousemove", moveChatWindow);
ev.currentTarget.removeEventListener("mouseup", stopMovingChatWindow);
ev.currentTarget.removeEventListener("mouseleave", stopMovingChatWindow);
}
//# sourceMappingURL=Chat.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Chat.js","sourceRoot":"","sources":["Chat.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,82 @@
import * as BrowserSockets from "./BrowserSockets.js";
export function CreateChatWindow(deviceID: string, deviceName: string) {
var chatWindow = document.getElementById("chat-" + deviceID);
if (!chatWindow) {
var windowHtml = `
<div class="chat-header">
<div class="text-right">
<i class="fas fa-window-close close-button pointer"></i>
</div>
<h6>Chat with ${deviceName}</h6>
</div>
<div class="chat-messages">
</div>
<textarea class="chat-input" value=""></textarea>
`;
chatWindow = document.createElement("div");
chatWindow.classList.add("chat-window");
chatWindow.style.right = "20px";
chatWindow.style.bottom = "20px";
chatWindow.setAttribute("id", "chat-" + deviceID);
chatWindow.innerHTML = windowHtml;
document.body.appendChild(chatWindow);
(chatWindow.querySelector(".close-button") as HTMLElement).onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
document.body.removeChild(chatWindow);
};
(chatWindow.querySelector(".chat-header") as HTMLDivElement).onmousedown = (ev) => {
ev.preventDefault();
ev.stopPropagation();
chatWindow.removeEventListener("mousemove", moveChatWindow);
chatWindow.removeEventListener("mouseup", stopMovingChatWindow);
chatWindow.removeEventListener("mouseleave", stopMovingChatWindow);
chatWindow.addEventListener("mousemove", moveChatWindow);
chatWindow.addEventListener("mouseup", stopMovingChatWindow);
chatWindow.addEventListener("mouseleave", stopMovingChatWindow);
};
(chatWindow.querySelector(".chat-input") as HTMLTextAreaElement).onkeypress = (ev) => {
if (ev.key.toLowerCase() == "enter") {
ev.preventDefault();
ev.stopPropagation();
var inputText = (ev.currentTarget as HTMLTextAreaElement).value;
(chatWindow.querySelector(".chat-messages") as HTMLDivElement).innerHTML += `
<div>
<span class="text-primary">You: </span>
<span>${inputText}</span>
</div>
`;
(ev.currentTarget as HTMLTextAreaElement).value = "";
BrowserSockets.Connection.invoke("Chat", inputText, [deviceID]);
}
};
}
}
export function ReceiveChatText(deviceID: string, deviceName: string, message: string) {
CreateChatWindow(deviceID, deviceName);
var chatWindow = document.getElementById("chat-" + deviceID) as HTMLDivElement;
var chatMessages = chatWindow.querySelector(".chat-messages") as HTMLDivElement;
chatMessages.innerHTML += `
<div>
<span class="text-primary">${deviceName}: </span>
<span>${message}</span>
</div>
`;
}
function moveChatWindow(ev: MouseEvent) {
var chatWindow = ev.currentTarget as HTMLDivElement;
chatWindow.style.right = String(parseInt(chatWindow.style.right || "0") - ev.movementX) + "px";
chatWindow.style.bottom = String(parseInt(chatWindow.style.bottom || "0") - ev.movementY) + "px";
}
function stopMovingChatWindow(ev: MouseEvent) {
ev.currentTarget.removeEventListener("mousemove", moveChatWindow);
ev.currentTarget.removeEventListener("mouseup", stopMovingChatWindow);
ev.currentTarget.removeEventListener("mouseleave", stopMovingChatWindow);
}

View File

@ -2,6 +2,7 @@ import * as UI from "./UI.js";
import { Main } from "./Main.js";
import { DeviceGrid } from "./UI.js";
import { AddConsoleOutput } from "./Console.js";
import { CreateChatWindow } from "./Chat.js";
export const DataSource = new Array();
export const FilterOptions = new class {
constructor() {
@ -63,12 +64,20 @@ export function AddOrUpdateDevice(device) {
<td>${device.TotalStorage.toLocaleString()}</td>
<td>${Math.round(device.UsedMemory / device.TotalMemory * 100)}%</td>
<td>${device.TotalMemory.toLocaleString()}</td>
<td><span class="fas fa-edit device-edit-button" style="font-size:1.5em" /></td>`;
<td>
<i class="fas fa-comment device-chat-button mr-2" title="Chat" style="font-size:1.5em"></i>
<span class="fas fa-edit device-edit-button" title="Edit" style="font-size:1.5em" />
</td>`;
recordRow.querySelector(".device-edit-button").onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
window.open(`${location.origin}/EditDevice?deviceID=${device.ID}`, "_blank");
};
recordRow.querySelector(".device-chat-button").onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
CreateChatWindow(device.ID, device.DeviceName);
};
UpdateDeviceCounts();
}
export function ApplyFilter() {

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@ import { Device } from "./Models/Device.js";
import { Main } from "./Main.js";
import { DeviceGrid } from "./UI.js";
import { AddConsoleOutput } from "./Console.js";
import { CreateChatWindow } from "./Chat.js";
export const DataSource: Array<Device> = new Array<Device>();
@ -71,7 +72,10 @@ export function AddOrUpdateDevice(device: Device) {
<td>${device.TotalStorage.toLocaleString()}</td>
<td>${Math.round(device.UsedMemory /device.TotalMemory * 100)}%</td>
<td>${device.TotalMemory.toLocaleString()}</td>
<td><span class="fas fa-edit device-edit-button" style="font-size:1.5em" /></td>`;
<td>
<i class="fas fa-comment device-chat-button mr-2" title="Chat" style="font-size:1.5em"></i>
<span class="fas fa-edit device-edit-button" title="Edit" style="font-size:1.5em" />
</td>`;
(recordRow.querySelector(".device-edit-button") as HTMLButtonElement).onclick = (ev) => {
@ -79,6 +83,11 @@ export function AddOrUpdateDevice(device: Device) {
ev.stopPropagation();
window.open(`${location.origin}/EditDevice?deviceID=${device.ID}`, "_blank");
};
(recordRow.querySelector(".device-chat-button") as HTMLButtonElement).onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
CreateChatWindow(device.ID, device.DeviceName);
};
UpdateDeviceCounts();
}
export function ApplyFilter() {