Commit a107ab41 authored by Mohammed Sadiq's avatar Mohammed Sadiq
Browse files

Add and use new history

This is pretty much a complete rewrite moving all database actions to
a separate thread.

Fixes #323 #353
parent db689dba
......@@ -285,7 +285,6 @@ chatty_application_startup (GApplication *application)
gtk_style_context_add_provider_for_screen (gdk_screen_get_default(),
GTK_STYLE_PROVIDER (self->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
chatty_manager_purple (self->manager);
g_signal_connect_object (self->manager, "authorize-buddy",
G_CALLBACK (application_authorize_buddy_cb), self,
G_CONNECT_SWAPPED);
......@@ -309,6 +308,7 @@ chatty_application_activate (GApplication *application)
if (!self->main_window) {
self->main_window = chatty_window_new (app);
chatty_manager_purple (self->manager);
g_object_add_weak_pointer (G_OBJECT (self->main_window), (gpointer *)&self->main_window);
}
......
......@@ -369,84 +369,6 @@ chatty_update_typing_status (ChattyChatView *self)
}
}
static void
chatty_conv_get_im_messages_cb (const guchar *msg,
int direction,
time_t time_stamp,
const guchar *uuid,
gpointer user_data,
int last_message)
{
ChattyChatView *self = user_data;
ChattyMsgDirection msg_direction;
g_assert (CHATTY_IS_CHAT_VIEW (self));
if (direction == 1)
msg_direction = CHATTY_DIRECTION_IN;
else if (direction == -1)
msg_direction = CHATTY_DIRECTION_OUT;
else
msg_direction = CHATTY_DIRECTION_SYSTEM; /* TODO: LELAND: Do we have this case for IMs? */
if (msg && *msg) {
g_autoptr(ChattyMessage) message = NULL;
message = chatty_message_new (NULL, NULL, (const char *)msg, (const char *)uuid,
time_stamp, msg_direction, 0);
chatty_chat_prepend_message (self->chat, message);
}
}
static void
chatty_conv_get_chat_messages_cb (const guchar *msg,
int direction,
int time_stamp,
const char *room,
const guchar *who,
const guchar *uuid,
gpointer user_data)
{
ChattyChatView *self = user_data;
g_autoptr(ChattyMessage) message = NULL;
g_assert (CHATTY_IS_CHAT_VIEW (self));
if (msg && *msg) {
if (direction == 1) {
ChattyPpBuddy *buddy = NULL;
const char *alias = NULL;
if (who)
alias = strchr ((const char *)who, '/');
/* Skip ‘/’ */
if (alias)
alias++;
else
alias = (const char *)who;
if (alias)
buddy = chatty_chat_find_user (self->chat, alias);
message = chatty_message_new ((ChattyItem *)buddy, alias,
(const char *)msg, (const char *)uuid,
time_stamp, CHATTY_DIRECTION_IN, 0);
chatty_chat_prepend_message (self->chat, message);
} else if (direction == -1) {
message = chatty_message_new (NULL, NULL, (const char *)msg, (const char *)uuid,
0, CHATTY_DIRECTION_OUT, 0);
chatty_chat_prepend_message (self->chat, message);
} else {
message = chatty_message_new (NULL, NULL, (const char *)msg, (const char *)uuid,
time_stamp, CHATTY_DIRECTION_SYSTEM, 0);
chatty_chat_prepend_message (self->chat, message);
}
}
}
static void
chat_view_edge_overshot_cb (ChattyChatView *self,
GtkPositionType pos)
......@@ -454,7 +376,7 @@ chat_view_edge_overshot_cb (ChattyChatView *self,
g_assert (CHATTY_IS_CHAT_VIEW (self));
if (pos == GTK_POS_TOP)
chatty_chat_view_load (self, LAZY_LOAD_INITIAL_MSGS_LIMIT);
chatty_manager_load_more_chat (chatty_manager_get_default (), self->chat, LAZY_LOAD_INITIAL_MSGS_LIMIT);
}
......@@ -1072,54 +994,6 @@ chatty_chat_view_get_chat (ChattyChatView *self)
}
void
chatty_chat_view_load (ChattyChatView *self,
guint limit)
{
g_autoptr(ChattyMessage) message = NULL;
GListModel *message_list;
PurpleAccount *account;
const gchar *conv_name;
const char *uid = NULL;
gboolean im;
g_return_if_fail (CHATTY_IS_CHAT_VIEW (self));
g_return_if_fail (self->chatty_conv);
im = (self->chatty_conv->conv->type == PURPLE_CONV_TYPE_IM);
conv_name = purple_conversation_get_name (self->chatty_conv->conv);
account = purple_conversation_get_account (self->chatty_conv->conv);
/* Get the uid of the first message */
message_list = chatty_chat_get_messages (self->chat);
message = g_list_model_get_item (message_list, 0);
if (message)
uid = chatty_message_get_uid (message);
if (im) {
g_autofree char *who = NULL;
/* Remove resource (user could be connecting from different devices/applications) */
who = chatty_utils_jabber_id_strip (conv_name);
chatty_history_get_im_messages (account->username,
who,
chatty_conv_get_im_messages_cb,
self,
limit,
uid);
} else {
chatty_history_get_chat_messages (account->username,
conv_name,
chatty_conv_get_chat_messages_cb,
self,
limit,
uid);
}
}
void
chatty_chat_view_remove_footer (ChattyChatView *self)
{
......
......@@ -1056,3 +1056,38 @@ chatty_chat_set_encryption (ChattyChat *self,
chatty_chat_lurch_changed_cb,
g_object_ref (self));
}
void
chatty_chat_set_show_notifications (ChattyChat *self,
gboolean show)
{
PurpleBlistNode *node = NULL;
g_return_if_fail (CHATTY_IS_CHAT (self));
if (self->buddy)
node = (PurpleBlistNode *)self->buddy;
else if (self->pp_chat)
node = (PurpleBlistNode *)self->pp_chat;
else
return;
purple_blist_node_set_bool (node, "chatty-notifications", !!show);
}
gboolean
chatty_chat_get_auto_join (ChattyChat *self)
{
PurpleBlistNode *node = NULL;
g_return_val_if_fail (CHATTY_IS_CHAT (self), FALSE);
if (self->buddy)
node = (PurpleBlistNode *)self->buddy;
else if (self->pp_chat)
node = (PurpleBlistNode *)self->pp_chat;
else
return FALSE;
return purple_blist_node_get_bool (node, "chatty-autojoin");
}
......@@ -79,5 +79,8 @@ ChattyEncryption chatty_chat_get_encryption_status (ChattyChat *self)
void chatty_chat_load_encryption_status (ChattyChat *self);
void chatty_chat_set_encryption (ChattyChat *self,
gboolean enable);
void chatty_chat_set_show_notifications (ChattyChat *self,
gboolean show);
gboolean chatty_chat_get_auto_join (ChattyChat *self);
G_END_DECLS
......@@ -188,7 +188,7 @@ chatty_conv_add_history_since_component (GHashTable *components,
g_autofree gchar *iso_timestamp = g_malloc0(MAX_GMT_ISO_SIZE * sizeof(char));
mtime = chatty_history_get_chat_last_message_time(account, room);
mtime = chatty_history_get_last_message_time (account, room);
mtime += 1; // Use the next epoch to exclude the last stored message(s)
timeinfo = gmtime (&mtime);
g_return_if_fail (strftime (iso_timestamp,
......
This diff is collapsed.
/*
* Copyright (C) 2018 Purism SPC
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* chatty-history.h
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __HISTORY_H_INCLUDE__
#define __HISTORY_H_INCLUDE__
#include <glib.h>
#include <purple.h>
#include <time.h>
/* #include "chatty-conversation.h" */
typedef struct chatty_log ChattyLog;
struct chatty_log {
time_t epoch; // TODO: @LELAND: Once log-parsing functions are cleaned, review this
char *uid;
char *msg;
int dir;
};
//TODO:LELAND: Document methods!
int chatty_history_open (const char *dir,
const char *file_name);
void chatty_history_close (void);
void chatty_history_add_chat_message (const char *stanza,
int direction,
const char *account,
const char *who,
const char *uid,
time_t m_time,
const char *room);
void chatty_history_add_im_message (const char *stanza,
int direction,
const char *account,
const char *who,
const char *uid,
time_t m_time);
/**
* Returns the timestamp (time_t) for the message stored under specified ID
* for the given account.
* Copyright 2018,2020 Purism SPC
*
* @param uuid zero terminated string containing message ID
* @param account zero terminated string containing account name
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* @return the int representing timestamp (time_t) of the stored message or
* INT_MAX if message not found.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
int get_im_timestamp_for_uuid(const char *uuid, const char *account);
int get_chat_timestamp_for_uuid(const char *uuid, const char *room);
void chatty_history_get_im_messages (const char* account,
const char* who,
void (*cb)(const unsigned char *msg,
int direction,
time_t time_stamp,
const unsigned char *uuid,
gpointer data,
int last_message),
gpointer data,
guint limit,
const char *oldest_message_displayed);
void
chatty_history_get_chat_messages (const char *account,
const char *room,
void (*cb)(const unsigned char *msg,
int direction,
int time_stamp,
const char *room,
const unsigned char *who,
const unsigned char *uuid,
gpointer data),
gpointer data,
guint limit,
const char *oldest_message_displayed);
int
chatty_history_get_chat_last_message_time (const char* account,
const char* room);
#pragma once
void
chatty_history_delete_chat (const char* account,
const char* room);
void
chatty_history_delete_im (const char *account,
const char *who);
char
chatty_history_get_im_last_message (const char* account,
const char* who,
ChattyLog* chatty_log);
/**
* Adds history message to persistent storage, acts as a default handler
* for "conversation_write" signal.
*
* @param pa PurpleAccount under which to store message/event
* @param pcm pointer to PurpleConvMessage object with message payload.
* Its fields could be updated by previous handlers and thus
* must be dynamic object freed after use (or re-allocated)
* The flags field also serves as stop-flag should
* PURPLE_MESSAGE_NO_LOG flag be set by previous handlers.
* The alias field serves as a room name for group-chats.
* @param sid pointer to the (char) string which should carry unique
* message ID. If points to NULL - will be dynamically allocated
* with random UUID.
* @param type Conversation type, if PURPLE_CONV_TYPE_CHAT the message will
* be stored in the MUC archive, otherwise it goes to IM store.
* @param data not used
*/
void
chatty_history_add_message (PurpleAccount *pa, PurpleConvMessage *pcm,
char **sid, PurpleConversationType type,
gpointer data);
#include <glib.h>
#endif
#include "chatty-chat.h"
#include "chatty-message.h"
G_BEGIN_DECLS
#define CHATTY_TYPE_HISTORY (chatty_history_get_type ())
G_DECLARE_FINAL_TYPE (ChattyHistory, chatty_history, CHATTY, HISTORY, GObject)
ChattyHistory *chatty_history_get_default (void);
void chatty_history_open_async (ChattyHistory *self,
char *dir,
const char *file_name,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean chatty_history_open_finish (ChattyHistory *self,
GAsyncResult *result,
GError **error);
gboolean chatty_history_is_open (ChattyHistory *self);
void chatty_history_close_async (ChattyHistory *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean chatty_history_close_finish (ChattyHistory *self,
GAsyncResult *result,
GError **error);
void chatty_history_get_messages_async (ChattyHistory *self,
ChattyChat *chat,
ChattyMessage *start,
guint limit,
GAsyncReadyCallback callback,
gpointer user_data);
GPtrArray *chatty_history_get_messages_finish (ChattyHistory *self,
GAsyncResult *result,
GError **error);
void chatty_history_add_message_async (ChattyHistory *self,
ChattyChat *chat,
ChattyMessage *message,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean chatty_history_add_message_finish (ChattyHistory *self,
GAsyncResult *result,
GError **error);
void chatty_history_delete_chat_async (ChattyHistory *self,
ChattyChat *chat,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean chatty_history_delete_chat_finish (ChattyHistory *self,
GAsyncResult *result,
GError **error);
/* old APIs */
void chatty_history_open (const char *dir,
const char *file_name);
void chatty_history_close (void);
int chatty_history_get_chat_timestamp (const char *uuid,
const char *room);
int chatty_history_get_im_timestamp (const char *uuid,
const char *account);
int chatty_history_get_last_message_time (const char *account,
const char *room);
void chatty_history_delete_chat (ChattyChat *chat);
gboolean chatty_history_im_exists (const char *account,
const char *who);
gboolean chatty_history_chat_exists (const char *account,
const char *room);
gboolean chatty_history_add_message (const char *account,
const char *room,
const char *who,
const char *message,
char **uid,
PurpleMessageFlags flags,
time_t time_stamp,
PurpleConversationType type);
G_END_DECLS
......@@ -128,6 +128,73 @@ manager_sort_chat_item (ChattyChat *a,
return difftime (b_time, a_time);
}
static void
manager_get_messages_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
ChattyHistory *history = (ChattyHistory *)object;
g_autoptr(ChattyManager) self = user_data;
g_autoptr(GPtrArray) messages = NULL;
g_autoptr(GError) error = NULL;
ChattyChat *chat;
g_assert (CHATTY_IS_MANAGER (self));
g_assert (CHATTY_IS_HISTORY (history));
messages = chatty_history_get_messages_finish (history, result, &error);
if (messages) {
chat = g_object_get_data (G_OBJECT (result), "chat");
g_assert (CHATTY_IS_CHAT (chat));
chatty_chat_prepend_messages (chat, messages);
} else if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warning ("Error fetching messages: %s,", error->message);
}
}
static void
manager_load_messages_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
ChattyHistory *history = (ChattyHistory *)object;
g_autoptr(ChattyManager) self = user_data;
g_autoptr(GPtrArray) messages = NULL;
g_autoptr(GError) error = NULL;
ChattyChat *chat;
g_assert (CHATTY_IS_MANAGER (self));
g_assert (CHATTY_IS_HISTORY (history));
messages = chatty_history_get_messages_finish (history, result, &error);
chat = g_object_get_data (G_OBJECT (result), "chat");
g_assert (CHATTY_IS_CHAT (chat));
if (!messages)
chatty_chat_set_show_notifications (chat, TRUE);
if (messages) {
if (chatty_chat_get_auto_join (chat)) {
GListModel *model;
ChattyChat *item;
item = chatty_manager_add_chat (chatty_manager_get_default (), chat);
model = chatty_chat_get_messages (item);
/* If at least one message is loaded, don’t add again. */
if (g_list_model_get_n_items (model) == 0)
chatty_chat_prepend_messages (item, messages);
}
} else if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warning ("Error fetching messages: %s,", error->message);
}
}
static void
manager_eds_is_ready (ChattyManager *self)
{
......@@ -416,63 +483,6 @@ chatty_blist_remove (PurpleBuddyList *list,
chatty_manager_remove_node (chatty_manager_get_default (), node);
}
static void
chatty_blist_update_buddy (PurpleBuddyList *list,
PurpleBlistNode *node)
{
PurpleBuddy *buddy;
g_autofree ChattyLog *log_data = NULL;
PurpleAccount *account;
const char *username;
g_autofree char *who = NULL;
char message_exists;
g_return_if_fail (PURPLE_BLIST_NODE_IS_BUDDY(node));
buddy = (PurpleBuddy*)node;
account = purple_buddy_get_account (buddy);
username = purple_account_get_username (account);
who = chatty_utils_jabber_id_strip (purple_buddy_get_name (buddy));
log_data = g_new0(ChattyLog, 1);
message_exists = chatty_history_get_im_last_message (username, who, log_data);
if (!message_exists && !purple_blist_node_get_bool (node, "chatty-notifications"))
purple_blist_node_set_bool (node, "chatty-notifications", TRUE);
if (purple_blist_node_get_bool (node, "chatty-autojoin") &&
purple_account_is_connected (buddy->account) &&
message_exists) {
g_autoptr(ChattyMessage) message = NULL;
g_autoptr(ChattyChat) chat = NULL;
GListModel *model;
ChattyChat *item;
ChattyMsgDirection direction;
if (log_data->dir == 1)
direction = CHATTY_DIRECTION_IN;
else if (log_data->dir == -1)
direction = CHATTY_DIRECTION_OUT;
else
direction = CHATTY_DIRECTION_SYSTEM;
chat = chatty_chat_new_im_chat (account, buddy);
item = chatty_manager_add_chat (chatty_manager_get_default (), chat);
model = chatty_chat_get_messages (item);
/* If at least one message is loaded, don’t add again. */
if (g_list_model_get_n_items (model) > 0)
return;
message = chatty_message_new (NULL, NULL, log_data->msg, log_data->uid,
log_data->epoch, direction, 0);
chatty_chat_append_message (item, message);
}
}
static void
chatty_blist_update (PurpleBuddyList *list,
PurpleBlistNode *node)
......@@ -481,14 +491,11 @@ chatty_blist_update (PurpleBuddyList *list,
return;
switch (node->type) {
case PURPLE_BLIST_BUDDY_NODE:
chatty_blist_update_buddy (list, node);
break;
case PURPLE_BLIST_CHAT_NODE:
chatty_manager_update_node (chatty_manager_get_default (), node);
break;
case PURPLE_BLIST_BUDDY_NODE:
case PURPLE_BLIST_CONTACT_NODE:
case PURPLE_BLIST_GROUP_NODE:
case PURPLE_BLIST_OTHER_NODE:
......@@ -626,22 +633,6 @@ chatty_conv_stack_add_conv (ChattyConversation *chatty_conv)
chatty_chat_view_focus_entry (CHATTY_CHAT_VIEW (chatty_conv->chat_view));
}
static void
chatty_conv_setup_pane (ChattyConversation *chatty_conv,
guint msg_type)
{
ChattyChat *chat;
gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
"/sm/puri/chatty/icons/ui/");
chatty_conv->chat_view = chatty_chat_view_new ();
chat = chatty_manager_add_conversation (chatty_manager_get_default (), chatty_conv->conv);
chatty_chat_view_set_chat (CHATTY_CHAT_VIEW (chatty_conv->chat_view), chat);
}
static void
chatty_conv_remove_conv (ChattyConversation *chatty_conv)
{
......@@ -700,6 +691,7 @@ chatty_conv_find_conv (PurpleConversation * conv)
static void
chatty_conv_new (PurpleConversation *conv)
{
ChattyChat *chat;