Infinite scrolling improvements by @UmairMukhtar
This commit is contained in:
Umair Mukhtar 2021-07-26 13:31:35 +05:00 committed by GitHub
parent 92d8b8ae89
commit 60a77f49c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 219 additions and 342 deletions

View File

@ -1,30 +1,62 @@
import { Component } from "preact";
import Helpers, { html } from "../Helpers.js";
import PublicMessage from "./PublicMessage.js";
import {
List,
WindowScroller,
CellMeasurer,
CellMeasurerCache,
} from "react-virtualized";
import State from "../State.js";
import "react-virtualized/styles.css";
import $ from "jquery";
import { Component } from 'preact';
import Helpers, { html } from '../Helpers.js';
import PublicMessage from './PublicMessage.js';
import { List, WindowScroller,CellMeasurer,CellMeasurerCache,} from 'react-virtualized';
import State from '../State.js';
import 'react-virtualized/styles.css';
import $ from 'jquery';
class MessageFeed extends Component {
constructor() {
super();
this.state = { sortedMessages: [] };
this.state = {sortedMessages:[]};
this.mappedMessages = new Map();
this.eventListeners = {};
this._cache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 50,
minHeight: 100,
});
this.rowRenderer = this.rowRenderer.bind(this);
}
rowRenderer = ({ index, key, parent, style }) => {
componentDidMount() {
this.props.node.map().on((v, k, x, e) => {
if (!this.eventListeners['node']) {
this.eventListeners['node'] = e;
}
if (v) {
this.mappedMessages.set(k, this.props.keyIsMsgHash ? k : v);
} else {
this.mappedMessages.delete(k);
}
this.setState({
sortedMessages: Array.from(this.mappedMessages.keys()).sort().reverse().map(k => this.mappedMessages.get(k))
})
});
let first = true;
State.local.get('scrollUp').on(() => {
!first && Helpers.animateScrollTop('.main-view');
first = false;
});
}
unsubscribe() {
Object.values(this.eventListeners).forEach(e => e.off());
this.eventListeners = {};
}
componentDidUpdate(prevProps) {
if (this.props.node._.id !== prevProps.node._.id) {
this.unsubscribe();
this.setState({sortedMessages: [], mappedMessages: new Map()});
}
}
componentWillUnmount() {
this.unsubscribe();
}
rowRenderer = ({ index, key, parent, style,isScrolling }) => {
const hash = this.state.sortedMessages[index];
const msg =
typeof hash === "string"
@ -45,6 +77,7 @@ class MessageFeed extends Component {
>
{({ measure, registerChild }) => (
<div ref={registerChild} style={style}>
{measure()}
{typeof hash === "string"
? html`<${PublicMessage}
filter=${this.props.filter}
@ -62,46 +95,6 @@ class MessageFeed extends Component {
);
};
componentDidMount() {
this.props.node.map().on((v, k, x, e) => {
if (!this.eventListeners["node"]) {
this.eventListeners["node"] = e;
}
if (v) {
this.mappedMessages.set(k, this.props.keyIsMsgHash ? k : v);
} else {
this.mappedMessages.delete(k);
}
this.setState({
sortedMessages: Array.from(this.mappedMessages.keys())
.sort()
.reverse()
.map((k) => this.mappedMessages.get(k)),
});
});
let first = true;
State.local.get("scrollUp").on(() => {
!first && Helpers.animateScrollTop(".main-view");
first = false;
});
}
unsubscribe() {
Object.values(this.eventListeners).forEach((e) => e.off());
this.eventListeners = {};
}
componentDidUpdate(prevProps) {
if (this.props.node._.id !== prevProps.node._.id) {
this.unsubscribe();
this.setState({ sortedMessages: [], mappedMessages: new Map() });
}
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return html`
<div class="feed-container">
@ -128,4 +121,4 @@ class MessageFeed extends Component {
}
}
export default MessageFeed;
export default MessageFeed;

View File

@ -1,50 +1,25 @@
import { html } from "../Helpers.js";
import Helpers from "../Helpers.js";
import Identicon from "./Identicon.js";
import PublicMessageForm from "./PublicMessageForm.js";
import State from "../State.js";
import { route } from "preact-router";
import Message from "./Message.js";
import SafeImg from "./SafeImg.js";
import Session from "../Session.js";
import Torrent from "./Torrent.js";
import Autolinker from "autolinker";
import iris from "iris-lib";
import $ from "jquery";
import { html } from '../Helpers.js';
import Helpers from '../Helpers.js';
import Identicon from './Identicon.js';
import PublicMessageForm from './PublicMessageForm.js';
import State from '../State.js';
import { route } from 'preact-router';
import Message from './Message.js';
import SafeImg from './SafeImg.js';
import Session from '../Session.js';
import Torrent from './Torrent.js';
import Autolinker from 'autolinker';
import iris from 'iris-lib';
import $ from 'jquery';
const autolinker = new Autolinker({
stripPrefix: false,
stripTrailingSlash: false,
});
const autolinker = new Autolinker({ stripPrefix: false, stripTrailingSlash: false});
const heartEmpty = html`<svg width="24" viewBox="0 -28 512.001 512">
<path
fill="currentColor"
d="m256 455.515625c-7.289062 0-14.316406-2.640625-19.792969-7.4375-20.683593-18.085937-40.625-35.082031-58.21875-50.074219l-.089843-.078125c-51.582032-43.957031-96.125-81.917969-127.117188-119.3125-34.644531-41.804687-50.78125-81.441406-50.78125-124.742187 0-42.070313 14.425781-80.882813 40.617188-109.292969 26.503906-28.746094 62.871093-44.578125 102.414062-44.578125 29.554688 0 56.621094 9.34375 80.445312 27.769531 12.023438 9.300781 22.921876 20.683594 32.523438 33.960938 9.605469-13.277344 20.5-24.660157 32.527344-33.960938 23.824218-18.425781 50.890625-27.769531 80.445312-27.769531 39.539063 0 75.910156 15.832031 102.414063 44.578125 26.191406 28.410156 40.613281 67.222656 40.613281 109.292969 0 43.300781-16.132812 82.9375-50.777344 124.738281-30.992187 37.398437-75.53125 75.355469-127.105468 119.308594-17.625 15.015625-37.597657 32.039062-58.328126 50.167969-5.472656 4.789062-12.503906 7.429687-19.789062 7.429687zm-112.96875-425.523437c-31.066406 0-59.605469 12.398437-80.367188 34.914062-21.070312 22.855469-32.675781 54.449219-32.675781 88.964844 0 36.417968 13.535157 68.988281 43.882813 105.605468 29.332031 35.394532 72.960937 72.574219 123.476562 115.625l.09375.078126c17.660156 15.050781 37.679688 32.113281 58.515625 50.332031 20.960938-18.253907 41.011719-35.34375 58.707031-50.417969 50.511719-43.050781 94.136719-80.222656 123.46875-115.617188 30.34375-36.617187 43.878907-69.1875 43.878907-105.605468 0-34.515625-11.605469-66.109375-32.675781-88.964844-20.757813-22.515625-49.300782-34.914062-80.363282-34.914062-22.757812 0-43.652344 7.234374-62.101562 21.5-16.441406 12.71875-27.894532 28.796874-34.609375 40.046874-3.453125 5.785157-9.53125 9.238282-16.261719 9.238282s-12.808594-3.453125-16.261719-9.238282c-6.710937-11.25-18.164062-27.328124-34.609375-40.046874-18.449218-14.265626-39.34375-21.5-62.097656-21.5zm0 0"
/>
</svg>`;
const heartEmpty = html`<svg width="24" viewBox="0 -28 512.001 512"><path fill="currentColor" d="m256 455.515625c-7.289062 0-14.316406-2.640625-19.792969-7.4375-20.683593-18.085937-40.625-35.082031-58.21875-50.074219l-.089843-.078125c-51.582032-43.957031-96.125-81.917969-127.117188-119.3125-34.644531-41.804687-50.78125-81.441406-50.78125-124.742187 0-42.070313 14.425781-80.882813 40.617188-109.292969 26.503906-28.746094 62.871093-44.578125 102.414062-44.578125 29.554688 0 56.621094 9.34375 80.445312 27.769531 12.023438 9.300781 22.921876 20.683594 32.523438 33.960938 9.605469-13.277344 20.5-24.660157 32.527344-33.960938 23.824218-18.425781 50.890625-27.769531 80.445312-27.769531 39.539063 0 75.910156 15.832031 102.414063 44.578125 26.191406 28.410156 40.613281 67.222656 40.613281 109.292969 0 43.300781-16.132812 82.9375-50.777344 124.738281-30.992187 37.398437-75.53125 75.355469-127.105468 119.308594-17.625 15.015625-37.597657 32.039062-58.328126 50.167969-5.472656 4.789062-12.503906 7.429687-19.789062 7.429687zm-112.96875-425.523437c-31.066406 0-59.605469 12.398437-80.367188 34.914062-21.070312 22.855469-32.675781 54.449219-32.675781 88.964844 0 36.417968 13.535157 68.988281 43.882813 105.605468 29.332031 35.394532 72.960937 72.574219 123.476562 115.625l.09375.078126c17.660156 15.050781 37.679688 32.113281 58.515625 50.332031 20.960938-18.253907 41.011719-35.34375 58.707031-50.417969 50.511719-43.050781 94.136719-80.222656 123.46875-115.617188 30.34375-36.617187 43.878907-69.1875 43.878907-105.605468 0-34.515625-11.605469-66.109375-32.675781-88.964844-20.757813-22.515625-49.300782-34.914062-80.363282-34.914062-22.757812 0-43.652344 7.234374-62.101562 21.5-16.441406 12.71875-27.894532 28.796874-34.609375 40.046874-3.453125 5.785157-9.53125 9.238282-16.261719 9.238282s-12.808594-3.453125-16.261719-9.238282c-6.710937-11.25-18.164062-27.328124-34.609375-40.046874-18.449218-14.265626-39.34375-21.5-62.097656-21.5zm0 0"/></svg>`;
const heartFull = html`<svg width="24" viewBox="0 -28 512.00002 512">
<path
fill="currentColor"
d="m471.382812 44.578125c-26.503906-28.746094-62.871093-44.578125-102.410156-44.578125-29.554687 0-56.621094 9.34375-80.449218 27.769531-12.023438 9.300781-22.917969 20.679688-32.523438 33.960938-9.601562-13.277344-20.5-24.660157-32.527344-33.960938-23.824218-18.425781-50.890625-27.769531-80.445312-27.769531-39.539063 0-75.910156 15.832031-102.414063 44.578125-26.1875 28.410156-40.613281 67.222656-40.613281 109.292969 0 43.300781 16.136719 82.9375 50.78125 124.742187 30.992188 37.394531 75.535156 75.355469 127.117188 119.3125 17.613281 15.011719 37.578124 32.027344 58.308593 50.152344 5.476563 4.796875 12.503907 7.4375 19.792969 7.4375 7.285156 0 14.316406-2.640625 19.785156-7.429687 20.730469-18.128907 40.707032-35.152344 58.328125-50.171876 51.574219-43.949218 96.117188-81.90625 127.109375-119.304687 34.644532-41.800781 50.777344-81.4375 50.777344-124.742187 0-42.066407-14.425781-80.878907-40.617188-109.289063zm0 0"
/>
</svg>`;
const heartFull = html`<svg width="24" viewBox="0 -28 512.00002 512"><path fill="currentColor" d="m471.382812 44.578125c-26.503906-28.746094-62.871093-44.578125-102.410156-44.578125-29.554687 0-56.621094 9.34375-80.449218 27.769531-12.023438 9.300781-22.917969 20.679688-32.523438 33.960938-9.601562-13.277344-20.5-24.660157-32.527344-33.960938-23.824218-18.425781-50.890625-27.769531-80.445312-27.769531-39.539063 0-75.910156 15.832031-102.414063 44.578125-26.1875 28.410156-40.613281 67.222656-40.613281 109.292969 0 43.300781 16.136719 82.9375 50.78125 124.742187 30.992188 37.394531 75.535156 75.355469 127.117188 119.3125 17.613281 15.011719 37.578124 32.027344 58.308593 50.152344 5.476563 4.796875 12.503907 7.4375 19.792969 7.4375 7.285156 0 14.316406-2.640625 19.785156-7.429687 20.730469-18.128907 40.707032-35.152344 58.328125-50.171876 51.574219-43.949218 96.117188-81.90625 127.109375-119.304687 34.644532-41.800781 50.777344-81.4375 50.777344-124.742187 0-42.066407-14.425781-80.878907-40.617188-109.289063zm0 0"/></svg>`;
const replyIcon = html`<svg
width="24"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 512 512"
style="enable-background:new 0 0 512 512;"
>
<path
fill="currentColor"
d="M256,21.952c-141.163,0-256,95.424-256,212.715c0,60.267,30.805,117.269,84.885,157.717l-41.109,82.219 c-2.176,4.331-1.131,9.579,2.496,12.779c2.005,1.771,4.501,2.667,7.04,2.667c2.069,0,4.139-0.597,5.952-1.813l89.963-60.395
c33.877,12.971,69.781,19.541,106.752,19.541C397.141,447.381,512,351.957,512,234.667S397.163,21.952,256,21.952z M255.979,426.048c-36.16,0-71.168-6.741-104.043-20.032c-3.264-1.323-6.997-0.96-9.941,1.024l-61.056,40.981l27.093-54.187 c2.368-4.757,0.896-10.517-3.477-13.547c-52.907-36.629-83.243-89.707-83.243-145.6c0-105.536,105.28-191.381,234.667-191.381 s234.667,85.824,234.667,191.36S385.365,426.048,255.979,426.048z"
/>
</svg>`;
const replyIcon = html`<svg width="24" version="1.1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;"><path fill="currentColor" d="M256,21.952c-141.163,0-256,95.424-256,212.715c0,60.267,30.805,117.269,84.885,157.717l-41.109,82.219 c-2.176,4.331-1.131,9.579,2.496,12.779c2.005,1.771,4.501,2.667,7.04,2.667c2.069,0,4.139-0.597,5.952-1.813l89.963-60.395
c33.877,12.971,69.781,19.541,106.752,19.541C397.141,447.381,512,351.957,512,234.667S397.163,21.952,256,21.952z M255.979,426.048c-36.16,0-71.168-6.741-104.043-20.032c-3.264-1.323-6.997-0.96-9.941,1.024l-61.056,40.981l27.093-54.187 c2.368-4.757,0.896-10.517-3.477-13.547c-52.907-36.629-83.243-89.707-83.243-145.6c0-105.536,105.28-191.381,234.667-191.381 s234.667,85.824,234.667,191.36S385.365,426.048,255.979,426.048z"/></svg>`;
class PublicMessage extends Message {
constructor() {
@ -54,126 +29,119 @@ class PublicMessage extends Message {
this.likedBy = new Set();
this.replies = {};
this.subscribedReplies = new Set();
this.state = { sortedReplies: [], calledMeasure: false };
this.state = { sortedReplies: [], calledMeasure: false,measureFirstCall:false };
}
fetchByHash() {
const hash = this.props.hash;
if (typeof hash !== "string")
throw new Error(
"hash must be a string, got " + typeof hash + " " + JSON.stringify(hash)
);
return new Promise((resolve) => {
State.local
.get("msgsByHash")
.get(hash)
.once((msg) => {
if (typeof msg === "string") {
try {
resolve(JSON.parse(msg));
} catch (e) {
console.error("message parsing failed", msg, e);
}
if (typeof hash !== 'string') throw new Error('hash must be a string, got ' + typeof hash + ' ' + JSON.stringify(hash));
return new Promise(resolve => {
State.local.get('msgsByHash').get(hash).once(msg => {
if (typeof msg === 'string') {
try {
resolve(JSON.parse(msg));
} catch (e) {
console.error('message parsing failed', msg, e);
}
});
State.public
.get("#")
.get(hash)
.on(async (serialized, a, b, event) => {
if (typeof serialized !== "string") {
console.error("message parsing failed", hash, serialized);
return;
}
event.off();
const msg = await iris.SignedMessage.fromString(serialized);
if (msg) {
resolve(msg);
State.local.get("msgsByHash").get(hash).put(JSON.stringify(msg));
}
});
}
});
State.public.get('#').get(hash).on(async (serialized, a, b, event) => {
if (typeof serialized !== 'string') {
console.error('message parsing failed', hash, serialized);
return;
}
event.off();
const msg = await iris.SignedMessage.fromString(serialized);
if (msg) {
resolve(msg);
State.local.get('msgsByHash').get(hash).put(JSON.stringify(msg));
}
});
});
}
componentDidMount() {
this.fetchByHash().then((r) => {
this.fetchByHash().then(r => {
const msg = r.signedData;
msg.info = { from: r.signerKeyHash };
this.setState({ msg });
msg.info = {from: r.signerKeyHash};
this.setState({msg});
if (this.props.showName && !this.props.name) {
State.public
.user(msg.info.from)
.get("profile")
.get("name")
.on((name, a, b, e) => {
this.eventListeners["name"] = e;
this.setState({ name });
});
State.public.user(msg.info.from).get('profile').get('name').on((name, a,b, e) => {
this.eventListeners['name'] = e;
this.setState({name});
});
}
State.group().on(
`likes/${encodeURIComponent(this.props.hash)}`,
(liked, a, b, e, from) => {
this.eventListeners[from + "likes"] = e;
liked ? this.likedBy.add(from) : this.likedBy.delete(from);
const s = { likes: this.likedBy.size };
if (from === Session.getPubKey()) s["liked"] = liked;
this.setState(s);
State.group().on(`likes/${encodeURIComponent(this.props.hash)}`, (liked,a,b,e,from) => {
this.eventListeners[from+'likes'] = e;
liked ? this.likedBy.add(from) : this.likedBy.delete(from);
const s = {likes: this.likedBy.size};
if (from === Session.getPubKey()) s['liked'] = liked;
this.setState(s);
});
State.group().map(`replies/${encodeURIComponent(this.props.hash)}`, (hash,time,b,e,from) => {
const k = from + time;
if (hash && this.replies[k]) return;
if (hash) {
this.replies[k] = {hash, time};
} else {
delete this.replies[k];
}
);
State.group().map(
`replies/${encodeURIComponent(this.props.hash)}`,
(hash, time, b, e, from) => {
const k = from + time;
if (hash && this.replies[k]) return;
if (hash) {
this.replies[k] = { hash, time };
} else {
delete this.replies[k];
}
this.eventListeners[from + "replies"] = e;
const sortedReplies = Object.values(this.replies).sort((a, b) =>
a.time > b.time ? 1 : -1
);
this.setState({
replyCount: Object.keys(this.replies).length,
sortedReplies,
});
}
);
this.eventListeners[from+'replies'] = e;
const sortedReplies = Object.values(this.replies).sort((a,b) => a.time > b.time ? 1 : -1);
this.setState({replyCount: Object.keys(this.replies).length, sortedReplies });
});
});
if(this.state.measureFirstCall===false && this.props.measure)
{
setTimeout(() => {
this.props.measure();
this.setState({measureFirstCall:false})
}, 1);
}
}
componentDidUpdate(prevProps) {
if (prevProps.hash !== this.props.hash) {
Object.values(this.eventListeners).forEach((e) => e.off());
Object.values(this.eventListeners).forEach(e => e.off());
this.eventListeners = {};
this.likedBy = new Set();
this.replies = new Set();
this.subscribedReplies = new Set();
this.linksDone = false;
this.setState({ replies: 0, likes: 0, sortedReplies: [] });
this.setState({replies:0, likes: 0, sortedReplies:[]});
this.componentDidMount();
}
if (this.state.msg && !this.linksDone) {
$(this.base)
.find("a")
.off()
.on("click", (e) => {
const href = $(e.target).attr("href");
if (href && href.indexOf("https://iris.to/") === 0) {
e.preventDefault();
window.location = href.replace("https://iris.to/", "");
}
});
$(this.base).find('a').off().on('click', e => {
const href = $(e.target).attr('href');
if (href && href.indexOf('https://iris.to/') === 0) {
e.preventDefault();
window.location = href.replace('https://iris.to/', '');
}
});
this.linksDone = true;
}
if (this.state.showReplyForm && this.state.calledMeasure === false) {
if (this.state.showReplyForm && this.state.calledMeasure === false && this.props.measure) {
console.log("This will call measure");
this.setState({ calledMeasure: true });
this.props.measure();
} else if (
this.state.showReplyForm === false &&
this.state.calledMeasure === true
this.state.calledMeasure === true && this.props.measure
) {
console.log("This will call else measure");
this.setState({ calledMeasure: false });
this.props.measure();
}
if (this.state.showLikes && this.state.calledMeasure === false && this.props.measure) {
console.log("This will call measure");
this.setState({ calledMeasure: true });
this.props.measure();
} else if (
this.state.showLikes === false &&
this.state.calledMeasure === true && this.props.measure
) {
console.log("This will call else measure");
this.setState({ calledMeasure: false });
@ -183,12 +151,11 @@ class PublicMessage extends Message {
toggleReplies() {
const showReplyForm = !this.state.showReplyForm;
this.setState({ showReplyForm });
// this.setState({ calledMeasure: false });
this.setState({showReplyForm});
}
componentWillUnmount() {
Object.values(this.eventListeners).forEach((e) => e.off());
Object.values(this.eventListeners).forEach(e => e.off());
}
shouldComponentUpdate() {
@ -196,203 +163,120 @@ class PublicMessage extends Message {
}
onClickName() {
route("/profile/" + this.state.msg.info.from);
route('/profile/' + this.state.msg.info.from);
}
likeBtnClicked(e) {
e.preventDefault();
const liked = !this.state.liked;
State.public.user().get("likes").get(this.props.hash).put(liked);
State.public.user().get('likes').get(this.props.hash).put(liked);
}
onDelete(e) {
e.preventDefault();
if (confirm("Delete message?")) {
if (confirm('Delete message?')) {
const msg = this.state.msg;
msg.torrentId && State.public.user().get("media").get(msg.time).put(null);
State.public
.user()
.get(this.props.index || "msgs")
.get(msg.time)
.put(null);
msg.replyingTo &&
State.public
.user()
.get("replies")
.get(msg.replyingTo)
.get(msg.time)
.put(null);
msg.torrentId && State.public.user().get('media').get(msg.time).put(null);
State.public.user().get(this.props.index || 'msgs').get(msg.time).put(null);
msg.replyingTo && State.public.user().get('replies').get(msg.replyingTo).get(msg.time).put(null);
}
}
render() {
if (!this.state.msg) {
return "";
}
if (this.props.filter && !this.props.filter(this.state.msg)) {
return "";
}
if (!this.state.msg) { return ''; }
if (this.props.filter && !this.props.filter(this.state.msg)) { return ''; }
//if (++this.i > 1) console.log(this.i);
let name = this.props.name || this.state.name;
const emojiOnly =
this.state.msg.text &&
this.state.msg.text.length === 2 &&
Helpers.isEmoji(this.state.msg.text);
const isThumbnail = this.props.thumbnail ? "thumbnail-item" : "";
const p = document.createElement("p");
const emojiOnly = this.state.msg.text && this.state.msg.text.length === 2 && Helpers.isEmoji(this.state.msg.text);
const isThumbnail = this.props.thumbnail ? 'thumbnail-item' : '';
const p = document.createElement('p');
let text = this.state.msg.text;
if (isThumbnail && text.length > 128) {
text = text.slice(0, 128) + "...";
text = text.slice(0,128) + '...';
}
p.innerText = text;
const h = emojiOnly ? p.innerHTML : Helpers.highlightEmoji(p.innerHTML);
const innerHTML = autolinker.link(h);
const time =
typeof this.state.msg.time === "object"
? this.state.msg.time
: new Date(this.state.msg.time);
const dateStr = time.toLocaleString(window.navigator.language, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
});
const timeStr = time.toLocaleTimeString(window.navigator.language, {
timeStyle: "short",
});
const time = typeof this.state.msg.time === 'object' ? this.state.msg.time : new Date(this.state.msg.time);
const dateStr = time.toLocaleString(window.navigator.language, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
const timeStr = time.toLocaleTimeString(window.navigator.language, {timeStyle: 'short'});
return html`
<div class="msg ${isThumbnail} ${this.props.asReply ? "reply" : ""}">
<div class="msg ${isThumbnail} ${this.props.asReply ? 'reply' : ''}">
<div class="msg-content">
<div class="msg-sender">
<div class="msg-sender-link" onclick=${() => this.onClickName()}>
${this.state.msg.info.from
? html`<${Identicon}
str=${this.state.msg.info.from}
width="40"
/>`
: ""}
${name &&
this.props.showName &&
html`<small class="msgSenderName">${name}</small>`}
${this.state.msg.info.from ? html`<${Identicon} str=${this.state.msg.info.from} width=40/>` : ''}
${name && this.props.showName && html`<small class="msgSenderName">${name}</small>`}
</div>
${this.state.msg.info.from === Session.getPubKey()
? html`
<div class="msg-menu-btn">
<div class="dropdown">
<div class="dropbtn"></div>
<div class="dropdown-content">
<a href="#" onClick=${(e) => this.onDelete(e)}
>Delete</a
>
</div>
</div>
${this.state.msg.info.from === Session.getPubKey() ? html`
<div class="msg-menu-btn">
<div class="dropdown">
<div class="dropbtn">\u2026</div>
<div class="dropdown-content">
<a href="#" onClick=${e => this.onDelete(e)}>Delete</a>
</div>
`
: ""}
</div>
${this.state.msg.torrentId
? html` <${Torrent} torrentId=${this.state.msg.torrentId} /> `
: ""}
${this.state.msg.attachments &&
this.state.msg.attachments.map(
(a) =>
html`<div class="img-container">
<${SafeImg}
src=${a.data}
onclick=${(e) => {
this.openAttachmentsGallery(e);
}}
/>
</div>` // TODO: escape a.data
)}
<div
class="text ${emojiOnly && "emoji-only"}"
dangerouslySetInnerHTML=${{ __html: innerHTML }}
/>
${this.state.msg.replyingTo && !this.props.asReply
? html`
<div>
<a
href="/post/${encodeURIComponent(
this.state.msg.replyingTo
)}"
>Show replied message</a
>
</div>
`
: ""}
</div>
`: ''}
</div>
${
this.state.msg.torrentId && this.props.measure && this.props.measure()
}
${this.state.msg.torrentId ? html`
<${Torrent} torrentId=${this.state.msg.torrentId}/>
`:''}
${
this.state.msg.attachments && this.props.measure && this.props.measure()
}
${this.state.msg.attachments && this.state.msg.attachments.map(a =>
html`<div class="img-container"><${SafeImg} src=${a.data} onclick=${e => { this.openAttachmentsGallery(e); }}/></div>`
)}
<div class="text ${emojiOnly && 'emoji-only'}" dangerouslySetInnerHTML=${{ __html: innerHTML }} />
${this.state.msg.replyingTo && !this.props.asReply ? html`
<div><a href="/post/${encodeURIComponent(this.state.msg.replyingTo)}">Show replied message</a></div>
` : ''}
<div class="below-text">
<a
class="msg-btn reply-btn"
onClick=${() => {
this.toggleReplies();
}}
>
<a class="msg-btn reply-btn" onClick=${() => this.toggleReplies()}>
${replyIcon}
</a>
<span class="count" onClick=${() => this.toggleReplies()}>
${this.state.replyCount || ""}
${this.state.replyCount || ''}
</span>
<a
class="msg-btn like-btn ${this.state.liked ? "liked" : ""}"
onClick=${(e) => this.likeBtnClicked(e)}
>
<a class="msg-btn like-btn ${this.state.liked ? 'liked' : ''}" onClick=${e => this.likeBtnClicked(e)}>
${this.state.liked ? heartFull : heartEmpty}
</a>
<span
class="count"
onClick=${() => {
this.setState({ showLikes: !this.state.showLikes });
}}
>
${this.state.likes || ""}
<span class="count" onClick=${() => this.setState({showLikes: !this.state.showLikes})}>
${this.state.likes || ''}
</span>
<div class="time">
<a
href="/post/${encodeURIComponent(this.props.hash)}"
class="tooltip"
>
${Helpers.getRelativeTimeText(time)}
<span class="tooltiptext"> ${dateStr} ${timeStr} </span>
<a href="/post/${encodeURIComponent(this.props.hash)}" class="tooltip">
${Helpers.getRelativeTimeText(time)}
<span class="tooltiptext">
${dateStr} ${timeStr}
</span>
</a>
</div>
</div>
${this.state.showLikes
? html`
<div class="likes">
${Array.from(this.likedBy).map((key) => {
return html`<${Identicon}
showTooltip=${true}
onClick=${() => route("/profile/" + key)}
str=${key}
width="32"
/>`;
})}
</div>
`
: ""}
${(this.props.showReplies || this.state.showReplyForm) &&
this.state.sortedReplies &&
this.state.sortedReplies.length
? this.state.sortedReplies.map(
(r) =>
html`<${PublicMessage}
hash=${r.hash}
asReply=${true}
showName=${true}
measure=${this.props.measure}
showReplies=${true}
/>`
)
: ""}
${this.state.showReplyForm
? html` <${PublicMessageForm} replyingTo=${this.props.hash} /> `
: ""}
${this.state.showLikes ? html`
<div class="likes">
${Array.from(this.likedBy).map(key => {
return html`<${Identicon} showTooltip=${true} onClick=${() => route('/profile/' + key)} str=${key} width=32/>`;
})}
</div>
`: ''}
${(this.props.showReplies || this.state.showReplyForm) && this.state.sortedReplies && this.state.sortedReplies.length ? this.state.sortedReplies.map(r =>
html`<${PublicMessage} hash=${r.hash} asReply=${true} showName=${true} showReplies=${true} />`
) : ''}
${this.state.showReplyForm ? html`
<${PublicMessageForm} replyingTo=${this.props.hash} />
` : ''}
</div>
</div>
`;
`;
}
}
export default PublicMessage;
export default PublicMessage;