add: polls
This commit is contained in:
parent
4dfa09207e
commit
6555324650
29 changed files with 339 additions and 56 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -3275,7 +3275,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tetratto"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"async-stripe",
|
||||
|
@ -3307,7 +3307,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tetratto-core"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"base16ct",
|
||||
|
@ -3332,7 +3332,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tetratto-l10n"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
dependencies = [
|
||||
"pathbufd",
|
||||
"serde",
|
||||
|
@ -3341,7 +3341,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tetratto-shared"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chrono",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tetratto"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -678,6 +678,28 @@ button.camo:hover,
|
|||
color: var(--color-text-lowered);
|
||||
}
|
||||
|
||||
.hover_left_bar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hover_left_bar::after {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 5px;
|
||||
content: "";
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background: var(--color-primary);
|
||||
border-top-left-radius: var(--radius);
|
||||
border-bottom-left-radius: var(--radius);
|
||||
opacity: 0%;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.hover_left_bar:hover::after {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
/* input */
|
||||
input,
|
||||
textarea,
|
||||
|
@ -707,6 +729,12 @@ select:focus {
|
|||
color: var(--color-text-raised);
|
||||
}
|
||||
|
||||
.poll_bar {
|
||||
background-color: var(--color-primary);
|
||||
border-radius: var(--radius);
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/* pillmenu */
|
||||
.pillmenu {
|
||||
display: flex;
|
||||
|
|
|
@ -151,6 +151,13 @@
|
|||
.querySelector(\"button.primary\")
|
||||
.classList.add(\"hidden\");
|
||||
|
||||
// poll
|
||||
const poll_data = get_poll_data();
|
||||
|
||||
if (!poll_data[0]) {
|
||||
return alert(poll_data[1]);
|
||||
}
|
||||
|
||||
// create body
|
||||
const body = new FormData();
|
||||
|
||||
|
@ -167,6 +174,7 @@
|
|||
community: document.getElementById(
|
||||
\"community_to_post_to\",
|
||||
).selectedOptions[0].value,
|
||||
poll: poll_data[1],
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
(text "{{ text \"communities:label.pinned\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in pinned %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {%- endif %} {% endfor %}")))
|
||||
(text "{% for post in pinned %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts, poll=post[4]) }} {%- endif %} {% endfor %}")))
|
||||
(text "{%- endif %}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
|
@ -22,6 +22,6 @@
|
|||
(text "{{ text \"communities:label.posts\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in feed %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=feed|length) }}"))))
|
||||
(text "{% for post in feed %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts, poll=post[4]) }} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=feed|length) }}"))))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
e.preventDefault();
|
||||
await trigger(\"atto::debounce\", [\"posts::create\"]);
|
||||
|
||||
// poll
|
||||
const poll_data = get_poll_data();
|
||||
|
||||
if (!poll_data[0]) {
|
||||
return alert(poll_data[1]);
|
||||
}
|
||||
|
||||
// create body
|
||||
const body = new FormData();
|
||||
|
||||
|
@ -75,6 +82,7 @@
|
|||
content: e.target.content.value,
|
||||
community: community ? community : \"{{ config.town_square }}\",
|
||||
answering,
|
||||
poll: poll_data[1],
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -222,6 +222,9 @@
|
|||
(text "{%- endif %} {%- endif %}"))
|
||||
(text "{{ self::post_media(upload_ids=post.uploads) }}")))
|
||||
(text "{%- endif %}")
|
||||
|
||||
(text "{% if poll -%} {{ self::poll(post=post, poll=poll) }} {%- endif %}")
|
||||
|
||||
(div
|
||||
("class" "flex flex-wrap gap-2 fade")
|
||||
(text "{% for tag in post.context.tags %}")
|
||||
|
@ -1205,6 +1208,14 @@
|
|||
(div
|
||||
("class" "flex gap-2")
|
||||
(text "{{ components::emoji_picker(element_id=\"content\", render_dialog=true) }} {% if not quoting -%} {% if is_supporter -%} {{ components::file_picker(files_list_id=\"files_list\") }} {%- endif %} {%- endif %}")
|
||||
|
||||
(button
|
||||
("class" "small square quaternary")
|
||||
("title" "Add poll")
|
||||
("onclick" "document.getElementById('poll_options_dialog').showModal()")
|
||||
("type" "button")
|
||||
(text "{{ icon \"list-todo\" }}"))
|
||||
|
||||
(button
|
||||
("class" "small square quaternary")
|
||||
("title" "More options")
|
||||
|
@ -1323,4 +1334,155 @@
|
|||
}
|
||||
};"))))
|
||||
|
||||
; poll data manager function and dialog
|
||||
;
|
||||
; `get_poll_data` returns `[bool, string | PollData]`, where the string in arg 1
|
||||
; represents an error message if arg 0 is `false`
|
||||
(script
|
||||
(text "window.POLL_OPTION_A = \"\";
|
||||
window.POLL_OPTION_B = \"\";
|
||||
window.POLL_OPTION_C = \"\";
|
||||
window.POLL_OPTION_D = \"\";
|
||||
|
||||
window.get_poll_data = () => {
|
||||
if (!POLL_OPTION_A && !POLL_OPTION_B) {
|
||||
return [true, null];
|
||||
}
|
||||
|
||||
if (POLL_OPTION_A && !POLL_OPTION_B || POLL_OPTION_B && !POLL_OPTION_A) {
|
||||
return [false, \"At least 2 options are required for a poll\"];
|
||||
}
|
||||
|
||||
return [true, {
|
||||
option_a: POLL_OPTION_A,
|
||||
option_b: POLL_OPTION_B,
|
||||
option_c: POLL_OPTION_C,
|
||||
option_d: POLL_OPTION_D
|
||||
}];
|
||||
}"))
|
||||
|
||||
(dialog
|
||||
("id" "poll_options_dialog")
|
||||
(div
|
||||
("class" "inner flex flex-col gap-2")
|
||||
(div
|
||||
("id" "poll_options")
|
||||
("class" "flex flex-col gap-2")
|
||||
|
||||
(b (text "Attach poll"))
|
||||
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
(b (text "Option A "))
|
||||
(span ("class" "fade red") (text "(required)")))
|
||||
|
||||
(input ("type" "text") ("placeholder" "option A") ("onchange" "window.POLL_OPTION_A = event.target.value")))
|
||||
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(span
|
||||
(b (text "Option B "))
|
||||
(span ("class" "fade red") (text "(required)")))
|
||||
|
||||
(input ("type" "text") ("placeholder" "option B") ("onchange" "window.POLL_OPTION_B = event.target.value")))
|
||||
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(b (text "Option C"))
|
||||
(input ("type" "text") ("placeholder" "option A") ("onchange" "window.POLL_OPTION_C = event.target.value")))
|
||||
|
||||
(div
|
||||
("class" "card flex flex-col gap-2")
|
||||
(b (text "Option D"))
|
||||
(input ("type" "text") ("placeholder" "option D") ("onchange" "window.POLL_OPTION_D = event.target.value"))))
|
||||
(hr)
|
||||
(div
|
||||
("class" "flex justify-between")
|
||||
(div)
|
||||
(div
|
||||
("class" "flex gap-2")
|
||||
(button
|
||||
("class" "bold red quaternary")
|
||||
("onclick" "document.getElementById('poll_options_dialog').close()")
|
||||
("type" "button")
|
||||
(text "{{ icon \"x\" }} {{ text \"dialog:action.close\" }}"))))))
|
||||
|
||||
(text "{%- endmacro %}")
|
||||
(text "{% macro poll(post, poll) -%}")
|
||||
(div
|
||||
("class" "card tertiary w-full flex flex-col gap-2")
|
||||
(text "{% set total = poll[0].votes_a + poll[0].votes_b + poll[0].votes_c + poll[0].votes_d %}")
|
||||
|
||||
(text "{% if poll[1] -%}")
|
||||
; already voted, show results
|
||||
(span ("class" "fade") (text "You've already voted!"))
|
||||
|
||||
; option a
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(span (text "{{ poll[0].option_a }} ({{ poll[0].votes_a }} votes)"))
|
||||
(div ("class" "poll_bar") ("style" "width: {{ (poll[0].votes_a / total) * 100 }}%")))
|
||||
|
||||
; option b
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(span (text "{{ poll[0].option_b }} ({{ poll[0].votes_b }} votes)"))
|
||||
(div ("class" "poll_bar") ("style" "width: {{ (poll[0].votes_b / total) * 100 }}%")))
|
||||
|
||||
; option c
|
||||
(text "{% if poll[0].option_c -%}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(span (text "{{ poll[0].option_c }} ({{ poll[0].votes_c }} votes)"))
|
||||
(div ("class" "poll_bar") ("style" "width: {{ (poll[0].votes_c / total) * 100 }}%")))
|
||||
(text "{%- endif %}")
|
||||
|
||||
; option d
|
||||
(text "{% if poll[0].option_d -%}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(span (text "{{ poll[0].option_d }} ({{ poll[0].votes_d }} votes)"))
|
||||
(div ("class" "poll_bar") ("style" "width: {{ (poll[0].votes_d / total) * 100 }}%")))
|
||||
(text "{%- endif %}")
|
||||
(text "{% else %}")
|
||||
; not voted yet, just show options so user can vote
|
||||
|
||||
; option a
|
||||
(button
|
||||
("class" "hover_left_bar tertiary justify-start w-full")
|
||||
("onclick" "trigger('me::vote', ['{{ post.id }}', 'A'])")
|
||||
(icon (text "tally-1"))
|
||||
(text "{{ poll[0].option_a }}"))
|
||||
|
||||
; option b
|
||||
(button
|
||||
("class" "hover_left_bar tertiary justify-start w-full")
|
||||
("onclick" "trigger('me::vote', ['{{ post.id }}', 'B'])")
|
||||
(icon (text "tally-2"))
|
||||
(text "{{ poll[0].option_b }}"))
|
||||
|
||||
; option c
|
||||
(text "{% if poll[0].option_c -%}")
|
||||
(button
|
||||
("class" "hover_left_bar tertiary justify-start w-full")
|
||||
("onclick" "trigger('me::vote', ['{{ post.id }}', 'C'])")
|
||||
(icon (text "tally-3"))
|
||||
(text "{{ poll[0].option_c }}"))
|
||||
(text "{%- endif %}")
|
||||
|
||||
; option d
|
||||
(text "{% if poll[0].option_d -%}")
|
||||
(button
|
||||
("class" "hover_left_bar tertiary justify-start w-full")
|
||||
("onclick" "trigger('me::vote', ['{{ post.id }}', 'D'])")
|
||||
(icon (text "tally-4"))
|
||||
(text "{{ poll[0].option_d }}"))
|
||||
(text "{%- endif %}")
|
||||
(text "{%- endif %}")
|
||||
|
||||
; show expiration date + totals
|
||||
(div
|
||||
("class" "flex w-full flex-wrap gap-2")
|
||||
(span ("class" "notification chip") (text "{{ total }} votes"))))
|
||||
(text "{%- endmacro %}")
|
||||
|
|
|
@ -179,6 +179,13 @@
|
|||
e.preventDefault();
|
||||
await trigger(\"atto::debounce\", [\"posts::create\"]);
|
||||
|
||||
// poll
|
||||
const poll_data = get_poll_data();
|
||||
|
||||
if (!poll_data[0]) {
|
||||
return alert(poll_data[1]);
|
||||
}
|
||||
|
||||
// create body
|
||||
const body = new FormData();
|
||||
|
||||
|
@ -194,6 +201,7 @@
|
|||
content: e.target.content.value,
|
||||
community: \"{{ config.town_square }}\",
|
||||
answering,
|
||||
poll: poll_data[1],
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
(text "{%- endif %}")
|
||||
(div
|
||||
("style" "display: contents;")
|
||||
(text "{% if post.context.repost and post.context.repost.reposting -%} {{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }} {%- endif %}"))
|
||||
(text "{% if post.context.repost and post.context.repost.reposting -%} {{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts, poll=poll) }} {%- endif %}"))
|
||||
(text "{% if user and post.context.comments_enabled -%}")
|
||||
(div
|
||||
("class" "card-nest")
|
||||
|
@ -273,13 +273,20 @@
|
|||
(text "{{ text \"communities:label.replies\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in replies %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false) }} {% endfor %} {{ components::pagination(page=page, items=replies|length) }}"))))
|
||||
(text "{% for post in replies %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, poll=post[4]) }} {% endfor %} {{ components::pagination(page=page, items=replies|length) }}"))))
|
||||
|
||||
(script
|
||||
(text "async function create_reply_from_form(e) {
|
||||
e.preventDefault();
|
||||
await trigger(\"atto::debounce\", [\"posts::create\"]);
|
||||
|
||||
// poll
|
||||
const poll_data = get_poll_data();
|
||||
|
||||
if (!poll_data[0]) {
|
||||
return alert(poll_data[1]);
|
||||
}
|
||||
|
||||
// create body
|
||||
const body = new FormData();
|
||||
|
||||
|
@ -295,6 +302,7 @@
|
|||
content: e.target.content.value,
|
||||
community: \"{{ community.id }}\",
|
||||
replying_to: \"{{ post.id }}\",
|
||||
poll: poll_data[1],
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -64,6 +64,6 @@
|
|||
(text "{{ text \"communities:label.quotes\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in list %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false) }} {% endfor %} {{ components::pagination(page=page, items=list|length, key=\"quotes\", value=\"true\") }}"))))
|
||||
(text "{% for post in list %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, poll=post[4]) }} {% endfor %} {{ components::pagination(page=page, items=list|length, key=\"quotes\", value=\"true\") }}"))))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
(text "{{ text \"communities:label.pinned\" }}")))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %}")))
|
||||
(text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %}")))
|
||||
|
||||
(text "{%- endif %} {{ macros::profile_nav(selected=\"posts\") }}")
|
||||
(div
|
||||
|
@ -41,6 +41,6 @@
|
|||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length, key=\"&tag=\", value=tag) }}")))
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length, key=\"&tag=\", value=tag) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex-col gap-4")
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
(text "{% for post in posts %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=posts|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -32,6 +32,6 @@
|
|||
("href" "/stacks/{{ stack.id }}/manage#/users")
|
||||
(text "add a user to this stack"))
|
||||
(text "!"))
|
||||
(text "{%- endif %} {% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}"))))
|
||||
(text "{%- endif %} {% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}"))))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -30,6 +30,6 @@
|
|||
(text "{%- endif %}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[3]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
(text "{{ macros::timelines_nav(selected=\"following\") }} {{ macros::timelines_secondary_nav(posts=\"/following\", questions=\"/following/questions\") }}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
(text "{% else %}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(text "{% for post in list %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}"))
|
||||
(text "{% for post in list %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}"))
|
||||
(text "{%- endif %}"))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
(text "{{ macros::timelines_nav(selected=\"popular\") }} {{ macros::timelines_secondary_nav(posts=\"/popular\", questions=\"/popular/questions\") }}")
|
||||
(div
|
||||
("class" "card w-full flex flex-col gap-2")
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=list|length) }}")))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -56,6 +56,6 @@
|
|||
(text "{{ icon \"circle-help\" }}"))
|
||||
(text "{%- endif %}"))))
|
||||
(text "{%- endif %}")
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2]) }} {%- endif %} {%- endif %} {% endfor %} {% if profile -%} {{ components::pagination(page=page, items=list|length, key=\"&profile=\" ~ profile.id, value=\"&query=\" ~ query) }} {% else %} {{ components::pagination(page=page, items=list|length, key=\"&query=\" ~ query) }} {%- endif %}"))))
|
||||
(text "{% for post in list %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %} {% if profile -%} {{ components::pagination(page=page, items=list|length, key=\"&profile=\" ~ profile.id, value=\"&query=\" ~ query) }} {% else %} {{ components::pagination(page=page, items=list|length, key=\"&query=\" ~ query) }} {%- endif %}"))))
|
||||
|
||||
(text "{% endblock %}")
|
||||
|
|
|
@ -104,6 +104,35 @@
|
|||
});
|
||||
});
|
||||
|
||||
self.define("vote", async (_, id, option) => {
|
||||
if (
|
||||
!(await trigger("atto::confirm", [
|
||||
"Are you sure you want to do this?",
|
||||
]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/api/v1/posts/${id}/poll_vote`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
option,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
trigger("atto::toast", [
|
||||
res.ok ? "success" : "error",
|
||||
res.message,
|
||||
]);
|
||||
|
||||
window.location.href = `/post/${id}`;
|
||||
});
|
||||
});
|
||||
|
||||
self.define("react", async (_, element, asset, asset_type, is_like) => {
|
||||
await trigger("atto::debounce", ["reactions::toggle"]);
|
||||
fetch("/api/v1/reactions", {
|
||||
|
|
|
@ -717,6 +717,12 @@ pub async fn post_request(
|
|||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||
};
|
||||
|
||||
// check poll
|
||||
let poll = match data.0.get_post_poll(&post, &user).await {
|
||||
Ok(q) => q,
|
||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||
};
|
||||
|
||||
// check permissions
|
||||
let (can_read, can_manage_pins) = check_permissions!(community, jar, data, user);
|
||||
|
||||
|
@ -755,6 +761,7 @@ pub async fn post_request(
|
|||
context.insert("post", &post);
|
||||
context.insert("reposting", &reposting);
|
||||
context.insert("question", &question);
|
||||
context.insert("poll", &poll);
|
||||
context.insert("replies", &feed);
|
||||
context.insert("page", &props.page);
|
||||
context.insert(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tetratto-core"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -21,7 +21,7 @@ impl DataManager {
|
|||
id: get!(x->0(i64)) as usize,
|
||||
owner: get!(x->1(i64)) as usize,
|
||||
created: get!(x->2(i64)) as usize,
|
||||
expires: get!(x->3(i64)) as usize,
|
||||
expires: get!(x->3(i32)) as usize,
|
||||
option_a: get!(x->4(String)),
|
||||
option_b: get!(x->5(String)),
|
||||
option_c: get!(x->6(String)),
|
||||
|
@ -74,15 +74,15 @@ impl DataManager {
|
|||
&(data.id as i64),
|
||||
&(data.owner as i64),
|
||||
&(data.created as i64),
|
||||
&(data.expires as i64),
|
||||
&(data.expires as i32),
|
||||
&data.option_a,
|
||||
&data.option_b,
|
||||
&data.option_c,
|
||||
&data.option_d,
|
||||
&(data.votes_a as i64),
|
||||
&(data.votes_b as i64),
|
||||
&(data.votes_c as i64),
|
||||
&(data.votes_d as i64),
|
||||
&(data.votes_a as i32),
|
||||
&(data.votes_b as i32),
|
||||
&(data.votes_c as i32),
|
||||
&(data.votes_d as i32),
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -120,6 +120,18 @@ impl DataManager {
|
|||
|
||||
self.2.remove(format!("atto.poll:{}", id)).await;
|
||||
|
||||
// remove votes
|
||||
let res = execute!(
|
||||
&conn,
|
||||
"DELETE FROM pollvotes WHERE poll_id = $1",
|
||||
&[&(id as i64)]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
// ...
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -127,15 +139,15 @@ impl DataManager {
|
|||
self.2.remove(format!("atto.poll:{}", poll.id)).await;
|
||||
}
|
||||
|
||||
auto_method!(incr_votes_a_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_a + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_a_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_a - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_a);
|
||||
auto_method!(incr_votes_a_count()@get_poll_by_id -> "UPDATE polls SET votes_a = votes_a + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_a_count()@get_poll_by_id -> "UPDATE polls SET votes_a = votes_a - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_a);
|
||||
|
||||
auto_method!(incr_votes_b_count()@get_poll_by_id -> "UPDATE users SET votes_b = votes_b + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_b_count()@get_poll_by_id -> "UPDATE users SET votes_b = votes_b - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_b);
|
||||
auto_method!(incr_votes_b_count()@get_poll_by_id -> "UPDATE polls SET votes_b = votes_b + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_b_count()@get_poll_by_id -> "UPDATE polls SET votes_b = votes_b - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_b);
|
||||
|
||||
auto_method!(incr_votes_c_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_d + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_c_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_d - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_c);
|
||||
auto_method!(incr_votes_c_count()@get_poll_by_id -> "UPDATE polls SET votes_c = votes_d + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_c_count()@get_poll_by_id -> "UPDATE polls SET votes_c = votes_d - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_c);
|
||||
|
||||
auto_method!(incr_votes_d_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_d + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_d_count()@get_poll_by_id -> "UPDATE users SET votes_a = votes_d - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_d);
|
||||
auto_method!(incr_votes_d_count()@get_poll_by_id -> "UPDATE polls SET votes_d = votes_d + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --incr);
|
||||
auto_method!(decr_votes_d_count()@get_poll_by_id -> "UPDATE polls SET votes_d = votes_d - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_poll --decr=votes_d);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::communities::PollVote;
|
||||
use crate::model::communities::{PollOption, PollVote};
|
||||
use crate::model::moderation::AuditLogEntry;
|
||||
use crate::model::{Error, Result, auth::User, permissions::FinePermission};
|
||||
use crate::{auto_method, execute, get, query_row, params};
|
||||
|
@ -45,7 +45,7 @@ impl DataManager {
|
|||
|
||||
let res = query_row!(
|
||||
&conn,
|
||||
"SELECT * FROM pollvotes WHERE id = $1 AND poll_id = $2",
|
||||
"SELECT * FROM pollvotes WHERE owner = $1 AND poll_id = $2",
|
||||
&[&(id as i64), &(poll_id as i64)],
|
||||
|x| { Ok(Self::get_pollvote_from_row(x)) }
|
||||
);
|
||||
|
@ -95,7 +95,7 @@ impl DataManager {
|
|||
&(data.owner as i64),
|
||||
&(data.created as i64),
|
||||
&(data.poll_id as i64),
|
||||
&(vote_u8 as i64),
|
||||
&(vote_u8 as i32),
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -104,10 +104,20 @@ impl DataManager {
|
|||
}
|
||||
|
||||
// update poll
|
||||
self.incr_votes_a_count(poll.id).await?;
|
||||
self.incr_votes_b_count(poll.id).await?;
|
||||
self.incr_votes_c_count(poll.id).await?;
|
||||
self.incr_votes_d_count(poll.id).await?;
|
||||
match data.vote {
|
||||
PollOption::A => {
|
||||
self.incr_votes_a_count(poll.id).await?;
|
||||
}
|
||||
PollOption::B => {
|
||||
self.incr_votes_b_count(poll.id).await?;
|
||||
}
|
||||
PollOption::C => {
|
||||
self.incr_votes_c_count(poll.id).await?;
|
||||
}
|
||||
PollOption::D => {
|
||||
self.incr_votes_d_count(poll.id).await?;
|
||||
}
|
||||
};
|
||||
|
||||
// ...
|
||||
Ok(data.id)
|
||||
|
|
|
@ -245,7 +245,7 @@ impl DataManager {
|
|||
let user = if let Some(ua) = user {
|
||||
ua
|
||||
} else {
|
||||
return Err(Error::MiscError("Could not get user for pull".to_string()));
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if post.poll_id != 0 {
|
||||
|
@ -259,7 +259,7 @@ impl DataManager {
|
|||
Err(_) => return Err(Error::MiscError("Invalid poll ID attached".to_string())),
|
||||
}))
|
||||
} else {
|
||||
return Err(Error::MiscError("Invalid poll ID attached".to_string()));
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,15 +374,10 @@ impl DataManager {
|
|||
Community,
|
||||
Option<(User, Post)>,
|
||||
Option<(Question, User)>,
|
||||
Option<(Poll, bool)>,
|
||||
)>,
|
||||
> {
|
||||
let mut out: Vec<(
|
||||
Post,
|
||||
User,
|
||||
Community,
|
||||
Option<(User, Post)>,
|
||||
Option<(Question, User)>,
|
||||
)> = Vec::new();
|
||||
let mut out = Vec::new();
|
||||
|
||||
let mut seen_before: HashMap<(usize, usize), (User, Community)> = HashMap::new();
|
||||
let mut seen_user_follow_statuses: HashMap<(usize, usize), bool> = HashMap::new();
|
||||
|
@ -403,6 +398,7 @@ impl DataManager {
|
|||
community.to_owned(),
|
||||
self.get_post_reposting(&post, ignore_users, user).await,
|
||||
self.get_post_question(&post, ignore_users).await?,
|
||||
self.get_post_poll(&post, user).await?,
|
||||
));
|
||||
} else {
|
||||
let ua = self.get_user_by_id(owner).await?;
|
||||
|
@ -450,6 +446,7 @@ impl DataManager {
|
|||
community,
|
||||
self.get_post_reposting(&post, ignore_users, user).await,
|
||||
self.get_post_question(&post, ignore_users).await?,
|
||||
self.get_post_poll(&post, user).await?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1423,7 +1420,7 @@ impl DataManager {
|
|||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
"INSERT INTO posts VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, null, $13)",
|
||||
"INSERT INTO posts VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, DEFAULT, $13)",
|
||||
params![
|
||||
&(data.id as i64),
|
||||
&(data.created as i64),
|
||||
|
@ -1541,6 +1538,11 @@ impl DataManager {
|
|||
self.delete_upload(upload).await?;
|
||||
}
|
||||
|
||||
// remove poll
|
||||
if y.poll_id != 0 {
|
||||
self.delete_poll(y.poll_id, user).await?;
|
||||
}
|
||||
|
||||
// return
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::communities::{Community, Post, Question};
|
||||
use crate::model::communities::{Community, Poll, Post, Question};
|
||||
use crate::model::stacks::{StackMode, StackSort};
|
||||
use crate::model::{
|
||||
Error, Result,
|
||||
|
@ -51,6 +51,7 @@ impl DataManager {
|
|||
Community,
|
||||
Option<(User, Post)>,
|
||||
Option<(Question, User)>,
|
||||
Option<(Poll, bool)>,
|
||||
)>,
|
||||
> {
|
||||
let stack = self.get_stack_by_id(id).await?;
|
||||
|
|
|
@ -425,7 +425,7 @@ impl Poll {
|
|||
/// Poll option (selectors) are stored in the database as numbers 0 to 3.
|
||||
///
|
||||
/// This enum allows us to convert from these numbers into letters.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum PollOption {
|
||||
A,
|
||||
B,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tetratto-l10n"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
edition = "2024"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tetratto-shared"
|
||||
version = "4.5.0"
|
||||
version = "5.0.0"
|
||||
edition = "2024"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue