xC/xP: relay and render channel topics
This commit is contained in:
		
							
								
								
									
										73
									
								
								xC-proto
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								xC-proto
									
									
									
									
									
								
							@@ -53,12 +53,12 @@ struct EventMessage {
 | 
				
			|||||||
	u32 event_seq;
 | 
						u32 event_seq;
 | 
				
			||||||
	union EventData switch (enum Event {
 | 
						union EventData switch (enum Event {
 | 
				
			||||||
		PING,
 | 
							PING,
 | 
				
			||||||
 | 
							BUFFER_LINE,
 | 
				
			||||||
		BUFFER_UPDATE,
 | 
							BUFFER_UPDATE,
 | 
				
			||||||
		BUFFER_STATS,
 | 
							BUFFER_STATS,
 | 
				
			||||||
		BUFFER_RENAME,
 | 
							BUFFER_RENAME,
 | 
				
			||||||
		BUFFER_REMOVE,
 | 
							BUFFER_REMOVE,
 | 
				
			||||||
		BUFFER_ACTIVATE,
 | 
							BUFFER_ACTIVATE,
 | 
				
			||||||
		BUFFER_LINE,
 | 
					 | 
				
			||||||
		BUFFER_CLEAR,
 | 
							BUFFER_CLEAR,
 | 
				
			||||||
		SERVER_UPDATE,
 | 
							SERVER_UPDATE,
 | 
				
			||||||
		SERVER_RENAME,
 | 
							SERVER_RENAME,
 | 
				
			||||||
@@ -69,41 +69,6 @@ struct EventMessage {
 | 
				
			|||||||
	case PING:
 | 
						case PING:
 | 
				
			||||||
		void;
 | 
							void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case BUFFER_UPDATE:
 | 
					 | 
				
			||||||
		string buffer_name;
 | 
					 | 
				
			||||||
		bool hide_unimportant;
 | 
					 | 
				
			||||||
		union BufferContext switch (enum BufferKind {
 | 
					 | 
				
			||||||
			GLOBAL,
 | 
					 | 
				
			||||||
			SERVER,
 | 
					 | 
				
			||||||
			CHANNEL,
 | 
					 | 
				
			||||||
			PRIVATE_MESSAGE,
 | 
					 | 
				
			||||||
		} kind) {
 | 
					 | 
				
			||||||
		case GLOBAL:
 | 
					 | 
				
			||||||
			void;
 | 
					 | 
				
			||||||
		case SERVER:
 | 
					 | 
				
			||||||
			string server_name;
 | 
					 | 
				
			||||||
		case CHANNEL:
 | 
					 | 
				
			||||||
			string server_name;
 | 
					 | 
				
			||||||
		case PRIVATE_MESSAGE:
 | 
					 | 
				
			||||||
			string server_name;
 | 
					 | 
				
			||||||
		} context;
 | 
					 | 
				
			||||||
	case BUFFER_STATS:
 | 
					 | 
				
			||||||
		string buffer_name;
 | 
					 | 
				
			||||||
		// These are cumulative, even for lines flushed out from buffers.
 | 
					 | 
				
			||||||
		// Updates to these values aren't broadcasted, thus handle:
 | 
					 | 
				
			||||||
		//  - BUFFER_LINE by bumping/setting them as appropriate,
 | 
					 | 
				
			||||||
		//  - BUFFER_ACTIVATE by clearing them for the previous buffer
 | 
					 | 
				
			||||||
		//    (this way, they can be used to mark unread messages).
 | 
					 | 
				
			||||||
		u32 new_messages;
 | 
					 | 
				
			||||||
		u32 new_unimportant_messages;
 | 
					 | 
				
			||||||
		bool highlighted;
 | 
					 | 
				
			||||||
	case BUFFER_RENAME:
 | 
					 | 
				
			||||||
		string buffer_name;
 | 
					 | 
				
			||||||
		string new;
 | 
					 | 
				
			||||||
	case BUFFER_REMOVE:
 | 
					 | 
				
			||||||
		string buffer_name;
 | 
					 | 
				
			||||||
	case BUFFER_ACTIVATE:
 | 
					 | 
				
			||||||
		string buffer_name;
 | 
					 | 
				
			||||||
	case BUFFER_LINE:
 | 
						case BUFFER_LINE:
 | 
				
			||||||
		string buffer_name;
 | 
							string buffer_name;
 | 
				
			||||||
		// Whether the line should also be displayed in the active buffer.
 | 
							// Whether the line should also be displayed in the active buffer.
 | 
				
			||||||
@@ -150,6 +115,42 @@ struct EventMessage {
 | 
				
			|||||||
		case FLIP_MONOSPACE:
 | 
							case FLIP_MONOSPACE:
 | 
				
			||||||
			void;
 | 
								void;
 | 
				
			||||||
		} items<>;
 | 
							} items<>;
 | 
				
			||||||
 | 
						case BUFFER_UPDATE:
 | 
				
			||||||
 | 
							string buffer_name;
 | 
				
			||||||
 | 
							bool hide_unimportant;
 | 
				
			||||||
 | 
							union BufferContext switch (enum BufferKind {
 | 
				
			||||||
 | 
								GLOBAL,
 | 
				
			||||||
 | 
								SERVER,
 | 
				
			||||||
 | 
								CHANNEL,
 | 
				
			||||||
 | 
								PRIVATE_MESSAGE,
 | 
				
			||||||
 | 
							} kind) {
 | 
				
			||||||
 | 
							case GLOBAL:
 | 
				
			||||||
 | 
								void;
 | 
				
			||||||
 | 
							case SERVER:
 | 
				
			||||||
 | 
								string server_name;
 | 
				
			||||||
 | 
							case CHANNEL:
 | 
				
			||||||
 | 
								string server_name;
 | 
				
			||||||
 | 
								ItemData topic<>;
 | 
				
			||||||
 | 
							case PRIVATE_MESSAGE:
 | 
				
			||||||
 | 
								string server_name;
 | 
				
			||||||
 | 
							} context;
 | 
				
			||||||
 | 
						case BUFFER_STATS:
 | 
				
			||||||
 | 
							string buffer_name;
 | 
				
			||||||
 | 
							// These are cumulative, even for lines flushed out from buffers.
 | 
				
			||||||
 | 
							// Updates to these values aren't broadcasted, thus handle:
 | 
				
			||||||
 | 
							//  - BUFFER_LINE by bumping/setting them as appropriate,
 | 
				
			||||||
 | 
							//  - BUFFER_ACTIVATE by clearing them for the previous buffer
 | 
				
			||||||
 | 
							//    (this way, they can be used to mark unread messages).
 | 
				
			||||||
 | 
							u32 new_messages;
 | 
				
			||||||
 | 
							u32 new_unimportant_messages;
 | 
				
			||||||
 | 
							bool highlighted;
 | 
				
			||||||
 | 
						case BUFFER_RENAME:
 | 
				
			||||||
 | 
							string buffer_name;
 | 
				
			||||||
 | 
							string new;
 | 
				
			||||||
 | 
						case BUFFER_REMOVE:
 | 
				
			||||||
 | 
							string buffer_name;
 | 
				
			||||||
 | 
						case BUFFER_ACTIVATE:
 | 
				
			||||||
 | 
							string buffer_name;
 | 
				
			||||||
	case BUFFER_CLEAR:
 | 
						case BUFFER_CLEAR:
 | 
				
			||||||
		string buffer_name;
 | 
							string buffer_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										205
									
								
								xC.c
									
									
									
									
									
								
							
							
						
						
									
										205
									
								
								xC.c
									
									
									
									
									
								
							@@ -2883,84 +2883,9 @@ relay_prepare_ping (struct app_context *ctx)
 | 
				
			|||||||
	relay_prepare (ctx)->data.event = RELAY_EVENT_PING;
 | 
						relay_prepare (ctx)->data.event = RELAY_EVENT_PING;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct relay_event_message *m = relay_prepare (ctx);
 | 
					 | 
				
			||||||
	struct relay_event_data_buffer_update *e = &m->data.buffer_update;
 | 
					 | 
				
			||||||
	e->event = RELAY_EVENT_BUFFER_UPDATE;
 | 
					 | 
				
			||||||
	e->buffer_name = str_from_cstr (buffer->name);
 | 
					 | 
				
			||||||
	e->hide_unimportant = buffer->hide_unimportant;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct str *server_name = NULL;
 | 
					 | 
				
			||||||
	switch (buffer->type)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
	case BUFFER_GLOBAL:
 | 
					 | 
				
			||||||
		e->context.kind = RELAY_BUFFER_KIND_GLOBAL;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case BUFFER_SERVER:
 | 
					 | 
				
			||||||
		e->context.kind = RELAY_BUFFER_KIND_SERVER;
 | 
					 | 
				
			||||||
		server_name = &e->context.server.server_name;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case BUFFER_CHANNEL:
 | 
					 | 
				
			||||||
		e->context.kind = RELAY_BUFFER_KIND_CHANNEL;
 | 
					 | 
				
			||||||
		server_name = &e->context.channel.server_name;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case BUFFER_PM:
 | 
					 | 
				
			||||||
		e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE;
 | 
					 | 
				
			||||||
		server_name = &e->context.private_message.server_name;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (server_name)
 | 
					 | 
				
			||||||
		*server_name = str_from_cstr (buffer->server->name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
relay_prepare_buffer_stats (struct app_context *ctx, struct buffer *buffer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct relay_event_message *m = relay_prepare (ctx);
 | 
					 | 
				
			||||||
	struct relay_event_data_buffer_stats *e = &m->data.buffer_stats;
 | 
					 | 
				
			||||||
	e->event = RELAY_EVENT_BUFFER_STATS;
 | 
					 | 
				
			||||||
	e->buffer_name = str_from_cstr (buffer->name);
 | 
					 | 
				
			||||||
	e->new_messages = MIN (UINT32_MAX,
 | 
					 | 
				
			||||||
		buffer->new_messages_count - buffer->new_unimportant_count);
 | 
					 | 
				
			||||||
	e->new_unimportant_messages = MIN (UINT32_MAX,
 | 
					 | 
				
			||||||
		buffer->new_unimportant_count);
 | 
					 | 
				
			||||||
	e->highlighted = buffer->highlighted;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
relay_prepare_buffer_rename (struct app_context *ctx, struct buffer *buffer,
 | 
					 | 
				
			||||||
	const char *new_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct relay_event_message *m = relay_prepare (ctx);
 | 
					 | 
				
			||||||
	struct relay_event_data_buffer_rename *e = &m->data.buffer_rename;
 | 
					 | 
				
			||||||
	e->event = RELAY_EVENT_BUFFER_RENAME;
 | 
					 | 
				
			||||||
	e->buffer_name = str_from_cstr (buffer->name);
 | 
					 | 
				
			||||||
	e->new = str_from_cstr (new_name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
relay_prepare_buffer_remove (struct app_context *ctx, struct buffer *buffer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct relay_event_message *m = relay_prepare (ctx);
 | 
					 | 
				
			||||||
	struct relay_event_data_buffer_remove *e = &m->data.buffer_remove;
 | 
					 | 
				
			||||||
	e->event = RELAY_EVENT_BUFFER_REMOVE;
 | 
					 | 
				
			||||||
	e->buffer_name = str_from_cstr (buffer->name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
relay_prepare_buffer_activate (struct app_context *ctx, struct buffer *buffer)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct relay_event_message *m = relay_prepare (ctx);
 | 
					 | 
				
			||||||
	struct relay_event_data_buffer_activate *e = &m->data.buffer_activate;
 | 
					 | 
				
			||||||
	e->event = RELAY_EVENT_BUFFER_ACTIVATE;
 | 
					 | 
				
			||||||
	e->buffer_name = str_from_cstr (buffer->name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static union relay_item_data *
 | 
					static union relay_item_data *
 | 
				
			||||||
relay_translate_formatter (struct app_context *ctx, union relay_item_data *p,
 | 
					relay_translate_formatter (struct app_context *ctx, union relay_item_data *p,
 | 
				
			||||||
	struct formatter_item *i)
 | 
						const struct formatter_item *i)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// XXX: See attr_printer_decode_color(), this is a footgun.
 | 
						// XXX: See attr_printer_decode_color(), this is a footgun.
 | 
				
			||||||
	int16_t c16  = i->color;
 | 
						int16_t c16  = i->color;
 | 
				
			||||||
@@ -3016,6 +2941,23 @@ relay_translate_formatter (struct app_context *ctx, union relay_item_data *p,
 | 
				
			|||||||
	return p;
 | 
						return p;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static union relay_item_data *
 | 
				
			||||||
 | 
					relay_items (struct app_context *ctx, const struct formatter_item *items,
 | 
				
			||||||
 | 
						uint32_t *len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t items_len = 0;
 | 
				
			||||||
 | 
						for (size_t i = 0; items[i].type; i++)
 | 
				
			||||||
 | 
							items_len++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Beware of the upper bound, currently dominated by FORMATTER_ITEM_ATTR.
 | 
				
			||||||
 | 
						union relay_item_data *a = xcalloc (items_len * 9, sizeof *a), *p = a;
 | 
				
			||||||
 | 
						for (const struct formatter_item *i = items; items_len--; i++)
 | 
				
			||||||
 | 
							p = relay_translate_formatter (ctx, p, i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*len = p - a;
 | 
				
			||||||
 | 
						return a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
 | 
					relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
 | 
				
			||||||
	struct buffer_line *line, bool leak_to_active)
 | 
						struct buffer_line *line, bool leak_to_active)
 | 
				
			||||||
@@ -3029,17 +2971,94 @@ relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
 | 
				
			|||||||
	e->rendition = 1 + line->r;
 | 
						e->rendition = 1 + line->r;
 | 
				
			||||||
	e->when = line->when * 1000;
 | 
						e->when = line->when * 1000;
 | 
				
			||||||
	e->leak_to_active = leak_to_active;
 | 
						e->leak_to_active = leak_to_active;
 | 
				
			||||||
 | 
						e->items = relay_items (ctx, line->items, &e->items_len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t len = 0;
 | 
					// TODO: Consider pushing this whole block of code much further down.
 | 
				
			||||||
	for (size_t i = 0; line->items[i].type; i++)
 | 
					static void formatter_add (struct formatter *self, const char *format, ...);
 | 
				
			||||||
		len++;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Beware of the upper bound, currently dominated by FORMATTER_ITEM_ATTR.
 | 
					static void
 | 
				
			||||||
	union relay_item_data *p = e->items = xcalloc (len * 9, sizeof *e->items);
 | 
					relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer)
 | 
				
			||||||
	for (struct formatter_item *i = line->items; len--; i++)
 | 
					{
 | 
				
			||||||
		p = relay_translate_formatter (ctx, p, i);
 | 
						struct relay_event_message *m = relay_prepare (ctx);
 | 
				
			||||||
 | 
						struct relay_event_data_buffer_update *e = &m->data.buffer_update;
 | 
				
			||||||
 | 
						e->event = RELAY_EVENT_BUFFER_UPDATE;
 | 
				
			||||||
 | 
						e->buffer_name = str_from_cstr (buffer->name);
 | 
				
			||||||
 | 
						e->hide_unimportant = buffer->hide_unimportant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	e->items_len = p - e->items;
 | 
						struct str *server_name = NULL;
 | 
				
			||||||
 | 
						switch (buffer->type)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case BUFFER_GLOBAL:
 | 
				
			||||||
 | 
							e->context.kind = RELAY_BUFFER_KIND_GLOBAL;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BUFFER_SERVER:
 | 
				
			||||||
 | 
							e->context.kind = RELAY_BUFFER_KIND_SERVER;
 | 
				
			||||||
 | 
							server_name = &e->context.server.server_name;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BUFFER_CHANNEL:
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							e->context.kind = RELAY_BUFFER_KIND_CHANNEL;
 | 
				
			||||||
 | 
							server_name = &e->context.channel.server_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							struct formatter f = formatter_make (ctx, buffer->server);
 | 
				
			||||||
 | 
							if (buffer->channel->topic)
 | 
				
			||||||
 | 
								formatter_add (&f, "#m", buffer->channel->topic);
 | 
				
			||||||
 | 
							e->context.channel.topic =
 | 
				
			||||||
 | 
								relay_items (ctx, f.items, &e->context.channel.topic_len);
 | 
				
			||||||
 | 
							formatter_free (&f);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						case BUFFER_PM:
 | 
				
			||||||
 | 
							e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE;
 | 
				
			||||||
 | 
							server_name = &e->context.private_message.server_name;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (server_name)
 | 
				
			||||||
 | 
							*server_name = str_from_cstr (buffer->server->name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					relay_prepare_buffer_stats (struct app_context *ctx, struct buffer *buffer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct relay_event_message *m = relay_prepare (ctx);
 | 
				
			||||||
 | 
						struct relay_event_data_buffer_stats *e = &m->data.buffer_stats;
 | 
				
			||||||
 | 
						e->event = RELAY_EVENT_BUFFER_STATS;
 | 
				
			||||||
 | 
						e->buffer_name = str_from_cstr (buffer->name);
 | 
				
			||||||
 | 
						e->new_messages = MIN (UINT32_MAX,
 | 
				
			||||||
 | 
							buffer->new_messages_count - buffer->new_unimportant_count);
 | 
				
			||||||
 | 
						e->new_unimportant_messages = MIN (UINT32_MAX,
 | 
				
			||||||
 | 
							buffer->new_unimportant_count);
 | 
				
			||||||
 | 
						e->highlighted = buffer->highlighted;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					relay_prepare_buffer_rename (struct app_context *ctx, struct buffer *buffer,
 | 
				
			||||||
 | 
						const char *new_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct relay_event_message *m = relay_prepare (ctx);
 | 
				
			||||||
 | 
						struct relay_event_data_buffer_rename *e = &m->data.buffer_rename;
 | 
				
			||||||
 | 
						e->event = RELAY_EVENT_BUFFER_RENAME;
 | 
				
			||||||
 | 
						e->buffer_name = str_from_cstr (buffer->name);
 | 
				
			||||||
 | 
						e->new = str_from_cstr (new_name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					relay_prepare_buffer_remove (struct app_context *ctx, struct buffer *buffer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct relay_event_message *m = relay_prepare (ctx);
 | 
				
			||||||
 | 
						struct relay_event_data_buffer_remove *e = &m->data.buffer_remove;
 | 
				
			||||||
 | 
						e->event = RELAY_EVENT_BUFFER_REMOVE;
 | 
				
			||||||
 | 
						e->buffer_name = str_from_cstr (buffer->name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					relay_prepare_buffer_activate (struct app_context *ctx, struct buffer *buffer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct relay_event_message *m = relay_prepare (ctx);
 | 
				
			||||||
 | 
						struct relay_event_data_buffer_activate *e = &m->data.buffer_activate;
 | 
				
			||||||
 | 
						e->event = RELAY_EVENT_BUFFER_ACTIVATE;
 | 
				
			||||||
 | 
						e->buffer_name = str_from_cstr (buffer->name);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
@@ -5291,6 +5310,20 @@ irc_make_channel (struct server *s, char *name)
 | 
				
			|||||||
	return channel;
 | 
						return channel;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					irc_channel_set_topic (struct channel *channel, const char *topic)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cstr_set (&channel->topic, xstrdup (topic));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct server *s = channel->s;
 | 
				
			||||||
 | 
						struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel->name);
 | 
				
			||||||
 | 
						if (buffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							relay_prepare_buffer_update (s->ctx, buffer);
 | 
				
			||||||
 | 
							relay_broadcast (s->ctx);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct channel_user *
 | 
					static struct channel_user *
 | 
				
			||||||
irc_channel_get_user (struct channel *channel, struct user *user)
 | 
					irc_channel_get_user (struct channel *channel, struct user *user)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -8074,7 +8107,7 @@ irc_handle_topic (struct server *s, const struct irc_message *msg)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// It would be weird for this to be false
 | 
						// It would be weird for this to be false
 | 
				
			||||||
	if (channel)
 | 
						if (channel)
 | 
				
			||||||
		cstr_set (&channel->topic, xstrdup (topic));
 | 
							irc_channel_set_topic (channel, topic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer)
 | 
						if (buffer)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -8486,7 +8519,7 @@ irc_handle_rpl_topic (struct server *s, const struct irc_message *msg)
 | 
				
			|||||||
	hard_assert (channel || !buffer);
 | 
						hard_assert (channel || !buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (channel)
 | 
						if (channel)
 | 
				
			||||||
		cstr_set (&channel->topic, xstrdup (topic));
 | 
							irc_channel_set_topic (channel, topic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer)
 | 
						if (buffer)
 | 
				
			||||||
		log_server_status (s, buffer, "The topic is: #m", topic);
 | 
							log_server_status (s, buffer, "The topic is: #m", topic);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,11 +30,16 @@ body {
 | 
				
			|||||||
	display: flex;
 | 
						display: flex;
 | 
				
			||||||
	justify-content: space-between;
 | 
						justify-content: space-between;
 | 
				
			||||||
	align-items: baseline;
 | 
						align-items: baseline;
 | 
				
			||||||
 | 
						column-gap: .3em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	position: relative;
 | 
						position: relative;
 | 
				
			||||||
	border-top: 3px solid #ccc;
 | 
						border-top: 3px solid #ccc;
 | 
				
			||||||
	border-bottom: 2px solid #888;
 | 
						border-bottom: 2px solid #888;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.title {
 | 
				
			||||||
 | 
						/* To approximate right-aligned space-between. */
 | 
				
			||||||
 | 
						flex-direction: row-reverse;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.title:before, .status:before {
 | 
					.title:before, .status:before {
 | 
				
			||||||
	content: " ";
 | 
						content: " ";
 | 
				
			||||||
	position: absolute;
 | 
						position: absolute;
 | 
				
			||||||
@@ -57,7 +62,7 @@ body {
 | 
				
			|||||||
.toolbar {
 | 
					.toolbar {
 | 
				
			||||||
	display: flex;
 | 
						display: flex;
 | 
				
			||||||
	align-items: baseline;
 | 
						align-items: baseline;
 | 
				
			||||||
	gap: .3em;
 | 
						column-gap: .3em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
button {
 | 
					button {
 | 
				
			||||||
	font: inherit;
 | 
						font: inherit;
 | 
				
			||||||
@@ -129,11 +134,13 @@ button:hover:active {
 | 
				
			|||||||
	overflow-y: auto;
 | 
						overflow-y: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.log, .content {
 | 
					.log, .content {
 | 
				
			||||||
	padding: .1em .3em;
 | 
					 | 
				
			||||||
	/* Note: https://bugs.chromium.org/p/chromium/issues/detail?id=1261435 */
 | 
						/* Note: https://bugs.chromium.org/p/chromium/issues/detail?id=1261435 */
 | 
				
			||||||
	white-space: break-spaces;
 | 
						white-space: break-spaces;
 | 
				
			||||||
	overflow-wrap: break-word;
 | 
						overflow-wrap: break-word;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.log, .buffer .content {
 | 
				
			||||||
 | 
						padding: .1em .3em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.leaked {
 | 
					.leaked {
 | 
				
			||||||
	opacity: 50%;
 | 
						opacity: 50%;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										107
									
								
								xP/public/xP.js
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								xP/public/xP.js
									
									
									
									
									
								
							@@ -254,12 +254,58 @@ rpc.addEventListener('event', event => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rpcEventHandlers['Ping'] = e => {
 | 
					rpcEventHandlers.set(Relay.Event.Ping, e => {
 | 
				
			||||||
	rpc.send({command: 'PingResponse', eventSeq: e.eventSeq})
 | 
						rpc.send({command: 'PingResponse', eventSeq: e.eventSeq})
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ~~~ Buffer events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					// ~~~ Buffer events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rpcEventHandlers.set(Relay.Event.BufferLine, e => {
 | 
				
			||||||
 | 
						let b = buffers.get(e.bufferName), line = {...e}
 | 
				
			||||||
 | 
						delete line.event
 | 
				
			||||||
 | 
						delete line.eventSeq
 | 
				
			||||||
 | 
						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 = document.visibilityState !== 'hidden' &&
 | 
				
			||||||
 | 
							bufferLog === undefined &&
 | 
				
			||||||
 | 
							bufferAutoscroll &&
 | 
				
			||||||
 | 
							(e.bufferName == bufferCurrent || e.leakToActive)
 | 
				
			||||||
 | 
						b.lines.push({...line})
 | 
				
			||||||
 | 
						if (!(visible || e.leakToActive) ||
 | 
				
			||||||
 | 
								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 (!visible || bc.newMessages || bc.newUnimportantMessages) {
 | 
				
			||||||
 | 
								if (line.isUnimportant)
 | 
				
			||||||
 | 
									bc.newUnimportantMessages++
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									bc.newMessages++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (line.isHighlight || (!visible && !line.isUnimportant &&
 | 
				
			||||||
 | 
								b.kind === Relay.BufferKind.PrivateMessage)) {
 | 
				
			||||||
 | 
							beep()
 | 
				
			||||||
 | 
							if (!visible)
 | 
				
			||||||
 | 
								b.highlighted = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rpcEventHandlers.set(Relay.Event.BufferUpdate, e => {
 | 
					rpcEventHandlers.set(Relay.Event.BufferUpdate, e => {
 | 
				
			||||||
	let b = buffers.get(e.bufferName)
 | 
						let b = buffers.get(e.bufferName)
 | 
				
			||||||
	if (b === undefined) {
 | 
						if (b === undefined) {
 | 
				
			||||||
@@ -274,6 +320,7 @@ rpcEventHandlers.set(Relay.Event.BufferUpdate, e => {
 | 
				
			|||||||
	b.hideUnimportant = e.hideUnimportant
 | 
						b.hideUnimportant = e.hideUnimportant
 | 
				
			||||||
	b.kind = e.context.kind
 | 
						b.kind = e.context.kind
 | 
				
			||||||
	b.server = servers.get(e.context.serverName)
 | 
						b.server = servers.get(e.context.serverName)
 | 
				
			||||||
 | 
						b.topic = e.context.topic
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rpcEventHandlers.set(Relay.Event.BufferStats, e => {
 | 
					rpcEventHandlers.set(Relay.Event.BufferStats, e => {
 | 
				
			||||||
@@ -332,52 +379,6 @@ rpcEventHandlers.set(Relay.Event.BufferActivate, e => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rpcEventHandlers.set(Relay.Event.BufferLine, e => {
 | 
					 | 
				
			||||||
	let b = buffers.get(e.bufferName), line = {...e}
 | 
					 | 
				
			||||||
	delete line.event
 | 
					 | 
				
			||||||
	delete line.eventSeq
 | 
					 | 
				
			||||||
	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 = document.visibilityState !== 'hidden' &&
 | 
					 | 
				
			||||||
		bufferLog === undefined &&
 | 
					 | 
				
			||||||
		bufferAutoscroll &&
 | 
					 | 
				
			||||||
		(e.bufferName == bufferCurrent || e.leakToActive)
 | 
					 | 
				
			||||||
	b.lines.push({...line})
 | 
					 | 
				
			||||||
	if (!(visible || e.leakToActive) ||
 | 
					 | 
				
			||||||
			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 (!visible || bc.newMessages || bc.newUnimportantMessages) {
 | 
					 | 
				
			||||||
			if (line.isUnimportant)
 | 
					 | 
				
			||||||
				bc.newUnimportantMessages++
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				bc.newMessages++
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (line.isHighlight || (!visible && !line.isUnimportant &&
 | 
					 | 
				
			||||||
			b.kind === Relay.BufferKind.PrivateMessage)) {
 | 
					 | 
				
			||||||
		beep()
 | 
					 | 
				
			||||||
		if (!visible)
 | 
					 | 
				
			||||||
			b.highlighted = true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rpcEventHandlers.set(Relay.Event.BufferClear, e => {
 | 
					rpcEventHandlers.set(Relay.Event.BufferClear, e => {
 | 
				
			||||||
	let b = buffers.get(e.bufferName)
 | 
						let b = buffers.get(e.bufferName)
 | 
				
			||||||
	if (b !== undefined)
 | 
						if (b !== undefined)
 | 
				
			||||||
@@ -548,6 +549,14 @@ let Content = {
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let Topic = {
 | 
				
			||||||
 | 
						view: vnode => {
 | 
				
			||||||
 | 
							let b = buffers.get(bufferCurrent)
 | 
				
			||||||
 | 
							if (b !== undefined && b.topic !== undefined)
 | 
				
			||||||
 | 
								return m(Content, {}, {items: b.topic})
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let Buffer = {
 | 
					let Buffer = {
 | 
				
			||||||
	controller: new AbortController(),
 | 
						controller: new AbortController(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -945,7 +954,7 @@ let Main = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return m('.xP', {}, [
 | 
							return m('.xP', {}, [
 | 
				
			||||||
			overlay,
 | 
								overlay,
 | 
				
			||||||
			m('.title', {}, `xP`),
 | 
								m('.title', {}, [m('b', {}, `xP`), m(Topic)]),
 | 
				
			||||||
			m('.middle', {}, [m(BufferList), m(BufferContainer)]),
 | 
								m('.middle', {}, [m(BufferList), m(BufferContainer)]),
 | 
				
			||||||
			m(Status),
 | 
								m(Status),
 | 
				
			||||||
			m('.input', {}, [m(Prompt), m(Input)]),
 | 
								m('.input', {}, [m(Prompt), m(Input)]),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user