~abyxcos/hummingbird

9fceb7c5fc7c9b2d13fde25982863d55881b557f — abyxcos 2 years ago 8bc5b61 master
Add some basic edit replaces previous message functionality. Along the way discover that matrix IDs are terrible CSS selector IDs. Regex our way around that, for now.
2 files changed, 42 insertions(+), 17 deletions(-)

M hummingbird.js
M hummingbird_ajax.js
M hummingbird.js => hummingbird.js +37 -15
@@ 35,18 35,18 @@ var Workspace = Backbone.Router.extend({
		sessionStorage.setItem('room_search', searchValue)

		updateNavButton('chevron-left', 'back', '#')
		if (!document.getElementById(room)) {
			let roomDiv = el('div', { id: room })
		if (!document.getElementById(esc(room))) {
			let roomDiv = el('div', { id: esc(room) })
			roomDiv.addEventListener('new_messages', e => { showRoom(room, e) })
			roomDiv.addEventListener('new_image', e => {
				document
					.getElementById(e.detail.id)
					.getElementById(esc(e.detail.id))
					.querySelector('.m_image')
					.src = e.detail.image
			})
			getApp().appendChild(roomDiv)
		}
		getApp().swapChild(room)
		getApp().swapChild(esc(room))

		getRoomHistory(room, 50)
		showRoom(room)


@@ 101,7 101,7 @@ function makeRoomList() {
		}

		let template = document.querySelector('#templates #roomlist').cloneNode(true)
		template.id = `roomlist_${room.id}`
		template.id = `roomlist_${esc(room.id)}`
		template.href = `#room/${room.id}`
		template.querySelector('.room-avatar').src = room.avatar.img
		// TODO: find a better name for the room if we don't have a name or alias.


@@ 159,7 159,6 @@ function showRoom(room) {

		// Group messages together if they're from the same sender.
		if (prevSender != m.sender) {
			prevSender = m.sender
			avatar = user.avatar.img
			body = `
				<strong>${user.name ?? ''}</strong> <small>${m.sender}</small>


@@ 197,10 196,6 @@ function showRoom(room) {
					body += m.body
				}

				if (m.edit) {
					body += ' (edited)'
				}

				break
			case 'm.image':
				body += `


@@ 215,17 210,37 @@ function showRoom(room) {
				continue
		}

		// If we have an edit, update the old message, don't insert one.
		if (m.edit) {
			// We don't have that message in our history.
			// Discard the edit event for now.
			if (!messageList.querySelector(`#${esc(m.edit)}`)) {
				continue
			}

			// XXX: We're stomping the message header here. It needs to be it's own elem.
			messageList
				.querySelector(`#${esc(m.edit)} #message_body`)
				.innerHTML = body + ' (edited)'

			continue
		}

		let template = document.querySelector('#templates #message').cloneNode(true)

		template.id = m.event_id
		template.classList.add(m.sender)
		template.id = esc(m.id)
		template.classList.add(esc(m.sender))
		template.querySelector('#message_body').innerHTML = body
		if (avatar) { template.querySelector('.user-avatar').src = avatar }

		template.appendTo(messageList)

		// Don't update previous sender until the end.
		// We may have an edit or other out of place event.
		prevSender = m.sender
	}

	document.getElementById(room).replaceChildren(messageList)
	document.getElementById(esc(room)).replaceChildren(messageList)

	// Scroll the viewport to the last message.
	if (messageList.children.length) {


@@ 340,6 355,13 @@ function countMessages(room) {
	return m
}

// Escape a matrix identifier for use in HTML/CSS.
// This is destructive, we can't go backwards.
function esc(s) {
	//return s.replace(/[#$@!:]/g, function(t) { return '\\' + t })
	return s.replace(/[#$@!:.]/g, '_')
}

// Main entry point
// ----------------



@@ 380,13 402,13 @@ document.addEventListener('DOMContentLoaded', e => {
		if (!e.detail.id) {
			// It's a room avatar.
			document
				.getElementById('roomlist_' + e.detail.room)
				.getElementById('roomlist_' + esc(e.detail.room))
				.querySelector('.room-avatar')
				.src = e.detail.avatar
		} else {
			// It's a user avatar for a specific room.
			// TODO: Just do this globally for now, handle room avatars later.
			let messages = getApp().getElementsByClassName(e.detail.id)
			let messages = getApp().getElementsByClassName(esc(e.detail.id))
			for (m of messages) {
				let a = m.querySelector('.user-avatar')
				if (a.src) { a.src = e.detail.avatar }

M hummingbird_ajax.js => hummingbird_ajax.js +5 -2
@@ 230,6 230,9 @@ function parseMessageEvents(events, room) {
			time:		e.origin_server_ts,
			sender:	e.sender,
			type:		e.content.msgtype,
			// For now we shove our room id inside the struct.
			// This makes sorting by last activity easier.
			id:		e.event_id,
		}

		// TODO: We still want to sanitize the HTML and rewrite links.


@@ 439,7 442,7 @@ function sync() {
		account.since = data.next_batch

		// Let any rooms with active listeners know about new events.
		var roomElem = document.getElementById(room)
		var roomElem = document.getElementById(esc(room))
		if (roomElem) { roomElem.dispatchEvent(ev('new_messages', { dir: 'down' })) }
	})
}


@@ 546,7 549,7 @@ function getRoomHistory(room, limit, prev) {
		}

		// Notify that we have new data.
		let roomElem = document.getElementById(room)
		let roomElem = document.getElementById(esc(room))
		if (roomElem) { roomElem.dispatchEvent(ev('new_messages', { dir: 'up' })) }
	})
}