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

started on custom kvps

This commit is contained in:
PhilReact 2023-10-29 19:18:51 +02:00
parent ce3129ae7e
commit 58bf4aa7c9
3 changed files with 435 additions and 238 deletions

View File

@ -1219,6 +1219,13 @@
"profile3": "Update profile",
"profile4": "Tagline",
"profile5": "Bio",
"profile6": "Wallet Addresses"
"profile6": "Wallet Addresses",
"profile7": "Fill from UI",
"profile8": "Add custom data",
"profile9": "key name",
"profile10": "Fields",
"profile11": "Add new field",
"profile12": "Field name",
"profile13": "Fill out"
}
}

View File

@ -8,11 +8,13 @@ import {
registerTranslateConfig,
} from 'lit-translate';
import '@material/mwc-button';
import '@material/mwc-icon';
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';
import { parentEpml } from '../show-plugin';
class ProfileModalUpdate extends connect(store)(LitElement) {
static get properties() {
@ -26,6 +28,11 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
tagline: { type: String },
bio: { type: String },
wallets: { type: Array },
hasFetchedArrr: { type: Boolean },
isOpenCustomDataModal: { type: Boolean },
customData: { type: Object },
newCustomDataField: {type: Object},
newFieldName: {type: String}
};
}
@ -36,20 +43,40 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
this.nodeUrl = this.getNodeUrl();
this.myNode = this.getMyNode();
this.tagline = '';
(this.bio = ''),
(this.walletList = [
'btc_Address',
'ltc_Address',
'doge_Address',
'dgb_Address',
'rvn_Address',
'arrr_Address',
]);
this.bio = '';
this.walletList = ['btc', 'ltc', 'doge', 'dgb', 'rvn', 'arrr'];
let wallets = {};
this.walletList.forEach((item) => {
wallets[item] = '';
});
this.wallets = wallets;
this.walletsUi = new Map();
let coinProp = {
wallet: null,
};
this.walletList.forEach((c, i) => {
this.walletsUi.set(c, { ...coinProp });
});
this.walletsUi.get('btc').wallet =
window.parent.reduxStore.getState().app.selectedAddress.btcWallet;
this.walletsUi.get('ltc').wallet =
window.parent.reduxStore.getState().app.selectedAddress.ltcWallet;
this.walletsUi.get('doge').wallet =
window.parent.reduxStore.getState().app.selectedAddress.dogeWallet;
this.walletsUi.get('dgb').wallet =
window.parent.reduxStore.getState().app.selectedAddress.dgbWallet;
this.walletsUi.get('rvn').wallet =
window.parent.reduxStore.getState().app.selectedAddress.rvnWallet;
this.walletsUi.get('arrr').wallet =
window.parent.reduxStore.getState().app.selectedAddress.arrrWallet;
this.hasFetchedArrr = false;
this.isOpenCustomDataModal = false;
this.customData = {};
this.newCustomDataKey = ""
this.newCustomDataField = {};
this.newFieldName = ''
}
static get styles() {
@ -75,7 +102,6 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
color: var(--chat-bubble-msg-color);
box-sizing: border-box;
}
.input::selection {
background-color: var(--mdc-theme-primary);
color: white;
@ -205,7 +231,85 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
`;
}
firstUpdated() {}
async updated(changedProperties) {
if (
changedProperties &&
changedProperties.has('editContent') &&
this.editContent
) {
this.bio = this.editContent.bio ?? '';
this.tagline = this.editContent.tagline ?? '';
this.wallets = this.editContent.wallets ?? {};
this.requestUpdate();
}
}
async firstUpdated() {
try {
await this.fetchWalletAddress('arrr');
} catch (error) {
console.log({ error });
} finally {
}
}
async fetchWalletAddress(coin) {
console.log({ coin });
switch (coin) {
case 'arrr':
console.log('arrr');
const arrrWalletName = `${coin}Wallet`;
const res2 = await parentEpml.request('apiCall', {
url: `/crosschain/${coin}/syncstatus?apiKey=${this.myNode.apiKey}`,
method: 'POST',
body: `${
window.parent.reduxStore.getState().app.selectedAddress[
arrrWalletName
].seed58
}`,
});
if (res2 !== null && res2 !== 'Synchronized') {
// Check again shortly after
await new Promise((resolve) => setTimeout(resolve, 2000));
this.fetchWalletAddress('arrr');
// No need to make balance or transaction list calls yet
return;
}
let res = await parentEpml.request('apiCall', {
url: `/crosschain/${coin}/walletaddress?apiKey=${this.myNode.apiKey}`,
method: 'POST',
body: `${
window.parent.reduxStore.getState().app.selectedAddress[
arrrWalletName
].seed58
}`,
});
if (res != null && res.error != 1201 && res.length === 78) {
this.arrrWalletAddress = res;
this.hasFetchedArrr = true;
}
break;
default:
// Not used for other coins yet
break;
}
}
getSelectedWalletAddress(wallet) {
switch (wallet) {
case 'arrr':
// Use address returned by core API
return this.arrrWalletAddress;
default:
// Use locally derived address
return this.walletsUi.get(wallet).wallet.address;
}
}
getNodeUrl() {
const myNode =
@ -226,9 +330,58 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
return myNode;
}
clearFields() {}
clearFields() {
this.bio = '';
this.tagline = '';
this.wallets = {};
}
fillAddress(coin) {
const address = this.getSelectedWalletAddress(coin);
if (address) {
this.wallets = {
...this.wallets,
[coin]: address,
};
}
}
async saveProfile() {
try {
const data = {
version: 1,
tagline: this.tagline,
bio: this.bio,
wallets: this.wallets,
};
await this.onSubmit(data);
this.setIsOpen(false);
this.clearFields();
this.onClose();
} catch (error) {}
}
removeField(key){
const copyObj = {...this.newCustomDataField}
delete copyObj[key]
this.newCustomDataField = copyObj
}
addField(){
const copyObj = {...this.newCustomDataField}
copyObj[this.newFieldName] = ''
this.newCustomDataField = copyObj
this.newFieldName = ""
}
addCustomData(){
const copyObj = {...this.customData}
copyObj[this.newCustomDataKey] = this.newCustomDataField
this.customData = copyObj
}
render() {
console.log('modal');
return html`
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
<div class="modal-content">
@ -280,18 +433,42 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
<div style="display: flex;flex-direction: column;">
${Object.keys(this.wallets).map((key) => {
return html`
<input
id=${key}
placeholder=${key}
class="input"
.value=${this.wallets[key]}
@change=${(e) => {
this.wallets = {
...this.wallets,
[key]: e.target.value,
};
}}
/>
<div
style="display:flex;justify-content:center;flex-direction:column"
>
<label
for=${key}
id="taglineLabel"
style="color: var(--black);font-size:16px"
>
${key}
</label>
<div
style="display:flex;gap:15px;align-items:center"
>
<input
id=${key}
placeholder=${`${key} ${get(
'settings.address'
)}`}
class="input"
.value=${this.wallets[key]}
@change=${(e) => {
this.wallets = {
...this.wallets,
[key]: e.target.value,
};
}}
/>
<mwc-icon
@click=${() =>
this.fillAddress(key)}
style="color:var(--black);cursor:pointer"
>upload_2</mwc-icon
>
</div>
</div>
`;
})}
</div>
@ -310,15 +487,156 @@ class ProfileModalUpdate extends connect(store)(LitElement) {
>
${translate('general.close')}
</button>
<div style="display:flex;gap:10px;align-items:center">
<button
?disabled="${this.isLoading}"
class="modal-button"
@click=${() => {
this.isOpenCustomDataModal = true;
}}
>
${translate('profile.profile8')}
</button>
<button
?disabled="${this.isLoading}"
class="modal-button"
@click=${() => {
this.saveProfile();
}}
>
${translate('profile.profile3')}
</button>
</div>
</div>
</div>
</div>
<!-- add custom vars -->
<div
class="modal-overlay ${this.isOpenCustomDataModal
? ''
: 'hidden'}"
style="z-index:1001"
>
<div class="modal-content">
<div class="inner-content">
<div style="height:15px"></div>
<div
style="display:flex;justify-content:center;flex-direction:column"
>
<label
for="key-name"
id="taglineLabel"
style="color: var(--black);font-size:16px"
>
${translate('profile.profile9')}
</label>
<div
style="display:flex;gap:15px;align-items:center"
>
<input
id="key-name"
placeholder=${`${get(
'profile.profile9'
)}`}
class="input"
.value=${this.newCustomDataKey}
@change=${(e) => {
this.newCustomDataKey = e.target.value
}}
/>
</div>
</div>
<div style="height:15px"></div>
<p>${get('profile.profile10')}</p>
<div style="display: flex;flex-direction: column;">
${Object.keys(this.newCustomDataField).map((key) => {
return html`
<div
style="display:flex;justify-content:center;flex-direction:column"
>
<label
for=${key}
id="taglineLabel"
style="color: var(--black);font-size:16px"
>
${key}
</label>
<div
style="display:flex;gap:15px;align-items:center"
>
<input
id=${key}
placeholder=${`${get('profile.profile13')}`}
class="input"
.value=${this.newCustomDataField[key]}
@change=${(e) => {
this.newCustomDataField = {
...this.newCustomDataField,
[key]: e.target.value,
};
}}
/>
<mwc-icon
@click=${() =>
this.removeField(key)}
style="color:var(--black);cursor:pointer"
>remove</mwc-icon
>
</div>
</div>
`;
})}
</div>
<div style="height:15px"></div>
<div style="width:100%;display:flex;justify-content:center;gap:10px">
<input
placeholder=${`${get('profile.profile12')}`}
class="input"
.value=${this.newFieldName}
@change=${(e) => {
this.newFieldName = e.target.value
}}
/>
<button
class="modal-button"
@click=${() => {
this.addField();
}}
>
${translate('profile.profile11')}
</button>
</div>
</div>
<div
style="display:flex;justify-content:space-between;align-items:center;margin-top:20px"
>
<button
class="modal-button-red"
?disabled="${this.isLoading}"
@click="${() => {
this.isOpenCustomDataModal = false
this.newCustomDataKey = ""
this.newCustomDataField = {};
}}"
>
${translate('general.close')}
</button>
<button
?disabled="${this.isLoading}"
class="modal-button"
@click=${() => {
this.addFriend();
this.addCustomData();
}}
>
${translate('profile.profile3')}
${translate('profile.profile8')}
</button>
</div>
</div>

