From ed5253d6d0479878b2df14e74ff02c116d1fd086 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 25 Oct 2023 01:17:14 +0300 Subject: [PATCH] started profile --- blog-test.json | 12 +- core/language/us.json | 7 + core/src/components/WebWorkerFile.js | 5 + core/src/components/app-view.js | 2 + core/src/components/computePowWorkerFile.js | 92 +++ core/src/components/friends-view/avatar.js | 310 ++++++++ .../friends-view/profile-modal-update.js | 301 ++++++++ core/src/components/friends-view/profile.js | 672 ++++++++++++++++++ .../friends-view/save-settings-qdn.js | 3 +- core/src/components/show-plugin.js | 1 + 10 files changed, 1394 insertions(+), 11 deletions(-) create mode 100644 core/src/components/WebWorkerFile.js create mode 100644 core/src/components/computePowWorkerFile.js create mode 100644 core/src/components/friends-view/avatar.js create mode 100644 core/src/components/friends-view/profile-modal-update.js create mode 100644 core/src/components/friends-view/profile.js diff --git a/blog-test.json b/blog-test.json index 35323b11..04dd800a 100644 --- a/blog-test.json +++ b/blog-test.json @@ -7,24 +7,16 @@ "version": 1, "updated": 1696646223261, "title": "Q-Blog Post creations", - "description": "blablabla", + "description": "Get your friends Q-Blog posts on your feed", "search": { "query": "-post-", "identifier": "q-blog-", "service": "BLOG_POST", "exactmatchnames": true }, - "click": "qortal://APP/Q-Blog/$${resource.name}$$/$${customParams.blogId}$$/$${customParams.shortIdentifier}$$", + "click": "qortal://APP/Q-Blog/$${resource.name}$$/blog/$${resource.identifier}$$", "display": { "title": "$${rawdata.title}$$" - }, - "customParams": { - "blogId": "**methods.getBlogId(resource)**", - "shortIdentifier": "**methods.getShortId(resource)**" - }, - "methods": { - "getShortId": "return resource.identifier.split('-post-')[1];", - "getBlogId": "const arr = resource.identifier.split('-post-'); const id = arr[0]; return id.startsWith('q-blog-') ? id.substring(7) : id;" } } ] diff --git a/core/language/us.json b/core/language/us.json index 8676082d..f9a003ec 100644 --- a/core/language/us.json +++ b/core/language/us.json @@ -1212,5 +1212,12 @@ "saving2": "Nothing to save", "saving3": "Save unsaved changes", "saving4": "Undo changes" + }, + "profile": { + "profile1": "You do not have a name", + "profile2": "Go to name registration", + "profile3": "Update profile", + "profile4": "Tagline", + "profile5": "Bio" } } \ No newline at end of file diff --git a/core/src/components/WebWorkerFile.js b/core/src/components/WebWorkerFile.js new file mode 100644 index 00000000..3b8c2b87 --- /dev/null +++ b/core/src/components/WebWorkerFile.js @@ -0,0 +1,5 @@ +import WebWorker from 'web-worker:./computePowWorkerFile.js'; + +// You can add any initialization or configuration for the Web Worker here + +export default WebWorker; \ No newline at end of file diff --git a/core/src/components/app-view.js b/core/src/components/app-view.js index 1bf6a5fa..bedaeda7 100644 --- a/core/src/components/app-view.js +++ b/core/src/components/app-view.js @@ -46,6 +46,7 @@ import './notification-view/notification-bell-general.js' import './friends-view/friends-side-panel-parent.js' import './friends-view/save-settings-qdn.js' import './friends-view/core-sync-status.js' +import './friends-view/profile.js' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class AppView extends connect(store)(LitElement) { @@ -584,6 +585,7 @@ class AppView extends connect(store)(LitElement) {
+ diff --git a/core/src/components/computePowWorkerFile.js b/core/src/components/computePowWorkerFile.js new file mode 100644 index 00000000..d9f5f662 --- /dev/null +++ b/core/src/components/computePowWorkerFile.js @@ -0,0 +1,92 @@ +import { Sha256 } from 'asmcrypto.js' + + + +function sbrk(size, heap){ + let brk = 512 * 1024 // stack top + let old = brk + brk += size + + if (brk > heap.length) + throw new Error('heap exhausted') + + return old +} + + + + +self.addEventListener('message', async e => { + const response = await computePow(e.data.convertedBytes, e.data.path) + postMessage(response) + +}) + + +const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 }) +const heap = new Uint8Array(memory.buffer) + + + +const computePow = async (convertedBytes, path) => { + + + let response = null + + await new Promise((resolve, reject)=> { + + const _convertedBytesArray = Object.keys(convertedBytes).map( + function (key) { + return convertedBytes[key] + } +) +const convertedBytesArray = new Uint8Array(_convertedBytesArray) +const convertedBytesHash = new Sha256() + .process(convertedBytesArray) + .finish().result +const hashPtr = sbrk(32, heap) +const hashAry = new Uint8Array( + memory.buffer, + hashPtr, + 32 +) + +hashAry.set(convertedBytesHash) +const difficulty = 14 +const workBufferLength = 8 * 1024 * 1024 +const workBufferPtr = sbrk( + workBufferLength, + heap +) + + const importObject = { + env: { + memory: memory + }, + }; + + function loadWebAssembly(filename, imports) { + return fetch(filename) + .then(response => response.arrayBuffer()) + .then(buffer => WebAssembly.compile(buffer)) + .then(module => { + return new WebAssembly.Instance(module, importObject); + }); +} + + +loadWebAssembly(path) + .then(wasmModule => { + response = { + nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty), + + } + resolve() + + }); + + + }) + + return response +} \ No newline at end of file diff --git a/core/src/components/friends-view/avatar.js b/core/src/components/friends-view/avatar.js new file mode 100644 index 00000000..12f7090b --- /dev/null +++ b/core/src/components/friends-view/avatar.js @@ -0,0 +1,310 @@ +import { LitElement, html, css } from 'lit'; +import { get, translate } from 'lit-translate'; +import axios from 'axios'; +import '@material/mwc-menu'; +import '@material/mwc-list/mwc-list-item.js'; +import { RequestQueueWithPromise } from '../../../../plugins/plugins/utils/queue'; +import '../../../../plugins/plugins/core/components/TimeAgo'; +import { connect } from 'pwa-helpers'; +import { store } from '../../store'; +import { setNewTab } from '../../redux/app/app-actions'; +import ShortUniqueId from 'short-unique-id'; + +const requestQueue = new RequestQueueWithPromise(3); +const requestQueueRawData = new RequestQueueWithPromise(3); +const requestQueueStatus = new RequestQueueWithPromise(3); + +export class AvatarComponent extends connect(store)(LitElement) { + static get properties() { + return { + resource: { type: Object }, + isReady: { type: Boolean }, + status: { type: Object }, + name: { type: String }, + }; + } + + static get styles() { + return css` + * { + --mdc-theme-text-primary-on-background: var(--black); + box-sizing: border-box; + } + :host { + width: 100%; + box-sizing: border-box; + } + img { + width: 100%; + max-height: 30vh; + border-radius: 5px; + cursor: pointer; + position: relative; + } + .smallLoading, + .smallLoading:after { + border-radius: 50%; + width: 2px; + height: 2px; + } + + .defaultSize { + width: 100%; + height: 160px; + } + .parent-feed-item { + position: relative; + display: flex; + background-color: var(--chat-bubble-bg); + flex-grow: 0; + flex-direction: column; + align-items: flex-start; + justify-content: center; + border-radius: 5px; + padding: 12px 15px 4px 15px; + min-width: 150px; + width: 100%; + box-sizing: border-box; + cursor: pointer; + font-size: 16px; + } + .avatar { + width: 36px; + height: 36px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + } + .avatarApp { + width: 30px; + height: 30px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + } + .feed-item-name { + user-select: none; + color: #03a9f4; + margin-bottom: 5px; + } + + .app-name { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + mwc-menu { + position: absolute; + } + `; + } + + constructor() { + super(); + this.resource = { + identifier: '', + name: '', + service: '', + }; + this.status = { + status: '', + }; + this.isReady = false; + this.nodeUrl = this.getNodeUrl(); + this.myNode = this.getMyNode(); + this.isFetching = false; + this.uid = new ShortUniqueId(); + } + getNodeUrl() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + return nodeUrl; + } + getMyNode() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + return myNode; + } + + getApiKey() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + let apiKey = myNode.apiKey; + return apiKey; + } + + async fetchResource() { + try { + if (this.isFetching) return; + this.isFetching = true; + await axios.get( + `${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}` + ); + this.isFetching = false; + } catch (error) { + this.isFetching = false; + } + } + + async fetchVideoUrl() { + this.fetchResource(); + } + + async getRawData() { + const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`; + return await requestQueueRawData.enqueue(() => { + return axios.get(url); + }); + // const response2 = await fetch(url, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json' + // } + // }) + + // const responseData2 = await response2.json() + // return responseData2 + } + + updateDisplayWithPlaceholders(display, resource, rawdata) { + const pattern = /\$\$\{([a-zA-Z0-9_\.]+)\}\$\$/g; + + for (const key in display) { + const value = display[key]; + + display[key] = value.replace(pattern, (match, p1) => { + if (p1.startsWith('rawdata.')) { + const dataKey = p1.split('.')[1]; + if (rawdata[dataKey] === undefined) { + console.error('rawdata key not found:', dataKey); + } + return rawdata[dataKey] || match; + } else if (p1.startsWith('resource.')) { + const resourceKey = p1.split('.')[1]; + if (resource[resourceKey] === undefined) { + console.error('resource key not found:', resourceKey); + } + return resource[resourceKey] || match; + } + return match; + }); + } + } + + async fetchStatus() { + let isCalling = false; + let percentLoaded = 0; + let timer = 24; + const response = await requestQueueStatus.enqueue(() => { + return axios.get( + `${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}` + ); + }); + if (response && response.data && response.data.status === 'READY') { + this.status = response.data; + + return; + } + const intervalId = setInterval(async () => { + if (isCalling) return; + isCalling = true; + + const data = await requestQueue.enqueue(() => { + return axios.get( + `${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}` + ); + }); + const res = data.data; + + isCalling = false; + if (res.localChunkCount) { + if (res.percentLoaded) { + if ( + res.percentLoaded === percentLoaded && + res.percentLoaded !== 100 + ) { + timer = timer - 5; + } else { + timer = 24; + } + if (timer < 0) { + clearInterval(intervalId); + } + percentLoaded = res.percentLoaded; + } + + this.status = res; + if (this.status.status === 'DOWNLOADED') { + this.fetchResource(); + } + } + + // check if progress is 100% and clear interval if true + if (res.status === 'READY') { + clearInterval(intervalId); + this.status = res; + this.isReady = true; + } + }, 5000); // 1 second interval + } + + async _fetchImage() { + try { + this.fetchVideoUrl(); + this.fetchStatus(); + } catch (error) { + /* empty */ + } + } + + firstUpdated() { + this._fetchImage(); + } + + render() { + console.log('hello', this.name, this.resource, this.status); + return html` +
+ ${this.status.status !== 'READY' + ? html` + account_circle + ` + : ''} + ${this.status.status === 'READY' + ? html` +
+ +
+ ` + : ''} +
+ `; + } +} + +customElements.define('avatar-component', AvatarComponent); diff --git a/core/src/components/friends-view/profile-modal-update.js b/core/src/components/friends-view/profile-modal-update.js new file mode 100644 index 00000000..e7ad8777 --- /dev/null +++ b/core/src/components/friends-view/profile-modal-update.js @@ -0,0 +1,301 @@ +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { + use, + get, + translate, + translateUnsafeHTML, + registerTranslateConfig, +} from 'lit-translate'; +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@material/mwc-checkbox'; +import { connect } from 'pwa-helpers'; +import { store } from '../../store'; +import '@polymer/paper-spinner/paper-spinner-lite.js'; + +class ProfileModalUpdate extends connect(store)(LitElement) { + static get properties() { + return { + isOpen: { type: Boolean }, + setIsOpen: { attribute: false }, + isLoading: { type: Boolean }, + onSubmit: { attribute: false }, + editContent: { type: Object }, + onClose: { attribute: false }, + tagline: {type: String}, + bio: {type: String} + }; + } + + constructor() { + super(); + this.isOpen = false; + this.isLoading = false; + this.nodeUrl = this.getNodeUrl(); + this.myNode = this.getMyNode(); + this.tagline = ""; + this.bio = "", + this.walletList = [ + "btcWallet", "ltcWallet", "dogeWallet","dgbWallet", "rvnWallet", "arrrWallet" + ] + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-min-width: 400px; + --mdc-dialog-max-width: 1024px; + box-sizing: border-box; + } + .input { + width: 90%; + outline: 0; + border-width: 0 0 2px; + border-color: var(--mdc-theme-primary); + background-color: transparent; + padding: 10px; + font-family: Roboto, sans-serif; + font-size: 15px; + color: var(--chat-bubble-msg-color); + box-sizing: border-box; + } + + .input::selection { + background-color: var(--mdc-theme-primary); + color: white; + } + + .input::placeholder { + opacity: 0.6; + color: var(--black); + } + + .modal-button { + font-family: Roboto, sans-serif; + font-size: 16px; + color: var(--mdc-theme-primary); + background-color: transparent; + padding: 8px 10px; + border-radius: 5px; + border: none; + transition: all 0.3s ease-in-out; + } + + .modal-button-red { + font-family: Roboto, sans-serif; + font-size: 16px; + color: #f44336; + background-color: transparent; + padding: 8px 10px; + border-radius: 5px; + border: none; + transition: all 0.3s ease-in-out; + } + + .modal-button-red:hover { + cursor: pointer; + background-color: #f4433663; + } + + .modal-button:hover { + cursor: pointer; + background-color: #03a8f475; + } + .checkbox-row { + position: relative; + display: flex; + align-items: center; + align-content: center; + font-family: Montserrat, sans-serif; + font-weight: 600; + color: var(--black); + } + .modal-overlay { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba( + 0, + 0, + 0, + 0.5 + ); /* Semi-transparent backdrop */ + z-index: 1000; + } + + .modal-content { + position: fixed; + top: 50vh; + left: 50vw; + transform: translate(-50%, -50%); + background-color: var(--mdc-theme-surface); + width: 80vw; + max-width: 600px; + padding: 20px; + box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px; + z-index: 1001; + border-radius: 5px; + display: flex; + flex-direction: column; + } + + .modal-overlay.hidden { + display: none; + } + .avatar { + width: 36px; + height: 36px; + display: flex; + align-items: center; + } + + .app-name { + display: flex; + gap: 20px; + align-items: center; + width: 100%; + cursor: pointer; + padding: 5px; + border-radius: 5px; + margin-bottom: 10px; + } + .inner-content { + display: flex; + flex-direction: column; + max-height: 75vh; + flex-grow: 1; + overflow: auto; + } + + .inner-content::-webkit-scrollbar-track { + background-color: whitesmoke; + border-radius: 7px; + } + + .inner-content::-webkit-scrollbar { + width: 12px; + border-radius: 7px; + background-color: whitesmoke; + } + + .inner-content::-webkit-scrollbar-thumb { + background-color: rgb(180, 176, 176); + border-radius: 7px; + transition: all 0.3s ease-in-out; + } + `; + } + + firstUpdated() {} + + getNodeUrl() { + const myNode = + store.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + return nodeUrl; + } + getMyNode() { + const myNode = + store.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + return myNode; + } + + clearFields() {} + + render() { + return html` + + `; + } +} + +customElements.define('profile-modal-update', ProfileModalUpdate); diff --git a/core/src/components/friends-view/profile.js b/core/src/components/friends-view/profile.js new file mode 100644 index 00000000..f8ef2ab6 --- /dev/null +++ b/core/src/components/friends-view/profile.js @@ -0,0 +1,672 @@ +import { LitElement, html, css } from 'lit'; +import '@material/mwc-icon'; +import './friends-side-panel.js'; +import { connect } from 'pwa-helpers'; +import { store } from '../../store.js'; +import WebWorker2 from '../WebWorkerFile.js'; +import '@polymer/paper-spinner/paper-spinner-lite.js'; +import '@vaadin/tooltip'; +import { get, translate } from 'lit-translate'; +import ShortUniqueId from 'short-unique-id'; + +import { + decryptGroupData, + encryptDataGroup, + objectToBase64, + uint8ArrayToBase64, + uint8ArrayToObject, +} from '../../../../plugins/plugins/core/components/qdn-action-encryption.js'; +import { publishData } from '../../../../plugins/plugins/utils/publish-image.js'; +import { parentEpml } from '../show-plugin.js'; +import '../notification-view/popover.js'; +import './avatar.js'; +import { setNewTab } from '../../redux/app/app-actions.js'; +import './profile-modal-update.js' + +class ProfileQdn extends connect(store)(LitElement) { + static get properties() { + return { + isOpen: { type: Boolean }, + syncPercentage: { type: Number }, + settingsRawData: { type: Object }, + valuesToBeSavedOnQdn: { type: Object }, + resourceExists: { type: Boolean }, + isSaving: { type: Boolean }, + fee: { type: Object }, + name: { type: String }, + isOpenProfileModalUpdate: {type: Boolean}, + editContent: {type: Object} + }; + } + + constructor() { + super(); + this.isOpen = false; + this.getProfile = this.getProfile.bind(this); + this._updateTempSettingsData = this._updateTempSettingsData.bind(this); + this.setValues = this.setValues.bind(this); + this.saveToQdn = this.saveToQdn.bind(this); + this.syncPercentage = 0; + this.hasRetrievedResource = false; + this.hasAttemptedToFetchResource = false; + this.resourceExists = undefined; + this.settingsRawData = null; + this.nodeUrl = this.getNodeUrl(); + this.myNode = this.getMyNode(); + this.valuesToBeSavedOnQdn = {}; + this.isSaving = false; + this.fee = null; + this.name = undefined; + this.uid = new ShortUniqueId(); + this.isOpenProfileModalUpdate = false + this.editContent = null + } + static styles = css` + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-bottom: 1px solid #e0e0e0; + } + + .content { + padding: 16px; + } + .close { + visibility: hidden; + position: fixed; + z-index: -100; + right: -1000px; + } + + .parent-side-panel { + transform: translateX(100%); /* start from outside the right edge */ + transition: transform 0.3s ease-in-out; + } + .parent-side-panel.open { + transform: translateX(0); /* slide in to its original position */ + } + .notActive { + opacity: 0.5; + cursor: default; + color: var(--black); + } + .active { + opacity: 1; + cursor: pointer; + color: green; + } + .accept-button { + font-family: Roboto, sans-serif; + letter-spacing: 0.3px; + font-weight: 300; + padding: 8px 5px; + border-radius: 3px; + text-align: center; + color: var(--mdc-theme-primary); + transition: all 0.3s ease-in-out; + display: flex; + align-items: center; + gap: 10px; + font-size: 18px; + } + + .accept-button:hover { + cursor: pointer; + background-color: #03a8f485; + } + + .undo-button { + font-family: Roboto, sans-serif; + letter-spacing: 0.3px; + font-weight: 300; + padding: 8px 5px; + border-radius: 3px; + text-align: center; + color: #f44336; + transition: all 0.3s ease-in-out; + display: flex; + align-items: center; + gap: 10px; + font-size: 18px; + } + + .undo-button:hover { + cursor: pointer; + background-color: #f4433663; + } + `; + + getNodeUrl() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + return nodeUrl; + } + getMyNode() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + return myNode; + } + + async getAvatar(dataItem) { + const url = `${this.nodeUrl}/arbitrary/${dataItem.service}/${dataItem.name}/${dataItem.identifier}?encoding=base64`; + const res = await fetch(url); + const data = await res.text(); + if (data.error) throw new Error('Cannot retrieve your data from qdn'); + const decryptedData = decryptGroupData(data); + const decryptedDataToBase64 = uint8ArrayToObject(decryptedData); + return decryptedDataToBase64; + } + + async getRawData(dataItem) { + const url = `${this.nodeUrl}/arbitrary/${dataItem.service}/${dataItem.name}/${dataItem.identifier}?encoding=base64`; + const res = await fetch(url); + const data = await res.text(); + if (data.error) throw new Error('Cannot retrieve your data from qdn'); + const decryptedData = decryptGroupData(data); + const decryptedDataToBase64 = uint8ArrayToObject(decryptedData); + return decryptedDataToBase64; + } + + async getMyFollowedNames() { + let myFollowedNames = []; + try { + myFollowedNames = await parentEpml.request('apiCall', { + url: `/lists/followedNames?apiKey=${this.myNode.apiKey}`, + }); + } catch (error) {} + + return myFollowedNames; + } + + async followNames(names) { + let items = names; + let namesJsonString = JSON.stringify({ items: items }); + + let ret = await parentEpml.request('apiCall', { + url: `/lists/followedNames?apiKey=${this.myNode.apiKey}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: `${namesJsonString}`, + }); + + return ret; + } + + async setValues(response, resource) { + this.settingsRawData = response; + const rawDataTimestamp = resource.updated; + + const tempSettingsData = JSON.parse( + localStorage.getItem('temp-settings-data') || '{}' + ); + + const userLists = response.userLists || []; + const friendsFeed = response.friendsFeed; + const myMenuPlugs = response.myMenuPlugs; + + this.valuesToBeSavedOnQdn = {}; + if ( + userLists.length > 0 && + (!tempSettingsData.userLists || + (tempSettingsData.userLists && + tempSettingsData.userLists.timestamp < rawDataTimestamp)) + ) { + const friendList = userLists[0]; + const copyPayload = [...friendList]; + const onlyNames = copyPayload.map((item) => item.name); + const followedList = await this.getMyFollowedNames(); + + const namesNotInFollowedList = onlyNames.filter( + (name) => !followedList.includes(name) + ); + if (namesNotInFollowedList.length > 0) { + await this.followNames(namesNotInFollowedList); + } + + localStorage.setItem( + 'friends-my-friend-list', + JSON.stringify(friendList) + ); + this.dispatchEvent( + new CustomEvent('friends-my-friend-list-event', { + bubbles: true, + composed: true, + detail: copyPayload, + }) + ); + } else if ( + tempSettingsData.userLists && + tempSettingsData.userLists.timestamp > rawDataTimestamp + ) { + this.valuesToBeSavedOnQdn = { + ...this.valuesToBeSavedOnQdn, + userLists: { + data: tempSettingsData.userLists.data, + }, + }; + } + + if ( + friendsFeed && + (!tempSettingsData.friendsFeed || + (tempSettingsData.friendsFeed && + tempSettingsData.friendsFeed.timestamp < rawDataTimestamp)) + ) { + const copyPayload = [...friendsFeed]; + + localStorage.setItem( + 'friends-my-selected-feeds', + JSON.stringify(friendsFeed) + ); + this.dispatchEvent( + new CustomEvent('friends-my-selected-feeds-event', { + bubbles: true, + composed: true, + detail: copyPayload, + }) + ); + } else if ( + tempSettingsData.friendsFeed && + tempSettingsData.friendsFeed.timestamp > rawDataTimestamp + ) { + this.valuesToBeSavedOnQdn = { + ...this.valuesToBeSavedOnQdn, + friendsFeed: { + data: tempSettingsData.friendsFeed.data, + }, + }; + } + + if ( + myMenuPlugs && + (!tempSettingsData.myMenuPlugs || + (tempSettingsData.myMenuPlugs && + tempSettingsData.myMenuPlugs.timestamp < rawDataTimestamp)) + ) { + if (Array.isArray(myMenuPlugs)) { + const copyPayload = [...myMenuPlugs]; + + localStorage.setItem( + 'myMenuPlugs', + JSON.stringify(myMenuPlugs) + ); + + this.dispatchEvent( + new CustomEvent('myMenuPlugs-event', { + bubbles: true, + composed: true, + detail: copyPayload, + }) + ); + } + } else if ( + tempSettingsData.myMenuPlugs && + tempSettingsData.myMenuPlugs.timestamp > rawDataTimestamp + ) { + this.valuesToBeSavedOnQdn = { + ...this.valuesToBeSavedOnQdn, + myMenuPlugs: { + data: tempSettingsData.myMenuPlugs.data, + }, + }; + } + } + + async getProfile() { + try { + const arbFee = await this.getArbitraryFee(); + this.fee = arbFee; + this.hasAttemptedToFetchResource = true; + let resource; + const nameObject = store.getState().app.accountInfo.names[0]; + if (!nameObject) { + this.name = null; + throw new Error('no name'); + } + const name = nameObject.name; + this.name = name; + this.error = ''; + const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=qortal_profile&name=${name}&prefix=true&exactmatchnames=true&excludeblocked=true&limit=20`; + const res = await fetch(url); + let data = ''; + try { + data = await res.json(); + if (Array.isArray(data)) { + data = data.filter( + (item) => item.identifier === 'qortal_profile' + ); + + if (data.length > 0) { + this.resourceExists = true; + const dataItem = data[0]; + try { + const response = await this.getRawData(dataItem); + if (response.version) { + // this.setValues(response, dataItem); + } else { + this.error = 'Cannot get saved user settings'; + } + } catch (error) { + console.log({ error }); + this.error = 'Cannot get saved user settings'; + } + } else { + this.resourceExists = false; + } + } else { + this.error = 'Unable to perform query'; + } + } catch (error) { + data = { + error: 'No resource found', + }; + } + + if (resource) { + this.hasRetrievedResource = true; + } + } catch (error) { + console.log({ error }); + } + } + + stateChanged(state) { + if ( + state.app.accountInfo && + state.app.accountInfo.names.length && + state.app.nodeStatus && + state.app.nodeStatus.syncPercent !== this.syncPercentage + ) { + this.syncPercentage = state.app.nodeStatus.syncPercent; + + if ( + !this.hasAttemptedToFetchResource && + state.app.nodeStatus.syncPercent === 100 + ) { + this.getProfile(); + } + } + } + + async getArbitraryFee() { + const timestamp = Date.now(); + const url = `${this.nodeUrl}/transactions/unitfee?txType=ARBITRARY×tamp=${timestamp}`; + const response = await fetch(url); + if (!response.ok) { + throw new Error('Error when fetching arbitrary fee'); + } + const data = await response.json(); + const arbitraryFee = (Number(data) / 1e8).toFixed(8); + return { + timestamp, + fee: Number(data), + feeToShow: arbitraryFee, + }; + } + + async saveToQdn() { + try { + this.isSaving = true; + if (this.resourceExists === true && this.error) + throw new Error('Unable to save'); + + const nameObject = store.getState().app.accountInfo.names[0]; + if (!nameObject) throw new Error('no name'); + const name = nameObject.name; + const identifer = 'qortal_general_settings'; + const filename = 'qortal_general_settings.json'; + const selectedAddress = store.getState().app.selectedAddress; + const getArbitraryFee = await this.getArbitraryFee(); + const feeAmount = getArbitraryFee.fee; + const friendsList = JSON.parse( + localStorage.getItem('friends-my-friend-list') || '[]' + ); + const friendsFeed = JSON.parse( + localStorage.getItem('friends-my-selected-feeds') || '[]' + ); + const myMenuPlugs = JSON.parse( + localStorage.getItem('myMenuPlugs') || '[]' + ); + + let newObject; + + if (this.resourceExists === false) { + newObject = { + version: 1, + userLists: [friendsList], + friendsFeed, + myMenuPlugs, + }; + } else if (this.settingsRawData) { + const tempSettingsData = JSON.parse( + localStorage.getItem('temp-settings-data') || '{}' + ); + newObject = { + ...this.settingsRawData, + }; + for (const key in tempSettingsData) { + if (tempSettingsData[key].hasOwnProperty('data')) { + if ( + key === 'userLists' && + !Array.isArray(tempSettingsData[key].data) + ) + continue; + if ( + key === 'friendsFeed' && + !Array.isArray(tempSettingsData[key].data) + ) + continue; + if ( + key === 'myMenuPlugs' && + !Array.isArray(tempSettingsData[key].data) + ) + continue; + newObject[key] = tempSettingsData[key].data; + } + } + } + + const newObjectToBase64 = await objectToBase64(newObject); + const encryptedData = encryptDataGroup({ + data64: newObjectToBase64, + publicKeys: [], + }); + + const worker = new WebWorker2(); + try { + const resPublish = await publishData({ + registeredName: encodeURIComponent(name), + file: encryptedData, + service: 'DOCUMENT_PRIVATE', + identifier: encodeURIComponent(identifer), + parentEpml: parentEpml, + uploadType: 'file', + selectedAddress: selectedAddress, + worker: worker, + isBase64: true, + filename: filename, + apiVersion: 2, + withFee: true, + feeAmount: feeAmount, + }); + + this.resourceExists = true; + this.setValues(newObject, { + updated: Date.now(), + }); + localStorage.setItem('temp-settings-data', JSON.stringify({})); + this.valuesToBeSavedOnQdn = {}; + worker.terminate(); + } catch (error) { + worker.terminate(); + } + } catch (error) { + console.log({ error }); + } finally { + this.isSaving = false; + } + } + + _updateTempSettingsData() { + this.valuesToBeSavedOnQdn = JSON.parse( + localStorage.getItem('temp-settings-data') || '{}' + ); + } + + connectedCallback() { + super.connectedCallback(); + window.addEventListener( + 'temp-settings-data-event', + this._updateTempSettingsData + ); + } + + disconnectedCallback() { + window.removeEventListener( + 'temp-settings-data-event', + this._updateTempSettingsData + ); + super.disconnectedCallback(); + } + + publishProfile(){ + + } + + onClose(){ + this.isOpenProfileModalUpdate = false + } + render() { + console.log('sup profile2', this.name); + return html` + ${this.isSaving || + (!this.error && this.resourceExists === undefined) + ? html` + + ` + : !this.name + ? html` + 0 || this.resourceExists === false + ? 'active' + : 'notActive'} + @click=${() => { + const target = this.shadowRoot.getElementById( + 'popover-notification' + ); + const popover = + this.shadowRoot.querySelector( + 'popover-component' + ); + if (popover) { + popover.openPopover(target); + } + }} + style="user-select:none;cursor:pointer" + >account_circle + 0 || + this.resourceExists === false + ? get('save.saving3') + : get('save.saving2')} + > + + +
+

+ ${`${get('profile.profile1')}`} +

+
+
+
+ ${translate('profile.profile2')} +
+
+
+ ` + : html` +
{ + this.isOpenProfileModalUpdate = !this.isOpenProfileModalUpdate + }}> + +
+ `} + + { + this.isOpenProfileModalUpdate = val + }} + .onSubmit=${(val, isEdit)=> this.publishProfile(val, isEdit)} + .editContent=${this.editContent} + .onClose=${()=> this.onClose()} + > + `; + } +} + +customElements.define('profile-qdn', ProfileQdn); diff --git a/core/src/components/friends-view/save-settings-qdn.js b/core/src/components/friends-view/save-settings-qdn.js index b2e558a5..710813f7 100644 --- a/core/src/components/friends-view/save-settings-qdn.js +++ b/core/src/components/friends-view/save-settings-qdn.js @@ -3,12 +3,13 @@ import '@material/mwc-icon'; import './friends-side-panel.js'; import { connect } from 'pwa-helpers'; import { store } from '../../store.js'; -import WebWorker from 'web-worker:./computePowWorkerFile.src.js'; +import WebWorker from '../WebWorkerFile.js'; import '@polymer/paper-spinner/paper-spinner-lite.js'; import '@vaadin/tooltip'; import { get, translate } from 'lit-translate'; import { decryptGroupData, + encryptDataGroup, objectToBase64, uint8ArrayToBase64, diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js index 266df4a2..8c12ee90 100644 --- a/core/src/components/show-plugin.js +++ b/core/src/components/show-plugin.js @@ -337,6 +337,7 @@ class ShowPlugin extends connect(store)(LitElement) { } render() { + console.log('this.tabs', this.tabs) const plugSrc = (myPlug) => { return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}` }