4
1
mirror of https://github.com/Qortal/qortal-ui.git synced 2025-02-11 17:55:51 +00:00

bug fixes

This commit is contained in:
PhilReact 2023-10-21 14:25:12 +03:00
parent d0507946aa
commit 547671cc35
9 changed files with 659 additions and 385 deletions

View File

@ -1203,6 +1203,7 @@
"save": {
"saving1": "Unable to fetch saved settings",
"saving2": "Nothing to save",
"saving3": "Save unsaved changes"
"saving3": "Save unsaved changes",
"saving4": "Undo changes"
}
}

View File

@ -269,7 +269,6 @@ class AddFriendsModal extends connect(store)(LitElement) {
this.willFollow = this.editContent.willFollow ?? true;
this.alias = this.editContent.alias ?? '';
this.requestUpdate()
console.log('this.editContent', this.editContent )
}
if (
changedProperties &&
@ -420,6 +419,7 @@ class AddFriendsModal extends connect(store)(LitElement) {
item.name === schema.name
);
if (findIndex === -1) {
if(this.mySelectedFeeds.length > 4) return
copymySelectedFeeds.push({
name: schema.name,
identifier: schema.identifier,

View File

@ -70,7 +70,6 @@ class FriendsFeed extends connect(store)(LitElement) {
_updateFeeds(event) {
const detail = event.detail
console.log('detail2', detail)
this.mySelectedFeeds = detail
this.reFetchFeedData()
this.requestUpdate()
@ -78,7 +77,6 @@ class FriendsFeed extends connect(store)(LitElement) {
connectedCallback() {
super.connectedCallback()
console.log('callback')
window.addEventListener('friends-my-selected-feeds-event', this._updateFeeds) }
disconnectedCallback() {
@ -325,9 +323,7 @@ this.getFeedOnInterval()
}
if(newItem.schema){
const resource = newItem
// First, evaluate methods to get values for customParams
await updateCustomParamsWithMethods(newItem.schema, newResource);
// Now, generate your final URLs
let clickValue1 = newItem.schema.click;
const resolvedClickValue1 = replacePlaceholders(clickValue1, resource, newItem.schema.customParams);
@ -445,95 +441,12 @@ export function constructUrl(base, search, dynamicVars) {
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base;
}
function validateMethodString(methodString) {
// Check for IIFE
const iifePattern = /^\(.*\)\s*\(\)/;
if (iifePattern.test(methodString)) {
throw new Error("IIFE detected!");
}
// Check for disallowed keywords
const disallowed = ["eval", "Function", "fetch", "XMLHttpRequest"];
for (const keyword of disallowed) {
if (methodString.includes(keyword)) {
throw new Error(`Disallowed keyword detected: ${keyword}`);
}
}
// ... Add more validation steps here ...
return true;
}
function executeMethodInWorker(methodString, externalArgs) {
return new Promise((resolve, reject) => {
if (!validateMethodString(methodString)) {
reject(new Error("Invalid method string provided."));
return;
}
const workerFunction = `
self.onmessage = function(event) {
const methodFunction = new Function("resource", "${methodString}");
const result = methodFunction(event.data.externalArgs);
if (typeof result === 'string' || typeof result === 'number') {
self.postMessage(result);
} else {
self.postMessage('');
}
}
`;
const blob = new Blob([workerFunction], { type: 'application/javascript' });
const blobURL = URL.createObjectURL(blob);
const worker = new Worker(blobURL);
worker.onmessage = function(event) {
if (typeof event.data === 'string' || typeof event.data === 'number') {
resolve(event.data);
worker.terminate();
URL.revokeObjectURL(blobURL);
} else {
resolve("");
event.data = null
worker.terminate();
URL.revokeObjectURL(blobURL);
}
};
worker.onerror = function(error) {
reject(error);
worker.terminate();
URL.revokeObjectURL(blobURL);
};
worker.postMessage({ externalArgs });
});
}
export async function updateCustomParamsWithMethods(schema,resource) {
for (const key in schema.customParams) {
const value = schema.customParams[key];
if (value.startsWith("**methods.") && value.endsWith("**")) {
const methodInvocation = value.slice(10, -2).split('(');
const methodName = methodInvocation[0];
if (schema.methods[methodName]) {
const newResource = {
identifier: resource.identifier,
name: resource.name,
service: resource.service
}
const methodResult = await executeMethodInWorker(schema.methods[methodName], newResource);
schema.customParams[key] = methodResult;
}
}
}
}
export function replacePlaceholders(template, resource, customParams) {
const dataSource = { resource, customParams };

View File

@ -3,45 +3,51 @@ 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 '@polymer/paper-spinner/paper-spinner-lite.js'
import WebWorker from 'web-worker:./computePowWorkerFile.src.js';
import '@polymer/paper-spinner/paper-spinner-lite.js';
import '@vaadin/tooltip';
import { get, translate } from 'lit-translate';
import {
get
} from 'lit-translate';
import { decryptGroupData, encryptDataGroup, objectToBase64, uint8ArrayToBase64, uint8ArrayToObject } from '../../../../plugins/plugins/core/components/qdn-action-encryption.js';
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';
class SaveSettingsQdn extends connect(store)(LitElement) {
class SaveSettingsQdn 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}
isOpen: { type: Boolean },
syncPercentage: { type: Number },
settingsRawData: { type: Object },
valuesToBeSavedOnQdn: { type: Object },
resourceExists: { type: Boolean },
isSaving: { type: Boolean },
fee: { type: Object },
};
}
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
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;
this.fee = null;
}
static styles = css`
.header {
@ -64,309 +70,491 @@ class SaveSettingsQdn extends connect(store)(LitElement) {
.parent-side-panel {
transform: translateX(100%); /* start from outside the right edge */
transition: transform 0.3s ease-in-out;
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)
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 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){
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;
}
const userLists = response.userLists || []
const friendsFeed = response.friendsFeed
getMyNode() {
const myNode =
window.parent.reduxStore.getState().app.nodeConfig.knownNodes[
window.parent.reduxStore.getState().app.nodeConfig.node
];
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
}
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;
const myMenuPlugs = response.myMenuPlugs
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,
},
};
}
if (
myMenuPlugs &&
(!tempSettingsData.myMenuPlugs ||
(tempSettingsData.myMenuPlugs &&
tempSettingsData.myMenuPlugs.timestamp < rawDataTimestamp))
) {
localStorage.setItem(
'myMenuPlugs',
JSON.stringify(myMenuPlugs)
);
this.dispatchEvent(
new CustomEvent('myMenuPlugs-event', {
bubbles: true,
composed: true,
detail: myMenuPlugs,
})
);
} else if (
tempSettingsData.myMenuPlugs &&
tempSettingsData.myMenuPlugs.timestamp > rawDataTimestamp
) {
this.valuesToBeSavedOnQdn = {
...this.valuesToBeSavedOnQdn,
myMenuPlugs: {
data: tempSettingsData.myMenuPlugs.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 {
const arbFee = await this.getArbitraryFee();
this.fee = arbFee;
this.hasAttemptedToFetchResource = true;
let resource;
const nameObject = store.getState().app.accountInfo.names[0];
if (!nameObject) throw new Error('no name');
const name = nameObject.name;
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'
);
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"
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';
}
} else {
this.resourceExists = false
} catch (error) {
console.log({ error });
this.error = 'Cannot get saved user settings';
}
} else {
this.error = "Unable to perform query"
}
} catch (error) {
data = {
error: 'No resource found'
this.resourceExists = false;
}
} else {
this.error = 'Unable to perform query';
}
} catch (error) {
data = {
error: 'No resource found',
};
}
if(resource){
this.hasRetrievedResource = true
if (resource) {
this.hasRetrievedResource = true;
}
} catch (error) {
console.log({ error });
}
} 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;
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()
if (
!this.hasAttemptedToFetchResource &&
state.app.nodeStatus.syncPercent === 100
) {
this.getGeneralSettingsQdn();
}
}
}
}
async getArbitraryFee (){
const timestamp = Date.now()
const url = `${this.nodeUrl}/transactions/unitfee?txType=ARBITRARY&timestamp=${timestamp}`
const response = await fetch(url)
if (!response.ok) {
throw new Error('Error when fetching arbitrary fee');
async getArbitraryFee() {
const timestamp = Date.now();
const url = `${this.nodeUrl}/transactions/unitfee?txType=ARBITRARY&timestamp=${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,
};
}
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
async saveToQdn() {
try {
this.isSaving = true;
if (this.resourceExists === true && this.error)
throw new Error('Unable to save');
if(this.resourceExists === false){
newObject = {
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') || '[]'
);
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})
friendsFeed,
};
} 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')) {
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 encryptedData = encryptDataGroup({
data64: newObjectToBase64,
publicKeys: [],
});
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
});
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
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') || "{}")
_updateTempSettingsData() {
this.valuesToBeSavedOnQdn = JSON.parse(
localStorage.getItem('temp-settings-data') || '{}'
);
}
}
connectedCallback() {
super.connectedCallback();
window.addEventListener(
'temp-settings-data-event',
this._updateTempSettingsData
);
}
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()
}
disconnectedCallback() {
window.removeEventListener(
'temp-settings-data-event',
this._updateTempSettingsData
);
super.disconnectedCallback();
}
render() {
console.log('this.resourceExists', this.resourceExists)
return html`
${this.isSaving || (!this.error && this.resourceExists === undefined) ? html`
<paper-spinner-lite active style="display: block; margin: 0 auto;"></paper-spinner-lite>
` : html`
<mwc-icon id="save-icon" class=${Object.values(this.valuesToBeSavedOnQdn).length > 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</mwc-icon
>
<vaadin-tooltip
for="save-icon"
position="bottom"
hover-delay=${300}
hide-delay=${1}
text=${this.error ? get('save.saving1') : Object.values(this.valuesToBeSavedOnQdn).length > 0 || this.resourceExists === false ? get('save.saving3') : get('save.saving2')}>
</vaadin-tooltip>
`}
${this.isSaving ||
(!this.error && this.resourceExists === undefined)
? html`
<paper-spinner-lite
active
style="display: block; margin: 0 auto;"
></paper-spinner-lite>
`
: html`
<mwc-icon
id="save-icon"
class=${Object.values(this.valuesToBeSavedOnQdn)
.length > 0 || this.resourceExists === false
? 'active'
: 'notActive'}
@click=${() => {
if (
Object.values(this.valuesToBeSavedOnQdn)
.length > 0 ||
this.resourceExists === false
) {
if (!this.fee) return;
// this.saveToQdn()
const target =
this.shadowRoot.getElementById(
'popover-notification'
);
const popover =
this.shadowRoot.querySelector(
'popover-component'
);
if (popover) {
popover.openPopover(target);
}
}
// this.isOpen = !this.isOpen
}}
style="user-select:none"
>save</mwc-icon
>
<vaadin-tooltip
for="save-icon"
position="bottom"
hover-delay=${300}
hide-delay=${1}
text=${this.error
? get('save.saving1')
: Object.values(this.valuesToBeSavedOnQdn)
.length > 0 ||
this.resourceExists === false
? get('save.saving3')
: get('save.saving2')}
>
</vaadin-tooltip>
<popover-component
for="save-icon"
message=""
>
<div style="margin-bottom:20px">
<p style="margin:10px 0px; font-size:16px">${`${get('walletpage.wchange12')}: ${
this.fee ? this.fee.feeToShow : ''
}`}</p>
</div>
<div style="display:flex;justify-content:space-between;gap:10px">
<div
class="undo-button"
@click="${() => {
localStorage.setItem('temp-settings-data', JSON.stringify({}));
this.valuesToBeSavedOnQdn = {}
const popover =
this.shadowRoot.querySelector(
'popover-component'
);
if (popover) {
popover.closePopover();
}
this.getGeneralSettingsQdn()
}}"
>
${translate('save.saving4')}
</div>
<div
class="accept-button"
@click="${() => {
this.saveToQdn();
const popover =
this.shadowRoot.querySelector(
'popover-component'
);
if (popover) {
popover.closePopover();
}
}}"
>
${translate('browserpage.bchange28')}
</div>
</div>
</popover-component>
`}
`;
}
}
customElements.define('save-settings-qdn', SaveSettingsQdn);

