From fff560b2f7a458cc6afd02f371b5640a14cf051f Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sat, 29 Jan 2022 05:22:51 -0800 Subject: [PATCH] Pagination --- .../data-management/data-management.src.js | 448 +++++++++++------- 1 file changed, 270 insertions(+), 178 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/qdn/data-management/data-management.src.js b/qortal-ui-plugins/plugins/core/qdn/data-management/data-management.src.js index f76c2882..1f2618be 100644 --- a/qortal-ui-plugins/plugins/core/qdn/data-management/data-management.src.js +++ b/qortal-ui-plugins/plugins/core/qdn/data-management/data-management.src.js @@ -11,25 +11,58 @@ import '@vaadin/vaadin-grid/theme/material/all-imports.js' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class DataManagement extends LitElement { - static get properties() { - return { + static get properties() { + return { loading: { type: Boolean }, - resources: { type: Array }, blockedNames: { type: Array }, - followedNames: { type: Array }, - } - } + followedNames: { type: Array }, + datres: { type: Array } + } + } - static get observers() { - return ['_kmxKeyUp(amount)'] - } - - static get styles() { + static get styles() { return css` - * { - --mdc-theme-primary: rgb(3, 169, 244); + * { + --mdc-theme-primary: rgb(3, 169, 244); --paper-input-container-focus-color: var(--mdc-theme-primary); } + + #pages { + display: flex; + flex-wrap: wrap; + padding: 10px 5px 5px 5px; + margin: 0px 20px 20px 20px; + } + + #pages > button { + user-select: none; + padding: 5px; + margin: 0 5px; + border-radius: 10%; + border: 0; + background: transparent; + font: inherit; + outline: none; + cursor: pointer; + } + + #pages > button:not([disabled]):hover, + #pages > button:focus { + color: #ccc; + background-color: #eee; + } + + #pages > button[selected] { + font-weight: bold; + color: white; + background-color: #ccc; + } + + #pages > button[disabled] { + opacity: 0.5; + cursor: default; + } + #websites-list-page { background: #fff; padding: 12px 24px; @@ -38,7 +71,6 @@ class DataManagement extends LitElement { .divCard { border: 1px solid #eee; padding: 1em; - /** box-shadow: 0 1px 1px 0 rgba(0,0,0,0.14), 0 2px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20); **/ box-shadow: 0 .3px 1px 0 rgba(0,0,0,0.14), 0 1px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20); margin-bottom: 2em; } @@ -61,14 +93,17 @@ class DataManagement extends LitElement { display: hidden !important; visibility: none !important; } + .details { display: flex; font-size: 18px; } + span { font-size: 18px; word-break: break-all; } + select { padding: 13px 20px; width: 100%; @@ -76,15 +111,18 @@ class DataManagement extends LitElement { color: #555; font-weight: 400; } + .title { font-weight:600; font-size:12px; line-height: 32px; opacity: 0.66; } + .itemList { padding:0; } + .default-identifier { font-size: 14px; font-style: italic; @@ -92,85 +130,123 @@ class DataManagement extends LitElement { ` } - render() { + constructor() { + super() + this.selectedAddress = {} + this.blockedNames = [] + this.followedNames = [] + this.datres = [] + this.isLoading = false + } + + render() { return html`

Data Management

-

Data hosted by this node

- - - { - render(html`${this.renderName(data.item)}`, root) - }}> - { - render(html`${this.renderService(data.item)}`, root) - }}> - { - render(html`${this.renderIdentifier(data.item)}`, root) - }}> - { - render(html`${this.renderDeleteButton(data.item)}`, root); - }}> - { - render(html`${this.renderBlockUnblockButton(data.item)}`, root); - }}> + + + + { + render(html`${this.renderIdentifier(data.item)}`, root) + }}> + { + render(html`${this.renderDeleteButton(data.item)}`, root); + }}> + { + render(html`${this.renderBlockUnblockButton(data.item)}`, root); + }}> - ${this.renderDefaultText()} +
+ ${this.renderDefaultText()}
` } - goBack() { - window.history.back(); + firstUpdated() { + + window.addEventListener('contextmenu', (event) => { + event.preventDefault() + this._textMenu(event) + }) + + window.addEventListener('click', () => { + parentEpml.request('closeCopyTextMenu', null) + }) + + window.onkeyup = (e) => { + if (e.keyCode === 27) { + parentEpml.request('closeCopyTextMenu', null) + } + } + + let configLoaded = false + + parentEpml.ready().then(() => { + parentEpml.subscribe('selected_address', async selectedAddress => { + this.selectedAddress = {} + selectedAddress = JSON.parse(selectedAddress) + if (!selectedAddress || Object.entries(selectedAddress).length === 0) return + this.selectedAddress = selectedAddress + }) + parentEpml.subscribe('config', c => { + this.config = JSON.parse(c) + if (!configLoaded) { + setTimeout(this.getFollowedNames, 1) + setTimeout(this.getBlockedNames, 1) + setInterval(this.showManagement(), 30 * 1000) + setInterval(this.getFollowedNames, 30 * 1000) + setInterval(this.getBlockedNames, 30 * 1000) + configLoaded = true + } + }) + parentEpml.subscribe('copy_menu_switch', async value => { + + if (value === 'false' && window.getSelection().toString().length !== 0) { + + this.clearSelection() + } + }) + }) + parentEpml.imReady() } - renderDefaultText() { - if (this.resources == null || !Array.isArray(this.resources)) { + renderDefaultText() { + if (this.datres == null || !Array.isArray(this.datres)) { return html`
Couldn't fetch hosted data list from node` } - if (this.isEmptyArray(this.resources)) { - return html`
This node isn't hosting any data`; - } - return ''; - } - - renderName(resource) { - let name = resource.name - return html`${name}` + if (this.isEmptyArray(this.datres)) { + return html`
This node isn't hosting any data`; + } + return ''; } - renderService(resource) { - let service = resource.service - return html`${service}` - } - - renderIdentifier(resource) { + renderIdentifier(resource) { return resource.identifier == null ? html`default` : html`${resource.identifier}` } - renderDeleteButton(resource) { + renderDeleteButton(resource) { let name = resource.name // Only show the block/unblock button if we have permission to modify the list on this node - // We can use the blocked names list for this, as it won't be a valid array if we have no access + // We can use the blocked names list for this, as it won't be a valid array if we have no access if (this.blockedNames == null || !Array.isArray(this.blockedNames)) { return html`` } - // We need to check if we are following this name, as if we are, there is no point in deleting anything - // as it will be re-fetched immediately. In these cases we should show an UNFOLLOW button. - if (this.followedNames.indexOf(name) != -1) { + // We need to check if we are following this name, as if we are, there is no point in deleting anything + // as it will be re-fetched immediately. In these cases we should show an UNFOLLOW button. + if (this.followedNames.indexOf(name) != -1) { // render unfollow button return html` this.unfollowName(resource)}>remove_from_queue Unfollow` } - // render delete button - return html` this.deleteResource(resource)} onclick="this.blur();">delete Delete` + // render delete button + return html` this.deleteResource(resource)} onclick="this.blur();">delete Delete` } renderBlockUnblockButton(resource) { @@ -191,12 +267,12 @@ class DataManagement extends LitElement { } } - async blockName(resource) { + async blockName(resource) { let name = resource.name let items = [ name ] - let namesJsonString = JSON.stringify({"items": items}) + let namesJsonString = JSON.stringify({ "items": items }) let ret = await parentEpml.request('apiCall', { url: `/lists/blockedNames?apiKey=${this.getApiKey()}`, @@ -211,7 +287,7 @@ class DataManagement extends LitElement { // Successfully blocked - add to local list // Remove it first by filtering the list - doing it this way ensures the UI updates // immediately, as apposed to only adding if it doesn't already exist - this.blockedNames = this.blockedNames.filter(item => item != name); + this.blockedNames = this.blockedNames.filter(item => item != name); this.blockedNames.push(name) } else { @@ -221,12 +297,12 @@ class DataManagement extends LitElement { return ret } - async unfollowName(websiteObj) { - let name = websiteObj.name + async unfollowName(resource) { + let name = resource.name let items = [ name ] - let namesJsonString = JSON.stringify({"items": items}) + let namesJsonString = JSON.stringify({ "items": items }) let ret = await parentEpml.request('apiCall', { url: `/lists/followedNames?apiKey=${this.getApiKey()}`, @@ -239,7 +315,7 @@ class DataManagement extends LitElement { if (ret === true) { // Successfully unfollowed - remove from local list - this.followedNames = this.followedNames.filter(item => item != name); + this.followedNames = this.followedNames.filter(item => item != name); } else { parentEpml.request('showSnackBar', 'Error occurred when trying to unfollow this registered name. Please try again') @@ -253,7 +329,7 @@ class DataManagement extends LitElement { let items = [ name ] - let namesJsonString = JSON.stringify({"items": items}) + let namesJsonString = JSON.stringify({ "items": items }) let ret = await parentEpml.request('apiCall', { url: `/lists/blockedNames?apiKey=${this.getApiKey()}`, @@ -266,7 +342,7 @@ class DataManagement extends LitElement { if (ret === true) { // Successfully unblocked - remove from local list - this.blockedNames = this.blockedNames.filter(item => item != name); + this.blockedNames = this.blockedNames.filter(item => item != name); } else { parentEpml.request('showSnackBar', 'Error occurred when trying to unblock this registered name. Please try again') @@ -275,8 +351,8 @@ class DataManagement extends LitElement { return ret } - async deleteResource(resource) { - let identifier = resource.identifier == null ? "default" : resource.identifier; + async deleteResource(resource) { + let identifier = resource.identifier == null ? "default" : resource.identifier; let ret = await parentEpml.request('apiCall', { url: `/arbitrary/resource/${resource.service}/${resource.name}/${identifier}?apiKey=${this.getApiKey()}`, @@ -285,7 +361,7 @@ class DataManagement extends LitElement { if (ret === true) { // Successfully deleted - so refresh the page - this.getArbitraryResources(); + this.getArbitraryResources(); } else { parentEpml.request('showSnackBar', 'Error occurred when trying to delete this resource. Please try again') @@ -295,124 +371,140 @@ class DataManagement extends LitElement { } - // Helper Functions (Re-Used in Most part of the UI ) + textColor(color) { + return color == 'light' ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.87)' + } - textColor(color) { - return color == 'light' ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.87)' - } + _textMenu(event) { + const getSelectedText = () => { + var text = '' + if (typeof window.getSelection != 'undefined') { + text = window.getSelection().toString() + } else if (typeof this.shadowRoot.selection != 'undefined' && this.shadowRoot.selection.type == 'Text') { + text = this.shadowRoot.selection.createRange().text + } + return text + } - _textMenu(event) { - const getSelectedText = () => { - var text = '' - if (typeof window.getSelection != 'undefined') { - text = window.getSelection().toString() - } else if (typeof this.shadowRoot.selection != 'undefined' && this.shadowRoot.selection.type == 'Text') { - text = this.shadowRoot.selection.createRange().text - } - return text - } + const checkSelectedTextAndShowMenu = () => { + let selectedText = getSelectedText() + if (selectedText && typeof selectedText === 'string') { + let _eve = { pageX: event.pageX, pageY: event.pageY, clientX: event.clientX, clientY: event.clientY } - const checkSelectedTextAndShowMenu = () => { - let selectedText = getSelectedText() - if (selectedText && typeof selectedText === 'string') { - let _eve = { pageX: event.pageX, pageY: event.pageY, clientX: event.clientX, clientY: event.clientY } + let textMenuObject = { selectedText: selectedText, eventObject: _eve, isFrame: true } - let textMenuObject = { selectedText: selectedText, eventObject: _eve, isFrame: true } + parentEpml.request('openCopyTextMenu', textMenuObject) + } + } - parentEpml.request('openCopyTextMenu', textMenuObject) - } - } + checkSelectedTextAndShowMenu() + } - checkSelectedTextAndShowMenu() - } + async getResourcesGrid() { + this.resourcesGrid = this.shadowRoot.querySelector(`#resourcesGrid`) + this.pagesControl = this.shadowRoot.querySelector('#pages') + this.pages = undefined + } - constructor() { - super() - this.selectedAddress = {} - this.resources = [] - this.blockedNames = [] - this.followedNames = [] - this.isLoading = false - } + async fetchManagementDetails() { + const myDat = await parentEpml.request('apiCall', { + url: `/arbitrary/hosted/resources?apiKey=${this.getApiKey()}` + }) + this.datres = myDat; + } - getArbitraryResources = async () => { - // this.resources = [] + async updateItemsFromPage(page) { + if (page === undefined) { + return + } - let resources = await parentEpml.request('apiCall', { - url: `/arbitrary/hosted/resources?apiKey=${this.getApiKey()}` - }) - - this.resources = resources - } - - getBlockedNames = async () => { - // this.blockedNames = [] - - let blockedNames = await parentEpml.request('apiCall', { - url: `/lists/blockedNames?apiKey=${this.getApiKey()}` - }) - - this.blockedNames = blockedNames - } - - getFollowedNames = async () => { - // this.followedNames = [] - - let followedNames = await parentEpml.request('apiCall', { - url: `/lists/followedNames?apiKey=${this.getApiKey()}` - }) - - this.followedNames = followedNames - } - - firstUpdated() { - - window.addEventListener('contextmenu', (event) => { - event.preventDefault() - this._textMenu(event) - }) - - window.addEventListener('click', () => { - parentEpml.request('closeCopyTextMenu', null) - }) - - window.onkeyup = (e) => { - if (e.keyCode === 27) { - parentEpml.request('closeCopyTextMenu', null) - } - } - - let configLoaded = false - parentEpml.ready().then(() => { - parentEpml.subscribe('selected_address', async selectedAddress => { - this.selectedAddress = {} - selectedAddress = JSON.parse(selectedAddress) - if (!selectedAddress || Object.entries(selectedAddress).length === 0) return - this.selectedAddress = selectedAddress + if (!this.pages) { + this.pages = Array.apply(null, { length: Math.ceil(this.datres.length / this.resourcesGrid.pageSize) }).map((item, index) => { + return index + 1 }) - parentEpml.subscribe('config', c => { - this.config = JSON.parse(c) - if (!configLoaded) { - setTimeout(this.getArbitraryResources, 1) - setTimeout(this.getFollowedNames, 1) - setTimeout(this.getBlockedNames, 1) - setInterval(this.getArbitraryResources, 30*1000) - setInterval(this.getFollowedNames, 30*1000) - setInterval(this.getBlockedNames, 30*1000) - configLoaded = true + + const prevBtn = document.createElement('button') + prevBtn.textContent = '<' + prevBtn.addEventListener('click', () => { + const selectedPage = parseInt(this.pagesControl.querySelector('[selected]').textContent) + this.updateItemsFromPage(selectedPage - 1) + }) + this.pagesControl.appendChild(prevBtn) + + this.pages.forEach((pageNumber) => { + const pageBtn = document.createElement('button') + pageBtn.textContent = pageNumber + pageBtn.addEventListener('click', (e) => { + this.updateItemsFromPage(parseInt(e.target.textContent)) + }) + if (pageNumber === page) { + pageBtn.setAttribute('selected', true) } + this.pagesControl.appendChild(pageBtn) }) - parentEpml.subscribe('copy_menu_switch', async value => { - if (value === 'false' && window.getSelection().toString().length !== 0) { + const nextBtn = window.document.createElement('button') + nextBtn.textContent = '>' + nextBtn.addEventListener('click', () => { + const selectedPage = parseInt(this.pagesControl.querySelector('[selected]').textContent) + this.updateItemsFromPage(selectedPage + 1) + }) + this.pagesControl.appendChild(nextBtn) + } - this.clearSelection() + const buttons = Array.from(this.pagesControl.children) + buttons.forEach((btn, index) => { + if (parseInt(btn.textContent) === page) { + btn.setAttribute('selected', true) + } else { + btn.removeAttribute('selected') + } + if (index === 0) { + if (page === 1) { + btn.setAttribute('disabled', '') + } else { + btn.removeAttribute('disabled') } - }) + } + if (index === buttons.length - 1) { + if (page === this.pages.length) { + btn.setAttribute('disabled', '') + } else { + btn.removeAttribute('disabled') + } + } + }) + let start = (page - 1) * this.resourcesGrid.pageSize + let end = page * this.resourcesGrid.pageSize + + this.resourcesGrid.items = this.datres.slice(start, end) + } + + async showManagement() { + await this.fetchManagementDetails() + await this.getResourcesGrid() + await this.updateItemsFromPage(1, true) + } + + + + getBlockedNames = async () => { + + let blockedNames = await parentEpml.request('apiCall', { + url: `/lists/blockedNames?apiKey=${this.getApiKey()}` }) - parentEpml.imReady() - } + this.blockedNames = blockedNames + } + + getFollowedNames = async () => { + + let followedNames = await parentEpml.request('apiCall', { + url: `/lists/followedNames?apiKey=${this.getApiKey()}` + }) + + this.followedNames = followedNames + } getApiKey() { const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; @@ -420,12 +512,12 @@ class DataManagement extends LitElement { return apiKey; } - clearSelection() { - window.getSelection().removeAllRanges() - window.parent.getSelection().removeAllRanges() - } + clearSelection() { + window.getSelection().removeAllRanges() + window.parent.getSelection().removeAllRanges() + } - isEmptyArray(arr) { + isEmptyArray(arr) { if (!arr) { return true } return arr.length === 0 }