From 4ba28c6ed3b952a06aba8ae96220c429d4d02365 Mon Sep 17 00:00:00 2001
From: Přemysl Eric Janouch <p@janouch.name>
Date: Wed, 7 Sep 2022 19:42:18 +0200
Subject: xC/xP: mark highlights and buffer activity

And more or less finalize out the protocol for this use case.
---
 xP/public/xP.css | 13 ++++++++-
 xP/public/xP.js  | 87 +++++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 83 insertions(+), 17 deletions(-)

(limited to 'xP/public')

diff --git a/xP/public/xP.css b/xP/public/xP.css
index a13afc6..81025c9 100644
--- a/xP/public/xP.css
+++ b/xP/public/xP.css
@@ -58,7 +58,13 @@ body {
 	padding: .05rem .3rem;
 	cursor: default;
 }
-.item.active {
+.item.highlighted {
+	color: #ff5f00;
+}
+.item.activity {
+	font-weight: bold;
+}
+.item.current {
 	font-style: italic;
 	background: #f8f8f8;
 	border-top: 1px solid #eee;
@@ -97,6 +103,11 @@ body {
 	grid-column: span 2;
 	font-weight: bold;
 }
+.unread {
+	height: 1px;
+	grid-column: span 2;
+	background: #ff5f00;
+}
 .time {
 	padding: .1rem .3rem;
 	background: #f8f8f8;
diff --git a/xP/public/xP.js b/xP/public/xP.js
index a941752..42a7a55 100644
--- a/xP/public/xP.js
+++ b/xP/public/xP.js
@@ -156,10 +156,13 @@ rpc.addEventListener('close', event => {
 rpc.addEventListener('BufferUpdate', event => {
 	let e = event.detail, b = buffers.get(e.bufferName)
 	if (b === undefined) {
-		b = {lines: []}
-		buffers.set(e.bufferName, b)
+		buffers.set(e.bufferName, {
+			lines: [],
+			newMessages: e.newMessages,
+			newUnimportantMessages: e.newUnimportantMessages,
+			highlighted: e.highlighted,
+		})
 	}
-	// TODO: Update any buffer properties.
 })
 
 rpc.addEventListener('BufferRename', event => {
@@ -174,8 +177,14 @@ rpc.addEventListener('BufferRemove', event => {
 })
 
 rpc.addEventListener('BufferActivate', event => {
-	let e = event.detail, b = buffers.get(e.bufferName)
 	let old = buffers.get(bufferCurrent)
+	if (old !== undefined) {
+		old.newMessages = 0
+		old.newUnimportantMessages = 0
+		old.highlighted = false
+	}
+
+	let e = event.detail, b = buffers.get(e.bufferName)
 	bufferCurrent = e.bufferName
 	bufferLog = undefined
 	bufferAutoscroll = true
@@ -195,12 +204,41 @@ rpc.addEventListener('BufferActivate', event => {
 })
 
 rpc.addEventListener('BufferLine', event => {
-	let e = event.detail, b = buffers.get(e.bufferName),
-		line = {when: e.when, rendition: e.rendition, items: e.items}
-	if (b !== undefined)
-		b.lines.push({...line})
-	if (e.leakToActive && (b = buffers.get(bufferCurrent)) !== undefined)
-		b.lines.push({leaked: true, ...line})
+	let e = event.detail, b = buffers.get(e.bufferName), line = {...e}
+	delete line.event
+	delete line.leakToActive
+	if (b === undefined)
+		return
+
+	// Initial sync: skip all other processing, let highlights be.
+	if (bufferCurrent === undefined) {
+		b.lines.push(line)
+		return
+	}
+
+	let visible = e.bufferName == bufferCurrent || e.leakToActive
+	b.lines.push({...line})
+	if (!visible || b.newMessages || b.newUnimportantMessages) {
+		if (line.isUnimportant)
+			b.newUnimportantMessages++
+		else
+			b.newMessages++
+	}
+	if (e.leakToActive) {
+		let bc = buffers.get(bufferCurrent)
+		bc.lines.push({...line, leaked: true})
+		if (bc.newMessages || bc.newUnimportantMessages) {
+			if (line.isUnimportant)
+				bc.newUnimportantMessages++
+			else
+				bc.newMessages++
+		}
+	}
+
+	// TODO: Find some way of highlighting the tab in a browser.
+	// TODO: Also highlight on unseen private messages, like xC does.
+	if (!visible && line.isHighlight)
+		b.highlighted = true
 })
 
 rpc.addEventListener('BufferClear', event => {
@@ -267,10 +305,21 @@ let BufferList = {
 
 	view: vnode => {
 		let items = Array.from(buffers, ([name, b]) => {
-			let attrs = {onclick: event => BufferList.activate(name)}
-			if (name == bufferCurrent)
-				attrs.class = 'active'
-			return m('.item', attrs, name)
+			let classes = [], displayName = name
+			if (name == bufferCurrent) {
+				classes.push('current')
+			} else {
+				if (b.highlighted)
+					classes.push('highlighted')
+				if (b.newMessages) {
+					classes.push('activity')
+					displayName += ` (${b.newMessages})`
+				}
+			}
+			return m('.item', {
+				onclick: event => BufferList.activate(name),
+				class: classes.join(' '),
+			}, displayName)
 		})
 		return m('.list', {}, items)
 	},
@@ -387,7 +436,9 @@ let Buffer = {
 			return
 
 		let lastDateMark = undefined
-		b.lines.forEach(line => {
+		let markBefore = b.lines.length
+			- b.newMessages - b.newUnimportantMessages
+		b.lines.forEach((line, i) => {
 			let date = new Date(line.when)
 			let dateMark = date.toLocaleDateString()
 			if (dateMark !== lastDateMark) {
@@ -395,16 +446,20 @@ let Buffer = {
 				lastDateMark = dateMark
 			}
 
+			if (i == markBefore)
+				lines.push(m('.unread'))
+
 			let attrs = {}
 			if (line.leaked)
 				attrs.class = 'leaked'
 
+			// TODO: Make use of isUnimportant.
 			lines.push(m('.time', {...attrs}, date.toLocaleTimeString()))
 			lines.push(m(Content, {...attrs}, line))
 		})
 
 		let dateMark = new Date().toLocaleDateString()
-		if (dateMark !== lastDateMark)
+		if (dateMark !== lastDateMark && lastDateMark !== undefined)
 			lines.push(m('.date', {}, dateMark))
 		return m('.buffer', {}, lines)
 	},
-- 
cgit v1.2.3-70-g09d2