Add files via upload
This commit is contained in:
parent
8bc397b18e
commit
d8464855b6
103
qortal-ui-plugins/plugins/core/components/BlockAddress.js
Normal file
103
qortal-ui-plugins/plugins/core/components/BlockAddress.js
Normal file
@ -0,0 +1,103 @@
|
||||
import { LitElement, html, css } from 'lit'
|
||||
import { render } from 'lit/html.js'
|
||||
import { Epml } from '../../../epml.js'
|
||||
import snackbar from './snackbar.js'
|
||||
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
import '@material/mwc-snackbar'
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
class BlockAddress extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
toblockaddress: { type: String, attribute: true },
|
||||
chatBlockedAdresses: { type: Array }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
* {
|
||||
--mdc-theme-primary: rgb(3, 169, 244);
|
||||
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||
--mdc-dialog-content-ink-color: var(--black);
|
||||
--mdc-theme-surface: var(--white);
|
||||
--mdc-theme-text-primary-on-background: var(--black);
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.chatBlockedAdresses = []
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mwc-button dense unelevated label="block" icon="voice_over_off" @click="${() => this.chatBlockAddress()}"></mwc-button>
|
||||
`
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.getChatBlockedAdresses()
|
||||
|
||||
setInterval(() => {
|
||||
this.getChatBlockedAdresses();
|
||||
}, 60000)
|
||||
}
|
||||
|
||||
updated(changedProps) {
|
||||
}
|
||||
|
||||
async getChatBlockedAdresses() {
|
||||
const chatBlockedAdresses = await parentEpml.request('apiCall', {
|
||||
url: `/lists/blockedAddresses?apiKey=${this.getApiKey()}`
|
||||
})
|
||||
this.chatBlockedAdresses = chatBlockedAdresses
|
||||
}
|
||||
|
||||
async chatBlockAddress() {
|
||||
let address = this.toblockaddress
|
||||
|
||||
let items = [
|
||||
address
|
||||
]
|
||||
|
||||
let addressJsonString = JSON.stringify({ "items": items })
|
||||
|
||||
let ret = await parentEpml.request('apiCall', {
|
||||
url: `/lists/blockedAddresses?apiKey=${this.getApiKey()}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: `${addressJsonString}`
|
||||
})
|
||||
|
||||
if (ret === true) {
|
||||
this.chatBlockedAdresses = this.chatBlockedAdresses.filter(item => item != address)
|
||||
this.chatBlockedAdresses.push(address)
|
||||
snackbar.add({
|
||||
labelText: `Success blocked this user.`,
|
||||
dismiss: true
|
||||
})
|
||||
} else {
|
||||
snackbar.add({
|
||||
labelText: `Error occurred when trying to block this user. Please try again.`,
|
||||
dismiss: true
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
getApiKey() {
|
||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||
let apiKey = myNode.apiKey;
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('block-address', BlockAddress)
|
@ -99,7 +99,7 @@ class ChatMessage extends LitElement {
|
||||
<span class="message-data-name">${this.message.sender}</span>
|
||||
<span class="message-data-time">10:10 AM, Today</span>
|
||||
</div>
|
||||
<div class="message ${this.message.sender === this.selectedAddress.address ? "my-message float-right" : "other-message"}">
|
||||
<div class="message ${this.message.sender === this.selectedAddress.address ? "my-message float-right" : "other-message float-left"}">
|
||||
${this.message.decodedMessage}
|
||||
</div>
|
||||
</li>
|
||||
|
@ -1,13 +1,19 @@
|
||||
import { LitElement, html, css } from 'lit'
|
||||
import { render } from 'lit/html.js'
|
||||
import { Epml } from '../../../epml.js'
|
||||
|
||||
import { escape, unescape } from 'html-escaper';
|
||||
import { inputKeyCodes } from '../../utils/keyCodes.js'
|
||||
import './ChatScroller.js'
|
||||
import './BlockAddress.js'
|
||||
import './TimeAgo.js'
|
||||
import { EmojiPicker } from 'emoji-picker-js';
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
class ChatPage extends LitElement {
|
||||
@ -120,15 +126,11 @@ class ChatPage extends LitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
// TODO: Build a nice preloader for loading messages...
|
||||
return html`
|
||||
${this.isLoadingMessages ? html`<h1>Loading Messages...</h1>` : this.renderChatScroller(this._initialMessages)}
|
||||
|
||||
<div class="chat-text-area">
|
||||
<div class="typing-area">
|
||||
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
|
||||
|
||||
<iframe class="chat-editor" id="_chatEditorDOM" tabindex="-1"></iframe>
|
||||
<button class="emoji-button" ?disabled=${this.isLoading || this.isLoadingMessages}>
|
||||
${this.isLoading === false ? html`<img class="emoji" draggable="false" alt="😀" src="/emoji/svg/1f600.svg">` : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||
@ -326,6 +328,7 @@ class ChatPage extends LitElement {
|
||||
*/
|
||||
chatMessageTemplate(messageObj) {
|
||||
let avatarImg = '';
|
||||
let blockButton = '';
|
||||
if (messageObj.senderName) {
|
||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
@ -333,11 +336,18 @@ class ChatPage extends LitElement {
|
||||
avatarImg = `<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`;
|
||||
}
|
||||
|
||||
if (messageObj.sender === this.selectedAddress.address) {
|
||||
blockButton = ``
|
||||
} else {
|
||||
blockButton = `<block-address toblockaddress="${messageObj.sender}"></block-address>`
|
||||
}
|
||||
|
||||
return `
|
||||
<li class="clearfix">
|
||||
<div class="message-data ${messageObj.sender === this.selectedAddress.address ? "align-right" : ""}">
|
||||
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
||||
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
||||
<span class="message-data-block">${blockButton}</span>
|
||||
</div>
|
||||
<div class="message-data-avatar" style="width:42px; height:42px; ${messageObj.sender === this.selectedAddress.address ? "float:right;" : "float:left;"} margin:3px;">${avatarImg}</div>
|
||||
<div class="message ${messageObj.sender === this.selectedAddress.address ? "my-message float-right" : "other-message float-left"}">${this.emojiPicker.parse(escape(messageObj.decodedMessage))}</div>
|
||||
@ -990,7 +1000,6 @@ class ChatPage extends LitElement {
|
||||
|
||||
this.chatEditor = new ChatEditor(editorConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.customElements.define('chat-page', ChatPage)
|
||||
|
@ -1,4 +1,14 @@
|
||||
import { LitElement, html, css } from 'lit'
|
||||
import { render } from 'lit/html.js'
|
||||
import { Epml } from '../../../epml.js'
|
||||
|
||||
import './BlockAddress.js'
|
||||
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-dialog'
|
||||
import '@material/mwc-icon'
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
class ChatScroller extends LitElement {
|
||||
static get properties() {
|
||||
@ -64,6 +74,17 @@ class ChatScroller extends LitElement {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.message-data-block {
|
||||
color: #03a9f4;
|
||||
font-size: 16px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.blockicon {
|
||||
color: #03a9f4;
|
||||
--mdc-icon-size: 16px;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: black;
|
||||
padding: 12px 10px;
|
||||
@ -153,7 +174,6 @@ class ChatScroller extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.messages = []
|
||||
this._upObserverhandler = this._upObserverhandler.bind(this)
|
||||
this.isLoading = false
|
||||
@ -162,17 +182,29 @@ class ChatScroller extends LitElement {
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
return html`
|
||||
<ul id="viewElement" class="chat-list clearfix">
|
||||
<div id="upObserver"></div>
|
||||
<div id="downObserver"></div>
|
||||
</ul>
|
||||
<ul id="viewElement" class="chat-list clearfix">
|
||||
<div id="upObserver"></div>
|
||||
<div id="downObserver"></div>
|
||||
</ul>
|
||||
`
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.viewElement = this.shadowRoot.getElementById('viewElement')
|
||||
this.upObserverElement = this.shadowRoot.getElementById('upObserver')
|
||||
this.downObserverElement = this.shadowRoot.getElementById('downObserver')
|
||||
this.renderChatMessages(this.initialMessages)
|
||||
|
||||
// Intialize Observers
|
||||
this.upElementObserver()
|
||||
|
||||
this.viewElement.scrollTop = this.viewElement.scrollHeight + 50
|
||||
}
|
||||
|
||||
chatMessageTemplate(messageObj) {
|
||||
let avatarImg = '';
|
||||
let blockButton = '';
|
||||
if (messageObj.senderName) {
|
||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
|
||||
@ -180,11 +212,18 @@ class ChatScroller extends LitElement {
|
||||
avatarImg = `<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`;
|
||||
}
|
||||
|
||||
if (messageObj.sender === this.myAddress) {
|
||||
blockButton = ``
|
||||
} else {
|
||||
blockButton = `<block-address toblockaddress="${messageObj.sender}"></block-address>`
|
||||
}
|
||||
|
||||
return `
|
||||
<li class="clearfix">
|
||||
<div class="message-data ${messageObj.sender === this.myAddress ? "align-right" : ""}">
|
||||
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
||||
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
||||
<span class="message-data-block">${blockButton}</span>
|
||||
</div>
|
||||
<div class="message-data-avatar" style="width:42px; height:42px; ${messageObj.sender === this.myAddress ? "float:right;" : "float:left;"} margin:3px;">${avatarImg}</div>
|
||||
<div id="messageContent" class="message ${messageObj.sender === this.myAddress ? "my-message float-right" : "other-message float-left"}">${this.emojiPicker.parse(this.escapeHTML(messageObj.decodedMessage))}</div>
|
||||
@ -193,7 +232,6 @@ class ChatScroller extends LitElement {
|
||||
}
|
||||
|
||||
renderChatMessages(messages) {
|
||||
|
||||
messages.forEach(message => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = this.chatMessageTemplate(message);
|
||||
@ -203,7 +241,6 @@ class ChatScroller extends LitElement {
|
||||
}
|
||||
|
||||
renderOldMessages(listOfOldMessages) {
|
||||
|
||||
let { oldMessages, scrollElement } = listOfOldMessages;
|
||||
|
||||
let _oldMessages = oldMessages.reverse();
|
||||
@ -217,7 +254,6 @@ class ChatScroller extends LitElement {
|
||||
}
|
||||
|
||||
_getOldMessage(_scrollElement) {
|
||||
|
||||
let listOfOldMessages = this.getOldMessage(_scrollElement)
|
||||
|
||||
if (listOfOldMessages) {
|
||||
@ -226,7 +262,6 @@ class ChatScroller extends LitElement {
|
||||
}
|
||||
|
||||
_upObserverhandler(entries) {
|
||||
|
||||
if (entries[0].isIntersecting) {
|
||||
let _scrollElement = entries[0].target.nextElementSibling
|
||||
|
||||
@ -244,21 +279,6 @@ class ChatScroller extends LitElement {
|
||||
const observer = new IntersectionObserver(this._upObserverhandler, options)
|
||||
observer.observe(this.upObserverElement)
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
|
||||
this.viewElement = this.shadowRoot.getElementById('viewElement');
|
||||
this.upObserverElement = this.shadowRoot.getElementById('upObserver');
|
||||
this.downObserverElement = this.shadowRoot.getElementById('downObserver');
|
||||
|
||||
this.renderChatMessages(this.initialMessages)
|
||||
|
||||
// Intialize Observers
|
||||
this.upElementObserver()
|
||||
|
||||
this.viewElement.scrollTop = this.viewElement.scrollHeight + 50;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.customElements.define('chat-scroller', ChatScroller)
|
||||
|
@ -6,6 +6,7 @@ import '@material/mwc-icon'
|
||||
import '@material/mwc-button'
|
||||
import '@material/mwc-dialog'
|
||||
import '@polymer/paper-spinner/paper-spinner-lite.js'
|
||||
import '@vaadin/grid'
|
||||
|
||||
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||
|
||||
@ -209,7 +210,7 @@ class ChatWelcomePage extends LitElement {
|
||||
<div class="start-chat" @click=${() => this.shadowRoot.querySelector('#startSecondChatDialog').show()}>New Private Message</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Start Chatting Dialog -->
|
||||
<mwc-dialog id="startSecondChatDialog" scrimClickAction="${this.isLoading ? '' : 'close'}">
|
||||
<div style="text-align:center">
|
||||
@ -292,7 +293,6 @@ class ChatWelcomePage extends LitElement {
|
||||
}
|
||||
|
||||
_sendMessage() {
|
||||
|
||||
this.isLoading = true
|
||||
|
||||
const recipient = this.shadowRoot.getElementById('sendTo').value
|
||||
@ -465,6 +465,17 @@ class ChatWelcomePage extends LitElement {
|
||||
_textArea(e) {
|
||||
if (e.keyCode === 13 && !e.shiftKey) this._sendMessage()
|
||||
}
|
||||
|
||||
isEmptyArray(arr) {
|
||||
if (!arr) { return true }
|
||||
return arr.length === 0
|
||||
}
|
||||
|
||||
getApiKey() {
|
||||
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||
let apiKey = myNode.apiKey;
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('chat-welcome-page', ChatWelcomePage)
|
||||
|
84
qortal-ui-plugins/plugins/core/components/snackbar.js
Normal file
84
qortal-ui-plugins/plugins/core/components/snackbar.js
Normal file
@ -0,0 +1,84 @@
|
||||
import { LitElement, html, css } from 'lit'
|
||||
import '@material/mwc-snackbar'
|
||||
|
||||
let queueElement
|
||||
|
||||
class SnackQueue extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
busy: {
|
||||
type: Boolean,
|
||||
attribute: 'busy',
|
||||
reflectToAttribute: true
|
||||
},
|
||||
currentSnack: {
|
||||
type: Object,
|
||||
attribute: 'current-snack',
|
||||
reflectToAttribute: true
|
||||
},
|
||||
_queue: {
|
||||
type: Array
|
||||
},
|
||||
_labelText: { type: String },
|
||||
_stacked: { type: Boolean },
|
||||
_leading: { type: Boolean },
|
||||
_closeOnEscape: { type: Boolean },
|
||||
_timeoutMs: { type: Number },
|
||||
action: {},
|
||||
_dismiss: {},
|
||||
_dismissIcon: { type: String }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css``
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this._queue = []
|
||||
this.busy = false
|
||||
this._timeoutMs = 5000
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mwc-snackbar id="snack" labelText="${this._labelText}" ?stacked=${this._stacked} ?leading=${this._leading} ?closeOnEscape=${this._closeOnEscape} timeoutMs=${this._timeoutMs}>
|
||||
${this._action}
|
||||
${this._dismiss ? html`
|
||||
<mwc-icon-button icon="${this._dismissIcon}" slot="dismiss"></mwc-icon-button>
|
||||
` : ''}
|
||||
</mwc-snackbar>
|
||||
`
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this._snackbar = this.shadowRoot.getElementById('snack')
|
||||
}
|
||||
|
||||
_shift() {
|
||||
if (this.busy || this._queue.length === 0) return
|
||||
const item = this._queue.shift()
|
||||
this._labelText = item.labelText || ''
|
||||
this._action = item.action || ''
|
||||
this._dismiss = item.dismiss || false
|
||||
this._dismissIcon = item.dismissIcon || 'close'
|
||||
this._leading = !!item.leading
|
||||
this._closeOnEscape = (item.closeOnEscape && item.closeOnEscape !== false) // JSON.parse maybe needs to be compared to 'false'...in which case no need for complex expression
|
||||
this._timeoutMs = (item.timeoutMs >= 4000 && item.timeoutMs <= 10000) ? item.timeoutMs : 5000
|
||||
this._snackbar.show()
|
||||
}
|
||||
|
||||
add(item) {
|
||||
this._queue.push(item)
|
||||
this._shift()
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('snack-queue', SnackQueue)
|
||||
|
||||
const queueNode = document.createElement('snack-queue')
|
||||
queueNode.id = 'queue-node'
|
||||
queueNode.loadingMessage = ''
|
||||
queueElement = document.body.appendChild(queueNode)
|
||||
export default queueElement
|
Loading…
x
Reference in New Issue
Block a user