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

started profile

This commit is contained in:
PhilReact 2023-10-25 01:17:14 +03:00
parent b2a5b13a22
commit ed5253d6d0
10 changed files with 1394 additions and 11 deletions

View File

@ -7,24 +7,16 @@
"version": 1, "version": 1,
"updated": 1696646223261, "updated": 1696646223261,
"title": "Q-Blog Post creations", "title": "Q-Blog Post creations",
"description": "blablabla", "description": "Get your friends Q-Blog posts on your feed",
"search": { "search": {
"query": "-post-", "query": "-post-",
"identifier": "q-blog-", "identifier": "q-blog-",
"service": "BLOG_POST", "service": "BLOG_POST",
"exactmatchnames": true "exactmatchnames": true
}, },
"click": "qortal://APP/Q-Blog/$${resource.name}$$/$${customParams.blogId}$$/$${customParams.shortIdentifier}$$", "click": "qortal://APP/Q-Blog/$${resource.name}$$/blog/$${resource.identifier}$$",
"display": { "display": {
"title": "$${rawdata.title}$$" "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;"
} }
} }
] ]

View File

@ -1212,5 +1212,12 @@
"saving2": "Nothing to save", "saving2": "Nothing to save",
"saving3": "Save unsaved changes", "saving3": "Save unsaved changes",
"saving4": "Undo changes" "saving4": "Undo changes"
},
"profile": {
"profile1": "You do not have a name",
"profile2": "Go to name registration",
"profile3": "Update profile",
"profile4": "Tagline",
"profile5": "Bio"
} }
} }

View File

@ -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;

View File

@ -46,6 +46,7 @@ import './notification-view/notification-bell-general.js'
import './friends-view/friends-side-panel-parent.js' import './friends-view/friends-side-panel-parent.js'
import './friends-view/save-settings-qdn.js' import './friends-view/save-settings-qdn.js'
import './friends-view/core-sync-status.js' import './friends-view/core-sync-status.js'
import './friends-view/profile.js'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
class AppView extends connect(store)(LitElement) { class AppView extends connect(store)(LitElement) {
@ -584,6 +585,7 @@ class AppView extends connect(store)(LitElement) {
</span> </span>
</div> </div>
<div style="display:flex;align-items:center;gap:20px"> <div style="display:flex;align-items:center;gap:20px">
<profile-qdn></profile-qdn>
<friends-side-panel-parent></friends-side-panel-parent> <friends-side-panel-parent></friends-side-panel-parent>
<notification-bell></notification-bell> <notification-bell></notification-bell>
<notification-bell-general></notification-bell-general> <notification-bell-general></notification-bell-general>

View File

@ -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
}

View File

@ -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`
<div>
${this.status.status !== 'READY'
? html`
<mwc-icon style="user-select:none;"
>account_circle</mwc-icon
>
`
: ''}
${this.status.status === 'READY'
? html`
<div
style="height: 24px;width: 24px;overflow: hidden;"
>
<img
src="${this
.nodeUrl}/arbitrary/THUMBNAIL/${this
.name}/qortal_avatar?async=true&apiKey=${this
.myNode.apiKey}"
style="width:100%; height:100%;border-radius:50%"
onerror="this.onerror=null; this.src='/img/incognito.png';"
/>
</div>
`
: ''}
</div>
`;
}
}
customElements.define('avatar-component', AvatarComponent);

View File

