diff --git a/xC-proto b/xC-proto index 25d48e8..3057404 100644 --- a/xC-proto +++ b/xC-proto @@ -131,6 +131,8 @@ struct EventMessage { case CHANNEL: string server_name; ItemData topic<>; + // This includes parameters, separated by spaces. + string modes; case PRIVATE_MESSAGE: string server_name; } context; @@ -169,7 +171,7 @@ struct EventMessage { void; case REGISTERED: string user; - string user_mode; + string user_modes; // Theoretically, we could also send user information in this state, // but we'd have to duplicate both fields. case DISCONNECTING: diff --git a/xC.c b/xC.c index 38f2c00..77d2a08 100644 --- a/xC.c +++ b/xC.c @@ -1841,7 +1841,7 @@ struct server struct user *irc_user; ///< Our own user int nick_counter; ///< Iterates "nicks" when registering - struct str irc_user_mode; ///< Our current user modes + struct str irc_user_modes; ///< Our current user modes char *irc_user_host; ///< Our current user@host bool autoaway_active; ///< Autoaway is currently active @@ -1889,7 +1889,7 @@ static struct ispect_field g_server_ispect[] = // TODO: either rename the underlying field or fix the plugins { "user", offsetof (struct server, irc_user), ISPECT_REF, 0, g_user_ispect, false }, - { "user_mode", offsetof (struct server, irc_user_mode), + { "user_mode", offsetof (struct server, irc_user_modes), ISPECT_STR, 0, NULL, false }, {} @@ -1978,7 +1978,7 @@ server_new (struct poller *poller) self->irc_buffer_map = str_map_make (NULL); self->irc_buffer_map.key_xfrm = irc_strxfrm; - self->irc_user_mode = str_make (); + self->irc_user_modes = str_make (); self->outstanding_joins = strv_make (); self->cap_ls_buf = strv_make (); @@ -2025,7 +2025,7 @@ server_destroy (struct server *self) if (self->irc_user) user_unref (self->irc_user); - str_free (&self->irc_user_mode); + str_free (&self->irc_user_modes); free (self->irc_user_host); strv_free (&self->outstanding_joins); @@ -2976,6 +2976,45 @@ relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer, // TODO: Consider pushing this whole block of code much further down. static void formatter_add (struct formatter *self, const char *format, ...); +static char *irc_to_utf8 (const char *text); + +static void +relay_prepare_channel_buffer_update (struct app_context *ctx, + struct buffer *buffer, struct relay_buffer_context_channel *e) +{ + struct channel *channel = buffer->channel; + struct formatter f = formatter_make (ctx, buffer->server); + if (channel->topic) + formatter_add (&f, "#m", channel->topic); + e->topic = relay_items (ctx, f.items, &e->topic_len); + formatter_free (&f); + + // As in make_prompt(), conceal the last known channel modes. + // XXX: This should use irc_channel_is_joined(). + if (!channel->users_len) + return; + + struct str modes = str_make (); + str_append_str (&modes, &channel->no_param_modes); + + struct str params = str_make (); + struct str_map_iter iter = str_map_iter_make (&channel->param_modes); + const char *param; + while ((param = str_map_iter_next (&iter))) + { + str_append_c (&modes, iter.link->key[0]); + str_append_c (¶ms, ' '); + str_append (¶ms, param); + } + + str_append_str (&modes, ¶ms); + str_free (¶ms); + + char *modes_utf8 = irc_to_utf8 (modes.str); + str_free (&modes); + e->modes = str_from_cstr (modes_utf8); + free (modes_utf8); +} static void relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer) @@ -2997,18 +3036,10 @@ relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer) 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); + relay_prepare_channel_buffer_update (ctx, buffer, &e->context.channel); break; - } case BUFFER_PM: e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE; server_name = &e->context.private_message.server_name; @@ -3086,9 +3117,6 @@ relay_server_state_for_server (struct server *s) return 0; } -// TODO: Consider pushing this whole block of code further down. -static char *irc_to_utf8 (const char *text); - static void relay_prepare_server_update (struct app_context *ctx, struct server *s) { @@ -3103,9 +3131,9 @@ relay_prepare_server_update (struct app_context *ctx, struct server *s) e->data.registered.user = str_from_cstr (user_utf8); free (user_utf8); - char *user_mode_utf8 = irc_to_utf8 (s->irc_user_mode.str); - e->data.registered.user_mode = str_from_cstr (user_mode_utf8); - free (user_mode_utf8); + char *user_modes_utf8 = irc_to_utf8 (s->irc_user_modes.str); + e->data.registered.user_modes = str_from_cstr (user_modes_utf8); + free (user_modes_utf8); } } @@ -5311,10 +5339,8 @@ irc_make_channel (struct server *s, char *name) } static void -irc_channel_set_topic (struct channel *channel, const char *topic) +irc_channel_broadcast_buffer_update (const struct channel *channel) { - 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) @@ -5324,6 +5350,13 @@ irc_channel_set_topic (struct channel *channel, const char *topic) } } +static void +irc_channel_set_topic (struct channel *channel, const char *topic) +{ + cstr_set (&channel->topic, xstrdup (topic)); + irc_channel_broadcast_buffer_update (channel); +} + static struct channel_user * irc_channel_get_user (struct channel *channel, struct user *user) { @@ -5349,6 +5382,9 @@ irc_left_channel (struct channel *channel) LIST_FOR_EACH (struct channel_user, iter, channel->users) irc_channel_unlink_user (channel, iter); + + // Send empty channel modes. + irc_channel_broadcast_buffer_update (channel); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5693,7 +5729,7 @@ irc_destroy_state (struct server *s) s->irc_user = NULL; } - str_reset (&s->irc_user_mode); + str_reset (&s->irc_user_modes); cstr_set (&s->irc_user_host, NULL); strv_reset (&s->outstanding_joins); @@ -6671,7 +6707,7 @@ make_chanmode_postfix (struct channel *channel, struct str *modes) str_append (modes, channel->no_param_modes.str); struct str_map_iter iter = str_map_iter_make (&channel->param_modes); - char *param; + const char *param; while ((param = str_map_iter_next (&iter))) str_append_c (modes, iter.link->key[0]); } @@ -6688,8 +6724,8 @@ make_server_postfix_registered (struct buffer *buffer, struct str *output) irc_get_channel_user_prefix (s, channel_user, output); } str_append (output, s->irc_user->nickname); - if (s->irc_user_mode.len) - str_append_printf (output, "(%s)", s->irc_user_mode.str); + if (s->irc_user_modes.len) + str_append_printf (output, "(%s)", s->irc_user_modes.str); } static void @@ -6736,7 +6772,7 @@ make_prompt (struct app_context *ctx, struct str *output) buffer_get_index (ctx, buffer), buffer->name); // We remember old modes, don't show them while we're not on the channel if (buffer->type == BUFFER_CHANNEL - && buffer->channel->users_len) + && irc_channel_is_joined (buffer->channel)) { struct str modes = str_make (); make_chanmode_postfix (buffer->channel, &modes); @@ -7100,7 +7136,11 @@ irc_handle_mode_channel (struct channel *channel, char **params) { struct mode_processor p = { .s = channel->s, .channel = channel }; mode_processor_run (&p, params, mode_processor_apply_channel); - return p.changes == p.usermode_changes; + if (p.changes == p.usermode_changes) + return true; + + irc_channel_broadcast_buffer_update (channel); + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7108,7 +7148,7 @@ irc_handle_mode_channel (struct channel *channel, char **params) static bool mode_processor_apply_user (struct mode_processor *self) { - mode_processor_toggle (self, &self->s->irc_user_mode); + mode_processor_toggle (self, &self->s->irc_user_modes); return true; } @@ -8191,7 +8231,7 @@ static void irc_on_registered (struct server *s, const char *nickname) { s->irc_user = irc_get_or_make_user (s, nickname); - str_reset (&s->irc_user_mode); + str_reset (&s->irc_user_modes); cstr_set (&s->irc_user_host, NULL); irc_set_state (s, IRC_REGISTERED); @@ -8262,7 +8302,7 @@ irc_handle_rpl_umodeis (struct server *s, const struct irc_message *msg) if (msg->params.len < 2) return; - str_reset (&s->irc_user_mode); + str_reset (&s->irc_user_modes); irc_handle_mode_user (s, msg->params.vector + 1); // XXX: do we want to log a message? diff --git a/xP/public/xP.js b/xP/public/xP.js index f418d50..7bbf530 100644 --- a/xP/public/xP.js +++ b/xP/public/xP.js @@ -328,6 +328,7 @@ rpcEventHandlers.set(Relay.Event.BufferUpdate, e => { b.kind = e.context.kind b.server = servers.get(e.context.serverName) b.topic = e.context.topic + b.modes = e.context.modes }) rpcEventHandlers.set(Relay.Event.BufferStats, e => { @@ -702,6 +703,8 @@ let Status = { return m('.status', {}, 'Synchronizing...') let status = `${bufferCurrent}` + if (b.modes) + status += `(+${b.modes})` if (b.hideUnimportant) status += `` return m('.status', {}, [status, m(Toolbar)]) @@ -716,8 +719,8 @@ let Prompt = { if (b.server.data.user !== undefined) { let user = b.server.data.user - if (b.server.data.userMode) - user += `(${b.server.data.userMode})` + if (b.server.data.userModes) + user += `(${b.server.data.userModes})` return m('.prompt', {}, `${user}`) }