mirror of
https://github.com/Qortal/qortal-ui.git
synced 2025-02-11 17:55:51 +00:00
feed in separate palce
This commit is contained in:
parent
8eacfca4d1
commit
fac7ae9004
@ -1188,6 +1188,8 @@
|
|||||||
"friend8": "Send Q-Chat",
|
"friend8": "Send Q-Chat",
|
||||||
"friend9": "Send Q-Mail",
|
"friend9": "Send Q-Mail",
|
||||||
"friend10": "Edit friend",
|
"friend10": "Edit friend",
|
||||||
"friend11": "Following"
|
"friend11": "Following",
|
||||||
|
"friend12": "Friends",
|
||||||
|
"friend13": "Feed"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,15 +7,23 @@ import axios from 'axios'
|
|||||||
import '@material/mwc-menu';
|
import '@material/mwc-menu';
|
||||||
import '@material/mwc-list/mwc-list-item.js'
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
import { RequestQueueWithPromise } from '../../../../plugins/plugins/utils/queue';
|
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(5);
|
const requestQueue = new RequestQueueWithPromise(5);
|
||||||
|
|
||||||
export class FeedItem extends LitElement {
|
export class FeedItem extends connect(store)(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
resource: { type: Object },
|
resource: { type: Object },
|
||||||
isReady: { type: Boolean},
|
isReady: { type: Boolean},
|
||||||
status: {type: Object},
|
status: {type: Object},
|
||||||
feedItem: {type: Object}
|
feedItem: {type: Object},
|
||||||
|
appName: {type: String},
|
||||||
|
link: {type: String}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,10 +31,15 @@ export class FeedItem extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
* {
|
* {
|
||||||
--mdc-theme-text-primary-on-background: var(--black);
|
--mdc-theme-text-primary-on-background: var(--black);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
max-width:45vh;
|
width:100%;
|
||||||
max-height:40vh;
|
max-height:30vh;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -51,9 +64,40 @@ export class FeedItem extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.defaultSize {
|
.defaultSize {
|
||||||
width: 45vh;
|
width: 100%;
|
||||||
height: 40vh;
|
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;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
.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 {
|
mwc-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -102,6 +146,7 @@ export class FeedItem extends LitElement {
|
|||||||
this.myNode = this.getMyNode()
|
this.myNode = this.getMyNode()
|
||||||
this.hasCalledWhenDownloaded = false
|
this.hasCalledWhenDownloaded = false
|
||||||
this.isFetching = false
|
this.isFetching = false
|
||||||
|
this.uid = new ShortUniqueId()
|
||||||
|
|
||||||
this.observer = new IntersectionObserver(entries => {
|
this.observer = new IntersectionObserver(entries => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
@ -283,11 +328,93 @@ getMyNode(){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async goToFeedLink(){
|
||||||
|
try {
|
||||||
|
console.log('this.link', this.link)
|
||||||
|
let newQuery = this.link
|
||||||
|
if (newQuery.endsWith('/')) {
|
||||||
|
newQuery = newQuery.slice(0, -1)
|
||||||
|
}
|
||||||
|
const res = await this.extractComponents(newQuery)
|
||||||
|
if (!res) return
|
||||||
|
const { service, name, identifier, path } = res
|
||||||
|
let query = `?service=${service}`
|
||||||
|
if (name) {
|
||||||
|
query = query + `&name=${name}`
|
||||||
|
}
|
||||||
|
if (identifier) {
|
||||||
|
query = query + `&identifier=${identifier}`
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
query = query + `&path=${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(setNewTab({
|
||||||
|
url: `qdn/browser/index.html${query}`,
|
||||||
|
id: this.uid.rnd(),
|
||||||
|
myPlugObj: {
|
||||||
|
"url": "myapp",
|
||||||
|
"domain": "core",
|
||||||
|
"page": `qdn/browser/index.html${query}`,
|
||||||
|
"title": name,
|
||||||
|
"icon": 'vaadin:external-browser',
|
||||||
|
"mwcicon": 'open_in_browser',
|
||||||
|
"menus": [],
|
||||||
|
"parent": false
|
||||||
|
},
|
||||||
|
openExisting: true
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.log({error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async extractComponents(url) {
|
||||||
|
if (!url.startsWith("qortal://")) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.replace(/^(qortal\:\/\/)/, "")
|
||||||
|
if (url.includes("/")) {
|
||||||
|
let parts = url.split("/")
|
||||||
|
const service = parts[0].toUpperCase()
|
||||||
|
parts.shift()
|
||||||
|
const name = parts[0]
|
||||||
|
parts.shift()
|
||||||
|
let identifier
|
||||||
|
|
||||||
|
if (parts.length > 0) {
|
||||||
|
identifier = parts[0] // Do not shift yet
|
||||||
|
// Check if a resource exists with this service, name and identifier combination
|
||||||
|
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
|
||||||
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
const url = `${nodeUrl}/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${myNode.apiKey}}`
|
||||||
|
|
||||||
|
const res = await fetch(url);
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.totalChunkCount > 0) {
|
||||||
|
// Identifier exists, so don't include it in the path
|
||||||
|
parts.shift()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
identifier = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = parts.join("/")
|
||||||
|
|
||||||
|
const components = {}
|
||||||
|
components["service"] = service
|
||||||
|
components["name"] = name
|
||||||
|
components["identifier"] = identifier
|
||||||
|
components["path"] = path
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -297,6 +424,20 @@ getMyNode(){
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log('this.feedItem', this.feedItem)
|
console.log('this.feedItem', this.feedItem)
|
||||||
|
let avatarImg
|
||||||
|
const avatarUrl = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.resource.name}/qortal_avatar?async=true&apiKey=${this.myNode.apiKey}`;
|
||||||
|
avatarImg = html`<img
|
||||||
|
src="${avatarUrl}"
|
||||||
|
style="max-width:100%; max-height:100%;"
|
||||||
|
onerror="this.onerror=null; this.src='/img/incognito.png';"
|
||||||
|
/>`;
|
||||||
|
let avatarImgApp
|
||||||
|
const avatarUrl2 = `${this.nodeUrl}/arbitrary/THUMBNAIL/${this.appName}/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`
|
return html`
|
||||||
<div
|
<div
|
||||||
class=${[
|
class=${[
|
||||||
@ -308,12 +449,13 @@ getMyNode(){
|
|||||||
? 'hideImg'
|
? 'hideImg'
|
||||||
: '',
|
: '',
|
||||||
].join(' ')}
|
].join(' ')}
|
||||||
|
style=" box-sizing: border-box;"
|
||||||
>
|
>
|
||||||
${
|
${
|
||||||
this.status.status !== 'READY'
|
this.status.status !== 'READY'
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center;"
|
style="display:flex;flex-direction:column;width:100%;height:100%;justify-content:center;align-items:center; box-sizing: border-box;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class=${`smallLoading`}
|
class=${`smallLoading`}
|
||||||
@ -325,8 +467,24 @@ getMyNode(){
|
|||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
${this.status.status === 'READY' && this.feedItem ? html`
|
${this.status.status === 'READY' && this.feedItem ? html`
|
||||||
<div style="position:relative">
|
<div class="parent-feed-item" style="position:relative" @click=${this.goToFeedLink}>
|
||||||
ready
|
<div style="display:flex;gap:10px;margin-bottom:20px">
|
||||||
|
<div class="avatar">
|
||||||
|
${avatarImg}</div> <span class="feed-item-name">${this.resource.name}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>${this.feedItem.title}</p>
|
||||||
|
</div>
|
||||||
|
<div class="app-name">
|
||||||
|
<div class="avatar">
|
||||||
|
${avatarImgApp}
|
||||||
|
</div>
|
||||||
|
<message-time
|
||||||
|
timestamp=${this
|
||||||
|
.resource
|
||||||
|
.created}
|
||||||
|
></message-time>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ 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'
|
||||||
|
|
||||||
const perEndpointCount = 20;
|
const perEndpointCount = 20;
|
||||||
const totalDesiredCount = 100;
|
const totalDesiredCount = 100;
|
||||||
const maxResultsInMemory = 300;
|
const maxResultsInMemory = 300;
|
||||||
@ -17,18 +18,25 @@ class FriendsFeed extends connect(store)(LitElement) {
|
|||||||
constructor(){
|
constructor(){
|
||||||
super()
|
super()
|
||||||
this.feed = []
|
this.feed = []
|
||||||
|
this.feedToRender = []
|
||||||
this.nodeUrl = this.getNodeUrl();
|
this.nodeUrl = this.getNodeUrl();
|
||||||
this.myNode = this.getMyNode();
|
this.myNode = this.getMyNode();
|
||||||
this.endpoints = []
|
this.endpoints = []
|
||||||
this.endpointOffsets = [] // Initialize offsets for each endpoint to 0
|
this.endpointOffsets = [] // Initialize offsets for each endpoint to 0
|
||||||
|
|
||||||
this.loadAndMergeData = this.loadAndMergeData.bind(this)
|
this.loadAndMergeData = this.loadAndMergeData.bind(this)
|
||||||
|
this.hasInitialFetch = false
|
||||||
|
this.observerHandler = this.observerHandler.bind(this);
|
||||||
|
this.elementObserver = this.elementObserver.bind(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [friendsViewStyles];
|
return [friendsViewStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getNodeUrl() {
|
getNodeUrl() {
|
||||||
const myNode =
|
const myNode =
|
||||||
store.getState().app.nodeConfig.knownNodes[
|
store.getState().app.nodeConfig.knownNodes[
|
||||||
@ -49,45 +57,27 @@ class FriendsFeed extends connect(store)(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async firstUpdated(){
|
async firstUpdated(){
|
||||||
console.log('sup')
|
this.viewElement = this.shadowRoot.getElementById('viewElement');
|
||||||
|
this.downObserverElement =
|
||||||
|
this.shadowRoot.getElementById('downObserver');
|
||||||
|
this.elementObserver();
|
||||||
const feedData = schema.feed[0]
|
const feedData = schema.feed[0]
|
||||||
let schemaObj = {...schema}
|
let schemaObj = {...schema}
|
||||||
const dynamicVars = {
|
const dynamicVars = {
|
||||||
name: 'Phil'
|
|
||||||
}
|
}
|
||||||
const getMail = async () => {
|
const getEndpoints = async () => {
|
||||||
|
|
||||||
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true`
|
const baseurl = `${this.nodeUrl}/arbitrary/resources/search?reverse=true`
|
||||||
const fullUrl = constructUrl(baseurl, feedData.search, dynamicVars);
|
const fullUrl = constructUrl(baseurl, feedData.search, dynamicVars);
|
||||||
this.endpoints= ['http://127.0.0.1:12391/arbitrary/resources/search?reverse=true&query=-post-&identifier=q-blog-&service=BLOG_POST&exactmatchnames=true&limit=20']
|
this.endpoints= [{url: fullUrl, schemaName: schema.name, schema: feedData }]
|
||||||
this.endpointOffsets = Array(this.endpoints.length).fill(0); // Initialize offsets for each endpoint to 0
|
this.endpointOffsets = Array(this.endpoints.length).fill(0);
|
||||||
|
|
||||||
console.log('this.endpoints', this.endpoints)
|
|
||||||
const response = await fetch(fullUrl, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = await response.json()
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
getMail()
|
getEndpoints()
|
||||||
const resource = {
|
|
||||||
name: 'Phil',
|
|
||||||
identifier: 'q-blog-Mugician-post-Love-Explosion-Festival--yJ8kuo'
|
|
||||||
}
|
|
||||||
// First, evaluate methods to get values for customParams
|
|
||||||
await updateCustomParamsWithMethods(schemaObj, resource);
|
|
||||||
console.log({schemaObj})
|
|
||||||
// Now, generate your final URLs
|
|
||||||
let clickValue1 = schemaObj.feed[0].click;
|
|
||||||
|
|
||||||
const resolvedClickValue1 = replacePlaceholders(clickValue1, resource, schemaObj.feed[0].customParams);
|
|
||||||
|
|
||||||
console.log(resolvedClickValue1);
|
|
||||||
this.loadAndMergeData();
|
this.loadAndMergeData();
|
||||||
|
|
||||||
|
|
||||||
@ -96,10 +86,59 @@ this.loadAndMergeData();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMoreFeed(){
|
||||||
|
if(!this.hasInitialFetch) return
|
||||||
|
console.log('getting more feed')
|
||||||
|
if(this.feedToRender.length === this.feed.length ) return
|
||||||
|
this.feedToRender = this.feed.slice(0, this.feedToRender.length + 20)
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
elementObserver() {
|
||||||
|
const options = {
|
||||||
|
rootMargin: '0px',
|
||||||
|
threshold: 1,
|
||||||
|
};
|
||||||
|
// identify an element to observe
|
||||||
|
console.log('this', this.viewElement, this.downObserverElement)
|
||||||
|
const elementToObserve = this.downObserverElement;
|
||||||
|
// passing it a callback function
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
this.observerHandler,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
// call `observe()` on that MutationObserver instance,
|
||||||
|
// passing it the element to observe, and the options object
|
||||||
|
observer.observe(elementToObserve);
|
||||||
|
}
|
||||||
|
|
||||||
|
observerHandler(entries) {
|
||||||
|
console.log({entries})
|
||||||
|
if (!entries[0].isIntersecting) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log('this.feedToRender', this.feedToRender)
|
||||||
|
if (this.feedToRender.length < 20) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.getMoreFeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fetchDataFromEndpoint(endpointIndex, count) {
|
async fetchDataFromEndpoint(endpointIndex, count) {
|
||||||
const offset = this.endpointOffsets[endpointIndex];
|
const offset = this.endpointOffsets[endpointIndex];
|
||||||
const url = `${this.endpoints[endpointIndex]}&limit=${count}&offset=${offset}`;
|
const url = `${this.endpoints[endpointIndex].url}&limit=${count}&offset=${offset}`;
|
||||||
return fetch(url).then(res => res.json());
|
const res = await fetch(url)
|
||||||
|
const data = await res.json()
|
||||||
|
console.log({data})
|
||||||
|
return data.map((i)=> {
|
||||||
|
return {
|
||||||
|
...this.endpoints[endpointIndex],
|
||||||
|
...i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -165,17 +204,55 @@ this.loadAndMergeData();
|
|||||||
const uniqueNewData = newData.filter(item => !existingIds.has(item.identifier));
|
const uniqueNewData = newData.filter(item => !existingIds.has(item.identifier));
|
||||||
return uniqueNewData.concat(existingData);
|
return uniqueNewData.concat(existingData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async addExtraData(data){
|
||||||
|
let newData = []
|
||||||
|
for (let item of data) {
|
||||||
|
let newItem = {
|
||||||
|
...item,
|
||||||
|
schema: {
|
||||||
|
...item.schema,
|
||||||
|
customParams: {...item.schema.customParams}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let newResource = {
|
||||||
|
identifier: newItem.identifier,
|
||||||
|
service: newItem.service,
|
||||||
|
name: newItem.name
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
newItem.link = resolvedClickValue1
|
||||||
|
newData.push(newItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newData
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
async loadAndMergeData() {
|
async loadAndMergeData() {
|
||||||
let allData = this.feed
|
let allData = this.feed
|
||||||
const newData = await this.initialLoad();
|
const newData = await this.initialLoad();
|
||||||
|
allData = await this.addExtraData(newData)
|
||||||
allData = this.mergeData(newData, allData);
|
allData = this.mergeData(newData, allData);
|
||||||
allData.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
|
allData.sort((a, b) => new Date(b.created) - new Date(a.created)); // Sort by timestamp, most recent first
|
||||||
allData = this.trimDataToLimit(allData, maxResultsInMemory); // Trim to the maximum allowed in memory
|
allData = this.trimDataToLimit(allData, maxResultsInMemory); // Trim to the maximum allowed in memory
|
||||||
this.feed = [...allData]
|
this.feed = [...allData]
|
||||||
|
this.feedToRender = this.feed.slice(0,20)
|
||||||
|
this.hasInitialFetch = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log('ron', this.feed)
|
console.log('ron', this.feed)
|
||||||
@ -183,13 +260,11 @@ this.loadAndMergeData();
|
|||||||
<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.feed.map((item) => {
|
${this.feedToRender.map((item) => {
|
||||||
return html`<feed-item
|
return html`<feed-item
|
||||||
.resource=${{
|
.resource=${item}
|
||||||
name: item.name,
|
appName=${'Q-Blog'}
|
||||||
service: item.service,
|
link=${item.link}
|
||||||
identifier: item.identifier,
|
|
||||||
}}
|
|
||||||
></feed-item>`;
|
></feed-item>`;
|
||||||
})}
|
})}
|
||||||
<div id="downObserver"></div>
|
<div id="downObserver"></div>
|
||||||
@ -223,13 +298,43 @@ export function constructUrl(base, search, dynamicVars) {
|
|||||||
return queryStrings.length > 0 ? `${base}&${queryStrings.join('&')}` : base;
|
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) {
|
function executeMethodInWorker(methodString, externalArgs) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!validateMethodString(methodString)) {
|
||||||
|
reject(new Error("Invalid method string provided."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const workerFunction = `
|
const workerFunction = `
|
||||||
self.onmessage = function(event) {
|
self.onmessage = function(event) {
|
||||||
const method = ${methodString};
|
const methodFunction = new Function("resource", "${methodString}");
|
||||||
const result = method(event.data.externalArgs);
|
const result = methodFunction(event.data.externalArgs);
|
||||||
self.postMessage(result);
|
|
||||||
|
if (typeof result === 'string' || typeof result === 'number') {
|
||||||
|
self.postMessage(result);
|
||||||
|
} else {
|
||||||
|
self.postMessage('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -238,9 +343,16 @@ function executeMethodInWorker(methodString, externalArgs) {
|
|||||||
const worker = new Worker(blobURL);
|
const worker = new Worker(blobURL);
|
||||||
|
|
||||||
worker.onmessage = function(event) {
|
worker.onmessage = function(event) {
|
||||||
resolve(event.data);
|
if (typeof event.data === 'string' || typeof event.data === 'number') {
|
||||||
worker.terminate();
|
resolve(event.data);
|
||||||
URL.revokeObjectURL(blobURL);
|
worker.terminate();
|
||||||
|
URL.revokeObjectURL(blobURL);
|
||||||
|
} else {
|
||||||
|
resolve("");
|
||||||
|
worker.terminate();
|
||||||
|
URL.revokeObjectURL(blobURL);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
worker.onerror = function(error) {
|
worker.onerror = function(error) {
|
||||||
@ -256,17 +368,24 @@ function executeMethodInWorker(methodString, externalArgs) {
|
|||||||
|
|
||||||
|
|
||||||
export async function updateCustomParamsWithMethods(schema,resource) {
|
export async function updateCustomParamsWithMethods(schema,resource) {
|
||||||
for (const key in schema.feed[0].customParams) {
|
console.log({schema, resource})
|
||||||
const value = schema.feed[0].customParams[key];
|
for (const key in schema.customParams) {
|
||||||
|
const value = schema.customParams[key];
|
||||||
|
console.log({value})
|
||||||
if (value.startsWith("**methods.") && value.endsWith("**")) {
|
if (value.startsWith("**methods.") && value.endsWith("**")) {
|
||||||
const methodInvocation = value.slice(10, -2).split('(');
|
const methodInvocation = value.slice(10, -2).split('(');
|
||||||
const methodName = methodInvocation[0];
|
const methodName = methodInvocation[0];
|
||||||
|
|
||||||
if (schema.feed[0].methods[methodName]) {
|
if (schema.methods[methodName]) {
|
||||||
const methodResult = await executeMethodInWorker(schema.feed[0].methods[methodName].toString(), resource);
|
const newResource = {
|
||||||
|
identifier: resource.identifier,
|
||||||
|
name: resource.name,
|
||||||
|
service: resource.service
|
||||||
|
}
|
||||||
|
console.log({newResource})
|
||||||
|
const methodResult = await executeMethodInWorker(schema.methods[methodName], newResource);
|
||||||
console.log({methodResult})
|
console.log({methodResult})
|
||||||
schema.feed[0].customParams[key] = methodResult;
|
schema.customParams[key] = methodResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,9 +411,9 @@ export function replacePlaceholders(template, resource, customParams) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// export const schemaList = [schema]
|
||||||
|
|
||||||
|
const schema = {
|
||||||
export const schema = {
|
|
||||||
name: "Q-Blog",
|
name: "Q-Blog",
|
||||||
defaultFeedIndex: 0,
|
defaultFeedIndex: 0,
|
||||||
feed: [
|
feed: [
|
||||||
@ -305,7 +424,6 @@ export const schema = {
|
|||||||
title: "Q-Blog Post creations",
|
title: "Q-Blog Post creations",
|
||||||
description: "blablabla",
|
description: "blablabla",
|
||||||
search: {
|
search: {
|
||||||
name:"$${name}$$",
|
|
||||||
query: "-post-",
|
query: "-post-",
|
||||||
identifier: "q-blog-",
|
identifier: "q-blog-",
|
||||||
service: "BLOG_POST",
|
service: "BLOG_POST",
|
||||||
@ -321,27 +439,35 @@ export const schema = {
|
|||||||
blogId: "**methods.getBlogId(resource)**",
|
blogId: "**methods.getBlogId(resource)**",
|
||||||
shortIdentifier: "**methods.getShortId(resource)**"
|
shortIdentifier: "**methods.getShortId(resource)**"
|
||||||
},
|
},
|
||||||
methods: {
|
"methods": {
|
||||||
getShortId: function(resource) {
|
"getShortId": "return resource.identifier.split('-post-')[1];",
|
||||||
const str = resource.identifier
|
"getBlogId": "const arr = resource.identifier.split('-post-'); const id = arr[0]; return id.startsWith('q-blog-') ? id.substring(7) : id;"
|
||||||
const arr = str.split('-post-')
|
|
||||||
const shortIdentifier = arr[1]
|
|
||||||
|
|
||||||
return shortIdentifier
|
|
||||||
},
|
|
||||||
getBlogId: function(resource) {
|
|
||||||
const str = resource.identifier
|
|
||||||
const arr = str.split('-post-')
|
|
||||||
const id = arr[0]
|
|
||||||
let blogId = ""
|
|
||||||
if (id.startsWith('q-blog-')) {
|
|
||||||
blogId = id.substring(7);
|
|
||||||
} else {
|
|
||||||
blogId= id;
|
|
||||||
}
|
|
||||||
return blogId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// methods: {
|
||||||
|
// getShortId: function(resource) {
|
||||||
|
// console.log({resource})
|
||||||
|
// const str = resource.identifier
|
||||||
|
// const arr = str.split('-post-')
|
||||||
|
// const shortIdentifier = arr[1]
|
||||||
|
|
||||||
|
// return shortIdentifier
|
||||||
|
// },
|
||||||
|
// getBlogId: function(resource) {
|
||||||
|
// console.log({resource})
|
||||||
|
// const str = resource.identifier
|
||||||
|
// const arr = str.split('-post-')
|
||||||
|
// const id = arr[0]
|
||||||
|
// let blogId = ""
|
||||||
|
// if (id.startsWith('q-blog-')) {
|
||||||
|
// blogId = id.substring(7);
|
||||||
|
// } else {
|
||||||
|
// blogId= id;
|
||||||
|
// }
|
||||||
|
// return blogId
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export const schema = JSON.stringify(schema2, null, 2); // 2 spaces indentation
|
||||||
|
@ -2,13 +2,20 @@ import { LitElement, html, css } from 'lit';
|
|||||||
import '@material/mwc-icon';
|
import '@material/mwc-icon';
|
||||||
import './friends-view'
|
import './friends-view'
|
||||||
import './friends-feed'
|
import './friends-feed'
|
||||||
|
import { translate } from 'lit-translate';
|
||||||
class FriendsSidePanel extends LitElement {
|
class FriendsSidePanel extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
setIsOpen: { attribute: false},
|
setIsOpen: { attribute: false},
|
||||||
isOpen: {type: Boolean}
|
isOpen: {type: Boolean},
|
||||||
|
selected: {type: String}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
this.selected = 'friends'
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
@ -21,7 +28,6 @@ class FriendsSidePanel extends LitElement {
|
|||||||
height: calc(100vh - 55px);
|
height: calc(100vh - 55px);
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
border-left: 1px solid rgb(224, 224, 224);
|
border-left: 1px solid rgb(224, 224, 224);
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
transform: translateX(100%); /* start from outside the right edge */
|
transform: translateX(100%); /* start from outside the right edge */
|
||||||
transition: transform 0.3s ease-in-out;
|
transition: transform 0.3s ease-in-out;
|
||||||
@ -40,23 +46,85 @@ class FriendsSidePanel extends LitElement {
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.content::-webkit-scrollbar-track {
|
||||||
|
background-color: whitesmoke;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
border-radius: 7px;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgb(180, 176, 176);
|
||||||
|
border-radius: 7px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
.parent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
font-size: 16px;
|
||||||
|
background: var(--black);
|
||||||
|
color: var(--white);
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--black);
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default-content {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -50;
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
|
<div class="parent">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span>Panel Title</span>
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
<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 class="content">
|
<div class="content">
|
||||||
<friends-view></friends-view>
|
<div class="${this.selected === 'friends' ? 'active-content' : 'default-content'}">
|
||||||
<friends-feed></friends-feed>
|
<friends-view></friends-view>
|
||||||
|
</div>
|
||||||
|
<div class="${this.selected === 'feed' ? 'active-content' : 'default-content'}">
|
||||||
|
<friends-feed></friends-feed>
|
||||||
|
</div>
|
||||||
|
<!-- ${this.selected === 'friends' ? html`<friends-view></friends-view>` : ''}
|
||||||
|
|
||||||
|
${this.selected === 'feed' ? html`<friends-feed></friends-feed>` : ''} -->
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { css } from 'lit'
|
import { css } from 'lit'
|
||||||
|
|
||||||
export const friendsViewStyles = css`
|
export const friendsViewStyles = css`
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
.top-bar-icon {
|
.top-bar-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
@ -41,6 +44,7 @@ export const friendsViewStyles = css`
|
|||||||
padding: 0px 6px;
|
padding: 0px 6px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-body::-webkit-scrollbar-track {
|
.container-body::-webkit-scrollbar-track {
|
||||||
@ -164,7 +168,7 @@ export const friendsViewStyles = css`
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 3px;
|
right: 3px;
|
||||||
color: var(--chat-bubble-msg-color);
|
color: var(--chat-bubble-msg-color);
|
||||||
transition: all 0.3s ease-in-out;
|
transition: hover 0.3s ease-in-out;
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 6px 3px;
|
padding: 6px 3px;
|
||||||
|
@ -270,7 +270,7 @@ class FriendsView extends connect(store)(LitElement) {
|
|||||||
?loading=${this.isLoading}>
|
?loading=${this.isLoading}>
|
||||||
</chat-search-results>
|
</chat-search-results>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
|
|
||||||
${this.friendList.map((item) => {
|
${this.friendList.map((item) => {
|
||||||
return html`<chat-side-nav-heads
|
return html`<chat-side-nav-heads
|
||||||
|
@ -281,10 +281,7 @@ class WebBrowser extends LitElement {
|
|||||||
else {
|
else {
|
||||||
identifier = null;
|
identifier = null;
|
||||||
}
|
}
|
||||||
}
|
}extractComponents
|
||||||
|
|
||||||
const path = parts.join("/");
|
|
||||||
|
|
||||||
const components = {};
|
const components = {};
|
||||||
components["service"] = service;
|
components["service"] = service;
|
||||||
components["name"] = name;
|
components["name"] = name;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user