preliminary guild view in the side bar

This commit is contained in:
Florian Stinglmayr 2019-07-19 20:52:12 +02:00
parent b879151e07
commit af49931b49
11 changed files with 440 additions and 8 deletions

View File

@ -21,27 +21,27 @@ typedef enum {
/* A direct message channel for 1:1 communication
*/
CHANNEL_TYPE_DM,
CHANNEL_TYPE_DM = 1,
/* A guild voice channel
*/
CHANNEL_TYPE_GUILD_VOICE,
CHANNEL_TYPE_GUILD_VOICE = 2,
/* Group direct message channel 1:N communication
*/
CHANNEL_TYPE_GROUP_DM,
CHANNEL_TYPE_GROUP_DM = 3,
/* Category within a GUILD
*/
CHANNEL_TYPE_GUILD_CATEGORY,
CHANNEL_TYPE_GUILD_CATEGORY = 4,
/* News channel
*/
CHANNEL_TYPE_GUILD_NEWS,
CHANNEL_TYPE_GUILD_NEWS = 5,
/* Guild store, no idea what this is
*/
CHANNEL_TYPE_GUILD_STORE,
CHANNEL_TYPE_GUILD_STORE = 6,
} dc_channel_type_t;
struct dc_channel_;
@ -51,6 +51,7 @@ dc_channel_t dc_channel_new(void);
dc_channel_t dc_channel_from_json(json_t *j);
char const *dc_channel_id(dc_channel_t c);
char const *dc_channel_name(dc_channel_t c);
dc_channel_type_t dc_channel_type(dc_channel_t c);
bool dc_channel_is_dm(dc_channel_t c);

View File

@ -1,12 +1,21 @@
#ifndef DC_GUILD_H
#define DC_GUILD_H
#include <dc/channel.h>
#include <jansson.h>
#include <stdint.h>
/* Discords version of groups or chat servers
*/
struct dc_guild_;
typedef struct dc_guild_ *dc_guild_t;
dc_guild_t dc_guild_new(void);
dc_guild_t dc_guild_from_json(json_t *j);
size_t dc_guild_channels(dc_guild_t d);
dc_channel_t dc_guild_nth_channel(dc_guild_t d, size_t idx);
char const *dc_guild_name(dc_guild_t d);
void dc_guild_set_name(dc_guild_t d, char const *val);

View File

@ -8,6 +8,7 @@
#include <dc/account.h>
#include <dc/channel.h>
#include <dc/gateway.h>
#include <dc/guild.h>
/**
* A session object will contain all information gathered after a user
@ -86,6 +87,12 @@ dc_channel_t dc_session_make_channel(dc_session_t s, dc_account_t *r,
dc_channel_t dc_session_channel_recipients(dc_session_t s,
dc_account_t *r, size_t sz);
/**
* Add a guild to be managed by this session.
*/
void dc_session_add_guild(dc_session_t s, dc_guild_t g);
GHashTable *dc_session_guilds(dc_session_t s);
/**
* comparision functions for sorting, and finding
*/

View File

