Accrued fixes, most recently making an effort to show all mentions.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3982 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
7077e69bf7
commit
88592886ca
@ -1 +1 @@
|
||||
{"type":"tildefriends-app","files":{"app.js":"&viCT+Sz8weP/j5V47w0wA4sk46HM4uy6lajX5NtoqHE=.sha256","lit-all.min.js":"&N4A12AsifdQgwdpII0SFtG513BfoLpmPjdJ9VTDftpg=.sha256","index.html":"&WH8A5tF25xlfPDGei2TCQc2/HJFJf5DuRN1GRSYQhhk=.sha256","script.js":"&G8puK9Q4MngHy3D4ppcKyT49WKbHD2OCeUcAw2ghTDE=.sha256","lit-all.min.js.map":"&oFY9wO4MnujgfGNGv4VggHc5V5JwX4C8csqKZ6KJYbE=.sha256","tf-id-picker.js":"&pg1gLK150HFai73TcmAe5E/dMpMqmbhyre/+/J4XmHo=.sha256","tf-app.js":"&Zt1x1urnzk0D9TxQvgJqOjdHelW+bei7CRupODgvAsk=.sha256","tf-message.js":"&KE1fWTqPMZR0yIRXPBGy8u1chR6LTguSK6swo+lFgE4=.sha256","tf-user.js":"&L6+7BnBq+UOoTMO6o8+u5JFTl0UBtCPDw8bb8ppDrkA=.sha256","tf-utils.js":"&N2yKZwFnb2GbPeipgQtu6xFvezENNOgud9G7EhCQ/K0=.sha256","commonmark.min.js":"&bfBaMLU19d1p/vPBF9hlARqDX002KXG/UOfxOahZhe4=.sha256","tf-compose.js":"&oo0iWvT+c2rU91zWpBIfPePRzmU8qmSnVOm+QCQqG/I=.sha256","emojis.json":"&h3P4pez+AI4aYdsN0dJ3pbUEFR0276t9AM20caj/W/s=.sha256","emojis.js":"&htPMi2z6Bmgi3f9jCnECCDZRCHACnDRjOl1kgPm+W80=.sha256","tf-styles.js":"&BkvFkMpGyL0DYP6FISFKR4pe6ZBOp8t6tQEzWZ4IQYs=.sha256","tf-profile.js":"&vRKjsnYvOiHCQahzEfznCvP5YDwUPtltlpWf+pxwZ1Y=.sha256","commonmark-linkify.js":"&X+hNNkmSRvKY86khyAun+cXksquXbMakZdINbGbx30g=.sha256","tf-tab-search.js":"&Q4bstnLzTPmCKJP+cf7FfRZJVuGAltEely4oIovUVaI=.sha256","tf-tab-news.js":"&Pc/FkHOPRPyWZi/znjquVtXeykzqqFygVuNm0dxrwlI=.sha256","tf-tab-connections.js":"&jSnF/5NmgqxRze1XQAEGOW5mPzOV1/8aCyrDRZu34IQ=.sha256","tf-news.js":"&C1dKe98kQOkClnAbGvcreC15IdlTrD9J4RFohspnsSE=.sha256"}}
|
||||
{"type":"tildefriends-app","files":{"app.js":"&viCT+Sz8weP/j5V47w0wA4sk46HM4uy6lajX5NtoqHE=.sha256","lit-all.min.js":"&N4A12AsifdQgwdpII0SFtG513BfoLpmPjdJ9VTDftpg=.sha256","index.html":"&WH8A5tF25xlfPDGei2TCQc2/HJFJf5DuRN1GRSYQhhk=.sha256","script.js":"&G8puK9Q4MngHy3D4ppcKyT49WKbHD2OCeUcAw2ghTDE=.sha256","lit-all.min.js.map":"&oFY9wO4MnujgfGNGv4VggHc5V5JwX4C8csqKZ6KJYbE=.sha256","tf-id-picker.js":"&pg1gLK150HFai73TcmAe5E/dMpMqmbhyre/+/J4XmHo=.sha256","tf-app.js":"&F64EHiKTMf/65Cc0w/F7oFbBcUOn7uTAjFBPd5rymSs=.sha256","tf-message.js":"&AFDKCN+hxYDs7zG8PBKmle11gE/2AIMzYr3FYz1hCto=.sha256","tf-user.js":"&L6+7BnBq+UOoTMO6o8+u5JFTl0UBtCPDw8bb8ppDrkA=.sha256","tf-utils.js":"&N2yKZwFnb2GbPeipgQtu6xFvezENNOgud9G7EhCQ/K0=.sha256","commonmark.min.js":"&bfBaMLU19d1p/vPBF9hlARqDX002KXG/UOfxOahZhe4=.sha256","tf-compose.js":"&oo0iWvT+c2rU91zWpBIfPePRzmU8qmSnVOm+QCQqG/I=.sha256","emojis.json":"&h3P4pez+AI4aYdsN0dJ3pbUEFR0276t9AM20caj/W/s=.sha256","emojis.js":"&pqYLDE/13PyEt2ceeFqvnwZ8NqWfPfpDBt4vP8SeHbs=.sha256","tf-styles.js":"&Ab+SjsySJ74kwK3EQD/j72yXYJlFAhkJ5EqyJfYpJEk=.sha256","tf-profile.js":"&vRKjsnYvOiHCQahzEfznCvP5YDwUPtltlpWf+pxwZ1Y=.sha256","commonmark-linkify.js":"&X+hNNkmSRvKY86khyAun+cXksquXbMakZdINbGbx30g=.sha256","tf-tab-search.js":"&NUGpMnLR3eYwrdjZaJAd8s4Rj+WPazJhWWX5jkMdNRI=.sha256","tf-tab-news.js":"&ehXkzOR+kQmiTHRtu5GPDMwrB4a4Z9vVsTo4ldhdu/E=.sha256","tf-tab-connections.js":"&jSnF/5NmgqxRze1XQAEGOW5mPzOV1/8aCyrDRZu34IQ=.sha256","tf-news.js":"&C1dKe98kQOkClnAbGvcreC15IdlTrD9J4RFohspnsSE=.sha256"}}
|
@ -38,7 +38,6 @@ export function picker(callback, anchor) {
|
||||
list.removeChild(list.firstChild);
|
||||
}
|
||||
let search = input.value;
|
||||
console.log('refresh', search);
|
||||
Object.entries(json).forEach(function(row) {
|
||||
let header = document.createElement('div');
|
||||
header.appendChild(document.createTextNode(row[0]));
|
||||
|
@ -11,6 +11,10 @@ class TfElement extends LitElement {
|
||||
tab: {type: String},
|
||||
broadcasts: {type: Array},
|
||||
connections: {type: Array},
|
||||
loading: {type: Boolean},
|
||||
loaded: {type: Boolean},
|
||||
following: {type: Array},
|
||||
users: {type: Object},
|
||||
};
|
||||
}
|
||||
|
||||
@ -24,6 +28,9 @@ class TfElement extends LitElement {
|
||||
this.tab = 'news';
|
||||
this.broadcasts = [];
|
||||
this.connections = [];
|
||||
this.following = [];
|
||||
this.users = {};
|
||||
this.loaded = false;
|
||||
tfrpc.rpc.getBroadcasts().then(b => { self.broadcasts = b || [] });
|
||||
tfrpc.rpc.getConnections().then(c => { self.connections = c || [] });
|
||||
tfrpc.rpc.getHash().then(hash => self.hash = hash || '#');
|
||||
@ -40,9 +47,7 @@ class TfElement extends LitElement {
|
||||
self.connections = value;
|
||||
}
|
||||
});
|
||||
tfrpc.rpc.localStorageGet('whoami').then(function(value) {
|
||||
self.whoami = value;
|
||||
});
|
||||
tfrpc.rpc.localStorageGet('whoami').then(whoami => self.whoami = whoami);
|
||||
}
|
||||
|
||||
async contacts_internal(id, last_row_id, following, max_row_id) {
|
||||
@ -71,31 +76,29 @@ class TfElement extends LitElement {
|
||||
delete result.blocking[contact.contact];
|
||||
}
|
||||
}
|
||||
following[id] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
async contact(id, last_row_id, following, max_row_id, contact_cache) {
|
||||
let result = await this.contacts_internal(id, last_row_id, following, max_row_id);
|
||||
contact_cache[id] = Object.assign(contact_cache[id] || {}, result);
|
||||
following[id] = contact_cache[id];
|
||||
return result;
|
||||
async contact(id, last_row_id, following, max_row_id) {
|
||||
return await this.contacts_internal(id, last_row_id, following, max_row_id);
|
||||
}
|
||||
|
||||
async following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id, contact_cache) {
|
||||
let contacts = await Promise.all([...new Set(ids)].map(x => this.contact(x, last_row_id, following, max_row_id, contact_cache)));
|
||||
async following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id) {
|
||||
let contacts = await Promise.all([...new Set(ids)].map(x => this.contact(x, last_row_id, following, max_row_id)));
|
||||
let result = {};
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
let id = ids[i];
|
||||
let contact = contacts[i];
|
||||
let found = Object.keys(contact.following).filter(y => !contact.blocking[y]);
|
||||
let deeper = depth > 1 ? await this.following_deep_internal(found, depth - 1, Object.assign({}, contact.blocking, blocking), last_row_id, following, max_row_id, contact_cache) : [];
|
||||
let deeper = depth > 1 ? await this.following_deep_internal(found, depth - 1, Object.assign({}, contact.blocking, blocking), last_row_id, following, max_row_id) : [];
|
||||
result[id] = [id, ...found, ...deeper];
|
||||
}
|
||||
return [...new Set(Object.values(result).flat())];
|
||||
}
|
||||
|
||||
async following_deep(ids, depth, blocking) {
|
||||
const k_cache_version = 4;
|
||||
const k_cache_version = 5;
|
||||
let cache = await tfrpc.rpc.databaseGet('following');
|
||||
cache = cache ? JSON.parse(cache) : {};
|
||||
if (cache.version !== k_cache_version) {
|
||||
@ -105,14 +108,14 @@ class TfElement extends LitElement {
|
||||
last_row_id: 0,
|
||||
};
|
||||
}
|
||||
let contact_cache = {};
|
||||
let max_row_id = (await tfrpc.rpc.query(`
|
||||
SELECT MAX(rowid) AS max_row_id FROM messages
|
||||
`, []))[0].max_row_id;
|
||||
let result = await this.following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id, contact_cache);
|
||||
let result = await this.following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id);
|
||||
cache.last_row_id = max_row_id;
|
||||
await tfrpc.rpc.databaseSet('following', JSON.stringify(cache));
|
||||
return result;
|
||||
console.log(cache);
|
||||
return [result, cache.following];
|
||||
}
|
||||
|
||||
async fetch_about(ids, users) {
|
||||
@ -191,24 +194,16 @@ class TfElement extends LitElement {
|
||||
WHERE messages.id = ?
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.allFollowing),
|
||||
JSON.stringify(this.following),
|
||||
id,
|
||||
]);
|
||||
let self = this;
|
||||
let mine = messages.filter(m => m.author === self.whoami);
|
||||
if (mine.length) {
|
||||
this.process_messages(mine);
|
||||
await this.finalize_messages();
|
||||
}
|
||||
let other = messages.filter(m => m.author !== self.whoami);
|
||||
if (other.length) {
|
||||
this.unread = [...this.unread, ...other];
|
||||
if (messages && messages.length) {
|
||||
this.unread = [...this.unread, ...messages];
|
||||
}
|
||||
}
|
||||
|
||||
_handle_whoami_changed(event) {
|
||||
if (this.whoami !== event.srcElement.selected) {
|
||||
console.log('whoami changed', event.srcElement.selected);
|
||||
this.whoami = event.srcElement.selected;
|
||||
}
|
||||
}
|
||||
@ -228,26 +223,58 @@ class TfElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
async render_tab() {
|
||||
let following = await this.following_deep([this.whoami], 2, {});
|
||||
let users = await this.fetch_about(following.sort());
|
||||
async load() {
|
||||
let whoami = this.whoami;
|
||||
let [following, users] = await this.following_deep([whoami], 2, {});
|
||||
users = await this.fetch_about(following.sort(), users);
|
||||
this.following = following;
|
||||
this.users = users;
|
||||
this.whoami = whoami;
|
||||
this.loaded = whoami;
|
||||
}
|
||||
|
||||
render_tab() {
|
||||
let following = this.following;
|
||||
let users = this.users;
|
||||
if (this.tab === 'news') {
|
||||
return html`
|
||||
<tf-tab-news .following=${following} whoami=${this.whoami} .users=${users} hash=${this.hash} .unread=${this.unread} @refresh=${() => this.unread = []}></tf-tab-news>
|
||||
<tf-tab-news .following=${this.following} whoami=${this.whoami} .users=${this.users} hash=${this.hash} .unread=${this.unread} @refresh=${() => this.unread = []}></tf-tab-news>
|
||||
`;
|
||||
} else if (this.tab === 'connections') {
|
||||
return html`
|
||||
<tf-tab-connections .users=${users} .connections=${this.connections} .broadcasts=${this.broadcasts}></tf-tab-connections>
|
||||
<tf-tab-connections .users=${this.users} .connections=${this.connections} .broadcasts=${this.broadcasts}></tf-tab-connections>
|
||||
`;
|
||||
} else if (this.tab === 'search') {
|
||||
return html`
|
||||
<tf-tab-search .following=${following} whoami=${this.whoami} .users=${users}></tf-tab-search>
|
||||
<tf-tab-search .following=${this.following} whoami=${this.whoami} .users=${this.users}></tf-tab-search>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
add_fake_news() {
|
||||
this.unread = [{
|
||||
author: this.whoami,
|
||||
placeholder: true,
|
||||
id: '%fake_id',
|
||||
text: 'text',
|
||||
content: 'hello',
|
||||
}, ...this.unread];
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
|
||||
if (!this.loading && this.whoami && this.loaded !== this.whoami) {
|
||||
console.log('loading', this.whoami);
|
||||
this.loading = true;
|
||||
this.following = [];
|
||||
this.users = {};
|
||||
this.load().finally(function() {
|
||||
self.loading = false;
|
||||
console.log('loaded');
|
||||
});
|
||||
}
|
||||
|
||||
let id_picker = html`
|
||||
${guard([this.whoami], () => until(this.render_id_picker(), html`<div>Loading...</div>`))}
|
||||
`;
|
||||
@ -258,10 +285,14 @@ class TfElement extends LitElement {
|
||||
<input type="button" value="Search" ?disabled=${self.tab == 'search'} @click=${event => self.tab = 'search'}></input>
|
||||
</div>
|
||||
`;
|
||||
let contents = !this.loaded ?
|
||||
html`<div>Loading...</div>` :
|
||||
this.render_tab();
|
||||
return html`
|
||||
${id_picker}
|
||||
${tabs}
|
||||
${until(this.render_tab(), html`<div>Loading...</div>`)}
|
||||
<input type="button" value="Fake News" @click=${this.add_fake_news}></input>
|
||||
${contents}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,40 @@ class TfMessageElement extends LitElement {
|
||||
emojis.picker(x => this.vote(x));
|
||||
}
|
||||
|
||||
render_mention(mention) {
|
||||
if (mention.link?.startsWith('&') &&
|
||||
mention.type?.startsWith('image/')) {
|
||||
return html`
|
||||
<img src=${'/' + mention.link + '/view'} style="max-width: 128px; max-height: 128px" title=${mention.name}>
|
||||
`;
|
||||
} else if (mention.link?.startsWith('&') &&
|
||||
mention.name?.startsWith('audio:')) {
|
||||
return html`
|
||||
<audio controls style="height: 32px">
|
||||
<source src=${'/' + mention.link + '/view'}></source>
|
||||
</audio>
|
||||
`;
|
||||
} else if (mention.link?.startsWith('%') || mention.link?.startsWith('@')) {
|
||||
return html` <a href=${'#' + encodeURIComponent(mention.link)}>${mention.name}</a>`;
|
||||
} else if (mention.link?.startsWith('#')) {
|
||||
return html` <a href=${'#q=' + encodeURIComponent(mention.link)}>${mention.link}</a>`;
|
||||
} else {
|
||||
return html`<pre>${JSON.stringify(mention)}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
render_mentions() {
|
||||
if (this.message?.content?.mentions?.length) {
|
||||
let self = this;
|
||||
return html`
|
||||
<fieldset style="background-color: rgba(0, 0, 0, 0.1); padding: 0.5em; border: 1px solid black">
|
||||
<legend>Mentions</legend>
|
||||
${(this.message?.content?.mentions || []).map(x => self.render_mention(x))}
|
||||
</fieldset>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = this.message?.content;
|
||||
let self = this;
|
||||
@ -153,6 +187,7 @@ class TfMessageElement extends LitElement {
|
||||
<span>${raw_button}</span>
|
||||
</div>
|
||||
<div>${body}</div>
|
||||
${this.render_mentions()}
|
||||
${this.render_votes()}
|
||||
<div>
|
||||
${reply}
|
||||
|
@ -12,4 +12,9 @@ a:visited {
|
||||
a:hover {
|
||||
color: #ddf;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 640px;
|
||||
max-height: 480px;
|
||||
}
|
||||
`;
|
@ -2,14 +2,14 @@ import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
class TfTabNewsElement extends LitElement {
|
||||
class TfTabNewsFeedElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
users: {type: Object},
|
||||
hash: {type: String},
|
||||
unread: {type: Array},
|
||||
following: {type: Array},
|
||||
messages: {type: Array},
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,9 +21,7 @@ class TfTabNewsElement extends LitElement {
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.hash = '#';
|
||||
this.unread = [];
|
||||
this.following = [];
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
async fetch_messages() {
|
||||
@ -65,42 +63,86 @@ class TfTabNewsElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
async show_more() {
|
||||
let unread = this.unread;
|
||||
this.unread = [];
|
||||
this.process_messages(unread);
|
||||
await this.finalize_messages();
|
||||
render() {
|
||||
if (!this.messages ||
|
||||
this._messages_hash !== this.hash ||
|
||||
this._messages_following !== this.following) {
|
||||
console.log('loading messages');
|
||||
let self = this;
|
||||
this.messages = [];
|
||||
this._messages_hash = this.hash;
|
||||
this._messages_following = this.following;
|
||||
this.fetch_messages().then(function(messages) {
|
||||
self.messages = messages;
|
||||
});
|
||||
}
|
||||
return html`<tf-news id="news" whoami=${this.whoami} .users=${this.users} .messages=${this.messages} .following=${this.following}></tf-news>`;
|
||||
}
|
||||
}
|
||||
|
||||
class TfTabNewsElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
users: {type: Object},
|
||||
hash: {type: String},
|
||||
unread: {type: Array},
|
||||
following: {type: Array},
|
||||
}
|
||||
}
|
||||
|
||||
async render_news() {
|
||||
if (this.cache.hash !== this.hash ||
|
||||
this.cache.whoami !== this.whoami ||
|
||||
this.cache.users !== this.users ||
|
||||
!this.cache.messages) {
|
||||
this.cache = {
|
||||
hash: this.hash,
|
||||
whoami: this.whoami,
|
||||
users: this.users,
|
||||
messages: this.fetch_messages(),
|
||||
};
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let self = this;
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.hash = '#';
|
||||
this.unread = [];
|
||||
this.following = [];
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
show_more() {
|
||||
let unread = this.unread;
|
||||
let news = this.renderRoot?.getElementById('news');
|
||||
if (news) {
|
||||
console.log('injecting messages', news.messages);
|
||||
news.messages = [...this.unread, ...news.messages];
|
||||
this.dispatchEvent(new CustomEvent('refresh'));
|
||||
}
|
||||
let messages = await this.cache.messages;
|
||||
return html`<tf-news whoami=${this.whoami} .users=${this.users} .messages=${messages}></tf-news>`;
|
||||
}
|
||||
|
||||
new_messages_text() {
|
||||
if (!this.unread?.length) {
|
||||
return 'No new messages.';
|
||||
}
|
||||
let counts = {};
|
||||
for (let message of this.unread) {
|
||||
let type = 'unknown';
|
||||
try {
|
||||
type = JSON.parse(message.content).type || type;
|
||||
} catch {
|
||||
}
|
||||
counts[type] = (counts[type] || 0) + 1;
|
||||
}
|
||||
return 'Show New: ' + Object.keys(counts).sort().map(x => (counts[x].toString() + ' ' + x + 's')).join(', ');
|
||||
}
|
||||
|
||||
render() {
|
||||
let profile = this.hash.startsWith('#@') ?
|
||||
html`<tf-profile id=${this.hash.substring(1)} whoami=${this.whoami} .users=${this.users}></tf-profile>` : undefined;
|
||||
return html`
|
||||
<div><input type="button" value=${'Show ' + this.unread.length + ' New Messages'} @click=${this.show_more}></input></div>
|
||||
<button id="load_button" @click=${this.load}>Load</button>
|
||||
<div><input type="button" value=${this.new_messages_text()} @click=${this.show_more}></input></div>
|
||||
<a target="_top" href="#" ?hidden=${this.hash.length <= 1}>🏠Home</a>
|
||||
<div>Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!</div>
|
||||
<div><tf-compose whoami=${this.whoami} .users=${this.users}></tf-compose></div>
|
||||
${profile}
|
||||
${until(this.render_news(), html`<div>Loading...</div>`)}
|
||||
<tf-tab-news-feed id="news" whoami=${this.whoami} .users=${this.users} .following=${this.following} hash=${this.hash}></tf-tab-news-feed>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-tab-news-feed', TfTabNewsFeedElement);
|
||||
customElements.define('tf-tab-news', TfTabNewsElement);
|
@ -31,7 +31,7 @@ class TfTabSearchElement extends LitElement {
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
ORDER BY timestamp DESC limit 100
|
||||
`,
|
||||
[query, JSON.stringify(this.following)]);
|
||||
['"' + query.replace('"', '""') + '"', JSON.stringify(this.following)]);
|
||||
console.log('Done.');
|
||||
this.renderRoot.getElementById('search').value = '';
|
||||
this.renderRoot.getElementById('news').messages = results;
|
||||
|
Loading…
Reference in New Issue
Block a user