View File

@ -23,6 +23,8 @@ export class PopoverComponent extends LitElement {
margin-left: 10px;
color: var(--black)
}
`;
static properties = {
@ -65,7 +67,8 @@ export class PopoverComponent extends LitElement {
render() {
return html`
<span class="close-icon" @click="${this.closePopover}"><mwc-icon style="color: var(--black)">close</mwc-icon></span>
<div><mwc-icon style="color: var(--black)">info</mwc-icon> ${this.message}</div>
<div><mwc-icon style="color: var(--black)">info</mwc-icon> ${this.message} <slot></slot>
</div>
`;
}
}

View File

@ -344,6 +344,7 @@ class ShowPlugin extends connect(store)(LitElement) {
return html`
<div class="tabs">
${this.tabs.map((tab, index) => {
console.log({tab})
let title = ''
let icon = ''
let count = 0
@ -433,9 +434,21 @@ class ShowPlugin extends connect(store)(LitElement) {
@click="${() => {
this.currentTab = index
}}"
@mousedown="${(event) => {
if (event.button === 1) {
event.preventDefault();
this.removeTab(index, tab.id);
}
}}"
>
<div id="icon-${tab.id}" class="${this.currentTab === index ? "iconActive" : "iconInactive"}">
<mwc-icon>${icon}</mwc-icon>
${tab.myPlugObj && tab.myPlugObj.url === "myapp" ? html`
<tab-avatar appname=${title} appicon=${icon}></tab-avatar>
` : html`
<mwc-icon>${icon}</mwc-icon>
`}
</div>
<div class="tabCard">
${count ? html`
@ -1028,7 +1041,7 @@ class NavBar extends connect(store)(LitElement) {
color: var(--black);
--mdc-icon-size: 28px;
cursor: pointer;
position: relative
position: relative;
z-index: 1;
}
@ -1190,6 +1203,7 @@ class NavBar extends connect(store)(LitElement) {
this.myFollowedNamesList = []
this.searchContentString = ''
this.searchNameResources = []
this._updateMyMenuPlugins = this._updateMyMenuPlugins.bind(this)
}
render() {
@ -1456,6 +1470,46 @@ class NavBar extends connect(store)(LitElement) {
await this.getMyFollowedNamesList()
}
_updateMyMenuPlugins(event) {
const detail = event.detail
console.log({detailPlugs: 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()
console.log('callback')
window.addEventListener('myMenuPlugs-event', this._updateMyMenuPlugins) }
disconnectedCallback() {
window.removeEventListener('myMenuPlugs-event', this._updateMyMenuPlugins)
super.disconnectedCallback()
}
openImportDialog() {
this.shadowRoot.getElementById('importTabMenutDialog').show()
}
@ -1467,6 +1521,7 @@ class NavBar extends connect(store)(LitElement) {
myFile = file
const newTabMenu = JSON.parse((myFile) || "[]")
localStorage.setItem("myMenuPlugs", JSON.stringify(newTabMenu))
this.saveSettingToTemp(newTabMenu)
this.shadowRoot.getElementById('importTabMenutDialog').close()
this.myMenuPlugins = JSON.parse(localStorage.getItem("myMenuPlugs") || "[]")
this.firstUpdated()
@ -1951,6 +2006,7 @@ class NavBar extends connect(store)(LitElement) {
oldMenuPlugs.push(newMenuPlugsItem)
localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs))
this.saveSettingToTemp(oldMenuPlugs)
let myplugstring2 = get("walletpage.wchange52")
parentEpml.request('showSnackBar', `${myplugstring2}`)
@ -2014,7 +2070,7 @@ class NavBar extends connect(store)(LitElement) {
oldMenuPlugs.push(newMenuPlugsItem)
localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs))
this.saveSettingToTemp(oldMenuPlugs)
let myplugstring2 = get("walletpage.wchange52")
parentEpml.request('showSnackBar', `${myplugstring2}`)
@ -2084,7 +2140,7 @@ class NavBar extends connect(store)(LitElement) {
oldMenuPlugs.push(newMenuPlugsItem)
localStorage.setItem("myMenuPlugs", JSON.stringify(oldMenuPlugs))
this.saveSettingToTemp(oldMenuPlugs)
let myplugstring2 = get("walletpage.wchange52")
parentEpml.request('showSnackBar', `${myplugstring2}`)
@ -2150,7 +2206,10 @@ class NavBar extends connect(store)(LitElement) {
return html`
<div class="menuIconPos" @click="${() => this.changePage(appplugin)}">
<div class="removeIconPos" title="${translate('tabmenu.tm22')}" @click="${() => this.openRemoveApp(appname, appid, appurl)}">
<div class="removeIconPos" title="${translate('tabmenu.tm22')}" @click="${(event) => {
event.stopPropagation();
this.openRemoveApp(appname, appid, appurl)
} }">
<mwc-icon class="removeIcon">backspace</mwc-icon>
</div>
${appurl === 'myapp' ? html`
@ -2218,11 +2277,31 @@ class NavBar extends connect(store)(LitElement) {
const myNewObj = JSON.stringify(this.newMenuFilter)
localStorage.removeItem("myMenuPlugs")
localStorage.setItem("myMenuPlugs", myNewObj)
this.saveSettingToTemp(myNewObj)
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 = ''
@ -2402,11 +2481,11 @@ class AppAvatar extends LitElement {
this.isImageLoaded = true;
}
imageHTMLRes.onerror = () => {
if (this.imageFetches < 4) {
if (this.imageFetches < 1) {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 2000);
}, 5000);
} else {
this.isImageLoaded = false
}
@ -2435,3 +2514,66 @@ class AppAvatar extends LitElement {
}
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`
<mwc-icon>${this.appicon}</mwc-icon>
`}
`
}
}
customElements.define('tab-avatar', TabAvatar)

