Compare commits

..

5 Commits

Author SHA1 Message Date
ac7d6c31f0 fix(ui): address PR review on WIN_CHAT scroll handling
All checks were successful
CI Code / Check coding style (pull_request) Successful in 36s
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Code Coverage (pull_request) Successful in 2m40s
CI Code / Linux (debian) (pull_request) Successful in 4m38s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m53s
CI Code / Linux (arch) (pull_request) Successful in 5m41s
- Drop DB_RESPONSE_ERROR -> REACHED_BOTTOM in win_page_down: a transient
  backend error (e.g. sqlite lock) should not force the scroll state
  forward, or the user would get stuck mid-conversation expecting more
  rows that did not load.
- Trim the _win_printf comment to a single WHY line; the rest belongs in
  the originating commit message.
2026-05-16 17:57:28 +03:00
0f24910685 fix(ui): preserve WIN_CHAT messages when DB cannot replay them
All checks were successful
CI Code / Check spelling (pull_request) Successful in 16s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Code Coverage (pull_request) Successful in 2m45s
CI Code / Linux (debian) (pull_request) Successful in 4m39s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m56s
CI Code / Linux (arch) (pull_request) Successful in 5m45s
_win_printf skipped buffer/pad append for WIN_CHAT while the user was
scrolled, relying on chatwin_db_history() to replay missed messages on
scroll-down. That replay is unavailable when PREF_DBLOG is "off" (no
write) or "redact" ([REDACTED] body), or when the backend failed to
initialize — in those cases the message was lost.

Introduce log_database_can_recover_messages() and gate the WIN_CHAT
early return on it: when recovery is impossible, fall through and
append to the buffer just like non-chat windows do. Also treat
DB_RESPONSE_ERROR in win_page_down the same as DB_RESPONSE_EMPTY so
the [SCROLLED] indicator doesn't hang when the backend is dead.
2026-05-16 17:06:31 +03:00
506794474a fix(ui): handle ai window scrolling in titlebar
All checks were successful
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Code Coverage (pull_request) Successful in 2m45s
CI Code / Linux (debian) (pull_request) Successful in 4m37s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m52s
CI Code / Linux (arch) (pull_request) Successful in 5m40s
Add missing scroll rendering for AI windows in
the title bar draw function. This ensures AI
windows display their scrolled state consistently
with other non-chat window types.
2026-05-16 12:24:36 +00:00
ef0dd74dc1 fix(ui): preserve messages in non-chat windows while scrolled
All checks were successful
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 27s
CI Code / Code Coverage (pull_request) Successful in 2m44s
CI Code / Linux (debian) (pull_request) Successful in 4m42s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m57s
CI Code / Linux (arch) (pull_request) Successful in 5m50s
_win_printf used to drop the buffer append and pad render for any window
in paged state, but only WIN_CHAT can recover lost messages via
chatwin_db_history on scroll-down. WIN_MUC, WIN_PRIVATE and WIN_AI have
no such fallback, so incoming and outgoing messages were silently lost
when the user was viewing history. Suppress the buffer/render only for
WIN_CHAT; for the rest, still bump unread_msg but let the message land
in the buffer so it becomes visible on scroll-down.

This also removes the manual paged/unread_msg reset before printing the
user message in cl_ev_send_ai_msg — it was a local workaround for the
same drop and is no longer needed.

The buffer-bottom reset in win_page_down (c167f487) is still required:
it is the only path that clears paged and unread_msg for non-chat
windows when the user scrolls back to the bottom (WIN_SCROLL_REACHED_BOTTOM
is set only on the is_chat DB branch).
2026-05-15 19:46:12 +03:00
c12a1bc355 fix(ui): reset paged flag at buffer bottom for non-chat windows 2026-05-15 19:46:12 +03:00
13 changed files with 124 additions and 221 deletions

2
.gitignore vendored
View File

