import {LitElement, html, unsafeHTML} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; import * as tfutils from './tf-utils.js'; const k_project = '%Hr+4xEVtjplidSKBlRWi4Aw/0Tfw7B+1OR9BzlDKmOI=.sha256'; class TfComposeElement extends LitElement { static get properties() { return { value: {type: String}, }; } input() { let input = this.renderRoot.getElementById('input'); let preview = this.renderRoot.getElementById('preview'); if (input && preview) { preview.innerHTML = tfutils.markdown(input.value); } } submit() { this.dispatchEvent( new CustomEvent('tf-submit', { bubbles: true, composed: true, detail: { value: this.renderRoot.getElementById('input').value, }, }) ); this.renderRoot.getElementById('input').value = ''; this.input(); } render() { return html`
`; } } customElements.define('tf-compose', TfComposeElement); class TfIssuesAppElement extends LitElement { static get properties() { return { issues: {type: Array}, selected: {type: Object}, }; } constructor() { super(); this.issues = []; this.load(); } async load() { let issues = {}; let messages = await tfrpc.rpc.query( ` WITH issues AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM messages_refs JOIN messages ON messages.id = messages_refs.message WHERE messages_refs.ref = ? AND json_extract(messages.content, '$.type') = 'issue'), edits AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM issues JOIN messages_refs ON issues.id = messages_refs.ref JOIN messages ON messages.id = messages_refs.message WHERE json_extract(messages.content, '$.type') IN ('issue-edit', 'post')) SELECT * FROM issues UNION SELECT * FROM edits ORDER BY timestamp `, [k_project] ); for (let message of messages) { let content = JSON.parse(message.content); switch (content.type) { case 'issue': issues[message.id] = { id: message.id, author: message.author, text: content.text, updates: [], created: message.timestamp, open: true, }; break; case 'issue-edit': case 'post': for (let issue of content.issues || []) { if (issues[issue.link]) { if (issue.open !== undefined) { issues[issue.link].open = issue.open; message.open = issue.open; } issues[issue.link].updates.push(message); issues[issue.link].updated = message.timestamp; } } break; } } this.issues = Object.values(issues).sort( (x, y) => y.open - x.open || y.created - x.created ); if (this.selected) { for (let issue of this.issues) { if (issue.id == this.selected.id) { this.selected = issue; } } } } render_issue_table_row(issue) { return html` ${issue.open ? '☐ open' : '☑ closed'} ${issue.author} (this.selected = issue)} > ${issue.text.split('\n')?.[0]} ${new Date(issue.updated ?? issue.created).toLocaleDateString()} `; } render_update(update) { let content = JSON.parse(update.content); let message; if (content.text) { message = unsafeHTML(tfutils.markdown(content.text)); } return html`
${new Date(update.timestamp).toLocaleString()}
${update.author}
${message}
${update.open !== undefined ? update.open ? 'issue opened' : 'issue closed' : undefined}
`; } async set_open(id, open) { if ( confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`) ) { let whoami = await tfrpc.rpc.getActiveIdentity(); await tfrpc.rpc.appendMessage(whoami, { type: 'issue-edit', issues: [ { link: id, open: open, }, ], }); await this.load(); } } async create_issue(event) { let whoami = await tfrpc.rpc.getActiveIdentity(); await tfrpc.rpc.appendMessage(whoami, { type: 'issue', project: k_project, text: event.detail.value, }); await this.load(); } async reply_to_issue(event) { let whoami = await tfrpc.rpc.getActiveIdentity(); await tfrpc.rpc.appendMessage(whoami, { type: 'post', text: event.detail.value, root: this.selected.id, branch: this.selected.updates.length ? this.selected.updates[this.selected.updates.length - 1].id : this.selected.id, issues: [ { link: this.selected.id, }, ], }); await this.load(); } render() { let header = html`

Tilde Friends Issues

`; if (this.selected) { return html` ${header}
(this.selected = undefined)}> ${ this.selected.open ? html` this.set_open(this.selected.id, false)}>` : html` this.set_open(this.selected.id, true)}>` }
${new Date(this.selected.created).toLocaleString()}
${this.selected.author}
${this.selected.id}
${unsafeHTML(tfutils.markdown(this.selected.text))}
${this.selected.updates.map((x) => this.render_update(x))} `; } else { return html` ${header}

New Issue

${this.issues.map((x) => this.render_issue_table_row(x))}
Status Author Title Date
`; } } } customElements.define('tf-issues-app', TfIssuesAppElement);