236 lines
5.9 KiB
JavaScript
236 lines
5.9 KiB
JavaScript
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
|
|
import * as tfrpc from '/static/tfrpc.js';
|
|
import {styles} from './tf-styles.js';
|
|
|
|
class TfNewsElement extends LitElement {
|
|
static get properties() {
|
|
return {
|
|
whoami: {type: String},
|
|
users: {type: Object},
|
|
messages: {type: Array},
|
|
following: {type: Array},
|
|
drafts: {type: Object},
|
|
expanded: {type: Object},
|
|
channel: {type: String},
|
|
channel_unread: {type: Number},
|
|
};
|
|
}
|
|
|
|
static styles = styles;
|
|
|
|
constructor() {
|
|
super();
|
|
let self = this;
|
|
this.whoami = null;
|
|
this.users = {};
|
|
this.messages = [];
|
|
this.following = [];
|
|
this.drafts = {};
|
|
this.expanded = {};
|
|
this.channel_unread = -1;
|
|
}
|
|
|
|
process_messages(messages) {
|
|
let self = this;
|
|
let messages_by_id = {};
|
|
|
|
console.log('processing', messages.length, 'messages');
|
|
|
|
function ensure_message(id, rowid) {
|
|
let found = messages_by_id[id];
|
|
if (found) {
|
|
return found;
|
|
} else {
|
|
let added = {
|
|
rowid: rowid,
|
|
id: id,
|
|
placeholder: true,
|
|
content: '"placeholder"',
|
|
parent_message: undefined,
|
|
child_messages: [],
|
|
votes: [],
|
|
};
|
|
messages_by_id[id] = added;
|
|
return added;
|
|
}
|
|
}
|
|
|
|
function link_message(message) {
|
|
if (message.content.type === 'vote') {
|
|
let parent = ensure_message(message.content.vote.link, message.rowid);
|
|
if (!parent.votes) {
|
|
parent.votes = [];
|
|
}
|
|
parent.votes.push(message);
|
|
message.parent_message = message.content.vote.link;
|
|
} else if (message.content.type == 'post') {
|
|
if (message.content.root) {
|
|
if (typeof message.content.root === 'string') {
|
|
let m = ensure_message(message.content.root, message.rowid);
|
|
if (!m.child_messages) {
|
|
m.child_messages = [];
|
|
}
|
|
m.child_messages.push(message);
|
|
message.parent_message = message.content.root;
|
|
} else {
|
|
let m = ensure_message(message.content.root[0], message.rowid);
|
|
if (!m.child_messages) {
|
|
m.child_messages = [];
|
|
}
|
|
m.child_messages.push(message);
|
|
message.parent_message = message.content.root[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let message of messages) {
|
|
message.votes = [];
|
|
message.parent_message = undefined;
|
|
message.child_messages = undefined;
|
|
}
|
|
|
|
for (let message of messages) {
|
|
try {
|
|
message.content = JSON.parse(message.content);
|
|
} catch {}
|
|
if (!messages_by_id[message.id]) {
|
|
messages_by_id[message.id] = message;
|
|
link_message(message);
|
|
} else if (messages_by_id[message.id].placeholder) {
|
|
let placeholder = messages_by_id[message.id];
|
|
messages_by_id[message.id] = message;
|
|
message.parent_message = placeholder.parent_message;
|
|
message.child_messages = placeholder.child_messages;
|
|
message.votes = placeholder.votes;
|
|
if (
|
|
placeholder.parent_message &&
|
|
messages_by_id[placeholder.parent_message]
|
|
) {
|
|
let children =
|
|
messages_by_id[placeholder.parent_message].child_messages;
|
|
children.splice(children.indexOf(placeholder), 1);
|
|
children.push(message);
|
|
}
|
|
link_message(message);
|
|
}
|
|
}
|
|
|
|
return messages_by_id;
|
|
}
|
|
|
|
update_latest_subtree_timestamp(messages) {
|
|
let latest = 0;
|
|
for (let message of messages || []) {
|
|
if (message.latest_subtree_timestamp === undefined) {
|
|
message.latest_subtree_timestamp = Math.max(
|
|
message.timestamp ?? 0,
|
|
this.update_latest_subtree_timestamp(message.child_messages)
|
|
);
|
|
}
|
|
latest = Math.max(latest, message.latest_subtree_timestamp);
|
|
}
|
|
return latest;
|
|
}
|
|
|
|
finalize_messages(messages_by_id) {
|
|
function recursive_sort(messages, top) {
|
|
if (messages) {
|
|
if (top) {
|
|
messages.sort(
|
|
(a, b) => b.latest_subtree_timestamp - a.latest_subtree_timestamp
|
|
);
|
|
} else {
|
|
messages.sort((a, b) => a.timestamp - b.timestamp);
|
|
}
|
|
for (let message of messages) {
|
|
recursive_sort(message.child_messages, false);
|
|
}
|
|
return messages.map((x) => Object.assign({}, x));
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
let roots = Object.values(messages_by_id).filter((x) => !x.parent_message);
|
|
this.update_latest_subtree_timestamp(roots);
|
|
return recursive_sort(roots, true);
|
|
}
|
|
|
|
group_following(messages) {
|
|
let result = [];
|
|
let group = [];
|
|
for (let message of messages) {
|
|
if (message?.content?.type === 'contact') {
|
|
group.push(message);
|
|
} else {
|
|
if (group.length > 0) {
|
|
result.push({
|
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
|
type: 'contact_group',
|
|
messages: group,
|
|
});
|
|
group = [];
|
|
}
|
|
result.push(message);
|
|
}
|
|
}
|
|
if (group.length > 0) {
|
|
result.push({
|
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
|
type: 'contact_group',
|
|
messages: group,
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
load_and_render(messages) {
|
|
let messages_by_id = this.process_messages(messages);
|
|
let final_messages = this.group_following(
|
|
this.finalize_messages(messages_by_id)
|
|
);
|
|
let unread_rowid = -1;
|
|
for (let message of final_messages) {
|
|
if (message.rowid >= this.channel_unread) {
|
|
unread_rowid = message.rowid;
|
|
}
|
|
}
|
|
return html`
|
|
<div>
|
|
${final_messages.map(
|
|
(x) => html`
|
|
<tf-message
|
|
.message=${x}
|
|
whoami=${this.whoami}
|
|
.users=${this.users}
|
|
.drafts=${this.drafts}
|
|
.expanded=${this.expanded}
|
|
collapsed="true"
|
|
channel=${this.channel}
|
|
channel_unread=${this.channel_unread}
|
|
></tf-message>
|
|
${x.rowid == unread_rowid && x != final_messages[0]
|
|
? html`<div style="display: flex; flex-direction: row">
|
|
<div
|
|
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
|
|
></div>
|
|
<div style="color: #f00; padding: 8px">unread</div>
|
|
<div
|
|
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
|
|
></div>
|
|
</div>`
|
|
: undefined}
|
|
`
|
|
)}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
render() {
|
|
return this.load_and_render(this.messages || []);
|
|
}
|
|
}
|
|
|
|
customElements.define('tf-news', TfNewsElement);
|