summaryrefslogtreecommitdiff
path: root/src/fruits/cmc
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-02-02 22:57:22 -0500
committerAndrew Opalach <andrew@akon.city> 2024-02-02 22:57:22 -0500
commit0b7a612a9baf49765db8b127e696913100b70f5f (patch)
tree78680777023d989b9148dd98a5a6e489e49b022a /src/fruits/cmc
parentb100eb175e0cfa59a15c4125f26ab5474c2347ec (diff)
downloadcamu-0b7a612a9baf49765db8b127e696913100b70f5f.tar.gz
camu-0b7a612a9baf49765db8b127e696913100b70f5f.tar.bz2
camu-0b7a612a9baf49765db8b127e696913100b70f5f.zip
wip
- Getting somewhere with the client Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/fruits/cmc')
-rw-r--r--src/fruits/cmc/cmc.c562
1 files changed, 386 insertions, 176 deletions
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 55f659c..f70cf8d 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -36,12 +36,14 @@ struct cmc_view {
struct ncplane *container;
u32 rows;
u32 y;
+ u32 pending_requests;
};
struct cmc_view_resource {
u16 id;
str unique_id;
u32 index;
+ array(struct cmc_view *) refs;
struct ncvisual *v;
struct ncvgeom geom;
struct ncvisual_options vopts;
@@ -79,17 +81,24 @@ struct cmc_view_page {
array(struct cmc_view *) views;
};
+enum {
+ SEARCH_NONE = 0,
+ SEARCH_UP,
+ SEARCH_DOWN
+};
+
struct cmc_search {
struct camu_search *search;
- bool find_anchor;
+ u8 find_anchor;
s32 anchor;
- s32 prev_anchor;
+ bool redraw;
u32 count;
s32 selected;
- s32 prev_selected;
+ array(struct cmc_view *) prev;
u32 max_page_num;
u32 min_page_num;
s32 end_of_page;
+ u32 pending_requests;
array(struct cmc_view_page *) pages;
};
@@ -98,6 +107,13 @@ struct cmc_tui {
u32 cols;
u32 rows;
struct ncplane *p;
+ struct {
+ struct ncplane *p;
+ u32 cols;
+ u32 rows;
+ bool active;
+ s32 selected;
+ } overlay;
struct aki_poll input;
u32 req_index;
array(struct cmc_search *) searches;
@@ -199,43 +215,6 @@ static u32 count_wrapped_lines(wstr *w, u32 width)
return wraps;
}
-static void date_to_relative_date(struct sho_post *post, char *out)
-{
- struct sho_post_date *created = NULL;
- struct sho_post_date *date;
- al_array_foreach_ptr(post->dates, i, date) {
- if (date->type == SHOKI_DATE_CREATED) {
- created = date;
- break;
- }
- }
- if (!created) return;
- f64 seconds = (aki_get_timestamp() / 1000000.0 - created->timestamp);
- s64 minutes = seconds / 60L;
- s64 hours = minutes / 60L;
- s64 days = hours / 24L;
- if (days > 0) {
- al_sprintf(out, "%lud", days);
- } else if (hours > 0) {
- al_sprintf(out, "%luh", hours);
- } else if (minutes > 0) {
- al_sprintf(out, "%lum", minutes);
- } else {
- al_sprintf(out, "%lus", (s64)seconds);
- }
-}
-
-static void number_to_shorthand(s64 number, char *out)
-{
- if (number >= 1000000) {
- al_sprintf(out, "%.1fM", number / 1000000.f);
- } else if (number >= 1000) {
- al_sprintf(out, "%.1fK", number / 1000.f);
- } else {
- al_sprintf(out, "%ld", number);
- }
-}
-
#define TEXT_OFFSET 10
#define PROFILE_SIZE 5
@@ -259,7 +238,7 @@ static u32 layout_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, u32
}
view->preview.width = view->text.width - 1;
- view->preview.x = TEXT_OFFSET + 1;
+ view->preview.x = TEXT_OFFSET;
u32 partition_width = view->preview.width / 2;
struct ncplane_options opts = { 0 };
@@ -318,15 +297,48 @@ static u32 layout_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, u32
return rows + view->preview.height;
}
-static char format_buf[32];
+static void date_to_relative_date(struct sho_post *post, char *out)
+{
+ struct sho_post_date *created = NULL;
+ struct sho_post_date *date;
+ al_array_foreach_ptr(post->dates, i, date) {
+ if (date->type == SHOKI_DATE_CREATED) {
+ created = date;
+ break;
+ }
+ }
+ if (!created) return;
+ f64 seconds = (aki_get_timestamp() / 1000000.0 - created->timestamp);
+ s64 minutes = seconds / 60L;
+ s64 hours = minutes / 60L;
+ s64 days = hours / 24L;
+ if (days > 0) {
+ al_sprintf(out, "%lud", days);
+ } else if (hours > 0) {
+ al_sprintf(out, "%luh", hours);
+ } else if (minutes > 0) {
+ al_sprintf(out, "%lum", minutes);
+ } else {
+ al_sprintf(out, "%lus", (s64)seconds);
+ }
+}
-static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struct ncplane *container, u32 rows, u32 width)
+static void number_to_shorthand(s64 number, char *out)
{
- struct sho_post *post = view->v.post;
- struct sho_post *repost = view->repost;
+ if (number >= 1000000000) {
+ al_sprintf(out, "%.1fB", number / 1000000000.f);
+ } else if (number >= 1000000) {
+ al_sprintf(out, "%.1fM", number / 1000000.f);
+ } else if (number >= 1000) {
+ al_sprintf(out, "%.1fK", number / 1000.f);
+ } else {
+ al_sprintf(out, "%ld", number);
+ }
+}
- ncplane_resize_simple(container, rows, width);
+static void draw_tweet_resources(struct cmc_tui *tui, struct cmc_view_twitter *view)
+{
u32 partition_width = view->preview.width / 2;
struct cmc_view_resource *resource;
switch (view->preview.mode) {
@@ -364,7 +376,7 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
if (view->preview.resources[3]) {
resource = view->preview.resources[3];
ncplane_resize_simple(view->preview.p[3], resource->geom.rcelly + 1, partition_width);
- ncplane_move_yx(view->preview.p[3], view->preview.resources[2]->geom.rcelly + view->text.height + 2, (view->preview.x + view->preview.resources[2]->geom.rcellx) + 3);
+ ncplane_move_yx(view->preview.p[3], view->preview.resources[1]->geom.rcelly + view->text.height + 2, (view->preview.x + view->preview.resources[2]->geom.rcellx) + 3);
resource->vopts.n = view->preview.p[3];
ncvisual_blit(tui->nc, resource->v, &resource->vopts);
}
@@ -373,6 +385,24 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
}
}
+ if (view->profile.resource && view->profile.resource->v) {
+ struct cmc_view_resource *resource = view->profile.resource;
+ ncplane_resize_simple(view->profile.p, (PROFILE_SIZE + 1) / 2, PROFILE_SIZE);
+ ncplane_move_yx(view->profile.p, view->text.y, PROFILE_SIZE - 2);
+ resource->vopts.n = view->profile.p;
+ ncvisual_blit(tui->nc, resource->v, &resource->vopts);
+ }
+}
+
+static char format_buf[32];
+
+static void draw_tweet(struct cmc_view_twitter *view, struct ncplane *container, u32 rows, u32 width)
+{
+ struct sho_post *post = view->v.post;
+ struct sho_post *repost = view->repost;
+
+ ncplane_resize_simple(container, rows, width);
+
ncplane_resize_simple(view->text.p, view->text.height, width);
ncplane_move_yx(view->text.p, 0, 0);
@@ -419,7 +449,7 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
ncplane_move_yx(view->stats.p, rows - 1, TEXT_OFFSET);
u32 part = 10;
- offset = 1;
+ offset = 0;
if (post->comments.set) {
const wchar_t *comment = L"🗩";
@@ -450,14 +480,6 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
number_to_shorthand(post->likes.i, format_buf);
ncplane_putstr_yx(view->stats.p, 0, offset + 2, format_buf);
}
-
- if (view->profile.resource && view->profile.resource->v) {
- struct cmc_view_resource *resource = view->profile.resource;
- ncplane_resize_simple(view->profile.p, (PROFILE_SIZE + 1) / 2, PROFILE_SIZE);
- ncplane_move_yx(view->profile.p, view->text.y, PROFILE_SIZE - 2);
- resource->vopts.n = view->profile.p;
- ncvisual_blit(tui->nc, resource->v, &resource->vopts);
- }
}
static struct cmc_view_page *get_page_from_num(struct cmc_search *search, u32 num)
@@ -477,18 +499,22 @@ static struct cmc_view_page *get_page_containing_index(struct cmc_search *search
if (!(page = get_page_from_num(search, i))) continue;
size = page->views.size;
count += size;
- if (count > index) break;
+ if (count > index) {
+ if (offset) *offset = size - (count - index);
+ return page;
+ }
}
- if (offset) *offset = size - (count - index);
- return page;
+ return NULL;
}
static void request_page_internal(struct cmc *c, u32 num)
{
- c->pending_requests++;
- camu_client_get_page(&c->client, c->tui.search->search, num);
+ struct cmc_search *search = c->tui.search;
+ search->pending_requests++;
+ camu_client_get_page(&c->client, search->search, num);
}
+// Given an index of a post, request the page to the offset of the page containing that post.
static bool request_page_at_page_offset(struct cmc *c, struct cmc_search *search, u32 index, s32 offset)
{
struct cmc_view_page *page = get_page_containing_index(search, index, NULL);
@@ -500,190 +526,283 @@ static bool request_page_at_page_offset(struct cmc *c, struct cmc_search *search
return true;
}
-static bool iterate_views_to_height(struct cmc_search *search, u32 height, u32 anchor,
- u32 *count, void (*func)(void *, struct cmc_view_twitter *, u32), void *userdata)
+static bool iterate_views_to_height(struct cmc_search *search, u32 height, u32 anchor, u32 *count,
+ void (*func)(void *, struct cmc_view_twitter *, u32), void *userdata)
{
u32 y = 0, offset;
- bool out_of_space = false;
struct cmc_view_page *page = get_page_containing_index(search, anchor, &offset);
al_assert(page);
struct cmc_view_twitter *view;
do {
+ if (!page->processed) {
+ // If the page is not processed, return out of space as to not display
+ // a discontinuity (if the page after happens to be processed).
+ return true;
+ }
for (; offset < page->views.size; offset++) {
view = (struct cmc_view_twitter *)al_array_at(page->views, offset);
- if (y + view->v.rows > height) {
- out_of_space = true;
- break;
- }
+ // If this post would draw past the height, return out of space.
+ if (y + view->v.rows > height) return true;
if (func) func(userdata, view, y);
- y += view->v.rows + 1;
(*count)++;
+ y += view->v.rows + 1;
}
- offset = 0;
- } while (!out_of_space && (page = get_page_from_num(search, page->num + 1)));
- return out_of_space;
+ } while ((offset = 0, (page = get_page_from_num(search, page->num + 1))));
+ return false;
}
static void draw_tweet_func(void *userdata, struct cmc_view_twitter *view, u32 y)
{
struct cmc_tui *tui = (struct cmc_tui *)userdata;
- draw_tweet(tui, view, view->v.container, view->v.rows, tui->cols);
+ struct cmc_search *search = tui->search;
ncplane_reparent_family(view->v.container, tui->p);
ncplane_move_yx(view->v.container, y, 0);
+ draw_tweet_resources(tui, view);
view->v.y = y;
+ al_array_push(search->prev, &view->v);
}
-static void draw_selection_line(struct cmc_view_twitter *view, u32 y, u32 rows)
+static void draw_selection_line(struct cmc_view *view, u32 y, u32 rows)
{
- ncplane_move_yx(view->v.container, y, 0);
+ ncplane_move_yx(view->container, y, 0);
wchar_t box = L'â–ˆ';
- u32 k = view->repost ? 1 : 0;
+ u32 k = ((struct cmc_view_twitter *)view)->repost ? 1 : 0;
for (; k < rows - 1; k++) {
- ncplane_cursor_move_yx(view->v.container, k, 0);
- ncplane_putwc(view->v.container, box);
+ ncplane_cursor_move_yx(view->container, k, 0);
+ ncplane_putwc(view->container, box);
}
}
-static struct cmc_view_twitter *get_view_by_visual_index(struct cmc_search *search, s32 index)
+static struct cmc_view *get_view_by_visual_index(struct cmc_search *search, s32 index)
{
u32 offset;
struct cmc_view_page *page = get_page_containing_index(search, search->anchor + index, &offset);
- if (!page) return NULL;
- return (struct cmc_view_twitter *)al_array_at(page->views, offset);
+ if (!page || !page->processed) return NULL;
+ return al_array_at(page->views, offset);
}
-static void layout_tweets(struct cmc *c, struct cmc_tui *tui, struct cmc_search *search)
+static void draw_overlay(struct cmc_tui *tui)
{
- struct cmc_view_page *page;
- struct cmc_view_twitter *view;
- al_array_foreach(search->pages, i, page) {
- if (page->processed) continue;
- for (u32 j = 0; j < page->views.size; j++) {
- view = (struct cmc_view_twitter *)al_array_at(page->views, j);
- view->v.rows = layout_tweet(tui, view, tui->cols);
+ ncplane_cursor_move_yx(tui->overlay.p, 0, 0);
+ ncplane_erase(tui->overlay.p);
+
+ u64 color = 0;
+ u32 background = 0;
+ ncchannel_set_rgb8(&background, 255, 255, 255);
+ ncchannel_set_alpha(&background, 255);
+ ncchannels_set_fchannel(&color, background);
+
+ /*
+ struct nccell ul, ur; nccell_init(&ul); nccell_init(&ur);
+ struct nccell ll, lr; nccell_init(&ll); nccell_init(&lr);
+ struct nccell hl, vl; nccell_init(&hl); nccell_init(&vl);
+ //nccell_set_channels(&hl, color);
+ //nccell_set_channels(&vl, color);
+
+ ncplane_box(tui->overlay.p, &ul, &ur, &ll, &lr, &hl, &vl, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+
+ nccell_release(tui->overlay.p, &ul); nccell_release(tui->overlay.p, &ur);
+ nccell_release(tui->overlay.p, &ll); nccell_release(tui->overlay.p, &lr);
+ nccell_release(tui->overlay.p, &hl); nccell_release(tui->overlay.p, &vl);
+ */
+
+ //ncplane_double_box(tui->overlay.p, NCSTYLE_NONE, color, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+ //ncplane_rounded_box(tui->overlay.p, NCSTYLE_NONE, color, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+
+ //ncchannel_set_palindex(&background, 1);
+ ncchannel_set_rgb8(&background, 124, 12, 53);
+ ncchannels_set_bchannel(&color, background);
+ ncchannels_set_fchannel(&color, background);
+ ncplane_gradient(tui->overlay.p, 0, 0, tui->overlay.rows, tui->overlay.cols, "", 0, color, color, color, color);
+
+ ncplane_set_bg_rgb8(tui->overlay.p, 124, 12, 53);
+ //ncplane_set_fg_palindex(tui->overlay.p, 4);
+ //ncplane_set_bg_alpha(tui->overlay.p, 0);
+ //ncplane_cursor_move_yx(tui->overlay.p, 1, 1);
+ //ncplane_cursor_move_yx(tui->overlay.p, 0, 1);
+
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ if ((s32)i == tui->overlay.selected) {
+ ncplane_set_fg_palindex(tui->overlay.p, 4);
+ } else {
+ ncplane_set_fg_default(tui->overlay.p);
+ }
+ str *query = &search->search->query;
+ for (u32 j = 0; j < query->len; j++) {
+ ncplane_putchar_yx(tui->overlay.p, i, j + 1, al_str_at(query, j));
}
- page->processed = true;
}
+}
- if (search->find_anchor) {
+static void refresh_pane_search(struct cmc *c, struct cmc_tui *tui, struct cmc_search *search)
+{
+ if (search->find_anchor == SEARCH_UP) {
+ u32 count;
+ // If find_anchor = SEARCH_UP, search->anchor is index of the requested view.
s32 anchor = search->anchor;
- if (anchor < 0) {
- if (!request_page_at_page_offset(c, search, 0, -1)) {
- search->anchor = 0;
- search->selected = 0;
- search->find_anchor = false;
- }
- return;
- }
- u32 count = 1, prev_count;
while (anchor >= 0) {
count = 0;
iterate_views_to_height(search, tui->rows, anchor, &count, NULL, NULL);
+ // Is the requested view still visible?
if ((s32)(anchor + count) - 1 < search->anchor) break;
anchor--;
- if (anchor < 0 && request_page_at_page_offset(c, search, 0, -1)) {
- return;
- }
- prev_count = count;
+ // Load more posts if needed.
+ if (anchor < 0 && request_page_at_page_offset(c, search, 0, -1)) return;
}
- search->anchor = anchor + 1;
- search->prev_selected = -1;
- search->selected = prev_count - 1;
- search->find_anchor = false;
+ anchor++; // We want the anchor before the last attempt above.
+ search->selected = abs(search->anchor - anchor);
+ search->anchor = anchor;
+ search->redraw = true;
+ search->find_anchor = SEARCH_NONE;
+ } else if (search->find_anchor == SEARCH_DOWN) {
+ // When moving down we already know how many views are being displayed
+ // so just move the anchor 1 past that.
+ search->selected = 0;
+ search->anchor += search->count;
+ search->redraw = true;
+ search->find_anchor = SEARCH_NONE;
}
+ if (tui->overlay.active) draw_overlay(tui);
+
+ struct cmc_view *view;
bool out_of_space = true;
- if (search->prev_anchor == -1 || search->anchor != search->prev_anchor) {
- al_array_foreach(search->pages, i, page) {
- for (u32 j = 0; j < page->views.size; j++) {
- view = (struct cmc_view_twitter *)al_array_at(page->views, j);
- ncplane_erase(view->v.container);
- ncplane_reparent_family(view->v.container, view->v.container);
- }
+ if (search->redraw) {
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
+ ncplane_reparent_family(view->container, view->container);
}
+ search->prev.size = 0;
search->count = 0;
out_of_space = iterate_views_to_height(search, tui->rows, search->anchor, &search->count, draw_tweet_func, tui);
- notcurses_refresh(tui->nc, NULL, NULL);
- } else if (search->prev_selected >= 0) {
- view = get_view_by_visual_index(search, search->prev_selected);
- if (view) {
- ncplane_erase(view->v.container);
+ } else {
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
}
}
- view = get_view_by_visual_index(search, search->selected);
- if (view) {
- draw_selection_line(view, view->v.y, view->v.rows);
+ if ((view = get_view_by_visual_index(search, search->selected))) {
+ draw_selection_line(view, view->y, view->rows);
+ }
+ if (search->redraw) {
+ search->redraw = false;
+ notcurses_refresh(tui->nc, NULL, NULL);
}
notcurses_render(tui->nc);
- if (search->count > 0 && !out_of_space && search->end_of_page == -1) {
- // Index is the last displayed view.
+ if (search->pending_requests == 0 && search->count > 0 && !out_of_space && search->end_of_page == -1) {
request_page_at_page_offset(c, search, (search->anchor + search->count) - 1, 1);
- search->prev_anchor = -1;
- } else {
- search->prev_anchor = search->anchor;
+ }
+}
+
+static void maybe_process_pages(struct cmc *c)
+{
+ struct cmc_tui *tui = &c->tui;
+ bool do_redraw = false;
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ struct cmc_view_page *page;
+ al_array_foreach(search->pages, j, page) {
+ if (page->processed) continue;
+ struct cmc_view *view;
+ bool any_pending_requests = false;
+ al_array_foreach(page->views, k, view) {
+ if (view->pending_requests > 0) {
+ any_pending_requests = true;
+ break;
+ }
+ }
+ if (any_pending_requests) continue;
+ do_redraw = true;
+ al_array_foreach(page->views, k, view) {
+ view->rows = layout_tweet(tui, (struct cmc_view_twitter *)view, tui->cols);
+ draw_tweet((struct cmc_view_twitter *)view, view->container, view->rows, tui->cols);
+ }
+ page->processed = true;
+ }
+ }
+ search = tui->search;
+ if (do_redraw) {
+ search->redraw = true;
+ refresh_pane_search(c, tui, search);
}
}
static void resource_client_callback(void *userdata, u16 id, struct aki_buffer *buffer)
{
struct cmc *c = (struct cmc *)userdata;
- struct cmc_tui *tui = &c->tui;
struct cmc_view_resource *resource;
al_array_foreach(c->resource_cache, i, resource) {
if (resource->id == id) {
s32 w, h, channels;
- u8 *pixels = stbi_load_from_memory(aki_buffer_get_ptr(buffer, 0), buffer->size, &w, &h, &channels, 4);
- resource->v = ncvisual_from_rgba(pixels, h, w * 4, w);
+ u8 *pixels = stbi_load_from_memory(aki_buffer_get_ptr(buffer, 0), buffer->size, &w, &h, &channels, 0);
+ if (channels == 3) {
+ resource->v = ncvisual_from_rgb_packed(pixels, h, w * channels, w, 255);
+ } else if (channels == 4) {
+ resource->v = ncvisual_from_rgba(pixels, h, w * channels, w);
+ } else {
+ al_log_warn("cmc", "Image has unsupported channel count.");
+ }
stbi_image_free(pixels);
ncvisual_geom(c->tui.nc, resource->v, &resource->vopts, &resource->geom);
+ struct cmc_view *view;
+ al_array_foreach(resource->refs, j, view) {
+ view->pending_requests--;
+ }
+ al_array_free(resource->refs);
break;
}
}
- if (--c->pending_requests == 0) layout_tweets(c, tui, tui->search);
+ maybe_process_pages(c);
}
static void add_selected_from_index(struct cmc *c, struct cmc_search *search, u32 index)
{
- struct cmc_view_twitter *view = get_view_by_visual_index(search, search->selected);
- if (view && view->v.post->media.size > index) {
- camu_client_add(&c->client, &view->v.post->unique_id, index);
+ struct cmc_view *view = get_view_by_visual_index(search, search->selected);
+ if (view && view->post->media.size > index) {
+ camu_client_add(&c->client, &view->post->unique_id, index);
camu_client_skip(&c->client, 1);
}
}
-static bool on_last_page(struct cmc_search *search)
-{
- return search->end_of_page >= 0 && search->anchor + search->count > (u32)search->end_of_page;
-}
+#define ON_LAST_PAGE(search) \
+ (search->end_of_page >= 0 && search->anchor + search->count > (u32)search->end_of_page)
static bool handle_input_search(struct cmc *c, struct cmc_search *search, struct ncinput *input)
{
if (!(input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN)) return false;
if (input->id == 'j') {
if (search->selected + 1 >= (s32)search->count) {
- if (c->pending_requests != 0 || on_last_page(search)) return false;
- search->anchor += search->count;
- search->prev_selected = -1;
- search->selected = 0;
+ if (search->pending_requests > 0 || search->find_anchor != SEARCH_NONE) return false;
+ if (ON_LAST_PAGE(search)) return false;
+ u32 index = search->anchor + search->count;
+ search->find_anchor = SEARCH_DOWN;
+ struct cmc_view_page *page = get_page_containing_index(search, index, NULL);
+ if (!page) {
+ request_page_at_page_offset(c, search, index - 1, 1);
+ return false;
+ }
} else {
- search->prev_selected = search->selected;
search->selected++;
}
return true;
} else if (input->id == 'k') {
if (search->selected - 1 < 0) {
- if (c->pending_requests != 0) return false;
+ if (search->pending_requests > 0 || search->find_anchor != SEARCH_NONE) return false;
+ if (search->anchor - 1 < 0 && !request_page_at_page_offset(c, search, 0, -1)) {
+ // The reason to do this here instead of letting it resolve in
+ // refresh_pane_search() is so we can return early if
+ // we are at the top of the page.
+ return false;
+ }
search->anchor--;
- search->find_anchor = true;
- search->prev_selected = -1;
+ search->find_anchor = SEARCH_UP;
+ // Wait for the requested previous page to come in.
+ if (search->anchor < 0) return false;
} else {
- search->prev_selected = search->selected;
search->selected--;
}
return true;
- } else if (input->id == NCKEY_RETURN) {
- add_selected_from_index(c, search, 0);
- } else if (input->id == '1') {
+ } else if (input->id == NCKEY_RETURN || input->id == '1') {
add_selected_from_index(c, search, 0);
} else if (input->id == '2') {
add_selected_from_index(c, search, 1);
@@ -695,15 +814,60 @@ static bool handle_input_search(struct cmc *c, struct cmc_search *search, struct
return false;
}
+static void toggle_overlay_internal(struct cmc_tui *tui)
+{
+ if (tui->overlay.active) {
+ ncplane_reparent_family(tui->overlay.p, tui->overlay.p);
+ } else {
+ ncplane_reparent_family(tui->overlay.p, tui->p);
+ }
+ tui->overlay.active = !tui->overlay.active;
+}
+
+static bool handle_input_overlay(struct cmc *c, struct cmc_tui *tui, struct ncinput *input)
+{
+ if (input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN) {
+ if (input->id == 'j') {
+ if (tui->overlay.selected + 1 < (s32)tui->searches.size) {
+ tui->overlay.selected++;
+ return true;
+ }
+ } else if (input->id == 'k') {
+ if (tui->overlay.selected - 1 >= 0) {
+ tui->overlay.selected--;
+ return true;
+ }
+ } else if (input->id == NCKEY_RETURN) {
+ toggle_overlay_internal(tui);
+ struct cmc_search *search = tui->search;
+ struct cmc_view *view;
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
+ ncplane_reparent_family(view->container, view->container);
+ }
+ search->prev.size = 0;
+ tui->search = al_array_at(tui->searches, tui->overlay.selected);
+ search = tui->search;
+ search->redraw = true;
+ u32 last_page = search->search->last_page;
+ if (!get_page_from_num(search, last_page)) {
+ request_page_internal(c, last_page);
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
static void input_poll_callback(void *userdata, s32 revents)
{
struct cmc *c = (struct cmc *)userdata;
struct cmc_tui *tui = &c->tui;
- struct cmc_search *search = tui->search;
(void)revents;
struct ncinput input;
u32 ret;
- bool do_layout = false;
+ bool do_refresh = false;
do {
ret = notcurses_get_nblock(c->tui.nc, &input);
if (ret == (u32)-1 || ret == 0) break;
@@ -711,11 +875,17 @@ static void input_poll_callback(void *userdata, s32 revents)
if (input.id == 'q') {
aki_event_loop_break(&c->loop);
break;
+ } else if (input.id == NCKEY_TAB) {
+ toggle_overlay_internal(tui);
+ do_refresh = true;
+ tui->search->redraw = true;
+ continue;
}
- if (search) do_layout = handle_input_search(c, search, &input);
+ if (tui->overlay.active) do_refresh |= handle_input_overlay(c, tui, &input);
+ else if (tui->search) do_refresh |= handle_input_search(c, tui->search, &input);
}
} while (1);
- if (do_layout && c->pending_requests == 0) layout_tweets(c, tui, search);
+ if (do_refresh) refresh_pane_search(c, tui, tui->search);
}
static struct cmc_view_resource *create_resource(struct cmc *c, str *unique_id, u32 index)
@@ -727,12 +897,13 @@ static struct cmc_view_resource *create_resource(struct cmc *c, str *unique_id,
resource->id = al_rand_u16();
al_str_clone(&resource->unique_id, unique_id);
resource->index = index;
- c->pending_requests++;
+ al_array_init(resource->refs);
camu_resource_request(&c->resource_client, unique_id, index, resource->id);
return resource;
}
-static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, str *unique_id, u32 index)
+static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, struct cmc_view *view,
+ str *unique_id, u32 index)
{
struct cmc_view_resource *resource;
al_array_foreach(c->resource_cache, i, resource) {
@@ -740,7 +911,10 @@ static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, str *uni
return resource;
}
}
- return create_resource(c, unique_id, index);
+ resource = create_resource(c, unique_id, index);
+ view->pending_requests++;
+ al_array_push(resource->refs, view);
+ return resource;
}
static void get_post_and_maybe_repost(struct cmc *c, str *unique_id, struct sho_post **post, struct sho_post **repost)
@@ -753,11 +927,31 @@ static void get_post_and_maybe_repost(struct cmc *c, str *unique_id, struct sho_
}
}
+static void strip_link_from_tweet(struct sho_post *post)
+{
+ wstr *check = al_wstr_c(L"https://t.co/");
+ s32 index = al_wstr_rfind(&post->text, L':');
+ if (index < 5) return;
+ index -= 5; // [h]ttps:// (if matched).
+ if (al_wstr_cmp(&post->text, check, index, check->len) == 0) {
+ al_wstr_at(&post->text, index) = L'\0';
+ post->text.len = index;
+ }
+}
+
static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_id)
{
struct cmc_view_twitter *view = al_alloc_object(struct cmc_view_twitter);
get_post_and_maybe_repost(c, unique_id, &view->v.post, &view->repost);
+ if (view->v.post->media.size > 0) {
+ strip_link_from_tweet(view->v.post);
+ }
+
+ view->v.rows = 0;
+ view->v.y = 0;
+ view->v.pending_requests = 0;
+
struct ncplane_options default_options = { 0 };
default_options.cols = 1;
default_options.rows = 1;
@@ -773,11 +967,11 @@ static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_i
view->stats.p = ncplane_create(view->v.container, &default_options);
struct sho_post *post = view->v.post;
- view->profile.resource = get_resource_from_cache(c, &post->author.unique_id, 0);
+ view->profile.resource = get_resource_from_cache(c, &view->v, &post->author.unique_id, 0);
struct sho_post_media *media;
al_array_foreach_ptr(post->media, i, media) {
(void)media;
- view->preview.resources[i] = get_resource_from_cache(c, &post->unique_id, i);
+ view->preview.resources[i] = get_resource_from_cache(c, &view->v, &post->unique_id, i);
}
switch (post->media.size) {
@@ -792,9 +986,6 @@ static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_i
break;
}
- view->v.rows = 0;
- view->v.y = 0;
-
return view;
}
@@ -802,14 +993,15 @@ static struct cmc_search *create_search(struct camu_search *s)
{
struct cmc_search *search = al_alloc_object(struct cmc_search);
search->search = s;
- search->find_anchor = false;
+ search->find_anchor = SEARCH_NONE;
search->anchor = 0;
- search->prev_anchor = -1;
+ search->redraw = true;
search->selected = 0;
- search->prev_selected = -1;
+ al_array_init(search->prev);
search->max_page_num = s->last_page;
search->min_page_num = s->last_page;
search->end_of_page = -1;
+ search->pending_requests = 0;
al_array_init(search->pages);
return search;
}
@@ -826,10 +1018,24 @@ static s32 resize_cb(struct ncplane *p)
nopts.y = 1;
nopts.flags = 0;
tui->p = ncplane_create(p, &nopts);
- // TODO: Consider layout.
+ tui->overlay.cols = nopts.cols = 35;
+ tui->overlay.rows = nopts.rows = 20;
+ nopts.x = (tui->cols / 2) - (tui->overlay.cols / 2);
+ nopts.y = (tui->rows / 2) - (tui->overlay.rows / 2);
+ tui->overlay.p = ncpile_create(tui->nc, &nopts);
+ //ncplane_reparent_family(tui->overlay.p, tui->p);
return 0;
}
+static struct cmc_search *get_search_from_id(struct cmc_tui *tui, s32 id)
+{
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ if (search->search->id == id) return search;
+ }
+ return NULL;
+}
+
static void client_callback(void *userdata, u8 op, void *opaque)
{
struct cmc *c = (struct cmc *)userdata;
@@ -854,6 +1060,8 @@ static void client_callback(void *userdata, u8 op, void *opaque)
} else {
tui->search = NULL;
}
+ tui->overlay.active = false;
+ tui->overlay.selected = 0;
struct ncplane *stdplane = notcurses_stdplane(tui->nc);
ncplane_set_resizecb(stdplane, resize_cb);
ncplane_set_userptr(stdplane, tui);
@@ -897,8 +1105,10 @@ static void client_callback(void *userdata, u8 op, void *opaque)
break;
}
case CAMU_CLIENT_RESULTS: {
- struct cmc_search *search = tui->search; // wrong
struct camu_result_list *results = (struct camu_result_list *)opaque;
+ struct cmc_search *search = get_search_from_id(tui, results->search->id);
+ if (!search) return;
+ search->pending_requests--;
struct cmc_view_page *page = al_alloc_object(struct cmc_view_page);
al_array_push(search->pages, page);
@@ -906,7 +1116,7 @@ static void client_callback(void *userdata, u8 op, void *opaque)
page->processed = false;
if (page->num > search->max_page_num) search->max_page_num = page->num;
if (page->num < search->min_page_num) search->min_page_num = page->num;
- if (search->find_anchor) {
+ if (search->find_anchor == SEARCH_UP) {
search->anchor += results->unique_ids.size;
if (search->end_of_page >= 0) {
search->end_of_page += results->unique_ids.size;
@@ -917,17 +1127,19 @@ static void client_callback(void *userdata, u8 op, void *opaque)
struct cmc_view_twitter *view;
al_array_foreach_ptr(results->unique_ids, i, unique_id) {
view = create_view_twitter(c, unique_id);
- al_array_push(page->views, (struct cmc_view *)view);
+ al_array_push(page->views, &view->v);
}
- if (--c->pending_requests == 0) layout_tweets(c, tui, search);
+ maybe_process_pages(c);
break;
}
case CAMU_CLIENT_END_OF_RESULTS: {
- struct cmc_search *search = tui->search; // wrong
+ struct cmc_search *search = get_search_from_id(tui, *((s32 *)opaque));
+ if (!search) return;
+ search->pending_requests--;
search->end_of_page = (search->anchor + search->count) - 1;
- if (--c->pending_requests == 0) layout_tweets(c, tui, search);
+ maybe_process_pages(c);
break;
}
case CAMU_CLIENT_DISCONNECTED:
@@ -984,9 +1196,7 @@ s32 main(s32 argc, char *argv[])
if (c.type == CMC_SEARCH) {
camu_client_create_search(&c.client, &module, &query);
}
- }
-
- if (c.action == CMC_ACTION_OPEN) {
+ } else if (c.action == CMC_ACTION_OPEN) {
c.pending_requests = 0;
camu_resource_client_init(&c.resource_client, &c.loop, resource_client_callback, &c);
camu_resource_client_connect(&c.resource_client, TREE_SERVER_IP, TREE_RESOURCE_PORT);