adds dbus messaging for getting conversations. needs org
This commit is contained in:
118
src/conversation-list-model.vala
Normal file
118
src/conversation-list-model.vala
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using GLib;
|
||||||
|
using Gee;
|
||||||
|
|
||||||
|
public class ConversationListModel : Object, ListModel
|
||||||
|
{
|
||||||
|
public SortedSet<Conversation> conversations {
|
||||||
|
owned get { return _conversations.read_only_view; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedSet<Conversation> _conversations;
|
||||||
|
private RepositoryService repository;
|
||||||
|
private uint dbus_watch_id;
|
||||||
|
|
||||||
|
public ConversationListModel() {
|
||||||
|
_conversations = new TreeSet<Conversation>((a, b) => {
|
||||||
|
// Sort by date in descending order (newest first)
|
||||||
|
return (int)(b.date - a.date);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect_to_dbus.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ConversationListModel() {
|
||||||
|
if (dbus_watch_id > 0) {
|
||||||
|
Bus.unwatch_name(dbus_watch_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void connect_to_dbus() {
|
||||||
|
bool connected = false;
|
||||||
|
const string path = "/net/buzzert/kordophonecd/daemon";
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug("Trying to connect to DBus service at path: %s", path);
|
||||||
|
repository = yield Bus.get_proxy(BusType.SESSION,
|
||||||
|
"net.buzzert.kordophonecd",
|
||||||
|
path);
|
||||||
|
|
||||||
|
// Test the connection
|
||||||
|
repository.get_version();
|
||||||
|
|
||||||
|
// If we get here, connection succeeded
|
||||||
|
debug("Connected to DBus service at path: %s", path);
|
||||||
|
connected = true;
|
||||||
|
|
||||||
|
// Listen for updates
|
||||||
|
repository.conversations_updated.connect(load_conversations);
|
||||||
|
|
||||||
|
// Initial load
|
||||||
|
load_conversations();
|
||||||
|
} catch (Error e) {
|
||||||
|
debug("Failed to connect to kordophonecd at %s: %s", path, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connected) {
|
||||||
|
warning("Failed to connect to kordophonecd on any known path");
|
||||||
|
|
||||||
|
// Watch for the service to appear
|
||||||
|
dbus_watch_id = Bus.watch_name(BusType.SESSION,
|
||||||
|
"net.buzzert.kordophonecd",
|
||||||
|
BusNameWatcherFlags.AUTO_START,
|
||||||
|
() => {
|
||||||
|
connect_to_dbus.begin();
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load_conversations() {
|
||||||
|
if (repository == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Variant conversations_variant = repository.get_conversations();
|
||||||
|
|
||||||
|
// Clear existing set
|
||||||
|
uint old_count = _conversations.size;
|
||||||
|
_conversations.clear();
|
||||||
|
|
||||||
|
// Notify of removal
|
||||||
|
if (old_count > 0) {
|
||||||
|
items_changed(0, old_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each conversation
|
||||||
|
size_t n_children = conversations_variant.n_children();
|
||||||
|
uint position = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n_children; i++) {
|
||||||
|
Variant child = conversations_variant.get_child_value(i);
|
||||||
|
var conversation = new Conversation.from_variant(child);
|
||||||
|
_conversations.add(conversation);
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify of additions
|
||||||
|
if (position > 0) {
|
||||||
|
items_changed(0, 0, position);
|
||||||
|
}
|
||||||
|
} catch (Error e) {
|
||||||
|
warning("Failed to load conversations: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListModel implementation
|
||||||
|
public Type get_item_type() {
|
||||||
|
return typeof(Conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint get_n_items() {
|
||||||
|
return _conversations.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object? get_item(uint position) {
|
||||||
|
return _conversations.to_array()[position];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,28 +5,43 @@ public class ConversationListView : Adw.Bin
|
|||||||
{
|
{
|
||||||
private Adw.ToolbarView container;
|
private Adw.ToolbarView container;
|
||||||
private ListBox list_box;
|
private ListBox list_box;
|
||||||
|
private ScrolledWindow scrolled_window;
|
||||||
private Adw.HeaderBar header_bar;
|
private Adw.HeaderBar header_bar;
|
||||||
|
private ConversationListModel conversation_model;
|
||||||
|
|
||||||
public ConversationListView () {
|
public ConversationListView () {
|
||||||
container = new Adw.ToolbarView ();
|
container = new Adw.ToolbarView ();
|
||||||
set_child (container);
|
set_child (container);
|
||||||
|
|
||||||
|
scrolled_window = new ScrolledWindow ();
|
||||||
|
container.set_content (scrolled_window);
|
||||||
|
|
||||||
list_box = new ListBox ();
|
list_box = new ListBox ();
|
||||||
list_box.add_css_class ("boxed-list");
|
list_box.add_css_class ("boxed-list");
|
||||||
list_box.set_selection_mode (SelectionMode.SINGLE);
|
list_box.set_selection_mode (SelectionMode.SINGLE);
|
||||||
container.set_content (list_box);
|
scrolled_window.set_child (list_box);
|
||||||
|
|
||||||
header_bar = new Adw.HeaderBar ();
|
header_bar = new Adw.HeaderBar ();
|
||||||
header_bar.set_title_widget (new Label ("Kordophone"));
|
header_bar.set_title_widget (new Label ("Kordophone"));
|
||||||
container.add_top_bar (header_bar);
|
container.add_top_bar (header_bar);
|
||||||
|
|
||||||
// Populate with test data
|
// Set up refresh button
|
||||||
for (int i = 0; i < 10; i++) {
|
var refresh_button = new Button.from_icon_name ("view-refresh-symbolic");
|
||||||
var row = new ActionRow ();
|
refresh_button.tooltip_text = "Refresh Conversations";
|
||||||
row.title = "Conversation %d".printf(i);
|
refresh_button.clicked.connect (() => {
|
||||||
list_box.append (row);
|
if (conversation_model != null) {
|
||||||
}
|
conversation_model.load_conversations ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
header_bar.pack_end (refresh_button);
|
||||||
|
|
||||||
|
// Set up model and bind to list
|
||||||
|
conversation_model = new ConversationListModel ();
|
||||||
|
list_box.bind_model (conversation_model, create_conversation_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Widget create_conversation_row (Object item) {
|
||||||
|
Conversation conversation = (Conversation) item;
|
||||||
|
return new ConversationRow (conversation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
45
src/conversation-row.vala
Normal file
45
src/conversation-row.vala
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Adw;
|
||||||
|
using Gtk;
|
||||||
|
|
||||||
|
public class ConversationRow : Adw.ActionRow {
|
||||||
|
private Label? unread_badge;
|
||||||
|
|
||||||
|
public ConversationRow(Conversation conversation) {
|
||||||
|
Object();
|
||||||
|
|
||||||
|
title = conversation.display_name;
|
||||||
|
subtitle = conversation.last_message_preview;
|
||||||
|
subtitle_lines = 1;
|
||||||
|
|
||||||
|
// Add unread badge if needed
|
||||||
|
if (conversation.is_unread && conversation.unread_count > 0) {
|
||||||
|
unread_badge = new Label(conversation.unread_count.to_string());
|
||||||
|
unread_badge.add_css_class("badge");
|
||||||
|
unread_badge.add_css_class("accent");
|
||||||
|
add_suffix(unread_badge);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add timestamp if available
|
||||||
|
if (conversation.date > 0) {
|
||||||
|
var datetime = new DateTime.from_unix_local(conversation.date);
|
||||||
|
if (datetime != null) {
|
||||||
|
var now = new DateTime.now_local();
|
||||||
|
|
||||||
|
string time_str;
|
||||||
|
if (datetime.get_year() == now.get_year() &&
|
||||||
|
datetime.get_day_of_year() == now.get_day_of_year()) {
|
||||||
|
// Today - show time
|
||||||
|
time_str = datetime.format("%H:%M");
|
||||||
|
} else {
|
||||||
|
// Not today - show date
|
||||||
|
time_str = datetime.format("%b %d");
|
||||||
|
}
|
||||||
|
|
||||||
|
var time_label = new Label(time_str);
|
||||||
|
time_label.add_css_class("dim-label");
|
||||||
|
time_label.margin_start = 8;
|
||||||
|
add_suffix(time_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/conversation.vala
Normal file
77
src/conversation.vala
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using GLib;
|
||||||
|
|
||||||
|
public class Conversation : Object {
|
||||||
|
public string id { get; set; default = ""; }
|
||||||
|
public string last_message_preview { get; set; default = ""; }
|
||||||
|
public bool is_unread { get; set; default = false; }
|
||||||
|
public int64 date { get; set; default = 0; }
|
||||||
|
public string[] participants { get; set; default = new string[0]; }
|
||||||
|
public int unread_count { get; set; default = 0; }
|
||||||
|
|
||||||
|
public string display_name {
|
||||||
|
owned get {
|
||||||
|
if (_display_name != null && _display_name.length > 0) {
|
||||||
|
return _display_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (participants.length == 1) {
|
||||||
|
return participants[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (participants.length > 1) {
|
||||||
|
return string.join(", ", participants);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Untitled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _display_name = null;
|
||||||
|
|
||||||
|
public Conversation.from_variant (Variant dict) {
|
||||||
|
id = "";
|
||||||
|
last_message_preview = "";
|
||||||
|
participants = new string[0];
|
||||||
|
|
||||||
|
if (dict.get_type_string() != "a{sv}") {
|
||||||
|
warning("Expected dictionary variant, got %s", dict.get_type_string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe extraction with type checking
|
||||||
|
Variant? id_variant = dict.lookup_value("id", VariantType.STRING);
|
||||||
|
if (id_variant != null) {
|
||||||
|
id = id_variant.get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? display_name_variant = dict.lookup_value("display_name", VariantType.STRING);
|
||||||
|
if (display_name_variant != null) {
|
||||||
|
_display_name = display_name_variant.get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? last_message_variant = dict.lookup_value("last_message_preview", VariantType.STRING);
|
||||||
|
if (last_message_variant != null) {
|
||||||
|
last_message_preview = last_message_variant.get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? is_unread_variant = dict.lookup_value("is_unread", VariantType.BOOLEAN);
|
||||||
|
if (is_unread_variant != null) {
|
||||||
|
is_unread = is_unread_variant.get_boolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? date_variant = dict.lookup_value("date", VariantType.INT64);
|
||||||
|
if (date_variant != null) {
|
||||||
|
date = date_variant.get_int64();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? participants_variant = dict.lookup_value("participants", new VariantType("as"));
|
||||||
|
if (participants_variant != null) {
|
||||||
|
participants = participants_variant.dup_strv();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant? unread_count_variant = dict.lookup_value("unread_count", VariantType.INT32);
|
||||||
|
if (unread_count_variant != null) {
|
||||||
|
unread_count = unread_count_variant.get_int32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,23 @@
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
dependency('gtk4', required : true),
|
dependency('gtk4', required : true),
|
||||||
dependency('libadwaita-1', required : true)
|
dependency('libadwaita-1', required : true),
|
||||||
|
dependency('gio-2.0', required : true),
|
||||||
|
dependency('gee-0.8', required : true)
|
||||||
]
|
]
|
||||||
|
|
||||||
sources = [
|
sources = [
|
||||||
'kordophone-application.vala',
|
'kordophone-application.vala',
|
||||||
'main-window.vala',
|
'main-window.vala',
|
||||||
'conversation-list-view.vala',
|
'conversation-list-view.vala',
|
||||||
|
'conversation.vala',
|
||||||
|
'conversation-row.vala',
|
||||||
|
'conversation-list-model.vala',
|
||||||
|
'repository-service.vala'
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('kordophone',
|
executable('kordophone',
|
||||||
sources,
|
sources,
|
||||||
dependencies : dependencies,
|
dependencies : dependencies,
|
||||||
|
vala_args: ['--pkg', 'posix'],
|
||||||
install : true
|
install : true
|
||||||
)
|
)
|
||||||
18
src/repository-service.vala
Normal file
18
src/repository-service.vala
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using GLib;
|
||||||
|
|
||||||
|
[DBus (name = "net.buzzert.kordophone.Repository")]
|
||||||
|
public interface RepositoryService : Object {
|
||||||
|
public abstract string get_version() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (signature = "aa{sv}")]
|
||||||
|
public abstract Variant get_conversations() throws DBusError, IOError;
|
||||||
|
|
||||||
|
public abstract void sync_all_conversations() throws DBusError, IOError;
|
||||||
|
public abstract void sync_conversation(string conversation_id) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (signature = "aa{sv}")]
|
||||||
|
public abstract Variant get_messages(string conversation_id, string last_message_id) throws DBusError, IOError;
|
||||||
|
|
||||||
|
public signal void conversations_updated();
|
||||||
|
public signal void messages_updated(string conversation_id);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user