Initial UI done for the gif explorer

This commit is contained in:
Justin Ferrari 2023-02-01 23:51:13 +02:00
parent f229ead7ae
commit 4cab597afa
15 changed files with 1495 additions and 1414 deletions

View File

@ -1,6 +1,6 @@
{
"name": "qortal-ui",
"version": "2.2.4",
"version": "3.0.1",
"description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [
"QORT",
@ -37,14 +37,12 @@
"os-locale": "3.0.0"
},
"devDependencies": {
"electron": "22.0.2",
"electron": "22.1.0",
"electron-builder": "23.6.0",
"electron-packager": "17.1.1",
"eslint-plugin-lit": "1.8.0",
"eslint-plugin-wc": "1.4.0",
"shelljs": "0.8.5"
},
"engines": {
"node": ">=16.17.1"
}
}
}

Binary file not shown.

View File

@ -14,9 +14,9 @@
@font-face {
font-family: 'Material Symbols Outlined';
font-style: normal;
src: local('MaterialSymbolsOutlined'),
url(MaterialSymbolsOutlined.ttf) format('truetype'),
url(MaterialSymbolsOutlined.woff2) format('woff2')
src: local('MaterialSymbolsOutlined'),
url(MaterialSymbolsOutlined.ttf) format('truetype'),
url(MaterialSymbolsOutlined.woff2) format('woff2')
}
@font-face {
@ -26,6 +26,13 @@
url(Montserrat.ttf) format('truetype');
}
@font-face {
font-family: 'MavenPro';
src: local('MavenPro'),
local('MavenPro'),
url(Montserrat.ttf) format('truetype');
}
@font-face {
font-family: 'Raleway';
src: local('Raleway'),
@ -77,7 +84,8 @@
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
font-size: 24px;
/* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;

View File

@ -51,6 +51,7 @@ html {
--lightChatHeadHover: #1e1f201a;
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
--gifs-drop-shadow: #32326926 0px 2px 5px 0px, #0000000d 0px 1px 1px 0px;
}
html[theme="dark"] {
@ -105,5 +106,6 @@ html[theme="dark"] {
--chatHeadTextActive: #ffffff;
--lightChatHeadHover: #e0e1e31a;
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px;
--gifs-drop-shadow: 0px 2px 2px 0px hsla(0, 0%, 0%, 0.14), 0px 3px 1px -2px hsla(0, 0%, 0%, 0.12), 0px 1px 5px 0px hsla(0, 0%, 0%, 0.2);
}

View File

@ -572,8 +572,9 @@
"cchange63": "Enter Enabled",
"cchange64": "Enter Disabled",
"cchange65": "Please enter a recipient",
"cchange66": "Cannot fetch replied-to message. Message is too old."
"cchange66": "Cannot fetch replied-to message. Message is too old.",
"cchange80": "Gif Explorer",
"cchange81": "Explore Collections"
},
"welcomepage": {
"wcchange1": "Welcome to Q-Chat",

View File

@ -17,7 +17,7 @@
"author": "QORTAL <admin@qortal.org>",
"license": "GPL-3.0",
"dependencies": {
"@hapi/hapi": "21.2.0",
"@hapi/hapi": "21.2.1",
"@hapi/inert": "7.0.0",
"sass": "1.57.1"
},
@ -58,12 +58,12 @@
"@rollup/plugin-commonjs": "24.0.1",
"@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-terser": "0.3.0",
"@vaadin/button": "23.3.5",
"@vaadin/grid": "23.3.5",
"@vaadin/icons": "23.3.5",
"@vaadin/password-field": "23.3.5",
"@vaadin/tooltip": "23.3.5",
"@rollup/plugin-terser": "0.4.0",
"@vaadin/button": "23.3.6",
"@vaadin/grid": "23.3.6",
"@vaadin/icons": "23.3.6",
"@vaadin/password-field": "23.3.6",
"@vaadin/tooltip": "23.3.6",
"asmcrypto.js": "2.3.2",
"bcryptjs": "2.4.3",
"epml": "0.3.3",
@ -73,7 +73,7 @@
"pwa-helpers": "0.9.1",
"redux": "4.2.0",
"redux-thunk": "2.4.2",
"rollup": "3.10.1",
"rollup": "3.12.0",
"rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-scss": "3.0.0",

View File

@ -48,6 +48,7 @@ html {
--chatHeadTextActive: #080808;
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
--gifs-drop-shadow: #32326926 0px 2px 5px 0px, #0000000d 0px 1px 1px 0px;
}
html[theme="dark"] {
@ -99,5 +100,6 @@ html[theme="dark"] {
--chatHeadText: #ffffff;
--chatHeadTextActive: #ffffff;
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px;
--gifs-drop-shadow: 0px 2px 2px 0px hsla(0, 0%, 0%, 0.14), 0px 3px 1px -2px hsla(0, 0%, 0%, 0.12), 0px 1px 5px 0px hsla(0, 0%, 0%, 0.2);
}

View File

@ -21,15 +21,16 @@
"@material/mwc-list": "0.27.0",
"@material/mwc-select": "0.27.0",
"@tiptap/core": "2.0.0-beta.209",
"@tiptap/extension-highlight": "2.0.0-beta.209",
"@tiptap/extension-image": "2.0.0-beta.209",
"@tiptap/extension-placeholder": "2.0.0-beta.209",
"@tiptap/extension-underline": "2.0.0-beta.209",
"@tiptap/extension-highlight": "2.0.0-beta.209",
"@tiptap/html": "2.0.0-beta.209",
"@tiptap/starter-kit": "2.0.0-beta.209",
"asmcrypto.js": "2.3.2",
"compressorjs": "1.1.1",
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js",
"localforage": "1.10.0",
"prosemirror-commands": "1.5.0",
"prosemirror-dropcursor": "1.6.1",
"prosemirror-gapcursor": "1.3.1",
@ -40,7 +41,6 @@
"prosemirror-state": "1.4.2",
"prosemirror-transform": "1.7.0",
"prosemirror-view": "1.29.1",
"localforage": "1.10.0",
"short-unique-id": "4.4.4"
},
"devDependencies": {
@ -48,6 +48,7 @@
"@material/mwc-button": "0.27.0",
"@material/mwc-checkbox": "0.27.0",
"@material/mwc-dialog": "0.27.0",
"@material/mwc-fab": "0.27.0",
"@material/mwc-formfield": "0.27.0",
"@material/mwc-icon": "0.27.0",
"@material/mwc-icon-button": "0.27.0",
@ -68,26 +69,27 @@
"@rollup/plugin-commonjs": "24.0.1",
"@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-terser": "0.3.0",
"@vaadin/avatar": "23.3.5",
"@vaadin/button": "23.3.5",
"@vaadin/grid": "23.3.5",
"@vaadin/icons": "23.3.5",
"@vaadin/tooltip": "23.3.5",
"@rollup/plugin-terser": "0.4.0",
"@vaadin/avatar": "23.3.6",
"@vaadin/button": "23.3.6",
"@vaadin/grid": "23.3.6",
"@vaadin/icons": "23.3.6",
"@vaadin/tooltip": "23.3.6",
"@zip.js/zip.js": "^2.6.62",
"epml": "0.3.3",
"file-saver": "2.0.5",
"highcharts": "10.3.3",
"html-escaper": "3.0.3",
"lit": "2.6.1",
"lit-translate": "2.0.1",
"rollup": "3.10.1",
"passive-events-support": "1.0.33",
"rollup": "3.12.0",
"rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-web-worker-loader": "1.6.1",
"@zip.js/zip.js": "2.6.62",
"validator": "13.7.0"
"validator": "^13.7.0"
},
"engines": {
"node": ">=16.17.1"
}
}
}

View File

@ -1,585 +0,0 @@
import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js'
import { Epml } from '../../../epml.js'
import * as zip from "@zip.js/zip.js";
import { saveAs } from 'file-saver';
import '@material/mwc-icon'
import ShortUniqueId from 'short-unique-id';
import { publishData } from '../../utils/publish-image.js';
import { get } from 'lit-translate';
import './ChatGifsExplore.js'
// import isAlphanumeric from 'validator/lib/isAlphanumeric'/
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
class ChatGifs extends LitElement {
static get properties() {
return {
selectedAddress: { type: Object },
myGifCollections: { type: Array },
mySubscribedCollections: {type: Array},
exploreCollections: { type: Array },
gifsToBeAdded: { type: Array},
webWorkerImage: {type: Object},
mode: {type: String},
currentCollection: {type: String},
isLoading: {type: String},
newCollectionName: {type: String}
}
}
static get styles() {
return css`
`
}
constructor() {
super()
this.uid = new ShortUniqueId()
this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress
this.myGifCollections = []
this.mySubscribedCollections = []
this.exploreCollections = []
this.myAccountName = ''
this.gifsToBeAdded = []
// mode can be 'myCollection', 'newCollection', 'explore', 'subscribedCollection'
this.mode = "myCollection"
this.currentCollection = null
this.pageNumber = 0
this.isLoading = false
this.newCollectionName = ""
}
async structureCollections(gifCollections){
try {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const getMetaDataGifs = (gifCollections || []).map(async (collection) => {
let collectionObj = collection
try {
const metaData = await parentEpml.request('apiCall', {
url: `/arbitrary/metadata/GIF_REPOSITORY/${this.myAccountName}/${collection.identifier}`
})
collectionObj = {
...collection,
gifUrls: []
}
if(metaData.files){
const metaDataArray = metaData.files.split(';').map((data)=> {
return `${nodeUrl}/arbitrary/GIF_REPOSITORY/${this.myAccountName}/${collection.identifier}?filepath=${data}`
})
collectionObj = {
...collection,
gifUrls: metaDataArray
}
}
} catch (error) {
console.log(error)
}
return collectionObj
})
return await Promise.all(getMetaDataGifs)
} catch (error) {
}
}
async getMoreExploreGifs(){
try {
const getAllGifCollections = await parentEpml.request("apiCall", {
type: "api",
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=20&offset=${this.pageNumber * 20}`,
});
const gifCollectionWithMetaData = await this.structureCollections(getAllGifCollections)
this.exploreCollections = [...this.exploreCollections, ...gifCollectionWithMetaData]
this.pageNumber = this.pageNumber + 1
} catch (error) {
console.error(error)
}
}
async getCollectionList(){
try {
return await parentEpml.request("apiCall", {
type: "api",
url: `/lists/gifSubscribedRepos`,
});
} catch (error) {
}
}
async addCollectionToList(collection){
try {
const body = {
"items": [
collection
]
}
const bodyToString = JSON.stringify(body)
await parentEpml.request("apiCall", {
type: "api",
method: "POST",
url: `/lists/gifSubscribedRepos`,
body: bodyToString,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
}
}
async removeCollectionFromList(collection){
try {
const body = {
"items": [
collection
]
}
const bodyToString = JSON.stringify(body)
await parentEpml.request("apiCall", {
type: "api",
method: 'DELETE',
url: `/lists/gifSubscribedRepos`,
body: bodyToString,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
}
}
async getMyGifCollections(){
const userName = await this.getName(this.selectedAddress.address);
this.myAccountName = userName
if(this.myAccountName){
const getMyGifColloctions = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${this.myAccountName}`
})
const gifCollectionWithMetaData = await this.structureCollections(getMyGifColloctions)
console.log({gifCollectionWithMetaData})
this.myGifCollections = gifCollectionWithMetaData
}
}
async getAllCollections(){
this.pageNumber = 0
// for the explore section
const getAllGifCollections = await parentEpml.request("apiCall", {
type: "api",
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=20&offset=${this.pageNumber * 20}`,
});
const gifCollectionWithMetaData = await this.structureCollections(getAllGifCollections)
this.exploreCollections = gifCollectionWithMetaData
this.pageNumber = this.pageNumber + 1
}
async getSavedCollections(){
const getCollectionList = await this.getCollectionList()
let savedCollections = []
const getSavedGifRepos = (getCollectionList || []).map(async (collection) => {
let splitCollection = collection.split('/')
const name = splitCollection[0]
const identifier = splitCollection[1]
try {
console.log({collection})
const data = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${name}&identifier=${identifier}`
})
if(data.length > 0){
savedCollections.push(data[0])
}
} catch (error) {
console.log(error)
}
return collection
})
await Promise.all(getSavedGifRepos)
const savedCollectionsWithMetaData = await this.structureCollections(savedCollections)
this.mySubscribedCollections = savedCollectionsWithMetaData
}
async firstUpdated() {
try {
this.isLoading = true
await this.getMyGifCollections()
await this.getAllCollections()
await this.getSavedCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
console.error(error)
}
}
async updated(changedProperties) {
console.log({changedProperties})
if (changedProperties && changedProperties.has('mode')) {
const mode = this.mode
console.log({mode})
if (mode === 'myCollection') {
try {
this.isLoading = true
await this.getMyGifCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
if (mode === 'explore') {
try {
this.isLoading = true
await this.getAllCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
if (mode === 'subscribedCollection') {
try {
this.isLoading = true
await this.getSavedCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
}
}
async getName (recipient) {
try {
const getNames = await parentEpml.request("apiCall", {
type: "api",
url: `/names/address/${recipient}`,
});
if (Array.isArray(getNames) && getNames.length > 0 ) {
return getNames[0].name
} else {
return ''
}
} catch (error) {
return ""
}
}
addGifs(gifs){
console.log('gifs', gifs)
const mapGifs = gifs.map((file)=> {
return {
file,
name: file.name
}
})
console.log({mapGifs})
this.gifsToBeAdded = [...this.gifsToBeAdded, ...mapGifs]
console.log('this.gifsToBeAdded', this.gifsToBeAdded)
}
async uploadGifCollection(){
if(!this.newCollectionName){
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
return
}
// if(!isAlphanumeric(this.newCollectionName)){
// parentEpml.request('showSnackBar', get("chatpage.cchange27"));
// return
// }
try {
const userName = await this.getName(this.selectedAddress.address);
const doesNameExist = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${userName}&identifier=${this.newCollectionName}`
})
if(doesNameExist.length !== 0){
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
return
}
function blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
const zipFileWriter = new zip.BlobWriter("application/zip");
// Creates a TextReader object storing the text of the entry to add in the zip
// (i.e. "Hello world!").
const helloWorldReader = new zip.TextReader("Hello world!");
// Creates a ZipWriter object writing data via `zipFileWriter`, adds the entry
// "hello.txt" containing the text "Hello world!" via `helloWorldReader`, and
// closes the writer.
const zipWriter = new zip.ZipWriter(zipFileWriter, { bufferedWrite: true });
for (let i = 0; i < this.gifsToBeAdded.length; i++) {
await zipWriter.add(this.gifsToBeAdded[i].name, new zip.BlobReader(this.gifsToBeAdded[i].file));
}
await zipWriter.close();
const zipFileBlob = await zipFileWriter.getData()
const blobTobase = await blobToBase64(zipFileBlob)
console.log({blobTobase})
if (!userName) {
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
this.isLoading = false;
return;
}
const id = this.uid();
const identifier = `gif_${id}`;
await publishData({
registeredName: userName,
file : blobTobase.split(',')[1],
service: 'GIF_REPOSITORY',
identifier: this.newCollectionName,
parentEpml,
metaData: undefined,
uploadType: 'zip',
selectedAddress: this.selectedAddress,
worker: this.webWorkerImage,
isBase64: true
})
await new Promise((res)=> {
let interval = null
let stop = false
const getAnswer = async () => {
if (!stop) {
stop = true
try {
let myCollection = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${userName}&identifier=${this.newCollectionName}`
})
if (myCollection.length > 0) {
clearInterval(interval)
res()
}
} catch (error) {}
stop = false
}
}
interval = setInterval(getAnswer, 5000)
})
saveAs(zipFileBlob, 'zipfile');
console.log({zipFileBlob})
} catch (error) {
console.log(error)
}
}
setCurrentCollection(val){
this.currentCollection = val
}
render() {
console.log('this.currentCollection', this.currentCollection)
return html`
<div>
<div class="dialog-container">
<button @click=${()=> {
if(this.isLoading) return
this.mode = "newCollection"
}}>Create Collection</button>
<button @click=${()=> {
if(this.isLoading) return
this.mode = "myCollection"
}}>My collections</button>
<button @click=${()=> {
if(this.isLoading) return
this.mode = "subscribedCollection"
}}>Subscribed colloctions</button>
<button @click=${()=> {
if(this.isLoading) return
this.mode = "explore"
}}>Explore collections</button>
${this.mode === "myCollection" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
${this.myGifCollections.map((collection)=> {
return html`
<div>
<p @click=${()=> {
this.currentCollection = collection
}}>${collection.identifier}</p>
</div>
`
})}
` : ''}
${this.mode === "subscribedCollection" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
${this.mySubscribedCollections.map((collection)=> {
return html`
<div>
<p @click=${()=> {
this.currentCollection = collection
}}>${collection.identifier}</p>
</div>
`
})}
` : ''}
${this.mode === "explore" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
<chat-gifs-explore currentCollection=${this.currentCollection} .getMoreExploreGifs=${(val)=> this.getMoreExploreGifs(val)} .exploreCollections=${this.exploreCollections}
.setCurrentCollection=${(val)=> this.setCurrentCollection(val)}
></chat-gifs-explore>
` : ''}
${this.currentCollection && this.mode === "myCollection" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.currentCollection && this.mode === "subscribedCollection" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.currentCollection && this.mode === "explore" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
<button @click=${()=> {
this.addCollectionToList(`${this.currentCollection.name}/${this.currentCollection.identifier}`)
}}>Subscribe to this collection</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.mode === "newCollection" ? html`
<input
@change="${e => {
this.addGifs(Array.from(e.target.files));
const filePickerInput = this.shadowRoot.getElementById('file-picker-gif')
if(filePickerInput){
filePickerInput.value = ""
}
}
}"
id="file-picker-gif"
?multiple=${true}
class="file-picker-input-gif" type="file" name="myGif" accept="image/gif" />
<button @click=${()=> {
this.uploadGifCollection()
}}>Upload Collection</button>
<input .value=${this.newCollectionName} @change=${(e=> {
this.newCollectionName = e.target.value
})} />
<div style="display: flex; flex-direction: column">
${this.gifsToBeAdded.map((gif, i)=> {
console.log({gif})
return html`
<div style="display: flex">
<img src=${URL.createObjectURL(gif.file)} style="width: 50px; height: 50px" />
<input .value=${gif.name} @change=${(e=> {
this.gifsToBeAdded[i] = {
...gif,
name: e.target.value
}
})} />
</div>
`
})}
</div>
` : ''}
</div>
</div>
`
}
}
window.customElements.define('chat-gifs', ChatGifs)

View File

@ -0,0 +1,41 @@
import { css } from 'lit'
export const gifExplorerStyles = css`
.gif-explorer-container {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
align-items: center;
gap: 15px;
}
.title-row {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.gif-explorer-title {
flex: 1;
text-align: center;
font-family: Roboto, sans-serif;
letter-spacing: 0.8px;
font-size: 25px;
color: var(--chat-bubble-msg-color);
margin: 0;
}
.explore-collections-icon {
margin-left: auto;
text-align: right;
font-size: 20px;
color: var(--chat-bubble-msg-color);
box-shadow: rgba(0, 0, 0, 0.1) 0px 20px 25px -5px, rgba(0, 0, 0, 0.04) 0px 10px 10px -5px;
padding: 5px;
background-color: var(--chat-menu-bg);
border: none;
border-radius: 12px;
}
`

View File

@ -0,0 +1,594 @@
import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js'
import { Epml } from '../../../../epml.js'
import * as zip from "@zip.js/zip.js";
import { saveAs } from 'file-saver';
import '@material/mwc-icon'
import ShortUniqueId from 'short-unique-id';
import { publishData } from '../../../utils/publish-image.js';
import { translate, get } from 'lit-translate';
import { gifExplorerStyles } from "./ChatGifs-css.js";
import './ChatGifsExplore.js';
import '@vaadin/tooltip';
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
class ChatGifs extends LitElement {
static get properties() {
return {
selectedAddress: { type: Object },
myGifCollections: { type: Array },
mySubscribedCollections: {type: Array},
exploreCollections: { type: Array },
gifsToBeAdded: { type: Array},
webWorkerImage: {type: Object},
mode: {type: String},
currentCollection: {type: String},
isLoading: {type: String},
newCollectionName: {type: String}
}
}
static styles = [gifExplorerStyles]
constructor() {
super()
this.uid = new ShortUniqueId()
this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress
this.myGifCollections = []
this.mySubscribedCollections = []
this.exploreCollections = []
this.myAccountName = ''
this.gifsToBeAdded = []
// mode can be 'myCollection', 'newCollection', 'explore', 'subscribedCollection'
this.mode = "myCollection"
this.currentCollection = null
this.pageNumber = 0
this.isLoading = false
this.newCollectionName = ""
}
async structureCollections(gifCollections){
try {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const getMetaDataGifs = (gifCollections || []).map(async (collection) => {
let collectionObj = collection
try {
const metaData = await parentEpml.request('apiCall', {
url: `/arbitrary/metadata/GIF_REPOSITORY/${this.myAccountName}/${collection.identifier}`
})
collectionObj = {
...collection,
gifUrls: []
}
if(metaData.files){
const metaDataArray = metaData.files.map((data)=> {
return `${nodeUrl}/arbitrary/GIF_REPOSITORY/${this.myAccountName}/${collection.identifier}?filepath=${data}`
})
collectionObj = {
...collection,
gifUrls: metaDataArray
}
}
} catch (error) {
console.log(error)
}
return collectionObj
})
return await Promise.all(getMetaDataGifs)
} catch (error) {
}
}
async getMoreExploreGifs(){
try {
const getAllGifCollections = await parentEpml.request("apiCall", {
type: "api",
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=20&offset=${this.pageNumber * 20}`,
});
const gifCollectionWithMetaData = await this.structureCollections(getAllGifCollections)
this.exploreCollections = [...this.exploreCollections, ...gifCollectionWithMetaData]
this.pageNumber = this.pageNumber + 1
} catch (error) {
console.error(error)
}
}
async getCollectionList(){
try {
return await parentEpml.request("apiCall", {
type: "api",
url: `/lists/gifSubscribedRepos`,
});
} catch (error) {
}
}
async addCollectionToList(collection){
try {
const body = {
"items": [
collection
]
}
const bodyToString = JSON.stringify(body)
await parentEpml.request("apiCall", {
type: "api",
method: "POST",
url: `/lists/gifSubscribedRepos`,
body: bodyToString,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
}
}
async removeCollectionFromList(collection){
try {
const body = {
"items": [
collection
]
}
const bodyToString = JSON.stringify(body)
await parentEpml.request("apiCall", {
type: "api",
method: 'DELETE',
url: `/lists/gifSubscribedRepos`,
body: bodyToString,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
}
}
async getMyGifCollections(){
const userName = await this.getName(this.selectedAddress.address);
this.myAccountName = userName
if(this.myAccountName){
const getMyGifColloctions = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${this.myAccountName}`
})
const gifCollectionWithMetaData = await this.structureCollections(getMyGifColloctions)
console.log({gifCollectionWithMetaData})
this.myGifCollections = gifCollectionWithMetaData
}
}
async getAllCollections(){
this.pageNumber = 0
// for the explore section
const getAllGifCollections = await parentEpml.request("apiCall", {
type: "api",
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=20&offset=${this.pageNumber * 20}`,
});
const gifCollectionWithMetaData = await this.structureCollections(getAllGifCollections)
this.exploreCollections = gifCollectionWithMetaData
this.pageNumber = this.pageNumber + 1
}
async getSavedCollections(){
const getCollectionList = await this.getCollectionList()
let savedCollections = []
const getSavedGifRepos = (getCollectionList || []).map(async (collection) => {
let splitCollection = collection.split('/')
const name = splitCollection[0]
const identifier = splitCollection[1]
try {
console.log({collection})
const data = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${name}&identifier=${identifier}`
})
if(data.length > 0){
savedCollections.push(data[0])
}
} catch (error) {
console.log(error)
}
return collection
})
await Promise.all(getSavedGifRepos)
const savedCollectionsWithMetaData = await this.structureCollections(savedCollections)
this.mySubscribedCollections = savedCollectionsWithMetaData
}
async firstUpdated() {
const tooltip = this.shadowRoot.querySelector('vaadin-tooltip');
const overlay = tooltip.shadowRoot.querySelector('vaadin-tooltip-overlay');
overlay.shadowRoot.getElementById("overlay").style.cssText = "background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px";
overlay.shadowRoot.getElementById('content').style.cssText = "background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;";
try {
this.isLoading = true
await this.getMyGifCollections()
await this.getAllCollections()
await this.getSavedCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
console.error(error)
}
}
async updated(changedProperties) {
console.log({changedProperties})
if (changedProperties && changedProperties.has('mode')) {
const mode = this.mode
console.log({mode})
if (mode === 'myCollection') {
try {
this.isLoading = true
await this.getMyGifCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
if (mode === 'explore') {
try {
this.isLoading = true
await this.getAllCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
if (mode === 'subscribedCollection') {
try {
this.isLoading = true
await this.getSavedCollections()
this.isLoading = false
} catch (error) {
this.isLoading = false
}
}
}
}
async getName (recipient) {
try {
const getNames = await parentEpml.request("apiCall", {
type: "api",
url: `/names/address/${recipient}`,
});
if (Array.isArray(getNames) && getNames.length > 0 ) {
return getNames[0].name
} else {
return ''
}
} catch (error) {
return ""
}
}
addGifs(gifs){
console.log('gifs', gifs)
const mapGifs = gifs.map((file)=> {
return {
file,
name: file.name
}
})
console.log({mapGifs})
this.gifsToBeAdded = [...this.gifsToBeAdded, ...mapGifs]
console.log('this.gifsToBeAdded', this.gifsToBeAdded)
}
async uploadGifCollection(){
if(!this.newCollectionName){
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
return
}
// if(!isAlphanumeric(this.newCollectionName)){
// parentEpml.request('showSnackBar', get("chatpage.cchange27"));
// return
// }
try {
const userName = await this.getName(this.selectedAddress.address);
const doesNameExist = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${userName}&identifier=${this.newCollectionName}`
})
if(doesNameExist.length !== 0){
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
return
}
function blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
const zipFileWriter = new zip.BlobWriter("application/zip");
// Creates a TextReader object storing the text of the entry to add in the zip
// (i.e. "Hello world!").
const helloWorldReader = new zip.TextReader("Hello world!");
// Creates a ZipWriter object writing data via `zipFileWriter`, adds the entry
// "hello.txt" containing the text "Hello world!" via `helloWorldReader`, and
// closes the writer.
const zipWriter = new zip.ZipWriter(zipFileWriter, { bufferedWrite: true });
for (let i = 0; i < this.gifsToBeAdded.length; i++) {
await zipWriter.add(this.gifsToBeAdded[i].name, new zip.BlobReader(this.gifsToBeAdded[i].file));
}
await zipWriter.close();
const zipFileBlob = await zipFileWriter.getData()
const blobTobase = await blobToBase64(zipFileBlob)
console.log({blobTobase})
if (!userName) {
parentEpml.request('showSnackBar', get("chatpage.cchange27"));
this.isLoading = false;
return;
}
const id = this.uid();
const identifier = `gif_${id}`;
await publishData({
registeredName: userName,
file : blobTobase.split(',')[1],
service: 'GIF_REPOSITORY',
identifier: this.newCollectionName,
parentEpml,
metaData: undefined,
uploadType: 'zip',
selectedAddress: this.selectedAddress,
worker: this.webWorkerImage,
isBase64: true
})
await new Promise((res)=> {
let interval = null
let stop = false
const getAnswer = async () => {
if (!stop) {
stop = true
try {
let myCollection = await parentEpml.request('apiCall', {
url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${userName}&identifier=${this.newCollectionName}`
})
if (myCollection.length > 0) {
clearInterval(interval)
res()
}
} catch (error) {}
stop = false
}
}
interval = setInterval(getAnswer, 5000)
})
saveAs(zipFileBlob, 'zipfile');
console.log({zipFileBlob})
} catch (error) {
console.log(error)
}
}
setCurrentCollection(val){
this.currentCollection = val
}
render() {
console.log('this.currentCollection', this.currentCollection)
console.log(3, "chat gifs here")
return html`
<div class="gif-explorer-container">
<div class="title-row">
<p class="gif-explorer-title">${translate("chatpage.cchange80")}</p>
<vaadin-icon
id="explore-collections-icon"
class="explore-collections-icon"
@click=${() => {
if(this.isLoading) return;
this.mode = "explore";
}}
icon="vaadin:search"
slot="icon">
</vaadin-icon>
<vaadin-tooltip
for="explore-collections-icon"
position="top"
hover-delay=${400}
hide-delay=${1}
text=${get("chatpage.cchange81")}>
</vaadin-tooltip>
</div>
<div>
<button
id="create-collection-button"
@click=${()=> {
if(this.isLoading) return
this.mode = "newCollection"
}}>
Create Collection
</button>
<button
id="my-collections-button"
@click=${()=> {
if(this.isLoading) return
this.mode = "myCollection"
}}>
My Collections
</button>
<button id="subscribed-collections-button"
@click=${()=> {
if(this.isLoading) return
this.mode = "subscribedCollection"
}}>
Subscribed Collections
</button>
${this.mode === "myCollection" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
${this.myGifCollections.map((collection)=> {
return html`
<div>
<p @click=${()=> {
this.currentCollection = collection
}}>${collection.identifier}</p>
</div>
`
})}
` : ''}
${this.mode === "subscribedCollection" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
${this.mySubscribedCollections.map((collection)=> {
return html`
<div>
<p @click=${()=> {
this.currentCollection = collection
}}>${collection.identifier}</p>
</div>
`
})}
` : ''}
${this.mode === "explore" && !this.currentCollection ? html`
${this.isLoading === true ? html`
<p>Loading...</p>
` : ''}
<chat-gifs-explore currentCollection=${this.currentCollection} .getMoreExploreGifs=${(val)=> this.getMoreExploreGifs(val)} .exploreCollections=${this.exploreCollections}
.setCurrentCollection=${(val)=> this.setCurrentCollection(val)}
></chat-gifs-explore>
` : ''}
${this.currentCollection && this.mode === "myCollection" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.currentCollection && this.mode === "subscribedCollection" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.currentCollection && this.mode === "explore" ? html`
<button @click=${()=> {
this.currentCollection = null
}}>Back</button>
<button @click=${()=> {
this.addCollectionToList(`${this.currentCollection.name}/${this.currentCollection.identifier}`)
}}>Subscribe to this collection</button>
${this.currentCollection.gifUrls.map((gif)=> {
console.log({gif})
return html`
<img onerror=${(e)=> {
e.target.src = gif
}} src=${gif} style="width: 50px; height: 50px" />
`
})}
` : ''}
${this.mode === "newCollection" ? html`
<input
@change="${e => {
this.addGifs(Array.from(e.target.files));
const filePickerInput = this.shadowRoot.getElementById('file-picker-gif')
if(filePickerInput){
filePickerInput.value = ""
}
}
}"
id="file-picker-gif"
?multiple=${true}
class="file-picker-input-gif" type="file" name="myGif" accept="image/gif" />
<button @click=${()=> {
this.uploadGifCollection()
}}>Upload Collection</button>
<input .value=${this.newCollectionName} @change=${(e=> {
this.newCollectionName = e.target.value
})} />
<div style="display: flex; flex-direction: column">
${this.gifsToBeAdded.map((gif, i)=> {
console.log({gif})
return html`
<div style="display: flex">
<img src=${URL.createObjectURL(gif.file)} style="width: 50px; height: 50px" />
<input .value=${gif.name} @change=${(e=> {
this.gifsToBeAdded[i] = {
...gif,
name: e.target.value
}
})} />
</div>
`
})}
</div>
` : ''}
</div>
</div>
`
}
}
window.customElements.define('chat-gifs', ChatGifs)

View File

@ -1,11 +1,11 @@
import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js'
import { Epml } from '../../../epml.js'
import { Epml } from '../../../../epml.js'
import * as zip from "@zip.js/zip.js";
import { saveAs } from 'file-saver';
import '@material/mwc-icon'
import ShortUniqueId from 'short-unique-id';
import { publishData } from '../../utils/publish-image.js';
import { publishData } from '../../../utils/publish-image.js';
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@ class ChatTextEditor extends LitElement {
},
toggleEnableChatEnter: {attribute: false},
isEnabledChatEnter: {type: Boolean},
openGifModal: { type: Boolean },
setOpenGifModal: {attribute: false}
}
}
@ -361,69 +362,63 @@ class ChatTextEditor extends LitElement {
return html`
<div
class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")}
style="align-items: center;">
class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")}
style="align-items: center;">
<button
@click=${() => this.editor.chain().focus().toggleBold().run()}
?disabled=${
this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleBold()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}
>
<!-- <mwc-icon >format_bold</mwc-icon> -->
<span class="material-symbols-outlined">&#xe238;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleItalic().run()}
?disabled=${ this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleItalic()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe23f;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
@click=${() => this.editor.chain().focus().toggleBold().run()}
?disabled=${
this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleBold()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}>
<span class="material-symbols-outlined">&#xe238;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleItalic().run()}
?disabled=${ this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleItalic()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe23f;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
</div>
<div
class=${["chatbar-container", (this.iframeId === "newChat" || this.iframeId === "privateMessage") ? "chatbar-caption" : ""].join(" ")}

View File

@ -163,8 +163,6 @@ class Chat extends LitElement {
}
render() {
console.log(12, "q-chat here");
console.log(window.location.href);
return html`
<div class="container clearfix">
<div class="people-list" id="people-list">