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,239 lines (997 loc) • 56.6 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/communication-icons'
import '@polymer/iron-icons/editor-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 { Menu } from './Menu';
import { Ringtone } from './Ringtone'
import { Account } from "../Account"
import { Model, generatePeerToken, getUrl } from "../Model"
import { ApplicationView } from '../ApplicationView';
import "./Autocomplete"
import { CreateNotificationRqst, SessionState, Notification, NotificationType, Call, SetCallRqst } from 'globular-web-client/resource/resource_pb';
import { Notification as Notification_ } from '../Notification';
import { randomUUID } from './utility';
import { Application } from '../Application';
import { LogRqst } from 'globular-web-client/log/log_pb';
import { VideoConversation } from './WebRTC';
import * as getUuidByString from 'uuid-by-string';
import { GetThumbnailsRequest } from 'globular-web-client/file/file_pb';
/**
* Login/Register functionality.
*/
export class ContactsMenu extends Menu {
// attributes.
// Create the contact view.
constructor() {
super("Contacts", "social:people", "Contacts")
// The logged account.
this.account = null;
this.inviteContactInput = null;
// Here is the class members.
this.onInviteConctact = null;
// Revoke contact invitation
this.onRevokeContact = null;
// Accept contact invitation
this.onAcceptContact = null;
// Decline contact invitation
this.onDeclineContact = null;
// Delecte contact
this.onDeleteContact = null;
this.width = 350;
if (this.hasAttribute("width")) {
this.width = parseInt(this.getAttribute("width"));
}
this.height = 550;
if (this.hasAttribute("height")) {
this.height = parseInt(this.getAttribute("height"));
}
}
// Init the contact at login time.
init(account) {
this.account = account;
let html = `
<style>
#Contacts-div {
display: flex;
flex-wrap: wrap;
height: 100%;
flex-direction: column;
overflow: hidden;
}
#Contacts_menu_div{
overflow: auto;
height: ${this.height}px;
max-height: 70vh;
overflow-y: auto;
}
#title{
display: none;
justify-content: center;
}
#Contacts-list{
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);
}
/* 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;
}
@media (min-width: 500px) {
paper-tabs {
min-width: 450px;
}
}
@media (max-width: 500px) {
#Contacts_menu_div{
margin-top: 5px;
height: calc(100vh - 100px);
max-height: calc(100vh - 100px);
}
#Contacts-list{
width: calc(100vw - 32px);
padding-bottom: 50px;
}
#title{
display: flex;
}
paper-tabs {
font-size: .95rem;
}
}
</style>
<div id="Contacts-div">
<div id="header" style="width: 100%;">
<div id="title">
<h1 style="flex-grow: 1;">Contact's</h1>
<paper-icon-button id="close-btn" icon="icons:close" role="button" tabindex="0" aria-disabled="false"></paper-icon-button>
</div>
<globular-autocomplete type="email" label="Search Contact" id="invite-contact-input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
<paper-tabs selected="0">
<paper-tab id="contacts-tab">
<span id="contacts-label">Contacts</span>
<paper-badge style="display: none;" for="contacts-label"></paper-badge>
</paper-tab>
<paper-tab id="sent-contact-invitations-tab">
<span id="sent-contact-invitations-label">Sent Invitations</span>
<paper-badge style="display: none;" for="sent-contact-invitations-label"></paper-badge>
</paper-tab>
<paper-tab id="received-contact-invitations-tab">
<span id="received-contact-invitations-label">Received Invitations</span>
<paper-badge style="display: none;" for="received-contact-invitations-label"></paper-badge>
</paper-tab>
</paper-tabs>
</div>
<div id="Contacts-list">
</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())
// Action's
let contactLst = this.shadowRoot.getElementById("Contacts-list")
let contactsTab = this.shadowRoot.getElementById("contacts-tab")
let sentContactInvitationsTab = this.shadowRoot.getElementById("sent-contact-invitations-tab")
let receivedContactInvitationsTab = this.shadowRoot.getElementById("received-contact-invitations-tab")
// The invite contact action.
this.inviteContactInput = this.shadowRoot.getElementById("invite-contact-input")
this.inviteContactInput.onkeyup = () => {
let val = this.inviteContactInput.getValue();
if (val.length >= 3) {
this.findAccountByEmail(val)
} else {
this.inviteContactInput.clear()
}
}
// That function must return the div that display the value that we want.
this.inviteContactInput.displayValue = (contact) => {
let card = new ContactCard(account, contact, true);
// Here depending if the contact is in contact list, in received invitation list or in sent invitation
// list displayed action will be different.
Account.getContacts(account, "{}",
(contacts) => {
const info = contacts.find(obj => {
return obj._id === contact.name;
})
if (contact._id != this.account._id) {
if (info == undefined) {
card.setInviteButton((contact) => {
this.onInviteConctact(contact);
this.inviteContactInput.clear();
})
} else if (info.status == "sent") {
// Here I will display the revoke invitation button.
card.setRevokeButton(this.onRevokeContact)
} else if (info.status == "received") {
// Here I will display the accept/decline button.
card.setAcceptDeclineButton(this.onAcceptContact, this.onDeclineContact)
} else if (info.status == "revoked" || info.status == "deleted") {
// Here I will display the accept/decline button.
card.setInviteButton((contact) => {
this.onInviteConctact(contact);
this.inviteContactInput.clear();
})
} else if (info.status == "accepted") {
// Here I will display the revoke invitation button.
card.setDeleteButton(this.onDeleteContact)
}
}
},
() => {
card.setInviteButton((contact) => {
this.onInviteConctact(contact);
this.inviteContactInput.clear();
})
})
return card
}
let contactList = new ContactList(account, this.onDeleteContact, contactsTab.children[1])
contactLst.appendChild(contactList)
let sentContactInvitations = new SentContactInvitations(account, this.onRevokeContact, sentContactInvitationsTab.children[1]);
contactLst.appendChild(sentContactInvitations)
let receivedContactInvitations = new ReceivedContactInvitations(account, this.onAcceptContact, this.onDeclineContact, receivedContactInvitationsTab.children[1]);
contactLst.appendChild(receivedContactInvitations)
contactsTab.onclick = () => {
contactList.style.display = "block"
receivedContactInvitations.style.display = "none"
sentContactInvitations.style.display = "none"
}
sentContactInvitationsTab.onclick = () => {
contactList.style.display = "none"
receivedContactInvitations.style.display = "none"
sentContactInvitations.style.display = "block"
}
receivedContactInvitationsTab.onclick = () => {
contactList.style.display = "none"
receivedContactInvitations.style.display = "block"
sentContactInvitations.style.display = "none"
}
// set active.
contactsTab.click();
window.dispatchEvent(new Event('resize'));
// Get the list of all accounts (mab).
if (this.getMenuDiv().parentNode)
this.getMenuDiv().parentNode.removeChild(this.getMenuDiv())
}
findAccountByEmail(email) {
Account.getAccounts(`{"email":{"$regex": "${email}", "$options": "im"}}`, (accounts) => {
accounts = accounts.filter((obj) => {
return obj.id !== this.account.id;
});
// set the getValues function that will return the list to be use as filter.
if (this.inviteContactInput != undefined) {
this.inviteContactInput.setValues(accounts)
}
}, (err) => {
//callback([])
ApplicationView.displayMessage(err, 3000)
})
}
}
customElements.define('globular-contacts-menu', ContactsMenu)
/**
* Display the list of sent contact invitation. If the invitation was not pending it will be removed.
*/
export class SentContactInvitations extends HTMLElement {
// Create the applicaiton view.
constructor(account, onRevokeContact, badge) {
super()
this.badge = badge;
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.account = account;
this.onRevokeContact = onRevokeContact;
Model.getGlobule(Application.account.domain).eventHub.subscribe("sent_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
// So here I will append the account into the list.
let invitation = JSON.parse(evt)
Account.getAccount(invitation._id,
(contact) => {
this.appendContact(contact)
},
err => {
ApplicationView.displayMessage(err, 3000)
})
},
false, this)
this.shadowRoot.innerHTML = `
<style>
.contact-invitations-list{
display: flex;
flex-direction: column;
}
.contact-invitations-list globular-contact-card{
border-bottom: 1px solid var(--palette-divider);
}
</style>
<div class="contact-invitations-list">
<slot></slot>
</div>
`
let globule = Model.getGlobule(account.domain)
// So here I will get the list of sent invitation for the account.
Account.getContacts(this.account, `{"status":"sent"}`, (invitations) => {
for (var i = 0; i < invitations.length; i++) {
Account.getAccount(invitations[i]._id,
(contact) => {
this.appendContact(contact)
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
}
}, err => {
ApplicationView.displayMessage(err, 3000)
})
globule.eventHub.subscribe("revoked_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("declined_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("accepted_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
}
// The connection callback.
connectedCallback() {
}
appendContact(contact) {
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_pending_invitation")
if (this.querySelector("#" + id) != undefined) {
return;
}
let card = new ContactCard(this.account, contact)
card.id = id
card.setRevokeButton(this.onRevokeContact)
this.appendChild(card)
this.badge.label = this.children.length
this.badge.style.display = "block"
window.dispatchEvent(new Event('resize'));
}
removeContact(contact) {
// simply remove it.
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_pending_invitation")
let card = this.querySelector("#" + id)
if (card != undefined) {
this.removeChild(card)
this.badge.label = this.children.length
if (this.children.length == 0) {
this.badge.style.display = "none"
}
}
window.dispatchEvent(new Event('resize'));
}
}
customElements.define('globular-sent-contact-invitations', SentContactInvitations)
/**
* Received contact invitations.
*/
export class ReceivedContactInvitations extends HTMLElement {
// Create the applicaiton view.
constructor(account, onAcceptContact, onDeclineContact, badge) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.badge = badge
this.account = account;
this.onAcceptContact = onAcceptContact;
this.onDeclineContact = onDeclineContact;
let domain = Application.domain
if (account.session) {
domain = account.domain
}
let globule = Model.getGlobule(domain) // connect to the local event hub...
globule.eventHub.subscribe("received_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.appendContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("revoked_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("declined_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("accepted_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
.contact-invitations-list{
display: flex;
flex-direction: column;
}
.contact-invitations-list globular-contact-card{
border-bottom: 1px solid var(--palette-divider);
}
</style>
<div class="contact-invitations-list">
<slot></slot>
</div>
`
// So here I will get the list of sent invitation for the account.
Account.getContacts(this.account, `{"status":"received"}`, (invitations) => {
for (var i = 0; i < invitations.length; i++) {
Account.getAccount(invitations[i]._id,
(contact) => {
this.appendContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
}
}, err => {
ApplicationView.displayMessage(err, 3000)
})
}
// The connection callback.
connectedCallback() {
}
appendContact(contact) {
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_pending_invitation")
if (this.querySelector("#" + id) != undefined) {
return;
}
let card = new ContactCard(this.account, contact)
card.id = id
card.setAcceptDeclineButton(this.onAcceptContact, this.onDeclineContact)
this.appendChild(card)
this.badge.label = this.children.length
this.badge.style.display = "block"
}
removeContact(contact) {
// simply remove it.
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_pending_invitation")
let card = this.querySelector("#" + id)
if (card != undefined) {
this.removeChild(card)
this.badge.label = this.children.length
if (this.children.length == 0) {
this.badge.style.display = "none"
}
}
}
}
customElements.define('globular-received-contact-invitations', ReceivedContactInvitations)
/**
* The contact list.
*/
export class ContactList extends HTMLElement {
// Create the applicaiton view.
constructor(account, onDeleteContact, badge) {
super()
this.badge = badge;
// Keep contact card in memory...
this.cards = {}
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.account = account;
this.onDeleteContact = onDeleteContact;
let globule = Model.getGlobule(account.domain)
globule.eventHub.subscribe("accepted_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
if (invitation.profilePicture)
contact.profilePicture = invitation.profilePicture
contact.ringtone = invitation.ringtone
this.appendContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
globule.eventHub.subscribe("deleted_" + account.id + "@" + account.domain + "_evt",
(uuid) => { },
(evt) => {
let invitation = JSON.parse(evt);
Account.getAccount(invitation._id,
(contact) => {
this.removeContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
},
false, this)
// calling events...
globule.eventHub.subscribe("calling_" + account.id + "@" + account.domain + "_evt", uuid => { }, evt => {
// The contact has answer the call!
let call = Call.deserializeBinary(Uint8Array.from(evt.split(",")))
Account.getAccount(call.getCaller(), caller => {
Account.getAccount(call.getCallee(), callee => {
let globule = Application.getGlobule(callee.domain)
generatePeerToken(globule, token => {
let url = getUrl(globule)
// so here I will found the caller ringtone...
let path = caller.ringtone
path = path.replace(globule.config.WebRoot, "")
path.split("/").forEach(item => {
item = item.trim()
if (item.length > 0) {
url += "/" + encodeURIComponent(item)
}
})
url += "?application=" + Model.application
url += "&token=" + token
let audio = new Audio(url)
audio.setAttribute("loop", "true")
audio.setAttribute("autoplay", "true")
// So now I will display the interface the user to ask...
// So here I will get the information from imdb and propose to assciate it with the file.
let toast = ApplicationView.displayMessage(`
<style>
#select-media-dialog{
display: flex; flex-direction: column;
justify-content: center;
width: 100%;
}
paper-icon-button {
width: 40px;
height: 40px;
border-radius: 50%;
}
#call-img{
width: 185.31px;
height: 100%;
align-self: center;
justify-self: center;
padding-top: 10px;
padding-bottom: 15px;
}
</style>
<div id="select-media-dialog">
<div>Incomming Call from</div>
<img style="width: 185.31px; height: 100%; align-self: center; padding-top: 10px; padding-bottom: 15px;" src="${caller.profilePicture}"> </img>
<div style="display: flex; justify-content: center; align-items: center;">
<span style="max-width: 300px; font-size: 1.5rem; margin-right: 16px;">${caller.name}</span>
<paper-icon-button id="ok-button" style="background-color: green; margin-right: 16px;" icon="communication:call"></paper-icon-button>
<paper-icon-button id="cancel-button" style="background-color: red;" icon="communication:call-end">Dismiss</paper-button>
</div>
</div>
`)
let timeout = setTimeout(() => {
audio.pause()
if (toast) {
toast.dismiss();
}
Model.getGlobule(caller.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
if (caller.domain != callee.domain)
Model.getGlobule(callee.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
}, 30 * 1000)
let cancelBtn = toast.el.querySelector("#cancel-button")
cancelBtn.onclick = () => {
toast.dismiss();
audio.pause()
clearTimeout(timeout)
// Here I will send miss call event...
Model.getGlobule(caller.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
if (caller.domain != callee.domain)
Model.getGlobule(callee.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
}
let okBtn = toast.el.querySelector("#ok-button")
okBtn.onclick = () => {
toast.dismiss();
audio.pause()
clearTimeout(timeout)
// The contact has answer the call!
let videoConversation = new VideoConversation(call.getUuid(), caller.domain)
videoConversation.style.position = "fixed"
videoConversation.style.left = "0px"
videoConversation.style.top = "0px"
// append it to the workspace.
ApplicationView.layout.workspace().appendChild(videoConversation)
Model.getGlobule(caller.domain).eventHub.publish(call.getUuid() + "_answering_call_evt", call.serializeBinary(), false)
if (callee.domain != caller.domain) {
Model.getGlobule(callee.domain).eventHub.publish(call.getUuid() + "_answering_call_evt", call.serializeBinary(), false)
}
}
// Here the call was miss...
Model.getGlobule(caller.domain).eventHub.subscribe(call.getUuid() + "_miss_call_evt", uuid => { }, evt => {
clearTimeout(timeout)
// The contact has answer the call!
audio.pause()
toast.dismiss();
}, false)
}, err => ApplicationView.displayMessage(err, 3000))
})
}, err => ApplicationView.displayMessage(err, 3000))
}, false)
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
.contact-invitations-list{
display: flex;
flex-direction: column;
}
.contact-invitations-list globular-contact-card{
border-bottom: 1px solid var(--palette-divider);
}
</style>
<div class="contact-invitations-list">
<slot></slot>
</div>
`
// if the peer is not connected when the user is log in the contact card will not be displayed...
Model.eventHub.subscribe("update_peers_evt_", uuid => { }, peer => {
Account.getContacts(this.account, `{"status":"accepted"}`, (invitations) => {
for (var i = 0; i < invitations.length; i++) {
let invitation = invitations[i]
Account.getAccount(invitation._id,
(contact) => {
if (invitation.profilePicture)
contact.profilePicture = invitation.profilePicture
contact.ringtone = invitation.ringtone
this.appendContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
}
}, err => {
ApplicationView.displayMessage(err, 3000);
})
}, true)
Model.eventHub.subscribe("stop_peer_evt_", uuid => { },
peer => {
Model.eventHub.publish("remove_contact_card_" + peer.getDomain() + "_evt_", {}, true)
}, true)
// So here I will get the list of sent invitation for the account.
Account.getContacts(this.account, `{"status":"accepted"}`, (invitations) => {
for (var i = 0; i < invitations.length; i++) {
let invitation = invitations[i]
Account.getAccount(invitation._id,
(contact) => {
if (invitation.profilePicture)
contact.profilePicture = invitation.profilePicture
contact.ringtone = invitation.ringtone
this.appendContact(contact);
},
err => {
ApplicationView.displayMessage(err, 3000)
console.log(err)
})
}
}, err => {
ApplicationView.displayMessage(err, 3000);
})
}
getContactCard(contact) {
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_accepted_invitation")
let card = this.querySelector("#" + id)
return card;
}
appendContact(contact) {
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_accepted_invitation")
if (this.querySelector("#" + id) != undefined) {
return
}
this.innerHtml = ""
let card = new ContactCard(this.account, contact)
card.id = id
card.setCallButton(this.onCallContact)
card.setDeleteButton(this.onDeleteContact)
card.showRingtone()
this.appendChild(card)
this.badge.label = this.children.length
this.badge.style.display = "block"
// if the globule is disconnected I will remove the contact...
Model.eventHub.subscribe("remove_contact_card_" + contact.domain + "_evt_", uuid => { }, evt => {
if (card.parentNode) {
card.parentNode.removeChild(card)
}
}, true)
}
removeContact(contact) {
// simply remove it.
let id = "_" + getUuidByString(contact.id + "@" + contact.domain + "_accepted_invitation")
let card = this.querySelector("#" + id)
if (card != undefined) {
this.removeChild(card)
this.badge.label = this.children.length
if (this.children.length == 0) {
this.badge.style.display = "none"
}
}
}
onCallContact(contact) {
let call = new Call()
call.setUuid(randomUUID())
call.setCallee(contact.id + "@" + contact.domain)
call.setCaller(Application.account.id + "@" + Application.account.domain)
call.setStarttime(Math.floor(Date.now() / 1000)) // set unix timestamp...
call.setEndtime(-1)
let rqst = new SetCallRqst
rqst.setCall(call)
// Set value on the callee...
let globule = Model.getGlobule(Application.account.domain)
generatePeerToken(globule, token => {
globule.resourceService.setCall(rqst, { application: Application.application, domain: globule.domain, token: token })
.then(rsp => {
Account.getAccount(call.getCaller(), caller => {
Account.getAccount(call.getCallee(), callee => {
let url = getUrl(globule)
// so here I will found the caller ringtone...
let path = callee.ringtone
path = path.replace(globule.config.WebRoot, "")
path.split("/").forEach(item => {
item = item.trim()
if (item.length > 0) {
url += "/" + encodeURIComponent(item)
}
})
url += "?application=" + Model.application
if (localStorage.getItem("user_token") != undefined) {
url += "&token=" + token
}
let audio = new Audio(url)
audio.setAttribute("loop", "true")
audio.setAttribute("autoplay", "true")
// So now I will display the interface the user to ask...
// So here I will get the information from imdb and propose to assciate it with the file.
let toast = ApplicationView.displayMessage(`
<style>
paper-icon-button {
width: 40px;
height: 40px;
border-radius: 50%;
}
</style>
<div id="select-media-dialog">
<div>Outgoing Call to </div>
<div style="display: flex; flex-direction: column; justify-content: center;">
<img style="width: 185.31px; align-self: center; padding-top: 10px; padding-bottom: 15px;" src="${callee.profilePicture}"> </img>
</div>
<span style="max-width: 300px; font-size: 1.5rem;">${callee.name}</span>
<div style="display: flex; justify-content: flex-end;">
<paper-icon-button id="cancel-button" style="background-color: red " icon="communication:call-end"></paper-icon-button>
</div>
</div>
`)
// set timeout...
let timeout = setTimeout(() => {
audio.pause()
toast.dismiss();
Model.getGlobule(caller.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
if (caller.domain != callee.domain)
Model.getGlobule(callee.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
}, 30 * 1000)
let cancelBtn = toast.el.querySelector("#cancel-button")
cancelBtn.onclick = () => {
toast.dismiss();
audio.pause()
clearTimeout(timeout)
// Here I will send miss call event...
Model.getGlobule(caller.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
if (caller.domain != callee.domain)
Model.getGlobule(callee.domain).eventHub.publish(call.getUuid() + "_miss_call_evt", call.serializeBinary(), false)
}
// Here the call succeed...
Model.getGlobule(contact.domain).eventHub.subscribe(call.getUuid() + "_answering_call_evt", uuid => { }, evt => {
// The contact has answer the call!
audio.pause()
toast.dismiss();
clearTimeout(timeout)
let call = Call.deserializeBinary(Uint8Array.from(evt.split(",")))
// The contact has answer the call!
let videoConversation = new VideoConversation(call.getUuid(), Application.account.domain)
videoConversation.style.position = "fixed"
videoConversation.style.left = "0px"
videoConversation.style.top = "0px"
// append it to the workspace.
ApplicationView.layout.workspace().appendChild(videoConversation)
// start the video conversation.
globule.eventHub.publish("start_video_conversation_" + call.getUuid() + "_evt", contact, true)
}, false)
// Here the call was miss...
Model.getGlobule(contact.domain).eventHub.subscribe(call.getUuid() + "_miss_call_evt", uuid => { }, evt => {
// The contact has answer the call!
audio.pause()
toast.dismiss();
clearTimeout(timeout)
generatePeerToken(Model.getGlobule(contact.domain), token => {
let rqst = new CreateNotificationRqst
let notification = new Notification
notification.setDate(parseInt(Date.now() / 1000)) // Set the unix time stamp...
notification.setId(call.getUuid())
notification.setRecipient(contact.id + "@" + contact.domain)
notification.setSender(Application.account.id + "@" + Application.account.domain)
notification.setNotificationType(NotificationType.USER_NOTIFICATION)
notification.setMac(Model.getGlobule(contact.domain).config.Mac)
let date = new Date()
let msg = `
<div style="display: flex; flex-direction: column; padding: 16px;">
<div>
${date.toLocaleString()}
</div>
<div>
Missed call from ${Application.account.name}
</div>
</div>
`
notification.setMessage(msg)
rqst.setNotification(notification)
// Create the notification...
Model.getGlobule(contact.domain).resourceService.createNotification(rqst, {
token: token,
application: Model.application,
domain: Model.domain,
address: Model.address
}).then((rsp) => {
/** nothing here... */
// use the ts class to send notification...
let notification_ = new Notification_
notification_.id = notification.getId()
notification_.date = date
notification_.sender = notification.getSender()
notification_.recipient = notification.getRecipient()
notification_.text = notification.getMessage()
notification_.type = 0
// Send notification...
Model.getGlobule(contact.domain).eventHub.publish(contact.id + "@" + contact.domain + "_notification_event", notification_.toString(), false)
}).catch(err => {
ApplicationView.displayMessage(err, 3000);
console.log(err)
})
Model.eventHub.publish("calling_" + contact.id + "@" + contact.domain + "_evt", call, true)
})
}, false)
// so here I will play the audio of the contact util it respond or the delay was done...
Model.getGlobule(contact.domain).eventHub.publish("calling_" + contact.id + "@" + contact.domain + "_evt", call.serializeBinary(), false)
})
}, err => ApplicationView.displayMessage(err, 3000))
}, err => ApplicationView.displayMessage(err, 3000))
if (contact.domain != Application.account.domain) {
let globule = Model.getGlobule(contact.domain)
generatePeerToken(globule, token => {
globule.resourceService.setCall(rqst, { application: Application.application, domain: globule.domain, token: token })
}, err => ApplicationView.displayMessage(err, 3000))
}
})
}
}
customElements.define('globular-contact-list', ContactList)
/**
* Accept contact button.
*/
export class AcceptDeclineContactBtns extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
let contact = this.getAttribute("contact");
this.onaccpect = null;
this.ondecline = null;
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
</style>
<div style="display: flex;">
<paper-button id="decline_contact_btn" style="font-size:.85rem; width: 20px;">Decline</paper-button>
<paper-button id="accept_contact_btn" style="font-size:.85rem; width: 20px;">Accept</paper-button>
</div>
`
let acceptContactBtn = this.shadowRoot.getElementById("accept_contact_btn")
acceptContactBtn.onclick = () => {
if (this.onaccpect != undefined) {
this.onaccpect(contact)
}
}
let declineContactBtn = this.shadowRoot.getElementById("decline_contact_btn")
declineContactBtn.onclick = () => {
if (this.ondecline != undefined) {
this.ondecline(contact);
}
}
}
}
customElements.define('globular-accept-decline-contact-btns', AcceptDeclineContactBtns)
/**
* Display Contact (account informations)
*/
export class ContactCard extends HTMLElement {
// Create the applicaiton view.
constructor(account, contact, actionable = false) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.account = account;
this.contact = contact;
this.actionable = actionable
t