View File

@ -68,7 +68,9 @@ export default (state = INITIAL_STATE, action) => {
loggedIn: false,
loggingIn: false,
wallet: INITIAL_STATE.wallet,
selectedAddress: INITIAL_STATE.selectedAddress
selectedAddress: INITIAL_STATE.selectedAddress,
accountInfo: INITIAL_STATE.accountInfo
}
case ADD_PLUGIN:
return {

View File

@ -1199,6 +1199,11 @@ class ChatPage extends LitElement {
}, 2000)
}
const chatScrollerElement = this.shadowRoot.querySelector('chat-scroller');
if (chatScrollerElement && chatScrollerElement.disableFetching) {
chatScrollerElement.disableFetching = false
}
return
}
this.isLoadingGoToRepliedMessage = {
@ -1668,12 +1673,20 @@ class ChatPage extends LitElement {
}
})
this.messagesRendered = {
messages: list,
type: 'inBetween',
message: messageToGoTo
}
const lastMsg = list.at(-1)
if(lastMsg){
const count = await parentEpml.request('apiCall', {
type: 'api',
url: `/chat/messages/count?after=${lastMsg.timestamp}&involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=false`
})
this.messagesRendered = {
messages: list,
type: 'inBetween',
message: messageToGoTo,
count
}
}
this.isLoadingOldMessages = false
@ -1727,11 +1740,20 @@ class ChatPage extends LitElement {
}
})
this.messagesRendered = {
messages: list,
type: 'inBetween',
signature: messageToGoTo.signature
}
const lastMsg = list.at(-1)
if(lastMsg){
const count = await parentEpml.request('apiCall', {
type: 'api',
url: `/chat/messages/count?after=${lastMsg.timestamp}&txGroupId=${Number(this._chatId)}&limit=20&reverse=false`
})
this.messagesRendered = {
messages: list,
type: 'inBetween',
signature: messageToGoTo.signature,
count
}
}
this.isLoadingOldMessages = false

View File

@ -345,7 +345,7 @@ class ChatScroller extends LitElement {
this.requestUpdate();
}
async newListMessages(newMessages) {
async newListMessages(newMessages, count) {
let data = [];
const copy = [...newMessages];
copy.forEach((newMessage) => {
@ -368,6 +368,9 @@ class ChatScroller extends LitElement {
// url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`
// })
this.messagesToRender = data;
if (count > 0) {
this.disableAddingNewMessages = true;
}
this.clearLoaders();
this.requestUpdate();
await this.updateComplete;
@ -645,7 +648,7 @@ class ChatScroller extends LitElement {
else if (this.messages.type === 'inBetween')
this.newListMessages(
this.messages.messages,
this.messages.signature
this.messages.count
);
else if (this.messages.type === 'update')
this.replaceMessagesWithUpdateByArray(this.messages.messages);