@ -124,7 +124,7 @@ dc_channel_t dc_channel_from_json(json_t *j)
}
v = json_object_get(j, "name");
if (v == NULL && json_is_string(v)) {
if (v != NULL && json_is_string(v)) {
c->name = strdup(json_string_value(v));
}
@ -237,6 +237,12 @@ json_t *dc_channel_to_json(dc_channel_t c)
return j;
}
char const *dc_channel_name(dc_channel_t c)
{
return_if_true(c == NULL, NULL);
return c->name;
}
char const *dc_channel_id(dc_channel_t c)
{
return_if_true(c == NULL, NULL);

View File

@ -9,6 +9,8 @@ struct dc_guild_
char *name;
char *id;
GPtrArray *channels;
};
static void dc_guild_free(dc_guild_t ptr)
@ -16,6 +18,11 @@ static void dc_guild_free(dc_guild_t ptr)
free(ptr->name);
free(ptr->id);
if (ptr->channels != NULL) {
g_ptr_array_unref(ptr->channels);
ptr->channels = NULL;
}
free(ptr);
}
@ -26,9 +33,64 @@ dc_guild_t dc_guild_new(void)
p->ref.cleanup = (dc_cleanup_t)dc_guild_free;
p->channels = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
if (p->channels == NULL) {
free(p);
return NULL;
}
return dc_ref(p);
}
dc_guild_t dc_guild_from_json(json_t *j)
{
dc_guild_t g = dc_guild_new();
json_t *val = NULL;
json_t *c = NULL;
size_t idx = 0;
val = json_object_get(j, "name");
goto_if_true(val == NULL || !json_is_string(val), error);
g->name = strdup(json_string_value(val));
val = json_object_get(j, "id");
goto_if_true(val == NULL || !json_is_string(val), error);
g->id = strdup(json_string_value(val));
/* there is a ton of more information here, that we should look
* add, including "member_count", "owner_id", but for channels
* will do nicely
*/
val = json_object_get(j, "channels");
goto_if_true(val == NULL || !json_is_array(val), error);
json_array_foreach(val, idx, c) {
dc_channel_t chan = dc_channel_from_json(c);
continue_if_true(chan == NULL);
g_ptr_array_add(g->channels, chan);
}
return g;
error:
dc_unref(g);
return NULL;
}
size_t dc_guild_channels(dc_guild_t d)
{
return_if_true(d == NULL || d->channels == NULL, 0);
return d->channels->len;
}
dc_channel_t dc_guild_nth_channel(dc_guild_t d, size_t idx)
{
return_if_true(d == NULL || d->channels == NULL, NULL);
return_if_true(idx >= d->channels->len, NULL);
return g_ptr_array_index(d->channels, idx);
}
char const *dc_guild_name(dc_guild_t d)
{
return_if_true(d == NULL, NULL);

View File

@ -12,6 +12,7 @@ struct dc_session_
GHashTable *accounts;
GHashTable *channels;
GHashTable *guilds;
};
/* event handlers
@ -40,6 +41,11 @@ static void dc_session_free(dc_session_t s)
s->channels = NULL;
}
if (s->guilds != NULL) {
g_hash_table_unref(s->guilds);
s->guilds = NULL;
}
dc_session_logout(s);
dc_unref(s->api);
@ -79,6 +85,7 @@ static void dc_session_handle_ready(dc_session_t s, dc_event_t e)
size_t idx = 0;
json_t *c = NULL;
json_t *channels = NULL;
json_t *guilds = NULL;
/* retrieve user information about ourselves, including snowflake,
* discriminator, and other things
@ -127,6 +134,17 @@ static void dc_session_handle_ready(dc_session_t s, dc_event_t e)
}
}
/* load guilds
*/
guilds = json_object_get(r, "guilds");
if (guilds != NULL && json_is_array(guilds)) {
json_array_foreach(guilds, idx, c) {
dc_guild_t guild = dc_guild_from_json(c);
continue_if_true(guild == NULL);
dc_session_add_guild(s, guild);
}
}
/* load channels
*/
channels = json_object_get(r, "private_channels");
@ -181,6 +199,11 @@ dc_session_t dc_session_new(dc_loop_t loop)
);
goto_if_true(s->channels == NULL, error);
s->guilds = g_hash_table_new_full(g_str_hash, g_str_equal,
free, dc_unref
);
goto_if_true(s->channels == NULL, error);
s->loop = dc_ref(loop);
s->api = dc_api_new();
@ -394,3 +417,23 @@ dc_channel_t dc_session_channel_recipients(dc_session_t s,
return NULL;
}
GHashTable *dc_session_guilds(dc_session_t s)
{
return_if_true(s == NULL, NULL);
return s->guilds;
}
void dc_session_add_guild(dc_session_t s, dc_guild_t g)
{
return_if_true(s == NULL || g == NULL,);
return_if_true(dc_guild_id(g) == NULL,);
char const *id = dc_guild_id(g);
if (!g_hash_table_contains(s->guilds, id)) {
g_hash_table_insert(s->guilds, strdup(id), dc_ref(g));
/* TODO: dedup for saving storage
*/
}
}

View File

@ -13,6 +13,7 @@ SET(SOURCES
"include/ncdc/mainwindow.h"
"include/ncdc/ncdc.h"
"include/ncdc/textview.h"
"include/ncdc/treeview.h"
"src/ack.c"
"src/cmds.c"
"src/config.c"
@ -26,6 +27,7 @@ SET(SOURCES
"src/ncdc.c"
"src/post.c"
"src/textview.c"
"src/treeview.c"
"src/util.c"
)

View File

@ -24,4 +24,6 @@ void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n);
void ncdc_mainwindow_rightview(ncdc_mainwindow_t n);
void ncdc_mainwindow_leftview(ncdc_mainwindow_t n);
void ncdc_mainwindow_update_guilds(ncdc_mainwindow_t n);
#endif

View File

