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,535 lines (1,198 loc) • 46.4 kB
text/typescript
import { View } from "./View";
import * as M from "materialize-css";
import "materialize-css/sass/materialize.scss";
import { Account } from "./Account";
import { ApplicationsSettings, GroupSettings, LogSettings, OrganizationSettings, PeersSettings, ResourcesPermissionsSettings, RoleSettings, UserSettings, UsersSettings, VideoSettings } from "./Settings"
import { Model } from "./Model";
import { DockerNames } from "./components/RandomName"
// web-components.
import { Layout } from "./components/Layout";
import { Login } from "./components/Login";
import { AccountMenu } from "./components/Account";
import { NotificationMenu } from "./components/Notification";
import { ApplicationsMenu } from "./components/Applications";
import { Camera } from "./components/Camera";
import { FilesMenu, FilesUploader } from "./components/File";
import { SystemInfosMenu } from "./components/SystemMonitor";
import { SearchBar, SearchResults } from "./components/Search";
import { ContactCard, ContactsMenu } from "./components/Contact";
import { MessengerMenu, Messenger } from "./components/Messenger";
import { CallsHistoryMenu } from "./components/Calls"
import { SettingsMenu, SettingsPanel, StringListSetting } from "./components/Settings";
import { Application } from "./Application";
// Not directly use here but must be include anyways
import { Wizard } from "./components/Wizard";
import { SlideShow } from "./components/SlideShow";
import { ImageCropper } from "./components/Image";
import "./components/table/table.js"
import { Conversation, Invitation } from "globular-web-client/conversation/conversation_pb";
import { ConversationManager } from "./Conversation";
import { BlogPostElement, BlogEditingMenu } from "./components/BlogPost"
import { Terminal } from "./components/Terminal"
import { Workspace } from './components/Workspace'
import { WatchingMenu } from './components/watching'
import { ContentManager } from './components/Content'
import { ShareMenu } from './components/Share'
// This variable is there to give acces to wait and resume...
const nameGenrator = new DockerNames();
// Must be imported to overide the materialyse style
import "./style.css"
import { ServerGeneralSettings } from "./serverGeneralSettings";
import { ServicesSettings } from "./ServicesSettings";
import * as getUuid from 'uuid-by-string'
import { GetBlogPostsRequest } from "globular-web-client/blog/blog_pb";
// The kind of application...
export enum ApplicationType {
WEB, DESKTOP, MOBILE
}
/**
* Application view made use of Web-component and Materialyse to create a basic application
* layout that can be use as starting point to any web-application.
*/
export class ApplicationView extends View {
private _workspace_childnodes: Array<any>;
private _sidemenu_childnodes: Array<any>;
private settings_event_listener: String;
private save_settings_event_listener: String;
private _application: Application;
public get application(): Application {
return this._application;
}
public set application(value: Application) {
this._application = value;
}
/** The application type */
private _type: ApplicationType;
public get type(): ApplicationType {
return this._type;
}
public set type(value: ApplicationType) {
this._type = value;
}
/** Display upload activities */
private static filesUploader: FilesUploader;
/** The application view component. */
private static layout: Layout;
/** The application internal layout */
private workspace_: Workspace;
/** The login panel */
private login_: Login;
/** The account panel */
private accountMenu: AccountMenu;
/** The call history menu */
private callsHistoryMenu: CallsHistoryMenu;
/** The nofitication panel */
private notificationMenu: NotificationMenu;
/** The applications menu */
private applicationsMenu: ApplicationsMenu;
protected hasApplicationsMenu: boolean;
/** The contact menu */
private contactsMenu: ContactsMenu;
/** The file menu */
private filesMenu: FilesMenu;
/** The share menu */
private shareMenu: ShareMenu;
/** The watching content */
private watchingMenu: WatchingMenu;
/** The blog editing menu */
private blogEditingMenu: BlogEditingMenu;
/** The messenger menu */
private messengerMenu: MessengerMenu;
/** Don't shoot the messenger! */
private messenger: Messenger;
/** The settings Menu */
protected settingsMenu: SettingsMenu;
/** The system infos menu */
private systemInfosMenu: SystemInfosMenu;
/** The settings Panel */
protected settingsPanel: SettingsPanel;
/** Keep title content between login and logout... */
private _title_: any;
/** The content editor... */
private contentManager: ContentManager;
/** The type of application... */
/** The camera */
private _camera: Camera;
public get camera(): Camera {
return this._camera;
}
/** The search bar */
private _searchBar: SearchBar;
public get searchBar(): SearchBar {
return this._searchBar;
}
/** The search result panel */
private _searchResults: SearchResults
public get searchResults(): SearchResults {
return this._searchResults;
}
/** various listener's */
private login_event_listener: string;
private logout_event_listener: string;
private _isLogin: boolean;
public get isLogin(): boolean {
return this._isLogin;
}
public set isLogin(value: boolean) {
this._isLogin = value;
}
private icon: any;
/** The current displayed view... */
protected activeView: View;
constructor() {
super();
// document.oncontextmenu = function() {return false;};
// The web-component use as layout is named globular-application
if (document.getElementsByTagName("globular-application") != undefined) {
ApplicationView.layout = <Layout>(
document.getElementsByTagName("globular-application")[0]
);
} else {
ApplicationView.layout = new Layout();
document.body.appendChild(ApplicationView.layout);
}
// set it to false.
this.isLogin = false;
// Set the login box...
this.login_ = new Login();
ApplicationView.layout.toolbar().appendChild(this.login_);
// This contain account informations.
this.accountMenu = new AccountMenu();
// This contain account/application notification
this.notificationMenu = new NotificationMenu();
// The applicaiton menu
this.hasApplicationsMenu = false; // set to true if needed in the futur, console is the only one application at this time.
this.applicationsMenu = new ApplicationsMenu();
// The concact menu
this.contactsMenu = new ContactsMenu();
// The messenger menu
this.messengerMenu = new MessengerMenu();
// the call history menu
this.callsHistoryMenu = new CallsHistoryMenu();
// The camera can be use to take picture.
this._camera = new Camera();
// The search bar to give end user the power to search almost anything...
this._searchBar = new SearchBar();
// The application layout
this.workspace_ = new Workspace()
// Now the save funciton..
this._camera.onsaveimage = (picture: any) => {
// save image from the picture.
};
this._camera.onsavevideo = (video: any) => {
// save image from the picture.
};
// The watching menu.
this.watchingMenu = new WatchingMenu();
// The blog editing menu...
this.blogEditingMenu = new BlogEditingMenu();
// The system infos menu
this.systemInfosMenu = new SystemInfosMenu();
// Set the close action for both blog edit and continue watching...
this.systemInfosMenu.onclose = this.blogEditingMenu.onclose = this.watchingMenu.onclose = () => {
// restore the workspace
this.restoreContent();
}
// The content creator.
this.contentManager = new ContentManager();
// The file menu
this.filesMenu = new FilesMenu();
// The share menu.
this.shareMenu = new ShareMenu();
// Init file upload event listener...
ApplicationView.filesUploader = new FilesUploader()
this._sidemenu_childnodes = new Array<any>();
this._workspace_childnodes = new Array<any>();
}
hideApplicationsMenu() {
this.hasApplicationsMenu = false;
if (this.applicationsMenu.parentNode)
this.applicationsMenu.parentNode.removeChild(this.applicationsMenu)
}
hideContent() {
if (this._workspace_childnodes.length != 0) {
this._sidemenu_childnodes = new Array<any>();
this._workspace_childnodes = new Array<any>();
}
// Keep the content of the workspace.
let i = this.getWorkspace().childNodes.length
while (i > 0) {
let node = this.getWorkspace().childNodes[this.getWorkspace().childNodes.length - 1]
if (!node.classList.contains("draggable")) {
if (node.tagName != "GLOBULAR-WEB-PAGE") {
this._workspace_childnodes.push(node)
}
this.getWorkspace().removeChild(node)
}
i--
}
// I will also remove the search filter...
let searchFacetFilters = document.getElementsByTagName("globular-facet-search-filter")
for (var index = 0; index < searchFacetFilters.length; index++) {
let parentNode = searchFacetFilters[index].parentNode
if (parentNode) {
parentNode.removeChild(searchFacetFilters[index])
}
}
}
restoreContent() {
let mediaWatching = null
let blogPosts = null
let searchResults = null
this.getWorkspace().innerHTML = ""
for (var i = 0; i < this._workspace_childnodes.length; i++) {
let node = this._workspace_childnodes[i]
if (node.tagName != "GLOBULAR-WEB-PAGE" && node.tagName != "GLOBULAR-MEDIA-WATCHING" && node.tagName != "GLOBULAR-BLOG-POSTS" && node.tagName != "GLOBULAR-SEARCH-RESULTS") {
this.getWorkspace().appendChild(node)
} else if (node.tagName == "GLOBULAR-MEDIA-WATCHING") {
mediaWatching = node
} else if (node.tagName == "GLOBULAR-BLOG-POSTS") {
blogPosts = node
} else if (node.tagName == "GLOBULAR-SEARCH-RESULTS") {
searchResults = node
}
}
if (mediaWatching) {
this.getWorkspace().appendChild(mediaWatching)
} else if (blogPosts) {
this.getWorkspace().appendChild(blogPosts)
} else if (searchResults) {
this.getWorkspace().appendChild(searchResults)
} else {
// set the active webpage...
let lnks = document.getElementsByTagName("globular-page-link")
for (var i = 0; i < lnks.length; i++) {
let lnk = <any>lnks[i]
if (lnk.active) {
this.getWorkspace().appendChild(lnk.webPage)
break
}
}
}
this._sidemenu_childnodes = new Array<any>();
this._workspace_childnodes = new Array<any>();
}
// Must be call by the model when after it initialisation was done.
init() {
// Create the setting components.
this.settingsMenu = new SettingsMenu();
this.settingsPanel = new SettingsPanel();
this.settingsPanel.id = "globular-setting-panel"
// init listener's in the layout.
ApplicationView.layout.init();
this.login_.init();
this.accountMenu.init();
this.applicationsMenu.init();
this.contentManager.init();
ApplicationView.layout.navigation().appendChild(this.contentManager)
if (!this.contentManager.hasContent()) {
ApplicationView.layout.hideSideBar()
}
// Set the current webpage...
Model.eventHub.subscribe("_set_web_page_",
uuid => { },
page => {
// remove actual nodes
this.hideContent()
// Append the watching component...
this.getWorkspace().appendChild(page);
}, true)
// Logout event
Model.eventHub.subscribe(
"logout_event",
(uuid: string) => {
this.logout_event_listener = uuid;
},
() => {
this.onLogout();
},
true, this
);
// Login event.
Model.eventHub.subscribe(
"login_event",
(uuid: string) => {
this.login_event_listener = uuid;
},
(account: Account) => {
// set the account
Application.account = account;
this.onLogin(account);
// Here I will set contact menu actions.
// On invite contact action.
this.contactsMenu.onInviteConctact = (contact: Account) => {
// Display the message to the user.
ApplicationView.displayMessage(
"<iron-icon icon='send' style='margin-right: 10px;'></iron-icon><div>Invitation was sent to " +
contact.email +
"</div>",
3000
);
// So here I will create a notification.
Model.eventHub.publish("invite_contact_event_", contact, true);
};
// On revoke contact invitation action.
this.contactsMenu.onRevokeContact = (contact: Account) => {
ApplicationView.displayMessage(
"<iron-icon icon='send' style='margin-right: 10px;'></iron-icon><div>Invitation to " +
contact.email +
" was revoked!</div>",
3000
);
Model.eventHub.publish("revoke_contact_invitation_event_", contact, true);
}
// On accept contact invitation
this.contactsMenu.onAcceptContact = (contact: Account) => {
ApplicationView.displayMessage(
"<iron-icon icon='send' style='margin-right: 10px;'></iron-icon><div>Invitation from " +
contact.email +
" was accepted!</div>",
3000
);
Model.eventHub.publish("accept_contact_invitation_event_", contact, true);
}
// Decline contact invitation
this.contactsMenu.onDeclineContact = (contact: Account) => {
ApplicationView.displayMessage(
"<iron-icon icon='send' style='margin-right: 10px;'></iron-icon><div>Invitation from " +
contact.email +
" was declined!</div>",
3000
);
Model.eventHub.publish("decline_contact_invitation_event_", contact, true);
}
// Delete contact.
this.contactsMenu.onDeleteContact = (contact: Account) => {
// Here I will ask the user for confirmation before actually delete the contact informations.
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
</style>
<div id="yes-no-contact-delete-box">
<div>Your about to delete the contact</div>
<globular-contact-card id="contact-to-delete" contact="${contact.id + "@" + contact.domain}"></globular-contact-card>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button raised id="yes-delete-contact">Yes</paper-button>
<paper-button raised id="no-delete-contact">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = <any>document.querySelector("#yes-delete-contact")
let noBtn = <any>document.querySelector("#no-delete-contact")
// On yes
yesBtn.onclick = () => {
toast.dismiss();
ApplicationView.displayMessage(
"<iron-icon icon='send' style='margin-right: 10px;'></iron-icon><div>Contact " +
contact.email +
" was remove from your contacts!</div>",
3000
);
Model.eventHub.publish("delete_contact_event_", contact, true);
}
noBtn.onclick = () => {
toast.dismiss();
}
}
// The contacts will be initialyse at login time only.
this.contactsMenu.init(account);
// Also the messenger menu
this.messengerMenu.init(account);
// Init the call history.
this.callsHistoryMenu.init(account);
// Set the messenger.
this.messenger = new Messenger(account);
// Display the conversation manager.
Model.eventHub.subscribe(`__join_conversation_evt__`,
(uuid) => {
},
(evt) => {
// Append the messenger in the workspace div and not the workspace component.
document.getElementById("workspace").appendChild(this.messenger)
this.messenger.style.zIndex = "1000"
}, true)
Model.eventHub.subscribe("__create_new_conversation_event__",
(uuid) => { },
(evt) => {
this.onCreateNewConversation();
},
true, this)
Model.eventHub.subscribe("__invite_conversation_evt__",
(uuid: string) => { },
(evt: any) => {
this.onInviteToConversaiton(evt);
}, true, this)
Model.eventHub.subscribe("__delete_conversation_evt__",
(uuid: string) => { },
(evt: any) => {
this.onDeleteConversaiton(evt);
}, true, this)
},
true, this
);
// Settings event
Model.eventHub.subscribe(
"settings_event_",
(uuid: string) => {
this.settings_event_listener = uuid;
},
() => {
this.onSettings();
},
true, this
);
// Settings event
Model.eventHub.subscribe(
"save_settings_evt",
(uuid: string) => {
this.save_settings_event_listener = uuid;
},
(saveSetting: boolean) => {
this.onSaveSettings(saveSetting)
},
true, this
);
Model.eventHub.subscribe("_display_blog_event_", uuid => { }, b => {
let displayBlog = () => {
let blog = this.getWorkspace().querySelector(`#_${b.blogPost.getUuid()}`)
if (blog == null) {
blog = new BlogPostElement(b.blogPost, b.globule)
}
this.hideContent()
// Generate the blog display and set in the list.
blog.read(() => {
blog.onclose = () => {
// remove the blog
if (blog.parentNode != undefined) {
blog.parentNode.removeChild(blog)
}
// restore the workspace
this.restoreContent()
}
this.getWorkspace().appendChild(blog)
})
}
if (b.blogPost.getText().length > 0) {
displayBlog()
} else {
let rqst = new GetBlogPostsRequest
let id = b.blogPost.getUuid()
rqst.setUuidsList([id])
let token = localStorage.getItem("user_token")
let globule = b.globule
let stream = globule.blogService.getBlogPosts(rqst, { application: Application.application, domain: globule.domain, token: token })
stream.on("data", (rsp: any) => {
b.blogPost = rsp.getBlogPost()
});
stream.on("status", (status: any) => {
if (status.code == 0) {
Model.eventHub.publish("_hide_search_results_", null, true)
displayBlog()
} else {
ApplicationView.displayMessage(status.details, 3000)
}
})
}
}, true)
// Display user watching content...
Model.eventHub.subscribe("_display_workspace_content_event_",
uuid => { },
evt => {
if (evt.parentNode == this.getWorkspace()) {
return // already the parent.
}
// remove actual nodes
this.hideContent()
// Append the watching component...
this.getWorkspace().appendChild(evt);
}, true)
Model.eventHub.subscribe("_open_file_explorer_event_",
uuid => { },
explorer => {
// Append the watching component...
ApplicationView.layout.workspace().appendChild(explorer);
explorer.style.zIndex = 1000;
}, true)
Model.eventHub.subscribe("_display_blogs_event_",
uuid => { },
evt => {
// remove actual nodes
this.hideContent()
// Append the watching component...
this.getWorkspace().appendChild(evt);
}, true)
// Search result event...
Model.eventHub.subscribe("_display_search_results_",
uuid => { },
evt => {
if (this._searchResults != undefined) {
if (this.getWorkspace().querySelectorAll("globular-search-results").length == 1) {
return
}
}
this.hideContent()
// The logout event.
// The search result panel where the result will be displayed.
if (this._searchResults == null) {
console.log("search result is null")
this._searchResults = new SearchResults()
}
this.getWorkspace().appendChild(this._searchResults);
}, true)
Model.eventHub.subscribe("_hide_search_results_",
uuid => { },
evt => {
// hide all the side bar...
let facetFilters = ApplicationView.layout.sideMenu().getElementsByTagName("globular-facet-search-filter")
for (var i = 0; i < facetFilters.length; i++) {
let f = <any>facetFilters[i]
f.style.display = "none"
}
// The search results
if (this._searchBar != undefined) {
if (this._searchResults) {
if (this._searchResults.parentNode != undefined) {
this._searchResults.parentNode.removeChild(this._searchResults)
}
}
}
// restore the workspace
this.restoreContent()
}, true)
/**
* The resize listener.
*/
window.addEventListener("resize", (evt: any) => {
if (!ApplicationView) {
return;
}
let w = ApplicationView.layout.width();
// TODO try to set it in the css propertie instead...
if (w < 700) {
if (this.hasApplicationsMenu) {
this.getSideMenu()
.insertBefore(
this.applicationsMenu,
this.getSideMenu().firstChild
);
this.applicationsMenu.shrink()
}
// Append the content manager.
this.getSideMenu().appendChild(this.contentManager);
this.contentManager.setVertical()
if (this.contentManager.hasContent()) {
ApplicationView.layout.showSideBar()
}
if (this.isLogin) {
ApplicationView.layout.showSideBar()
this.getSideMenu().appendChild(this.blogEditingMenu);
this.blogEditingMenu.shrink()
this.getSideMenu().appendChild(this.watchingMenu);
this.watchingMenu.shrink()
this.getSideMenu().appendChild(this.shareMenu);
this.shareMenu.shrink()
this.getSideMenu().appendChild(this.filesMenu);
this.filesMenu.shrink()
this.getSideMenu().appendChild(ApplicationView.filesUploader);
ApplicationView.filesUploader.shrink()
this.getSideMenu().appendChild(this.contactsMenu);
this.contactsMenu.shrink()
this.getSideMenu().appendChild(this.callsHistoryMenu);
this.callsHistoryMenu.shrink()
this.getSideMenu().appendChild(this.messengerMenu);
this.messengerMenu.shrink()
this.getSideMenu().appendChild(this.notificationMenu);
this.notificationMenu.shrink()
this.getSideMenu().appendChild(this.systemInfosMenu);
this.systemInfosMenu.shrink()
this.getSideMenu().appendChild(this.accountMenu);
this.accountMenu.shrink()
}
} else {
if (this.hasApplicationsMenu) {
ApplicationView.layout
.toolbar()
.insertBefore(
this.applicationsMenu,
ApplicationView.layout.toolbar().firstChild
);
this.applicationsMenu.expand()
}
// set back to the navigation div.
ApplicationView.layout.navigation().appendChild(this.contentManager);
this.contentManager.setHorizontal()
if (this.isLogin) {
// set back menu item to toolbar...
ApplicationView.layout.toolbar().appendChild(this.systemInfosMenu);
this.systemInfosMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.blogEditingMenu);
this.blogEditingMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.watchingMenu);
this.watchingMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.shareMenu);
this.shareMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.filesMenu);
this.filesMenu.expand()
ApplicationView.layout.toolbar().appendChild(ApplicationView.filesUploader);
ApplicationView.filesUploader.expand()
ApplicationView.layout.toolbar().appendChild(this.contactsMenu);
this.contactsMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.callsHistoryMenu);
this.callsHistoryMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.messengerMenu);
this.messengerMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.notificationMenu);
this.notificationMenu.expand()
ApplicationView.layout.toolbar().appendChild(this.accountMenu);
this.accountMenu.expand()
}
}
});
window.dispatchEvent(new Event("resize"));
}
///////////////////////////////////////////////////////////////////////////////////////
// Application action's
///////////////////////////////////////////////////////////////////////////////////////
/**
* Free resource here.
*/
close() {
// Disconnect event listener's
Model.eventHub.unSubscribe("login_event", this.login_event_listener);
Model.eventHub.unSubscribe("logout_event", this.logout_event_listener);
super.close();
}
/**
* Clear the workspace.
*/
clear() {
this.clearWorkspace();
if (this.activeView != null) {
this.activeView.hide();
}
}
/**
* Refresh various component.
*/
update() { }
/**
* The title of the application
* @param title The title.
*/
setTitle(title: string) {
ApplicationView.layout.title().innerHTML = "<span>" + title + "</span>";
}
hideHeader() {
ApplicationView.layout.hideHeader();
}
showHeader() {
ApplicationView.layout.showHeader();
}
setIcon(imgUrl: string) {
let icon = document.createElement("img");
icon.src = imgUrl;
let title = <any>ApplicationView.layout.title();
title.style.display = "flex";
title.insertBefore(icon, title.firstChild);
}
/**
* The icon
*/
getIcon() {
return this.icon;
}
/**
* Try to extract error message from input object.
* @param err Can be a string or object, in case of object I will test if the object contain a field named 'message'
*/
private static getErrorMessage(err: any) {
try {
let errObj = err;
if (typeof err === "string" || err instanceof String) {
errObj = JSON.parse(<string>err);
} else if (errObj.message != undefined) {
errObj = JSON.parse(errObj.message);
}
if (errObj.ErrorMsg != undefined) {
return errObj.ErrorMsg;
} else {
return err;
}
} catch {
if (err.message != undefined) {
return err.message;
}
return err;
}
}
/**
* Display a message to the user.
* @param msg The message to display in toast!
*/
public static displayMessage(msg: any, duration: number) {
let uuid = "_" + getUuid(JSON.stringify(msg))
// Not print the same message more than once at time...
if (document.querySelector("#" + uuid) != undefined) {
return;
}
let t = M.toast({
html: this.getErrorMessage(msg),
displayLength: duration,
});
t.el.id = uuid
document.getElementById(uuid).classList.add("popup")
return t
}
// Block user input
static wait(msg: string = "wait ...") {
ApplicationView.layout.wait(msg);
}
// Resume user input.
static resume() {
ApplicationView.layout.resume();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Event listener's
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Login event handler
* @param accountId The id of the user
*/
onLogin(account: Account) {
// Do nothing if it's already login...
if (this.isLogin == true) {
return
}
// Test if the user can edit the website...
this.contentManager.setEditMode()
/** implement it as needed */
if (this.login_.parentNode != null) {
this.login_.parentNode.removeChild(this.login_);
}
ApplicationView.layout.title().innerHTML = "";
ApplicationView.layout.title().appendChild(this._searchBar);
ApplicationView.layout.toolbar().appendChild(this.notificationMenu);
ApplicationView.layout.toolbar().appendChild(this.accountMenu);
this.accountMenu.setAccount(account);
this.settingsPanel.innerHTML = ""; // remove the content of the setting panel.
this.settingsPanel.clear(); // clear various stuff...
this.settingsMenu.clear();
// Create the settings menu and panel here
let userSettings = new UserSettings(account, this.settingsMenu, this.settingsPanel);
// Manage server settings
let serverGeneralSettings = new ServerGeneralSettings(this.settingsMenu, this.settingsPanel);
// Manage applications
let applicationsSettings = new ApplicationsSettings(this.settingsMenu, this.settingsPanel)
// Manage services settings.
let servicesSettings = new ServicesSettings(this.settingsMenu, this.settingsPanel);
// Manage services settings.
let videoSettings = new VideoSettings(this.settingsMenu, this.settingsPanel);
// The accounts settings
let usersSettings = new UsersSettings(this.settingsMenu, this.settingsPanel);
// Manage peers
let peersSettings = new PeersSettings(this.settingsMenu, this.settingsPanel)
// Manage organizations
let organizationsSettings = new OrganizationSettings(this.settingsMenu, this.settingsPanel)
// Manage roles
let roleSettings = new RoleSettings(this.settingsMenu, this.settingsPanel)
// Manage groups
let groupSettings = new GroupSettings(this.settingsMenu, this.settingsPanel)
// Manage resources permissions
let resourcesPermissions = new ResourcesPermissionsSettings(this.settingsMenu, this.settingsPanel)
// The logs
let logs = new LogSettings(this.settingsMenu, this.settingsPanel);
this._title_ = ApplicationView.layout.title().innerHTML;
ApplicationView.layout.title().innerHTML = ""
ApplicationView.layout.title().appendChild(this._searchBar)
// set menu...
this.notificationMenu.init();
this.filesMenu.init();
this.shareMenu.init(account);
this.systemInfosMenu.init();
this.watchingMenu.init();
this.blogEditingMenu.init();
// Init the file uploader.
ApplicationView.filesUploader.init();
this.isLogin = true;
// create the workspace...
this.getWorkspace()
window.dispatchEvent(new Event("resize"));
}
/**
* Logout event handler
* @param accountId
*/
onLogout() {
this.isLogin = false;
/** Remove it from the body */
ApplicationView.filesUploader.parentNode.removeChild(ApplicationView.filesUploader)
/** implement it as needed */
ApplicationView.layout.toolbar().appendChild(this.login_);
ApplicationView.layout.title().removeChild(this._searchBar);
this.accountMenu.parentNode.removeChild(this.notificationMenu);
this.accountMenu.parentNode.removeChild(this.accountMenu);
this.clearWorkspace();
this.clearSideMenu();
// Close the messenger
if (this.messenger.parentNode != null) {
this.messenger.parentNode.removeChild(this.messenger)
}
// set back the title.
if (this._searchBar.parentNode != null) {
this._searchBar.parentNode.removeChild(this._searchBar)
}
ApplicationView.layout.title().innerHTML = this._title_
window.dispatchEvent(new Event("resize"));
}
onSettings() {
if (this.getWorkspace().childNodes.length > 0) {
if (this.getWorkspace().childNodes[0].id == "globular-setting-panel") {
return
}
}
this.hideContent()
// Keep the content of the side menu
while (this.getSideMenu().childNodes.length > 0) {
let node = this.getSideMenu().childNodes[this.getSideMenu().childNodes.length - 1]
this._sidemenu_childnodes.push(node)
this.getSideMenu().removeChild(node)
}
this.getSideMenu().appendChild(this.settingsMenu);
this.getWorkspace().appendChild(this.settingsPanel);
}
onSaveSettings(saveSetting: boolean) {
this.settingsMenu.parentNode.removeChild(this.settingsMenu)
this.settingsPanel.parentNode.removeChild(this.settingsPanel)
if (saveSetting) {
} else {
}
// restore the workspace
this.restoreContent()
// restore the side menu
for (var i = 0; i < this._sidemenu_childnodes.length; i++) {
this.getSideMenu().appendChild(this._sidemenu_childnodes[i])
}
}
showCamera(onclose: () => void, onsaveimage: (picture: any) => void, onsavevideo: (video: any) => void) {
// Here I will create a non-selectable div and put camera in center of it...
document.body.style.position = "relative"
let modal = document.createElement("div")
modal.style.position = "fixed"
modal.style.top = "0px"
modal.style.bottom = "0px"
modal.style.left = "0px"
modal.style.right = "0px"
modal.style.zIndex = "10000";
modal.style.background = "rgba(0.0, 0.0, 0.0, .85)"
document.body.appendChild(modal)
this.camera.onclose = () => {
this.camera.parentNode.removeChild(this.camera)
modal.parentNode.removeChild(modal)
if (onclose != null) {
onclose()
}
}
modal.appendChild(this.camera)
this.camera.onsaveimage = onsaveimage
this.camera.onsavevideo = onsavevideo
this.camera.open();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Conversations
///////////////////////////////////////////////////////////////////////////////////////////
onCreateNewConversation() {
if (document.getElementById("new-conversation-box")) {
return;
}
const name = nameGenrator.getRandomName(false);
// Display the box if not already displayed...
let toast = ApplicationView.displayMessage(
`
<style>
new-conversation-box{
display: flex;
flex-direction: column;
}
#new-conversation-box .title{
font-size: 1.1rem;
font-weight: 400;
}
#new-conversation-box .actions{
display: flex;
justify-content: flex-end;
}
#new-conversation-box paper-button{
height: 35px;
font-size: .85rem;
}
paper-input{
flex-grow: 1;
}
</style>
<div id="new-conversation-box">
<span class="title">New Conversation...</span>
<paper-input id="conversation-name-input" type="text" label="Name" tabindex="0" aria-disabled="false"></paper-input>
<paper-input id="conversation-keywords-input" type="text" label="Keyword (comma separated)" tabindex="1" aria-disabled="false"></paper-input>
<div class="actions">
<paper-button id="create-new-conversation-btn">Create</paper-button>
<paper-button id="cancel-create-new-conversation-btn">Cancel</paper-button>
</div>
</div>
`,
1000 * 60 * 15// 15 minutes...
);
let nameInput = <any>document.getElementById("conversation-name-input")
nameInput.value = name;
setTimeout(() => {
nameInput.focus()
nameInput.inputElement.inputElement.select()
}, 100)
let cancelBtn = document.getElementById("cancel-create-new-conversation-btn")
cancelBtn.onclick = () => {
toast.dismiss();
}
let createBtn = document.getElementById("create-new-conversation-btn")
createBtn.onclick = () => {
let language = window.navigator.language.split("-")[0]
let keywordsInput = <any>document.getElementById("conversation-keywords-input")
let keywords = new Array<string>();
if (keywordsInput.value != undefined) {
keywordsInput.value.split(",").forEach((keyword: string) => {
keywords.push(keyword.trim())
})
}
ConversationManager.createConversation(nameInput.value, keywords, language,
(conversation) => {
/** Publish a new conversation event. */
Model.eventHub.publish("__new_conversation_event__", conversation, true)
toast.dismiss();
},
(err: any) => {
ApplicationView.displayMessage(err, 3000)
})
toast.dismiss();
}
}
/**
* Invite to conversation.
* @param conversation
*/
onInviteToConversaiton(conversation: Conversation) {
let toast = ApplicationView.displayMessage(
`
<style>
#invite-conversation-participants{
display: flex;
flex-direction: column;
}
invite-conversation-participants #yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#invite-conversation-participants div{
display: flex;
padding-bottom: 10px;
}
paper-button{
font-size: .85rem;
height: 32px;
}
</style>
<div id="invite-conversation-participants">
<div>Select contact you want to invite to ${conversation.getName()}</div>
<globular-autocomplete type="email" label="Search" id="invite-conversation-participants-invite_contact_input" width="${355}" style="flex-grow: 1;"></globular-autocomplete>
<div style="justify-content: flex-end;">
<paper-button id="invite-conversation-participants-cancel-btn">Cancel</paper-button>
</div>
</div>
`,
60 * 1000 * 5 // 5 minutes sec...
);
let inviteContactInput = <any>document.querySelector("#invite-conversation-participants-invite_contact_input")
inviteContactInput.focus();
let findAccountByEmail = (email: string) => {
Account.getAccounts(`{"email":{"$regex": "${email}", "$options": "im"}}`, (accounts) => {
// set the getValues function that will return the list to be use as filter.
accounts = accounts.filter((obj: Account) => {
// remove participant already in the conversation and the current account.
let index = conversation.getParticipantsList().indexOf(obj.id);
return obj.id !== Application.account.id && index == -1;
});
inviteContactInput.setValues(accounts)
}, (err) => {
ApplicationView.displayMessage(err, 3000)
})
}
inviteContactInput.onkeyup = () => {
let val = inviteContactInput.getValue();
if (val.length >= 2) {
findAccountByEmail(val)
} else {
inviteContactInput.clear()
}
}
inviteContactInput.displayValue = (contact: Account) => {
let card = new ContactCard(Application.account, contact);
card.setInviteButton((a: Account) => {
ConversationManager.sendConversationInvitation(conversation, Application.account.id + "@" + Application.account.domain, a.id + "@" + a.domain,
() => {
Model.eventHub.publish("send_conversation_invitation_event_", { participant: a.id + "@" + a.domain, conversation: conversation.getName() }, true)
},
(err: any) => {
ApplicationView.displayMessage(err, 3000)
})
// So here I will create a
toast.dismiss();
})
return card
}
let cancelBtn = <any>document.querySelector("#invite-conversation-participants-cancel-btn")
cancelBtn.onclick = () => {
toast.dismiss();
}
}
/**
* Delete a conversation.
* @param conversation
*/
onDeleteConversaiton(conversation: Conversation) {
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
paper-button{
font-size: .85rem;
height: 32px;
}
</style>
<div id="yes-no-contact-delete-box">
<div>Your about to delete the conversation ${conversation.getName()}</div>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button id="yes-delete-contact">Yes</paper-button>
<paper-button id="no-delete-contact">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = <any>document.querySelector("#yes-delete-contact")
let noBtn = <any>document.querySelector("#no-delete-contact")
// On yes
yesBtn.onclick = () => {
ConversationManager.deleteConversation(conversation, () => {
toast.dismiss();
// Publish the list of participant with the account removed from it.
let participants = conversation.getParticipantsList()
participants.splice(participants.indexOf(Application.account.id + "@" + Application.account.domain), 1)
Model.getGlobule(conversation.getMac()).eventHub.publish(`leave_conversation_${conversation.getUuid()}_evt`, JSON.stringify({ "conversationUuid": conversation.getUuid(), "participants": participants, "participant": Application.account.id + "@" + Application.account.domain }), false)
ApplicationView.displayMessage(
"<iron-icon icon='communication:message' style='margin-right: 10px;'></iron-icon><div>Conversation named " +
conversation.getName() +
" was deleted!</div>",
3000
);
}, (err) => {
ApplicationView.displayMessage(err, 3000)
toast.dismiss();
})
}
noBtn.onclick = () => {
toast.dismiss();
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Gui function's
//////////////////////////////////////////////////////////////////////////////////////////
/**
* The workspace div where the application draw it content.
*/
getWorkspace(): any {
if (this.workspace_.parentElement == null) {
ApplicationView.layout.workspace().appendChild(this.workspace_)
}
return this.workspace_;
}
/**
* Clear the workspace.
*/
clearWorkspace(): void {
this.getWorkspace().innerHTML = "";
ApplicationView.layout.clearWorkspace();
}
/**
* The side menu that contain application actions.
*/
getSideMenu(): any {
let sideMenu = ApplicationView.layout.sideMenu();
return sideMenu;
}
/**
* Clear the side menu
*/
clearSideMenu(): void {
ApplicationView.layout.clearSideMenu();
}
// In case of application view
setView(view: View) {
this.activeView = view;
}
hideSideMenu() {
(<any>ApplicationView.layout.appDrawer).toggle();
}
/**
* Create a side menu button div.
*/
createSideMenuButton(id: string, text: string, icon: string): any {
let html = `
<style>
.side_menu_btn{
display: flex;
position: relative;
align-items: center;
font-family: Roboto,RobotoDraft,Helvetica,Arial,sans-serif;
letter-spacing: .2px;
color: #202124;
text-shadow: none;
}
.side_menu_btn:hover{
cursor: pointer;
}
</style>
<div id="${id}" class="side_menu_btn">
<paper-ripple recenters></paper-ripple>
<paper-icon-button id="${id}_close_btn" icon="${icon}"></paper-icon-button>
<span style="flex-grow: 1;">${text}</span>
</div>
`;
let range = document.createRange();
this.getSideMenu().insertBefore(
range.createContextualFragment(html),
this.getSideMenu().firstChild
);
return document.getElementById(id);
}
}