| 
									
										
										
										
											2024-04-24 20:19:14 -04:00
										 |  |  |  | import {LitElement, html, unsafeHTML, live} from './lit-all.min.js'; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | import * as tfutils from './tf-utils.js'; | 
					
						
							|  |  |  |  | import * as tfrpc from '/static/tfrpc.js'; | 
					
						
							| 
									
										
										
										
											2025-10-22 19:39:20 -04:00
										 |  |  |  | import {styles, generate_theme} from './tf-styles.js'; | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | import Tribute from './tribute.esm.js'; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class TfComposeElement extends LitElement { | 
					
						
							|  |  |  |  | 	static get properties() { | 
					
						
							|  |  |  |  | 		return { | 
					
						
							|  |  |  |  | 			whoami: {type: String}, | 
					
						
							|  |  |  |  | 			users: {type: Object}, | 
					
						
							|  |  |  |  | 			root: {type: String}, | 
					
						
							|  |  |  |  | 			branch: {type: String}, | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 			apps: {type: Object}, | 
					
						
							| 
									
										
										
										
											2023-01-21 00:16:18 +00:00
										 |  |  |  | 			drafts: {type: Object}, | 
					
						
							| 
									
										
										
										
											2024-04-04 00:50:59 +01:00
										 |  |  |  | 			author: {type: String}, | 
					
						
							| 
									
										
										
										
											2024-11-30 15:05:14 -05:00
										 |  |  |  | 			channel: {type: String}, | 
					
						
							| 
									
										
										
										
											2024-12-23 12:06:32 -05:00
										 |  |  |  | 			new_thread: {type: Boolean}, | 
					
						
							| 
									
										
										
										
											2025-08-13 19:53:28 -04:00
										 |  |  |  | 			recipients: {type: Array}, | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 	static styles = styles; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	constructor() { | 
					
						
							|  |  |  |  | 		super(); | 
					
						
							|  |  |  |  | 		this.users = {}; | 
					
						
							|  |  |  |  | 		this.root = undefined; | 
					
						
							|  |  |  |  | 		this.branch = undefined; | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		this.apps = undefined; | 
					
						
							| 
									
										
										
										
											2023-01-21 00:16:18 +00:00
										 |  |  |  | 		this.drafts = {}; | 
					
						
							| 
									
										
										
										
											2024-04-04 00:50:59 +01:00
										 |  |  |  | 		this.author = undefined; | 
					
						
							| 
									
										
										
										
											2024-12-23 12:06:32 -05:00
										 |  |  |  | 		this.new_thread = false; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 00:16:18 +00:00
										 |  |  |  | 	process_text(text) { | 
					
						
							|  |  |  |  | 		if (!text) { | 
					
						
							|  |  |  |  | 			return ''; | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 		/* Update mentions. */ | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		let updated = false; | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 		for (let match of text.matchAll(/\[([^\[]+)]\(([@&%][^\)]+)/g)) { | 
					
						
							|  |  |  |  | 			let name = match[1]; | 
					
						
							|  |  |  |  | 			let link = match[2]; | 
					
						
							|  |  |  |  | 			let balance = 0; | 
					
						
							|  |  |  |  | 			let bracket_end = match.index + match[1].length + '[]'.length - 1; | 
					
						
							|  |  |  |  | 			for (let i = bracket_end; i >= 0; i--) { | 
					
						
							|  |  |  |  | 				if (text.charAt(i) == ']') { | 
					
						
							|  |  |  |  | 					balance++; | 
					
						
							|  |  |  |  | 				} else if (text.charAt(i) == '[') { | 
					
						
							|  |  |  |  | 					balance--; | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				if (balance <= 0) { | 
					
						
							|  |  |  |  | 					name = text.substring(i + 1, bracket_end); | 
					
						
							|  |  |  |  | 					break; | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			if (!draft.mentions) { | 
					
						
							|  |  |  |  | 				draft.mentions = {}; | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			if (!draft.mentions[link]) { | 
					
						
							|  |  |  |  | 				draft.mentions[link] = { | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 					link: link, | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			draft.mentions[link].name = name.startsWith('@') | 
					
						
							|  |  |  |  | 				? name.substring(1) | 
					
						
							|  |  |  |  | 				: name; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			updated = true; | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if (updated) { | 
					
						
							| 
									
										
										
										
											2024-04-24 20:19:14 -04:00
										 |  |  |  | 			setTimeout(() => this.notify(draft), 0); | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-21 00:16:18 +00:00
										 |  |  |  | 		return tfutils.markdown(text); | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 01:39:00 +00:00
										 |  |  |  | 	input(event) { | 
					
						
							| 
									
										
										
										
											2023-01-21 00:16:18 +00:00
										 |  |  |  | 		let edit = this.renderRoot.getElementById('edit'); | 
					
						
							|  |  |  |  | 		let preview = this.renderRoot.getElementById('preview'); | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 		preview.innerHTML = this.process_text(edit.innerText); | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		let content_warning = this.renderRoot.getElementById('content_warning'); | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		draft.text = edit.innerText; | 
					
						
							| 
									
										
										
										
											2024-06-26 19:27:15 -04:00
										 |  |  |  | 		draft.content_warning = content_warning?.value; | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 		setTimeout(() => this.notify(draft), 0); | 
					
						
							| 
									
										
										
										
											2023-01-21 01:39:00 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-28 19:39:41 +00:00
										 |  |  |  | 	notify(draft) { | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 		this.dispatchEvent( | 
					
						
							|  |  |  |  | 			new CustomEvent('tf-draft', { | 
					
						
							|  |  |  |  | 				bubbles: true, | 
					
						
							|  |  |  |  | 				composed: true, | 
					
						
							|  |  |  |  | 				detail: { | 
					
						
							| 
									
										
										
										
											2025-08-14 12:40:58 -04:00
										 |  |  |  | 					id: | 
					
						
							|  |  |  |  | 						this.branch ?? | 
					
						
							|  |  |  |  | 						(this.recipients ? this.recipients.join(',') : undefined), | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					draft: draft, | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 			}) | 
					
						
							|  |  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2023-01-28 19:39:41 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-14 22:27:35 +00:00
										 |  |  |  | 	convert_to_format(buffer, type, mime_type) { | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 		return new Promise(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 			let img = new Image(); | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			img.onload = function () { | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 				let canvas = document.createElement('canvas'); | 
					
						
							| 
									
										
										
										
											2022-12-10 00:35:53 +00:00
										 |  |  |  | 				let width_scale = Math.min(img.width, 1024) / img.width; | 
					
						
							|  |  |  |  | 				let height_scale = Math.min(img.height, 1024) / img.height; | 
					
						
							|  |  |  |  | 				let scale = Math.min(width_scale, height_scale); | 
					
						
							|  |  |  |  | 				canvas.width = img.width * scale; | 
					
						
							|  |  |  |  | 				canvas.height = img.height * scale; | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 				let context = canvas.getContext('2d'); | 
					
						
							| 
									
										
										
										
											2022-12-10 00:35:53 +00:00
										 |  |  |  | 				context.drawImage(img, 0, 0, canvas.width, canvas.height); | 
					
						
							| 
									
										
										
										
											2023-01-14 22:27:35 +00:00
										 |  |  |  | 				let data_url = canvas.toDataURL(mime_type); | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				let result = atob(data_url.split(',')[1]) | 
					
						
							|  |  |  |  | 					.split('') | 
					
						
							|  |  |  |  | 					.map((x) => x.charCodeAt(0)); | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 				resolve(result); | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			img.onerror = function (event) { | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 				reject(new Error('Failed to load image.')); | 
					
						
							|  |  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			let raw = Array.from(new Uint8Array(buffer)) | 
					
						
							|  |  |  |  | 				.map((b) => String.fromCharCode(b)) | 
					
						
							|  |  |  |  | 				.join(''); | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 			let original = `data:${type};base64,${btoa(raw)}`; | 
					
						
							|  |  |  |  | 			img.src = original; | 
					
						
							|  |  |  |  | 		}); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-30 02:37:27 +00:00
										 |  |  |  | 	async add_file(file) { | 
					
						
							|  |  |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			let draft = this.get_draft(); | 
					
						
							| 
									
										
										
										
											2022-11-30 02:37:27 +00:00
										 |  |  |  | 			let self = this; | 
					
						
							|  |  |  |  | 			let buffer = await file.arrayBuffer(); | 
					
						
							|  |  |  |  | 			let type = file.type; | 
					
						
							|  |  |  |  | 			if (type.startsWith('image/')) { | 
					
						
							| 
									
										
										
										
											2023-01-14 22:27:35 +00:00
										 |  |  |  | 				let best_buffer; | 
					
						
							|  |  |  |  | 				let best_type; | 
					
						
							| 
									
										
										
										
											2023-01-17 23:10:17 +00:00
										 |  |  |  | 				for (let format of ['image/png', 'image/jpeg', 'image/webp']) { | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					let test_buffer = await self.convert_to_format( | 
					
						
							|  |  |  |  | 						buffer, | 
					
						
							|  |  |  |  | 						file.type, | 
					
						
							|  |  |  |  | 						format | 
					
						
							|  |  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2023-01-14 22:27:35 +00:00
										 |  |  |  | 					if (!best_buffer || test_buffer.length < best_buffer.length) { | 
					
						
							|  |  |  |  | 						best_buffer = test_buffer; | 
					
						
							|  |  |  |  | 						best_type = format; | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				buffer = best_buffer; | 
					
						
							|  |  |  |  | 				type = best_type; | 
					
						
							| 
									
										
										
										
											2022-11-30 02:37:27 +00:00
										 |  |  |  | 			} else { | 
					
						
							|  |  |  |  | 				buffer = Array.from(new Uint8Array(buffer)); | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			let id = await tfrpc.rpc.store_blob(buffer); | 
					
						
							|  |  |  |  | 			let name = type.split('/')[0] + ':' + file.name; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			if (!draft.mentions) { | 
					
						
							|  |  |  |  | 				draft.mentions = {}; | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 			draft.mentions[id] = { | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 				link: id, | 
					
						
							| 
									
										
										
										
											2022-11-30 02:37:27 +00:00
										 |  |  |  | 				name: name, | 
					
						
							|  |  |  |  | 				type: type, | 
					
						
							|  |  |  |  | 				size: buffer.length ?? buffer.byteLength, | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 			}; | 
					
						
							|  |  |  |  | 			let edit = self.renderRoot.getElementById('edit'); | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 			edit.innerText += `\n`; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			self.input(); | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 		} catch (e) { | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 			alert(e?.message); | 
					
						
							| 
									
										
										
										
											2022-11-30 02:37:27 +00:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-05 01:40:21 +00:00
										 |  |  |  | 	paste(event) { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2022-10-16 20:31:32 +00:00
										 |  |  |  | 		for (let item of event.clipboardData.items) { | 
					
						
							| 
									
										
										
										
											2022-10-05 01:40:21 +00:00
										 |  |  |  | 			if (item.type?.startsWith('image/')) { | 
					
						
							|  |  |  |  | 				let file = item.getAsFile(); | 
					
						
							|  |  |  |  | 				if (!file) { | 
					
						
							|  |  |  |  | 					continue; | 
					
						
							|  |  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 				self.add_file(file); | 
					
						
							| 
									
										
										
										
											2022-10-05 01:40:21 +00:00
										 |  |  |  | 				break; | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-10-17 12:41:29 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 		event.preventDefault(); | 
					
						
							| 
									
										
										
										
											2024-10-23 15:38:49 -04:00
										 |  |  |  | 		document.execCommand( | 
					
						
							|  |  |  |  | 			'insertText', | 
					
						
							|  |  |  |  | 			false, | 
					
						
							|  |  |  |  | 			event.clipboardData.getData('text/plain') | 
					
						
							|  |  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2022-10-05 01:40:21 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 	async submit() { | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		let draft = this.get_draft(); | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 		let edit = this.renderRoot.getElementById('edit'); | 
					
						
							|  |  |  |  | 		let message = { | 
					
						
							|  |  |  |  | 			type: 'post', | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 			text: edit.innerText, | 
					
						
							| 
									
										
										
										
											2024-11-30 15:05:14 -05:00
										 |  |  |  | 			channel: this.channel, | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 		}; | 
					
						
							|  |  |  |  | 		if (this.root || this.branch) { | 
					
						
							| 
									
										
										
										
											2024-12-24 14:22:24 -05:00
										 |  |  |  | 			message.root = this.new_thread ? (this.branch ?? this.root) : this.root; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 			message.branch = this.branch; | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-12-24 14:22:24 -05:00
										 |  |  |  | 		let reply = Object.fromEntries( | 
					
						
							|  |  |  |  | 			( | 
					
						
							|  |  |  |  | 				await tfrpc.rpc.query( | 
					
						
							|  |  |  |  | 					`
 | 
					
						
							| 
									
										
										
										
											2024-12-24 13:09:27 -05:00
										 |  |  |  | 				SELECT messages.id, messages.author FROM messages | 
					
						
							|  |  |  |  | 				JOIN json_each(?) AS refs ON messages.id = refs.value | 
					
						
							|  |  |  |  | 			`,
 | 
					
						
							| 
									
										
										
										
											2024-12-24 14:22:24 -05:00
										 |  |  |  | 					[JSON.stringify([this.root, this.branch])] | 
					
						
							|  |  |  |  | 				) | 
					
						
							|  |  |  |  | 			).map((row) => [row.id, row.author]) | 
					
						
							|  |  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2024-12-24 13:09:27 -05:00
										 |  |  |  | 		if (Object.keys(reply).length) { | 
					
						
							|  |  |  |  | 			message.reply = reply; | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		if (Object.values(draft.mentions || {}).length) { | 
					
						
							|  |  |  |  | 			message.mentions = Object.values(draft.mentions); | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if (draft.content_warning !== undefined) { | 
					
						
							|  |  |  |  | 			message.contentWarning = draft.content_warning; | 
					
						
							| 
									
										
										
										
											2022-10-15 19:02:09 +00:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 		console.log('Would post:', message); | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 		if (draft.encrypt_to) { | 
					
						
							|  |  |  |  | 			let to = new Set(draft.encrypt_to); | 
					
						
							|  |  |  |  | 			to.add(this.whoami); | 
					
						
							|  |  |  |  | 			to = [...to]; | 
					
						
							| 
									
										
										
										
											2023-09-02 13:25:31 +00:00
										 |  |  |  | 			message.recps = to; | 
					
						
							|  |  |  |  | 			console.log('message is now', message); | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			message = await tfrpc.rpc.encrypt( | 
					
						
							|  |  |  |  | 				this.whoami, | 
					
						
							|  |  |  |  | 				to, | 
					
						
							|  |  |  |  | 				JSON.stringify(message) | 
					
						
							|  |  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 			console.log('encrypted as', message); | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2024-06-26 19:30:58 -04:00
										 |  |  |  | 			await tfrpc.rpc.appendMessage(this.whoami, message); | 
					
						
							|  |  |  |  | 			self.notify(undefined); | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 		} catch (error) { | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 			alert(error.message); | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	discard() { | 
					
						
							| 
									
										
										
										
											2023-01-28 19:39:41 +00:00
										 |  |  |  | 		this.notify(undefined); | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	attach() { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							|  |  |  |  | 		let input = document.createElement('input'); | 
					
						
							|  |  |  |  | 		input.type = 'file'; | 
					
						
							| 
									
										
										
										
											2025-03-05 19:48:39 -05:00
										 |  |  |  | 		input.addEventListener('change', function (event) { | 
					
						
							|  |  |  |  | 			input.parentNode.removeChild(input); | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 			let file = event.target.files[0]; | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 			self.add_file(file); | 
					
						
							| 
									
										
										
										
											2025-03-05 19:48:39 -05:00
										 |  |  |  | 		}); | 
					
						
							|  |  |  |  | 		document.body.appendChild(input); | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 		input.click(); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 	async autocomplete(text, callback) { | 
					
						
							|  |  |  |  | 		this.last_autocomplete = text; | 
					
						
							|  |  |  |  | 		let results = []; | 
					
						
							|  |  |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			let rows = await tfrpc.rpc.query( | 
					
						
							|  |  |  |  | 				`
 | 
					
						
							| 
									
										
										
										
											2024-06-06 20:14:00 -04:00
										 |  |  |  | 				SELECT json(messages.content) AS content FROM messages_fts(?) | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 				JOIN messages ON messages.rowid = messages_fts.rowid | 
					
						
							| 
									
										
										
										
											2024-06-06 20:14:00 -04:00
										 |  |  |  | 				WHERE json(messages.content) LIKE ? | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 				ORDER BY timestamp DESC LIMIT 10 | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			`,
 | 
					
						
							|  |  |  |  | 				['"' + text.replace('"', '""') + '"', `%%`] | 
					
						
							|  |  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 			for (let row of rows) { | 
					
						
							|  |  |  |  | 				for (let match of row.content.matchAll(/!\[([^\]]*)\]\((&.*?)\)/g)) { | 
					
						
							| 
									
										
										
										
											2023-09-21 00:40:47 +00:00
										 |  |  |  | 					if (match[1].toLowerCase().indexOf(text.toLowerCase()) != -1) { | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 						results.push({key: match[1], value: match[2]}); | 
					
						
							|  |  |  |  | 					} | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} finally { | 
					
						
							|  |  |  |  | 			if (this.last_autocomplete === text) { | 
					
						
							|  |  |  |  | 				callback(results); | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-19 12:54:19 -04:00
										 |  |  |  | 	get_values() { | 
					
						
							| 
									
										
										
										
											2024-04-04 00:50:59 +01:00
										 |  |  |  | 		let values = Object.entries(this.users).map((x) => ({ | 
					
						
							|  |  |  |  | 			key: x[1].name ?? x[0], | 
					
						
							|  |  |  |  | 			value: x[0], | 
					
						
							|  |  |  |  | 		})); | 
					
						
							|  |  |  |  | 		if (this.author) { | 
					
						
							|  |  |  |  | 			values = [].concat( | 
					
						
							|  |  |  |  | 				[ | 
					
						
							|  |  |  |  | 					{ | 
					
						
							|  |  |  |  | 						key: this.users[this.author]?.name, | 
					
						
							|  |  |  |  | 						value: this.author, | 
					
						
							|  |  |  |  | 					}, | 
					
						
							|  |  |  |  | 				], | 
					
						
							|  |  |  |  | 				values | 
					
						
							|  |  |  |  | 			); | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-08-19 12:54:19 -04:00
										 |  |  |  | 		return values; | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	firstUpdated() { | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 		let tribute = new Tribute({ | 
					
						
							| 
									
										
										
										
											2024-06-06 19:52:37 -04:00
										 |  |  |  | 			iframe: this.shadowRoot, | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 			collection: [ | 
					
						
							|  |  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2025-08-19 12:54:19 -04:00
										 |  |  |  | 					values: this.get_values(), | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					selectTemplate: function (item) { | 
					
						
							| 
									
										
										
										
											2024-05-12 07:48:34 -04:00
										 |  |  |  | 						return item | 
					
						
							|  |  |  |  | 							? `[@${item.original.key}](${item.original.value})` | 
					
						
							|  |  |  |  | 							: undefined; | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 					}, | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 				{ | 
					
						
							|  |  |  |  | 					trigger: '&', | 
					
						
							|  |  |  |  | 					values: this.autocomplete, | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					selectTemplate: function (item) { | 
					
						
							| 
									
										
										
										
											2024-05-12 07:48:34 -04:00
										 |  |  |  | 						return item | 
					
						
							|  |  |  |  | 							? `` | 
					
						
							|  |  |  |  | 							: undefined; | 
					
						
							| 
									
										
										
										
											2023-09-21 00:26:29 +00:00
										 |  |  |  | 					}, | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 			], | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 		}); | 
					
						
							|  |  |  |  | 		tribute.attach(this.renderRoot.getElementById('edit')); | 
					
						
							| 
									
										
										
										
											2025-08-19 12:54:19 -04:00
										 |  |  |  | 		this._tribute = tribute; | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 	updated() { | 
					
						
							|  |  |  |  | 		super.updated(); | 
					
						
							|  |  |  |  | 		let edit = this.renderRoot.getElementById('edit'); | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 		if (this.last_updated_text !== edit.innerText) { | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			let preview = this.renderRoot.getElementById('preview'); | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 			preview.innerHTML = this.process_text(edit.innerText); | 
					
						
							|  |  |  |  | 			this.last_updated_text = edit.innerText; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-08-19 12:54:19 -04:00
										 |  |  |  | 		this._tribute.collection[0].values = this.get_values(); | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 		let encrypt = this.renderRoot.getElementById('encrypt_to'); | 
					
						
							|  |  |  |  | 		if (encrypt) { | 
					
						
							|  |  |  |  | 			let tribute = new Tribute({ | 
					
						
							| 
									
										
										
										
											2024-06-06 19:52:37 -04:00
										 |  |  |  | 				iframe: this.shadowRoot, | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				values: Object.entries(this.users).map((x) => ({ | 
					
						
							|  |  |  |  | 					key: x[1].name, | 
					
						
							|  |  |  |  | 					value: x[0], | 
					
						
							|  |  |  |  | 				})), | 
					
						
							|  |  |  |  | 				selectTemplate: function (item) { | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 					return item.original.value; | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 			}); | 
					
						
							|  |  |  |  | 			tribute.attach(encrypt); | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 	remove_mention(id) { | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		delete draft.mentions[id]; | 
					
						
							| 
									
										
										
										
											2025-04-13 07:53:15 -04:00
										 |  |  |  | 		setTimeout(() => this.notify(draft), 0); | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	render_mention(mention) { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 		return html` <div style="display: flex; flex-direction: row">
 | 
					
						
							|  |  |  |  | 			<div style="align-self: center; margin: 0.5em"> | 
					
						
							|  |  |  |  | 				<button | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 					class="w3-button w3-theme-d1" | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					title="Remove ${mention.name} mention" | 
					
						
							|  |  |  |  | 					@click=${() => self.remove_mention(mention.link)} | 
					
						
							|  |  |  |  | 				> | 
					
						
							|  |  |  |  | 					🚮 | 
					
						
							|  |  |  |  | 				</button> | 
					
						
							|  |  |  |  | 			</div> | 
					
						
							|  |  |  |  | 			<div style="display: flex; flex-direction: column"> | 
					
						
							|  |  |  |  | 				<h3>${mention.name}</h3> | 
					
						
							|  |  |  |  | 				<div style="padding-left: 1em"> | 
					
						
							|  |  |  |  | 					${Object.entries(mention) | 
					
						
							|  |  |  |  | 						.filter((x) => x[0] != 'name') | 
					
						
							|  |  |  |  | 						.map( | 
					
						
							|  |  |  |  | 							(x) => | 
					
						
							|  |  |  |  | 								html`<div>
 | 
					
						
							|  |  |  |  | 									<span style="font-weight: bold">${x[0]}</span>: ${x[1]} | 
					
						
							|  |  |  |  | 								</div>` | 
					
						
							|  |  |  |  | 						)} | 
					
						
							| 
									
										
										
										
											2023-05-02 16:47:27 +00:00
										 |  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			</div> | 
					
						
							|  |  |  |  | 		</div>`; | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 	render_attach_app() { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		async function attach_selected_app() { | 
					
						
							|  |  |  |  | 			let name = self.renderRoot.getElementById('select').value; | 
					
						
							|  |  |  |  | 			let id = self.apps[name]; | 
					
						
							|  |  |  |  | 			let mentions = {}; | 
					
						
							|  |  |  |  | 			mentions[id] = { | 
					
						
							|  |  |  |  | 				name: name, | 
					
						
							|  |  |  |  | 				link: id, | 
					
						
							|  |  |  |  | 				type: 'application/tildefriends', | 
					
						
							|  |  |  |  | 			}; | 
					
						
							|  |  |  |  | 			if (name && id) { | 
					
						
							|  |  |  |  | 				let app = JSON.parse(await tfrpc.rpc.get_blob(id)); | 
					
						
							|  |  |  |  | 				for (let entry of Object.entries(app.files)) { | 
					
						
							|  |  |  |  | 					mentions[entry[1]] = { | 
					
						
							|  |  |  |  | 						name: entry[0], | 
					
						
							|  |  |  |  | 						link: entry[1], | 
					
						
							|  |  |  |  | 					}; | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 			let draft = self.get_draft(); | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 			draft.mentions = Object.assign(draft.mentions || {}, mentions); | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 			self.requestUpdate(); | 
					
						
							|  |  |  |  | 			self.notify(draft); | 
					
						
							|  |  |  |  | 			self.apps = null; | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if (this.apps) { | 
					
						
							|  |  |  |  | 			return html`
 | 
					
						
							| 
									
										
										
										
											2024-01-12 04:23:31 +00:00
										 |  |  |  | 				<div class="w3-card-4 w3-margin w3-padding"> | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 					<select id="select" class="w3-select w3-theme-d1"> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 						${Object.keys(self.apps).map( | 
					
						
							|  |  |  |  | 							(app) => html`<option value=${app}>${app}</option>` | 
					
						
							|  |  |  |  | 						)} | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 					</select> | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 					<button class="w3-button w3-theme-d1" @click=${attach_selected_app}> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 						Attach | 
					
						
							|  |  |  |  | 					</button> | 
					
						
							|  |  |  |  | 					<button | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 						class="w3-button w3-theme-d1" | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 						@click=${() => (this.apps = null)} | 
					
						
							|  |  |  |  | 					> | 
					
						
							|  |  |  |  | 						Cancel | 
					
						
							|  |  |  |  | 					</button> | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			`;
 | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	render_attach_app_button() { | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		async function attach_app() { | 
					
						
							| 
									
										
										
										
											2023-03-29 22:02:12 +00:00
										 |  |  |  | 			self.apps = await tfrpc.rpc.apps(); | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 		if (!this.apps) { | 
					
						
							| 
									
										
										
										
											2025-06-02 21:47:35 -04:00
										 |  |  |  | 			return html`<button
 | 
					
						
							|  |  |  |  | 				class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							|  |  |  |  | 				@click=${attach_app} | 
					
						
							|  |  |  |  | 			> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				Attach App | 
					
						
							|  |  |  |  | 			</button>`; | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			return html`<button
 | 
					
						
							| 
									
										
										
										
											2025-06-02 21:47:35 -04:00
										 |  |  |  | 				class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				@click=${() => (this.apps = null)} | 
					
						
							|  |  |  |  | 			> | 
					
						
							|  |  |  |  | 				Discard App | 
					
						
							|  |  |  |  | 			</button>`; | 
					
						
							| 
									
										
										
										
											2022-10-18 23:00:57 +00:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 	set_content_warning(value) { | 
					
						
							|  |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		draft.content_warning = value; | 
					
						
							|  |  |  |  | 		this.notify(draft); | 
					
						
							|  |  |  |  | 		this.requestUpdate(); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	render_content_warning() { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							|  |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		if (draft.content_warning !== undefined) { | 
					
						
							|  |  |  |  | 			return html`
 | 
					
						
							| 
									
										
										
										
											2024-01-12 04:23:31 +00:00
										 |  |  |  | 				<div class="w3-container w3-padding"> | 
					
						
							| 
									
										
										
										
											2024-06-26 19:27:15 -04:00
										 |  |  |  | 					<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input> | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 				</div> | 
					
						
							|  |  |  |  | 			`;
 | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-23 12:06:32 -05:00
										 |  |  |  | 	render_new_thread() { | 
					
						
							|  |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2024-12-24 14:22:24 -05:00
										 |  |  |  | 		if ( | 
					
						
							|  |  |  |  | 			this.root !== undefined && | 
					
						
							|  |  |  |  | 			this.branch !== undefined && | 
					
						
							|  |  |  |  | 			this.root != this.branch | 
					
						
							|  |  |  |  | 		) { | 
					
						
							| 
									
										
										
										
											2024-12-23 12:06:32 -05:00
										 |  |  |  | 			return html`
 | 
					
						
							| 
									
										
										
										
											2024-12-24 14:22:24 -05:00
										 |  |  |  | 				<input type="checkbox" class="w3-check w3-theme-d1" id="new_thread" @change=${() => (self.new_thread = !self.new_thread)} ?checked=${self.new_thread}></input> | 
					
						
							| 
									
										
										
										
											2024-12-23 12:06:32 -05:00
										 |  |  |  | 				<label for="new_thread">New Thread</label> | 
					
						
							|  |  |  |  | 			`;
 | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 	get_draft() { | 
					
						
							| 
									
										
										
										
											2025-08-13 19:53:28 -04:00
										 |  |  |  | 		let key = | 
					
						
							|  |  |  |  | 			this.branch || | 
					
						
							|  |  |  |  | 			(this.recipients ? this.recipients.join(',') : undefined) || | 
					
						
							|  |  |  |  | 			''; | 
					
						
							|  |  |  |  | 		let draft = this.drafts[key] || {}; | 
					
						
							|  |  |  |  | 		if (this.recipients && !draft.encrypt_to?.length) { | 
					
						
							|  |  |  |  | 			draft.encrypt_to = [ | 
					
						
							|  |  |  |  | 				...new Set(this.recipients).union(new Set(draft.encrypt_to ?? [])), | 
					
						
							|  |  |  |  | 			]; | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return draft; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 	update_encrypt(event) { | 
					
						
							|  |  |  |  | 		let input = event.srcElement; | 
					
						
							|  |  |  |  | 		let matches = input.value.match(/@.*?\.ed25519/g); | 
					
						
							|  |  |  |  | 		if (matches) { | 
					
						
							|  |  |  |  | 			let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 			let to = [...new Set(matches.concat(draft.encrypt_to))]; | 
					
						
							|  |  |  |  | 			this.set_encrypt(to); | 
					
						
							|  |  |  |  | 			input.value = ''; | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	render_encrypt() { | 
					
						
							|  |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		if (draft.encrypt_to === undefined) { | 
					
						
							|  |  |  |  | 			return; | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return html`
 | 
					
						
							|  |  |  |  | 			<div style="display: flex; flex-direction: row; width: 100%"> | 
					
						
							|  |  |  |  | 				<label for="encrypt_to">🔐 To:</label> | 
					
						
							| 
									
										
										
										
											2025-05-18 07:55:10 -04:00
										 |  |  |  | 				<input type="text" id="encrypt_to" class="w3-input w3-theme-d1 w3-border" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input> | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 				<button class="w3-button w3-theme-d1" @click=${() => this.set_encrypt(undefined)}>🚮</button> | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 			</div> | 
					
						
							|  |  |  |  | 			<ul> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				${draft.encrypt_to.map( | 
					
						
							|  |  |  |  | 					(x) => html`
 | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 					<li> | 
					
						
							|  |  |  |  | 						<tf-user id=${x} .users=${this.users}></tf-user> | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 						<input type="button" class="w3-button w3-theme-d1" value="🚮" @click=${() => this.set_encrypt(draft.encrypt_to.filter((id) => id != x))}></input> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					</li>` | 
					
						
							|  |  |  |  | 				)} | 
					
						
							| 
									
										
										
										
											2023-09-01 01:34:01 +00:00
										 |  |  |  | 			</ul> | 
					
						
							|  |  |  |  | 		`;
 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	set_encrypt(encrypt) { | 
					
						
							|  |  |  |  | 		let draft = this.get_draft(); | 
					
						
							|  |  |  |  | 		draft.encrypt_to = encrypt; | 
					
						
							|  |  |  |  | 		this.notify(draft); | 
					
						
							|  |  |  |  | 		this.requestUpdate(); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-02 21:47:35 -04:00
										 |  |  |  | 	toggle_menu(event) { | 
					
						
							|  |  |  |  | 		event.srcElement.parentNode | 
					
						
							|  |  |  |  | 			.querySelector('.w3-dropdown-content') | 
					
						
							|  |  |  |  | 			.classList.toggle('w3-show'); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	connectedCallback() { | 
					
						
							|  |  |  |  | 		super.connectedCallback(); | 
					
						
							|  |  |  |  | 		this._click_callback = this.document_click.bind(this); | 
					
						
							|  |  |  |  | 		document.body.addEventListener('mouseup', this._click_callback); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	disconnectedCallback() { | 
					
						
							|  |  |  |  | 		super.disconnectedCallback(); | 
					
						
							|  |  |  |  | 		document.body.removeEventListener('mouseup', this._click_callback); | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	document_click(event) { | 
					
						
							|  |  |  |  | 		let content = this.renderRoot.querySelector('.w3-dropdown-content'); | 
					
						
							|  |  |  |  | 		let target = event.target; | 
					
						
							|  |  |  |  | 		if (content && !content.contains(target)) { | 
					
						
							|  |  |  |  | 			content.classList.remove('w3-show'); | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	render() { | 
					
						
							| 
									
										
										
										
											2022-10-16 19:05:22 +00:00
										 |  |  |  | 		let self = this; | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 		let draft = self.get_draft(); | 
					
						
							|  |  |  |  | 		let content_warning = | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			draft.content_warning !== undefined | 
					
						
							| 
									
										
										
										
											2024-04-04 20:35:09 -04:00
										 |  |  |  | 				? html`<div class="w3-panel w3-round-xlarge w3-theme-d2">
 | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 						<p id="content_warning_preview">${draft.content_warning}</p> | 
					
						
							|  |  |  |  | 					</div>` | 
					
						
							|  |  |  |  | 				: undefined; | 
					
						
							|  |  |  |  | 		let encrypt = | 
					
						
							|  |  |  |  | 			draft.encrypt_to !== undefined | 
					
						
							|  |  |  |  | 				? undefined | 
					
						
							|  |  |  |  | 				: html`<button
 | 
					
						
							| 
									
										
										
										
											2025-06-02 21:47:35 -04:00
										 |  |  |  | 						class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 						@click=${() => this.set_encrypt([])} | 
					
						
							|  |  |  |  | 					> | 
					
						
							| 
									
										
										
										
											2025-07-02 18:30:35 -04:00
										 |  |  |  | 						🔐 Encrypt | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 					</button>`; | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 		let result = html`
 | 
					
						
							| 
									
										
										
										
											2025-10-22 19:39:20 -04:00
										 |  |  |  | 			<style> | 
					
						
							|  |  |  |  | 				${generate_theme()} | 
					
						
							|  |  |  |  | 			</style> | 
					
						
							| 
									
										
										
										
											2024-12-29 15:11:14 -05:00
										 |  |  |  | 			<style> | 
					
						
							|  |  |  |  | 				.w3-input:empty::before { | 
					
						
							|  |  |  |  | 					content: attr(placeholder); | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				.w3-input:empty:focus::before { | 
					
						
							|  |  |  |  | 					content: ''; | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 			</style> | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 			<div | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 				class="w3-card-4 w3-theme-d4 w3-padding w3-margin-top w3-margin-bottom" | 
					
						
							| 
									
										
										
										
											2024-02-24 11:09:34 -05:00
										 |  |  |  | 				style="box-sizing: border-box" | 
					
						
							|  |  |  |  | 			> | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 				<header class="w3-container"> | 
					
						
							|  |  |  |  | 					${this.channel !== undefined | 
					
						
							|  |  |  |  | 						? html`<p>To #${this.channel}:</p>` | 
					
						
							|  |  |  |  | 						: undefined} | 
					
						
							|  |  |  |  | 					${this.render_encrypt()} | 
					
						
							|  |  |  |  | 				</header> | 
					
						
							| 
									
										
										
										
											2025-07-02 18:29:02 -04:00
										 |  |  |  | 				<div class="w3-container" style="padding: 0 0 16px 0"> | 
					
						
							| 
									
										
										
										
											2024-04-21 18:24:57 -04:00
										 |  |  |  | 					<div class="w3-half"> | 
					
						
							| 
									
										
										
										
											2024-04-24 19:23:13 -04:00
										 |  |  |  | 						<span | 
					
						
							|  |  |  |  | 							class="w3-input w3-theme-d1 w3-border" | 
					
						
							| 
									
										
										
										
											2025-09-09 19:27:32 -04:00
										 |  |  |  | 							style="resize: vertical; width: 100%; white-space: pre-wrap" | 
					
						
							| 
									
										
										
										
											2024-04-24 19:23:13 -04:00
										 |  |  |  | 							placeholder="Write a post here." | 
					
						
							|  |  |  |  | 							id="edit" | 
					
						
							|  |  |  |  | 							@input=${this.input} | 
					
						
							|  |  |  |  | 							@paste=${this.paste} | 
					
						
							| 
									
										
										
										
											2024-07-22 14:42:37 -04:00
										 |  |  |  | 							contenteditable="plaintext-only" | 
					
						
							| 
									
										
										
										
											2024-04-24 20:32:09 -04:00
										 |  |  |  | 							.innerText=${live(draft.text ?? '')} | 
					
						
							| 
									
										
										
										
											2024-05-12 07:48:34 -04:00
										 |  |  |  | 						></span> | 
					
						
							| 
									
										
										
										
											2024-01-12 04:23:31 +00:00
										 |  |  |  | 					</div> | 
					
						
							| 
									
										
										
										
											2025-01-14 20:51:56 -05:00
										 |  |  |  | 					<div class="w3-half w3-container"> | 
					
						
							| 
									
										
										
										
											2024-01-12 04:23:31 +00:00
										 |  |  |  | 						${content_warning} | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 						<p id="preview"></p> | 
					
						
							| 
									
										
										
										
											2024-01-12 04:23:31 +00:00
										 |  |  |  | 					</div> | 
					
						
							| 
									
										
										
										
											2023-01-30 01:45:23 +00:00
										 |  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2024-04-11 18:36:31 -04:00
										 |  |  |  | 				${Object.values(draft.mentions || {}).map((x) => | 
					
						
							|  |  |  |  | 					self.render_mention(x) | 
					
						
							|  |  |  |  | 				)} | 
					
						
							| 
									
										
										
										
											2025-07-02 18:29:02 -04:00
										 |  |  |  | 				<footer> | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 					${this.render_attach_app()} ${this.render_content_warning()} | 
					
						
							|  |  |  |  | 					${this.render_new_thread()} | 
					
						
							| 
									
										
										
										
											2025-01-04 12:41:04 -05:00
										 |  |  |  | 					<button | 
					
						
							|  |  |  |  | 						class="w3-button w3-theme-d1" | 
					
						
							|  |  |  |  | 						id="submit" | 
					
						
							|  |  |  |  | 						@click=${this.submit} | 
					
						
							|  |  |  |  | 					> | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 						Submit | 
					
						
							|  |  |  |  | 					</button> | 
					
						
							| 
									
										
										
										
											2025-06-02 21:47:35 -04:00
										 |  |  |  | 					<div class="w3-dropdown-click"> | 
					
						
							|  |  |  |  | 						<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}> | 
					
						
							|  |  |  |  | 							⚙️ | 
					
						
							|  |  |  |  | 						</button> | 
					
						
							|  |  |  |  | 						<div class="w3-dropdown-content w3-bar-block"> | 
					
						
							|  |  |  |  | 							${this.get_draft().content_warning === undefined | 
					
						
							|  |  |  |  | 								? html`
 | 
					
						
							|  |  |  |  | 										<button | 
					
						
							|  |  |  |  | 											class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							|  |  |  |  | 											@click=${() => self.set_content_warning('')} | 
					
						
							|  |  |  |  | 										> | 
					
						
							|  |  |  |  | 											Add Content Warning | 
					
						
							|  |  |  |  | 										</button> | 
					
						
							|  |  |  |  | 									`
 | 
					
						
							|  |  |  |  | 								: html`
 | 
					
						
							|  |  |  |  | 										<button | 
					
						
							|  |  |  |  | 											class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							|  |  |  |  | 											@click=${() => self.set_content_warning(undefined)} | 
					
						
							|  |  |  |  | 										> | 
					
						
							|  |  |  |  | 											Remove Content Warning | 
					
						
							|  |  |  |  | 										</button> | 
					
						
							|  |  |  |  | 									`}
 | 
					
						
							|  |  |  |  | 							<button | 
					
						
							|  |  |  |  | 								class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							|  |  |  |  | 								@click=${this.attach} | 
					
						
							|  |  |  |  | 							> | 
					
						
							|  |  |  |  | 								Attach | 
					
						
							|  |  |  |  | 							</button> | 
					
						
							|  |  |  |  | 							${this.render_attach_app_button()} ${encrypt} | 
					
						
							|  |  |  |  | 							<button | 
					
						
							|  |  |  |  | 								class="w3-button w3-bar-item w3-theme-d1" | 
					
						
							|  |  |  |  | 								@click=${this.discard} | 
					
						
							|  |  |  |  | 							> | 
					
						
							|  |  |  |  | 								Discard | 
					
						
							|  |  |  |  | 							</button> | 
					
						
							|  |  |  |  | 						</div> | 
					
						
							|  |  |  |  | 					</div> | 
					
						
							| 
									
										
										
										
											2024-12-29 15:42:12 -05:00
										 |  |  |  | 				</footer> | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 			</div> | 
					
						
							|  |  |  |  | 		`;
 | 
					
						
							| 
									
										
										
										
											2022-10-15 18:22:13 +00:00
										 |  |  |  | 		return result; | 
					
						
							| 
									
										
										
										
											2022-09-06 23:26:43 +00:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 00:30:48 +00:00
										 |  |  |  | customElements.define('tf-compose', TfComposeElement); |