add: ability to convert existing communities into forums

This commit is contained in:
trisua 2025-08-05 22:36:25 -04:00
parent 3958d5eaef
commit 2407e6b213
8 changed files with 125 additions and 24 deletions

View file

@ -5,22 +5,26 @@
--hue: 16;
--sat: 6%;
--lit: 0%;
--color-surface: hsl(var(--hue), var(--sat), calc(97% - var(--lit)));
--color-lowered: hsl(var(--hue), var(--sat), calc(94% - var(--lit)));
--color-raised: hsl(var(--hue), var(--sat), calc(99% - var(--lit)));
--color-surface: hsl(var(--hue), var(--sat), calc(94% - var(--lit)));
--color-lowered: hsl(var(--hue), var(--sat), calc(90% - var(--lit)));
--color-raised: hsl(var(--hue), var(--sat), calc(97% - var(--lit)));
--color-super-lowered: hsl(var(--hue), var(--sat), calc(85% - var(--lit)));
--color-super-raised: hsl(var(--hue), var(--sat), calc(100% - var(--lit)));
--color-text: hsl(0, 0%, 0%);
--color-super-raised: hsl(var(--hue), var(--sat), calc(99% - var(--lit)));
--color-text: hsl(0, 0%, 9%);
--color-text-raised: var(--color-text);
--color-text-lowered: var(--color-text);
--color-primary: hsl(330, 18%, 26%);
--color-primary-lowered: hsl(330, 18%, 21%);
--color-text-primary: hsl(0, 0%, 100%);
--color-text-primary: hsl(0, 0%, 91%);
--color-secondary: hsl(6, 18%, 66%);
--color-secondary-lowered: hsl(6, 18%, 61%);
--color-text-secondary: hsl(0, 0%, 0%);
--color-secondary: hsl(277, 27%, 70%);
--color-secondary-lowered: hsl(277, 27%, 65%);
--color-text-secondary: hsl(0, 0%, 9%);
--color-accent: hsl(237, 27%, 28%);
--color-accent-lowered: hsl(237, 27%, 23%);
--color-text-accent: hsl(0, 0%, 91%);
--color-link: #2949b2;
--color-shadow: rgba(0, 0, 0, 0.08);
@ -54,15 +58,19 @@
--color-raised: hsl(var(--hue), var(--sat), calc(2% + var(--lit)));
--color-super-lowered: hsl(var(--hue), var(--sat), calc(12% + var(--lit)));
--color-super-raised: hsl(var(--hue), var(--sat), calc(4% + var(--lit)));
--color-text: hsl(0, 0%, 95%);
--color-text: hsl(0, 0%, 91%);
--color-primary: hsl(331, 18%, 74%);
--color-primary-lowered: hsl(331, 18%, 69%);
--color-text-primary: hsl(0, 0%, 0%);
--color-text-primary: hsl(0, 0%, 9%);
--color-secondary: hsl(6, 18%, 34%);
--color-secondary-lowered: hsl(6, 18%, 29%);
--color-text-secondary: hsl(0, 0%, 100%);
--color-secondary: hsl(277, 27%, 30%);
--color-secondary-lowered: hsl(277, 27%, 25%);
--color-text-secondary: hsl(0, 0%, 91%);
--color-accent: hsl(237, 27%, 72%);
--color-accent-lowered: hsl(237, 27%, 67%);
--color-text-accent: hsl(0, 0%, 9%);
--color-link: #93c5fd;
--color-red: hsl(0, 94%, 82%);

View file

