Skip to content
This repository has been archived by the owner on Mar 5, 2021. It is now read-only.

Commit

Permalink
Improve chat a lot with header, topic and better message formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
elisee committed Jul 7, 2016
1 parent 6edc613 commit cf885ba
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 69 deletions.
121 changes: 94 additions & 27 deletions src/renderer/chat/ChatTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ import { tabStrip, panesElt } from "../tabs";
import * as chat from "./index";
import * as tabs from "../tabs";

import html from "../html";
import * as escapeHTML from "escape-html";
import getBackgroundColor from "./getBackgroundColor";

const tabTemplate = document.querySelector("template.chat-tab") as HTMLTemplateElement;
const commandRegex = /^\/([^\s]*)(?:\s(.*))?$/;

export default class ChatTab {
tabElt: HTMLLIElement;
private tabElt: HTMLLIElement;
paneElt: HTMLDivElement;
private label: string;

logElt: HTMLDivElement;
textAreaElt: HTMLTextAreaElement;
previousMessage: string;
private topicElt: HTMLDivElement;
private waitingForTopic: boolean;

usersTreeView: TreeView;
users: string[] = [];
private logElt: HTMLDivElement;
private textAreaElt: HTMLTextAreaElement;
private previousMessage: string;

private usersTreeView: TreeView;
private users: string[] = [];

constructor(public target: string, options?: { label?: string; isChannel?: boolean; showTab?: boolean; }) {
if (options == null) options = {};
Expand All @@ -34,6 +39,27 @@ export default class ChatTab {
panesElt.appendChild(this.paneElt);
this.paneElt.appendChild(document.importNode(tabTemplate.content, true));

const headerElt = this.paneElt.querySelector(".header") as HTMLDivElement;

let chatTabName = this.target;
let chatTabDetails = `on ${chat.ircNetwork.host}:${chat.ircNetwork.port}`;
let chatTabTopic = "(Waiting for topic...)";

if (this.target === "status") {
chatTabName = `${chat.ircNetwork.host}:${chat.ircNetwork.port}`;
chatTabDetails = "";
chatTabTopic = "Connection status";
} else if (this.target[0] !== "#") {
chatTabTopic = "Private chat";
} else {
this.waitingForTopic = true;
}

(headerElt.querySelector(".info .name") as HTMLDivElement).textContent = chatTabName;
(headerElt.querySelector(".info .details") as HTMLDivElement).textContent = chatTabDetails;
this.topicElt = (headerElt.querySelector(".topic") as HTMLDivElement);
this.topicElt.textContent = chatTabTopic;

this.logElt = this.paneElt.querySelector(".log") as HTMLDivElement;
this.textAreaElt = this.paneElt.querySelector("textarea") as HTMLTextAreaElement;

Expand Down Expand Up @@ -108,32 +134,61 @@ export default class ChatTab {
return text;
}

addInfo(text: string) {
const elt = document.createElement("div");
elt.className = "info";
elt.innerHTML = this.linkify(text);
lastLogItem: {
elt: HTMLDivElement;
type: string;
from: string;
style: string;
};

this.logElt.appendChild(elt);
this.logElt.scrollTop = 9e9;
}
private appendLogElt(type: string, from: string, style: string) {
let elt: HTMLDivElement;
let contentElt: HTMLDivElement;

addMessage(from: string, text: string, style: string) {
const elt = document.createElement("div");
elt.className = "message";
if (style != null) elt.classList.add(style);
if (this.lastLogItem != null &&
this.lastLogItem.elt.classList.contains(type) &&
this.lastLogItem.from === from &&
this.lastLogItem.style === style) {
elt = this.lastLogItem.elt;
contentElt = elt.querySelector(".content") as HTMLDivElement;
} else {
elt = html("div", type);
if (style != null) style.split(" ").forEach((x) => elt.classList.add(x));

if (from != null) {
const gutterElt = html("div", "gutter", { parent: elt });
html("div", "avatar", {
textContent: from.substring(0, 2),
style: { backgroundColor: getBackgroundColor(from), },
parent: gutterElt
});
}

contentElt = html("div", "content", { parent: elt });
html("div", "from", { parent: contentElt, textContent: from });

this.logElt.appendChild(elt);
}

const fromElt = document.createElement("span");
fromElt.className = "from";
fromElt.textContent = `${from}: `;
elt.appendChild(fromElt);
this.lastLogItem = { elt, type, from, style };

const textElt = document.createElement("span");
textElt.className = "text";
textElt.innerHTML = this.linkify(text);
elt.appendChild(textElt);
return contentElt;
}

this.logElt.appendChild(elt);
private scrollToBottom() {
this.logElt.scrollTop = 9e9;
}

addInfo(text: string) {
const contentElt = this.appendLogElt("info", null, null);
html("div", "text", { innerHTML: this.linkify(text), parent: contentElt });
this.scrollToBottom();
}

addMessage(from: string, text: string, style: string) {
const contentElt = this.appendLogElt("message", from, style);
html("div", "text", { innerHTML: this.linkify(text), parent: contentElt });
this.scrollToBottom();
}

hasUser(name: string) {
Expand Down Expand Up @@ -200,7 +255,7 @@ export default class ChatTab {
return;
}

chat.join(params);
chat.join(params, true);
} break;
default:
this.addInfo(`Unsupported command: ${command}`);
Expand All @@ -220,6 +275,12 @@ export default class ChatTab {
}
}

onTopic(event: SlateIRC.TopicEvent) {
this.topicElt.classList.toggle("disabled", event.topic.length === 0);
this.topicElt.textContent = event.topic.length > 0 ? event.topic : "(No topic)";
this.waitingForTopic = false;
}

onJoin(event: SlateIRC.JoinEvent) {
this.addInfo(`${event.nick} has joined ${event.channel}.`);

Expand Down Expand Up @@ -307,5 +368,11 @@ export default class ChatTab {
this.usersTreeView.treeRoot.innerHTML = "";
names.sort((a, b) => a.name.localeCompare(b.name));
for (const name of names) this.addUser(name.name);

if (this.waitingForTopic) {
// If we receive the names before the topic then we can assume no topic has been set
this.waitingForTopic = false;
this.topicElt.textContent = "(No topic)";
}
};
}
16 changes: 16 additions & 0 deletions src/renderer/chat/getBackgroundColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function getBackgroundColor(name: string) {
const color = intToRGB(hashCode(name));

return `rgba(${parseInt(color.substring(0, 2), 16)}, ${parseInt(color.substring(2, 4), 16)}, ${parseInt(color.substring(4, 6), 16)}, 0.2)`;
}

function hashCode(str: string) {
let hash = 0;
for (let i = 0; i < str.length; i++) hash = str.charCodeAt(i) + ((hash << 5) - hash);
return hash;
}

function intToRGB(i: number){
const c = (i & 0x00FFFFFF).toString(16).toUpperCase();
return "00000".substring(0, 6 - c.length) + c;
}
12 changes: 9 additions & 3 deletions src/renderer/chat/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as net from "net";
import * as net from "net";
import * as tls from "tls";
import * as SlateIRC from "slate-irc";

Expand All @@ -15,7 +15,7 @@ export const languageChatRooms = [ "fr" ];

export let irc: SlateIRC.Client;
let socket: net.Socket;
const ircNetwork = { host: "irc.freenode.net", port: 6697 };
export const ircNetwork = { host: "irc.freenode.net", port: 6697 };
let mentionRegex: RegExp;

const statusChatTab = new ChatTab("status", { label: ircNetwork.host, showTab: false });
Expand Down Expand Up @@ -92,6 +92,7 @@ function connect() {
irc = SlateIRC(socket);
irc.on("welcome", onWelcome);
irc.on("motd", onMOTD);
irc.on("topic", onTopic);
irc.on("join", onJoin);
irc.on("part", onPart);
irc.on("nick", onNick);
Expand Down Expand Up @@ -173,6 +174,11 @@ export function join(channelName: string, focus?: boolean) {
chatTab.showTab(focus === true);
}

function onTopic(event: SlateIRC.TopicEvent) {
const chatTab = channelChatTabs[event.channel];
if (chatTab != null) chatTab.onTopic(event);
}

function onJoin(event: SlateIRC.JoinEvent) {
const chatTab = channelChatTabs[event.channel];
if (chatTab != null) chatTab.onJoin(event);
Expand Down Expand Up @@ -271,7 +277,7 @@ function notify(title: string, body: string, callback: Function) {

function onNotice(event: SlateIRC.MessageEvent) {
if (event.to === "*") {
statusChatTab.addMessage(`(private) ${event.from}`, event.message, "notice");
statusChatTab.addMessage(event.from, event.message, "private notice");
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/renderer/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ interface HTMLOptions {
style?: HTMLStyleOptions;
dataset?: { [key: string]: string; };
textContent?: string;
innerHTML?: string;
title?: string;
hidden?: boolean;
disabled?: boolean;
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ html

template.chat-tab
.channel
.header
.info
.name
.details
.topic.disabled
.log
.input
textarea
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/index.styl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
@import "styles/resizeHandle";
@import "styles/tabStrip";
@import "styles/dialogs";
@import "styles/ChatTab";

.chat-tab {
@import "styles/ChatTab";
}

body {
display: flex;
Expand Down
Loading

0 comments on commit cf885ba

Please sign in to comment.