diff --git a/qortal-ui-core/font/MavenPro.ttf b/qortal-ui-core/font/MavenPro.ttf
new file mode 100644
index 00000000..ab146a80
Binary files /dev/null and b/qortal-ui-core/font/MavenPro.ttf differ
diff --git a/qortal-ui-core/font/PaytoneOne.ttf b/qortal-ui-core/font/PaytoneOne.ttf
new file mode 100644
index 00000000..5cb0bbb8
Binary files /dev/null and b/qortal-ui-core/font/PaytoneOne.ttf differ
diff --git a/qortal-ui-core/font/material-icons.css b/qortal-ui-core/font/material-icons.css
index 881a68be..6340d60f 100644
--- a/qortal-ui-core/font/material-icons.css
+++ b/qortal-ui-core/font/material-icons.css
@@ -26,6 +26,12 @@
url(Montserrat.ttf) format('truetype');
}
+@font-face {
+ font-family: 'MavenPro';
+ src: local('MavenPro'),
+ local('MavenPro'),
+ url(Montserrat.ttf) format('truetype');
+}
@font-face {
font-family: 'WorkSans';
src: local('WorkSans'),
@@ -54,12 +60,19 @@
url(Livvic.ttf) format('truetype');
}
+@font-face {
+ font-family: 'Paytone One', sans-serif;
+ src: local('PaytoneOne'),
+ local('PaytoneOne'),
+ url(PaytoneOne.ttf) format('truetype');
+}
+
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
- /* Preferred icon size */
+ /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
diff --git a/qortal-ui-core/font/switch-theme.css b/qortal-ui-core/font/switch-theme.css
index c8b139c3..13f38f37 100644
--- a/qortal-ui-core/font/switch-theme.css
+++ b/qortal-ui-core/font/switch-theme.css
@@ -53,6 +53,13 @@ html {
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
--reactions-tooltip-bg: #ffffff;
+ --gifs-drop-shadow: #32326926 0px 2px 5px 0px, #0000000d 0px 1px 1px 0px;
+ --gif-tooltip-bg: #dad7ef;
+ --gif-search-icon-bs: rgb(17 17 26 / 10%) 0px 4px 16px, rgb(17 17 26 / 5%) 0px 8px 32px;
+ --gif-search-icon: #ffffff;
+ --gif-button-row-bg: #eaeaef;
+ --gif-button-row-color: #464040;
+ --gif-collection-hover-bg: #eaeaefa3;
}
html[theme="dark"] {
@@ -110,4 +117,11 @@ html[theme="dark"] {
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px;
--reactions-tooltip-bg: #161515;
+ --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);
+ --gif-tooltip-bg: #586b8d;
+ --gif-search-icon-bs: 0px 8px 10px 1px hsla(0, 0%, 0%, 0.14), 0px 3px 14px 2px hsla(0, 0%, 0%, 0.12), 0px 5px 5px -3px hsla(0, 0%, 0%, 0.2);
+ --gif-search-icon: #586b8d;
+ --gif-button-row-bg: #82899c;
+ --gif-button-row-color: #151212;
+ --gif-collection-hover-bg: #ffffff26;
}
\ No newline at end of file
diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json
index 008bee19..088c7d54 100644
--- a/qortal-ui-core/language/us.json
+++ b/qortal-ui-core/language/us.json
@@ -165,6 +165,34 @@
"balances": "YOUR WALLET BALANCES",
"update": "UPDATE WALLET BALANCES"
},
+ "gifs": {
+ "gchange1": "Gif Explorer",
+ "gchange2": "Explore Collections",
+ "gchange3": "My Collections",
+ "gchange4": "Subscribed Collections",
+ "gchange5": "Upload your gif files",
+ "gchange6": "File should be .Gif",
+ "gchange7": "Upload Collection",
+ "gchange8": "A collection name is required!",
+ "gchange9": "Collection Name",
+ "gchange10": "Gif Collection Uploaded Successfully!",
+ "gchange11": "Gifs uploading, please wait...",
+ "gchange12": "Something went wrong! Please try changing tabs and coming back.",
+ "gchange13": "You currently have no collections.",
+ "gchange14": "You currently have no subscribed collections.",
+ "gchange15": "Error fetching GIF. Retrying...",
+ "gchange16": "Failed to fetch GIF! Please visit another collection and try again!",
+ "gchange17": "Subscribe to this collection",
+ "gchange18": "Searching for collections...",
+ "gchange19": "No collections found!",
+ "gchange20": "Subscribed to collection successfully!",
+ "gchange21": "Unsubscribed to collection successfully!",
+ "gchange22": "Unsubscribe from this collection",
+ "gchange23": "Your gif collection cannot contain two gifs with the same name!",
+ "gchange24": "This collection name is already taken. Try another name!",
+ "gchange25": "GIF (click to view)",
+ "gchange26": "A name is needed to access and send GIF files"
+ },
"startminting": {
"smchange1": "Cannot fetch minting accounts",
"smchange2": "Failed to remove key",
diff --git a/qortal-ui-core/src/styles/switch-theme.css b/qortal-ui-core/src/styles/switch-theme.css
index 130e8b8d..2a700a48 100644
--- a/qortal-ui-core/src/styles/switch-theme.css
+++ b/qortal-ui-core/src/styles/switch-theme.css
@@ -50,6 +50,13 @@ html {
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
--reactions-tooltip-bg: #ffffff;
+ --gifs-drop-shadow: #32326926 0px 2px 5px 0px, #0000000d 0px 1px 1px 0px;
+ --gif-tooltip-bg: #dad7ef;
+ --gif-search-icon-bs: rgb(17 17 26 / 10%) 0px 4px 16px, rgb(17 17 26 / 5%) 0px 8px 32px;
+ --gif-search-icon: #ffffff;
+ --gif-button-row-bg: #eaeaef;
+ --gif-button-row-color: #464040;
+ --gif-collection-hover-bg: #eaeaefa3;
}
html[theme="dark"] {
@@ -104,4 +111,11 @@ html[theme="dark"] {
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px;
--reactions-tooltip-bg: #161515;
+ --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);
+ --gif-tooltip-bg: #586b8d;
+ --gif-search-icon-bs: 0px 8px 10px 1px hsla(0, 0%, 0%, 0.14), 0px 3px 14px 2px hsla(0, 0%, 0%, 0.12), 0px 5px 5px -3px hsla(0, 0%, 0%, 0.2);
+ --gif-search-icon: #586b8d;
+ --gif-button-row-bg: #82899c;
+ --gif-button-row-color: #151212;
+ --gif-collection-hover-bg: #ffffff26;
}
\ No newline at end of file
diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json
index 759a24e6..6121f786 100644
--- a/qortal-ui-plugins/package.json
+++ b/qortal-ui-plugins/package.json
@@ -77,6 +77,7 @@
"@vaadin/icons": "23.3.7",
"@vaadin/tooltip": "23.3.7",
"axios": "1.3.3",
+ "@zip.js/zip.js": "^2.6.62",
"epml": "0.3.3",
"file-saver": "2.0.5",
"highcharts": "10.3.3",
@@ -93,4 +94,4 @@
"engines": {
"node": ">=18.12.1"
}
-}
\ No newline at end of file
+}
diff --git a/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs-css.js b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs-css.js
new file mode 100644
index 00000000..7f68de4f
--- /dev/null
+++ b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs-css.js
@@ -0,0 +1,515 @@
+import { css } from 'lit';
+
+export const gifExplorerStyles = css`
+.gifs-container {
+position: relative;
+display: flex;
+padding: 10px 15px;
+border-radius: 12px;
+box-shadow: rgba(0, 0, 0, 0.09) 0px 3px 12px;
+background-color: var(--chat-menu-bg);
+width: fit-content;
+justify-self: flex-end;
+place-self: end flex-end;
+min-height: 400px;
+max-height: calc(95vh - 90px);
+min-width: 370px;
+max-width: 370px;
+box-shadow: var(--gifs-drop-shadow);
+}
+
+.gif-explorer-container {
+min-height: 400px;
+display: flex;
+flex-direction: column;
+justify-content: flex-start;
+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;
+user-select: none;
+}
+
+.explore-collections-icon {
+text-align: right;
+font-size: 20px;
+color: var(--chat-group);
+box-shadow: var(--gif-search-icon-bs);
+padding: 7px;
+background-color: var(--gif-search-icon);
+border: none;
+border-radius: 8px;
+cursor: pointer;
+}
+
+.create-collections-icon {
+position: absolute;
+bottom: 5px;
+left: 50%;
+transform: translateX(-50%);
+padding: 4px;
+font-size: 22px;
+background-color: var(--mdc-theme-primary);
+color: white;
+border-radius: 8px;
+box-shadow: 0 0 0 rgba(0, 0, 0, 0.2);
+transition: all 0.3s ease-in-out;
+}
+
+.create-collections-icon:hover {
+cursor: pointer;
+box-shadow: 0px 4px 5px 0px hsla(0, 0%, 0%, 0.14),
+0px 1px 10px 0px hsla(0, 0%, 0%, 0.12),
+0px 2px 4px -1px hsla(0, 0%, 0%, 0.2);
+}
+
+.collections-button-row {
+width: auto;
+background-color: var(--gif-button-row-bg);
+border-radius: 35px;
+padding: 2px;
+margin-top: 10px;
+}
+
+.collections-button-innerrow {
+display: flex;
+flex-direction: row;
+align-items: center;
+}
+
+.my-collections-button {
+font-size: 16px;
+font-family: 'Maven Pro', sans-serif;
+letter-spacing: 0.5px;
+color: var(--gif-button-row-color);
+border-radius: 35px;
+padding: 8px 20px;
+margin: 2px 0;
+cursor: pointer;
+user-select: none;
+}
+
+.subscribed-collections-button {
+font-size: 16px;
+font-family: 'Maven Pro', sans-serif;
+letter-spacing: 0.5px;
+color: var(--gif-button-row-color);
+border-radius: 35px;
+padding: 8px 20px;
+margin: 2px 0;
+cursor: pointer;
+user-select: none;
+}
+
+.collections-button-active {
+display: flex;
+align-items: center;
+justify-content: center;
+background-color: white;
+color: var(--mdc-theme-primary);
+border-radius: 25px;
+padding: 8px 20px;
+margin: 2px 0;
+box-shadow: rgb(0 0 0 / 14%) 0px 1px 1px 0px,
+rgb(0 0 0 / 12%) 0px 2px 1px -1px, rgb(0 0 0 / 20%) 0px 1px 3px 0px;
+transition: all 0.3s ease-in-out;
+cursor: auto;
+}
+
+.collection-wrapper {
+display: flex;
+flex-direction: column;
+width: 100%;
+height: 100%;
+overflow: hidden;
+}
+
+.collection-gifs {
+display: grid;
+grid-template-columns: repeat(2, 1fr);
+grid-gap: 10px;
+margin-top: 10px;
+overflow-y: auto;
+overflow-x: hidden;
+}
+
+.collection-gifs::-webkit-scrollbar-track {
+background-color: whitesmoke;
+border-radius: 7px;
+}
+
+.collection-gifs::-webkit-scrollbar {
+width: 6px;
+border-radius: 7px;
+background-color: whitesmoke;
+}
+
+.collection-gifs::-webkit-scrollbar-thumb {
+background-color: rgb(180, 176, 176);
+border-radius: 7px;
+transition: all 0.3s ease-in-out;
+}
+
+.collection-gif {
+border-radius: 15px;
+background-color: transparent;
+cursor: pointer;
+width: 100%;
+height: 150px;
+object-fit: cover;
+border: 1px solid transparent;
+transition: all 0.2s cubic-bezier(0, 0.55, 0.45, 1);
+box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px;
+}
+
+.collection-gif:hover {
+border: 1px solid var(--mdc-theme-primary );
+}
+
+.new-collection-row {
+display: flex;
+flex-direction: column;
+justify-content: center;
+height: 100%;
+}
+
+.new-collection-subrow {
+display: flex;
+flex-direction: column;
+justify-content: center;
+align-items: center;
+gap: 5px;
+}
+
+.new-collection-title {
+font-family: Maven Pro, sans-serif;
+color: var(--chat-bubble-msg-color);
+font-size: 18px;
+letter-spacing: 0.6px;
+margin: 0;
+user-select: none;
+}
+
+.new-collection-subtitle {
+font-family: Roboto, sans-serif;
+color: var(--chat-bubble-msg-color);
+font-weight: 300;
+opacity: 0.9;
+font-size: 14px;
+letter-spacing: 0.3px;
+margin: 0;
+user-select: none;
+}
+
+.new-collection-container {
+display: flex;
+margin: 15px 20px;
+border: 3.5px dashed #b898c1;
+border-radius: 10px;
+background-color: #d7d3db2e;
+align-items: center;
+justify-content: center;
+cursor: pointer;
+}
+
+.new-collection-icon {
+font-size: 30px;
+color: var(--mdc-theme-primary);
+}
+
+.gifs-added-col {
+display: flex;
+flex-direction: column;
+justify-content: flex-end;
+flex: 1 1 0%;
+margin-top: 10px;
+overflow-y: auto;
+max-height: 300px;
+}
+
+.gifs-added-row {
+display: flex;
+flex-direction: column;
+gap: 5px;
+overflow-y: auto;
+}
+
+.gifs-added-row .gif-input:last-child {
+border-bottom: none;
+}
+
+.gifs-added-row::-webkit-scrollbar-track {
+background-color: whitesmoke;
+border-radius: 7px;
+}
+
+.gifs-added-row::-webkit-scrollbar {
+width: 6px;
+border-radius: 7px;
+background-color: whitesmoke;
+}
+
+.gifs-added-row::-webkit-scrollbar-thumb {
+background-color: rgb(180, 176, 176);
+border-radius: 7px;
+transition: all 0.3s ease-in-out;
+}
+
+.gif-input {
+display: flex;
+flex-direction: row;
+align-items: center;
+gap: 10px;
+background-color: transparent;
+padding: 15px 5px;
+border-bottom: 1px solid #7b787888;
+}
+
+.gif-input-img {
+width: 70px;
+height: 70px;
+border-radius: 10px;
+}
+
+.gif-input-field {
+height: 30px;
+background-color: transparent;
+border: none;
+color: var(--chat-bubble-msg-color);
+border-bottom: 1px solid var(--chat-bubble-msg-color);
+width: 100%;
+padding: 0;
+margin: 0;
+outline: 0;
+font-size: 16px;
+font-family: Roboto, sans-serif;
+letter-spacing: 0.3px;
+font-weight: 300;
+}
+
+.upload-collection-row {
+display: flex;
+align-items: center;
+justify-content: space-between;
+width: 100%;
+margin-top: 10px;
+}
+
+.upload-collection-name {
+display: block;
+padding: 8px 10px;
+font-size: 16px;
+font-family: Montserrat, sans-serif;
+font-weight: 600;
+background-color: #ebeaea21;
+border: 1px solid var(--mdc-theme-primary);
+border-radius: 5px;
+color: var(--chat-bubble-msg-color);
+outline: none;
+}
+
+.upload-collection-name::placeholder {
+font-size: 16px;
+font-family: Montserrat, sans-serif;
+font-weight: 600;
+opacity: 0.6;
+color: var(--chat-bubble-msg-color);
+}
+
+.collection-back-button {
+ display: flex;
+ font-family: Roboto, sans-serif;
+ font-weight: 300;
+ letter-spacing: 0.3px;
+ font-size: 16px;
+ width: fit-content;
+ gap: 10px;
+ color: var(--chat-bubble-msg-color);
+ flex-direction: row;
+ align-items: center;
+ transition: box-shadow 0.2s ease-in-out;
+ background-color: var(--gif-button-row-bg);
+ border-radius: 3px;
+ box-shadow: rgb(0 0 0 / 20%) 0px 0px 0px;
+ padding: 8px 10px;
+ cursor: pointer;
+}
+
+.collection-back-button:hover {
+ border: none;
+ box-sizing: border-box;
+ box-shadow: rgb(0 0 0 / 14%) 0px 4px 5px 0px, rgb(0 0 0 / 12%) 0px 1px 10px 0px, rgb(0 0 0 / 20%) 0px 2px 4px -1px;
+}
+
+.collection-back-button-arrow {
+ font-size: 10px;
+}
+
+.no-collections {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ color: var(--chat-bubble-msg-color);
+ font-size: 20px;
+ font-family: Paytone One, sans-serif;
+ margin-top: 20px;
+ user-select: none;
+}
+
+.collection-card {
+ display: flex;
+ font-family: Roboto, sans-serif;
+ font-weight: 300;
+ letter-spacing: 0.3px;
+ font-size: 19px;
+ color: var(--chat-bubble-msg-color);
+ flex-direction: row;
+ align-items: center;
+ transition: all 0.3s ease-in-out;
+ box-shadow: none;
+ padding: 10px;
+ cursor: pointer;
+}
+
+.collection-card:hover {
+ border: none;
+ border-radius: 5px;
+ background-color: var(--gif-collection-hover-bg);
+}
+
+.upload-button {
+font-family: Roboto, sans-serif;
+font-size: 16px;
+color: var(--mdc-theme-primary);
+background-color: transparent;
+padding: 8px 10px;
+border-radius: 5px;
+border: none;
+transition: all 0.4s ease-in-out;
+}
+
+.upload-back-button {
+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;
+}
+
+.upload-back-button:hover {
+cursor: pointer;
+background-color: #f4433663;
+}
+
+.upload-button:hover {
+cursor: pointer;
+background-color: #03a8f475;
+}
+
+.lds-circle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: 70px;
+}
+
+.lds-circle > div {
+ display: inline-block;
+ width: 80px;
+ height: 80px;
+ margin: 8px;
+ border-radius: 50%;
+ background: var(--mdc-theme-primary);
+ animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
+}
+
+@keyframes lds-circle {
+ 0%, 100% {
+ animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
+ }
+ 0% {
+ transform: rotateY(0deg);
+ }
+ 50% {
+ transform: rotateY(1800deg);
+ animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
+ }
+ 100% {
+ transform: rotateY(3600deg);
+ }
+}
+
+.gifs-loading-message {
+ font-family: Montserrat, sans-serif;
+ font-size: 20px;
+ font-weight: 600;
+ color: var(--chat-bubble-msg-color);
+ margin: 0 0 10px 0;
+ text-align: center;
+ user-select: none;
+}
+
+.subscribe-button {
+ position: absolute;
+ bottom: 3px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-family: Raleway, sans-serif;
+ font-weight: 500;
+ font-size: 14px;
+ background-color: var(--mdc-theme-primary);
+ border: none;
+ border-radius: 8px;
+ outline: none;
+ padding: 5px 10px;
+ transition: all 0.3s cubic-bezier(0.5, 1, 0.89, 1);
+}
+
+.subscribe-button:hover {
+ cursor: pointer;
+ box-shadow: 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);
+}
+
+.unsubscribe-button {
+ position: absolute;
+ width: max-content;
+ bottom: 3px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-family: Raleway, sans-serif;
+ font-weight: 500;
+ font-size: 14px;
+ background-color: #f44336;
+ border: none;
+ border-radius: 8px;
+ outline: none;
+ padding: 5px 10px;
+ transition: all 0.3s cubic-bezier(0.5, 1, 0.89, 1);
+}
+
+.unsubscribe-button:hover {
+ cursor: pointer;
+ box-shadow: 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);
+}
+
+`;
diff --git a/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs.js b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs.js
new file mode 100644
index 00000000..7b02b003
--- /dev/null
+++ b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifs.js
@@ -0,0 +1,951 @@
+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 '@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 '../ImageComponent.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},
+editor: {type: Object},
+isSubscribed: { type: Boolean },
+setGifsLoading: { attribute: false },
+sendMessage: { attribute: false },
+setOpenGifModal: { attribute: false }
+};
+}
+
+ 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 = [];
+ this.mode = 'myCollection';
+ this.currentCollection = null;
+ this.pageNumber = 0;
+ this.isLoading = false;
+ this.isSubscribed = false;
+ this.newCollectionName = '';
+ this.getAllCollections = this.getAllCollections.bind(this);
+ }
+
+ 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; border-radius: 10px; 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(--gif-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; 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;
+ const myCollections = await this.getMyGifCollections();
+ const savedCollections = await this.getSavedCollections();
+ const allCollections = await this.getAllCollections();
+
+ if (!Array.isArray(myCollections) && !Array.isArray(savedCollections)) {
+ parentEpml.request('showSnackBar', get('gifs.gchange12'));
+ return;
+ }
+
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000)
+ });
+ this.myGifCollections = myCollections;
+ this.mySubscribedCollections = savedCollections;
+ this.exploreCollections = allCollections;
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+ async updated(changedProperties) {
+ if (changedProperties && changedProperties.has('mode')) {
+ const mode = this.mode;
+ if (mode === 'myCollection') {
+ try {
+ this.myGifCollections = [];
+ this.isLoading = true;
+ const collections = await this.getMyGifCollections();
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000)
+ });
+ this.myGifCollections = collections;
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+ if (mode === 'explore') {
+ try {
+ this.exploreCollections = [];
+ this.isLoading = true;
+ const allCollections = await this.getAllCollections();
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000)
+ });
+ this.exploreCollections = allCollections;
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+ if (mode === 'subscribedCollection') {
+ try {
+ this.mySubscribedCollections = [];
+ this.isLoading = true;
+ const savedCollections = await this.getSavedCollections();
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000)
+ });
+ this.mySubscribedCollections = savedCollections;
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+ }
+
+ if (changedProperties && changedProperties.has('currentCollection')) {
+ if (this.mode === 'explore') {
+ const subbedCollection = this.mySubscribedCollections.find((collection) => ((collection.name === this.currentCollection.name) && (collection.identifier === this.currentCollection.identifier)));
+ if (subbedCollection) {
+ this.isSubscribed = true;
+ } else {
+ this.isSubscribed = false;
+ }
+ }
+ }
+ }
+
+ async structureCollections(gifCollections) {
+ const userName = await this.getName(this.selectedAddress.address);
+ if (!userName) {
+ return;
+ }
+ 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}?apiKey=${this.getApiKey()}`,
+ });
+
+ collectionObj = {
+ ...collection,
+ gifUrls: [],
+ };
+ if (metaData.files) {
+ const metaDataArray = metaData.files.map((data) => {
+ return {
+ url: `${nodeUrl}/arbitrary/GIF_REPOSITORY/${this.myAccountName}/${collection.identifier}?filepath=${data}&apiKey=${this.getApiKey()}`,
+ filePath: data,
+ identifier: collection.identifier,
+ name: this.myAccountName
+ };
+ });
+
+ collectionObj = {
+ ...collection,
+ gifUrls: metaDataArray,
+ };
+ }
+ } catch (error) {
+ console.log(error);
+ }
+
+ return collectionObj;
+ }
+ );
+ return await Promise.all(getMetaDataGifs);
+ } catch (error) {}
+ }
+
+ getApiKey() {
+ const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
+ let apiKey = myNode.apiKey;
+ return apiKey;
+ }
+
+ async getMoreExploreGifs() {
+ try {
+ const getAllGifCollections = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=20&offset=${
+ this.pageNumber * 20
+ }&apiKey=${this.getApiKey()}`,
+ });
+
+ 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?apiKey=${this.getApiKey()}`,
+ });
+ } 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?apiKey=${this.getApiKey()}`,
+ 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?apiKey=${this.getApiKey()}`,
+ 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 getMyGifCollections = await parentEpml.request('apiCall', {
+ url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${this.myAccountName}&apiKey=${this.getApiKey()}`,
+ });
+ const gifCollectionWithMetaData = await this.structureCollections(
+ getMyGifCollections
+ );
+
+ return gifCollectionWithMetaData;
+ } else {
+ return [];
+ }
+ }
+
+ 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
+ }&apiKey=${this.getApiKey()}`,
+ });
+ const gifCollectionWithMetaData = await this.structureCollections(
+ getAllGifCollections
+ );
+ this.pageNumber = this.pageNumber + 1;
+ return gifCollectionWithMetaData;
+ }
+
+ 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 {
+ const data = await parentEpml.request('apiCall', {
+ url: `/arbitrary/resources?service=GIF_REPOSITORY&limit=0&name=${name}&identifier=${identifier}&apiKey=${this.getApiKey()}`,
+ });
+ 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
+ );
+ return savedCollectionsWithMetaData;
+ }
+
+ async getName(recipient) {
+ try {
+ const getNames = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/names/address/${recipient}?apiKey=${this.getApiKey()}`,
+ });
+
+ if (Array.isArray(getNames) && getNames.length > 0) {
+ return getNames[0].name;
+ } else {
+ return '';
+ }
+ } catch (error) {
+ return '';
+ }
+ }
+
+ removeDotGIF(arr) {
+ return arr.map(obj => {
+ const newObj = { ...obj };
+ if (newObj.hasOwnProperty('name') && newObj.name.endsWith('.gif')) {
+ newObj.name = newObj.name.slice(0, -4);
+ }
+ return newObj;
+ });
+ }
+
+ addDotGIF(arr) {
+ return arr.map(obj => {
+ const newObj = { ...obj };
+ if (newObj.hasOwnProperty('name') && !newObj.name.endsWith('.gif')) {
+ newObj.name += '.gif';
+ }
+ return newObj;
+ });
+ }
+
+ addGifs(gifs) {
+ const mapGifs = gifs.map((file) => {
+ return {
+ file,
+ name: file.name,
+ };
+ });
+ const removedExtensions = this.removeDotGIF(mapGifs);
+ this.gifsToBeAdded = [...this.gifsToBeAdded, ...removedExtensions];
+ }
+
+ async uploadGifCollection() {
+ if (!this.newCollectionName) {
+ parentEpml.request('showSnackBar', get('gifs.gchange8'));
+ return;
+ }
+ try {
+ this.setGifsLoading(true);
+ this.isLoading = true;
+ 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}&apiKey=${this.getApiKey()}`,
+ });
+
+ if (!userName) {
+ parentEpml.request('showSnackBar', get('chatpage.cchange27'));
+ this.setGifsLoading(false);
+ this.isLoading = false;
+ return;
+ }
+
+ if (doesNameExist.length !== 0) {
+ parentEpml.request('showSnackBar', get('gifs.gchange24'));
+ this.isLoading = false;
+ this.setGifsLoading(false);
+ return;
+ }
+
+ function validateDuplicateGifNames(arr) {
+ let names = [];
+ for (let i = 0; i < arr.length; i++) {
+ if (names.includes(arr[i].name)) {
+ return false;
+ }
+ names.push(arr[i].name);
+ }
+ return true;
+ }
+
+ let result = validateDuplicateGifNames(this.gifsToBeAdded);
+
+ if (!result) {
+ parentEpml.request('showSnackBar', get('gifs.gchange23'));
+ this.isLoading = false;
+ this.setGifsLoading(false);
+ return;
+ }
+
+ const addedGifExtensionsArr = this.addDotGIF(this.gifsToBeAdded);
+
+ 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');
+
+ const zipWriter = new zip.ZipWriter(zipFileWriter, {
+ bufferedWrite: true,
+ });
+
+ for (let i = 0; i < addedGifExtensionsArr.length; i++) {
+ await zipWriter.add(
+ addedGifExtensionsArr[i].name,
+ new zip.BlobReader(addedGifExtensionsArr[i].file)
+ );
+ }
+
+ await zipWriter.close();
+
+ const zipFileBlob = await zipFileWriter.getData();
+
+ const blobTobase = await blobToBase64(zipFileBlob);
+
+
+ await publishData({
+ registeredName: userName,
+ file: blobTobase.split(',')[1],
+ service: 'GIF_REPOSITORY',
+ identifier: this.newCollectionName,
+ parentEpml,
+ metaData: `title=${this.newCollectionName}`,
+ 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}&apiKey=${this.getApiKey()}`,
+ }
+ );
+ if (myCollection.length > 0) {
+ clearInterval(interval);
+ res();
+ }
+ } catch (error) {
+ console.error(error);
+ this.isLoading = false;
+ this.setGifsLoading(false);
+ this.mode = 'myCollection';
+ this.gifsToBeAdded = [];
+ this.newCollectionName = '';
+ parentEpml.request('showSnackBar', get('gifs.gchange12'));
+ }
+ stop = false;
+ }
+ };
+ interval = setInterval(getAnswer, 5000);
+ });
+
+ this.isLoading = false;
+ this.setGifsLoading(false);
+ this.mode = 'myCollection';
+ this.gifsToBeAdded = [];
+ this.newCollectionName = '';
+ parentEpml.request('showSnackBar', get('gifs.gchange10'));
+ } catch (error) {
+ console.log(error);
+ parentEpml.request('showSnackBar', get('gifs.gchange12'));
+ this.setGifsLoading(false);
+ this.isLoading = false;
+ }
+ }
+
+ setCurrentCollection(val) {
+ this.currentCollection = val;
+ }
+
+ clearGifSelections() {
+ this.mode = 'myCollection';
+ this.gifsToBeAdded = [];
+ }
+
+ async subscribeToCollection() {
+ await this.addCollectionToList(
+ `${this.currentCollection.name}/${this.currentCollection.identifier}`
+ );
+ parentEpml.request('showSnackBar', get('gifs.gchange20'));
+ this.isSubscribed = true;
+ const savedCollections = await this.getSavedCollections();
+ this.mySubscribedCollections = savedCollections;
+ }
+
+ async unsubscribeToCollection() {
+ await this.removeCollectionFromList(
+ `${this.currentCollection.name}/${this.currentCollection.identifier}`
+ );
+ parentEpml.request('showSnackBar', get('gifs.gchange21'));
+ this.isSubscribed = false;
+ const savedCollections = await this.getSavedCollections();
+ this.mySubscribedCollections = savedCollections;
+ }
+
+ render() {
+ return html`
+
+
+
{
+ if (this.isLoading) return;
+ this.mode = 'newCollection';
+ }}
+ icon="vaadin:plus"
+ slot="icon">
+
+
+
{
+ if (this.mode === 'explore' && !this.currentCollection) {
+ this.mode = 'myCollection';
+ this.currentCollection = null;
+ } else if (this.mode === 'explore' && this.currentCollection) {
+ this.mode = 'explore';
+ this.currentCollection = null;
+ this.isSubscribed = false;
+ } else {
+ this.currentCollection = null;
+ }
+ }}
+ >
+
+
+
+ ${translate(
+ 'gifs.gchange1'
+ )}
+
+
{
+ if (this.isLoading) return;
+ this.mode = 'explore';
+ this.currentCollection = null;
+ }}
+ icon="vaadin:search"
+ slot="icon">
+
+
+
+
+
+
+ ${this.mode === 'myCollection' && !this.currentCollection
+ ? html`
+ ${this.isLoading === true
+ ? html`
`
+ : ''}
+ ${(this.myGifCollections.length === 0 && !this.isLoading) ? (
+ html`
+
${translate('gifs.gchange13')}
+ `
+ ) : (
+ html`
+ ${(this.myGifCollections || []).map((collection) => {
+ return html`
+
{
+ this.currentCollection =
+ collection;
+ }} class='collection-card'>
+ ${collection.identifier}
+
+ `;
+ })}
+ `
+ )}
+ `
+ : ''
+ }
+ ${this.mode === 'subscribedCollection' &&
+ !this.currentCollection
+ ? html`
+ ${this.isLoading === true
+ ? html`
`
+ : ''}
+ ${(this.mySubscribedCollections.length === 0 && !this.isLoading) ? (
+ html`
+
${translate('gifs.gchange14')}
+ `
+ ) : (
+ html`
+ ${this.mySubscribedCollections.map(
+ (collection) => {
+ return html`
+
{
+ this.currentCollection =
+ collection;
+ }} class='collection-card'>
+ ${collection.identifier}
+
+ `;
+ }
+ )}
+ `
+ )}
+ `
+ : ''
+ }
+ ${this.mode === 'explore' && !this.currentCollection
+ ? html`
+ ${this.isLoading === true
+ ? html`
+
+ `
+ : html`
+
+ this.getAllCollections(val)}
+ .getMoreExploreGifs=${(val) =>
+ this.getMoreExploreGifs(val)}
+ .exploreCollections=${this
+ .exploreCollections}
+ .setCurrentCollection=${(val) =>
+ this.setCurrentCollection(val)}
+ >
+ `
+ }
+ `
+ : ''
+ }
+ ${this.currentCollection && this.mode === 'myCollection'
+ ? html`
+
+ ${this.currentCollection.gifUrls.map((gif) => {
+ return html`
+ this.sendMessage(val)}
+ .setOpenGifModal=${(val) => this.setOpenGifModal(val)}
+ .class=${'gif-image'}
+ .gif=${gif}
+ .alt=${'gif-image'}>
+
+ `;
+ })}
+
+ `
+ : ''
+ }
+ ${this.currentCollection &&
+ this.mode === 'subscribedCollection'
+ ? html`
+
+ ${this.currentCollection.gifUrls.map((gif) => {
+ return html`
+ this.sendMessage(val)}
+ .setOpenGifModal=${(val) => this.setOpenGifModal(val)}
+ .class=${'gif-image'}
+ .gif=${gif}
+ .alt=${'gif-image'}>
+
+ `;
+ })}
+
+ `
+ : ''
+ }
+ ${this.currentCollection && this.mode === 'explore'
+ ? html`
+
+ ${this.currentCollection.gifUrls.map((gif) => {
+ return html`
+ this.sendMessage(val)}
+ .setOpenGifModal=${(val) => this.setOpenGifModal(val)}
+ .class=${'gif-image'}
+ .gif=${gif}
+ .alt=${'gif-image'}>
+
+ `;
+ })}
+
+ ${this.isSubscribed ? (
+ html`
+
+ `
+ ) : (
+ html`
+
+ `
+ )}
+ `
+ : ''
+ }
+ ${this.mode === 'newCollection' && this.isLoading === false
+ ? html`
+
+
+
+ ${translate('gifs.gchange5')}
+
+
+ ${translate('gifs.gchange6')}
+
+
+
+ this.shadowRoot
+ .getElementById(
+ 'file-picker-gif'
+ )
+ .click()}
+ class="new-collection-container"
+ style=${this.gifsToBeAdded.length > 0 ? "padding: 10px 0;" : "padding: 60px 0;"}
+ >
+
+
+ {
+ 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}
+ type="file"
+ name="myGif"
+ accept="image/gif"
+ style=${'display: none;'}
+ />
+
+
+
{
+ this.newCollectionName =
+ e.target.value;
+ }}
+ />
+
+
+ ${this.gifsToBeAdded.map((gif, i) => {
+ return html`
+
+ `;
+ })}
+
+
+
+
+
+
+
+ `
+ : this.mode === 'newCollection' && this.isLoading === true ? (
+ html`
+
+
${translate("gifs.gchange11")}
+
+
+ `
+ )
+ : ''
+ }
+
+
+
+
+ `;
+ }
+
+}
+
+window.customElements.define('chat-gifs', ChatGifs);
diff --git a/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore-css.js b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore-css.js
new file mode 100644
index 00000000..2ce632ba
--- /dev/null
+++ b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore-css.js
@@ -0,0 +1,137 @@
+import { css } from 'lit';
+
+export const chatGifsExploreStyles = css`
+.container-body {
+display: flex;
+flex-direction: column;
+align-items: center;
+max-width: 100%;
+height: 100%;
+}
+
+.collection-wrapper {
+display: flex;
+flex-direction: column;
+width: 100%;
+height: 100%;
+overflow: hidden;
+}
+
+.collection-card {
+display: flex;
+font-family: Roboto, sans-serif;
+font-weight: 300;
+letter-spacing: 0.3px;
+font-size: 19px;
+color: var(--chat-bubble-msg-color);
+flex-direction: row;
+align-items: center;
+transition: all 0.3s ease-in-out;
+box-shadow: none;
+padding: 10px;
+cursor: pointer;
+}
+
+.collection-card:hover {
+border: none;
+border-radius: 5px;
+background-color: var(--gif-collection-hover-bg);
+}
+
+.search-collection-name {
+display: block;
+padding: 8px 10px;
+font-size: 16px;
+font-family: Montserrat, sans-serif;
+font-weight: 600;
+background-color: #ebeaea21;
+border: 1px solid var(--mdc-theme-primary);
+border-radius: 5px;
+color: var(--chat-bubble-msg-color);
+width: 90%;
+margin: 10px 0;
+outline: none;
+}
+
+.search-collection-name::placeholder {
+font-size: 16px;
+font-family: Montserrat, sans-serif;
+font-weight: 600;
+opacity: 0.6;
+color: var(--chat-bubble-msg-color);
+}
+
+.search-collection-wrapper {
+display: flex;
+justify-content: center;
+align-items: center;
+width: 100%;
+position: relative;
+}
+
+.explore-collections-icon {
+position: absolute;
+right: 20px;
+font-size: 13px;
+color: var(--chat-group);
+cursor: pointer;
+}
+
+.clear-search-icon {
+position: absolute;
+right: 15px;
+font-size: 16px;
+color: var(--chat-group);
+padding: 1px;
+border-radius: 50%;
+background-color: transparent;
+transition: all 0.3s ease-in-out;
+}
+
+.clear-search-icon:hover {
+cursor: pointer;
+background-color: #e4e3e389
+}
+
+.gifs-loading-message {
+font-family: Montserrat, sans-serif;
+font-size: 20px;
+font-weight: 600;
+color: var(--chat-bubble-msg-color);
+margin: 0 0 10px 0;
+text-align: center;
+user-select: none;
+}
+
+.lds-circle {
+display: flex;
+align-items: center;
+justify-content: center;
+}
+
+.lds-circle > div {
+display: inline-block;
+width: 80px;
+height: 80px;
+margin: 8px;
+border-radius: 50%;
+background: var(--mdc-theme-primary);
+animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
+}
+
+@keyframes lds-circle {
+0%, 100% {
+ animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
+}
+0% {
+ transform: rotateY(0deg);
+}
+50% {
+ transform: rotateY(1800deg);
+ animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
+}
+100% {
+ transform: rotateY(3600deg);
+}
+}
+`
diff --git a/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore.js b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore.js
new file mode 100644
index 00000000..df825b74
--- /dev/null
+++ b/qortal-ui-plugins/plugins/core/components/ChatGifs/ChatGifsExplore.js
@@ -0,0 +1,175 @@
+import { LitElement, html, css } from 'lit';
+import { Epml } from '../../../../epml.js';
+import { chatGifsExploreStyles } from './ChatGifsExplore-css.js';
+import { translate, get } from 'lit-translate';
+import '@material/mwc-icon';
+
+const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
+
+class ChatGifsExplore extends LitElement {
+ static get properties() {
+ return {
+ currentCollection: { type: String },
+ searchCollectionName: {type: String},
+ getMoreExploreGifs: { attribute: false },
+ exploreCollections: { type: Array },
+ setCurrentCollection: { attribute: false },
+ isLoading: { type: Boolean },
+ isSearched: { type: Boolean },
+ getAllCollections: { attribute: false }
+ };
+ }
+
+ static styles = [chatGifsExploreStyles];
+
+ constructor() {
+ super();
+ this.searchCollectionName = '';
+ this.downObserverElement = '';
+ this.viewElement = '';
+ this.exploreCollections = [];
+ this.isLoading = false;
+ this.isSearched = false;
+ }
+
+ elementObserver() {
+ const options = {
+ root: this.viewElement,
+ rootMargin: '0px',
+ threshold: 1,
+ };
+ // identify an element to observe
+ 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) {
+ if (!entries[0].isIntersecting) {
+ return;
+ } else {
+ if (this.exploreCollections.length < 20) {
+ return;
+ }
+
+ this.getMoreExploreGifs();
+ }
+ }
+
+ async firstUpdated() {
+ this.viewElement = this.shadowRoot.getElementById('viewElement');
+ this.downObserverElement =
+ this.shadowRoot.getElementById('downObserver');
+ this.elementObserver();
+ }
+ getApiKey() {
+ const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
+ let apiKey = myNode.apiKey;
+ return apiKey;
+ }
+
+ async searchCollections() {
+ this.isSearched = true;
+ try {
+ this.exploreCollections = [];
+ this.isLoading = true;
+ const response = await parentEpml.request('apiCall', {
+ url: `/arbitrary/resources/search?service=GIF_REPOSITORY&query=${this.searchCollectionName}&limit=0&apiKey=${this.getApiKey()}
+ `,
+ });
+ await new Promise((res) => {
+ setTimeout(() => {
+ res();
+ }, 1000)
+ });
+ this.exploreCollections = response;
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+ render() {
+ console.log(18, "chat-gifs-explore-here");
+ console.log(this.searchCollectionName, "search collection name");
+ return html`
+
+
+ {
+ this.searchCollectionName =
+ e.target.value;
+ }}
+ @keyup=${async (e) => {
+ console.log(e.key);
+ if (e.key === 'Enter' && this.searchCollectionName) {
+ await this.searchCollections()
+ }
+ }}
+ />
+ ${this.isSearched ? (
+ html`
+ {
+ if (this.isLoading) return;
+ const latestCollections = await this.getAllCollections();
+ this.exploreCollections = latestCollections;
+ this.searchCollectionName = '';
+ this.isSearched = false;
+ }}
+ icon='vaadin:close-small'
+ slot='icon'>
+
+ `
+ ) : html`
+ {
+ if (this.isLoading || !this.searchCollectionName) return;
+ await this.searchCollections();
+ }}
+ icon='vaadin:search'
+ slot='icon'>
+
+ `}
+
+
+ ${this.isLoading ? html`
+
+
${translate('gifs.gchange18')}
+
+
+
`
+ : this.isSearched && this.exploreCollections.length === 0 ? (
+ html`
${translate('gifs.gchange19')}
`
+ ) : (
+ html`${this.exploreCollections.map((collection) => {
+ return html`
+
{
+ this.setCurrentCollection(collection);
+ }}>
+ ${collection.identifier}
+
+ `;
+ })}`
+ )}
+
+
+
+ `;
+ }
+
+}
+
+window.customElements.define('chat-gifs-explore', ChatGifsExplore);
diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js
index 32f5840c..d6311b28 100644
--- a/qortal-ui-plugins/plugins/core/components/ChatPage.js
+++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js
@@ -9,6 +9,9 @@ import Placeholder from '@tiptap/extension-placeholder'
import Highlight from '@tiptap/extension-highlight'
import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import { Editor, Extension } from '@tiptap/core'
+import * as zip from "@zip.js/zip.js";
+import { saveAs } from 'file-saver';
+import './ChatGifs/ChatGifs.js';
import localForage from "localforage";
registerTranslateConfig({
@@ -107,6 +110,8 @@ class ChatPage extends LitElement {
openUserInfo: { type: Boolean },
selectedHead: { type: Object },
userName: { type: String },
+ openGifModal: { type: Boolean },
+ gifsLoading: { type: Boolean },
goToRepliedMessage: {attribute: false},
isLoadingGoToRepliedMessage: {type: Object}
}
@@ -507,183 +512,730 @@ class ChatPage extends LitElement {
border-radius: 15px;
line-height: 1.6;
overflow-y: auto;
+ overflow-x: hidden;
+ width: 100%;
}
- .buttons {
- text-align:right;
+
+ .repliedTo-container {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 10px 8px 10px;
}
-
- .dialogCustom {
- position: fixed;
- z-index: 10000;
- display: flex;
- justify-content: center;
- flex-direction: column;
- align-items: center;
- top: 10px;
- right: 20px;
- user-select: none;
- }
-
- .dialogCustom p {
- color: var(--black)
- }
-
- .dialogCustomInner {
- min-width: 300px;
- height: 40px;
- background-color: var(--white);
- box-shadow: rgb(119 119 119 / 32%) 0px 4px 12px;
- padding: 10px;
- border-radius: 4px;
- }
-
- .dialogCustomInner ul {
- padding-left: 0px
- }
-
- .dialogCustomInner li {
- margin-bottom: 10px;
- }
-
- .marginLoader {
- margin-right: 8px;
- }
-
- .smallLoading,
- .smallLoading:after {
- border-radius: 50%;
- width: 2px;
- height: 2px;
- }
-
- .smallLoading {
- border-width: 0.8em;
- border-style: solid;
- border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
- rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
- font-size: 10px;
- position: relative;
- text-indent: -9999em;
- transform: translateZ(0px);
- animation: 1.1s linear 0s infinite normal none running loadingAnimation;
- }
-
- @-webkit-keyframes loadingAnimation {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
+
+ .senderName {
+ margin: 0;
+ color: var(--mdc-theme-primary);
+ font-weight: bold;
+ user-select: none;
+ }
+
+ .original-message {
+ color: var(--chat-bubble-msg-color);
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ margin: 0;
+ width: 800px;
+ }
+
+
+ .close-icon {
+ color: #676b71;
+ width: 18px;
+ transition: all 0.1s ease-in-out;
+ }
+
+ .close-icon:hover {
+ cursor: pointer;
+ color: #494c50;
+ }
+
+ .chat-text-area .typing-area .chatbar {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: auto;
+ padding: 5px 5px 5px 7px;
+ overflow: hidden;
+ }
+
+ .chat-text-area .typing-area .emoji-button {
+ width: 45px;
+ height: 40px;
+ padding-top: 4px;
+ border: none;
+ outline: none;
+ background: transparent;
+ cursor: pointer;
+ max-height: 40px;
+ color: var(--black);
+ }
+
+ .emoji-button-caption {
+ width: 45px;
+ height: 40px;
+ padding-top: 4px;
+ border: none;
+ outline: none;
+ background: transparent;
+ cursor: pointer;
+ max-height: 40px;
+ color: var(--black);
+ }
+
+ .caption-container {
+ width: 100%;
+ display: flex;
+ height: auto;
+ overflow: hidden;
+ justify-content: center;
+ background-color: var(--white);
+ padding: 5px;
+ border-radius: 1px;
+ }
+
+ .chatbar-caption {
+ font-family: Roboto, sans-serif;
+ width: 70%;
+ margin-right: 10px;
+ outline: none;
+ align-items: center;
+ font-size: 18px;
+ resize: none;
+ border-top: 0;
+ border-right: 0;
+ border-left: 0;
+ border-bottom: 1px solid #cac8c8;
+ padding: 3px;
+ }
+
+ .message-size-container {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ }
+
+ .message-size {
+ font-family: Roboto, sans-serif;
+ font-size: 12px;
+ color: black;
+ }
+
+ .lds-grid {
+ width: 120px;
+ height: 120px;
+ position: absolute;
+ left: 50%;
+ top: 40%;
+ }
+
+ img {
+ border-radius: 25%;
+ }
+
+ .dialogCustom {
+ position: fixed;
+ z-index: 10000;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ top: 10px;
+ right: 20px;
+ user-select: none;
+ }
+
+ .dialogCustomInner {
+ min-width: 300px;
+ height: 40px;
+ background-color: var(--white);
+ box-shadow: rgb(119 119 119 / 32%) 0px 4px 12px;
+ padding: 10px;
+ border-radius: 4px;
+ }
+
+ .dialogCustomInner ul {
+ padding-left: 0px
+ }
+
+ .dialogCustomInner li {
+ margin-bottom: 10px;
+ }
+
+ .marginLoader {
+ margin-right: 8px;
+ }
+
+ .last-message-ref {
+ position: absolute;
+ font-size: 18px;
+ top: -40px;
+ right: 30px;
+ width: 50;
+ height: 50;
+ z-index: 5;
+ color: black;
+ background-color: white;
+ border-radius: 50%;
+ transition: all 0.1s ease-in-out;
+ }
+
+ .last-message-ref:hover {
+ cursor: pointer;
+ transform: scale(1.1);
+ }
+
+ .arrow-down-icon {
+ transform: scale(1.15);
+ }
+
+ .chat-container {
+ display: grid;
+ max-height: 100%;
}
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
+
+ .chat-text-area {
+ display: flex;
+ position: relative;
+ justify-content: center;
+ min-height: 60px;
+ max-height: 100%;
}
- }
-
- @keyframes loadingAnimation {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
+
+ .chat-text-area .typing-area {
+ display: flex;
+ flex-direction: column;
+ width: 98%;
+ box-sizing: border-box;
+ margin-bottom: 8px;
+ border: 1px solid var(--chat-bubble-bg);
+ border-radius: 10px;
+ background: var(--chat-bubble-bg);
}
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
+
+ .chat-text-area .typing-area textarea {
+ display: none;
}
- }
-
- /* Add Image Modal Dialog Styling */
-
- .dialog-container {
- position: relative;
- display: flex;
- align-items: center;
- flex-direction: column;
- padding: 0 10px;
- gap: 10px;
- height: 100%;
- }
-
- .dialog-container-title {
- font-family: Montserrat;
- color: var(--black);
- font-size: 20px;
- margin: 15px 0 0 0;
- }
-
- .divider {
- height: 1px;
- background-color: var(--chat-bubble-msg-color);
- user-select: none;
- width: 70%;
- margin-bottom: 20px;
- }
-
- .dialog-container-loader {
- position: relative;
- display: flex;
- align-items: center;
- padding: 0 10px;
- gap: 10px;
- height: 100%;
- }
-
- .dialog-image {
- width: 100%;
- max-height: 300px;
- border-radius: 0;
- object-fit: contain;
- }
-
- .chat-right-panel {
- flex: 0;
- border-left: 3px solid rgb(221, 221, 221);
- height: 100%;
- overflow-y: auto;
- background: transparent;
- }
-
- .movedin {
- flex: 1 !important;
- background: transparent;
- }
-
- .main-container {
- display: flex;
- height: 100%;
- }
-
- .group-nav-container {
- display: flex;
- height: 40px;
- padding: 25px 5px 25px 20px;
+
+ .chat-text-area .typing-area .chat-editor {
+ display: flex;
+ max-height: -webkit-fill-available;
+ width: 100%;
+ border-color: transparent;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+
+ .repliedTo-container {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 10px 8px 10px;
+ }
+
+ .repliedTo-subcontainer {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 15px;
+ width: 100%;
+ }
+
+ .repliedTo-message {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ width: 100%;
+ word-break: break-all;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ max-height: 60px;
+ }
+ .repliedTo-message p {
margin: 0px;
- background-color: var(--chat-bubble-bg);
- box-sizing: border-box;
- align-items: center;
- justify-content: space-between;
- box-shadow: var(--group-drop-shadow);
- }
-
- .top-bar-icon {
- border-radius: 50%;
- color: var(--chat-bubble-msg-color);
- transition: 0.3s all ease-in-out;
- padding: 5px;
- background-color: transparent;
- }
-
- .top-bar-icon:hover {
- background-color: #e6e6e69b;
+ padding: 0px;
+ }
+
+ .repliedTo-message pre {
+ white-space: pre-wrap;
+ }
+
+ .repliedTo-message p mark {
+ background-color: #ffe066;
+ border-radius: 0.25em;
+ box-decoration-break: clone;
+ padding: 0.125em 0;
+ }
+
+ .reply-icon {
+ width: 20px;
+ color: var(--mdc-theme-primary);
+ }
+
+ .close-icon {
+ color: #676b71;
+ width: 18px;
+ transition: all 0.1s ease-in-out;
+ }
+
+ .close-icon:hover {
+ cursor: pointer;
+ color: #494c50;
+ }
+
+ .chatbar-container {
+ width: 100%;
+ display: flex;
+ height: auto;
+ overflow: hidden;
+ }
+
+ .lds-grid {
+ width: 120px;
+ height: 120px;
+ position: absolute;
+ left: 50%;
+ top: 40%;
+ }
+
+ .lds-grid div {
+ position: absolute;
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ background: #03a9f4;
+ animation: lds-grid 1.2s linear infinite;
+ }
+
+ .lds-grid div:nth-child(1) {
+ top: 4px;
+ left: 4px;
+ animation-delay: 0s;
+ }
+
+ .lds-grid div:nth-child(2) {
+ top: 4px;
+ left: 48px;
+ animation-delay: -0.4s;
+ }
+
+ .lds-grid div:nth-child(3) {
+ top: 4px;
+ left: 90px;
+ animation-delay: -0.8s;
+ }
+
+ .lds-grid div:nth-child(4) {
+ top: 50px;
+ left: 4px;
+ animation-delay: -0.4s;
+ }
+
+ .lds-grid div:nth-child(5) {
+ top: 50px;
+ left: 48px;
+ animation-delay: -0.8s;
+ }
+
+ .lds-grid div:nth-child(6) {
+ top: 50px;
+ left: 90px;
+ animation-delay: -1.2s;
+ }
+
+ .lds-grid div:nth-child(7) {
+ top: 95px;
+ left: 4px;
+ animation-delay: -0.8s;
+ }
+
+ .lds-grid div:nth-child(8) {
+ top: 95px;
+ left: 48px;
+ animation-delay: -1.2s;
+ }
+
+ .lds-grid div:nth-child(9) {
+ top: 95px;
+ left: 90px;
+ animation-delay: -1.6s;
+ }
+
+ @keyframes lds-grid {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.5;
+ }
+ }
+
+ .float-left {
+ float: left;
+ }
+
+ img {
+ border-radius: 25%;
+ }
+
+ paper-dialog.warning {
+ width: 50%;
+ max-width: 50vw;
+ height: 30%;
+ max-height: 30vh;
+ text-align: center;
+ background-color: var(--white);
+ color: var(--black);
+ border: 1px solid var(--black);
+ border-radius: 15px;
+ line-height: 1.6;
+ overflow-y: auto;
+ }
+ .buttons {
+ text-align:right;
+ }
+
+ .dialogCustom {
+ position: fixed;
+ z-index: 10000;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ top: 10px;
+ right: 20px;
+ user-select: none;
+ }
+
+ .dialogCustom p {
+ color: var(--black)
+ }
+
+ .dialogCustomInner {
+ min-width: 300px;
+ height: 40px;
+ background-color: var(--white);
+ box-shadow: rgb(119 119 119 / 32%) 0px 4px 12px;
+ padding: 10px;
+ border-radius: 4px;
+ }
+
+ .dialogCustomInner ul {
+ padding-left: 0px
+ }
+
+ .dialogCustomInner li {
+ margin-bottom: 10px;
+ }
+
+ .marginLoader {
+ margin-right: 8px;
+ }
+
+ .smallLoading,
+ .smallLoading:after {
+ border-radius: 50%;
+ width: 2px;
+ height: 2px;
+ }
+
+ .smallLoading {
+ border-width: 0.8em;
+ border-style: solid;
+ border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
+ rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
+ font-size: 10px;
+ position: relative;
+ text-indent: -9999em;
+ transform: translateZ(0px);
+ animation: 1.1s linear 0s infinite normal none running loadingAnimation;
+ }
+
+ @-webkit-keyframes loadingAnimation {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+
+ @keyframes loadingAnimation {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+
+ /* Add Image Modal Dialog Styling */
+
+ .dialog-container {
+ position: relative;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ padding: 0 10px;
+ gap: 10px;
+ height: 100%;
+ }
+
+ .dialog-container-title {
+ font-family: Montserrat;
+ color: var(--black);
+ font-size: 20px;
+ margin: 15px 0 0 0;
+ }
+
+ .divider {
+ height: 1px;
+ background-color: var(--chat-bubble-msg-color);
+ user-select: none;
+ width: 70%;
+ margin-bottom: 20px;
+ }
+
+ .dialog-container-loader {
+ position: relative;
+ display: flex;
+ align-items: center;
+ padding: 0 10px;
+ gap: 10px;
+ height: 100%;
+ }
+
+ .dialog-image {
+ width: 100%;
+ max-height: 300px;
+ border-radius: 0;
+ object-fit: contain;
+ }
+
+ .chat-right-panel {
+ flex: 0;
+ border-left: 3px solid rgb(221, 221, 221);
+ height: 100%;
+ overflow-y: auto;
+ background: transparent;
+ }
+
+ .movedin {
+ flex: 1 !important;
+ background: transparent;
+ }
+
+ .main-container {
+ display: flex;
+ height: 100%;
+ }
+
+ .group-nav-container {
+ display: flex;
+ height: 40px;
+ padding: 25px 5px 25px 20px;
+ margin: 0px;
+ background-color: var(--chat-bubble-bg);
+ box-sizing: border-box;
+ align-items: center;
+ justify-content: space-between;
+ box-shadow: var(--group-drop-shadow);
+ }
+
+ .top-bar-icon {
+ border-radius: 50%;
+ color: var(--chat-bubble-msg-color);
+ transition: 0.3s all ease-in-out;
+ padding: 5px;
+ background-color: transparent;
+ }
+
+ .top-bar-icon:hover {
+ background-color: #e6e6e69b;
+ cursor: pointer;
+ color: var(--black)
+ }
+
+ .group-name {
+ font-family: Raleway, sans-serif;
+ font-size: 16px;
+ color: var(--black);
+ margin:0px;
+ padding:0px;
+ }
+
+
+ .modal-button {
+ font-family: Roboto, sans-serif;
+ font-size: 16px;
+ color: var(--mdc-theme-primary);
+ background-color: transparent;
+ padding: 8px 10px;
+ border-radius: 5px;
+ border: none;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .modal-button-red {
+ font-family: Roboto, sans-serif;
+ font-size: 16px;
+ color: #F44336;
+ background-color: transparent;
+ padding: 8px 10px;
+ border-radius: 5px;
+ border: none;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .modal-button-red:hover {
+ cursor: pointer;
+ background-color: #f4433663;
+ }
+
+ .modal-button:hover {
+ cursor: pointer;
+ background-color: #03a8f475;
+ }
+
+ .name-input {
+ width: 100%;
+ margin-bottom: 15px;
+ outline: 0;
+ border-width: 0 0 2px;
+ border-color: var(--mdc-theme-primary);
+ background-color: transparent;
+ padding: 10px;
+ font-family: Roboto, sans-serif;
+ font-size: 15px;
+ color: var(--chat-bubble-msg-color);
+ box-sizing: border-box;
+ }
+
+ .name-input::selection {
+ background-color: var(--mdc-theme-primary);
+ color: white;
+ }
+
+ .name-input::placeholder {
+ opacity: 0.9;
+ color: var(--black);
+ }
+
+ .search-results-div {
+ position: absolute;
+ top: 25px;
+ right: 25px;
+ }
+
+ .search-field {
+ width: 100%;
+ position: relative;
+ margin-bottom: 5px;
+ }
+
+ .search-icon {
+ position: absolute;
+ right: 3px;
+ top: 0;
+ color: var(--chat-bubble-msg-color);
+ transition: all 0.3s ease-in-out;
+ background: none;
+ border-radius: 50%;
+ padding: 6px 3px;
+ font-size: 21px;
+ }
+
+ .search-icon:hover {
cursor: pointer;
- color: var(--black)
- }
-
- .group-name {
- font-family: Raleway, sans-serif;
+ background: #d7d7d75c;
+ }
+
+ .user-verified {
+ position: absolute;
+ top: 0;
+ right: 5px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ color: #04aa2e;
+ font-size: 13px;
+ }
+
+ .user-selected {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0;
+ box-shadow: rgb(0 0 0 / 16%) 0px 3px 6px, rgb(0 0 0 / 23%) 0px 3px 6px;
+ padding: 18px 20px;
+ color: var(--chat-bubble-msg-color);
+ border-radius: 5px;
+ background-color: #ececec96;
+ }
+
+ .user-selected-name {
+ font-family: Roboto, sans-serif;
+ margin: 0;
font-size: 16px;
- color: var(--black);
- margin:0px;
- padding:0px;
- }
+ }
+
+ .forwarding-container {
+ display: flex;
+ gap: 15px;
+ }
+
+ .user-selected-forwarding {
+ font-family: Livvic, sans-serif;
+ margin: 0;
+ font-size: 16px;
+ }
+
+ .close-forwarding {
+ color: #676b71;
+ width: 14px;
+ transition: all 0.1s ease-in-out;
+ }
+
+ .close-forwarding:hover {
+ cursor: pointer;
+ color: #4e5054;
+ }
+
+ .chat-gifs {
+ position: absolute;
+ right: 15px;
+ bottom: 100px;
+ justify-self: flex-end;
+ width: fit-content;
+ height: auto;
+ transform: translateY(30%);
+ animation: smooth-appear 0.5s ease forwards;
+ z-index: 5;
+ }
+
+ @keyframes smooth-appear {
+ to {
+ transform: translateY(0);
+ }
+ }
+
+ .gifs-backdrop {
+ top: 0;
+ height: 100vh;
+ width: 100vw;
+ background: transparent;
+ position: fixed;
+ }
.modal-button-row {
display: flex;
@@ -692,141 +1244,6 @@ class ChatPage extends LitElement {
width: 100%;
}
- .modal-button {
- font-family: Roboto, sans-serif;
- font-size: 16px;
- color: var(--mdc-theme-primary);
- background-color: transparent;
- padding: 8px 10px;
- border-radius: 5px;
- border: none;
- transition: all 0.3s ease-in-out;
- }
-
- .modal-button-red {
- font-family: Roboto, sans-serif;
- font-size: 16px;
- color: #F44336;
- background-color: transparent;
- padding: 8px 10px;
- border-radius: 5px;
- border: none;
- transition: all 0.3s ease-in-out;
- }
-
- .modal-button-red:hover {
- cursor: pointer;
- background-color: #f4433663;
- }
-
- .modal-button:hover {
- cursor: pointer;
- background-color: #03a8f475;
- }
-
- .name-input {
- width: 100%;
- margin-bottom: 15px;
- outline: 0;
- border-width: 0 0 2px;
- border-color: var(--mdc-theme-primary);
- background-color: transparent;
- padding: 10px;
- font-family: Roboto, sans-serif;
- font-size: 15px;
- color: var(--chat-bubble-msg-color);
- box-sizing: border-box;
- }
-
- .name-input::selection {
- background-color: var(--mdc-theme-primary);
- color: white;
- }
-
- .name-input::placeholder {
- opacity: 0.9;
- color: var(--black);
- }
-
- .search-results-div {
- position: absolute;
- top: 25px;
- right: 25px;
- }
-
- .search-field {
- width: 100%;
- position: relative;
- margin-bottom: 5px;
- }
-
- .search-icon {
- position: absolute;
- right: 3px;
- top: 0;
- color: var(--chat-bubble-msg-color);
- transition: all 0.3s ease-in-out;
- background: none;
- border-radius: 50%;
- padding: 6px 3px;
- font-size: 21px;
- }
-
- .search-icon:hover {
- cursor: pointer;
- background: #d7d7d75c;
- }
-
- .user-verified {
- position: absolute;
- top: 0;
- right: 5px;
- display: flex;
- align-items: center;
- gap: 10px;
- color: #04aa2e;
- font-size: 13px;
- }
-
- .user-selected {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: 0;
- box-shadow: rgb(0 0 0 / 16%) 0px 3px 6px, rgb(0 0 0 / 23%) 0px 3px 6px;
- padding: 18px 20px;
- color: var(--chat-bubble-msg-color);
- border-radius: 5px;
- background-color: #ececec96;
- }
-
- .user-selected-name {
- font-family: Roboto, sans-serif;
- margin: 0;
- font-size: 16px;
- }
-
- .forwarding-container {
- display: flex;
- gap: 15px;
- }
-
- .user-selected-forwarding {
- font-family: Livvic, sans-serif;
- margin: 0;
- font-size: 16px;
- }
-
- .close-forwarding {
- color: #676b71;
- width: 14px;
- transition: all 0.1s ease-in-out;
- }
-
- .close-forwarding:hover {
- cursor: pointer;
- color: #4e5054;
- }
.attachment-icon-container {
display: flex;
@@ -866,6 +1283,7 @@ class ChatPage extends LitElement {
this.setOpenUserInfo = this.setOpenUserInfo.bind(this)
this.setUserName = this.setUserName.bind(this)
this.setSelectedHead = this.setSelectedHead.bind(this)
+ this.setGifsLoading = this.setGifsLoading.bind(this)
this.selectedAddress = {}
this.userName = ""
this.chatId = ''
@@ -920,7 +1338,9 @@ class ChatPage extends LitElement {
this.webWorkerFile = null;
this.currentEditor = '_chatEditorDOM'
this.initialChat = this.initialChat.bind(this)
+ this.setOpenGifModal = this.setOpenGifModal.bind(this)
this.isEnabledChatEnter = true
+ this.openGifModal = false
this.isLoadingGoToRepliedMessage = {
isLoading: false,
top: 0,
@@ -929,6 +1349,11 @@ class ChatPage extends LitElement {
}
}
+
+ setOpenGifModal(value){
+ this.openGifModal = value
+ }
+
_toggle(value) {
this.shifted = value === (false || true) ? value : !this.shifted;
this.requestUpdate()
@@ -959,6 +1384,16 @@ class ChatPage extends LitElement {
localStorage.setItem('isEnabledChatEnter', !this.isEnabledChatEnter )
this.isEnabledChatEnter = !this.isEnabledChatEnter
}
+
+ addGifs(gifs){
+ this.gifsToBeAdded = [...this.gifsToBeAdded, ...gifs]
+ }
+
+ setGifsLoading(props) {
+ this.gifsLoading = props;
+ }
+
+
render() {
return html`
@@ -994,14 +1429,33 @@ class ChatPage extends LitElement {
` :
this.renderChatScroller()}
+ {
+ if (this.gifsLoading) return;
+ this.setOpenGifModal(false);
+ this.editor.commands.focus("end");
+ this.shadowRoot.querySelector("chat-gifs").clearGifSelections();
+ }}
+ style=${this.openGifModal ? "visibility: visible; z-index: 4" : "visibility: hidden; z-index: -100"}>
+
+
${this.isLoadingGoToRepliedMessage && this.isLoadingGoToRepliedMessage.loading ? html`
` : ''}
-
+
+
this.setGifsLoading(val)}
+ .sendMessage=${(val) => this._sendMessage(val)}
+ .setOpenGifModal=${(val)=> this.setOpenGifModal(val)}>
+
+ style=${(this.lastMessageRefVisible && !this.imageFile && !this.openGifModal) ? 'opacity: 1;' : 'opacity: 0;'}>
{
this.shadowRoot.querySelector("chat-scroller").shadowRoot.getElementById("downObserver")
.scrollIntoView({
@@ -1082,6 +1536,8 @@ class ChatPage extends LitElement {
.repliedToMessageObj=${this.repliedToMessageObj}
.toggleEnableChatEnter=${this.toggleEnableChatEnter}
?isEnabledChatEnter=${this.isEnabledChatEnter}
+ ?openGifModal=${this.openGifModal}
+ .setOpenGifModal=${(val)=> this.setOpenGifModal(val)}
chatId=${this.chatId}
>
@@ -1559,8 +2015,8 @@ class ChatPage extends LitElement {
if(this.webWorker){
this.webWorker.terminate();
}
- if(this.webWorkerImage){
- this.webWorkerImage.terminate();
+ if(this.webWorkerFile){
+ this.webWorkerFile.terminate();
}
if(this.editor){
this.editor.destroy()
@@ -1582,7 +2038,7 @@ class ChatPage extends LitElement {
}
initialChat(e) {
- if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser) {
+ if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser && !this.openGifModal) {
// WARNING: Deprecated methods from KeyBoard Event
if (e.code === "Space" || e.keyCode === 32 || e.which === 32) {
} else if (inputKeyCodes.includes(e.keyCode)) {
@@ -2972,8 +3428,28 @@ class ChatPage extends LitElement {
};
const stringifyMessageObject = JSON.stringify(messageObject);
this.sendMessage(stringifyMessageObject, typeMessage);
- }
- else if (outSideMsg && outSideMsg.type === 'attachment') {
+ } else if (outSideMsg && outSideMsg.type === 'gif') {
+ const userName = await getName(this.selectedAddress.address);
+ if (!userName) {
+ parentEpml.request('showSnackBar', get("chatpage.cchange27"));
+ this.isLoading = false;
+ return;
+ }
+
+ const messageObject = {
+ messageText: '',
+ gifs: [{
+ service: outSideMsg.service,
+ name: outSideMsg.name,
+ identifier: outSideMsg.identifier,
+ filePath: outSideMsg.filePath
+ }],
+ repliedTo: '',
+ version: 2
+ };
+ const stringifyMessageObject = JSON.stringify(messageObject);
+ this.sendMessage(stringifyMessageObject, typeMessage);
+ } else if (outSideMsg && outSideMsg.type === 'attachment') {
this.isUploadingAttachment = true;
const userName = await getName(this.selectedAddress.address);
if (!userName) {
diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js
index 7ed44c52..ab4ab7ed 100644
--- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js
+++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js
@@ -281,10 +281,12 @@ class MessageTemplate extends LitElement {
setEditedMessageObj: { attribute: false },
sendMessage: { attribute: false },
sendMessageForward: { attribute: false },
- openDialogImage: { attribute: false },
+ openDialogImage: { type: Boolean },
+ openDialogGif: { type: Boolean },
openDeleteImage: { type: Boolean },
openDeleteAttachment: { type: Boolean },
isImageLoaded: { type: Boolean },
+ isGifLoaded: { type: Boolean },
isFirstMessage: { type: Boolean },
isSingleMessageInGroup: { type: Boolean },
isLastMessageInGroup: { type: Boolean },
@@ -311,8 +313,11 @@ class MessageTemplate extends LitElement {
this.showBlockAddressIcon = false
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
this.imageFetches = 0
+ this.gifFetches = 0
this.openDialogImage = false
+ this.openDialogGif = false
this.isImageLoaded = false
+ this.isGifLoaded = false
this.isFirstMessage = false
this.isSingleMessageInGroup = false
this.isLastMessageInGroup = false
@@ -384,6 +389,7 @@ class MessageTemplate extends LitElement {
let reactions = [];
let repliedToData = null;
let image = null;
+ let gif = null;
let isImageDeleted = false;
let isAttachmentDeleted = false;
let version = 0;
@@ -392,8 +398,7 @@ class MessageTemplate extends LitElement {
let attachment = null;
try {
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage);
- if(+parsedMessageObj.version > 1){
-
+ if(+parsedMessageObj.version > 1 && parsedMessageObj.messageText){
messageVersion2 = generateHTML(parsedMessageObj.messageText, [
StarterKit,
Underline,
@@ -415,13 +420,20 @@ class MessageTemplate extends LitElement {
if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
image = parsedMessageObj.images[0];
}
+ if (parsedMessageObj.gifs && Array.isArray(parsedMessageObj.gifs) && parsedMessageObj.gifs.length > 0) {
+ gif = parsedMessageObj.gifs[0];
+ }
} catch (error) {
+ console.error(error);
message = this.messageObj.decodedMessage;
}
let avatarImg = '';
let imageHTML = '';
let imageHTMLDialog = '';
let imageUrl = '';
+ let gifHTML = '';
+ let gifHTMLDialog = '';
+ let gifUrl = '';
let nameMenu = '';
let levelFounder = '';
let hideit = hidemsg.includes(this.messageObj.sender);
@@ -463,6 +475,33 @@ class MessageTemplate extends LitElement {
};
return imageHTMLRes;
}
+
+ const createGif = (gif) => {
+ const gifHTMLRes = new Image();
+ gifHTMLRes.src = gif;
+ gifHTMLRes.style= "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer";
+ gifHTMLRes.onclick= () => {
+ this.openDialogGif = true;
+ }
+ gifHTMLRes.onload = () => {
+ this.isGifLoaded = true;
+ }
+ gifHTMLRes.onerror = () => {
+ if (this.gifFetches < 4) {
+ setTimeout(() => {
+ this.gifFetches = this.gifFetches + 1;
+ gifHTMLRes.src = gif;
+ }, 500);
+ } else {
+ gifHTMLRes.src = '/img/chain.png';
+ gifHTMLRes.style= "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5)";
+ gifHTMLRes.onclick= () => {}
+ this.isGifLoaded = true
+ }
+ };
+ return gifHTMLRes;
+ }
+
if (image) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
@@ -475,6 +514,17 @@ class MessageTemplate extends LitElement {
}
}
+ if (gif) {
+ const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
+ const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
+ gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`;
+ if (this.viewImage || this.myAddress === this.messageObj.sender){
+ gifHTML = createGif(gifUrl);
+ gifHTMLDialog = createGif(gifUrl)
+ gifHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px";
+ }
+ }
+
nameMenu = html`
${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)}
@@ -646,6 +696,25 @@ class MessageTemplate extends LitElement {
` : image && isImageDeleted ? html`
${translate("chatpage.cchange80")}
` : html``}
+ ${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
+ {
+ this.viewImage = true
+ }}
+ class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
+ style=${this.isFirstMessage && "margin-top: 10px;"}>
+
+ ${translate("gifs.gchange25")}
+
+
+ ` : html``}
+ ${gif && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
+
+ ${gifHTML}
+
+ ` : html``}
${attachment && !isAttachmentDeleted ?
html`
await this.downloadAttachment(attachment)} class="attachment-container">
@@ -753,6 +822,7 @@ class MessageTemplate extends LitElement {
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.setOpenTipUser=${(val) => this.setOpenTipUser(val)}
.setUserName=${(val) => this.setUserName(val)}
+ .gif=${!!gif}
>
@@ -855,6 +925,28 @@ class MessageTemplate extends LitElement {
${translate("general.close")}
+ {
+ this.openDialogGif = false
+ }}>
+
+
+ ${gifHTMLDialog}
+
+ {
+
+ this.openDialogGif = false
+ }}
+ >
+ ${translate("general.close")}
+
+
- ${this.myAddress === this.originalMessage.sender ? (
+ ${((this.myAddress === this.originalMessage.sender) && (
+ !this.gif)) ? (
html`