4
1
mirror of https://github.com/Qortal/qortal-ui.git synced 2025-02-14 11:15:50 +00:00

extra fixes

This commit is contained in:
PhilReact 2023-10-11 21:59:34 -05:00
parent 35b71ea311
commit 435f6b1905
9 changed files with 584 additions and 220 deletions

31
blog-test.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "Q-Blog",
"defaultFeedIndex": 0,
"feed": [
{
"id": "post-creation",
"version": 1,
"updated": 1696646223261,
"title": "Q-Blog Post creations",
"description": "blablabla",
"search": {
"query": "-post-",
"identifier": "q-blog-",
"service": "BLOG_POST",
"exactmatchnames": true
},
"click": "qortal://APP/Q-Blog/$${resource.name}$$/$${customParams.blogId}$$/$${customParams.shortIdentifier}$$",
"display": {
"title": "$${rawdata.title}$$"
},
"customParams": {
"blogId": "**methods.getBlogId(resource)**",
"shortIdentifier": "**methods.getShortId(resource)**"
},
"methods": {
"getShortId": "return resource.identifier.split('-post-')[1];",
"getBlogId": "const arr = resource.identifier.split('-post-'); const id = arr[0]; return id.startsWith('q-blog-') ? id.substring(7) : id;"
}
}
]
}

View File

@ -1190,6 +1190,9 @@
"friend10": "Edit friend", "friend10": "Edit friend",
"friend11": "Following", "friend11": "Following",
"friend12": "Friends", "friend12": "Friends",
"friend13": "Feed" "friend13": "Feed",
"friend14": "Remove friend",
"friend15": "Feed settings",
"friend16": "Select the Q-Apps you want updates from, especially those related to your friends."
} }
} }

View File

@ -10,8 +10,10 @@ import {
import '@material/mwc-button'; import '@material/mwc-button';
import '@material/mwc-dialog'; import '@material/mwc-dialog';
import '@material/mwc-checkbox'; import '@material/mwc-checkbox';
import { connect } from 'pwa-helpers';
import { store } from '../../store';
class AddFriendsModal extends LitElement { class AddFriendsModal extends connect(store)(LitElement) {
static get properties() { static get properties() {
return { return {
isOpen: { type: Boolean }, isOpen: { type: Boolean },
@ -21,9 +23,11 @@ class AddFriendsModal extends LitElement {
alias: { type: String }, alias: { type: String },
willFollow: { type: Boolean }, willFollow: { type: Boolean },
notes: { type: String }, notes: { type: String },
onSubmit: {attribute: false}, onSubmit: { attribute: false },
editContent: {type: Object}, editContent: { type: Object },
onClose: {attribute: false} onClose: { attribute: false },
mySelectedFeeds: { type: Array },
availableFeeedSchemas: {type: Array}
}; };
} }
@ -34,18 +38,22 @@ class AddFriendsModal extends LitElement {
this.alias = ''; this.alias = '';
this.willFollow = true; this.willFollow = true;
this.notes = ''; this.notes = '';
this.nodeUrl = this.getNodeUrl();
this.myNode = this.getMyNode();
this.mySelectedFeeds = [];
this.availableFeeedSchemas = []
} }
static get styles() { static get styles() {
return css` return css`
* { * {
--mdc-theme-primary: rgb(3, 169, 244); --mdc-theme-primary: rgb(3, 169, 244);
--mdc-theme-secondary: var(--mdc-theme-primary); --mdc-theme-secondary: var(--mdc-theme-primary);
--mdc-theme-surface: var(--white); --mdc-theme-surface: var(--white);
--mdc-dialog-content-ink-color: var(--black); --mdc-dialog-content-ink-color: var(--black);
--mdc-dialog-min-width: 400px; --mdc-dialog-min-width: 400px;
--mdc-dialog-max-width: 1024px; --mdc-dialog-max-width: 1024px;
} }
.input { .input {
width: 90%; width: 90%;
outline: 0; outline: 0;
@ -69,9 +77,36 @@ class AddFriendsModal extends LitElement {
color: var(--black); color: var(--black);
} }
.close-button { .modal-button {
display: block; font-family: Roboto, sans-serif;
--mdc-theme-primary: red; 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 { .checkbox-row {
position: relative; position: relative;
@ -83,167 +118,331 @@ class AddFriendsModal extends LitElement {
color: var(--black); color: var(--black);
} }
.modal-overlay { .modal-overlay {
display: block; 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; position: fixed;
top: 50vh; top: 0;
left: 50vw; left: 0;
transform: translate(-50%, -50%); width: 100vw;
background-color: var(--mdc-theme-surface); height: 100vh;
width: 80vw; background-color: rgba(
max-width: 600px; 0,
padding: 20px; 0,
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px; 0,
z-index: 1001; 0.5
border-radius: 5px; ); /* Semi-transparent backdrop */
max-height: 80vh; z-index: 1000;
overflow: auto; }
}
.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;
max-height: 80vh;
overflow: auto;
}
.modal-overlay.hidden { .modal-overlay.hidden {
display: none; 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;
}
`; `;
} }
firstUpdated() {} firstUpdated() {}
clearFields(){ 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() {
this.alias = ''; this.alias = '';
this.willFollow = true; this.willFollow = true;
this.notes = ''; this.notes = '';
} }
addFriend(){ addFriend() {
this.onSubmit({
name: this.userSelected.name,
alias: this.alias,
notes: this.notes,
willFollow: this.willFollow,
mySelectedFeeds: this.mySelectedFeeds
this.onSubmit({ });
name: this.userSelected.name, this.clearFields();
alias: this.alias, this.onClose();
notes: this.notes, }
willFollow: this.willFollow
})
this.clearFields()
this.onClose()
}
removeFriend() {
this.onSubmit(
{
name: this.userSelected.name,
alias: this.alias,
notes: this.notes,
willFollow: this.willFollow,
mySelectedFeeds: this.mySelectedFeeds
},
true
);
this.clearFields();
this.onClose();
}
async updated(changedProperties) { async updated(changedProperties) {
if (changedProperties && changedProperties.has('editContent') && this.editContent) { if (
changedProperties &&
changedProperties.has('editContent') &&
this.editContent
) {
this.userSelected = { this.userSelected = {
name: this.editContent.name ?? '', name: this.editContent.name ?? '',
} };
this.notes = this.editContent.notes ?? '' this.notes = this.editContent.notes ?? '';
this.willFollow = this.editContent.willFollow ?? true this.willFollow = this.editContent.willFollow ?? true;
this.alias = this.editContent.alias ?? '' this.alias = this.editContent.alias ?? '';
}
if (
changedProperties &&
changedProperties.has('isOpen') && this.isOpen
) {
this.getAvailableFeedSchemas()
} }
}
async getAvailableFeedSchemas() {
try {
const url = `${this.nodeUrl}/arbitrary/resources/search?service=DOCUMENT&identifier=ui_schema_feed&prefix=true`;
const res = await fetch(url);
const data = await res.json();
if (data.error === 401) {
this.availableFeeedSchemas = [];
} else {
const result = data.filter(
(item) => item.identifier === 'ui_schema_feed'
);
this.availableFeeedSchemas = result;
}
this.userFoundModalOpen = true;
} catch (error) {}
} }
render() { render() {
return html` return html`
<div class="modal-overlay ${this.isOpen ? '' : 'hidden'}"> <div class="modal-overlay ${this.isOpen ? '' : 'hidden'}">
<div class="modal-content"> <div class="modal-content">
<div style="text-align:center"> <div style="text-align:center">
<h1>${this.editContent ? translate('friends.friend10') : translate('friends.friend2')}</h1> <h1>
<hr /> ${this.editContent
</div> ? translate('friends.friend10')
<p>${translate('friends.friend3')}</p> : translate('friends.friend2')}
<div class="checkbox-row"> </h1>
<label <hr />
for="willFollow" </div>
id="willFollowLabel" <p>${translate('friends.friend3')}</p>
style="color: var(--black);" <div class="checkbox-row">
<label
for="willFollow"
id="willFollowLabel"
style="color: var(--black);"
>
${get('friends.friend5')}
</label>
<mwc-checkbox
style="margin-right: -15px;"
id="willFollow"
@change=${(e) => {
this.willFollow = e.target.checked;
}}
?checked=${this.willFollow}
></mwc-checkbox>
</div>
<div style="height: 15px"></div>
<div style="display: flex;flex-direction: column;">
<label
for="name"
id="nameLabel"
style="color: var(--black);"
>
${get('login.name')}
</label>
<input
id="name"
class="input"
?disabled=${true}
value=${this.userSelected
? this.userSelected.name
: ''}
/>
</div>
<div style="height: 15px"></div>
<div style="display: flex;flex-direction: column;">
<label
for="alias"
id="aliasLabel"
style="color: var(--black);"
>
${get('friends.friend6')}
</label>
<input
id="alias"
placeholder=${translate('friends.friend7')}
class="input"
value=${this.alias}
@change=${(e) => (this.alias = e.target.value)}
/>
</div>
<div style="height: 15px"></div>
<div style="margin-bottom:0;">
<textarea
class="input"
@change=${(e) => (this.notes = e.target.value)}
value=${this.notes}
?disabled=${this.isLoading}
id="messageBoxAddFriend"
placeholder="${translate('friends.friend4')}"
rows="3"
></textarea>
</div>
<div style="height: 15px"></div>
<h2>${translate('friends.friend15')}</h2>
<div style="margin-bottom:0;">
<p>${translate('friends.friend16')}</p>
</div>
<div>
${this.availableFeeedSchemas.map((schema) => {
const isAlreadySelected = this.mySelectedFeeds.find(
(item) => item.name === schema.name
);
let avatarImgApp;
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${schema.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
avatarImgApp = html`<img
src="${avatarUrl2}"
style="max-width:100%; max-height:100%;"
onerror="this.onerror=null; this.src='/img/incognito.png';"
/>`;
return html`
<div
class="app-name"
style="background:${isAlreadySelected ? 'lightblue': ''}"
@click=${() => {
const copymySelectedFeeds = [
...this.mySelectedFeeds,
];
const findIndex =
copymySelectedFeeds.findIndex(
(item) =>
item.name === schema.name
);
if (findIndex === -1) {
copymySelectedFeeds.push({
name: schema.name,
identifier: schema.identifier,
service: schema.service,
});
this.mySelectedFeeds =
copymySelectedFeeds;
} else {
this.mySelectedFeeds =
copymySelectedFeeds.filter(
(item) =>
item.name !==
schema.name
);
}
}}
>
<div class="avatar">${avatarImgApp}</div>
<span
style="color:${isAlreadySelected ? 'var(--white)': 'var(--black)'};font-size:16px"
>${schema.name}</span
>
</div>
`;
})}
</div>
<div
style="display:flex;justify-content:space-between;align-items:center;margin-top:20px"
> >
${get('friends.friend5')} <button
</label> class="modal-button-red"
<mwc-checkbox ?disabled="${this.isLoading}"
style="margin-right: -15px;" @click="${() => {
id="willFollow" this.setIsOpen(false);
@change=${(e) => { this.clearFields();
this.willFollow = e.target.checked; this.onClose();
}} }}"
?checked=${this.willFollow} >
></mwc-checkbox> ${translate('general.close')}
</div> </button>
<div style="height: 15px"></div> ${this.editContent
<div style="display: flex;flex-direction: column;"> ? html`
<label <button
for="name" ?disabled="${this.isLoading}"
id="nameLabel" class="modal-button-red"
style="color: var(--black);" @click=${() => {
> this.removeFriend();
${get('login.name')} }}
</label> >
<input ${translate('friends.friend14')}
id="name" </button>
class="input" `
?disabled=${true} : ''}
value=${this.userSelected ? this.userSelected.name : ''}
/>
</div>
<div style="height: 15px"></div>
<div style="display: flex;flex-direction: column;">
<label
for="alias"
id="aliasLabel"
style="color: var(--black);"
>
${get('friends.friend6')}
</label>
<input
id="alias"
placeholder=${translate('friends.friend7')}
class="input"
value=${this.alias}
@change=${(e) => (this.alias = e.target.value)}
/>
</div>
<div style="height: 15px"></div>
<div style="margin-bottom:0;">
<textarea
class="input"
@change=${(e) => (this.notes = e.target.value)}
value=${this.notes}
?disabled=${this.isLoading}
id="messageBoxAddFriend"
placeholder="${translate('friends.friend4')}"
rows="3"
></textarea>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:20px">
<mwc-button <button
?disabled="${this.isLoading}" ?disabled="${this.isLoading}"
slot="secondaryAction" class="modal-button"
@click="${() => { @click=${() => {
this.setIsOpen(false) this.addFriend();
this.clearFields() }}
this.onClose() >
} }" ${this.editContent
class="close-button" ? translate('friends.friend10')
> : translate('friends.friend2')}
${translate('general.close')} </button>
</mwc-button> </div>
<mwc-button
?disabled="${this.isLoading}"
slot="primaryAction"
@click=${() => {
this.addFriend();
}}
>${this.editContent ? translate('friends.friend10') : translate('friends.friend2')}
</mwc-button>
</div>
</div>
</div> </div>
</div>
`; `;
} }
} }

