diff options
| author | 2024-02-02 22:57:22 -0500 | |
|---|---|---|
| committer | 2024-02-02 22:57:22 -0500 | |
| commit | 0b7a612a9baf49765db8b127e696913100b70f5f (patch) | |
| tree | 78680777023d989b9148dd98a5a6e489e49b022a /src/fruits/cmc | |
| parent | b100eb175e0cfa59a15c4125f26ab5474c2347ec (diff) | |
| download | camu-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.c | 562 |
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); |