@ -0,0 +1,31 @@
#ifndef NCDC_TREEVIEW_H
#define NCDC_TREEVIEW_H
#include <string.h>
#include <stdlib.h>
#include <ncdc/ncdc.h>
struct ncdc_treeitem_;
typedef struct ncdc_treeitem_ *ncdc_treeitem_t;
ncdc_treeitem_t ncdc_treeitem_new(void);
ncdc_treeitem_t ncdc_treeitem_new_string(wchar_t const *s);
wchar_t const *ncdc_treeitem_label(ncdc_treeitem_t i);
void ncdc_treeitem_set_label(ncdc_treeitem_t i, wchar_t const *s);
void *ncdc_treeitem_tag(ncdc_treeitem_t i);
void ncdc_treeitem_set_tag(ncdc_treeitem_t i, void *t);
void ncdc_treeitem_clear(ncdc_treeitem_t i);
void ncdc_treeitem_add(ncdc_treeitem_t i, ncdc_treeitem_t c);
void ncdc_treeitem_remove(ncdc_treeitem_t i, ncdc_treeitem_t c);
struct ncdc_treeview_;
typedef struct ncdc_treeview_ *ncdc_treeview_t;
ncdc_treeview_t ncdc_treeview_new(void);
ncdc_treeitem_t ncdc_treeview_root(ncdc_treeview_t t);
void ncdc_treeview_render(ncdc_treeview_t t, WINDOW *w, int lines, int cols);
#endif

View File

