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
JavaScript
// 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")