@ -370,7 +370,17 @@ button.secondary,
.button.secondary {
background: var(--color-secondary);
color: var(--color-text-secondary);
font-weight: 500;
}
button.accent:hover,
.button.accent:hover {
background: var(--color-accent-lowered);
}
button.accent,
.button.accent {
background: var(--color-accent);
color: var(--color-text-accent);
}
button.secondary:hover,
@ -861,18 +871,19 @@ dialog {
display: none;
background: var(--color-surface);
border: solid 1px var(--color-super-lowered) !important;
border-radius: var(--radius);
max-width: 100%;
border-radius: calc(var(--radius) * 2);
max-width: 95%;
border-style: none;
margin: auto;
color: var(--color-text);
animation: popin ease-in-out 1 0.1s forwards running;
animation: popin ease-in-out 1 0.15s forwards running;
}
dialog .inner {
padding: var(--pad-4);
width: 25rem;
max-width: 100%;
font-weight: 500;
}
dialog .inner hr:not(.flipped):last-of-type {
@ -891,6 +902,11 @@ dialog[open] {
dialog::backdrop {
background: hsla(0, 0%, 0%, 50%);
backdrop-filter: blur(5px);
animation: fadein ease-in-out 1 0.1s forwards running;
}
dialog:is(.dark *)::backdrop {
background: hsla(0, 0%, 100%, 15%);
}
/* dropdown */
@ -905,7 +921,7 @@ dialog::backdrop {
background: var(--color-raised);
/* border: solid 1px var(--color-super-lowered); */
z-index: 2;
border-radius: var(--radius);
border-radius: calc(var(--radius) * 2);
top: calc(100% + 5px);
right: 0;
width: max-content;

View file

@ -311,7 +311,7 @@
(div ("id" "tokens") ("style" "display: contents"))
(div
("class" "flex justify_between")
("class" "flex justify_right gap_2")
(a
("href" "/auth/login")
("class" "button")
@ -323,7 +323,8 @@
("class" "lowered")
("onclick" "document.getElementById('tokens_dialog').close()")
("type" "button")
(icon (text "check")))))))
(icon (text "check"))
(str (text "dialog:action.okay")))))))
; user scripts
(text "{%- endif %} {% if user and use_user_theme -%} {{ components::theme(user=user, theme_preference=user.settings.theme_preference) }}

View file

@ -37,7 +37,7 @@
("name" "is_forum")
("class" "w_content"))
(span
(text "Is forum")))
(text "Make this a forum community")))
(button
(text "{{ text \"communities:action.create\" }}"))))
(text "{% if list|length >= 4 -%} {{ components::supporter_ad(body=\"Become a supporter to create up to 10 communities!\") }} {%- endif %} {%- endif %}")

View file

@ -37,14 +37,14 @@
(text "{{ icon \"rss\" }}")
(span
(text "{{ text \"communities:tab.channels\" }}")))
(text "{%- endif %} {% if community.is_forum -%}")
(text "{%- endif %}")
(a
("href" "#/topics")
("data-tab-button" "topics")
(icon (text "list"))
(span
(str (text "communities:tab.topics"))))
(text "{%- endif %} {% if can_manage_emojis -%}")
(text "{% if can_manage_emojis -%}")
(a
("href" "#/emojis")
("data-tab-button" "emojis")
@ -825,6 +825,42 @@
]);
});
}"))
(text "{% else %}")
(div
("class" "card lowered w_full hidden flex flex_col gap_2")
("data-tab" "topics")
(p (text "You can only manage topics for forum communities. You can convert this community into a forum, but you will not be able to go back."))
(p (text "This will permanently change your community. Currently existing posts will no longer be visible on the community."))
(button
("onclick" "convert_to_forum()")
(icon (text "circle-fading-arrow-up"))
(text "Switch to forum")))
(script
(text "globalThis.convert_to_forum = async () => {
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this? It cannot be undone.\",
]))
) {
return;
}
fetch(\"/api/v1/communities/{{ community.id }}/is_forum\", {
method: \"POST\",
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
if (res.ok) {
window.location.reload();
}
});
}"))
(text "{%- endif %}"))
(script

View file

@ -264,6 +264,41 @@ pub async fn update_owner_request(
}
}
pub async fn update_is_forum_request(
jar: CookieJar,
Extension(data): Extension<State>,
Path(id): Path<usize>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::CommunityManage) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
let mut community = match data.get_community_by_id_no_void(id).await {
Ok(x) => x,
Err(e) => return Json(e.into()),
};
community.context.enable_titles = true;
community.context.require_titles = true;
match data.update_community_is_forum(id, &user, 1).await {
Ok(_) => match data
.update_community_context(id, &user, community.context)
.await
{
Ok(_) => Json(ApiReturn {
ok: true,
message: "Community updated".to_string(),
payload: (),
}),
Err(e) => Json(e.into()),
},
Err(e) => Json(e.into()),
}
}
pub async fn get_membership(
jar: CookieJar,
Extension(data): Extension<State>,

View file

@ -95,6 +95,10 @@ pub fn routes() -> Router {
"/communities/{id}/context",
post(communities::communities::update_context_request),
)
.route(
"/communities/{id}/is_forum",
post(communities::communities::update_is_forum_request),
)
.route(
"/communities/{id}/access/read",
post(communities::communities::update_read_access_request),

View file

@ -556,6 +556,7 @@ impl DataManager {
auto_method!(update_community_write_access(CommunityWriteAccess)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET write_access = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);
auto_method!(update_community_join_access(CommunityJoinAccess)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET join_access = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);
auto_method!(update_community_topics(HashMap<usize, ForumTopic>)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET topics = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);
auto_method!(update_community_is_forum(i32)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET is_forum = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_community);
auto_method!(incr_community_likes()@get_community_by_id_no_void -> "UPDATE communities SET likes = likes + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_community --incr);
auto_method!(incr_community_dislikes()@get_community_by_id_no_void -> "UPDATE communities SET dislikes = dislikes + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_community --incr);