UNPKG

globular-mvc

Version:

Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)

1,506 lines (1,278 loc) 56.4 kB
import { generatePeerToken, Model } from "./Model"; import * as resource from "globular-web-client/resource/resource_pb"; import * as authentication from "globular-web-client/authentication/authentication_pb" //import 'source-map-support/register' // That resolve the error map and give real source name and plage in function. import * as jwt from "jwt-decode"; import { ApplicationView } from "./ApplicationView"; import { Account } from "./Account"; import { NotificationType, Notification } from "./Notification"; import { UpdateOneRsp, DeleteOneRqst, CreateConnectionRqst, Connection, StoreType } from "globular-web-client/persistence/persistence_pb"; import { v4 as uuidv4 } from "uuid"; import { mergeTypedArrays, uint8arrayToStringMethod } from "./Utility"; import { ConversationManager } from "./Conversation"; import { Conversation, Conversations } from "globular-web-client/conversation/conversation_pb"; import { LogInfo, LogLevel, LogRqst, LogRsp, Occurence } from "globular-web-client/log/log_pb"; import { Session, SessionState } from "./Session"; import { formatBoolean } from "./components/utility"; import { DomPlatform } from "chart.js"; import { File } from "./File"; import { playVideo } from "./components/Video"; import { playAudio } from "./components/Audio"; import { FileExplorer, getAudioInfo, getLocalDir, getTitleInfo, getVideoInfo } from "./components/File"; import { Audio, Title, Video } from "globular-web-client/title/title_pb"; import { Globular } from "globular-web-client"; // Get the configuration from url function getFileConfig(url: string, callback: (obj: any) => void, errorcallback: (err: any) => void) { var xmlhttp = new XMLHttpRequest(); xmlhttp.timeout = 1500 xmlhttp.onreadystatechange = function () { if (this.readyState == 4 && (this.status == 201 || this.status == 200)) { var obj = JSON.parse(this.responseText); callback(obj); } else if (this.readyState == 4) { // Here no configuration was found... so I will ask the user give the errorcallback("fail to get the configuration file at url " + url + " status " + this.status) } }; url += "?domain=" + Model.domain // application is not know at this time... if (localStorage.getItem("user_token") != undefined) { url += "&token=" + localStorage.getItem("user_token") } xmlhttp.open("GET", url, true); xmlhttp.setRequestHeader("domain", Model.domain); xmlhttp.send(); } /** * That class can be use to create any other application. */ export class Application extends Model { public static uuid: string; public static language: string; private static infos: Map<string, any>; private contactsListener: any; private _appConfig: any; // contain value defines in the config.json file of the application if one is found. public get appConfig(): any { return this._appConfig; } public set appConfig(value: any) { this._appConfig = value; } private _name: string; public get name(): string { return this._name; } public set name(value: string) { this._name = value; } private _title: string; public get title(): string { return this._title; } public set title(value: string) { this._title = value; } // those informations are keep in mongo db. // The id of the application on mongodb. private _id: string; public get id(): string { return this._id; } public set id(value: string) { this._id = value; } // The path of the application on the server. private _path: string; public get path(): string { return this._path; } public set path(value: string) { this._path = value; } public static account: Account; /** * Create a new application with a given name. The view * can be any ApplicationView or derived ApplicationView class. * @param name The name of the application. */ constructor(name: string, title: string, view: ApplicationView) { super(); // set the application name. Model.application = name; // The map of contact listener. this.contactsListener = {} // set the application view in it model. view.application = this; // generate client uuid, this is use to set information about a client. if (localStorage.getItem("globular_client_uuid") == undefined) { Application.uuid = uuidv4(); localStorage.setItem("globular_client_uuid", Application.uuid); } else { Application.uuid = localStorage.getItem("globular_client_uuid"); } // The application name. this.name = name; this.title = title; this.view = view; view.setTitle(title); // set the title. // Set the application theme... let theme = localStorage.getItem("globular_theme") if (theme != null) { let html = document.querySelector("html") html.setAttribute("theme", theme) } } /** * Get value from config.json file if any... * @param callback */ initApplicationConfig(callback: (config: any) => void, errorCallback: (err: any) => void) { // remove let url = window.location.toString() url = url.replace("index.html", "") getFileConfig(url + "config.json", (config: any) => { callback(config) }, errorCallback) } /** * Connect the listner's and call the initcallback. * @param url the backend url. * @param initCallback * @param errorCallback * @param configurationPort */ init( url: string, initCallback: () => void, errorCallback: (err: any) => void ) { let init_ = () => { // So here I will intercept all error and log it on the server log. // This error will be available in the setting -> error(s) window.onerror = (message, source, lineno, colno, error) => { let info = new LogInfo info.setLevel(LogLevel.ERROR_MESSAGE) let occurence = new Occurence occurence.setDate(Math.trunc(Date.now() / 1000)) occurence.setApplication(Application.application) occurence.setUserid("") occurence.setUsername("") if (Application.account != undefined) { occurence.setUserid(Application.account.id) occurence.setUsername(Application.account.name) } if (error == undefined) { return } info.setMethod(error.name + " " + error.message) info.setMessage(error.stack.toString()) let rqst = new LogRqst rqst.setInfo(info) rqst.setOccurence(occurence) Model.globular.logService.log(rqst, { token: localStorage.getItem("user_token"), application: Model.application, domain: Model.domain, address: Model.address }).then((rsp: LogRsp) => { }) .catch((err: any) => { ApplicationView.displayMessage(err, 3000) }) }; // Here I will connect the listener's // The login event. Model.eventHub.subscribe( "login_event_", (uuid: string) => { }, (evt: any) => { // Here I will try to login the user. this.login( evt.userId, evt.pwd, (account: Account) => { // Here I will send a login success. Model.eventHub.publish("login_event", account, true); }, (err: any) => { ApplicationView.displayMessage(err, 4000); } ); }, true, this ); Model.eventHub.subscribe( "logout_event_", (uuid: string) => { }, (evt: any) => { Application.logout(); }, true, this ); // The register event. Model.eventHub.subscribe( "settings_event_", (uuid: string) => { }, (evt: any) => { this.settings(); }, true, this ); // The register event. Model.eventHub.subscribe( "register_event_", (uuid: string) => { }, (evt: any) => { // Here I will try to login the user. this.register( evt.userId, evt.email, evt.pwd, evt.repwd, evt.domain, (data: any) => { }, (err: any) => { ApplicationView.displayMessage(err, 4000); } ); }, true, this ); // That listener is use to keep the client up to date with the server. // if an accout is connect it will not restart it session automaticaly... Model.eventHub.subscribe( `update_${Application.domain}_${Application.application}_evt`, (uuid: string) => { }, (version: string) => { if (Application.account == undefined) { // reload the page... location.reload(); } else { // ApplicationView.displayMessage(` <div style="display: flex; flex-direction: column"> <div>A new version of <span style="font-weight: 500;"> ${Application.application}</span> (v.${version}) is available. </div> <div> Press <span style="font-weight: 500;">f5</span> to refresh the page. </div> </div> `, 10 * 1000); } }, false, this ); // Invite contact event. Model.eventHub.subscribe( "send_conversation_invitation_event_", (uuid: string) => { }, (evt: any) => { // Here I will try to login the user. this.onInviteParticipant(evt); }, true, this ); // Invite contact event. Model.eventHub.subscribe( "invite_contact_event_", (uuid: string) => { }, (contact: Account) => { // Here I will try to login the user. this.onInviteContact(contact); }, true, this ); // Revoke contact invitation. Model.eventHub.subscribe( "revoke_contact_invitation_event_", (uuid: string) => { }, (contact: Account) => { // Here I will try to login the user. this.onRevokeContactInvitation(contact); }, true, this ); // Accept contact invitation Model.eventHub.subscribe( "accept_contact_invitation_event_", (uuid: string) => { }, (contact: Account) => { // Here I will try to login the user. this.onAcceptContactInvitation(contact); }, true, this ); // Decline contact invitation Model.eventHub.subscribe( "decline_contact_invitation_event_", (uuid: string) => { }, (contact: Account) => { // Here I will try to login the user. this.onDeclineContactInvitation(contact); }, true, this ); // Decline contact invitation Model.eventHub.subscribe( "delete_contact_event_", (uuid: string) => { }, (contact: Account) => { // Here I will try to login the user. this.onDeleteContact(contact); }, true, this ); // Delete user notification. Model.eventHub.subscribe( "delete_notification_event_", (uuid: string) => { }, (notification: any) => { notification = Notification.fromObject(notification); this.removeNotification(notification); }, true, this ); Model.eventHub.subscribe( "follow_link_event_", (uuid: string) => { }, (lnk: any) => { let openFileExplorer = (globule: Globular, file: File, callback: (dir: File, explorer: FileExplorer) => void) => { // So the file is not a stream I will open the file explorer... // The file explorer object. let fileExplorer = new FileExplorer(globule); // Set the file explorer... fileExplorer.init((shared: any, public_: any) => { let dir = shared[file.path.split("/")[2]] if (dir) { Model.eventHub.publish("__set_dir_event__", { dir: dir, file_explorer_id: fileExplorer.id }, true) callback(dir, fileExplorer) } else { File.readDir(file.path, false, (dir: File) => { Model.eventHub.publish("__set_dir_event__", { dir: dir, file_explorer_id: fileExplorer.id }, true) callback(dir, fileExplorer) }, err => { }, globule) } }); // Set the onerror callback for the component. fileExplorer.onerror = (err: any) => { return ApplicationView.displayMessage(err, 4000) }; fileExplorer.onclose = () => { // Remove the file explorer. fileExplorer.parentNode.removeChild(fileExplorer) fileExplorer.delete() // remove all listeners. fileExplorer = null; } Model.eventHub.publish("_open_file_explorer_event_", fileExplorer, true) fileExplorer.open() } let globule = Model.getGlobule(lnk.domain) File.getFile(globule, lnk.path, -1, -1, (file: any) => { if (file.mime.startsWith("video")) { getTitleInfo(file, (titles: Title[]) => { if (titles) { playVideo(file.path, () => { }, () => { }, titles[0], globule) } else { getVideoInfo(file, (videos: Video[]) => { playVideo(file.path, () => { }, () => { }, videos[0], globule) }) } }) } else if (file.mime.startsWith("audio")) { getAudioInfo(file, (audios: Audio[]) => { playAudio(file.path, () => { }, () => { }, audios[0], globule) }) } else if (file.mime == "inode/directory") { File.getFile(file.globule, file.path + "/playlist.m3u8", -1, -1, f => { getVideoInfo(file, (videos: Video[]) => { playVideo(file.path, () => { }, () => { }, videos[0], globule) }) }, err => { openFileExplorer(globule, file, (dir: File, explorer: FileExplorer) => { }) }) } else if (file.mime.startsWith("text/") || file.mime == "application/pdf" || file.mime == "application/json") { let path = file.path.substring(0, file.path.lastIndexOf("/")); File.getFile(file.globule, path, -1, -1, f => { openFileExplorer(file.globule, f, (dir: File, explorer: FileExplorer) => { /** nothing */ explorer.readFile(file); }) }, err => ApplicationView.displayMessage(err, 3000)) } else if (file.mime.startsWith("image/")) { let path = file.path.substring(0, file.path.lastIndexOf("/")); File.getFile(file.globule, path, -1, -1, f => { openFileExplorer(file.globule, f, (dir: File, explorer: FileExplorer) => { /** nothing */ setTimeout(() => { explorer.setDir(dir, () => { explorer.showImage(file) }) }, 1000) }) }, err => ApplicationView.displayMessage(err, 3000)) } else { // Open the file location if no reader's are available... let path = file.path.substring(0, file.path.lastIndexOf("/")); File.getFile(file.globule, path, -1, -1, f => { openFileExplorer(file.globule, f, (dir: File, explorer: FileExplorer) => { /** nothing */ explorer.setDir(dir, null) }) }, err => ApplicationView.displayMessage(err, 3000)) } }, err => ApplicationView.displayMessage(err, 3000)) }, true, this ); // Get backend application infos. Application.getAllApplicationInfo( (infos: Array<any>) => { if (initCallback != undefined) { let appInfo = Application.getApplicationInfo(this.name); if (appInfo != undefined) { (<ApplicationView>this.view).setIcon( Application.getApplicationInfo(this.name).getIcon() ); this.view.init(); } else { console.log( "no application information found for " + this.name + " make sure your application has the correct name in your class derived from Application!" ); } initCallback(); } }, (err: any) => { ApplicationView.displayMessage(err, 3000) } ); // Connect automatically... let rememberMe = localStorage.getItem("remember_me"); if (rememberMe) { // Here I will renew the last token... let userId = localStorage.getItem("user_id"); let userEmail = localStorage.getItem("user_email"); let userName = localStorage.getItem("user_name"); let userDomain = localStorage.getItem("user_domain"); let userInfo = localStorage.getItem(userId); let userFirstName = "" let userLastName = "" let userMiddleName = "" let userProfilePicture = "" if (userInfo) { let userInfo_ = JSON.parse(userInfo) userFirstName = userInfo_["firstName_"] userLastName = userInfo_["lastName_"] userMiddleName = userInfo_["middleName_"] userProfilePicture = userInfo_["profilePicture_"] } ApplicationView.wait( "<div>log in</div><div>" + userName + "</div><div>...</div>" ); Application.account = new Account(userId, userEmail, userName, userDomain, userFirstName, userLastName, userMiddleName, userProfilePicture) this.refreshToken( (account: Account) => { // send a refresh token event. // Model.eventHub.publish("refresh_token_event", account, true); Model.eventHub.publish("login_event", account, true); // When new contact is accepted. Model.getGlobule(Application.account.domain).eventHub.subscribe("accepted_" + account.id + "@" + account.domain + "_evt", (uuid) => { }, (evt) => { let invitation = JSON.parse(evt); this.addContactListener(invitation) }, false, this) account.session.state = SessionState.Online Model.publish(`__session_state_${account.id + "@" + account.domain}_change_event__`, account.session, true) Model.getGlobule(account.domain).eventHub.subscribe("deleted_" + account.id + "@" + account.domain + "_evt", (uuid) => { }, (evt) => { let invitation = JSON.parse(evt); Model.getGlobule(account.domain).eventHub.unSubscribe(`session_state_${invitation._id}_change_event`, this.contactsListener[invitation._id]) }, false, this) // retreive the contacts Account.getContacts(account, `{"status":"accepted"}`, (contacts: Array<Account>) => { contacts.forEach(contact => { this.addContactListener(contact) }) }, (err: any) => { ApplicationView.displayMessage(err, 3000) }) // Retreive conversations... ConversationManager.loadConversations(account, (conversations: Array<Conversation>) => { Model.eventHub.publish("__load_conversations_event__", conversations, true) }, (err: any) => { /* this.displayMessage(err, 3000)*/ /** no conversation found... */ }) // Connect to to the conversation manager. ConversationManager.connect((err: any) => { ApplicationView.displayMessage(err, 3000) }) this.initNotifications(); this.startRefreshToken(); }, (err: any) => { ApplicationView.displayMessage(err, 4000); ApplicationView.resume(); } ); } else { // simply remove invalid token and user infos. localStorage.removeItem("remember_me"); localStorage.removeItem("user_token"); localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); localStorage.removeItem("user_email"); localStorage.removeItem("token_expired"); } } // Initialise the application configuration from it config.json file. // if no file was found the config is simply set to an empty object. // Config.json must contain only application setting ex backend address // It's preferable to store any application data into it database. this.initApplicationConfig((config: any) => { // keep the config in appConfig member. this.appConfig = config; if (this.appConfig.GlobularConfigurationAddress != undefined) { super.init(this.appConfig.GlobularConfigurationAddress, init_, errorCallback); } else { // use the default url here in that case. super.init(url, init_, errorCallback); } }, err => { this.appConfig = {}; super.init(url, init_, errorCallback); }) } /** * Return the list of all applicaitons informations. * @param callback * @param errorCallback */ static getAllApplicationInfo( callback: (infos: Array<any>) => void, errorCallback: (err: any) => void, force: boolean = false ) { if (Application.infos != undefined && !force) { if (Application.infos.size != 0) { callback(Array.from(Application.infos.values())); return } } const rqst = new resource.GetApplicationsRqst(); const stream = Model.globular.resourceService.getApplications(rqst, { application: Model.application.length > 0 ? Model.application : Model.globular.config.IndexApplication, domain: Model.domain, address: Model.address }); let applications = new Array<resource.Application>(); stream.on("data", (rsp: resource.GetApplicationsRsp) => { applications = applications.concat(rsp.getApplicationsList()); }); stream.on("status", (status) => { if (status.code === 0) { Application.infos = new Map<string, resource.Application>(); for (var i = 0; i < applications.length; i++) { // Keep application info up to date. Application.eventHub.subscribe(`update_application_${applications[i].getId()}_settings_evt`, (uuid: string) => { }, (__application_info__: string) => { // Set the icon... let info = JSON.parse(__application_info__) Application.infos.set(applications[i].getId(), info); }, false) Application.infos.set(applications[i].getId(), applications[i]); } callback(applications); } else { errorCallback({ message: status.details }); } }); } /** * Return application infos * @param id */ static getApplicationInfo(id: string): resource.Application { // TODO manage application domain... if (id.indexOf("@") != -1) { return Application.infos.get(id.split("@")[0]); } return Application.infos.get(id); } /** * Return partial information only. */ toString(): string { let obj = { name: this.name, title: this.title, icon: "" }; return JSON.stringify(obj); } ///////////////////////////////////////////////////// // Account releated functionality. ///////////////////////////////////////////////////// /** * Display true if a session is open. */ public get isLogged(): boolean { return Application.account != null; } /** * Refresh the token and open a new session if the token is valid. */ private refreshToken( initCallback: (account: Account) => void, onError: (err: any) => void ) { let rqst = new authentication.RefreshTokenRqst(); let existingToken = localStorage.getItem("user_token"); if (existingToken == undefined) { onError("No token found to be refresh!"); return; } if (existingToken.length == 0) { onError("Invalid token found!"); localStorage.removeItem("user_token"); return; } rqst.setToken(existingToken); Model.globular.authenticationService .refreshToken(rqst) .then((rsp: authentication.RefreshTokenRsp) => { // Refresh the token at session timeout let token = rsp.getToken(); let decoded = jwt(token); let id = (<any>decoded).id; let userName = (<any>decoded).username; let email = (<any>decoded).email; let domain = (<any>decoded).user_domain; // here I will save the user token and user_name in the local storage. localStorage.setItem("user_token", token); localStorage.setItem("token_expired", (<any>decoded).exp); localStorage.setItem("user_id", id); localStorage.setItem("user_name", userName); localStorage.setItem("user_email", email); // Set the account Account.getAccount(userName + "@" + domain, (account: Account) => { Application.account = account; initCallback(Application.account); }, onError); }) .catch((err) => { // remove old information in that case. localStorage.removeItem("user_token"); localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); localStorage.removeItem("user_email"); localStorage.removeItem("token_expired"); localStorage.removeItem("remember_me"); ApplicationView.displayMessage(err, 3000) onError(err); }); } /** * Refresh the token to keep it usable. */ private startRefreshToken() { let expireAt = parseInt(localStorage.getItem("token_expired"), 10) let now = Math.floor(Date.now() / 1000) let delay = (expireAt - now) * 1000// in second (10 second to give time to token to refresh before error) let __setTimeout = setTimeout(() => { this.refreshToken( (account: Account) => { Application.account = account; // remove resource... clearTimeout(__setTimeout); // go for next loop... this.startRefreshToken() }, (err: any) => { // simply display the error on the view. ApplicationView.displayMessage(err, 4000); // Stop runing... clearTimeout(__setTimeout); } ); }, delay - 10 * 1000); // refresh 10 sec before exipire timeout. } /** * Register a new account with the application. * @param name The account name * @param email The account email * @param password The account password */ register( name: string, email: string, password: string, confirmPassord: string, domain: string, onRegister: (account: Account) => void, onError: (err: any) => void ): Account { // Create the register request. let rqst = new resource.RegisterAccountRqst(); rqst.setConfirmPassword(confirmPassord); let account = new resource.Account(); account.setPassword(password); account.setEmail(email); account.setName(name); account.setId(name) account.setDomain(domain) rqst.setAccount(account); ApplicationView.wait( "<div>register account </div><div>" + name + "</div><div>...</div>" ); // Register a new account. Model.globular.resourceService .registerAccount(rqst) .then((rsp: resource.RegisterAccountRsp) => { // Here I will set the token in the localstorage. let token = rsp.getResult(); let decoded = jwt(token); // here I will save the user token and user_name in the local storage. localStorage.setItem("user_token", token); localStorage.setItem("user_id", (<any>decoded).id); localStorage.setItem("user_name", (<any>decoded).username); localStorage.setItem("token_expired", (<any>decoded).exp); localStorage.setItem("user_email", (<any>decoded).email); localStorage.setItem("user_domain", (<any>decoded).user_domain); let rqst = new CreateConnectionRqst let connectionId = name.split("@").join("_").split(".").join("_"); let address = (<any>decoded).address; let domain = (<any>decoded).domain; let globule = Model.getGlobule(address) // So here i will open the use database connection. let connection = new Connection connection.setId(connectionId) connection.setUser(connectionId) connection.setPassword(password) connection.setStore(StoreType.MONGO) connection.setName(name) connection.setPort(globule.config.BackendPort) connection.setTimeout(60) connection.setHost(domain) rqst.setConnection(connection) globule.persistenceService.createConnection(rqst, { token: localStorage.getItem("user_token"), application: Model.application, domain: domain, address: address }).then(() => { // Callback on login. Application.account = new Account(name, email, name, domain, "", "", "", ""); Application.account.initData( (account: Account) => { // Here I will send a login success. Model.eventHub.publish("login_event", account, true); onRegister(Application.account); this.initNotifications(); this.startRefreshToken(); ApplicationView.resume(); }, (err: any) => { Model.eventHub.publish("login_event", account, true); onRegister(Application.account); this.initNotifications(); this.startRefreshToken(); ApplicationView.resume(); onError(err); } ); }).catch(err => { ApplicationView.resume(); onError(err); }) }) .catch((err: any) => { ApplicationView.resume(); onError(err); }); return null; } // Display message when contact state change. addContactListener(contact: any) { Account.getAccount(contact._id, (account: Account) => { let globule = Model.getGlobule(account.domain) if (globule) { // subscribe to session event change. globule.eventHub.subscribe(`session_state_${account.id + "@" + account.domain}_change_event`, (uuid: string) => { this.contactsListener[contact._id] = uuid; }, (evt: string) => { if (account.session) { const obj = JSON.parse(evt) account.session.lastStateTime = new Date(obj.lastStateTime * 1000) account.session.state = obj.state // Here I will ask the user for confirmation before actually delete the contact informations. let toast = <any>ApplicationView.displayMessage( ` <style> #contact-session-info-box{ display: flex; flex-direction: column; } #contact-session-info-box globular-contact-card{ padding-bottom: 10px; } #contact-session-info-box div{ display: flex; font-size: 1.2rem; padding-bottom: 10px; } </style> <div id="contact-session-info-box"> <div>Session state change... </div> <globular-contact-card contact="${account.id + "@" + account.domain}"></globular-contact-card> </div>`, 5000 // 15 sec... ) } }, false, this) } else { console.log("fail to connect to ", account.domain) } }, err => console.log(err)) } /** * Login into the application * @param email * @param password */ login( userId: string, password: string, onLogin: (account: Account) => void, onError: (err: any) => void ) { let globule = Model.globular if (userId.indexOf("@") != -1) { globule = Model.getGlobule(userId.split("@")[1]) } let rqst = new authentication.AuthenticateRqst(); rqst.setName(userId); rqst.setPassword(password); rqst.setIssuer(Model.globular.config.Mac) // The token will be issue for the current globule. ApplicationView.wait("<div>log in</div><div>" + userId + "</div><div>...</div>"); globule.authenticationService .authenticate(rqst) .then((rsp: authentication.AuthenticateRsp) => { // Here I will set the token in the localstorage. let token = rsp.getToken(); let decoded = jwt(token); let userName = (<any>decoded).username; let email = (<any>decoded).email; let id = (<any>decoded).id; let address = (<any>decoded).address; let userDomain = (<any>decoded).user_domain; // here I will save the user token and user_name in the local storage. localStorage.setItem("user_token", token); localStorage.setItem("token_expired", (<any>decoded).exp); localStorage.setItem("user_email", email); localStorage.setItem("user_name", userName); localStorage.setItem("user_id", id); localStorage.setItem("user_domain", userDomain); let rqst = new CreateConnectionRqst let connectionId = userName.split("@").join("_").split(".").join("_"); // So here i will open the use database connection. let connection = new Connection connection.setId(connectionId) connection.setUser(connectionId) connection.setPassword(password) connection.setStore(StoreType.MONGO) connection.setName(id) connection.setPort(globule.config.BackendPort) connection.setTimeout(60) connection.setHost(userDomain) rqst.setConnection(connection) globule.persistenceService.createConnection(rqst, { token: token, application: Model.application, domain: Model.domain, address: address }).then(() => { let userInfo = localStorage.getItem(id); let userFirstName = "" let userLastName = "" let userMiddleName = "" let userProfilePicture = "" if (userInfo) { let userInfo_ = JSON.parse(userInfo) userFirstName = userInfo_["firstName_"] userLastName = userInfo_["lastName_"] userMiddleName = userInfo_["middleName_"] userProfilePicture = userInfo_["profilePicture_"] } Application.account = new Account(id, email, userName, userDomain, userFirstName, userLastName, userMiddleName, userProfilePicture); Account.getAccount(userId + "@" + userDomain, (account: Account) => { Application.account = account; // so here I will get the session for the account... Application.account.session.state = SessionState.Online; Application.account.session.lastStateTime = new Date() Application.account.session.save(() => { }, (err: any) => { ApplicationView.displayMessage(err, 3000) }) onLogin(account); // When new contact is accepted. Model.getGlobule(Application.account.domain).eventHub.subscribe("accepted_" + account.id + "@" + account.domain + "_evt", (uuid) => { }, (evt) => { let invitation = JSON.parse(evt); this.addContactListener(invitation) }, false, this) Model.getGlobule(account.domain).eventHub.subscribe("deleted_" + account.id + "@" + account.domain + "_evt", (uuid) => { }, (evt) => { let invitation = JSON.parse(evt); let domain = invitation._id.split("@")[1] Model.getGlobule(domain).eventHub.unSubscribe(`session_state_${invitation._id}_change_event`, this.contactsListener[invitation._id]) }, false, this) Model.eventHub.publish(`__session_state_${Application.account.id + "@" + Application.account.domain}_change_event__`, Application.account.session, true) // retreive the contacts Account.getContacts(Application.account, `{"status":"accepted"}`, (contacts: Array<Account>) => { contacts.forEach(contact => { this.addContactListener(contact) }) }, (err: any) => { ApplicationView.displayMessage(err, 3000) }) // Retreive conversations... ConversationManager.loadConversations(Application.account, (conversations: Array<Conversation>) => { Model.eventHub.publish("__load_conversations_event__", conversations, true) }, (err: any) => { /* this.displayMessage(err, 3000)*/ /** no conversation found... */ }) // Connect to to the conversation manager. ConversationManager.connect((err: any) => { ApplicationView.displayMessage(err, 3000) }) //ApplicationView.resume(); this.initNotifications(); // Start refresh as needed. this.startRefreshToken(); }, (err: any) => { ApplicationView.resume(); onError(err); }) }).catch((err: any) => { console.log("fail to create the connection ", err) }) }) .catch((err) => { ApplicationView.resume(); onError(err); }); } /////////////////////////////////////////////////////////////// // Application close funtions. ////////////////////////////////////////////////////////////// /** * Close the current session explicitelty. */ static logout() { // Send local event. if (Application.account != undefined) { // refresh the page to be sure all variable are clear... ApplicationView.wait("bye bye " + Application.account.name + "!") // So here I will set the account session state to onlise. Application.account.session.state = SessionState.Offline; Model.eventHub.publish("logout_event", Application.account, true); Model.eventHub.publish(`__session_state_${Application.account.id + "@" + Application.account.domain}_change_event__`, Application.account.session, true) Model.getGlobule(Application.account.domain).eventHub.publish(`session_state_${Application.account.id + "@" + Application.account.domain}_change_event`, Application.account.session.toString(), false) // Set room to undefined. Application.account = null; } // remove token informations localStorage.removeItem("remember_me"); localStorage.removeItem("user_token"); localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); localStorage.removeItem("user_email"); localStorage.removeItem("token_expired"); setTimeout(() => { ApplicationView.resume() window.location.reload(); }, 1000) } /** * Exit application. */ exit() { // Close the view. if (this.view != undefined) { this.view.close(); } // remove token informations localStorage.removeItem("remember_me"); localStorage.removeItem("user_token"); localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); localStorage.removeItem("user_email"); localStorage.removeItem("token_expired"); } /** * Settings application. */ settings() { } /** * That function must be use to update application information store * in level db in local_ressource table. * @param id * @param info */ static saveApplicationInfo(id: string, info: any, successCallback: (infos: any) => void, errorCallback: (err: any) => void) { if (Object.keys(info).length == 0) { errorCallback("Nothing has change!") return; } let info_ = Application.infos.get(id) let value = "" let i = 0; for (var field in info) { const v = info[field] if (typeof v === 'string' || v instanceof String) { value += `"${field}":"${v}"` } else { value += `"${field}":${v}` } i++ if (i < Object.keys(info).length) { value += ", " } info_[field] = info[field] } value = `{"$set":{${value}}}` // Get the actual value and set values from info. const rqst = new resource.UpdateApplicationRqst rqst.setApplicationid(id) rqst.setValues(value) Model.globular.resourceService.updateApplication(rqst, { token: localStorage.getItem("user_token"), application: Model.application, domain: Model.domain, address: Model.address }).then((rsp: UpdateOneRsp) => { successCallback(info_); }).catch(errorCallback) } /////////////////////////////////////////////////////////////////////////////////////////// // Notifications /////////////////////////////////////////////////////////////////////////////////////////// /** * Initialyse the user and application notifications */ private initNotifications() { // Initialyse application notifications. this.getNotifications( NotificationType.Application, (notifications: Array<Notification>) => { Model.eventHub.publish( "set_application_notifications_event", notifications, true ); }, (err: any) => { ApplicationView.displayMessage(err, 4000); } ); this.getNotifications( NotificationType.User, (notifications: Array<Notification>) => { Model.eventHub.publish( "set_user_notifications_event", notifications, true ); }, (err: any) => { ApplicationView.displayMessage(err, 4000); } ); } /** * Send application notifications. * @param notification The notification can contain html text. */ static sendNotifications( notification: Notification, callback: () => void, onError: (err: any) => void ) { // first of all I will save the notificaiton. let rqst = new resource.CreateNotificationRqst // init the notification infos. let notification_ = new resource.Notification notification_.setId(notification.id) notification_.setDate(Math.floor(notification.date.getTime() / 1000)) notification_.setMessage(notification.text) notification_.setRecipient(notification.recipient) if (notification.type == NotificationType.Application) { notification_.setSender(Model.application) notification_.setNotificationType(resource.NotificationType.APPLICATION_NOTIFICATION) } else { notification_.setNotificationType(resource.NotificationType.USER_NOTIFICATION) if (notification.sender.length == 0) { notification_.setSender(Application.account.id + "@" + Application.account.domain) } else { notification_.setSender(notification.sender) } if (notification.mac.length == 0) { notification_.setMac(Model.getGlobule(Application.account.domain).config.Mac) } else { notification_.setMac(notification.mac) } } rqst.setNotification(notification_) let globule = Application.getGlobule(notification.recipient.split("@")[1]) globule.resourceService .createNotification(rqst, { token: localStorage.getItem("user_token"), application: Model.application, domain: Model.domain, address: Model.address }) .then(() => { // Here I will throw a network event... // Publish the notification Model.publish( notification.recipient + "_notification_event", notification.toString(), false ); if (callback != undefined) { callback(); } }) .catch((err: any) => { onError(err); }); } /** * Retreive the list of nofitications * @param callback The success callback with the list of notifications. * @param errorCallback The error callback with the error message. */ getNotifications( type: NotificationType, callback: (notifications: Array<Notification>) => void, errorCallback: (err: any) => void ) { let globule = Model.getGlobule(Application.account.domain) generatePeerToken(globule, token => { // So here I will get the list of notification for the given type. let rqst = new resource.GetNotificationsRqst if (type == NotificationType.Application) { rqst.setRecipient(Model.application) } else { rqst.setRecipient(Application.account.id) globule = Model.getGlobule(Application.account.domain) } let stream = globule.resourceService.getNotifications(rqst, { token: token, application: Model.application, domain: Model.domain, address: Model.address }); let notifications = new Array<resource.Notification>(); stream.on("data", (rsp: resource.GetNotificationsRsp) => { notifications = notifications.concat(rsp.getNotificationsList()) }); stream.on("status", (status) => { if (status.code == 0) { let notifications_ = new Array<Notification>(); for (var i = 0; i < notifications.length; i++) { // Here I will convert the notification from resource object to // my own Notification type. let n = new Notification(notifications[i].getMac(), notifications[i].getSender(), notifications[i].getNotificationType().valueOf(), notifications[i].getRecipient(), notifications[i].getMessage(), new Date(notifications[i].getDate() * 1000)) n.id = notifications[i].getId(); notifications_.push(n); } callback(notifications_) } else { // In case of error I will return an empty array callback([]); } }); }, err => ApplicationView.displayMessage(err, 3000)) } removeNotification(notification: Notification) { let rqst = new resource.DeleteNotificationRqst let globule = Model.globular if (notification.type == NotificationType.User) { globule = Model.getGlobule(Application.account.domain) } rqst.setId(notification.id) rqst.setRecipient(notification.recipient) globule.resourceService .deleteNotification(rqst, { token: localStorage.getItem("user_token"), application: Model.application, domain: Model.domain, address: Model.address }) .then(() => { // The notification is not deleted so I will send network event to remove it from // the display. Model.publish( notification.id + "_delete_notification_event", notification.toString(), false ); }) .catch((err: any) => { ApplicationView.displayMessage(err, 4000); }); } /** * Remove all notification. */ clearNotifications(type: NotificationType) { } /////////////////////////////////////////////////////////////////// // Invite to a conversation. /////////////////////////////////////////////////////////////////// onInviteParticipant(evt: any) { let participant