import {LitElement, html, unsafeHTML} from './lit-all.min.js'; import * as tfrpc from '/static/tfrpc.js'; import * as tfutils from './tf-utils.js'; import {styles} from './tf-styles.js'; class TfProfileElement extends LitElement { static get properties() { return { editing: {type: Object}, whoami: {type: String}, id: {type: String}, users: {type: Object}, size: {type: Number}, export_progress: {type: Object}, }; } static styles = styles; constructor() { super(); let self = this; this.editing = null; this.whoami = null; this.id = null; this.users = {}; this.size = 0; this.export_progress = null; } modify(change) { tfrpc.rpc.appendMessage(this.whoami, Object.assign({ type: 'contact', contact: this.id, }, change)).catch(function(error) { alert(error?.message); }); } follow() { this.modify({following: true}); } unfollow() { this.modify({following: false}); } block() { this.modify({blocking: true}); } unblock() { this.modify({blocking: false}); } edit() { let original = this.users[this.id]; this.editing = { name: original.name, description: original.description, image: original.image, }; console.log(this.editing); } save_edits() { let self = this; let message = { type: 'about', about: this.whoami, }; for (let key of Object.keys(this.editing)) { if (this.editing[key] !== this.users[this.id][key]) { message[key] = this.editing[key]; } } tfrpc.rpc.appendMessage(this.whoami, message).then(function() { self.editing = null; }).catch(function(error) { alert(error?.message); }); } discard_edits() { this.editing = null; } attach_image() { let self = this; let input = document.createElement('input'); input.type = 'file'; input.onchange = function(event) { let file = event.target.files[0]; file.arrayBuffer().then(function(buffer) { let bin = Array.from(new Uint8Array(buffer)); return tfrpc.rpc.store_blob(bin); }).then(function(id) { self.editing = Object.assign({}, self.editing, {image: id}); console.log(self.editing); }).catch(function(e) { alert(e.message); }); }; input.click(); } format_message(message) { let out = { previous: message.previous ?? null, }; if (message.sequence_before_author) { out.sequence = message.sequence; out.author = message.author; } else { out.author = message.author; out.sequence = message.sequence; } out.timestamp = message.timestamp; out.hash = message.hash; out.content = message.content; out.signature = message.signature; return out; } async export() { let all_messages = []; let sequence = -1; let messages_max = (await tfrpc.rpc.query('SELECT MAX(sequence) FROM messages WHERE author = ?', [this.id]))[0].sequence; while (true) { let messages = await tfrpc.rpc.query( 'SELECT * FROM messages WHERE author = ? AND SEQUENCE > ? ORDER BY sequence LIMIT 100', [this.id, sequence] ); if (messages?.length) { all_messages = [].concat(all_messages, messages.map(x => this.format_message(x))); sequence = messages[messages.length - 1].sequence; this.export_progress = {name: 'messages', value: all_messages.length, max: messages_max}; } else { break; } } let zip = new JSZip(); zip.file('messages.txt', JSON.stringify(all_messages, null, 2)); let blobs = await tfrpc.rpc.query( `SELECT blobs.id FROM messages JOIN messages_refs ON messages.id = messages_refs.message JOIN blobs ON messages_refs.ref = blobs.id WHERE messages.author = ?`, [this.id]); let blobs_done = 0; for (let row of blobs) { this.export_progress = {name: 'blobs', value: blobs_done, max: blobs.length}; let blob = await tfrpc.rpc.get_blob(row.id); zip.folder('blobs').file(row.id.replaceAll('/', '_').replaceAll('+', '-'), blob); blobs_done++; } this.export_progress = {name: 'saving'}; let blob = await zip.generateAsync({type: 'blob'}); saveAs(blob, `${this.id.replaceAll('/', '_').replaceAll('+', '-')}.zip`); this.export_progress = null; } render() { let self = this; let profile = this.users[this.id] || {}; tfrpc.rpc.query( `SELECT SUM(LENGTH(content)) AS size FROM messages WHERE author = ?`, [this.id]).then(function(result) { self.size = result[0].size; }); let edit; let follow; let block; if (this.id === this.whoami) { if (this.editing) { edit = html` `; } else { edit = html``; } } if (this.id !== this.whoami && this.users[this.whoami]?.following) { follow = this.users[this.whoami].following[this.id] ? html`` : html``; } if (this.id !== this.whoami && this.users[this.whoami]?.blocking) { block = this.users[this.whoami].blocking[this.id] ? html`` : html``; } let edit_profile = this.editing ? html`
this.editing = Object.assign({}, this.editing, {name: event.srcElement.value})}>
` : null; let export_state = html``; if (this.export_progress) { if (this.export_progress.max) { export_state = html`${this.export_progress.name}`; } else { export_state = html`${this.export_progress.name}`; } } let image = typeof(profile.image) == 'string' ? profile.image : profile.image?.link; image = this.editing?.image ?? image; let description = this.editing?.description ?? profile.description; return html`
(${tfutils.human_readable_size(this.size)})
${edit_profile}
${unsafeHTML(tfutils.markdown(description))}
Following ${Object.keys(profile.following || {}).length} identities. Followed by ${Object.values(self.users).filter(x => (x.following || {})[self.id]).length} identities. Blocking ${Object.keys(profile.blocking || {}).length} identities. Blocked by ${Object.values(self.users).filter(x => (x.blocking || {})[self.id]).length} identities.
${edit} ${follow} ${block} ${export_state}
`; } } customElements.define('tf-profile', TfProfileElement);