@ -1,6 +1,7 @@
#include <ncdc/mainwindow.h>
#include <ncdc/input.h>
#include <ncdc/textview.h>
#include <ncdc/treeview.h>
#include <ncdc/cmds.h>
#include <ncdc/ncdc.h>
@ -37,6 +38,8 @@ struct ncdc_mainwindow_
WINDOW *sep2;
ncdc_input_t in;
ncdc_treeview_t guildview;
ncdc_treeitem_t root;
GPtrArray *views;
int curview;
@ -62,6 +65,7 @@ static void ncdc_mainwindow_free(ncdc_mainwindow_t n)
delwin(n->sep2);
dc_unref(n->in);
dc_unref(n->guildview);
if (n->views != NULL) {
g_ptr_array_unref(n->views);
@ -81,6 +85,9 @@ ncdc_mainwindow_t ncdc_mainwindow_new(void)
ptr->in = ncdc_input_new();
ncdc_input_set_callback(ptr->in, ncdc_mainwindow_callback, ptr);
ptr->guildview = ncdc_treeview_new();
ptr->root = ncdc_treeview_root(ptr->guildview);
ptr->views = g_ptr_array_new_with_free_func(
(GDestroyNotify)dc_unref
);
@ -134,7 +141,7 @@ ncdc_mainwindow_callback(ncdc_input_t i, wchar_t const *s,
static void ncdc_mainwindow_resize(ncdc_mainwindow_t n)
{
n->guilds_h = LINES - 2;
n->guilds_w = (COLS / 5);
n->guilds_w = (COLS / 4);
n->guilds_y = 0;
n->guilds_x = 0;
@ -239,6 +246,85 @@ static void ncdc_mainwindow_render_status(ncdc_mainwindow_t n)
free(status);
}
void ncdc_mainwindow_update_guilds(ncdc_mainwindow_t n)
{
GHashTableIter iter;
gpointer key = NULL, value = NULL;
size_t idx = 0;
ncdc_treeitem_clear(n->root);
if (!is_logged_in()) {
return;
}
g_hash_table_iter_init(&iter, dc_session_guilds(current_session));
while (g_hash_table_iter_next(&iter, &key, &value)) {
dc_guild_t g = (dc_guild_t)value;
ncdc_treeitem_t i = ncdc_treeitem_new();
wchar_t *name = NULL;
if (i == NULL) {
continue;
}
name = s_convert(dc_guild_name(g));
if (name == NULL) {
continue;
}
ncdc_treeitem_set_label(i, name);
free(name);
name = NULL;
ncdc_treeitem_set_tag(i, g);
/* add subchannels
*/
for (idx = 0; idx < dc_guild_channels(g); idx++) {
dc_channel_t c = dc_guild_nth_channel(g, idx);
ncdc_treeitem_t ci = NULL;
if (dc_channel_name(c) == NULL ||
dc_channel_id(c) == NULL) {
continue;
}
/* skip categories, and shop channels
*/
if (dc_channel_type(c) == CHANNEL_TYPE_GUILD_CATEGORY ||
dc_channel_type(c) == CHANNEL_TYPE_GUILD_NEWS ||
dc_channel_type(c) == CHANNEL_TYPE_GUILD_STORE) {
continue;
}
ci = ncdc_treeitem_new();
if (ci == NULL) {
continue;
}
aswprintf(&name, L"[%s] %s",
(dc_channel_type(c) == CHANNEL_TYPE_GUILD_VOICE ?
"<" : "#"),
dc_channel_name(c)
);
if (name == NULL) {
continue;
}
ncdc_treeitem_set_label(ci, name);
free(name);
name = NULL;
ncdc_treeitem_set_tag(ci, c);
ncdc_treeitem_add(i, ci);
}
ncdc_treeitem_add(n->root, i);
}
}
void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n)
{
wint_t i = 0;
@ -311,6 +397,8 @@ void ncdc_mainwindow_refresh(ncdc_mainwindow_t n)
{
ncdc_textview_t v = 0;
ncdc_mainwindow_update_guilds(n);
ncdc_treeview_render(n->guildview, n->guilds, n->guilds_h, n->guilds_w);
wnoutrefresh(n->guilds);
/* render active text view

181
ncdc/src/treeview.c Normal file
View File

@ -0,0 +1,181 @@
#include <ncdc/treeview.h>
#include <ncdc/ncdc.h>
struct ncdc_treeitem_
{
dc_refable_t ref;
/* content
*/
wchar_t *content;
/* collapsed?
*/
bool collapsed;
/* children
*/
GPtrArray *children;
/* user defined data
*/
void *tag;
};
static void ncdc_treeitem_free(ncdc_treeitem_t t)
{
return_if_true(t == NULL,);
free(t->content);
t->content = NULL;
if (t->children != NULL) {
g_ptr_array_unref(t->children);
t->children = NULL;
}
free(t);
}
ncdc_treeitem_t ncdc_treeitem_new(void)
{
ncdc_treeitem_t t = calloc(1, sizeof(struct ncdc_treeitem_));
return_if_true(t == NULL, NULL);
t->ref.cleanup = (dc_cleanup_t)ncdc_treeitem_free;
t->children = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
if (t->children == NULL) {
free(t);
return NULL;
}
return dc_ref(t);
}
ncdc_treeitem_t ncdc_treeitem_new_string(wchar_t const *s)
{
ncdc_treeitem_t t = ncdc_treeitem_new();
return_if_true(t == NULL, NULL);
t->content = wcsdup(s);
return t;
}
void ncdc_treeitem_add(ncdc_treeitem_t i, ncdc_treeitem_t c)
{
return_if_true(i == NULL || c == NULL,);
g_ptr_array_add(i->children, dc_ref(c));
}
void ncdc_treeitem_remove(ncdc_treeitem_t i, ncdc_treeitem_t c)
{
return_if_true(i == NULL || c == NULL,);
g_ptr_array_remove(i->children, c);
}
void ncdc_treeitem_clear(ncdc_treeitem_t i)
{
return_if_true(i == NULL || i->children == NULL,);
return_if_true(i->children->len == 0,);
g_ptr_array_remove_range(i->children, 0, i->children->len);
}
void *ncdc_treeitem_tag(ncdc_treeitem_t i)
{
return_if_true(i == NULL, NULL);
return i->tag;
}
void ncdc_treeitem_set_tag(ncdc_treeitem_t i, void *t)
{
return_if_true(i == NULL,);
i->tag = t;
}
wchar_t const *ncdc_treeitem_label(ncdc_treeitem_t i)
{
return_if_true(i == NULL, NULL);
return i->content;
}
void ncdc_treeitem_set_label(ncdc_treeitem_t i, wchar_t const *s)
{
return_if_true(i == NULL || s == NULL,);
free(i->content);
i->content = wcsdup(s);
}
static int
ncdc_treeitem_render(ncdc_treeitem_t t, WINDOW *win,
int lines, int cols, int l, int c)
{
size_t i = 0, off = 0;
int ret = 0;
if (t->content != NULL) {
size_t len = wcslen(t->content);
mvwaddwstr(win, l, c, t->content);
off = ((len + c) / cols) + 1;
}
for (i = 0; i < t->children->len; i++) {
ncdc_treeitem_t child = g_ptr_array_index(t->children, i);
ret = ncdc_treeitem_render(child, win, lines, cols,
l + off, c + 1
);
off += ret;
}
return off;
}
struct ncdc_treeview_
{
dc_refable_t ref;
ncdc_treeitem_t root;
};
static void ncdc_treeview_free(ncdc_treeview_t t)
{
return_if_true(t == NULL,);
dc_unref(t->root);
t->root = NULL;
free(t);
}
ncdc_treeview_t ncdc_treeview_new(void)
{
ncdc_treeview_t t = calloc(1, sizeof(struct ncdc_treeview_));
return_if_true(t == NULL, NULL);
t->ref.cleanup = (dc_cleanup_t)ncdc_treeview_free;
t->root = ncdc_treeitem_new();
goto_if_true(t->root == NULL, error);
return dc_ref(t);
error:
dc_unref(t);
return NULL;
}
void ncdc_treeview_render(ncdc_treeview_t t, WINDOW *w, int lines, int cols)
{
werase(w);
wmove(w, 0, 0);
ncdc_treeitem_render(t->root, w, lines, cols, 0, 0);
}
ncdc_treeitem_t ncdc_treeview_root(ncdc_treeview_t t)
{
return_if_true(t == NULL, NULL);
return t->root;
}