import {css, html, LitElement} from 'lit' import {render} from 'lit/html.js' import {connect} from 'pwa-helpers' import {store} from '../store.js' import {Epml} from '../epml.js' import {addPluginRoutes} from '../plugins/addPluginRoutes.js' import {repeat} from 'lit/directives/repeat.js'; import ShortUniqueId from 'short-unique-id'; import {setIsOpenDevDialog, setNewTab} from '../redux/app/app-actions.js' import FileSaver from 'file-saver' import {get, registerTranslateConfig, translate, use} from '../../translate/index.js' import '@material/mwc-button' import '@material/mwc-dialog' import '@material/mwc-icon' import '@material/mwc-textfield' import '@polymer/paper-icon-button/paper-icon-button.js' import '@polymer/iron-icons/iron-icons.js' import '@polymer/paper-dialog/paper-dialog.js' import '@vaadin/grid' import '@vaadin/text-field' import '../custom-elements/frag-file-input.js' import { defaultQappsTabs } from '../data/defaultQapps.js' registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) }) export const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class ShowPlugin extends connect(store)(LitElement) { static get properties() { return { app: { type: Object }, pluginConfig: { type: Object }, url: { type: String }, linkParam: { type: String }, registeredUrls: { type: Array }, currentTab: { type: Number }, tabs: { type: Array }, theme: { type: String, reflect: true }, tabInfo: { type: Object }, chatLastSeen: { type: Array }, chatHeads: { type: Array }, proxyPort: { type: Number }, isOpenDevDialog: {type: Boolean} } } static get styles() { return css` html { --scrollbarBG: #a1a1a1; --thumbBG: #6a6c75; } *::-webkit-scrollbar { width: 11px; } * { scrollbar-width: thin; scrollbar-color: var(--thumbBG) var(--scrollbarBG); --mdc-theme-primary: rgb(3, 169, 244); --mdc-theme-surface: var(--white); --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); --mdc-text-field-label-ink-color: var(--black); --mdc-text-field-ink-color: var(--black); --mdc-select-ink-color: var(--black); --mdc-select-fill-color: var(--black); --mdc-select-label-ink-color: var(--black); --mdc-select-idle-line-color: var(--black); --mdc-select-hover-line-color: var(--black); --mdc-select-outlined-idle-border-color: var(--txtfieldborder); --mdc-select-outlined-hover-border-color: var(--txtfieldhoverborder); --mdc-dialog-content-ink-color: var(--black); --mdc-dialog-shape-radius: 25px; --mdc-dialog-min-width: 400px; --mdc-dialog-max-width: 700px; } *::-webkit-scrollbar-track { background: var(--scrollbarBG); } *::-webkit-scrollbar-thumb { background-color: var(--thumbBG); border-radius: 6px; border: 3px solid var(--scrollbarBG); } .hideIframe { display: none; position: absolute; z-Index: -10; } .showIframe { display: flex; position: relative; z-Index: 1; } .tabs { display: flex; width: 100%; max-width: 100%; justify-content: flex-start; padding-top: 0.5em; padding-left: 0.5em; background: var(--sidetopbar); border-bottom: 1px solid var(--black); height: 48px; box-sizing: border-box; } .tab { padding: 0.5em; background: var(--white); border-top-right-radius: 10px; border-top-left-radius: 10px; border-top: 1px solid grey; border-left: 1px solid grey; border-right: 1px solid grey; color: grey; cursor: pointer; transition: background 0.3s; position: relative; width: auto; min-width: 110px; max-width: 220px; overflow: hidden; z-index: 2; } .tabCard { display: inline-block; } .tabTitle { display: inline-block; position: relative; width: auto; min-width: 1px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .tab:hover { background: var(--nav-color-hover); color: var(--black); min-width: fit-content; } .tab.active { display: inline-block; min-width: fit-content; max-width: 200px; margin-bottom: -1px; background: var(--white); color: var(--black); border-top-right-radius: 10px; border-top-left-radius: 10px; border-top: 1px solid var(--black); border-left: 1px solid var(--black); border-right: 1px solid var(--black); border-bottom: 1px solid var(--white); z-index: 1; } .close { position: absolute; top: 8px; right: 5px; color: var(--black); --mdc-icon-size: 20px; } .close:hover { color: #C6011F; font-weight: bold; } .tab .close, .tab .show { display: none; } .tab.active .close, .tab.active .show { display: inline-block; color: var(--black); } .tab:hover .close, .tab:hover .show { display: inline-block; color: var(--black); } .tab .close:hover, .tab.active .close:hover { color: #C6011F; font-weight: bold; } .add-tab-button { margin-left: 10px; font-weight: bold; background: none; border: none; color: var(--accent-color); font-size: 2em; cursor: pointer; transition: color 0.3s; } .add-tab-button:hover { color: var(--black); } .add-dev-button { position: fixed; right: 20px; margin-left: 10px; margin-top: 4px; max-height: 28px; padding: 5px 5px; font-size: 14px; background-color: var(--accent-color); color: white; border: 1px solid transparent; border-radius: 3px; cursor: pointer; } .add-dev-button:hover { opacity: 0.8; cursor: pointer; } .red { --mdc-theme-primary: #F44336; } .iconActive { position: absolute; top: 5px; color: var(--accent-color); --mdc-icon-size: 24px; } .iconInactive { position: absolute; top: 5px; color: #999; --mdc-icon-size: 24px; } .tab:hover .iconInactive { color: var(--accent-color); } .count { position: relative; top: -5px; font-weight: bold; background-color: #C6011F; color: white; font-size: 12px; padding: 2px 6px; text-align: center; border-radius: 5px; animation: pulse 1500ms infinite; animation-duration: 6s; } .ml-5 { margin-left: 5px; } .ml-10 { margin-left: 10px; } .ml-15 { margin-left: 15px; } .ml-20 { margin-left: 20px; } .ml-25 { margin-left: 25px; } .ml-30 { margin-left: 30px; } .ml-35 { margin-left: 35px; } .ml-40 { margin-left: 40px; } @keyframes pulse { 0% { box-shadow:#C6011F 0 0 0 0; } 75% { box-shadow:#ff69b400 0 0 0 16px; } } ` } constructor() { super() this.registeredUrls = [] this.initialRegisteredUrls = [] this.currentTab = 0 this.tabs = [] this.uid = new ShortUniqueId() this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.tabInfo = {} this.chatLastSeen = [] this.chatHeads = [] this.proxyPort = 0 this.isOpenDevDialog = false } render() { const plugSrc = (myPlug) => { return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}` } return html`
${this.tabs.map((tab, index) => { let title = '' let icon = '' let count = 0 if (tab.myPlugObj && tab.myPlugObj.title === "Overview Page") { title = html`${translate('tabmenu.tm28')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Minting Details") { title = html`${translate('tabmenu.tm1')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Become a Minter") { title = html`${translate('tabmenu.tm2')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Sponsorship List") { title = html`${translate('tabmenu.tm3')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Wallets") { title = html`${translate('tabmenu.tm4')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Trade Portal") { title = html`${translate('tabmenu.tm5')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Auto Buy") { title = html`${translate('tabmenu.tm6')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Reward Share") { title = html`${translate('tabmenu.tm7')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Q-Chat") { title = html`${translate('tabmenu.tm8')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Name Registration") { title = html`${translate('tabmenu.tm9')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Names Market") { title = html`${translate('tabmenu.tm10')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Websites") { title = html`${translate('tabmenu.tm11')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Q-Apps") { title = html`${translate('tabmenu.tm12')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Group Management") { title = html`${translate('tabmenu.tm13')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Data Management") { title = html`${translate('tabmenu.tm14')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Puzzles") { title = html`${translate('tabmenu.tm15')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Node Management") { title = html`${translate('tabmenu.tm16')}` } else if (tab.myPlugObj && tab.myPlugObj.title === "Qortal Lottery") { title = html`${translate('tabmenu.tm42')}` } else if (tab.myPlugObj && tab.myPlugObj.url === "myapp") { title = tab.myPlugObj && tab.myPlugObj.title } else if (tab.myPlugObj && tab.myPlugObj.url === "devmode") { title = html`${translate('tabmenu.tm38')}` } else { title = html`${translate('tabmenu.tm17')}` } if (tab.myPlugObj && tab.myPlugObj.mwcicon) { icon = tab.myPlugObj.mwcicon } else { icon = 'tab' } if (tab.myPlugObj && (tab.myPlugObj.url === 'myapp') && this.tabInfo[tab.id]) { title = this.tabInfo[tab.id].name } if (tab.myPlugObj && (tab.myPlugObj.url === 'myapp') && this.tabInfo[tab.id]) { count = this.tabInfo[tab.id].count } if (tab.myPlugObj && tab.myPlugObj.url === 'q-chat') { for (const chat of this.chatHeads) { const lastReadMessage = this.chatLastSeen.find((ch) => { let id if (chat.groupId === 0) { id = chat.groupId } else if (chat.groupId) { id = chat.groupId } else { id = chat.address } return ch.key.includes(id) }) if (lastReadMessage && lastReadMessage.timestamp < chat.timestamp) { count = count + 1 } } } return html`
${tab.myPlugObj && tab.myPlugObj.url === "myapp" ? html` ` : html` ${icon} `}
${count ? html` ${title} ${count} { event.stopPropagation(); this.removeTab(index, tab.id) }}>close ` : html` ${title} {event.stopPropagation(); this.removeTab(index, tab.id)}}>close `}
` })}
${repeat(this.tabs, (tab) => tab.id, (tab, index) => html`
this.changePage(val)} >
`)} { this.shadowRoot.getElementById('domainInput').value = '' this.shadowRoot.getElementById('portInput').value = '' this.isOpenDevDialog = false store.dispatch(setIsOpenDevDialog(false)) }} >

${translate('tabmenu.tm39')}


${translate("general.close")} ${translate('tabmenu.tm40')}
` } firstUpdated() { this.changeLanguage() this.tabs.forEach((tab, index) => { const frame = this.shadowRoot.getElementById(`showPluginFrame${index}`) this.createEpmlInstance(frame, index) }) window.addEventListener('storage', () => { const checkLanguage = localStorage.getItem('qortalLanguage') const checkTheme = localStorage.getItem('qortalTheme') use(checkLanguage) if (checkTheme === 'dark') { this.theme = 'dark' } else { this.theme = 'light' } document.querySelector('html').setAttribute('theme', this.theme) }) } changeLanguage() { const checkLanguage = localStorage.getItem('qortalLanguage') if (checkLanguage === null || checkLanguage.length === 0) { localStorage.setItem('qortalLanguage', 'us') use('us') } else { use(checkLanguage) } } async getUpdateComplete() { await super.getUpdateComplete() return true } async getProxyPort() { this.proxyPort = 0 let framework = '' const domain = this.shadowRoot.getElementById('domainInput').value const port = this.shadowRoot.getElementById('portInput').value if (domain.length >= 3 && port.length >= 2) { framework = domain + ':' + port } else { let errorString = get("tabmenu.tm41") parentEpml.request('showSnackBar', `${errorString}`) return } let framePort = await parentEpml.request('apiCall', { url: `/developer/proxy/start`, method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: `${framework}` }) this.createUrl(framePort) } createUrl(framePort) { this.proxyPort = framePort const myFrameNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const myFrameNodeUrl = myFrameNode.protocol + '://' + myFrameNode.domain + ':' + this.proxyPort this.changePage({ "url": "devmode", "domain": "core", "page": `qdn/browser/index.html?link=${myFrameNodeUrl}&dev=FRAMEWORK`, "title": "Dev Server", "icon": "vaadin:desktop", "mwcicon": "api", "menus": [], "parent": false }) this.shadowRoot.querySelector("#addDevDialog").close() } async addTab(tab) { if (this.tabs == []) { // ...Nothing to do } else { this.tabs.forEach((rac, index) => { let racId = '' let tabRacId = '' let frameRacId = '' let plugRacId = '' let iconRacId = '' racId = rac.id tabRacId = 'tab-' + racId frameRacId = 'frame-' + racId plugRacId = 'plug-' + racId iconRacId = 'icon-' + racId const plugObjRac = rac.url var tabActiveRac = this.shadowRoot.getElementById(tabRacId) var frameActiveRac = this.shadowRoot.getElementById(frameRacId) var plugActiveRac = this.shadowRoot.getElementById(plugRacId) var iconActiveRac = this.shadowRoot.getElementById(iconRacId) if (plugObjRac === undefined || "") { tabActiveRac.classList.remove("active") iconActiveRac.classList.remove("iconActive") iconActiveRac.classList.add("iconInactive") plugActiveRac.classList.remove("showIframe") plugActiveRac.classList.add("hideIframe") } else { tabActiveRac.classList.remove("active") iconActiveRac.classList.remove("iconActive") iconActiveRac.classList.add("iconInactive") frameActiveRac.classList.remove("showIframe") frameActiveRac.classList.add("hideIframe") } }) } this.tabs = [...this.tabs, tab] await this.getUpdateComplete() // add the new tab to the tabs array const newIndex = this.tabs.length - 1 // render the tab and wait for it to be added to the DOM const frame = this.shadowRoot.getElementById(`showPluginFrame${newIndex}`) this.createEpmlInstance(frame, newIndex) } removeTab(index, tabA) { const tabB = this.tabs.length - 1 const tabC = this.tabs[tabB].id if (tabC === tabA) { let theId = '' let tabId = '' let frameId = '' let plugId = '' let iconId = '' this.tabs = this.tabs.filter((tab, tIndex) => tIndex !== index) this.currentTab = this.tabs.length - 1; const tabD = this.tabs.length - 1 const plugObj = this.tabs[tabD].url theId = this.tabs[tabD].id tabId = 'tab-' + theId frameId = 'frame-' + theId plugId = 'plug-' + theId iconId = 'icon-' + theId var tabActive = this.shadowRoot.getElementById(tabId) var frameActive = this.shadowRoot.getElementById(frameId) var plugActive = this.shadowRoot.getElementById(plugId) var iconActive = this.shadowRoot.getElementById(iconId) if (plugObj === undefined || "") { tabActive.classList.add("active") iconActive.classList.remove("iconInactive") iconActive.classList.add("iconActive") plugActive.classList.remove("hideIframe") plugActive.classList.add("showIframe") } else { tabActive.classList.add("active") iconActive.classList.remove("iconInactive") iconActive.classList.add("iconActive") frameActive.classList.remove("hideIframe") frameActive.classList.add("showIframe") } this.requestUpdate() } else { // Remove tab from array this.tabs = this.tabs.filter((tab, tIndex) => tIndex !== index) if (this.tabs.length !== 0) { this.currentTab = 0 } this.requestUpdate() } } createEpmlInstance(frame, index) { const showingPluginEpml = new Epml({ type: 'WINDOW', source: frame.contentWindow }) addPluginRoutes(showingPluginEpml); showingPluginEpml.imReady() // store Epml instance in tab for later use this.tabs[index].epmlInstance = showingPluginEpml // Register each instance with a unique identifier Epml.registerProxyInstance(`visible-plugin-${index}`, showingPluginEpml) } updated(changedProps) { if (changedProps.has('url') || changedProps.has('registeredUrls')) { const plugArr = [] this.registeredUrls.forEach(myPlugArr => { myPlugArr.menus.length === 0 ? plugArr.push(myPlugArr) : myPlugArr.menus.forEach(i => plugArr.push(myPlugArr, i)) }) const myPlugObj = plugArr.find(pagePlug => { return pagePlug.url === this.url }) if (this.tabs.length === 0) { this.addTab({ url: "", id: this.uid.rnd() }) } else { const copiedTabs = [...this.tabs] copiedTabs[this.currentTab] = { ...copiedTabs[this.currentTab], url: this.url, myPlugObj } this.tabs = copiedTabs } this.requestUpdate() } if (changedProps.has('computerUrl')) { if (this.computedUrl !== 'about:blank') { this.loading = true } } } changePage(page) { const copiedTabs = [...this.tabs] copiedTabs[this.currentTab] = { ...copiedTabs[this.currentTab], myPlugObj: page, url: page.url } this.tabs = copiedTabs } async stateChanged(state) { const split = state.app.url.split('/') const newRegisteredUrls = state.app.registeredUrls let newUrl, newLinkParam if (newRegisteredUrls !== this.registeredUrls) { this.registeredUrls = newRegisteredUrls } if (split[0] === '' && split[1] === 'app' && split[2] === undefined) { newUrl = '' newLinkParam = '' } else if (split.length === 5 && split[1] === 'app') { newUrl = split[2] newLinkParam = split[3] === undefined ? '' : '?' + split[3] + '/' + split[4] } else if (split[1] === 'app') { newUrl = split[2] newLinkParam = '' } else { newUrl = '404' newLinkParam = '' } if (newUrl !== this.url) { this.url = newUrl } if (newLinkParam !== this.linkParam) { this.linkParam = newLinkParam } if (this.tabInfo !== state.app.tabInfo) { this.tabInfo = state.app.tabInfo } if (this.chatLastSeen !== state.app.chatLastSeen) { this.chatLastSeen = state.app.chatLastSeen } if (state.app.chatHeads !== this.unModifiedChatHeads) { let chatHeads = [] if (state.app.chatHeads && state.app.chatHeads.groups) { chatHeads = [...chatHeads, ...state.app.chatHeads.groups] } if (state.app.chatHeads && state.app.chatHeads.direct) { chatHeads = [...chatHeads, ...state.app.chatHeads.direct] } this.chatHeads = chatHeads this.unModifiedChatHeads = state.app.chatHeads } if (state.app.newTab) { const newTab = state.app.newTab if(newTab.openExisting && this.tabs.find((tab)=> tab.url === newTab.url)){ const findIndex = this.tabs.findIndex((tab) => tab.url === newTab.url) if (findIndex !== -1) { this.currentTab = findIndex } store.dispatch(setNewTab(null)) } else if (!this.tabs.find((tab) => tab.id === newTab.id)) { this.addTab(newTab) this.currentTab = this.tabs.length - 1 store.dispatch(setNewTab(null)) //clear newTab } else { const findIndex = this.tabs.findIndex((tab) => tab.id === newTab.id) if (findIndex !== -1) { const copiedTabs = [...this.tabs] copiedTabs[findIndex] = newTab this.tabs = copiedTabs this.currentTab = findIndex } store.dispatch(setNewTab(null)) //clear newTab } } if(state.app.isOpenDevDialog){ this.isOpenDevDialog = state.app.isOpenDevDialog } } } window.customElements.define('show-plugin', ShowPlugin) class NavBar extends connect(store)(LitElement) { static get properties() { return { menuList: { type: Array }, newMenuList: { type: Array }, myMenuList: { type: Array }, myMenuPlugins: { type: Array }, myApps: { type: Array }, addressInfo: { type: Object }, changePage: { attribute: false }, pluginName: { type: String }, pluginType: { type: String }, pluginPage: { type: String }, mwcIcon: { type: String }, pluginNameToDelete: { type: String }, pluginNumberToDelete: { type: String }, textFieldDisabled: { type: Boolean }, initialName: { type: String }, newId: { type: String }, removeTitle: { type: String }, myFollowedNames: { type: Array }, myFollowedNamesList: { type: Array }, searchNameContentString: { type: String }, searchNameResources: { type: Array } } } static styles = css` * { --mdc-theme-primary: rgb(3, 169, 244); --mdc-theme-surface: var(--white); --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); --mdc-text-field-label-ink-color: var(--black); --mdc-text-field-ink-color: var(--black); --mdc-dialog-content-ink-color: var(--black); --mdc-dialog-shape-radius: 25px; --mdc-dialog-min-width: 300px; --mdc-dialog-max-width: 700px; } .parent { display: flex; flex-direction: column; flex-flow: column; align-items: center; padding: 20px; height: calc(100vh - 120px); overflow-y: auto; } .navbar { display: flex; justify-content: space-between; align-items: center; background-color: var(--white); padding: 10px 20px; max-width: 750px; width: 80%; } .navbar input { font-size: 16px; color: #000; padding: 5px; flex-grow: 1; margin-right: 10px; border: 1px solid var(--black); } .navbar button { padding: 5px 10px; font-size: 18px; background-color: var(--app-background-1); background-image: linear-gradient(315deg, var(--app-background-1) 0%, var(--app-background-2) 74%); color: var(--app-icon); border: 1px solid transparent; border-radius: 3px; cursor: pointer; } .navbar button:hover { background-color: #45a049; } .app-list { display: flex; justify-content: space-between; padding: 10px 0; gap: 10px; flex-wrap: wrap; } .app-list .app-icon { position: relative; text-align: center; font-size: 15px; font-weight: bold; color: var(--black); width: 175px; height: 110px; background: transparent; padding: 5px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; } .text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; width: 100%; min-width: 1px; } .app-list .app-icon span { display: block; } .app-icon-box { display: flex; align-items: center; padding-left: 14px; width: 80px; min-width: 80px; height: 80px; min-height: 80px; background-color: var(--app-background-1); background-image: linear-gradient(315deg, var(--app-background-1) 0%, var(--app-background-2) 74%); border-top-left-radius: 10px; border-top-right-radius: 20px; border-bottom-left-radius: 20px; border-bottom-right-radius: 10px; position: relative; } .app-list .app-icon:hover .removeIcon { display: inline; } .menuIcon { color: var(--app-icon); --mdc-icon-size: 64px; cursor: pointer; } .menuIconPos { right: -2px; } .removeIconPos { position: absolute; top: -10px; right: -10px; z-index: 1; } .menuIconPos:hover .removeIcon { display: inline; } .removeIcon { display: none; color: var(--black); --mdc-icon-size: 28px; cursor: pointer; position: relative; z-index: 1; } .removeIcon:hover { color: #C6011F; font-weight: bold; } .red { --mdc-theme-primary: #F44336; } select { padding: 10px 10px 10px 10px; width: 100%; font-size: 16px; font-weight: 500; background: var(--white); color: var(--black); -webkit-appearance: none; -moz-appearance: none; appearance: none; background-image: url('/img/arrow.png'); background-repeat: no-repeat; background-position: right 10px center; background-size: 20px; } .resetIcon { position: fixed; right: 20px; top: 116px; color: #666; --mdc-icon-size: 32px; cursor: pointer; } .resetIcon:hover { color: var(--accent-color); font-weight: bold; } .searchIcon { position: fixed; left: 20px; top: 116px; color: #666; --mdc-icon-size: 32px; cursor: pointer; } .searchIcon:hover { color: var(--accent-color); font-weight: bold; } .importIcon { position: fixed; left: 20px; bottom: 16px; color: #666; --mdc-icon-size: 32px; cursor: pointer; } .importIcon:hover { color: var(--accent-color); font-weight: bold; } .exportIcon { position: fixed; right: 20px; bottom: 16px; color: #666; --mdc-icon-size: 32px; cursor: pointer; } .exportIcon:hover { color: var(--accent-color); font-weight: bold; } paper-dialog.searchSettings { width: 100%; max-width: 550px; height: auto; max-height: 600px; background-color: var(--white); color: var(--black); line-height: 1.6; overflow: hidden; border: 1px solid var(--black); border-radius: 10px; padding: 15px; } paper-dialog button { padding: 5px 10px; font-size: 18px; background-color: var(--accent-color); color: white; border: 1px solid transparent; border-radius: 5px; cursor: pointer; } paper-dialog button:hover { opacity: 0.8; cursor: pointer; } .search { display: inline; width: 50%; align-items: center; } .divCard { height: auto; max-height: 500px; border: 1px solid var(--border); padding: 1em; margin-bottom: 1em; } img { border-radius: 25%; max-width: 32px; height: 100%; max-height: 32px; } vaadin-text-field[focused]::part(input-field) { border-color: var(--accent-color); } ` constructor() { super() this.menuList = [] this.newMenuList = [] this.myMenuList = [] this.myMenuPlugins = [] this.addressInfo = {} this.pluginName = '' this.pluginType = '' this.pluginPage = '' this.myApps = '' this.mwcIcon = '' this.pluginNameToDelete = '' this.pluginNumberToDelete = '' this.textFieldDisabled = false this.initialName = '' this.newId = '' this.removeTitle = '' this.myFollowedNames = [] this.myFollowedNamesList = [] this.searchContentString = '' this.searchNameResources = [] this._updateMyMenuPlugins = this._updateMyMenuPlugins.bind(this) } render() { return html`
reset_tv person_search upload download
${repeat(this.myMenuList, (plugin) => plugin.url, (plugin, index) => html`
${this.renderRemoveIcon(plugin.url, plugin.mwcicon, plugin.title, plugin.pluginNumber, plugin)}
${this.renderTitle(plugin.url, plugin.title)}
`)}
add
${translate("tabmenu.tm19")}

${translate("tabmenu.tm26")}



${translate("tabmenu.tm24")}

${translate("tabmenu.tm19")} ${translate("general.close")}

${translate("tabmenu.tm27")}



${translate("tabmenu.tm23")}

${this.removeTitle}

${translate("general.yes")} ${translate("general.no")}

{ render(html`${this.renderNameAvatar(data.item)}`, root) }} > { render(html`${data.item.name}`, root) }} > { render(html`${this.renderMyFollowUnfollowButton(data.item)}`, root) }} > ${this.isEmptyArray(this.searchNameResources) ? html` ${translate("login.entername")} `: ''}

${translate("tabmenu.tm31")}


{ render(html`${this.renderNameAvatar(data.item)}`, root) }} > { render(html`${data.item.name}`, root) }} > { render(html`${this.renderMyFollowUnfollowButton(data.item)}`, root) }} > ${this.isEmptyArray(this.myFollowedNamesList) ? html` ${translate("tabmenu.tm32")} `: ''}

${translate("tabmenu.tm33")}



${translate("walletpage.wchange56")}

${translate("tabmenu.tm35")}
${translate("general.close")}
` } async firstUpdated() { addPluginRoutes(parentEpml) parentEpml.imReady() const addressInfo = this.addressInfo const isMinter = addressInfo?.error !== 124 && +addressInfo?.level > 0 const isSponsor = +addressInfo?.level >= 5 const appDelay = ms => new Promise(res => setTimeout(res, ms)) await appDelay(50) await this.checkMyMenuPlugins() if (!isMinter) { this.newMenuList = this.myMenuPlugins.filter((minter) => { return minter.url !== 'minting' }) } else { this.newMenuList = this.myMenuPlugins.filter((minter) => { return minter.url !== 'become-minter' }) } if (!isSponsor) { this.myMenuList = this.newMenuList.filter((sponsor) => { return sponsor.url !== 'sponsorship-list' }) } else { this.myMenuList = this.newMenuList } await this.getMyFollowedNames() await this.getMyFollowedNamesList() } async _updateMyMenuPlugins(event) { await new Promise((res)=> { setTimeout(() => { res() }, 1000); }) const detail = event.detail this.myMenuPlugins = detail const addressInfo = this.addressInfo const isMinter = addressInfo?.error !== 124 && +addressInfo?.level > 0 const isSponsor = +addressInfo?.level >= 5 if (!isMinter) { this.newMenuList = this.myMenuPlugins.filter((minter) => { return minter.url !== 'minting' }) } else { this.newMenuList = this.myMenuPlugins.filter((minter) => { return minter.url !== 'become-minter' }) } if (!isSponsor) { this.myMenuList = this.newMenuList.filter((sponsor) => { return sponsor.url !== 'sponsorship-list' }) } else { this.myMenuList = this.newMenuList } this.requestUpdate() } connectedCallback() { super.connectedCallback() window.addEventListener('myMenuPlugs-event', this._updateMyMenuPlugins) } disconnectedCallback() { window.removeEventListener('myMenuPlugs-event', this._updateMyMenuPlugins) super.disconnectedCallback() } openImportDialog() { this.shadowRoot.getElementById('importTabMenutDialog').show() } importTabMenu(file) { this.myMenuPlugins = [] let myFile = '' localStorage.removeItem("myMenuPlugs") myFile = file const newTabMenu = JSON.parse((myFile) || "[]") const copyPayload = [...newTabMenu] localStorage.setItem("myMenuPlugs", JSON.stringify(newTabMenu)) this.saveSettingToTemp(copyPayload) this.shadowRoot.getElementById('importTabMenutDialog').close() this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") this.firstUpdated() let success5string = get("tabmenu.tm36") parentEpml.request('showSnackBar', `${success5string}`) } exportTabMenu() { let tabMenu = "" const qortalTabMenu = JSON.stringify(localStorage.getItem("myMenuPlugs")) const qortalTabMenuSave = JSON.parse((qortalTabMenu) || "[]") const blob = new Blob([qortalTabMenuSave ], { type: 'text/plain;charset=utf-8' }) tabMenu = "qortal.tabmenu" this.saveFileToDisk(blob, tabMenu) } async saveFileToDisk(blob, fileName) { try { const fileHandle = await self.showSaveFilePicker({ suggestedName: fileName, types: [{ description: "File", }] }) const writeFile = async (fileHandle, contents) => { const writable = await fileHandle.createWritable() await writable.write(contents) await writable.close() } writeFile(fileHandle, blob).then(() => console.log("FILE SAVED")) let snack4string = get("tabmenu.tm37") parentEpml.request('showSnackBar', `${snack4string} ${fileName}`) } catch (error) { if (error.name === 'AbortError') { return } FileSaver.saveAs(blob, fileName) let snack4string = get("tabmenu.tm37") parentEpml.request('showSnackBar', `${snack4string} ${fileName}`) } } openNameSearch() { this.searchNameResources = [] this.shadowRoot.getElementById('searchNameContent').value = '' this.shadowRoot.getElementById('myFollowedNamesDialog').close() this.shadowRoot.getElementById('searchNameDialog').open() } closeNameSearch() { this.shadowRoot.getElementById('searchNameDialog').close() } openMyFollowedNames() { this.shadowRoot.getElementById('searchNameDialog').close() this.shadowRoot.getElementById('myFollowedNamesDialog').open() this.getMyFollowedNamesList() } closeMyFollowedNames() { this.shadowRoot.getElementById('myFollowedNamesDialog').close() } async getMyFollowedNames() { let myFollowedNames = await parentEpml.request('apiCall', { url: `/lists/followedNames?apiKey=${this.getApiKey()}` }) this.myFollowedNames = myFollowedNames } searchNameKeyListener(e) { if (e.key === 'Enter') { this.searchNameResult() } } async getMyFollowedNamesList() { const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const myNodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port const followedNamesUrl = `${myNodeUrl}/lists/followedNames?apiKey=${this.getApiKey()}` var myFollowedNamesNew = [] this.myFollowedNamesList = [] await fetch(followedNamesUrl).then(response => { return response.json() }).then(data => { return data.map(item => { const addListName = { name: item } myFollowedNamesNew.push(addListName) }) }) this.myFollowedNamesList = myFollowedNamesNew if(this.shadowRoot.getElementById('myFollowedNamesDialog').opened) { this.shadowRoot.getElementById('myFollowedNamesDialog').notifyResize() } } async searchNameResult() { let searchMyName = this.shadowRoot.getElementById('searchNameContent').value if (searchMyName.length === 0) { let err1string = get("appspage.schange34") parentEpml.request('showSnackBar', `${err1string}`) } else { let searchNameResources = await parentEpml.request('apiCall', { url: `/names/search?query=${searchMyName}&prefix=true&limit=0&reverse=true` }) if (this.isEmptyArray(searchNameResources)) { let err2string = get("appspage.schange17") parentEpml.request('showSnackBar', `${err2string}`) } else { this.searchNameResources = searchNameResources if(this.shadowRoot.getElementById('searchNameDialog').opened) { this.shadowRoot.getElementById('searchNameDialog').notifyResize() } } } } renderNameAvatar(nameObj) { let myName = nameObj.name const myNameNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const myNodeUrl = myNameNode.protocol + '://' + myNameNode.domain + ':' + myNameNode.port const nameUrl = `${myNodeUrl}/arbitrary/THUMBNAIL/${myName}/qortal_avatar?async=true` return html`` } renderMyFollowUnfollowButton(nameObj) { let name = nameObj.name if (this.myFollowedNames == null || !Array.isArray(this.myFollowedNames)) { return html`` } if (this.myFollowedNames.indexOf(name) === -1) { return html` this.myFollowName(nameObj)}>add_to_queue ${translate("appspage.schange29")}` } else { return html` this.myUnfollowName(nameObj)}>remove_from_queue ${translate("appspage.schange30")}` } } async myFollowName(nameObj) { let name = nameObj.name let items = [ name ] let namesJsonString = JSON.stringify({ "items": items }) let ret = await parentEpml.request('apiCall', { url: `/lists/followedNames?apiKey=${this.getApiKey()}`, method: 'POST', headers: { 'Content-Type': 'application/json' }, body: `${namesJsonString}` }) if (ret === true) { this.myFollowedNames = this.myFollowedNames.filter(item => item != name) this.myFollowedNames.push(name) } else { let err3string = get("appspage.schange22") parentEpml.request('showSnackBar', `${err3string}`) } this.getMyFollowedNamesList() return ret } async myUnfollowName(nameObj) { let name = nameObj.name let items = [ name ] let namesJsonString = JSON.stringify({ "items": items }) let ret = await parentEpml.request('apiCall', { url: `/lists/followedNames?apiKey=${this.getApiKey()}`, method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: `${namesJsonString}` }) if (ret === true) { this.myFollowedNames = this.myFollowedNames.filter(item => item != name) } else { let err4string = get("appspage.schange23") parentEpml.request('showSnackBar', `${err4string}`) } this.getMyFollowedNamesList() return ret } async checkMyMenuPlugins() { const appDelay = ms => new Promise(res => setTimeout(res, ms)) if (localStorage.getItem("myMenuPlugs") === null) { await appDelay(1000) const listOfPlugins = this.menuList.filter(plugin=> plugin.url !== "puzzles") const addQapps = [...listOfPlugins, ...defaultQappsTabs] const myObj = JSON.stringify(addQapps) localStorage.setItem("myMenuPlugs", myObj) this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") } else { this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") } } resetMenu() { localStorage.removeItem("myMenuPlugs") this.firstUpdated() } val() { const theValue = this.shadowRoot.getElementById("pluginTypeInput").value if (theValue === "reject") { this.textFieldDisabled = false this.initialName = '' this.mwcIcon = '' } else if (theValue === "0") { this.textFieldDisabled = false this.initialName = '' this.mwcIcon = '' } else if (theValue === "1") { this.textFieldDisabled = false this.initialName = '' this.mwcIcon = '' } else if (theValue === 'overview-page') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Overview Page' this.mwcIcon = 'home' } else if (theValue === 'minting') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Minting Details' this.mwcIcon = 'info_outline' } else if (theValue === 'become-minter') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Become a Minter' this.mwcIcon = 'thumb_up' } else if (theValue === 'sponsorship-list') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Sponsorship List' this.mwcIcon = 'format_list_numbered' } else if (theValue === 'wallet') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Wallets' this.mwcIcon = 'account_balance_wallet' } else if (theValue === 'trade-portal') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Trade Portal' this.mwcIcon = 'format_list_bulleted' } else if (theValue === 'trade-bot-portal') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Auto Buy' this.mwcIcon = 'shop' } else if (theValue === 'reward-share') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Reward Share' this.mwcIcon = 'ios_share' } else if (theValue === 'q-chat') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Q-Chat' this.mwcIcon = 'forum' } else if (theValue === 'name-registration') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Name Registration' this.mwcIcon = 'manage_accounts' } else if (theValue === 'names-market') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Names Market' this.mwcIcon = 'store' } else if (theValue === 'websites') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Websites' this.mwcIcon = 'desktop_mac' } else if (theValue === 'qapps') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Q-Apps' this.mwcIcon = 'apps' } else if (theValue === 'group-management') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Group Management' this.mwcIcon = 'group' } else if (theValue === 'data-management') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Data Management' this.mwcIcon = 'storage' } else if (theValue === 'puzzles') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Puzzles' this.mwcIcon = 'extension' } else if (theValue === 'node-management') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Node Management' this.mwcIcon = 'cloud' } else if (theValue === 'lottery') { this.mwcIcon = '' this.initialName = '' this.textFieldDisabled = true this.initialName = 'Qortal Lottery' this.mwcIcon = 'token' } } filterSelectMenu() { const addressInfoSelect = this.addressInfo const isMinterSelect = addressInfoSelect?.error !== 124 && +addressInfoSelect?.level > 0 const isSponsorSelect = +addressInfoSelect?.level >= 5 if (!isMinterSelect) { return html` ` } else if (isMinterSelect && isSponsorSelect) { return html` ` } else { return html` ` } } openAddNewPlugin() { this.shadowRoot.getElementById("pluginTypeInput").value = 'reject' this.shadowRoot.getElementById("pluginNameInput").value = '' this.initialName = '' this.textFieldDisabled = false this.shadowRoot.querySelector('#addNewPlugin').show() } async addToMyMenuPlugins() { this.newId = '' const newUid = new ShortUniqueId({ length: 10 }) this.newId = 'plugin-' + newUid.rnd() this.pluginType = this.shadowRoot.getElementById("pluginTypeInput").value if (this.pluginType === "reject") { let myplugerr = get("tabmenu.tm25") parentEpml.request('showSnackBar', `${myplugerr}`) return false } else if (this.pluginType === "0") { this.mwcIcon = '' this.pluginName = this.shadowRoot.getElementById('pluginNameInput').value if (this.pluginName === "Q-Blog") { this.mwcIcon = 'rss_feed' } else if (this.pluginName === "Q-Mail") { this.mwcIcon = 'mail' } else { this.mwcIcon = 'apps' } var oldMenuPlugs = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") const newMenuPlugsItem = { "url": "myapp", "domain": "core", "page": `qdn/browser/index.html?name=${this.pluginName}&service=APP`, "title": this.pluginName, "icon": "vaadin:external-browser", "mwcicon": this.mwcIcon, "pluginNumber": this.newId, "menus": [], "parent": false } const validatePluginName = async () => { if (this.pluginType === "0" && this.pluginName.length == 0) { let myplugstring1 = get("walletpage.wchange50") parentEpml.request('showSnackBar', `${myplugstring1}`) return false } let myPluginName = false this.myPluginNameRes = [] await parentEpml.request('apiCall', { url: `/arbitrary/resources/search?service=APP&query=${this.pluginName}&exactmatchnames=true&limit=1` }).then(res => { this.myPluginNameRes = res }) if (this.myPluginNameRes === undefined || this.myPluginNameRes.length == 0) { myPluginName = false } else { myPluginName = true } return myPluginName } let myNameRes = await validatePluginName() if (myNameRes !== false) { oldMenuPlugs.push(newMenuPlugsItem) const copyPayload = [...oldMenuPlugs] localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs)) this.saveSettingToTemp(copyPayload) let myplugstring2 = get("walletpage.wchange52") parentEpml.request('showSnackBar', `${myplugstring2}`) this.closeAddNewPlugin() this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") this.firstUpdated() } else { let myplugstring3 = get("websitespage.schange17") parentEpml.request('showSnackBar', `${myplugstring3}`) return false } } else if (this.pluginType === "1") { this.mwcIcon = '' this.pluginName = this.shadowRoot.getElementById('pluginNameInput').value this.mwcIcon = 'web' var oldMenuPlugs = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") const newMenuPlugsItem = { "url": "myapp", "domain": "core", "page": `qdn/browser/index.html?name=${this.pluginName}&service=WEBSITE`, "title": this.pluginName, "icon": "vaadin:external-browser", "mwcicon": this.mwcIcon, "pluginNumber": this.newId, "menus": [], "parent": false } const validatePluginName = async () => { if (this.pluginType === "1" && this.pluginName.length == 0) { let myplugstring1 = get("walletpage.wchange50") parentEpml.request('showSnackBar', `${myplugstring1}`) return false } let myPluginName = false this.myPluginNameRes = [] await parentEpml.request('apiCall', { url: `/arbitrary/resources/search?service=WEBSITE&query=${this.pluginName}&exactmatchnames=true&limit=1` }).then(res => { this.myPluginNameRes = res }) if (this.myPluginNameRes === undefined || this.myPluginNameRes.length == 0 ) { myPluginName = false } else { myPluginName = true } return myPluginName } let myNameRes = await validatePluginName() if (myNameRes !== false) { oldMenuPlugs.push(newMenuPlugsItem) const copyPayload = [...oldMenuPlugs] localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs)) this.saveSettingToTemp(copyPayload) let myplugstring2 = get("walletpage.wchange52") parentEpml.request('showSnackBar', `${myplugstring2}`) this.closeAddNewPlugin() this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") this.firstUpdated() } else { let myplugstring3 = get("websitespage.schange17") parentEpml.request('showSnackBar', `${myplugstring3}`) return false } } else { this.pluginPage = '' if (this.pluginType === 'overview-page') { this.pluginPage = 'overview-page/index.html' } else if (this.pluginType === 'minting') { this.pluginPage = 'minting/index.html' } else if (this.pluginType === 'become-minter') { this.pluginPage = 'become-minter/index.html' } else if (this.pluginType === 'sponsorship-list') { this.pluginPage = 'sponsorship-list/index.html' } else if (this.pluginType === 'wallet') { this.pluginPage = 'wallet/index.html' } else if (this.pluginType === 'trade-portal') { this.pluginPage = 'trade-portal/index.html' } else if (this.pluginType === 'trade-bot-portal') { this.pluginPage = 'trade-bot/index.html' } else if (this.pluginType === 'reward-share') { this.pluginPage = 'reward-share/index.html' } else if (this.pluginType === 'q-chat') { this.pluginPage = 'messaging/q-chat/index.html' } else if (this.pluginType === 'name-registration') { this.pluginPage = 'name-registration/index.html' } else if (this.pluginType === 'names-market') { this.pluginPage = 'names-market/index.html' } else if (this.pluginType === 'websites') { this.pluginPage = 'qdn/index.html' } else if (this.pluginType === 'qapps') { this.pluginPage = 'q-app/index.html' } else if (this.pluginType === 'group-management') { this.pluginPage = 'group-management/index.html' } else if (this.pluginType === 'data-management') { this.pluginPage = 'qdn/data-management/index.html' } else if (this.pluginType === 'puzzles') { this.pluginPage = 'puzzles/index.html' } else if (this.pluginType === 'node-management') { this.pluginPage = 'node-management/index.html' } else if (this.pluginType === 'lottery') { this.pluginPage = 'qortal-lottery/index.html' } var oldMenuPlugs = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") const newMenuPlugsItem = { "url": this.pluginType, "domain": "core", "page": this.pluginPage, "title": this.initialName, "icon": "vaadin:external-browser", "mwcicon": this.mwcIcon, "pluginNumber": this.newId, "menus": [], "parent": false } oldMenuPlugs.push(newMenuPlugsItem) const copyPayload = [...oldMenuPlugs] localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs)) this.saveSettingToTemp(copyPayload) let myplugstring2 = get("walletpage.wchange52") parentEpml.request('showSnackBar', `${myplugstring2}`) this.closeAddNewPlugin() this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") this.firstUpdated() } } closeAddNewPlugin() { this.shadowRoot.querySelector('#addNewPlugin').close() this.shadowRoot.getElementById("pluginTypeInput").value = 'reject' this.shadowRoot.getElementById("pluginNameInput").value = '' this.initialName = '' this.textFieldDisabled = false } renderTitle(theUrl, theName) { if (theUrl === 'overview-page') { return html`${translate('tabmenu.tm28')}` } else if (theUrl === 'minting') { return html`${translate('tabmenu.tm1')}` } else if (theUrl === 'become-minter') { return html`${translate('tabmenu.tm2')}` } else if (theUrl === 'sponsorship-list') { return html`${translate('tabmenu.tm3')}` } else if (theUrl === 'wallet') { return html`${translate('tabmenu.tm4')}` } else if (theUrl === 'trade-portal') { return html`${translate('tabmenu.tm5')}` } else if (theUrl === 'trade-bot-portal') { return html`${translate('tabmenu.tm6')}` } else if (theUrl === 'reward-share') { return html`${translate('tabmenu.tm7')}` } else if (theUrl === 'q-chat') { return html`${translate('tabmenu.tm8')}` } else if (theUrl === 'name-registration') { return html`${translate('tabmenu.tm9')}` } else if (theUrl === 'names-market') { return html`${translate('tabmenu.tm10')}` } else if (theUrl === 'websites') { return html`${translate('tabmenu.tm11')}` } else if (theUrl === 'qapps') { return html`${translate('tabmenu.tm12')}` } else if (theUrl === 'group-management') { return html`${translate('tabmenu.tm13')}` } else if (theUrl === 'data-management') { return html`${translate('tabmenu.tm14')}` } else if (theUrl === 'puzzles') { return html`${translate('tabmenu.tm15')}` } else if (theUrl === 'node-management') { return html`${translate('tabmenu.tm16')}` } else if (theUrl === 'lottery') { return html`${translate('tabmenu.tm42')}` } else { return html`${theName}` } } renderRemoveIcon(appurl, appicon, appname, appid, appplugin) { return html` ` } openRemoveApp(pluginNameTD, pluginNumberTD, pluginUrlTD) { this.pluginNameToDelete = '' this.pluginNameToDelete = pluginNameTD this.pluginNumberToDelete = '' this.pluginNumberToDelete = pluginNumberTD this.removeTitle = '' if (pluginUrlTD === 'overview-page') { this.removeTitle = html`${translate('tabmenu.tm28')}` } else if (pluginUrlTD === 'minting') { this.removeTitle = html`${translate('tabmenu.tm1')}` } else if (pluginUrlTD === 'become-minter') { this.removeTitle = html`${translate('tabmenu.tm2')}` } else if (pluginUrlTD === 'sponsorship-list') { this.removeTitle = html`${translate('tabmenu.tm3')}` } else if (pluginUrlTD === 'wallet') { this.removeTitle = html`${translate('tabmenu.tm4')}` } else if (pluginUrlTD === 'trade-portal') { this.removeTitle = html`${translate('tabmenu.tm5')}` } else if (pluginUrlTD === 'trade-bot-portal') { this.removeTitle = html`${translate('tabmenu.tm6')}` } else if (pluginUrlTD === 'reward-share') { this.removeTitle = html`${translate('tabmenu.tm7')}` } else if (pluginUrlTD === 'q-chat') { this.removeTitle = html`${translate('tabmenu.tm8')}` } else if (pluginUrlTD === 'name-registration') { this.removeTitle = html`${translate('tabmenu.tm9')}` } else if (pluginUrlTD === 'names-market') { this.removeTitle = html`${translate('tabmenu.tm10')}` } else if (pluginUrlTD === 'websites') { this.removeTitle = html`${translate('tabmenu.tm11')}` } else if (pluginUrlTD === 'qapps') { this.removeTitle = html`${translate('tabmenu.tm12')}` } else if (pluginUrlTD === 'group-management') { this.removeTitle = html`${translate('tabmenu.tm13')}` } else if (pluginUrlTD === 'data-management') { this.removeTitle = html`${translate('tabmenu.tm14')}` } else if (pluginUrlTD === 'puzzles') { this.removeTitle = html`${translate('tabmenu.tm15')}` } else if (pluginUrlTD === 'node-management') { this.removeTitle = html`${translate('tabmenu.tm16')}` } else if (pluginUrlTD === 'lottery') { this.removeTitle = html`${translate('tabmenu.tm42')}` } else { this.removeTitle = html`${pluginNameTD}` } this.shadowRoot.querySelector('#removePlugin').show() } removeAppFromArray() { const pluginToRemove = this.pluginNumberToDelete this.newMenuFilter = [] this.newMenuFilter = this.myMenuList.filter((item) => item.pluginNumber !== pluginToRemove) const copyPayload = [...this.newMenuFilter] const myNewObj = JSON.stringify(this.newMenuFilter) localStorage.removeItem("myMenuPlugs") localStorage.setItem("myMenuPlugs", myNewObj) this.saveSettingToTemp(copyPayload) this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]") this.firstUpdated() this.closeRemoveApp() } saveSettingToTemp(data){ const tempSettingsData= JSON.parse(localStorage.getItem('temp-settings-data') || "{}") const newTemp = { ...tempSettingsData, myMenuPlugs: { data: data, timestamp: Date.now() } } localStorage.setItem('temp-settings-data', JSON.stringify(newTemp)); this.dispatchEvent( new CustomEvent('temp-settings-data-event', { bubbles: true, composed: true }), ); } closeRemoveApp() { this.shadowRoot.querySelector('#removePlugin').close() this.pluginNameToDelete = '' this.pluginNumberToDelete = '' } async extractComponents(url) { if (!url.startsWith("qortal://")) { return null } url = url.replace(/^(qortal\:\/\/)/, "") if (url.includes("/")) { let parts = url.split("/") const service = parts[0].toUpperCase() parts.shift() const name = parts[0] parts.shift() let identifier if (parts.length > 0) { identifier = parts[0] // Do not shift yet // Check if a resource exists with this service, name and identifier combination const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}` const res = await fetch(url); const data = await res.json(); if (data.totalChunkCount > 0) { // Identifier exists, so don't include it in the path parts.shift() } else { identifier = null } } const path = parts.join("/") const components = {} components["service"] = service components["name"] = name components["identifier"] = identifier components["path"] = path return components } return null } async getQuery(value) { let newQuery = value if (newQuery.endsWith('/')) { newQuery = newQuery.slice(0, -1) } const res = await this.extractComponents(newQuery) if (!res) return const { service, name, identifier, path } = res let query = `?service=${service}` if (name) { query = query + `&name=${name}` } if (identifier) { query = query + `&identifier=${identifier}` } if (path) { query = query + `&path=${path}` } if (service === "APP") { this.changePage({ "url": "myapp", "domain": "core", "page": `qdn/browser/index.html${query}`, "title": name || "Q-App", "icon": "vaadin:external-browser", "mwcicon": "open_in_browser", "menus": [], "parent": false }) } else if (service === "WEBSITE") { this.changePage({ "url": "myapp", "domain": "core", "page": `qdn/browser/index.html${query}`, "title": name || "Website", "icon": "vaadin:desktop", "mwcicon": "desktop_mac", "menus": [], "parent": false }) } } async handlePasteLink(e) { try { const value = this.shadowRoot.getElementById('linkInput').value this.getQuery(value) } catch (error) { } } async _handleKeyDown(e) { if (e.key === 'Enter') { try { const value = this.shadowRoot.getElementById('linkInput').value this.getQuery(value) } catch (error) { } } } getApiKey() { const apiNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] let apiKey = apiNode.apiKey return apiKey } isEmptyArray(arr) { if (!arr) { return true } return arr.length === 0 } stateChanged(state) { this.menuList = state.app.registeredUrls this.addressInfo = state.app.accountInfo.addressInfo } } customElements.define('nav-bar', NavBar) class AppAvatar extends LitElement { static get properties() { return { hasAvatar: { type: Boolean }, isImageLoaded: {type: Boolean}, appicon: {type: String}, appname: {type: String} } } constructor() { super() this.hasAvatar = false this.isImageLoaded = false this.imageFetches = 0 } static get styles() { return css` :host { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); cursor: pointer; } .menuIcon { color: var(--app-icon); --mdc-icon-size: 64px; cursor: pointer; } ` } createImage(imageUrl) { const imageHTMLRes = new Image(); imageHTMLRes.src = imageUrl; imageHTMLRes.style= "border-radius:10px; font-size:14px; object-fit: fill;height:60px;width:60px"; imageHTMLRes.onload = () => { this.isImageLoaded = true; } imageHTMLRes.onerror = () => { if (this.imageFetches < 1) { setTimeout(() => { this.imageFetches = this.imageFetches + 1; imageHTMLRes.src = imageUrl; }, 5000); } else { this.isImageLoaded = false } }; return imageHTMLRes; } render(){ let avatarImg = "" if (this.appname) { const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.appname}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; avatarImg = this.createImage(avatarUrl) } return html` ${this.isImageLoaded ? html` ${avatarImg} ` : html` ${this.appicon} `} ` } } customElements.define('app-avatar', AppAvatar) class TabAvatar extends LitElement { static get properties() { return { hasAvatar: { type: Boolean }, isImageLoaded: {type: Boolean}, appicon: {type: String}, appname: {type: String} } } constructor() { super() this.hasAvatar = false this.isImageLoaded = false this.imageFetches = 0 } createImage(imageUrl) { const imageHTMLRes = new Image(); imageHTMLRes.src = imageUrl; imageHTMLRes.style= "border-radius:4px; font-size:14px; object-fit: fill;height:24px;width:24px"; imageHTMLRes.onload = () => { this.isImageLoaded = true; } imageHTMLRes.onerror = () => { if (this.imageFetches < 1) { setTimeout(() => { this.imageFetches = this.imageFetches + 1; imageHTMLRes.src = imageUrl; }, 5000); } else { this.isImageLoaded = false } }; return imageHTMLRes; } render(){ let avatarImg = "" if (this.appname) { const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.appname}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; avatarImg = this.createImage(avatarUrl) } return html` ${this.isImageLoaded ? html` ${avatarImg} ` : html` ${this.appicon} `} ` } } customElements.define('tab-avatar', TabAvatar)