summaryrefslogtreecommitdiff
path: root/html.c
diff options
context:
space:
mode:
authorGravatar violette2025-12-18 07:58:24 +0100
committerGravatar grunfink2025-12-18 07:58:24 +0100
commit85ed0eb0d535700a5df837c37f51848811e461a0 (patch)
tree818c80cc48c445b2950796230946c63eca3a3242 /html.c
parentBumped version. (diff)
downloadsnac2-85ed0eb0d535700a5df837c37f51848811e461a0.tar.gz
snac2-85ed0eb0d535700a5df837c37f51848811e461a0.tar.xz
snac2-85ed0eb0d535700a5df837c37f51848811e461a0.zip
Added emoji reactions (contributed by violette).
Diffstat (limited to 'html.c')
-rw-r--r--html.c317
1 files changed, 305 insertions, 12 deletions
diff --git a/html.c b/html.c
index 8f7c4a9..8cc0067 100644
--- a/html.c
+++ b/html.c
@@ -54,9 +54,10 @@ int login(snac *user, const xs_dict *headers)
54 return logged_in; 54 return logged_in;
55} 55}
56 56
57 57xs_str *_replace_shortnames(xs_str *s, const xs_list *tag, int ems,
58xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *proxy) 58 const char *proxy, const xs_list *cl, const char *act)
59/* replaces all the :shortnames: with the emojis in tag */ 59/* replace but also adds a class list and an actor in its alt text.
60 * Used for emoji reactions */
60{ 61{
61 if (!xs_is_null(tag)) { 62 if (!xs_is_null(tag)) {
62 xs *tag_list = NULL; 63 xs *tag_list = NULL;
@@ -69,11 +70,15 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
69 tag_list = xs_dup(tag); 70 tag_list = xs_dup(tag);
70 } 71 }
71 72
72 xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); 73 xs *style = xs_fmt("max-height: %dem; max-width: %dem;", ems, ems);
73 xs *class = xs_fmt("snac-emoji snac-emoji-%d-em", ems); 74 xs *class = xs_fmt("snac-emoji snac-emoji-%d-em", ems);
75 if (cl)
76 class = xs_str_cat(class, " ", xs_join(cl, " "));
74 77
75 const xs_dict *v;
76 int c = 0; 78 int c = 0;
79 const xs_val *v;
80
81 c = 0;
77 82
78 xs_set rep_emoji; 83 xs_set rep_emoji;
79 xs_set_init(&rep_emoji); 84 xs_set_init(&rep_emoji);
@@ -100,6 +105,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
100 if (!xs_is_string(mt)) 105 if (!xs_is_string(mt))
101 mt = xs_mime_by_ext(u); 106 mt = xs_mime_by_ext(u);
102 107
108 act = act ? xs_fmt("%s\n%s", n, act) : xs_fmt("%s", n);
109
103 if (strcmp(mt, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg"))) 110 if (strcmp(mt, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg")))
104 s = xs_replace_i(s, n, ""); 111 s = xs_replace_i(s, n, "");
105 else { 112 else {
@@ -108,8 +115,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
108 xs_html *img = xs_html_sctag("img", 115 xs_html *img = xs_html_sctag("img",
109 xs_html_attr("loading", "lazy"), 116 xs_html_attr("loading", "lazy"),
110 xs_html_attr("src", url), 117 xs_html_attr("src", url),
111 xs_html_attr("alt", n), 118 xs_html_attr("alt", act),
112 xs_html_attr("title", n), 119 xs_html_attr("title", act),
113 xs_html_attr("class", class), 120 xs_html_attr("class", class),
114 xs_html_attr("style", style)); 121 xs_html_attr("style", style));
115 122
@@ -130,6 +137,13 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
130} 137}
131 138
132 139
140xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *proxy)
141/* replaces all the :shortnames: with the emojis in tag */
142{
143 return _replace_shortnames(s, tag, ems, proxy, NULL, NULL);
144}
145
146
133xs_str *actor_name(xs_dict *actor, const char *proxy) 147xs_str *actor_name(xs_dict *actor, const char *proxy)
134/* gets the actor name */ 148/* gets the actor name */
135{ 149{
@@ -430,6 +444,52 @@ void html_note_render_visibility(snac* user, xs_html *form, const int scope)
430 xs_html_add(form, paragraph); 444 xs_html_add(form, paragraph);
431} 445}
432 446
447/* html_note but moddled for emoji's needs. here and not bellow, since the
448 * other one is already so complex. */
449xs_html *html_emoji(snac *user, const char *summary,
450 const char *div_id, const char *form_id,
451 const char* placeholder, const char *post_id,
452 const char* eid)
453{
454 xs *action = xs_fmt("%s/admin/action", user->actor);
455
456 xs_html *form;
457 const int react = eid == NULL ? 0 : 1;
458
459 xs_html *note = xs_html_tag("div",
460 xs_html_tag("details",
461 xs_html_tag("summary",
462 xs_html_text(summary)),
463 xs_html_tag("p", NULL),
464 xs_html_tag("div",
465 xs_html_attr("class", "snac-note"),
466 xs_html_attr("id", div_id),
467 form = xs_html_tag("form",
468 xs_html_attr("autocomplete", "off"),
469 xs_html_attr("method", "post"),
470 xs_html_attr("action", action),
471 xs_html_attr("enctype", "multipart/form-data"),
472 xs_html_attr("id", form_id),
473 xs_html_sctag("input",
474 xs_html_attr("type", "hidden"),
475 xs_html_attr("name", "id"),
476 xs_html_attr("value", post_id)),
477 xs_html_sctag("input",
478 xs_html_attr("type", react ? "hidden" : "text"),
479 xs_html_attr("name", "eid"),
480 xs_html_attr(react ? "value" : "placeholder", react ? eid : placeholder)),
481 xs_html_text(" "),
482 xs_html_sctag("input",
483 xs_html_attr("type", "submit"),
484 xs_html_attr("name", "action"),
485 xs_html_attr("eid", "action"),
486 xs_html_attr("value", react ? L("EmojiUnreact") : L("EmojiReact"))),
487 xs_html_text(" "),
488 xs_html_tag("p", NULL)))));
489
490 return note;
491}
492
433xs_html *html_note(snac *user, const char *summary, 493xs_html *html_note(snac *user, const char *summary,
434 const char *div_id, const char *form_id, 494 const char *div_id, const char *form_id,
435 const char *ta_plh, const char *ta_content, 495 const char *ta_plh, const char *ta_content,
@@ -1356,6 +1416,28 @@ xs_html *html_top_controls(snac *user)
1356 xs_html_attr("value", L("Like"))), 1416 xs_html_attr("value", L("Like"))),
1357 xs_html_text(" "), 1417 xs_html_text(" "),
1358 xs_html_text(L("(by URL)"))), 1418 xs_html_text(L("(by URL)"))),
1419 xs_html_tag("form",
1420 xs_html_attr("autocomplete", "off"),
1421 xs_html_attr("method", "post"),
1422 xs_html_attr("action", ops_action),
1423 xs_html_sctag("input",
1424 xs_html_attr("type", "text"),
1425 xs_html_attr("name", "eid"),
1426 xs_html_attr("required", "required"),
1427 xs_html_attr("placeholder", ":neocat:")),
1428 xs_html_text(" "),
1429 xs_html_sctag("input",
1430 xs_html_attr("type", "text"),
1431 xs_html_attr("name", "id"),
1432 xs_html_attr("required", "required"),
1433 xs_html_attr("placeholder", "https:/" "/fedi.example.com/bob/...")),
1434 xs_html_text(" "),
1435 xs_html_sctag("input",
1436 xs_html_attr("type", "submit"),
1437 xs_html_attr("name", "action"),
1438 xs_html_attr("value", L("EmojiReact"))),
1439 xs_html_text(" "),
1440 xs_html_text(L("(by URL)"))),
1359 xs_html_tag("p", NULL))); 1441 xs_html_tag("p", NULL)));
1360 1442
1361 /** user settings **/ 1443 /** user settings **/
@@ -2019,6 +2101,21 @@ xs_html *html_entry_controls(snac *user, const char *actor,
2019 xs_html_tag("p", NULL)); 2101 xs_html_tag("p", NULL));
2020 } 2102 }
2021 2103
2104 { /** emoji react **/
2105 /* the post textarea */
2106 xs *div_id = xs_fmt("%s_reply", md5);
2107 xs *form_id = xs_fmt("%s_reply_form", md5);
2108
2109 xs_html_add(controls, xs_html_tag("div",
2110 xs_html_tag("p", NULL),
2111 html_emoji(
2112 user, L("Emoji react"),
2113 div_id, form_id,
2114 ":neocat:", id,
2115 emoji_reacted(user, id))),
2116 xs_html_tag("p", NULL));
2117 }
2118
2022 { /** reply **/ 2119 { /** reply **/
2023 /* the post textarea */ 2120 /* the post textarea */
2024 xs *ct = build_mentions(user, msg); 2121 xs *ct = build_mentions(user, msg);
@@ -2345,6 +2442,168 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2345 xs_html_add(snac_content_wrap, 2442 xs_html_add(snac_content_wrap,
2346 snac_content); 2443 snac_content);
2347 2444
2445 /* add all emoji reacts */
2446 int is_emoji = 0;
2447 {
2448 int c = 0;
2449 const xs_dict *k;
2450 xs *ls = xs_list_new();
2451 xs *sfrl = xs_dict_new();
2452 xs *rl = object_get_emoji_reacts(id);
2453
2454 xs_dict *m = NULL;
2455 while (xs_list_next(rl, &v, &c)) {
2456 if (valid_status(object_get_by_md5(v, &m))) {
2457 const char *content = xs_dict_get(m, "content");
2458 const char *actor = xs_dict_get(m, "actor");
2459 const xs_list *contentl = xs_dict_get(sfrl, content);
2460 xs *actors = xs_list_new();
2461 actors = xs_list_append(actors, actor);
2462 char me = actor && user && strcmp(actor, user->actor) == 0;
2463 int count = 1;
2464
2465 if (contentl) {
2466 count = atoi(xs_list_get(contentl, 0)) + 1;
2467 const xs_list *actorsc = xs_list_get(contentl, 1);
2468 if (strncmp(xs_list_get(contentl, 2), "1", 1) == 0)
2469 me = 1;
2470
2471 if (xs_list_in(actorsc, actor) != -1) {
2472 xs_free(actors);
2473 actors = xs_dup(actorsc);
2474 }
2475 else
2476 actors = xs_list_cat(actors, actorsc);
2477 }
2478
2479 xs *fl = xs_list_new();
2480 fl = xs_list_append(fl, xs_fmt("%d", count), actors, xs_fmt("%d", me));
2481 sfrl = xs_dict_append(sfrl, content, fl);
2482 }
2483 }
2484
2485 c = 0;
2486
2487 while (xs_list_next(rl, &k, &c)) {
2488 if (valid_status(object_get_by_md5(k, &m))) {
2489 const xs_dict *tag = xs_dict_get(m, "tag");
2490 const xs_dict *ide = xs_dict_get(m, "id");
2491
2492 const char *content = xs_dict_get(m, "content");
2493 const char *shortname;
2494 shortname = xs_dict_get(m, "content");
2495
2496 const xs_list *items = xs_dict_get(sfrl, content);
2497 const char *nb = xs_list_get(items, 0);
2498 const xs_list *actors = xs_list_get(items, 1);
2499 const char me = *xs_list_get(items, 2) == '1';
2500
2501 if (!xs_is_null(nb)) {
2502 is_emoji = 1;
2503
2504 const char *act = atoi(nb) > 1 ?
2505 xs_fmt("%d different actors \n\t%s", atoi(nb), xs_join(actors, ",\n\t")) :
2506 xs_dict_get(m, "actor");
2507
2508 xs *class = xs_list_new();
2509 class = xs_list_append(class, "snac-reaction");
2510
2511 xs_html *ret = NULL;
2512 if (tag && shortname) {
2513 xs *cl = xs_list_new();
2514 cl = xs_list_append(cl, "snac-reaction-image");
2515 xs *emoji = _replace_shortnames(xs_dup(shortname), tag, 2, proxy, cl, act);
2516
2517 if (me)
2518 class = xs_list_append(class, "snac-reacted");
2519
2520 ret = xs_html_tag("button",
2521 xs_html_attr("type", "submit"),
2522 xs_html_attr("name", "action"),
2523 xs_html_attr("value", me ? L("EmojiReact") : L("EmojiUnreact")),
2524 xs_html_raw(emoji),
2525 xs_html_tag("span",
2526 xs_html_raw(nb),
2527 xs_html_attr("style", "padding-left: 5px;")),
2528 xs_html_attr("title", act),
2529 xs_html_attr("class", xs_join(class, " ")));
2530
2531 if (!(ide && xs_startswith(ide, srv_baseurl)))
2532 xs_html_add(ret, xs_html_attr("disabled", "true"));
2533 }
2534 else if (shortname) {
2535 xs *sn = xs_dup(shortname);
2536 const char *sna = sn;
2537 unsigned int utf = xs_utf8_dec((const char **)&sna);
2538
2539 if (xs_is_emoji(utf)) {
2540 const char *style = "font-size: large;";
2541 if (me)
2542 class = xs_list_append(class, "snac-reacted");
2543 ret = xs_html_tag("button",
2544 xs_html_attr("type", "submit"),
2545 xs_html_attr("name", "action"),
2546 xs_html_attr("value", me ? L("EmojiUnreact") : L("EmojiReact")),
2547 xs_html_raw(xs_fmt("&#%d", utf)),
2548 xs_html_tag("span",
2549 xs_html_raw(nb),
2550 xs_html_attr("style", "font-size: initial; padding-left: 5px;")),
2551 xs_html_attr("title", act),
2552 xs_html_attr("class", xs_join(class, " ")),
2553 xs_html_attr("style", style));
2554 }
2555 }
2556 if (ret) {
2557 xs *s1;
2558 if (user) {
2559 xs *action = xs_fmt("%s/admin/action", user->actor);
2560 xs *form_id = xs_fmt("%s_reply_form", md5);
2561
2562 xs_html *form =
2563 xs_html_tag("form",
2564 xs_html_attr("autocomplete", "off"),
2565 xs_html_attr("method", "post"),
2566 xs_html_attr("action", action),
2567 xs_html_attr("enctype", "multipart/form-data"),
2568 xs_html_attr("style", "display: inline-flex;"
2569 "vertical-align: middle;"),
2570 xs_html_attr("id", form_id),
2571 xs_html_sctag("input",
2572 xs_html_attr("type", "hidden"),
2573 xs_html_attr("name", "id"),
2574 xs_html_attr("value", id)),
2575 xs_html_sctag("input",
2576 xs_html_attr("type", "hidden"),
2577 xs_html_attr("name", "eid"),
2578 xs_html_attr("value", shortname)),
2579 ret);
2580 s1 = xs_html_render(form);
2581 }
2582 else
2583 s1 = xs_html_render(ret);
2584
2585 ls = xs_list_append(ls, s1);
2586 sfrl = xs_dict_del(sfrl, content);
2587 }
2588 }
2589 }
2590 }
2591
2592 c = 0;
2593
2594 xs_html *emoji_div;
2595 if (xs_list_len(ls) > 0) {
2596 emoji_div = xs_html_tag("div", xs_html_text(L("Emoji reactions: ")),
2597 xs_html_attr("class", "snac-reaction-div"));
2598
2599 while (ls != NULL && xs_list_next(ls, &k, &c))
2600 xs_html_add(emoji_div, xs_html_raw(k));
2601
2602 xs_html_add(snac_content_wrap, emoji_div);
2603 }
2604
2605 }
2606
2348 { 2607 {
2349 /** build the content string **/ 2608 /** build the content string **/
2350 const char *content = xs_dict_get(msg, "content"); 2609 const char *content = xs_dict_get(msg, "content");
@@ -2371,7 +2630,8 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2371 2630
2372 c = xs_replace_i(c, "<br><br>", "<p>"); 2631 c = xs_replace_i(c, "<br><br>", "<p>");
2373 2632
2374 c = xs_str_cat(c, "<p>"); 2633 if (is_emoji == 0)
2634 c = xs_str_cat(c, "<p>");
2375 2635
2376 /* replace the :shortnames: */ 2636 /* replace the :shortnames: */
2377 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2, proxy); 2637 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2, proxy);
@@ -3686,9 +3946,18 @@ xs_str *html_notifications(snac *user, int skip, int show)
3686 if (strcmp(type, "EmojiReact") == 0 || strcmp(type, "Like") == 0) { 3946 if (strcmp(type, "EmojiReact") == 0 || strcmp(type, "Like") == 0) {
3687 const char *content = xs_dict_get_path(noti, "msg.content"); 3947 const char *content = xs_dict_get_path(noti, "msg.content");
3688 3948
3949 xs *cd = xs_dup(content);
3950 const char *sna = cd;
3951 const xs_dict *tag = xs_dict_get_path(noti, "msg.tag");
3952 unsigned int utf = xs_utf8_dec((const char **)&sna);
3953
3954 int isEmoji = 0;
3955 if (xs_is_emoji(utf) || (tag && xs_list_len(tag) > 0))
3956 isEmoji = 1;
3957
3689 if (xs_type(content) == XSTYPE_STRING) { 3958 if (xs_type(content) == XSTYPE_STRING) {
3690 xs *emoji = replace_shortnames(xs_dup(content), xs_dict_get_path(noti, "msg.tag"), 1, proxy); 3959 xs *emoji = replace_shortnames(xs_dup(content), xs_dict_get_path(noti, "msg.tag"), 1, proxy);
3691 wrk = xs_fmt("%s (%s&#xFE0F;)", type, emoji); 3960 wrk = xs_fmt("%s (%s&#xFE0F;)", isEmoji ? "EmojiReact" : "Like", emoji);
3692 label = wrk; 3961 label = wrk;
3693 } 3962 }
3694 } 3963 }
@@ -4583,8 +4852,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
4583 xs *msg = msg_admiration(&snac, id, *action == 'L' ? "Like" : "Announce"); 4852 xs *msg = msg_admiration(&snac, id, *action == 'L' ? "Like" : "Announce");
4584 4853
4585 if (msg != NULL) { 4854 if (msg != NULL) {
4855 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, *action == 'L' ? 1 : 0, msg);
4586 enqueue_message(&snac, msg); 4856 enqueue_message(&snac, msg);
4587 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, *action == 'L' ? 1 : 0);
4588 4857
4589 status = HTTP_STATUS_SEE_OTHER; 4858 status = HTTP_STATUS_SEE_OTHER;
4590 } 4859 }
@@ -4892,12 +5161,36 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4892 5161
4893 status = HTTP_STATUS_SEE_OTHER; 5162 status = HTTP_STATUS_SEE_OTHER;
4894 5163
5164 if (strcmp(action, L("EmojiUnreact")) == 0) { /** **/
5165 const char *eid = xs_dict_get(p_vars, "eid");
5166
5167 if (eid != NULL) {
5168 xs *n_msg = msg_emoji_unreact(&snac, id, eid);
5169
5170 if (n_msg != NULL)
5171 enqueue_message(&snac, n_msg);
5172 }
5173 }
5174 else
5175 if (strcmp(action, L("EmojiReact")) == 0) { /** **/
5176 const char *eid = xs_dict_get(p_vars, "eid");
5177
5178 eid = xs_strip_chars_i(xs_dup(eid), ":");
5179
5180 const xs_dict *ret = msg_emoji_init(&snac, id, eid);
5181 /* fails if either invalid or already reacted */
5182 if (!ret)
5183 ret = msg_emoji_unreact(&snac, id, eid);
5184 if (!ret)
5185 status = HTTP_STATUS_NOT_FOUND;
5186 }
5187 else
4895 if (strcmp(action, L("Like")) == 0) { /** **/ 5188 if (strcmp(action, L("Like")) == 0) { /** **/
4896 xs *msg = msg_admiration(&snac, id, "Like"); 5189 xs *msg = msg_admiration(&snac, id, "Like");
4897 5190
4898 if (msg != NULL) { 5191 if (msg != NULL) {
5192 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, 1, msg);
4899 enqueue_message(&snac, msg); 5193 enqueue_message(&snac, msg);
4900 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, 1);
4901 } 5194 }
4902 } 5195 }
4903 else 5196 else
@@ -4905,8 +5198,8 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4905 xs *msg = msg_admiration(&snac, id, "Announce"); 5198 xs *msg = msg_admiration(&snac, id, "Announce");
4906 5199
4907 if (msg != NULL) { 5200 if (msg != NULL) {
5201 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, 0, msg);
4908 enqueue_message(&snac, msg); 5202 enqueue_message(&snac, msg);
4909 timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, 0);
4910 } 5203 }
4911 } 5204 }
4912 else 5205 else