diff --git a/core/src/components/friends-view/save-settings-qdn.js b/core/src/components/friends-view/save-settings-qdn.js
index b7b4516e..4f2531f8 100644
--- a/core/src/components/friends-view/save-settings-qdn.js
+++ b/core/src/components/friends-view/save-settings-qdn.js
@@ -1,10 +1,27 @@
import { LitElement, html, css } from 'lit';
import '@material/mwc-icon';
import './friends-side-panel.js';
-class SaveSettingsQdn extends LitElement {
+import { connect } from 'pwa-helpers';
+import { store } from '../../store.js';
+import WebWorker from 'web-worker:./computePowWorkerFile.src.js'
+import '@polymer/paper-spinner/paper-spinner-lite.js'
+import '@vaadin/tooltip';
+import {
+ get
+} from 'lit-translate';
+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';
+
+class SaveSettingsQdn extends connect(store)(LitElement) {
static get properties() {
return {
- isOpen: {type: Boolean}
+ isOpen: {type: Boolean},
+ syncPercentage: {type: Number},
+ settingsRawData: {type: Object},
+ valuesToBeSavedOnQdn: {type: Object},
+ resourceExists: {type: Boolean},
+ isSaving: {type: Boolean}
};
}
@@ -12,6 +29,19 @@ class SaveSettingsQdn extends LitElement {
constructor() {
super();
this.isOpen = false
+ this.getGeneralSettingsQdn = this.getGeneralSettingsQdn.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
}
static styles = css`
.header {
@@ -40,15 +70,297 @@ class SaveSettingsQdn extends LitElement {
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;
+ }
`;
+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 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 setValues(response, resource){
+ this.settingsRawData = response
+ const rawDataTimestamp = resource.updated
+
+ const tempSettingsData = JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
+ if(tempSettingsData){
+
+ }
+ const userLists = response.userLists || []
+ const friendsFeed = response.friendsFeed
+
+ this.valuesToBeSavedOnQdn = {}
+ if(userLists.length > 0 && (!tempSettingsData.userLists || (tempSettingsData.userLists && (tempSettingsData.userLists.timestamp < rawDataTimestamp)))){
+ const friendList = userLists[0]
+ localStorage.setItem('friends-my-friend-list', JSON.stringify(friendList));
+ this.dispatchEvent(
+ new CustomEvent('friends-my-friend-list-event', {
+ bubbles: true,
+ composed: true,
+ detail: friendList,
+ }),
+ );
+ } 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)))){
+ localStorage.setItem('friends-my-selected-feeds', JSON.stringify(friendsFeed));
+ this.dispatchEvent(
+ new CustomEvent('friends-my-selected-feeds-event', {
+ bubbles: true,
+ composed: true,
+ detail: friendsFeed,
+ }),
+ );
+ } else if(tempSettingsData.friendsFeed && tempSettingsData.friendsFeed.timestamp > rawDataTimestamp){
+ this.valuesToBeSavedOnQdn = {
+ ...this.valuesToBeSavedOnQdn,
+ friendsFeed: {
+ data: tempSettingsData.friendsFeed.data
+ }
+ }
+ }
+}
+
+async getGeneralSettingsQdn(){
+ try {
+ this.hasAttemptedToFetchResource = true
+ let resource
+ const name = "palmas"
+ this.error = ''
+ const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT_PRIVATE&identifier=qortal_general_settings&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_general_settings')
+
+ if(data.length > 0){
+
+ this.resourceExists = true
+ const dataItem = data[0]
+ try {
+ const response = await this.getRawData(dataItem)
+ console.log({response})
+ 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.nodeStatus && state.app.nodeStatus.syncPercent !== this.syncPercentage){
+ this.syncPercentage = state.app.nodeStatus.syncPercent
+
+ if(!this.hasAttemptedToFetchResource && state.app.nodeStatus.syncPercent === 100){
+ console.log('hello')
+ this.getGeneralSettingsQdn()
+ }
+
+ }
+}
+
+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')
+
+ console.log('info', store.getState())
+ const nameObject = store.getState().app.accountInfo.names[0]
+ if(!nameObject) throw new Error('no name')
+ const name = nameObject.name
+ console.log({name})
+ const identifer = 'qortal_general_settings'
+ const filename = 'qortal_general_settings.json'
+ const selectedAddress = store.getState().app.selectedAddress
+ console.log({selectedAddress})
+ const getArbitraryFee = await this.getArbitraryFee()
+ const feeAmount = getArbitraryFee.fee
+ console.log({feeAmount})
+ const friendsList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
+ const friendsFeed = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
+
+ let newObject
+
+ if(this.resourceExists === false){
+ newObject = {
+ version: 1,
+ userLists: [friendsList],
+ friendsFeed
+ }
+ } else if(this.settingsRawData) {
+ const tempSettingsData= JSON.parse(localStorage.getItem('temp-settings-data') || "{}")
+ console.log({tempSettingsData})
+ newObject = {
+ ...this.settingsRawData,
+ }
+ for (const key in tempSettingsData) {
+ if (tempSettingsData[key].hasOwnProperty('data')) {
+ newObject[key] = tempSettingsData[key].data;
+ }
+ }
+
+ }
+
+ console.log({newObject})
+ const newObjectToBase64 = await objectToBase64(newObject);
+ console.log({newObjectToBase64})
+ const encryptedData = encryptDataGroup({data64: newObjectToBase64, publicKeys: []})
+
+ console.log({encryptedData})
+ const worker = new WebWorker();
+ 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()
+ console.log('callback')
+ window.addEventListener('temp-settings-data-event', this._updateTempSettingsData)
+}
+
+disconnectedCallback() {
+ window.removeEventListener('temp-settings-data-event', this._updateTempSettingsData)
+ super.disconnectedCallback()
+}
render() {
+ console.log('this.resourceExists', this.resourceExists)
return html`
-
{
- this.isOpen = !this.isOpen
- }} style="color: var(--black); cursor:pointer;user-select:none"
+ ${this.isSaving || (!this.error && this.resourceExists === undefined) ? html`
+
+
+ ` : html`
+ 0 || this.resourceExists === false ? 'active' : 'notActive'} @click=${()=> {
+ if(Object.values(this.valuesToBeSavedOnQdn).length > 0 || this.resourceExists === false ){
+ this.saveToQdn()
+ }
+ // this.isOpen = !this.isOpen
+ }} style="user-select:none"
>save
+ 0 || this.resourceExists === false ? get('save.saving3') : get('save.saving2')}>
+
+ `}
+
`;
diff --git a/core/src/components/qort-theme-toggle.js b/core/src/components/qort-theme-toggle.js
index 02833cea..6baa688c 100644
--- a/core/src/components/qort-theme-toggle.js
+++ b/core/src/components/qort-theme-toggle.js
@@ -109,12 +109,13 @@ class QortThemeToggle extends LitElement {
toggleTheme() {
+ console.log('toggle')
if (this.theme === 'light') {
this.theme = 'dark';
} else {
this.theme = 'light';
}
-
+ console.log('dispatch')
this.dispatchEvent(
new CustomEvent('qort-theme-change', {
bubbles: true,
diff --git a/core/src/components/show-plugin.js b/core/src/components/show-plugin.js
index abf21b9c..a0857176 100644
--- a/core/src/components/show-plugin.js
+++ b/core/src/components/show-plugin.js
@@ -532,6 +532,7 @@ class ShowPlugin extends connect(store)(LitElement) {
})
window.addEventListener('storage', () => {
+ console.log('show plugin')
const checkLanguage = localStorage.getItem('qortalLanguage')
const checkTheme = localStorage.getItem('qortalTheme')
diff --git a/core/src/components/theme-toggle.js b/core/src/components/theme-toggle.js
index d2af6e82..8c833895 100644
--- a/core/src/components/theme-toggle.js
+++ b/core/src/components/theme-toggle.js
@@ -83,6 +83,7 @@ class ThemeToggle extends LitElement {
} else {
this.theme = 'light'
}
+ console.log('theme toggle')
this.dispatchEvent(new CustomEvent('qort-theme-change', {
bubbles: true,
diff --git a/plugins/plugins/core/components/qdn-action-encryption.js b/plugins/plugins/core/components/qdn-action-encryption.js
index cfd7a092..06ada168 100644
--- a/plugins/plugins/core/components/qdn-action-encryption.js
+++ b/plugins/plugins/core/components/qdn-action-encryption.js
@@ -89,6 +89,47 @@ export function base64ToUint8Array(base64) {
return bytes
}
+export function uint8ArrayToObject(uint8Array) {
+ // Decode the byte array using TextDecoder
+ const decoder = new TextDecoder()
+ const jsonString = decoder.decode(uint8Array)
+
+ // Convert the JSON string back into an object
+ const obj = JSON.parse(jsonString)
+
+ return obj
+ }
+
+ export function objectToBase64(obj) {
+ // Step 1: Convert the object to a JSON string
+ const jsonString = JSON.stringify(obj);
+
+ // Step 2: Create a Blob from the JSON string
+ const blob = new Blob([jsonString], { type: 'application/json' });
+
+ // Step 3: Create a FileReader to read the Blob as a base64-encoded string
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ if (typeof reader.result === 'string') {
+ // Remove 'data:application/json;base64,' prefix
+ const base64 = reader.result.replace(
+ 'data:application/json;base64,',
+ ''
+ );
+ resolve(base64);
+ } else {
+ reject(new Error('Failed to read the Blob as a base64-encoded string'));
+ }
+ };
+ reader.onerror = () => {
+ reject(reader.error);
+ };
+ reader.readAsDataURL(blob);
+ });
+ }
+
+
export const encryptData = ({ data64, recipientPublicKey }) => {