@ -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`
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
<div class="modal-content">
<div class="inner-content">
<div style="height:15px"></div>
<div style="display: flex;flex-direction: column;">
<label
for="tagline"
id="taglineLabel"
style="color: var(--black);"
>
${get('profile.profile4')}
</label>
<textarea
class="input"
@change=${(e) => {
this.tagline = e.target.value
}}
.value=${this.tagline}
?disabled=${this.isLoading}
id="tagline"
placeholder="${translate('profile.profile4')}"
rows="3"
></textarea>
</div>
<div style="height:15px"></div>
<div style="display: flex;flex-direction: column;">
<label
for="bio"
id="bioLabel"
style="color: var(--black);"
>
${get('profile.profile5')}
</label>
<textarea
class="input"
@change=${(e) => {
this.bio = e.target.value
}}
.value=${this.bio}
?disabled=${this.isLoading}
id="bio"
placeholder="${translate('profile.profile5')}"
rows="3"
></textarea>
</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.setIsOpen(false);
this.clearFields();
this.onClose();
}}"
>
${translate('general.close')}
</button>
<button
?disabled="${this.isLoading}"
class="modal-button"
@click=${() => {
this.addFriend();
}}
>
${translate('profile.profile3')}
</button>
</div>
</div>
</div>
`;
}
}
customElements.define('profile-modal-update', ProfileModalUpdate);

View File

@ -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&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,
};
}
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`
<paper-spinner-lite
active
style="display: block; margin: 0 auto;"
></paper-spinner-lite>
`
: !this.name
? html`
<mwc-icon
id="profile-icon"
class=${Object.values(this.valuesToBeSavedOnQdn)
.length > 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</mwc-icon
>
<vaadin-tooltip
for="profile-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="profile-icon" message="">
<div style="margin-bottom:20px">
<p style="margin:10px 0px; font-size:16px">
${`${get('profile.profile1')}`}
</p>
</div>
<div
style="display:flex;justify-content:center;gap:10px"
>
<div
class="accept-button"
@click="${() => {
store.dispatch(
setNewTab({
url: `group-management`,
id: this.uid.rnd(),
myPlugObj: {
url: 'name-registration',
domain: 'core',
page: 'name-registration/index.html',
title: 'Name Registration',
icon: 'vaadin:user-check',
mwcicon: 'manage_accounts',
pluginNumber:
'plugin-qCmtXAQmtu',
menus: [],
parent: false,
},
openExisting: true,
})
);
const popover =
this.shadowRoot.querySelector(
'popover-component'
);
if (popover) {
popover.closePopover();
}
}}"
>
${translate('profile.profile2')}
</div>
</div>
</popover-component>
`
: html`
<div style="user-select:none;cursor:pointer" @click=${()=> {
this.isOpenProfileModalUpdate = !this.isOpenProfileModalUpdate
}}>
<avatar-component
.resource=${{
name: this.name,
service: 'THUMBNAIL',
identifier: 'qortal_avatar',
}}
name=${this.name}
></avatar-component>
</div>
`}
<profile-modal-update
?isOpen=${this.isOpenProfileModalUpdate}
.setIsOpen=${(val)=> {
this.isOpenProfileModalUpdate = val
}}
.onSubmit=${(val, isEdit)=> this.publishProfile(val, isEdit)}
.editContent=${this.editContent}
.onClose=${()=> this.onClose()}
></profile-modal-update>
`;
}
}
customElements.define('profile-qdn', ProfileQdn);

View File

@ -3,12 +3,13 @@ import '@material/mwc-icon';
import './friends-side-panel.js'; import './friends-side-panel.js';
import { connect } from 'pwa-helpers'; import { connect } from 'pwa-helpers';
import { store } from '../../store.js'; 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 '@polymer/paper-spinner/paper-spinner-lite.js';
import '@vaadin/tooltip'; import '@vaadin/tooltip';
import { get, translate } from 'lit-translate'; import { get, translate } from 'lit-translate';
import { import {
decryptGroupData, decryptGroupData,
encryptDataGroup, encryptDataGroup,
objectToBase64, objectToBase64,
uint8ArrayToBase64, uint8ArrayToBase64,

View File

@ -337,6 +337,7 @@ class ShowPlugin extends connect(store)(LitElement) {
} }
render() { render() {
console.log('this.tabs', this.tabs)
const plugSrc = (myPlug) => { const plugSrc = (myPlug) => {
return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}` return myPlug === undefined ? 'about:blank' : `${window.location.origin}/plugin/${myPlug.domain}/${myPlug.page}${this.linkParam}`
} }