View File

@ -13,7 +13,10 @@ import { store } from '../../store';
import { setNewTab } from '../../redux/app/app-actions'; import { setNewTab } from '../../redux/app/app-actions';
import ShortUniqueId from 'short-unique-id'; import ShortUniqueId from 'short-unique-id';
const requestQueue = new RequestQueueWithPromise(5); const requestQueue = new RequestQueueWithPromise(3);
const requestQueueRawData = new RequestQueueWithPromise(3);
const requestQueueStatus = new RequestQueueWithPromise(3);
export class FeedItem extends connect(store)(LitElement) { export class FeedItem extends connect(store)(LitElement) {
static get properties() { static get properties() {
@ -140,7 +143,6 @@ export class FeedItem extends connect(store)(LitElement) {
this.status = { this.status = {
status: '' status: ''
} }
this.url = ""
this.isReady = false this.isReady = false
this.nodeUrl = this.getNodeUrl() this.nodeUrl = this.getNodeUrl()
this.myNode = this.getMyNode() this.myNode = this.getMyNode()
@ -195,21 +197,23 @@ getMyNode(){
async fetchVideoUrl() { async fetchVideoUrl() {
this.fetchResource() this.fetchResource()
this.url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?async=true&apiKey=${this.myNode.apiKey}`
} }
async getRawData(){ async getRawData(){
const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}` const url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`
const response2 = await fetch(url, { return await requestQueueRawData.enqueue(()=> {
method: 'GET', return axios.get(url)
headers: {
'Content-Type': 'application/json'
}
}) })
// const response2 = await fetch(url, {
// method: 'GET',
// headers: {
// 'Content-Type': 'application/json'
// }
// })
const responseData2 = await response2.json() // const responseData2 = await response2.json()
return responseData2 // return responseData2
} }
updateDisplayWithPlaceholders(display, resource, rawdata) { updateDisplayWithPlaceholders(display, resource, rawdata) {
@ -244,13 +248,16 @@ getMyNode(){
let isCalling = false let isCalling = false
let percentLoaded = 0 let percentLoaded = 0
let timer = 24 let timer = 24
const response = await axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`) 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'){ if(response && response.data && response.data.status === 'READY'){
const rawData = await this.getRawData() const rawData = await this.getRawData()
console.log({rawData})
const object = { const object = {
title: "$${rawdata.title}$$", ...this.resource.schema.display
} }
this.updateDisplayWithPlaceholders(object, {},rawData) this.updateDisplayWithPlaceholders(object, {},rawData.data)
this.feedItem = object this.feedItem = object
this.status = response.data this.status = response.data
@ -301,8 +308,12 @@ getMyNode(){
// check if progress is 100% and clear interval if true // check if progress is 100% and clear interval if true
if (res.status === 'READY') { if (res.status === 'READY') {
const rawData = await this.getRawData()
this.feedItem = await this.getRawData() const object = {
...this.resource.schema.display
}
this.updateDisplayWithPlaceholders(object, {},rawData.data)
this.feedItem = object
clearInterval(intervalId) clearInterval(intervalId)
this.status = res this.status = res
this.isReady = true this.isReady = true
@ -312,11 +323,7 @@ getMyNode(){
async _fetchImage() { async _fetchImage() {
try { try {
this.fetchVideoUrl({ this.fetchVideoUrl()
name: this.resource.name,
service: this.resource.service,
identifier: this.resource.identifier
})
this.fetchStatus() this.fetchStatus()
} catch (error) { /* empty */ } } catch (error) { /* empty */ }
} }

View File

@ -90,7 +90,6 @@ export class FriendItemActions extends connect(store)(LitElement) {
} }
attachToTarget(target) { attachToTarget(target) {
console.log({ target });
if (!this.popperInstance && target) { if (!this.popperInstance && target) {
this.popperInstance = createPopper(target, this, { this.popperInstance = createPopper(target, this, {
placement: 'bottom', placement: 'bottom',

View File

@ -5,6 +5,8 @@ import { friendsViewStyles } from './friends-view-css';
import { connect } from 'pwa-helpers'; import { connect } from 'pwa-helpers';
import { store } from '../../store'; import { store } from '../../store';
import './feed-item' import './feed-item'
import '@polymer/paper-spinner/paper-spinner-lite.js'
const perEndpointCount = 20; const perEndpointCount = 20;
const totalDesiredCount = 100; const totalDesiredCount = 100;
@ -13,7 +15,8 @@ class FriendsFeed extends connect(store)(LitElement) {
static get properties() { static get properties() {
return { return {
feed: {type: Array}, feed: {type: Array},
setHasNewFeed: {attribute:false} setHasNewFeed: {attribute:false},
isLoading: {type: Boolean}
}; };
} }
constructor(){ constructor(){
@ -57,30 +60,84 @@ class FriendsFeed extends connect(store)(LitElement) {
return myNode; return myNode;
} }
async getSchemas(schemas){
const getAllSchemas = (schemas || []).map(
async (schema) => {
try {
const url = `${this.nodeUrl}/arbitrary/${schema.service}/${schema.name}/${schema.identifier}`;
const res = await fetch(url)
const data = await res.json()
if(data.error) return false
return data
} catch (error) {
console.log(error);
return false
}
}
);
const res = await Promise.all(getAllSchemas);
return res.filter((item)=> !!item)
}
getFeedOnInterval(){
let interval = null;
let stop = false;
const getAnswer = async () => {
if (!stop) {
stop = true;
try {
await this.reFetchFeedData()
} catch (error) {}
stop = false;
}
};
interval = setInterval(getAnswer, 600000);
}
async firstUpdated(){ async firstUpdated(){
this.viewElement = this.shadowRoot.getElementById('viewElement'); this.viewElement = this.shadowRoot.getElementById('viewElement');
this.downObserverElement = this.downObserverElement =
this.shadowRoot.getElementById('downObserver'); this.shadowRoot.getElementById('downObserver');
this.elementObserver(); this.elementObserver();
const feedData = schema.feed[0]
let schemaObj = {...schema} let schemaObj = {...schema}
const dynamicVars = { const dynamicVars = {
} }
const getEndpoints = async () => { const getEndpoints = async () => {
const schemas = await this.getSchemas(schemaList)
const friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
console.log({friendList})
const names = friendList.map(friend => `name=${friend.name}`).join('&');
console.log({names})
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true&exactmatchnames=true&${names}`
let formEndpoints = []
schemas.forEach((schema)=> {
const feedData = schema.feed[0]
if(feedData){
const copyFeedData = {...feedData}
const fullUrl = constructUrl(baseurl, copyFeedData.search, dynamicVars);
if(fullUrl){
formEndpoints.push({
url: fullUrl, schemaName: schema.name, schema: copyFeedData
})
}
}
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true`
const fullUrl = constructUrl(baseurl, feedData.search, dynamicVars); })
this.endpoints= [{url: fullUrl, schemaName: schema.name, schema: feedData }] this.endpoints= formEndpoints
this.endpointOffsets = Array(this.endpoints.length).fill(0); this.endpointOffsets = Array(this.endpoints.length).fill(0);
} }
try { try {
getEndpoints() await getEndpoints()
this.loadAndMergeData(); this.loadAndMergeData();
this.getFeedOnInterval()
} catch (error) { } catch (error) {
console.log(error) console.log(error)
@ -147,7 +204,7 @@ this.loadAndMergeData();
while (totalFetched < totalDesiredCount && madeProgress) { while (totalFetched < totalDesiredCount && madeProgress) {
madeProgress = false; madeProgress = false;
this.isLoading = true
for (i = 0; i < this.endpoints.length; i++) { for (i = 0; i < this.endpoints.length; i++) {
if (exhaustedEndpoints.has(i)) { if (exhaustedEndpoints.has(i)) {
continue; continue;
@ -182,7 +239,7 @@ this.loadAndMergeData();
break; break;
} }
} }
this.isLoading = false
// Trim the results if somehow they are over the totalDesiredCount // Trim the results if somehow they are over the totalDesiredCount
return results.slice(0, totalDesiredCount); return results.slice(0, totalDesiredCount);
} }
@ -233,6 +290,37 @@ this.loadAndMergeData();
return newData return newData
} }
async reFetchFeedData() {
// Resetting offsets to start fresh.
this.endpointOffsets = Array(this.endpoints.length).fill(0);
const oldIdentifiers = new Set(this.feed.map(item => item.identifier));
const newData = await this.initialLoad();
// Filter out items that are already in the feed
const trulyNewData = newData.filter(item => !oldIdentifiers.has(item.identifier));
if (trulyNewData.length > 0) {
// Adding extra data and merging with old data
const enhancedNewData = await this.addExtraData(trulyNewData);
// Merge new data with old data immutably
this.feed = [...enhancedNewData, ...this.feed];
this.feed.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
this.feed = this.trimDataToLimit(this.feed, maxResultsInMemory); // Trim to the maximum allowed in memory
this.feedToRender = this.feed.slice(0, 20);
this.hasInitialFetch = true;
const created = trulyNewData[0].created;
let value = localStorage.getItem('lastSeenFeed');
if (((+value || 0) < created)) {
this.setHasNewFeed(true);
}
}
}
async loadAndMergeData() { async loadAndMergeData() {
let allData = this.feed let allData = this.feed
@ -261,7 +349,11 @@ this.loadAndMergeData();
return html` return html`
<div class="container"> <div class="container">
<div id="viewElement" class="container-body" style=${"position: relative"}> <div id="viewElement" class="container-body" style=${"position: relative"}>
${this.isLoading ? html`
<div style="width:100%;display: flex; justify-content:center">
<paper-spinner-lite active></paper-spinner-lite>
</div>
` : ''}
${this.feedToRender.map((item) => { ${this.feedToRender.map((item) => {
return html`<feed-item return html`<feed-item
.resource=${item} .resource=${item}
@ -351,6 +443,7 @@ function executeMethodInWorker(methodString, externalArgs) {
URL.revokeObjectURL(blobURL); URL.revokeObjectURL(blobURL);
} else { } else {
resolve(""); resolve("");
event.data = null
worker.terminate(); worker.terminate();
URL.revokeObjectURL(blobURL); URL.revokeObjectURL(blobURL);
} }
@ -409,9 +502,12 @@ export function replacePlaceholders(template, resource, customParams) {
// export const schemaList = [schema] export const schemaList = [{
identifier: 'ui_schema_feed',
const schema = { name: 'Q-Blog',
service: 'DOCUMENT'
}]
export const schema = {
name: "Q-Blog", name: "Q-Blog",
defaultFeedIndex: 0, defaultFeedIndex: 0,
feed: [ feed: [
@ -429,9 +525,7 @@ export function replacePlaceholders(template, resource, customParams) {
}, },
click: "qortal://APP/Q-Blog/$${resource.name}$$/$${customParams.blogId}$$/$${customParams.shortIdentifier}$$", click: "qortal://APP/Q-Blog/$${resource.name}$$/$${customParams.blogId}$$/$${customParams.shortIdentifier}$$",
display: { display: {
title: "$${rawdata.title}$$", title: "$${rawdata.title}$$"
description: "$${rawdata.description}$$",
coverImage: "$${rawdata.image}$$"
}, },
customParams: { customParams: {
blogId: "**methods.getBlogId(resource)**", blogId: "**methods.getBlogId(resource)**",
@ -446,3 +540,4 @@ export function replacePlaceholders(template, resource, customParams) {
] ]
} }
export const listSchemas = [schema]

View File

@ -107,9 +107,15 @@ class FriendsSidePanel extends LitElement {
<span @click=${()=> this.selected = 'friends'} class="${this.selected === 'friends' ? 'active' : 'default'}">${translate('friends.friend12')}</span> <span @click=${()=> this.selected = 'friends'} class="${this.selected === 'friends' ? 'active' : 'default'}">${translate('friends.friend12')}</span>
<span @click=${()=> this.selected = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span> <span @click=${()=> this.selected = 'feed'} class="${this.selected === 'feed' ? 'active' : 'default'}">${translate('friends.friend13')}</span>
</div> </div>
<div style="display:flex;gap:15px;align-items:center">
<mwc-icon @click=${()=> {
this.shadowRoot.querySelector('friends-feed').reFetchFeedData()
}} style="color: var(--black); cursor:pointer;">refresh</mwc-icon>
<mwc-icon style="cursor:pointer" @click=${()=> { <mwc-icon style="cursor:pointer" @click=${()=> {
this.setIsOpen(false) this.setIsOpen(false)
}}>close</mwc-icon> }}>close</mwc-icon>
</div>
</div> </div>
<div class="content"> <div class="content">
<div class="${this.selected === 'friends' ? 'active-content' : 'default-content'}"> <div class="${this.selected === 'friends' ? 'active-content' : 'default-content'}">
@ -118,9 +124,7 @@ class FriendsSidePanel extends LitElement {
<div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}"> <div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
<friends-feed .setHasNewFeed=${(val)=> this.setHasNewFeed(val)}></friends-feed> <friends-feed .setHasNewFeed=${(val)=> this.setHasNewFeed(val)}></friends-feed>
</div> </div>
<!-- ${this.selected === 'friends' ? html`<friends-view></friends-view>` : ''}
${this.selected === 'feed' ? html`<friends-feed></friends-feed>` : ''} -->
</div> </div>

View File

@ -40,7 +40,8 @@ class FriendsView extends connect(store)(LitElement) {
userFoundModalOpen: {type: Boolean}, userFoundModalOpen: {type: Boolean},
userFound: { type: Array}, userFound: { type: Array},
isOpenAddFriendsModal: {type: Boolean}, isOpenAddFriendsModal: {type: Boolean},
editContent: {type: Object} editContent: {type: Object},
mySelectedFeeds: {type: Array}
}; };
} }
static get styles() { static get styles() {
@ -57,11 +58,7 @@ class FriendsView extends connect(store)(LitElement) {
window.parent.reduxStore.getState().app.selectedAddress.address; window.parent.reduxStore.getState().app.selectedAddress.address;
this.errorMessage = ''; this.errorMessage = '';
this.successMessage = ''; this.successMessage = '';
this.friendList = [{ this.friendList = [];
name: "Phil"
}];
this.userSelected = {}; this.userSelected = {};
this.isLoading = false; this.isLoading = false;
this.userFoundModalOpen = false this.userFoundModalOpen = false
@ -99,6 +96,8 @@ class FriendsView extends connect(store)(LitElement) {
this.downObserverElement = this.downObserverElement =
this.shadowRoot.getElementById('downObserver'); this.shadowRoot.getElementById('downObserver');
this.elementObserver(); this.elementObserver();
this.mySelectedFeeds = JSON.parse(localStorage.getItem('friends-my-selected-feeds') || "[]")
this.friendList = JSON.parse(localStorage.getItem('friends-my-friend-list') || "[]")
} }
elementObserver() { elementObserver() {
@ -177,41 +176,68 @@ class FriendsView extends connect(store)(LitElement) {
body: `${namesJsonString}` body: `${namesJsonString}`
}) })
if (ret === true) {
this.myFollowedNames = this.myFollowedNames.filter(item => item != name)
this.myFollowedNames.push(name)
} else {
let err3string = get("appspage.schange22")
parentEpml.request('showSnackBar', `${err3string}`)
}
return ret return ret
} }
addToFriendList(val){
if(this.editContent){ async unFollowName(name) {
const findFriend = this.friendList.findIndex(item=> item.name === val.name) let items = [
name
]
let namesJsonString = JSON.stringify({ "items": items })
let ret = await parentEpml.request('apiCall', {
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: `${namesJsonString}`
})
return ret
}
addToFriendList(val, isRemove){
const copyVal = {...val}
delete copyVal.mySelectedFeeds
if(isRemove){
this.friendList = this.friendList.filter((item)=> item.name !== copyVal.name)
}else if(this.editContent){
const findFriend = this.friendList.findIndex(item=> item.name === copyVal.name)
if(findFriend !== -1){ if(findFriend !== -1){
const copyList = [...this.friendList] const copyList = [...this.friendList]
copyList[findFriend] = val copyList[findFriend] = copyVal
this.friendList = copyList this.friendList = copyList
} }
} else { } else {
this.friendList = [...this.friendList, val] this.friendList = [...this.friendList, copyVal]
} }
if(val.willFollow){ if(!copyVal.willFollow || isRemove) {
this.myFollowName(val.name) this.unFollowName(copyVal.name)
} else if(copyVal.willFollow){
this.myFollowName(copyVal.name)
} }
this.setMySelectedFeeds(val.mySelectedFeeds)
this.userSelected = {}; this.userSelected = {};
this.isLoading = false; this.isLoading = false;
this.isOpenAddFriendsModal = false this.isOpenAddFriendsModal = false
this.editContent = null this.editContent = null
this.setMyFriends(this.friendList)
}
setMyFriends(friendList){
localStorage.setItem('friends-my-friend-list', JSON.stringify(friendList));
}
setMySelectedFeeds(mySelectedFeeds){
this.mySelectedFeeds = mySelectedFeeds
localStorage.setItem('friends-my-selected-feeds', JSON.stringify(mySelectedFeeds));
} }
openEditFriend(val){ openEditFriend(val){
this.isOpenAddFriendsModal = true this.isOpenAddFriendsModal = true
this.userSelected = val this.userSelected = val
this.editContent = val this.editContent = {...val, mySelectedFeeds: this.mySelectedFeeds}
} }
onClose(){ onClose(){
@ -288,9 +314,10 @@ class FriendsView extends connect(store)(LitElement) {
this.isOpenAddFriendsModal = val this.isOpenAddFriendsModal = val
}} }}
.userSelected=${this.userSelected} .userSelected=${this.userSelected}
.onSubmit=${(val)=> this.addToFriendList(val)} .onSubmit=${(val, isRemove)=> this.addToFriendList(val, isRemove)}
.editContent=${this.editContent} .editContent=${this.editContent}
.onClose=${()=> this.onClose()} .onClose=${()=> this.onClose()}
.mySelectedFeeds=${this.mySelectedFeeds}
> >
</add-friends-modal> </add-friends-modal>
`; `;

View File

@ -40,7 +40,6 @@ export class PopoverComponent extends LitElement {
} }
attachToTarget(target) { attachToTarget(target) {
console.log({target})
if (!this.popperInstance && target) { if (!this.popperInstance && target) {
this.popperInstance = createPopper(target, this, { this.popperInstance = createPopper(target, this, {
placement: 'bottom', placement: 'bottom',