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,274 lines (1,045 loc) 135 kB
// I will made use of polymer instead of materialyze for the main // layout because materialyse dosen't react to well with the shadow doom. import '@polymer/iron-icon/iron-icon.js'; import '@polymer/iron-icons/social-icons' import '@polymer/iron-icons/editor-icons' import '@polymer/iron-icons/communication-icons' import '@polymer/iron-icons/iron-icons.js'; import '@polymer/paper-icon-button/paper-icon-button.js'; import '@polymer/paper-ripple/paper-ripple.js'; import '@polymer/paper-input/paper-input.js'; import '@polymer/paper-card/paper-card.js'; import '@polymer/paper-button/paper-button.js'; import '@polymer/paper-checkbox/paper-checkbox.js'; import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js'; import '@polymer/paper-tabs/paper-tabs.js'; import '@polymer/paper-tabs/paper-tab.js'; import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js'; import { Menu } from './Menu'; import { File as File__ } from "../File"; // File object already exist in js and I need to use it... import { Account } from "../Account" import { generatePeerToken, getUrl, Model } from "../Model" import { PermissionManager } from '../Permission'; import { ConversationManager } from '../Conversation'; import { Invitation } from 'globular-web-client/conversation/conversation_pb'; import { decode } from 'uint8-to-base64'; import { v4 as uuidv4 } from "uuid"; import { ApplicationView } from '../ApplicationView'; import { getCoords, mobileCheck } from "./utility.js" import { Application } from '../Application'; import { setMoveable } from './moveable' import { setResizeable } from './rezieable' import { createThumbmail } from './BlogPost'; import { Link } from './Link'; import { getAudioInfo, getTitleInfo, getVideoInfo } from './File'; import { playAudios } from './Audio'; import { playVideos } from './Video'; import * as getUuidByString from 'uuid-by-string'; import { FileMetaDataInfo } from './Informations'; import { GetResourcePermissionsRqst, Permission, SetResourcePermissionRsp, SetResourcePermissionsRqst } from 'globular-web-client/rbac/rbac_pb'; /** * Communication with your contact's */ export class MessengerMenu extends Menu { // attributes. // Create the contact view. constructor() { super("Messenger", "communication:forum", "Messenger") this.account = null; // Actions handlers // When new conversation is receive. this.onConversationRequest = null; // When a new message is receive. this.onReceiveMessage = null; this.width = 320; if (this.hasAttribute("width")) { this.width = parseInt(this.getAttribute("width")); } this.height = 550; if (this.hasAttribute("height")) { this.height = parseInt(this.getAttribute("height")); } // Element of interfaces needed between function call this.conversationsLst = null; this.sentConversationsInvitationsLst = null; this.receivedConversationsInvitationsLst = null; this.conversationsTab = null this.sentConversationsInvitationsTab = null this.receivedConversationsInvitationsTab = null } // Init the message menu at login time. init(account) { // Keep the account reference. this.account = account; // Event listener when a new conversation is created... Model.getGlobule(Application.account.domain).eventHub.subscribe(`send_conversation_invitation_${this.account.id + "@" + this.account.domain}_evt`, (uuid) => { }, (data) => { console.log(`receive event send_conversation_invitation_${this.account.id + "@" + this.account.domain}_evt`) const decoded = decode(data); let invitation = Invitation.deserializeBinary(decoded) this.appendSentInvitation(invitation) }, false) Model.getGlobule(Application.account.domain).eventHub.subscribe(`receive_conversation_invitation_${this.account.id + "@" + this.account.domain}_evt`, (uuid) => { }, (data) => { console.log(`receive event receive_conversation_invitation_${this.account.id + "@" + this.account.domain}_evt`) const decoded = decode(data); let invitation = Invitation.deserializeBinary(decoded) this.appendReceivedInvitation(invitation) }, false) Model.eventHub.subscribe("delete_conversation_evt", () => { }, () => { this.conversationsTab.innerHTML = `<span id="conversations_label">Conversations</span>` if (this.conversationsLst.children.length > 0) { this.conversationsTab.innerHTML += `<paper-badge for="conversations_label" label="${this.conversationsLst.children.length}"></paper-badge>` } }, true) let html = ` <style> ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: var(--palette-background-default); } ::-webkit-scrollbar-thumb { background: var(--palette-divider); } #Messages-div { display: flex; flex-wrap: wrap; height: 100%; flex-direction: column; overflow: hidden; min-width: 200px; } #Messenger_menu_div{ overflow: auto; height: ${this.height}px; max-height: 70vh; overflow-y: auto; } #title{ display: none; justify-content: center; } @media (max-width: 500px) { #Messenger_menu_div{ margin-top: 5px; max-height: calc(100vh - 85px); } #title{ display: flex; } #search-conversation-box { max-width: calc(100vw - 70px); } } #conversations-lst{ flex: 1; overflow: auto; } ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: var(--palette-background-default); } ::-webkit-scrollbar-thumb { background: var(--palette-divider); } .conversations-lst{ display: flex; flex-direction: column; } .btn_: hover{ cursor: pointer; } #conversation-search-results{ position: relative; } /* Need to position the badge to look like a text superscript */ paper-tab { padding-right: 25px; } paper-tabs { /* custom CSS property */ --paper-tabs-selection-bar-color: var(--palette-primary-main); color: var(--palette-text-primary); --paper-tab-ink: var(--palette-action-disabled); } paper-tab paper-badge { --paper-badge-background: var(--palette-warning-main); --paper-badge-width: 16px; --paper-badge-height: 16px; --paper-badge-margin-left: 10px; } paper-card{ background-color: var(--palette-background-paper); color: var(--palette-text-primary); padding: 10px; } paper-card h1 { font-size: 1.65rem; margin: 0px; margin-bottom: 10px; } </style> <div id="Messages-div"> <div id="header" style="width: 100%;"> <div id="title"> <h1 style="flex-grow: 1;">Conversation's</h1> <paper-icon-button id="close-btn" icon="icons:close" role="button" tabindex="0" aria-disabled="false"></paper-icon-button> </div> </div> <div style="display: flex; align-items: center;"> <paper-input type="text" label="Search Contact" id="search-conversation-box" width="${this.width}" style="flex-grow: 1;"></paper-input> <div id="new-conversation-btn" class="btn_" style="position: relative;"> <iron-icon style="flex-grow: 1; --iron-icon-fill-color:var(--palette-text-primary);" icon="add"></iron-icon> <paper-ripple class="circle" recenters=""></paper-ripple> </div> </div> <div id="conversation-search-results"></div> <paper-tabs selected="0"> <paper-tab id="conversations-tab">Conversations</paper-tab> <paper-tab id="sent-conversations-invitations-tab">Sent Invitations</paper-tab> <paper-tab id="received-conversations-invitations-tab">Received Invitations</paper-tab> </paper-tabs> <div id="conversations-lst"> <div id="conversations-lst_" class="conversations-lst"></div> <div id="sent-conversations-invitations-lst" class="conversations-lst" style="display: none;"></div> <div id="received-conversations-invitations-lst" class="conversations-lst" style="display: none;"></div> </div> </div> ` let range = document.createRange() this.getMenuDiv().innerHTML = "" // remove existing elements. this.getMenuDiv().appendChild(range.createContextualFragment(html)); this.getMenuDiv().querySelector("#close-btn").onclick = () => { this.getMenuDiv().parentNode.removeChild(this.getMenuDiv()) } this.shadowRoot.appendChild(this.getMenuDiv()) this.conversationsTab = this.shadowRoot.querySelector("#conversations-tab") this.conversationsLst = this.shadowRoot.querySelector("#conversations-lst_") this.sentConversationsInvitationsTab = this.shadowRoot.querySelector("#sent-conversations-invitations-tab") this.sentConversationsInvitationsLst = this.shadowRoot.querySelector("#sent-conversations-invitations-lst") this.receivedConversationsInvitationsTab = this.shadowRoot.querySelector("#received-conversations-invitations-tab") this.receivedConversationsInvitationsLst = this.shadowRoot.querySelector("#received-conversations-invitations-lst") // Setup the list of received invitations ConversationManager.getReceivedInvitations(account.id + "@" + account.domain, (invitations) => { /** Get initial invitation list. */ invitations.forEach(invitation => { this.appendReceivedInvitation(invitation) }) }, (err) => { ApplicationView.displayMessage(err, 3000) }) // Setup the list of sent invitations. ConversationManager.getSentInvitations(account.id + "@" + account.domain, invitations => { invitations.forEach(invitation => { this.appendSentInvitation(invitation) }) }, err => { ApplicationView.displayMessage(err, 3000) }) this.conversationsTab.onclick = () => { this.conversationsLst.style.display = "flex" this.sentConversationsInvitationsLst.style.display = "none" this.receivedConversationsInvitationsLst.style.display = "none" } this.sentConversationsInvitationsTab.onclick = () => { this.conversationsLst.style.display = "none" this.sentConversationsInvitationsLst.style.display = "flex" this.receivedConversationsInvitationsLst.style.display = "none" } this.receivedConversationsInvitationsTab.onclick = () => { this.sentConversationsInvitationsLst.style.display = "none" this.receivedConversationsInvitationsLst.style.display = "flex" this.conversationsLst.style.display = "none" } // Find a conversation... let searchBox = this.shadowRoot.querySelector("#search-conversation-box"); searchBox.onkeyup = (evt) => { let searchconversationResults = this.shadowRoot.querySelector("#search-conversation-results") if (searchconversationResults != undefined) { searchconversationResults.innerHTML = "" } if (evt.code == "Enter") { ConversationManager.findConversations(searchBox.value, (conversations) => { let html = ` <style> ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: var(--palette-background-default); } ::-webkit-scrollbar-thumb { background: var(--palette-divider); } #search-conversation-results{ position: absolute; top: 0px; left: 0px; display: flex; flex-direction: column; z-index: 100; max-height: 450px; overflow-y: auto; } paper-card{ background-color: var(--palette-background-paper); color: var(--palette-text-primary); } </style> <paper-card id="search-conversation-results"> </paper-card> ` this.shadowRoot.querySelector("#conversation-search-results").appendChild(document.createRange().createContextualFragment(html)) searchconversationResults = this.shadowRoot.querySelector("#search-conversation-results") for (var i = 0; i < conversations.length; i++) { let conversationInfos = new ConversationInfos(null, this.account) conversationInfos.init(conversations[i]) searchconversationResults.appendChild(conversationInfos) } }, (err) => { ApplicationView.displayMessage(err, 3000) }) } else { } } this.newconversationBtn = this.shadowRoot.querySelector("#new-conversation-btn") this.newconversationBtn.onclick = () => { // simply publish create new conversation... Model.eventHub.publish("__create_new_conversation_event__", {}, true) } /** Event's subscribe... */ Model.eventHub.subscribe("__new_conversation_event__", (uuid) => { }, (conversation) => { this.appendConversation(conversation) }, true) /** Load conversations */ Model.eventHub.subscribe("__load_conversations_event__", (uuid) => { }, (conversations) => { this.conversationsLst.innerHTML = ""; for (var i = 0; i < conversations.length; i++) { this.appendConversation(conversations[i]) } }, true) Model.eventHub.subscribe("__refresh_invitations__", (uuid) => { }, () => { this.receivedConversationsInvitationsTab.innerHTML = `<span id="received_invitations_label">Received Invitations</span>` if (this.receivedConversationsInvitationsLst.children.length > 0) { this.receivedConversationsInvitationsTab.innerHTML += `<paper-badge for="received_invitations_label" label="${this.receivedConversationsInvitationsLst.children.length}"></paper-badge>` window.dispatchEvent(new Event('resize')); } this.sentConversationsInvitationsTab.innerHTML = `<span id="sent_invitations_label">Sent Invitations</span>` if (this.sentConversationsInvitationsLst.children.length > 0) { this.sentConversationsInvitationsTab.innerHTML += `<paper-badge for="sent_invitations_label" label="${this.sentConversationsInvitationsLst.children.length}"></paper-badge>` window.dispatchEvent(new Event('resize')); } }, true) this.shadowRoot.removeChild(this.getMenuDiv()) } // Display base conversation info appendConversation(conversation) { let conversationUuid = conversation.getUuid() // set the conversation in the conversation manager. ConversationManager.conversations.set(conversation.getUuid(), conversation) if (this.conversationsLst.querySelector("#conversation_" + conversationUuid + "_infos") != undefined) { return; } let conversationName = conversation.getName() let conversationInfos = new ConversationInfos(null, this.account) conversationInfos.id = `conversation_${conversationUuid}_infos` conversationInfos.init(conversation) conversationInfos.setJoinButton(); // here I will display the join button. this.conversationsLst.appendChild(conversationInfos) this.conversationsTab.innerHTML = `<span id="conversation_label">Conversations</span> <paper-badge for="conversation_label" label="${this.conversationsLst.children.length}"></paper-badge>` window.dispatchEvent(new Event('resize')); Model.getGlobule(conversation.getMac()).eventHub.subscribe(`delete_conversation_${conversationUuid}_evt`, (uuid) => { //this.delete_conversation_listener = uuid; }, (evt) => { // simply remove it from it parent. let conversationInfos = this.conversationsLst.querySelector(`#conversation_${evt}_infos`) if (conversationInfos != undefined) { this.conversationsLst.removeChild(conversationInfos) // publish local event from network one. Model.eventHub.publish("delete_conversation_evt", null, true) } }, false) Model.eventHub.subscribe(`__delete_conversation_${conversationUuid}_evt__`, (uuid) => { //this.delete_conversation_listener = uuid; }, (conversationUuid) => { // simply remove it from it parent. let conversationInfos = this.shadowRoot.querySelector("#conversation_" + conversationUuid + "_infos") if (conversationInfos != undefined) { this.conversationsLst.removeChild(conversationInfos) // publish local event from network one. Model.eventHub.publish("delete_conversation_evt", "", true) } }, true) Model.getGlobule(conversation.getMac()).eventHub.subscribe(`kickout_conversation_${conversationUuid}_evt`, (uuid) => { // }, (evt) => { let participant = evt.accountId let conversationUuid = evt.conversationUuid // check for kickout... if (participant == this.account.id + "@" + this.account.domain) { let conversationInfos = this.conversationsLst.querySelector("#conversation_" + conversationUuid + "_infos") if (conversationInfos != undefined) { // That's mean the user get kickout from the conversation... this.conversationsLst.removeChild(conversationInfos) Model.eventHub.publish("delete_conversation_evt", conversationUuid, true) ApplicationView.displayMessage(`You got kicked out of the conversation <span style="font-style:italic;">${conversationName}</span>`, 3000) } } }, false) } appendReceivedInvitation(invitation) { Model.getGlobule(invitation.getMac()).eventHub.subscribe(`accept_conversation_invitation_${invitation.getConversation()}_${invitation.getFrom()}_evt`, (uuid) => { }, (evt) => { // re-init the conversation list. ConversationManager.loadConversations(this.account, (conversations) => { Model.eventHub.publish("__load_conversations_event__", conversations, true) }, (err) => { /** no conversation found... */ ApplicationView.displayMessage(err, 3000) }) }, false) let invitationCard = new InvitationCard(invitation, this.account.id + "@" + this.account.domain, invitation.getFrom(), invitation.getConversation()); invitationCard.setAcceptButton((invitation) => { ConversationManager.acceptConversationInvitation(invitation, () => { // Publish network events Model.publish(`accept_conversation_invitation_${invitation.getConversation()}_${invitation.getFrom()}_evt`, `{}`, false) // re-init the conversation list. ConversationManager.loadConversations(this.account, (conversations) => { Model.eventHub.publish("__load_conversations_event__", conversations, true) }, (err) => { /** no conversation found... */ ApplicationView.displayMessage(err, 3000) }) }, (err) => { ApplicationView.displayMessage(err, 3000) }) }) invitationCard.setDeclineButton((invitation) => { ConversationManager.declineConversationInvitation(invitation, () => { // Publish network events Model.publish(`decline_conversation_invitation_${invitation.getConversation()}_${invitation.getFrom()}_evt`, {}, false) }, (err) => { ApplicationView.displayMessage(err, 3000) }) }) this.receivedConversationsInvitationsLst.appendChild(invitationCard) // Now I will set the number of received conversations in the tab. this.receivedConversationsInvitationsTab.innerHTML = `<span id="received_invitation_label">Received Invitations</span> <paper-badge for="received_invitation_label" label="${this.receivedConversationsInvitationsLst.children.length}"></paper-badge>` window.dispatchEvent(new Event('resize')); } appendSentInvitation(invitation) { let invitationCard = new InvitationCard(invitation, invitation.getFrom(), invitation.getTo(), invitation.getConversation()); this.sentConversationsInvitationsLst.appendChild(invitationCard) invitationCard.setRevokeButton((invitation) => { ConversationManager.revokeConversationInvitation(invitation, () => { let domain = invitation.getTo().split("@")[1] // Publish network events Model.getGlobule(domain).eventHub.publish(`revoke_conversation_invitation_${invitation.getConversation()}_${invitation.getTo()}_evt`, `{}`, false) }, (err) => { ApplicationView.displayMessage(err, 3000) }) }) this.sentConversationsInvitationsTab.innerHTML = `<span id="sent_invitations_label">Sent Invitations</span> <paper-badge for="sent_invitations_label" label="${this.sentConversationsInvitationsLst.children.length}"></paper-badge>` window.dispatchEvent(new Event('resize')); } } customElements.define('globular-messenger-menu', MessengerMenu) /** * Display conversation basic info... */ export class ConversationInfos extends HTMLElement { constructor(opened, account) { super(); this.account = account; this.delete_conversation_listener = null; this.join_conversation_listener = null; this.leave_conversation_listener = null; // Set the shadow dom. this.attachShadow({ mode: "open" }); if (this.hasAttribute("opened")) { opened = `opened="${this.getAttribute("opened")}"` } this.shadowRoot.innerHTML = ` <style> .conversation-infos{ transition: background 0.2s ease,padding 0.8s linear; background-color: var(--palette-background-paper); color: var(--palette-text-primary); padding-left: 10px; padding-right: 10px; display: flex; flex-direction: column; min-width: 295px; border-bottom: 1px solid var(--palette-divider); } .conversation-infos .header{ display: flex; } .conversation-infos .header .title{ padding-top: 5px; font-size: 1.1rem; flex-grow: 1; } .conversation-infos .action{ display: flex; align-items: center; justify-content: flex-end; } .conversation-infos:hover { filter: invert(10%); } .conversation-infos paper-button{ font-size:.85rem; width: 20px; } .conversation-infos .info{ display:table; border-spacing: 5px; } .conversation-infos .info .row{ display: table-row; } .conversation-infos .info .row div{ font-size: .85rem; display: table-cell; } .conversation-infos .info .row .label{ padding-right: 10px; font-weight: 410; } .keywords{ display: flex; padding-right: 10px; } .keywords span{ margin-right: 10px; font-style: italic; background-color: yellow; text-align: center; } paper-button{ display: flex; font-size: .85rem; border: none; color: var(--palette-text-accent); background: var(--palette-primary-accent); max-height: 32px; } paper-icon-button { color: var(--paper-pink-500); --paper-icon-button-ink-color: var(--paper-indigo-500); } .header-actions{ display: flex; width: 32px; height: 32px; justify-content: center; align-items: center; position: relative; } .header paper-icon-button { min-width: 40px; } @media (max-width: 500px) { .header-actions { --iron-icon-height: 32px; --iron-icon-width: 32px; } } </style> <div class="conversation-infos"> <div class="header"> <span class="title"></span> <div class="header-actions" style=""> <iron-icon id="hide-btn" icon="unfold-less" style="flex-grow: 1; --iron-icon-fill-color:var(--palette-text-primary);" icon="add"></iron-icon> <paper-ripple class="circle" recenters=""></paper-ripple> </div> </div> <iron-collapse id="conversation-infos-collapse" ${opened}> <div class="info" style=""> <div class="row"> <div class="label">Created</div> <div class="created"></div> </div> <div class="row"> <div class="label">Last message</div> <div class="last-message"></div> </div> <div class="row"> <div class="label">Keywords</div> <div class="keywords"></div> </div> <div class="row"> <div class="label">Owner'(s)</div> <div class="owners"></div> </div> </div> </iron-collapse> <slot class="action"></slot> <paper-progress indeterminate style="width: 100%; display: none;"></paper-progress> </div> ` this.titleDiv = this.shadowRoot.querySelector(".title") this.created = this.shadowRoot.querySelector(".created") this.lastMessage = this.shadowRoot.querySelector(".last-message") this.keywords = this.shadowRoot.querySelector(".keywords") this.owners = this.shadowRoot.querySelector(".owners") this.hideBtn = this.shadowRoot.querySelector("#hide-btn") this.leaveBtn = null this.joinBtn = null this.hideBtn.onclick = () => { let button = this.shadowRoot.querySelector("#hide-btn") let content = this.shadowRoot.querySelector("#conversation-infos-collapse") if (button && content) { if (!content.opened) { button.icon = "unfold-more" } else { button.icon = "unfold-less" } content.toggle(); } } this.conversation = null; } // init(conversation) { this.conversation = conversation; // Set the value in the interfaces. this.titleDiv.innerHTML = conversation.getName(); let creationTime = new Date(conversation.getCreationTime() * 1000) this.created.innerHTML = creationTime.toLocaleDateString() + " " + creationTime.toLocaleTimeString() if (conversation.getLastMessageTime() > 0) { let lastMessageTime = new Date(conversation.getLastMessageTime() * 1000) this.lastMessage.innerHTML = lastMessageTime.toLocaleDateString() + " " + lastMessageTime.toLocaleTimeString() } else { this.lastMessage.innerHTML = "no messages reveceived yet..." } conversation.getKeywordsList().forEach((keyword) => { let span = document.createElement("span") span.innerHTML = keyword this.keywords.appendChild(span) }) // Here I will display the list of owner for that conversation. PermissionManager.getResourcePermissions(this.conversation.getUuid(), (permissions) => { permissions.getOwners().getAccountsList().forEach(owner => { let span = document.createElement("span") span.innerHTML = owner this.owners.appendChild(span) this.setLeaveButton() this.setJoinButton(); if (owner == this.account.id + "@" + this.account.domain) { this.setInviteButton(); } this.setDeleteButton(); }) }, (err) => { }, Model.getGlobule(this.conversation.getMac())) Model.eventHub.subscribe(`__join_conversation_evt__`, (uuid) => { this.join_conversation_listener = uuid; }, (evt) => { if (evt.conversation.getUuid() == this.conversation.getUuid()) { if (this.joinBtn) this.joinBtn.style.display = "none" if (this.leaveBtn) this.leaveBtn.style.display = "flex" this.shadowRoot.querySelector("paper-progress").style.display = "none" } }, true) Model.eventHub.subscribe(`__leave_conversation_evt__`, (uuid) => { this.leave_conversation_listener = uuid; }, (evt) => { if (evt == this.conversation.getUuid()) { if (this.joinBtn) this.joinBtn.style.display = "flex" if (this.leaveBtn) this.leaveBtn.style.display = "none" this.shadowRoot.querySelector("paper-progress").style.display = "none" } }, true) } setInviteButton() { if (this.querySelector(`#invite_${this.conversation.getUuid()}_btn`) != undefined) { return } this.innerHtml = "" let range = document.createRange() this.appendChild(range.createContextualFragment(`<paper-button style="font-size:.85rem; width: 20px;" id="invite_${this.conversation.getUuid()}_btn">Invite</paper-button>`)) this.querySelector(`#invite_${this.conversation.getUuid()}_btn`).onclick = () => { Model.eventHub.publish("__invite_conversation_evt__", this.conversation, true) } } /** Leave the conversation */ setLeaveButton(onLeaveConversation) { if (this.leaveBtn) { return } this.innerHtml = "" let range = document.createRange() this.appendChild(range.createContextualFragment(`</div><paper-button style="display:none; font-size:.85rem; width: 20px;" id="leave_${this.conversation.getUuid()}_btn">Leave</paper-button>`)) this.leaveBtn = this.querySelector(`#leave_${this.conversation.getUuid()}_btn`) this.leaveBtn.onclick = () => { ConversationManager.leaveConversation(this.conversation, (messages) => { if (onLeaveConversation != null) { onLeaveConversation(messages); } // local event Model.eventHub.publish("__leave_conversation_evt__", this.conversation.getUuid(), true) this.joinBtn.style.display = "flex" this.leaveBtn.style.display = "none" }, (err) => { ApplicationView.displayMessage(err, 3000) }) } } /** Display join conversation button */ setJoinButton(onJoinConversation) { if (this.joinBtn) { return } this.innerHtml = "" let range = document.createRange() this.appendChild(range.createContextualFragment(`<paper-button style="font-size:.85rem; width: 20px;" id="join_${this.conversation.getUuid()}_btn">Join</paper-button>`)) this.joinBtn = this.querySelector(`#join_${this.conversation.getUuid()}_btn`) this.joinBtn.onclick = () => { if (this.shadowRoot.querySelector("paper-progress").style == "block") { // Block power clicker return } this.shadowRoot.querySelector("paper-progress").style.display = "block" ConversationManager.joinConversation(this.conversation, (conversation, messages) => { if (onJoinConversation != null) { onJoinConversation(messages); } this.conversation = conversation; // local event Model.eventHub.publish("__join_conversation_evt__", { conversation: this.conversation, messages: messages }, true) // network event. if (this.joinBtn) this.joinBtn.style.display = "none" if (this.leaveBtn) this.leaveBtn.style.display = "flex" this.shadowRoot.querySelector("paper-progress").style.display = "none" }, (err) => { ApplicationView.displayMessage(err, 3000) }) } } /** Display delete conversation button */ setDeleteButton(onDeleteConversation) { if (this.querySelector(`#delete_${this.conversation.getUuid()}_btn`) != undefined) { return } this.innerHtml = "" let range = document.createRange() this.appendChild(range.createContextualFragment(`<paper-button style="font-size:.85rem; width: 20px;" id="delete_${this.conversation.getUuid()}_btn">Delete</paper-button>`)) this.querySelector(`#delete_${this.conversation.getUuid()}_btn`).onclick = () => { Model.eventHub.publish("__delete_conversation_evt__", this.conversation, true) if (onDeleteConversation != null) { onDeleteConversation(); } } } } customElements.define('globular-conversation-infos', ConversationInfos) /** * Messenger conversation manager. */ export class Messenger extends HTMLElement { constructor(account) { super(); this.account = account; // Set the shadow dom. this.attachShadow({ mode: "open" }); // The list of listener's this.listeners = {} // Here I will keep the opened conversation infos... this.conversations = {} this.shadowRoot.innerHTML = ` <style> ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: var(--palette-background-default); } ::-webkit-scrollbar-thumb { background: var(--palette-divider); } #layout-div-1{ display: flex; height: 100vh; } #layout-div-1-1{ display: flex; flex-direction: column; } #layout-div-1-2{ flex-grow: 1; } paper-card{ display: none; flex-direction: column; min-width: 477.58px; background-color: var(--palette-background-paper); color: var(--palette-text-primary); max-height: calc(100vh - 60px); border-left: 1px solid var(--palette-divider); border-right: 1px solid var(--palette-divider); border-top: 1px solid var(--palette-divider); } .header{ display: flex; justify-content: center; align-items: center; } .header paper-icon-button { min-width: 40px; } .summary{ font-size: 1.1rem; flex-grow: 1; text-align: center; } .btn_{ display: flex; width: 32px; height: 32px; justify-content: center; align-items: center;position: relative; } .btn_ iron-icon{ --iron-icon-fill-color:var(--palette-text-primary); } .btn_:hover{ cursor:pointer; } .conversations-detail{ border-bottom: 1px solid var(--palette-divider); display: flex; } .messenger-content { flex-grow: 1; display: flex; flex-direction: column; overflow-y: auto; background-color: var(--palette-background-default); } #globular-messages-list-div{ position: relative; display: flex; flex-direction: column; flex-grow: 1; } globular-messages-list{ height: 100%; width: 100%; } globular-conversations-list { border-right: 1px solid var(--palette-divider); } paper-card{ background-color: var(--palette-background-paper); color: var(--palette-text-primary); } paper-tabs { /* custom CSS property */ --paper-tabs-selection-bar-color: var(--palette-primary-main); color: var(--palette-text-primary); --paper-tab-ink: var(--palette-action-disabled); } #conversations-detail-content{ display: flex; flex-direction: column; width: 100%; } globular-paticipants-list{ width: 100%; } globular-attached-files-list{ width: 100%; } @media (max-width: 500px) { .conversations-detail{ flex-direction: column; } globular-conversations-list { border-right: none; border-bottom: 1px solid var(--palette-divider); } paper-card { min-width: 0px; height: calc(100vh - 60px); } .container{ min-height: 200px; min-width: 200px; } } </style> <paper-card class="container"> <div class="header"> <div class="btn_"> <iron-icon id="hide-btn-0" icon="expand-more" style="" icon="add"></iron-icon> <paper-ripple class="circle" recenters=""></paper-ripple> </div> <div class="summary"></div> <div class="btn_"> <paper-ripple class="circle" recenters=""></paper-ripple> <iron-icon style="height: 18px;" id="leave_conversation_btn" icon="exit-to-app"></iron-icon> </div> </div> <iron-collapse class="conversations-detail"> <globular-conversations-list ></globular-conversations-list> <div id="conversations-detail-content"> <paper-tabs selected="0"> <paper-tab id="paticipants-tab">Participants</paper-tab> <paper-tab id="attached-files-tab">Files</paper-tab> </paper-tabs> <div style="display: flex; flex-grow: 1; overflow-y: auto; max-height: 175px;"> <globular-attached-files-list style="display: none;"></globular-attached-files-list> <globular-paticipants-list></globular-paticipants-list> </div> </div> </iron-collapse> <div class="messenger-content"> <div id="globular-messages-list-div"> <div id="scroll-div" style="position: absolute; top:0px; left:0px; right: 0px; bottom: 0px; overflow-y: auto;"> <globular-messages-list></globular-messages-list> </div> </div> <globular-message-editor></globular-message-editor> </div> </paper-card> ` let container = this.shadowRoot.querySelector(".container") let header = this.shadowRoot.querySelector(".header") let offsetTop = header.offsetHeight if (offsetTop == 0) { offsetTop = 60 } container.name = "messenger" setMoveable(header, container, (left, top) => { /** */ }, this, offsetTop) // Set resizable properties... setResizeable(container, (width, height) => { // fix min size. if (height < 600) { height = 600 } if (width < 400) { width = 400 } localStorage.setItem("__messenger_dimension__", JSON.stringify({ width: width, height: height })) let w = ApplicationView.layout.width(); if (w < 500) { container.style.height = "calc(100vh - 60px)" container.style.width = "100vw" } else { container.style.height = height + "px" container.style.width = width + "px" } // 80 is the heigth of the header plus the height of the search bar this.setScroll() }) if (localStorage.getItem("__messenger_dimension__")) { let dimension = JSON.parse(localStorage.getItem("__messenger_dimension__")) if (!dimension) { dimension = { with: 400, height: 600 } } if (dimension.width < 400) { dimension.width = 400 } if (dimension.height < 600) { dimension.height = 600 } container.style.width = dimension.width + "px" container.style.height = dimension.height + "px" localStorage.setItem("__messenger_dimension__", JSON.stringify({ width: dimension.width, height: dimension.height })) } else { container.style.width = "600px" container.style.height = "400px" localStorage.setItem("__messenger_dimension__", JSON.stringify({ width: 600, height: 400 })) } this.setScroll() this.shadowRoot.querySelector("#hide-btn-0").onclick = () => { let button = this.shadowRoot.querySelector("#hide-btn-0") let content = this.shadowRoot.querySelector(".conversations-detail") if (button && content) { if (!content.opened) { button.icon = "expand-less" } else { button.icon = "expand-more" } content.toggle(); } } // Here I will get interfaces components and initialyse each of them. this.conversationsList = this.shadowRoot.querySelector("globular-conversations-list"); this.conversationsList.setAccount(this.account) this.attachedFilesList = this.shadowRoot.querySelector("globular-attached-files-list"); this.attachedFilesList.setAccount(this.account) this.participantsList = this.shadowRoot.querySelector("globular-paticipants-list"); this.participantsList.setAccount(this.account) this.messagesLst = this.shadowRoot.querySelector("globular-messages-list"); this.messagesLst.setAccount(this.account) this.messageEditor = this.shadowRoot.querySelector("globular-message-editor"); this.messageEditor.setAccount(this.account) let participantsTab = this.shadowRoot.querySelector("#paticipants-tab") participantsTab.onclick = () => { this.attachedFilesList.style.display = "none" this.participantsList.style.display = "block" } let filesTab = this.shadowRoot.querySelector("#attached-files-tab")