View File

@ -21,7 +21,7 @@ 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'
import './profile-modal-update.js';
class ProfileQdn extends connect(store)(LitElement) {
static get properties() {
@ -34,8 +34,9 @@ class ProfileQdn extends connect(store)(LitElement) {
isSaving: { type: Boolean },
fee: { type: Object },
name: { type: String },
isOpenProfileModalUpdate: {type: Boolean},
editContent: {type: Object}
isOpenProfileModalUpdate: { type: Boolean },
editContent: { type: Object },
profileData: {type: Object}
};
}
@ -58,8 +59,9 @@ class ProfileQdn extends connect(store)(LitElement) {
this.fee = null;
this.name = undefined;
this.uid = new ShortUniqueId();
this.isOpenProfileModalUpdate = false
this.editContent = null
this.isOpenProfileModalUpdate = false;
this.editContent = null;
this.profileData = null
}
static styles = css`
.header {
@ -157,24 +159,14 @@ class ProfileQdn extends connect(store)(LitElement) {
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 url = `${this.nodeUrl}/arbitrary/${dataItem.service}/${dataItem.name}/${dataItem.identifier}`;
const res = await fetch(url);
const data = await res.text();
const data = await res.json();
if (data.error) throw new Error('Cannot retrieve your data from qdn');
const decryptedData = decryptGroupData(data);
const decryptedDataToBase64 = uint8ArrayToObject(decryptedData);
return decryptedDataToBase64;
return data
}
async getMyFollowedNames() {
@ -205,122 +197,9 @@ class ProfileQdn extends connect(store)(LitElement) {
}
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,
},
};
console.log('hello', response)
if(response){
this.profileData = response
}
}
@ -342,7 +221,9 @@ class ProfileQdn extends connect(store)(LitElement) {
const res = await fetch(url);
let data = '';
try {
console.log({res})
data = await res.json();
console.log({data})
if (Array.isArray(data)) {
data = data.filter(
(item) => item.identifier === 'qortal_profile'
@ -353,8 +234,9 @@ class ProfileQdn extends connect(store)(LitElement) {
const dataItem = data[0];
try {
const response = await this.getRawData(dataItem);
if (response.version) {
// this.setValues(response, dataItem);
console.log({response})
if (response.wallets) {
this.setValues(response, dataItem);
} else {
this.error = 'Cannot get saved user settings';
}
@ -369,6 +251,7 @@ class ProfileQdn extends connect(store)(LitElement) {
this.error = 'Unable to perform query';
}
} catch (error) {
console.log({error})
data = {
error: 'No resource found',
};
@ -416,8 +299,9 @@ class ProfileQdn extends connect(store)(LitElement) {
};
}
async saveToQdn() {
async saveToQdn(data) {
try {
console.log({data})
this.isSaving = true;
if (this.resourceExists === true && this.error)
throw new Error('Unable to save');
@ -425,71 +309,39 @@ class ProfileQdn extends connect(store)(LitElement) {
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 identifer = 'qortal_profile';
const filename = 'qortal_profile.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;
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;
}
for (const key of Object.keys(data)) {
if (key.includes('-private')) {
const toBase64 = await objectToBase64(newObject);
const encryptedData = encryptDataGroup({
data64: toBase64,
publicKeys: [],
});
newObject[key] = encryptedData;
} else {
newObject[key] = data[key];
}
}
const newObjectToBase64 = await objectToBase64(newObject);
const encryptedData = encryptDataGroup({
data64: newObjectToBase64,
publicKeys: [],
});
// const encryptedData = encryptDataGroup({
// data64: newObjectToBase64,
// publicKeys: [],
// });
const worker = new WebWorker2();
try {
const resPublish = await publishData({
registeredName: encodeURIComponent(name),
file: encryptedData,
service: 'DOCUMENT_PRIVATE',
file: newObjectToBase64,
service: 'DOCUMENT',
identifier: encodeURIComponent(identifer),
parentEpml: parentEpml,
uploadType: 'file',
@ -503,17 +355,17 @@ class ProfileQdn extends connect(store)(LitElement) {
});
this.resourceExists = true;
this.setValues(newObject, {
updated: Date.now(),
});
localStorage.setItem('temp-settings-data', JSON.stringify({}));
this.valuesToBeSavedOnQdn = {};
this.profileData = data
// this.setValues(newObject, {
// updated: Date.now(),
// });
worker.terminate();
} catch (error) {
worker.terminate();
}
} catch (error) {
console.log({ error });
throw new Error(error.message)
} finally {
this.isSaving = false;
}
@ -541,15 +393,12 @@ class ProfileQdn extends connect(store)(LitElement) {
super.disconnectedCallback();
}
publishProfile(){
}
onClose(){
this.isOpenProfileModalUpdate = false
onClose() {
this.isOpenProfileModalUpdate = false;
this.editContent = null
}
render() {
console.log('sup profile2', this.name);
console.log('sup profile2', {profileData: this.profileData});
return html`
${this.isSaving ||
(!this.error && this.resourceExists === undefined)
@ -641,10 +490,33 @@ class ProfileQdn extends connect(store)(LitElement) {
</div>
</popover-component>
`
: html`
<div style="user-select:none;cursor:pointer" @click=${()=> {
this.isOpenProfileModalUpdate = !this.isOpenProfileModalUpdate
}}>
: this.error ? html`
<div
style="user-select:none;cursor:pointer;opacity:0.5"
>
<avatar-component
.resource=${{
name: this.name,
service: 'THUMBNAIL',
identifier: 'qortal_avatar',
}}
name=${this.name}
></avatar-component>
</div>
` : html`
<div
style="user-select:none;cursor:pointer"
@click=${() => {
if(this.resourceExists && this.profileData){
this.editContent = this.profileData
} else if(this.resourceExists && !this.profileData){
return
}
this.isOpenProfileModalUpdate =
!this.isOpenProfileModalUpdate;
}}
>
<avatar-component
.resource=${{
name: this.name,
@ -657,13 +529,13 @@ class ProfileQdn extends connect(store)(LitElement) {
`}
<profile-modal-update
?isOpen=${this.isOpenProfileModalUpdate}
.setIsOpen=${(val)=> {
this.isOpenProfileModalUpdate = val
?isOpen=${this.isOpenProfileModalUpdate}
.setIsOpen=${(val) => {
this.isOpenProfileModalUpdate = val;
}}
.onSubmit=${(val, isEdit)=> this.publishProfile(val, isEdit)}
.editContent=${this.editContent}
.onClose=${()=> this.onClose()}
.onSubmit=${this.saveToQdn}
.editContent=${this.editContent}
.onClose=${() => this.onClose()}
></profile-modal-update>
`;
}