@@ -17,8 +17,6 @@ compile_commands.json
.project
.settings/
.vscode/
.kilo/
.roo/
*.plist/
# autotools

View File

@@ -186,65 +186,14 @@ ai_json_escape(const gchar* str)
return g_string_free(result, FALSE);
}
/**
* Convert a 4-hex-digit sequence at @p to a 16-bit code point.
* Returns -1 on invalid hex.
*/
static int
_hex4_to_int(const gchar* p)
{
int val = 0;
for (int i = 0; i < 4; i++) {
char c = p[i];
val <<= 4;
if (c >= '0' && c <= '9')
val |= c - '0';
else if (c >= 'a' && c <= 'f')
val |= c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
val |= c - 'A' + 10;
else
return -1;
}
return val;
}
/**
* Write a UTF-8 encoding of @cp to @w, advancing @w.
* Returns the updated pointer.
*/
static gchar*
_write_utf8(gchar* w, unsigned int cp)
{
if (cp <= 0x7F) {
*w++ = (char)cp;
} else if (cp <= 0x7FF) {
*w++ = (char)(0xC0 | (cp >> 6));
*w++ = (char)(0x80 | (cp & 0x3F));
} else if (cp <= 0xFFFF) {
*w++ = (char)(0xE0 | (cp >> 12));
*w++ = (char)(0x80 | ((cp >> 6) & 0x3F));
*w++ = (char)(0x80 | (cp & 0x3F));
} else if (cp <= 0x10FFFF) {
*w++ = (char)(0xF0 | (cp >> 18));
*w++ = (char)(0x80 | ((cp >> 12) & 0x3F));
*w++ = (char)(0x80 | ((cp >> 6) & 0x3F));
*w++ = (char)(0x80 | (cp & 0x3F));
}
/* Invalid code point: silently skip */
return w;
}
/* Decode JSON string escapes in [start, end) into a freshly allocated buffer.
* Handles \" \\ \/ \n \t \r \b \f and \uXXXX (including surrogate pairs). */
* Handles \" \\ \/ \n \t \r \b \f. \uXXXX sequences are passed through verbatim
* since the rest of the AI pipeline already deals in UTF-8 byte streams. */
static gchar*
_json_unescape_substring(const gchar* start, const gchar* end)
{
/* Estimate upper bound: each \uXXXX becomes up to 6 UTF-8 bytes. */
gsize max_len = (end - start) * 6 + 1;
gchar* out = g_new0(gchar, max_len);
gchar* out = g_new0(gchar, (end - start) + 1);
gchar* w = out;
for (const gchar* in = start; in < end;) {
if (*in == '\\' && in + 1 < end) {
switch (in[1]) {
@@ -280,64 +229,6 @@ _json_unescape_substring(const gchar* start, const gchar* end)
*w++ = '\f';
in += 2;
break;
case 'u':
/* Handle \uXXXX or surrogate pair \uHHHH\uLLLL */
if (in + 6 > end) {
/* Not enough characters — emit literal */
*w++ = *in++;
break;
}
if (in[2] != '\\' || in[3] != 'u') {
/* Single \uXXXX */
int cp = _hex4_to_int(in + 2);
if (cp < 0) {
/* Invalid hex — emit literal backslash */
*w++ = *in++;
} else if (cp < 0xD800 || cp > 0xDFFF) {
/* Not a surrogate — encode directly */
w = _write_utf8(w, (unsigned int)cp);
in += 6;
} else {
/* High or low surrogate — expect pair */
unsigned int high = (unsigned int)cp;
if ((high >= 0xD800 && high <= 0xDBFF) && in + 12 <= end && in[6] == '\\' && in[7] == 'u') {
/* High surrogate followed by \u */
int low = _hex4_to_int(in + 8);
if (low >= 0xDC00 && low <= 0xDFFF) {
/* Valid surrogate pair */
unsigned int cp2 = (0x10000 + ((high & 0x3FF) << 10)) | (low & 0x3FF);
w = _write_utf8(w, cp2);
in += 12;
} else {
/* Invalid low surrogate — emit replacement */
w = _write_utf8(w, 0xFFFD);
in += 6;
}
} else {
/* Orphan high surrogate or lone low surrogate */
w = _write_utf8(w, 0xFFFD);
in += 6;
}
}
} else {
/* \uHHHH\uLLLL — surrogate pair */
int high = _hex4_to_int(in + 2);
if (high < 0) {
*w++ = *in++;
break;
}
int low = _hex4_to_int(in + 8);
if ((high >= 0xD800 && high <= 0xDBFF) && (low >= 0xDC00 && low <= 0xDFFF)) {
unsigned int cp2 = (0x10000 + ((unsigned int)(high & 0x3FF) << 10)) | (unsigned int)(low & 0x3FF);
w = _write_utf8(w, cp2);
in += 12;
} else {
/* Invalid pair — emit replacement for high */
w = _write_utf8(w, 0xFFFD);
in += 6;
}
}
break;
default:
*w++ = *in++;
break;
@@ -347,9 +238,7 @@ _json_unescape_substring(const gchar* start, const gchar* end)
}
}
*w = '\0';
/* Realloc to exact size */
return g_realloc(out, w - out + 1);
return out;
}
/* Locate "field":"...value..." in json and return the unescaped value.

View File

@@ -1132,7 +1132,6 @@ cmd_ac_init(void)
autocomplete_add(statusbar_show_ac, "name");
autocomplete_add(statusbar_show_ac, "number");
autocomplete_add(statusbar_show_ac, "read");
autocomplete_add(statusbar_show_ac, "dbbackend");
autocomplete_add(statusbar_tabmode_ac, "actlist");
autocomplete_add(statusbar_tabmode_ac, "dynamic");
@@ -1147,6 +1146,9 @@ cmd_ac_init(void)
autocomplete_add(status_state_ac, "xa");
autocomplete_add(status_state_ac, "dnd");
autocomplete_add(logging_ac, "chat");
autocomplete_add(logging_ac, "group");
autocomplete_add(privacy_ac, "logging");
autocomplete_add(privacy_ac, "os");
@@ -4077,6 +4079,11 @@ _logging_autocomplete(ProfWin* window, const char* const input, gboolean previou
return result;
}
result = autocomplete_param_with_func(input, "/logging chat", prefs_autocomplete_boolean_choice, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_ac(input, "/logging group", logging_group_ac, TRUE, previous);
return result;
}

View File

@@ -1289,8 +1289,8 @@ static const struct cmd_t command_defs[] = {
CMD_TAGS(
CMD_TAG_UI)
CMD_SYN(
"/statusbar show name|number|read|dbbackend",
"/statusbar hide name|number|read|dbbackend",
"/statusbar show name|number|read",
"/statusbar hide name|number|read",
"/statusbar maxtabs <value>",
"/statusbar tablen <value>",
"/statusbar tabmode default|dynamic|actlist",
@@ -1308,7 +1308,6 @@ static const struct cmd_t command_defs[] = {
{ "show|hide name", "Show or hide names in tabs." },
{ "show|hide number", "Show or hide numbers in tabs." },
{ "show|hide read", "Show or hide inactive tabs." },
{ "show|hide dbbackend", "Show or hide the database backend indicator [sqlite] or [flatfile]." },
{ "self user|barejid|fulljid", "Show account user name, barejid, fulljid as status bar title." },
{ "self off", "Disable showing self as status bar title." },
{ "chat user|jid", "Show users name, or fulljid. Change needs a redraw/restart to take effect." },
@@ -1660,14 +1659,23 @@ static const struct cmd_t command_defs[] = {
},
{ CMD_PREAMBLE("/logging",
parse_args, 0, 0, NULL)
parse_args, 2, 3, &cons_logging_setting)
CMD_MAINFUNC(cmd_logging)
CMD_TAGS(
CMD_TAG_CHAT)
CMD_SYN(
"/logging")
"/logging chat|group on|off")
CMD_DESC(
"Deprecated. Use '/history' instead.")
"Configure chat logging. "
"Switch logging on or off. "
"Chat logging will be enabled if /history is set to on. "
"When disabling this option, /history will also be disabled. ")
CMD_ARGS(
{ "chat on|off", "Enable/Disable regular chat logging." },
{ "group on|off", "Enable/Disable groupchat (room) logging." })
CMD_EXAMPLES(
"/logging chat on",
"/logging group off")
},
{ CMD_PREAMBLE("/states",
@@ -1880,16 +1888,15 @@ static const struct cmd_t command_defs[] = {
"/history export [<jid>]",
"/history import [<jid>]")
CMD_DESC(
"Enable or disable chat history. "
"When on, messages are persisted via the active database backend and shown in chat windows. "
"When off, persistence stops and old history is hidden on chat-window open. "
"Switch chat history on or off, /logging chat will automatically be enabled when this setting is on. "
"When history is enabled, previous messages are shown in chat windows. "
"Use 'backend' to show the active database backend. "
"Use 'switch' to change the active database backend at runtime without reconnecting. "
"Use 'verify' to check integrity of stored message history. "
"Use 'export' to copy messages from SQLite to flat-file format, or 'import' to copy from flat-file to SQLite. "
"Both export and import merge with existing data (duplicates are skipped).")
CMD_ARGS(
{ "on|off", "Enable or disable persistence and showing chat history." },
{ "on|off", "Enable or disable showing chat history." },
{ "backend", "Show the name of the active database backend." },
{ "switch sqlite|flatfile", "Switch the active database backend at runtime." },
{ "verify [<jid>]", "Verify integrity of message history. Optionally specify a JID to check only one contact." },
@@ -2734,7 +2741,7 @@ static const struct cmd_t command_defs[] = {
"clientid to set the client identification name "
"session_alarm to configure an alarm when more clients log in.")
CMD_ARGS(
{ "logging on|redact|off|flatfile", "Switch chat logging backend. 'on' uses the SQLite database (default). 'flatfile' stores messages as plain-text files under ~/.local/share/profanity/flatlog/ that can be edited with any text editor. 'off' disables logging entirely. 'redact' keeps logging but replaces every message body with '[REDACTED]'. Note: 'off' may have side effects such as not being able to decrypt OMEMO messages received later via MAM. Backend changes ('on' / 'flatfile') take effect immediately when connected, otherwise on next connect." },
{ "logging on|redact|off|flatfile", "Switch chat logging. 'on' uses SQLite database (default). 'flatfile' stores messages as plain text files in ~/.local/share/profanity/flatlog/ that can be manually edited with any text editor. 'off' disables logging entirely. 'redact' stores messages with content replaced by '[redacted]'. Note: 'off' might have unintended consequences, such as not being able to decrypt OMEMO encrypted messages received later via MAM. The 'flatfile' setting takes effect on next connection." },
{ "os on|off", "Choose whether to include the OS name if a user asks for software information (XEP-0092)." }
)
CMD_EXAMPLES(

View File

@@ -6002,12 +6002,6 @@ cmd_statusbar(ProfWin* window, const char* const command, gchar** args)
ui_resize();
return TRUE;
}
if (g_strcmp0(args[1], "dbbackend") == 0) {
prefs_set_boolean(PREF_STATUSBAR_SHOW_DBBACKEND, TRUE);
cons_show("Enabled showing database backend indicator.");
ui_resize();
return TRUE;
}
cons_bad_cmd_usage(command);
return TRUE;
}
@@ -6041,12 +6035,6 @@ cmd_statusbar(ProfWin* window, const char* const command, gchar** args)
ui_resize();
return TRUE;
}
if (g_strcmp0(args[1], "dbbackend") == 0) {
prefs_set_boolean(PREF_STATUSBAR_SHOW_DBBACKEND, FALSE);
cons_show("Disabled showing database backend indicator.");
ui_resize();
return TRUE;
}
cons_bad_cmd_usage(command);
return TRUE;
}
@@ -6680,48 +6668,36 @@ cmd_privacy(ProfWin* window, const char* const command, gchar** args)
}
if (g_strcmp0(args[0], "logging") == 0) {
const gchar* arg = args[1];
gchar* arg = args[1];
if (arg == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
const gboolean is_on = (g_strcmp0(arg, "on") == 0);
const gboolean is_off = (g_strcmp0(arg, "off") == 0);
const gboolean is_redact = (g_strcmp0(arg, "redact") == 0);
const gboolean is_flatfile = (g_strcmp0(arg, "flatfile") == 0);
if (!is_on && !is_off && !is_redact && !is_flatfile) {
if (g_strcmp0(arg, "on") == 0) {
cons_show("Logging enabled.");
prefs_set_string(PREF_DBLOG, arg);
prefs_set_boolean(PREF_CHLOG, TRUE);
prefs_set_boolean(PREF_HISTORY, TRUE);
} else if (g_strcmp0(arg, "off") == 0) {
cons_show("Logging disabled.");
prefs_set_string(PREF_DBLOG, arg);
prefs_set_boolean(PREF_CHLOG, FALSE);
prefs_set_boolean(PREF_HISTORY, FALSE);
} else if (g_strcmp0(arg, "redact") == 0) {
cons_show("Messages are going to be redacted.");
prefs_set_string(PREF_DBLOG, arg);
} else if (g_strcmp0(arg, "flatfile") == 0) {
auto_gchar gchar* ff_path = files_get_data_path(DIR_FLATLOG);
cons_show("Using flat-file backend for message logging. Takes effect on next connection.");
cons_show("Flatfile directory: %s", ff_path);
prefs_set_string(PREF_DBLOG, arg);
prefs_set_boolean(PREF_CHLOG, TRUE);
prefs_set_boolean(PREF_HISTORY, TRUE);
} else {
cons_bad_cmd_usage(command);
return TRUE;
}
prefs_set_string(PREF_DBLOG, arg);
prefs_set_boolean(PREF_CHLOG, !is_off);
prefs_set_boolean(PREF_HISTORY, !is_off);
// For backend-switching values (on = sqlite, flatfile), apply the
// change immediately when connected so the user does not have to
// reconnect to see the new backend take effect. off/redact change
// write semantics but keep the currently-open backend alive.
if ((is_on || is_flatfile)
&& active_db_backend
&& connection_get_status() == JABBER_CONNECTED
&& g_strcmp0(active_db_backend->name, is_on ? "sqlite" : "flatfile") != 0) {
log_database_switch_backend(is_on ? "on" : "flatfile");
}
if (is_on) {
cons_show("Database logging enabled (sqlite).");
} else if (is_off) {
cons_show("Database logging disabled.");
} else if (is_redact) {
cons_show("Database logging set to redact (message bodies replaced with [REDACTED]).");
} else if (is_flatfile) {
auto_gchar gchar* ff_path = files_get_data_path(DIR_FLATLOG);
cons_show("Database logging set to flat-file backend.");
cons_show("Flatfile directory: %s", ff_path);
}
return TRUE;
}
@@ -6731,7 +6707,26 @@ cmd_privacy(ProfWin* window, const char* const command, gchar** args)
gboolean
cmd_logging(ProfWin* window, const char* const command, gchar** args)
{
cons_show("The '/logging' command is deprecated, use '/history' instead.");
if (args[0] == NULL) {
cons_logging_setting();
return TRUE;
}
if (strcmp(args[0], "chat") == 0 && args[1] != NULL) {
_cmd_set_boolean_preference(args[1], "Chat logging", PREF_CHLOG);
// if set to off, disable history
if (strcmp(args[1], "off") == 0) {
prefs_set_boolean(PREF_HISTORY, FALSE);
}
return TRUE;
} else if (g_strcmp0(args[0], "group") == 0 && args[1] != NULL) {
_cmd_set_boolean_preference(args[1], "Groupchat logging", PREF_GRLOG);
return TRUE;
}
cons_bad_cmd_usage(command);
return TRUE;
}
@@ -6878,20 +6873,9 @@ cmd_history(ProfWin* window, const char* const command, gchar** args)
_cmd_set_boolean_preference(args[0], "Chat history", PREF_HISTORY);
// /history off should stop persisting messages too, not just hide
// existing history on chat-window open. /history on resumes
// persistence (default backend) and parallel text chatlogs.
// if set to on, set chlog (/logging chat on)
if (strcmp(args[0], "on") == 0) {
prefs_set_boolean(PREF_CHLOG, TRUE);
auto_gchar gchar* dblog = prefs_get_string(PREF_DBLOG);
if (g_strcmp0(dblog, "off") == 0) {
prefs_set_string(PREF_DBLOG, "on");
cons_show("Database logging re-enabled (sqlite).");
}
} else if (strcmp(args[0], "off") == 0) {
prefs_set_boolean(PREF_CHLOG, FALSE);
prefs_set_string(PREF_DBLOG, "off");
cons_show("Database logging disabled.");
}
return TRUE;
@@ -7308,7 +7292,7 @@ cmd_pgp(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_PGP_LOG, "on");
cons_show("PGP messages will be logged as plaintext.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else if (g_strcmp0(choice, "off") == 0) {
prefs_set_string(PREF_PGP_LOG, "off");
@@ -7317,7 +7301,7 @@ cmd_pgp(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_PGP_LOG, "redact");
cons_show("PGP messages will be logged as '[redacted]'.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else {
cons_bad_cmd_usage(command);
@@ -7804,7 +7788,7 @@ cmd_ox_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OX_LOG, "on");
cons_show("OX messages will be logged as plaintext.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else if (g_strcmp0(choice, "off") == 0) {
prefs_set_string(PREF_OX_LOG, "off");
@@ -7813,7 +7797,7 @@ cmd_ox_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OX_LOG, "redact");
cons_show("OX messages will be logged as '[redacted]'.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else {
cons_bad_cmd_usage(command);
@@ -7853,7 +7837,7 @@ cmd_otr_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OTR_LOG, "on");
cons_show("OTR messages will be logged as plaintext.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else if (g_strcmp0(choice, "off") == 0) {
prefs_set_string(PREF_OTR_LOG, "off");
@@ -7862,7 +7846,7 @@ cmd_otr_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OTR_LOG, "redact");
cons_show("OTR messages will be logged as '[redacted]'.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else {
cons_bad_cmd_usage(command);
@@ -8799,7 +8783,7 @@ cmd_omemo_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OMEMO_LOG, "on");
cons_show("OMEMO messages will be logged as plaintext.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else if (g_strcmp0(choice, "off") == 0) {
prefs_set_string(PREF_OMEMO_LOG, "off");
@@ -8808,7 +8792,7 @@ cmd_omemo_log(ProfWin* window, const char* const command, gchar** args)
prefs_set_string(PREF_OMEMO_LOG, "redact");
cons_show("OMEMO messages will be logged as '[redacted]'.");
if (!prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging is currently disabled, use '/history on' to enable.");
cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
}
} else {
cons_bad_cmd_usage(command);
@@ -10932,16 +10916,23 @@ cmd_ai_start(ProfWin* window, const char* const command, gchar** args)
if (g_strv_length(args) >= 2) {
provider_name = args[1];
}
if (!provider_name) {
cons_show_error("Please specify provider name.");
return TRUE;
}
if (g_strv_length(args) >= 3) {
model = args[2];
}
// Resolve defaults
auto_gchar gchar* owned_provider_name = NULL;
if (!provider_name) {
// Use default provider from preferences
auto_gchar gchar* default_provider = prefs_get_string(PREF_AI_PROVIDER);
if (default_provider && strlen(default_provider) > 0) {
provider_name = default_provider;
}
}
if (!provider_name) {
provider_name = "openai"; // Fallback
}
AIProvider* provider = ai_get_provider(provider_name);
if (!provider) {
cons_show_error("Provider '%s' not found. Use '/ai set provider %s <url>' to add it.",

View File

@@ -2208,7 +2208,6 @@ _get_group(preference_t pref)
case PREF_STATUSBAR_SHOW_NAME:
case PREF_STATUSBAR_SHOW_NUMBER:
case PREF_STATUSBAR_SHOW_READ:
case PREF_STATUSBAR_SHOW_DBBACKEND:
case PREF_STATUSBAR_SELF:
case PREF_STATUSBAR_CHAT:
case PREF_STATUSBAR_ROOM_TITLE:
@@ -2536,8 +2535,6 @@ _get_key(preference_t pref)
return "statusbar.show.number";
case PREF_STATUSBAR_SHOW_READ:
return "statusbar.show.read";
case PREF_STATUSBAR_SHOW_DBBACKEND:
return "statusbar.show.dbbackend";
case PREF_STATUSBAR_SELF:
return "statusbar.self";
case PREF_STATUSBAR_CHAT:
@@ -2631,7 +2628,6 @@ _get_default_boolean(preference_t pref)
case PREF_ROOM_LIST_CACHE:
case PREF_STATUSBAR_SHOW_NUMBER:
case PREF_STATUSBAR_SHOW_READ:
case PREF_STATUSBAR_SHOW_DBBACKEND:
case PREF_REVEAL_OS:
case PREF_CORRECTION_ALLOW:
case PREF_RECEIPTS_SEND:

View File

@@ -162,7 +162,6 @@ typedef enum {
PREF_STATUSBAR_SHOW_NAME,
PREF_STATUSBAR_SHOW_NUMBER,
PREF_STATUSBAR_SHOW_READ,
PREF_STATUSBAR_SHOW_DBBACKEND,
PREF_STATUSBAR_SELF,
PREF_STATUSBAR_CHAT,
PREF_STATUSBAR_ROOM_TITLE,

View File

@@ -88,8 +88,9 @@ _ff_cache_line_ids(ff_contact_state_t* state, const char* line)
return;
// Split metadata on unescaped '|'
auto_gchar gchar* meta_str = g_strndup(bracket + 1, close - bracket - 1);
char* meta_str = g_strndup(bracket + 1, close - bracket - 1);
char** parts = ff_split_meta(meta_str);
g_free(meta_str);
char* stanza_id = NULL;
char* archive_id = NULL;
@@ -140,13 +141,15 @@ _ff_maybe_index_line(ff_contact_state_t* state, const char* buf, off_t pos)
if (!space)
return;
auto_gchar gchar* ts_str = g_strndup(buf, space - buf);
char* ts_str = g_strndup(buf, space - buf);
GDateTime* dt = g_date_time_new_from_iso8601(ts_str, NULL);
if (!dt) {
log_warning("flatfile: unparsable timestamp in %s at offset %ld: %s",
state->filepath, (long)pos, ts_str);
g_free(ts_str);
return;
}
g_free(ts_str);
if (state->n_entries >= state->cap_entries) {
state->cap_entries = state->cap_entries ? state->cap_entries * 2 : 64;

View File

@@ -1884,12 +1884,6 @@ cons_statusbar_setting(void)
cons_show("Show tab with no actions (/statusbar) : OFF");
}
if (prefs_get_boolean(PREF_STATUSBAR_SHOW_DBBACKEND)) {
cons_show("Show database backend (/statusbar) : ON");
} else {
cons_show("Show database backend (/statusbar) : OFF");
}
cons_show("Max tabs (/statusbar) : %d", prefs_get_statusbartabs());
gint pref_len = prefs_get_statusbartablen();
@@ -1948,12 +1942,27 @@ cons_log_setting(void)
cons_show("Log level (/log level) : %s", level);
}
void
cons_logging_setting(void)
{
if (prefs_get_boolean(PREF_CHLOG))
cons_show("Chat logging (/logging chat) : ON");
else
cons_show("Chat logging (/logging chat) : OFF");
if (prefs_get_boolean(PREF_GRLOG))
cons_show("Groupchat logging (/logging group) : ON");
else
cons_show("Groupchat logging (/logging group) : OFF");
}
void
cons_show_log_prefs(void)
{
cons_show("Logging preferences:");
cons_show("");
cons_log_setting();
cons_logging_setting();
cons_alert(NULL);
}
@@ -2847,6 +2856,11 @@ cons_privacy_setting(void)
{
cons_show("Database logging : %s", prefs_get_string(PREF_DBLOG));
if (prefs_get_boolean(PREF_CHLOG)) {
cons_show("Chat logging (/logging chat) : ON");
} else {
cons_show("Chat logging (/logging chat) : OFF");
}
if (prefs_get_boolean(PREF_HISTORY)) {
cons_show("Chat history (/history) : ON");
} else {

View File

@@ -609,8 +609,6 @@ _status_bar_draw_maintext(int pos)
static int
_status_bar_draw_dbbackend(int pos)
{
if (!prefs_get_boolean(PREF_STATUSBAR_SHOW_DBBACKEND))
return pos;
if (!active_db_backend || !active_db_backend->name)
return pos;

View File

@@ -330,6 +330,7 @@ void cons_history_setting(void);
void cons_carbons_setting(void);
void cons_receipts_setting(void);
void cons_log_setting(void);
void cons_logging_setting(void);
void cons_autoaway_setting(void);
void cons_reconnect_setting(void);
void cons_autoping_setting(void);

View File

@@ -1608,7 +1608,7 @@ win_print_outgoing(ProfWin* window, const char* show_char, const char* const id,
GDateTime* timestamp = g_date_time_new_now_local();
const char* myjid = connection_get_fulljid();
if (!prefs_get_boolean(PREF_CORRECTION_ALLOW) || !_win_correct(window, message, id, replace_id, myjid)) {
if (!_win_correct(window, message, id, replace_id, myjid)) {
auto_gchar gchar* outgoing_str = prefs_get_string(PREF_OUTGOING_STAMP);
_win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, outgoing_str, myjid, id, "%s", message);
}
@@ -1755,7 +1755,7 @@ win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const ch
receipt->received = FALSE;
const char* myjid = connection_get_fulljid();
if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && _win_correct(window, message, id, replace_id, myjid)) {
if (_win_correct(window, message, id, replace_id, myjid)) {
free(receipt); // TODO: probably we should use this in _win_correct()
} else {
int y_start_pos = getcury(window->layout->win);

View File

@@ -69,7 +69,7 @@ cmd_otr_log_on_shows_warning_when_chlog_disabled(void** state)
prefs_set_boolean(PREF_CHLOG, FALSE);
expect_cons_show("OTR messages will be logged as plaintext.");
expect_cons_show("Chat logging is currently disabled, use '/history on' to enable.");
expect_cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
gboolean result = cmd_otr_log(NULL, CMD_OTR, args);
assert_true(result);
@@ -115,7 +115,7 @@ cmd_otr_log_redact_shows_warning_when_chlog_disabled(void** state)
prefs_set_boolean(PREF_CHLOG, FALSE);
expect_cons_show("OTR messages will be logged as '[redacted]'.");
expect_cons_show("Chat logging is currently disabled, use '/history on' to enable.");
expect_cons_show("Chat logging is currently disabled, use '/logging chat on' to enable.");
gboolean result = cmd_otr_log(NULL, CMD_OTR, args);
assert_true(result);