A self hosted chat application with end-to-end encrypted messaging.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
import { Observer } from "../observe/Observer"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ChatModel } from "../model/ChatModel"; import log = require("loglevel"); import * as DOMPurify from "dompurify"; import { SearchService } from "../service/SearchService"; import { UserModel } from "../model/UserModel"; import { UserViewDeps } from "./UserViewDeps"; import { ObserverData } from "../observe/ObserverData"; import { JsonAPI } from "../singleton/JsonAPI"; import { NotificationService } from "../service/NotificationService"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
export class UserView implements Observer<ActiveUserViewModel> { private readonly _model: UserModel; private readonly _chatModel: ChatModel; private readonly _usersListElement: HTMLElement; private readonly _userSearchInputElement: HTMLInputElement; private readonly _userSearchButton: HTMLElement; private readonly _userSearchCancelButton: HTMLElement; private readonly _searchService: SearchService<ActiveUserViewModel>; private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate< ActiveUserViewModel >; private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate< ActiveUserViewModel >; private readonly _notificationService: NotificationService; private _newMessagesLoop: any;
constructor(deps: UserViewDeps) { this._model = deps.model; this._chatModel = deps.chatModel; this._usersListElement = deps.usersListElement; this._userSearchInputElement = deps.userSearchInputElement; this._userSearchButton = deps.userSearchButton; this._userSearchCancelButton = deps.userSearchCancelButton; this._searchService = deps.searchService; this._userContactOnlineTemplate = deps.userContactOnlineTemplate; this._userContactOfflineTemplate = deps.userContactOfflineTemplate; this._notificationService = deps.notificationService;
this._addSearchEventListeners(); }
update(d: ObserverData<ActiveUserViewModel>): void { let html: string = ""; d.data.forEach((element: ActiveUserViewModel) => { element.online ? (html += this._userContactOnlineTemplate(element)) : (html += this._userContactOfflineTemplate(element)); }); $(this._usersListElement).html(DOMPurify.sanitize(html)); this._addUserCallBacks(); }
private _addSearchEventListeners(): void { this._addSearchButtonEL(); this._addSearchCancelEL(); this._addSearchInputEL(); }
private _addUserCallBacks(): void { let userBoxes = document.getElementsByClassName("user-box");
Array.from(userBoxes).forEach((ub: Element) => ub.addEventListener("click", this._userCallBack.bind(this, ub)) ); }
private _userCallBack(el: Element): void { this._chatModel.clear(); clearInterval(this._newMessagesLoop); let current = document.getElementsByClassName("user-box active");
if (current.length > 0) { current[0].className = current[0].className.replace(" active", ""); } // Add the active class to the current/clicked button
else if (current.length == 0) { // @ts-ignore: Object is possibly 'null'.
document.getElementById("no-user-selected").hidden = true; // @ts-ignore: Object is possibly 'null'.
document.getElementById("chat-card").hidden = false; } // console.log(this.getElementsByClassName('to-user-span'));
let elem = el.getElementsByClassName("to-user-span")[0] as HTMLElement; let userName = elem.innerText; JsonAPI.contactName = userName; // @ts-ignore: Object is possibly 'null'.
document.getElementById("user-name-span").innerText = userName; let currentUser = this._model.activeUsersList.find((vm) => vm.userName === userName) || new ActiveUserViewModel(); currentUser.userName = userName;
if (!currentUser?.passphrase) { this._notificationService.passphrasePrompt( async (result) => { if (result) {
const valid = await this._chatModel.isPassphraseValid(result, currentUser.userName!); if (!valid) { bootbox.alert("Some error occured. Please check your password"); log.error("invalid password"); return; } currentUser.unlocked = true; currentUser.passphrase = result; const chatMessages: ChatMessageViewModel[] = await this._chatModel.getMessages(currentUser, "new");
this._model.activeUsersList .filter((v) => v.userName == currentUser!.userName) .forEach((v) => { v.passphrase = result; v.unlocked = true; if (chatMessages.length > 0) { v.lastMessageTime = new Date( chatMessages[chatMessages.length - 1].messageTime ); const lastMessageText = (v.lastMessageText = chatMessages[chatMessages.length - 1].message); if (lastMessageText.length > 15) { v.lastMessageText = chatMessages[chatMessages.length - 1].message.slice( 0, 15 ) + "..."; } } }); this._promptHandler(currentUser);
} } ); } else { this._chatModel.getMessages(currentUser, "new"); }
el.className += " active"; if (currentUser.unlocked && currentUser.lastMessageTime) { this._newMessagesLoop = setInterval( this._chatModel.getMessages.bind(this._chatModel, currentUser, "update"), 10_000 ); this._model.notify(); } }
private _promptHandler(vm: ActiveUserViewModel) { // vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName)
// log.debug(vms);
if (vm.lastMessageTime) { this._newMessagesLoop = setInterval( this._chatModel.getMessages.bind(this._chatModel, vm, "update"), 10_000 ); this._model.notify(); } }
private _addSearchButtonEL() { this._userSearchButton.addEventListener("submit", (e) => { e.preventDefault(); // log.trace(temp);
const searchTerm = this._userSearchInputElement.value; if (searchTerm.length > 0) { log.debug("search term value = " + searchTerm); const list = this._model.activeUsersList; log.debug("active users"); log.debug(list); if (!list) { log.error("Users list is null"); return; } let searchResult = this._searchService.search(list, searchTerm); this.update({ data: searchResult, op: "" }); log.debug(searchResult); } else { this._notificationService.error("Please enter a name") } }); }
private _addSearchInputEL() { this._userSearchInputElement.addEventListener("input", (e) => { e.preventDefault(); if (this._userSearchInputElement.value.length < 2) { this._userSearchCancelButton.hidden = false; } }); }
private _addSearchCancelEL() { this._userSearchCancelButton.addEventListener("click", (e) => { e.preventDefault(); this._userSearchInputElement.value = ""; this._userSearchCancelButton.hidden = true; let list = this._model.activeUsersList; this.update({ data: list, op: "" }); }); } }
|