diff --git a/chatto/src/main/javascript/package.json b/chatto/src/main/javascript/package.json
index 65a5d62..d23ab30 100644
--- a/chatto/src/main/javascript/package.json
+++ b/chatto/src/main/javascript/package.json
@@ -1,11 +1,13 @@
{
"dependencies": {
+ "@types/bootbox": "^5.2.0",
"@types/dompurify": "^2.0.0",
"@types/jquery": "^3.3.31",
"@types/markdown-it": "^0.0.9",
"@types/sjcl": "^1.0.28",
"@types/sprintf-js": "^1.1.2",
"alertifyjs": "^1.12.0",
+ "bootbox": "^5.4.0",
"builder-pattern": "^1.2.3",
"chart.js": "^2.9.3",
"dompurify": "^2.0.7",
@@ -43,7 +45,13 @@
"fuse.js": "global:Fuse",
"sjcl": "global:sjcl",
"sprintf-js": "global:sprintf",
- "alertifyjs": "global:alertify"
+ "alertifyjs": "global:alertify",
+ "bootbox": {
+ "exports": "global:bootbox",
+ "depends": [
+ "@types/jquery"
+ ]
+ }
},
"scripts": {
"watch": "watchify ts/src/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o ../resources/static/js/bundle.js"
diff --git a/chatto/src/main/javascript/ts/src/main.ts b/chatto/src/main/javascript/ts/src/main.ts
index d9b0469..5fbd71a 100644
--- a/chatto/src/main/javascript/ts/src/main.ts
+++ b/chatto/src/main/javascript/ts/src/main.ts
@@ -66,7 +66,8 @@ const uvDeps: UserViewDeps = {
userSearchCancelButton: userSearchCancelButton,
searchService: activeUserSearchService,
userContactOnlineTemplate: TemplateFactory.getTemplate('user-contact-online-template'),
- userContactOfflineTemplate: TemplateFactory.getTemplate('user-contact-offline-template')
+ userContactOfflineTemplate: TemplateFactory.getTemplate('user-contact-offline-template'),
+ notificationService: ns
}
const userView = new UserView(uvDeps);
userModel.attach(userView);
@@ -79,14 +80,22 @@ Handlebars.registerHelper('avatar', function () {
return '
';
});
Handlebars.registerHelper('fromNow', function (date: string) {
- if(date == null)
- return ": Never"
+ if (date == null)
+ return ": Never"
return moment(date).fromNow();
})
Handlebars.registerHelper('msgDateFormat', function (date: string) {
return moment(date).calendar(moment.now(), { lastWeek: "DD/MM/YY hh:mm A", sameElse: "DD/MM/YY hh:mm A" })
})
+Handlebars.registerHelper('lockIcon', function (unlocked: boolean) {
+ switch (unlocked) {
+ case true: { return ''; }
+ default: { return ''; }
+ }
+})
+
+
ns.success("Welcome");
diff --git a/chatto/src/main/javascript/ts/src/model/ChatModel.ts b/chatto/src/main/javascript/ts/src/model/ChatModel.ts
index 085b267..300047e 100644
--- a/chatto/src/main/javascript/ts/src/model/ChatModel.ts
+++ b/chatto/src/main/javascript/ts/src/model/ChatModel.ts
@@ -94,8 +94,9 @@ export class ChatModel implements Subject {
}
public clear(): void {
+ log.info("Clearing model")
this._messagePageMap.set(JsonAPI.contactName!, 0);
- this.storeUserMessages(JsonAPI.contactName!, [], "clear");
+ this.storeUserMessages(JsonAPI.contactName!, Array(), "clear");
this.notify({ userName: "", data: [], op: "clear" })
}
diff --git a/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts b/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts
index fbc2d00..e81a62b 100644
--- a/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts
+++ b/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts
@@ -1,7 +1,11 @@
import { NotificationService } from "./NotificationService";
// @ts-ignore
import * as alertify from "alertifyjs";
+import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
+import log = require("loglevel");
+import bootbox = require("bootbox")
export class AlertifyNotificationService implements NotificationService {
+
private readonly _alertify = alertify;
constructor() {
this._alertify.set('notifier', 'position', 'top-center');
@@ -24,4 +28,84 @@ export class AlertifyNotificationService implements NotificationService {
message(message: string): void {
this._alertify.message(message);
}
+ passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (contactName: string, passphrase: string,
+ lastMessageTime: string | null, op: string) => void,
+ cb2: () => any): void {
+
+ // alertify.myprompt || alertify.dialog('myprompt', function () {
+
+ // // document.getElementById('passphraseFormNew')!.addEventListener('submit', function (e: Event) {
+ // // e.preventDefault();
+ // // log.debug(this.querySelectorAll('input'))
+ // // Array.from(this.querySelectorAll('input')).map((el) => {
+ // // const el2 = el as HTMLInputElement;
+ // // vm.passphrase = el2.value;
+ // // vm.unlocked = true;
+ // // cb1(vm.userName!, vm.passphrase, null, "new");
+ // // cb2();
+ // // alertify.myprompt().close();
+ // // })
+ // // log.debug(vm)
+
+ // // })
+
+ // return {
+ // main: function (content: HTMLElement) {
+ // log.debug(vm)
+
+ // // @ts-ignore
+ // this.setContent(content);
+ // },
+
+ // setup: function () {
+ // return {
+ // focus: {
+ // // @ts-ignore
+ // element: function () {
+ // // @ts-ignore
+ // return this.elements.body.querySelector(this.get('selector'));
+ // },
+ // select: true
+ // },
+ // options: {
+ // title: 'Enter Passphrase',
+ // basic: true,
+ // maximizable: false,
+ // resizable: false,
+ // padding: false
+ // }
+ // };
+ // },
+ // settings: {
+ // // @ts-ignore
+ // selector: undefined
+ // },
+ // }
+
+
+ // })
+
+ // alertify.myprompt(document.getElementById('passphraseFormNew')).set('selector', 'input[type="password"]');
+
+ if (vm.passphrase == null) {
+ bootbox.prompt({
+ title: "Please enter the passphrase",
+ inputType: 'password',
+ callback: function (result) {
+ if (result) {
+ log.debug(result);
+ cb1(vm.userName!, result, null, "new");
+ vm.unlocked = true
+ vms.filter(v => v.userName == vm.userName).map(v => { v.passphrase = result; v.unlocked = true })
+ cb2();
+ log.debug(vm)
+ log.debug(vms)
+ }
+ }
+ });
+ }
+ else {
+ cb1(vm.userName!, vm.passphrase, null, "new");
+ }
+ }
}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/ts/src/service/NotificationService.ts b/chatto/src/main/javascript/ts/src/service/NotificationService.ts
index 9d9b208..5c97f86 100644
--- a/chatto/src/main/javascript/ts/src/service/NotificationService.ts
+++ b/chatto/src/main/javascript/ts/src/service/NotificationService.ts
@@ -1,7 +1,10 @@
+import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
+
export interface NotificationService {
success(message: string): void;
error(message: string): void;
errorWithDelay(message: string, delay: number): void;
warning(message: string): void;
message(message: string): void;
+ passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (...x: any) => any, cb2: (...x: any) => any): void;
}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/ts/src/view/UserView.ts b/chatto/src/main/javascript/ts/src/view/UserView.ts
index c2ef9ba..9744a6b 100644
--- a/chatto/src/main/javascript/ts/src/view/UserView.ts
+++ b/chatto/src/main/javascript/ts/src/view/UserView.ts
@@ -8,10 +8,11 @@ import { UserModel } from "../model/UserModel";
import { UserViewDeps } from "./UserViewDeps";
import { ObserverData } from "../observe/ObserverData";
import { JsonAPI } from "../singleton/JsonAPI";
+import { NotificationService } from "../service/NotificationService";
export class UserView implements Observer {
-
+
private readonly _model: UserModel;
private readonly _chatModel: ChatModel;
private readonly _usersListElement: HTMLElement;
@@ -21,6 +22,7 @@ export class UserView implements Observer {
private readonly _searchService: SearchService;
private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate;
private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate;
+ private readonly _notificationService: NotificationService;
constructor(deps: UserViewDeps) {
this._model = deps.model;
@@ -32,6 +34,7 @@ export class UserView implements Observer {
this._searchService = deps.searchService;
this._userContactOnlineTemplate = deps.userContactOnlineTemplate;
this._userContactOfflineTemplate = deps.userContactOfflineTemplate;
+ this._notificationService = deps.notificationService;
this._addSearchEventListeners();
}
@@ -39,7 +42,8 @@ export class UserView implements Observer {
update(d: ObserverData): void {
let html: string = "";
d.data.forEach((element: ActiveUserViewModel) => {
- element.online ? html += this._userContactOnlineTemplate(element) : html += this._userContactOfflineTemplate(element);
+ element.online ? html += this._userContactOnlineTemplate(element) :
+ html += this._userContactOfflineTemplate(element);
});
$(this._usersListElement).html(DOMPurify.sanitize(html));
this._addUserCallBacks();
@@ -53,10 +57,9 @@ export class UserView implements Observer {
private _addUserCallBacks(): void {
let userBoxes = document.getElementsByClassName('user-box');
- for (let i = 0; i < userBoxes.length; i++) {
- let userBox = userBoxes[i];
- userBoxes[i].addEventListener('click', this._userCallBack.bind(this, userBox));
- }
+
+ Array.from(userBoxes).forEach((ub: Element) =>
+ ub.addEventListener('click', this._userCallBack.bind(this, ub)))
}
@@ -109,10 +112,24 @@ export class UserView implements Observer {
JsonAPI.contactName = userName;
// @ts-ignore: Object is possibly 'null'.
document.getElementById('user-name-span').innerText = userName;
- this._chatModel.getMessages(userName, passphrase, null, "new");
+ let vm = this._model.activeUsersList.find(vm => vm.userName === userName);
+ if (!vm) {
+ vm = new ActiveUserViewModel();
+ vm.userName = userName;
+ }
+ this._notificationService.passphrasePrompt(vm, this._model.activeUsersList,
+ this._chatModel.getMessages.bind(this._chatModel),
+ this._promptHandler.bind(this));
+ // this._chatModel.getMessages(userName, vm.passphrase, null, "new");
el.className += " active";
}
+ private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) {
+ // vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName)
+ log.debug(vms)
+ this._model.notify();
+ }
+
private _addSearchButtonEL() {
this._userSearchButton.addEventListener('submit', (e) => {
e.preventDefault();
@@ -127,7 +144,7 @@ export class UserView implements Observer {
return;
}
let searchResult = this._searchService.search(list, searchTerm);
- this.update({data: searchResult, op: ""});
+ this.update({ data: searchResult, op: "" });
log.debug(searchResult);
})
}
@@ -148,7 +165,7 @@ export class UserView implements Observer {
this._userSearchInputElement.value = "";
this._userSearchCancelButton.hidden = true;
let list = this._model.activeUsersList;
- this.update({data: list, op: ""})
+ this.update({ data: list, op: "" })
})
}
diff --git a/chatto/src/main/javascript/ts/src/view/UserViewDeps.ts b/chatto/src/main/javascript/ts/src/view/UserViewDeps.ts
index 6b29880..491d07c 100644
--- a/chatto/src/main/javascript/ts/src/view/UserViewDeps.ts
+++ b/chatto/src/main/javascript/ts/src/view/UserViewDeps.ts
@@ -5,6 +5,7 @@ import { ChatModel } from "../model/ChatModel";
import { SearchService } from "../service/SearchService";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
+import { NotificationService } from "../service/NotificationService";
export interface UserViewDeps {
model: UserModel;
@@ -16,4 +17,5 @@ export interface UserViewDeps {
searchService: SearchService;
userContactOnlineTemplate: Handlebars.TemplateDelegate;
userContactOfflineTemplate: Handlebars.TemplateDelegate;
+ notificationService: NotificationService;
}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/ts/src/viewmodel/ActiveUserViewModel.ts b/chatto/src/main/javascript/ts/src/viewmodel/ActiveUserViewModel.ts
index 3ffdde5..555a62e 100644
--- a/chatto/src/main/javascript/ts/src/viewmodel/ActiveUserViewModel.ts
+++ b/chatto/src/main/javascript/ts/src/viewmodel/ActiveUserViewModel.ts
@@ -1,5 +1,7 @@
export class ActiveUserViewModel {
userName: string | undefined;
- online: boolean | undefined;
+ online: boolean = false;
+ unlocked: boolean = false;
+ passphrase: string = "";
lastActive: Date | undefined;
}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/yarn.lock b/chatto/src/main/javascript/yarn.lock
index c65bd9f..1e5a5d0 100644
--- a/chatto/src/main/javascript/yarn.lock
+++ b/chatto/src/main/javascript/yarn.lock
@@ -2,6 +2,13 @@
# yarn lockfile v1
+"@types/bootbox@^5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@types/bootbox/-/bootbox-5.2.0.tgz#0e51344914dbe2fbb5c720b3bc2797c467d2115a"
+ integrity sha512-aHKihTnKaYUqWvii5DlJDQqVud4Bp2fmfGKIaLRfsb/DzuLyvdFLnI9RKiXaNnVLQ0g6sorQZMwiV102r6UASw==
+ dependencies:
+ "@types/jquery" "*"
+
"@types/dompurify@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.0.0.tgz#9616caa5bf2569aea2e4889d4f929d968c081b40"
@@ -9,6 +16,13 @@
dependencies:
"@types/trusted-types" "*"
+"@types/jquery@*":
+ version "3.3.32"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.32.tgz#93e27fdc45dd38ee07f2f0acf34b59c1ccee036f"
+ integrity sha512-UKoof2mnV/X1/Ix2g+V2Ny5sgHjV8nK/UJbiYxuo4zPwzGyFlZ/mp4KaePb2VqQrqJctmcDQNA57buU84/2uIw==
+ dependencies:
+ "@types/sizzle" "*"
+
"@types/jquery@^3.3.31":
version "3.3.31"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.31.tgz#27c706e4bf488474e1cb54a71d8303f37c93451b"
@@ -258,6 +272,20 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+bootbox@^5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/bootbox/-/bootbox-5.4.0.tgz#2857a63c270b1b797d62e4c5597e74b497267655"
+ integrity sha512-GCPrDwZpJsUnqzrto3ZURVafypl13+DAyE3YZx5jR5EIoTDSyREPhr77hlCuPKvM6VvXR5Mh/34W3DYMC7JE9g==
+ dependencies:
+ bootstrap "^4.4.0"
+ jquery "^3.4.1"
+ popper.js "^1.16.0"
+
+bootstrap@^4.4.0:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
+ integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
+
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -1689,6 +1717,11 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+jquery@^3.4.1:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
+ integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
+
js-yaml@~3.13.0:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
@@ -2215,6 +2248,11 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+popper.js@^1.16.0:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
+ integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
+
posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
diff --git a/chatto/src/main/resources/templates/chat.html b/chatto/src/main/resources/templates/chat.html
index 054b907..509de84 100644
--- a/chatto/src/main/resources/templates/chat.html
+++ b/chatto/src/main/resources/templates/chat.html
@@ -256,7 +256,7 @@
-
{{userName}}
+
{{userName}} {{{lockIcon unlocked}}}
{{userName}} is online
@@ -273,7 +273,7 @@
-
{{userName}}
+
{{userName}} {{{lockIcon unlocked}}}
Last active {{fromNow lastActive}}
diff --git a/chatto/src/main/resources/templates/fragments/head.html b/chatto/src/main/resources/templates/fragments/head.html
index 768e113..430488d 100644
--- a/chatto/src/main/resources/templates/fragments/head.html
+++ b/chatto/src/main/resources/templates/fragments/head.html
@@ -14,6 +14,8 @@
+
+