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)
796 lines (655 loc) • 29.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/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-radio-button/paper-radio-button.js';
import '@polymer/paper-radio-group/paper-radio-group.js';
import { Menu } from './Menu';
import { Application } from '../Application';
import { getAllApplicationsInfo } from 'globular-web-client/api';
import { AddApplicationActionsRqst, AddApplicationActionsRsp, DeleteApplicationRqst, RemoveApplicationActionRqst } from 'globular-web-client/resource/resource_pb';
import { Model } from '../Model';
import { SearchableList } from './List';
import { GetAllActionsRequest } from 'globular-web-client/services_manager/services_manager_pb';
import { ApplicationView } from '../ApplicationView';
import { UninstallApplicationRequest } from 'globular-web-client/applications_manager/applications_manager_pb';
/**
* Login/Register functionality.
*/
export class ApplicationsMenu extends Menu {
// attributes.
// Create the application view.
constructor() {
super("applications", "apps", "Applications")
let html = `
<style>
#applications_menu_div{
background-color: var(--palette-background-paper);
}
#applications-div {
display: none;
height: 100%;
flex-wrap: wrap;
padding: 10px;
width: 300px;
}
</style>
<div id="applications-div">
<globular-applications-panel id="application-panel-toolbar-menu"></globular-applications-panel>
</div>
`
this.shadowRoot.appendChild(this.getMenuDiv())
let range = document.createRange()
this.getMenuDiv().innerHTML = "" // remove existing elements.
this.getMenuDiv().appendChild(range.createContextualFragment(html));
this.getMenuDiv().style.height = "380px";
this.getMenuDiv().style.overflowY = "auto";
}
init() {
this.shadowRoot.appendChild(this.getMenuDiv())
// Action's
this.getMenuDiv().querySelector("#application-panel-toolbar-menu").init(() => {
this.shadowRoot.querySelector(`#applications-div`).style.display = "flex"
this.shadowRoot.removeChild(this.getMenuDiv())
});
}
}
customElements.define('globular-applications-menu', ApplicationsMenu)
/**
* Login/Register functionality.
*/
export class ApplicationsPanel extends HTMLElement {
// attributes.
// Create the application view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.size = "normal"
this.iconSize = 56;
if (this.hasAttribute("size")) {
this.size = this.getAttribute("size")
}
if (this.size == "large") {
this.iconSize = 64
}
this.shadowRoot.innerHTML = `
<style>
.container {
display: inline-flex;
flex-flow: wrap;
}
.application-div {
display: flex;
position: relative;
flex-direction: column;
align-items: center;
width: ${this.iconSize * 1.25}px;
margin: 5px;
padding: 25px;
border-radius: 5px;
transition: background 0.2s ease,padding 0.8s linear;
background-color: var(--palette-background-paper);
}
.application-div img{
filter: invert(0%);
}
.application-div:hover{
cursor: pointer;
-webkit-filter: invert(10%);
filter: invert(10%);
}
.application-div img{
height: ${this.iconSize}px;
width: ${this.iconSize}px;
}
.application-div span{
margin-top: 5px;
color: #404040;
display: inline-block;
font-family: 'Google Sans',Roboto,RobotoDraft,Helvetica,Arial,sans-serif;
font-size: 1rem;
letter-spacing: .09px;
line-height: 16px;
width: 125px;
text-align: center;
color: var(--palette-text-primary);
}
.application-div.normal span{
font-size: .85rem;
width: 100%;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.application-div.normal{
padding: 10px;
}
</style>
<div class="container"></div>
`
}
// The connection callback.
connectedCallback() {
}
init(callback) {
Application.getAllApplicationInfo((infos) => {
let range = document.createRange()
for (var i = 0; i < infos.length; i++) {
let application = infos[i]
let html = `
<div id="${application.getId()}_div" class="application-div">
<paper-ripple recenters></paper-ripple>
<img id="${application.getId()}_img"></img>
<span id="${application.getId()}_span"></span>
<a id="${application.getId()}_lnk" style="display: none;"></a>
</div>
<paper-tooltip for="${application.getId()}_div" style="font-size: .85rem;" role="tooltip" tabindex="-1">${application.getDescription()}</paper-tooltip>
`
let container = this.shadowRoot.querySelector(".container")
container.appendChild(range.createContextualFragment(html))
let div_ = container.querySelector(`#${application.getId()}_div`)
if (div_ != null) {
if (this.size == "normal") {
div_.classList.add("normal")
}
let img = this.shadowRoot.getElementById(application.getId() + "_img")
let lnk = this.shadowRoot.getElementById(application.getId() + "_lnk")
var currentLocation = window.location;
lnk.href = currentLocation.origin + application.getPath();
let title = this.shadowRoot.getElementById(application.getId() + "_span")
img.src = application.getIcon();
title.innerHTML = application.getId();
title.title = application.getId();
if (application.getAlias().length > 0) {
title.innerHTML = application.getAlias()
}
div_.onclick = () => {
lnk.click()
}
// Keep the image up to date.
Application.eventHub.subscribe(`update_application_${application.getId()}_settings_evt`,
(uuid) => {
},
(__applicationInfoStr__) => {
// Set the icon...
let application = JSON.parse(__applicationInfoStr__)
img.src = application.icon;
}, false)
} else {
console.log("no found ", div_)
}
}
if (callback != undefined) {
callback()
}
}, (err) => {
console.log(err)
if (callback != undefined) {
callback()
}
})
}
}
customElements.define('globular-applications-panel', ApplicationsPanel)
/**
* Globular application manager.
*/
export class ApplicationManager extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
#container{
display: flex;
flex-direction: column;
position: relative;
}
.card-content {
min-width: 728px;
padding: 0px;
font-size: 1rem;
}
@media (max-width: 800px) {
.card-content{
min-width: 580px;
}
}
@media (max-width: 600px) {
.card-content{
min-width: 380px;
}
}
paper-card {
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<div id="container">
<paper-card>
<div class="card-content" style="padding: 0px;">
</div>
</paper-card>
</div>
`
// give the focus to the input.
let content = this.shadowRoot.querySelector(".card-content")
// give the focus to the input.
let container = this.shadowRoot.querySelector("#container")
let displayApplications = () => {
content.innerHTML = ""
// Here I will get the list of all roles.
getAllApplicationsInfo(Model.globular,
(applications) => {
applications.forEach(a => {
let panel = new ApplicationPanel(a)
content.appendChild(panel)
})
}, err => { ApplicationView.displayMessage(err, 3000) })
}
// call once
displayApplications()
Model.globular.eventHub.subscribe("refresh_application_evt", uuid => { }, evt => {
displayApplications()
}, true)
}
// The connection callback.
connectedCallback() {
}
}
customElements.define('globular-application-manager', ApplicationManager)
export class ApplicationPanel extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor(application) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Keep group informations.
this.application = application;
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
#container{
display: flex;
flex-direction: column;
align-items: center;
border-bottom: 1px solid var(--palette-background-default);
background-color: var(--palette-background-paper);
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
#content{
padding: 15px;
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
.header{
display: flex;
align-items: center;
width: 100%;
transition: background 0.2s ease,padding 0.8s linear;
background-color: var(--palette-background-paper);
}
.header:hover{
-webkit-filter: invert(10%);
filter: invert(10%);
}
.title{
flex-grow: 1;
margin-left: 16px;
}
img, iron-icon{
margin: 8px;
}
#collapse-panel{
display: flex;
flex-direction: column;
width: 100%;
}
.row{
display: flex;
color: var(--cr-primary-text-color);
font-family: Roboto, Arial, sans-serif;
align-items: center;
padding: 15px 16px 16px;
flex-direction: row;
}
.row span{
flex-basis: 156px;
line-height: 1rem;
font-size: .6875rem;
font-weight: 500;
flex-basis: 156px;
letter-spacing: .07272727em;
text-transform: uppercase;
hyphens: auto;
word-break: break-word;
word-wrap: break-word;
}
.row img {
max-width: 64px;
max-height: 64px;
}
.row div{
flex-grow: 1;
letter-spacing: .00625em;
font-size: 1rem;
font-weight: 400;
line-height: 1.5rem;
hyphens: auto;
word-break: break-word;
word-wrap: break-word;
}
#delete-application-btn{
margin-bottom: 10px;
width: 100px;
align-self: flex-end;
font-size: .85rem;
height: fit-content;
border: none;
color: var(--palette-text-accent);
background: var(--palette-warning-dark);
max-height: 32px;
}
#uninstall-application-btn{
font-size: .85rem;
max-height: 32px;
}
#application-action {
padding: 5px;
}
</style>
<div id="container">
<div class="header">
<img style="width: 32px; height: 32px;" src="${this.application.getIcon()}"></img>
<span class="title">${this.application.getName() + "@" + this.application.getDomain()}</span>
<paper-button id="uninstall-application-btn" >Uninstall</paper-button>
<div style="display: flex; width: 32px; height: 32px; justify-content: center; align-items: center;position: relative;">
<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="collapse-panel">
<div class="row">
<span>Icon</span>
<img src="${this.application.getIcon()}"></img>
</div>
<div class="row">
<span>Id</span>
<div>${this.application.getId()}</div>
</div>
<div class="row">
<span>Publisher</span>
<div>${this.application.getPublisherid()}</div>
</div>
<div class="row">
<span>Alias</span>
<paper-input value="${this.application.getAlias()}"></paper-input>
</div>
<div class="row">
<span>Description</span>
<paper-textarea value="${this.application.getDescription()}"></paper-input>
</div>
<div id="application-action">
</div>
<paper-button class="" id="delete-application-btn" raised="" role="button" tabindex="0" animated="" elevation="1" aria-disabled="false">Delete</paper-button>
</iron-collapse>
</div>
`
let content = this.shadowRoot.querySelector("#collapse-panel")
this.hideBtn = this.shadowRoot.querySelector("#hide-btn")
let deleteBtn = this.shadowRoot.querySelector("#delete-application-btn")
deleteBtn.onclick = () => {
this.onDeleteApplication(application)
}
// Get the uninstall button...
let uninstallBtn = this.shadowRoot.querySelector("#uninstall-application-btn")
uninstallBtn.onclick = () => {
this.onUninstallApplication(this.application)
}
// Here I will create the searchable actions list.
let actionsList = new SearchableList("Actions", this.application.getActionsList(),
(action) => {
// remove action...
let rqst = new RemoveApplicationActionRqst
rqst.setAction(action)
rqst.setApplicationid(application.getId() + "@" + application.getDomain())
Model.globular.resourceService.removeApplicationAction(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
actionsList.removeItem(action)
ApplicationView.displayMessage("Action " + action + " was removed from application " + application.getId() + "@" + application.getDomain(), 3000)
}).catch(err => {
console.log(err)
ApplicationView.displayMessage(err, 3000)
})
},
(action) => {
ApplicationView.displayMessage("Action " + action + " was added to application " + application.getId() + "@" + application.getDomain(), 3000)
},
(actions) => {
// Now I will get the list of all actions install on the server.
let getAllActionsRqst = new GetAllActionsRequest
Model.globular.servicesManagerService.getAllActions(getAllActionsRqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
let actions_ = rsp.getActionsList()
actions.forEach(a => {
actions_.splice(actions_.indexOf(a), 1);
});
// sort the array.
actions_.sort()
let html = `
<style>
#add-application-action-panel{
position: absolute;
right: 0px;
z-index: 1;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
.card-content{
overflow-y: auto;
max-height: 260px;
overflow-y: auto;
}
</style>
<paper-card id="add-application-action-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Action
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<div></div>
</div>
</paper-card>
`
let headerDiv = actionsList.getHeader()
let panel = headerDiv.querySelector("#add-application-action-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-application-action-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
actions_.forEach(a => {
let html = `
<div class="item-div" style="">
<span style="flex-grow: 1;">${a}</span>
<paper-icon-button id="add-action-btn" icon="add" role="button" tabindex="0" aria-disabled="false"></paper-icon-button>
</div>
`
let content = panel.querySelector(".card-content")
content.appendChild(document.createRange().createContextualFragment(html))
let actionDiv = content.children[content.children.length - 1]
let actionAddBtn = actionDiv.children[1]
actionAddBtn.onclick = () => {
let rqst = new AddApplicationActionsRqst
rqst.setApplicationid(application.getId() + "@" + application.getDomain())
rqst.setActionsList([a])
Model.globular.resourceService.addApplicationActions(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") })
.then(rsp => {
actionDiv.parentNode.removeChild(actionDiv)
actionsList.appendItem(a)
// call the onadditem.
actionsList.onadditem(a)
}).catch(err => {
ApplicationView.displayMessage(err, 3000)
})
}
})
}
}).catch(err => {
console.log(err)
ApplicationView.displayMessage(err, 3000)
})
})
actionsList.style.padding = "15px 16px 16px"
content.querySelector("#application-action").appendChild(actionsList)
// give the focus to the input.
this.hideBtn.onclick = () => {
let button = this.shadowRoot.querySelector("#hide-btn")
if (button && content) {
if (!content.opened) {
button.icon = "unfold-more"
} else {
button.icon = "unfold-less"
}
content.toggle();
}
}
}
onDeleteApplication(application) {
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
paper-button{
font-size: .85rem;
height: 32px;
}
</style>
<div id="yes-no-contact-delete-box">
<div>Your about to delete application named ${application.getName() + "@" + application.getDomain()}</div>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button id="yes-delete-contact">Yes</paper-button>
<paper-button id="no-delete-contact">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = document.querySelector("#yes-delete-contact")
let noBtn = document.querySelector("#no-delete-contact")
// On yes
yesBtn.onclick = () => {
let rqst = new DeleteApplicationRqst
rqst.setApplicationid(application.getId() + "@" + application.getDomain())
Model.globular.resourceService.deleteApplication(rqst, { domain: Model.domain, application: Model.application, address: Model.address, token: localStorage.getItem("user_token") }).then((rsp) => {
ApplicationView.displayMessage(
"<iron-icon icon='communication:message' style='margin-right: 10px;'></iron-icon><div>Application named " +
application.getName() +
" was deleted!</div>",
3000
);
Model.eventHub.publish("refresh_application_evt", {}, true)
toast.dismiss();
}).catch(e => {
ApplicationView.displayMessage(e, 3000)
toast.dismiss();
})
}
noBtn.onclick = () => {
toast.dismiss();
}
}
onUninstallApplication(application) {
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
paper-button{
font-size: .85rem;
height: 32px;
}
</style>
<div id="yes-no-contact-delete-box">
<div>Your about to uninstall application named ${application.getName() + "@" + application.getDomain()}</div>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button id="yes-uninstall-btn">Yes</paper-button>
<paper-button id="no-uninstall-btn">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = document.querySelector("#yes-uninstall-btn")
let noBtn = document.querySelector("#no-uninstall-btn")
// On yes
yesBtn.onclick = () => {
let rqst = new UninstallApplicationRequest
rqst.setApplicationid(application.getId() + "@" + application.getDomain())
Model.globular.applicationsManagerService.uninstallApplication(rqst, { domain: Model.domain, address: Model.address, application: Model.application, token: localStorage.getItem("user_token") }).then((rsp) => {
ApplicationView.displayMessage(
"<iron-icon icon='communication:message' style='margin-right: 10px;'></iron-icon><div>Application named " +
application.getName() +
" was uninstall!</div>",
3000
);
Model.eventHub.publish("refresh_application_evt", {}, true)
toast.dismiss();
}).catch(e => {
ApplicationView.displayMessage(e, 3000)
toast.dismiss();
})
}
noBtn.onclick = () => {
toast.dismiss();
}
}
}
customElements.define('globular-application-panel', ApplicationPanel)