diff --git a/crates/app/src/public/css/root.css b/crates/app/src/public/css/root.css index a0c2a18..08437da 100644 --- a/crates/app/src/public/css/root.css +++ b/crates/app/src/public/css/root.css @@ -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%); diff --git a/crates/app/src/public/css/style.css b/crates/app/src/public/css/style.css index 900afd0..d02e7b0 100644 --- a/crates/app/src/public/css/style.css +++ b/crates/app/src/public/css/style.css @@ -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; diff --git a/crates/app/src/public/html/body.lisp b/crates/app/src/public/html/body.lisp index 00dcfd8..89b6d53 100644 --- a/crates/app/src/public/html/body.lisp +++ b/crates/app/src/public/html/body.lisp @@ -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) }} diff --git a/crates/app/src/public/html/communities/list.lisp b/crates/app/src/public/html/communities/list.lisp index 5c0abac..bb243c8 100644 --- a/crates/app/src/public/html/communities/list.lisp +++ b/crates/app/src/public/html/communities/list.lisp @@ -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 %}") diff --git a/crates/app/src/public/html/communities/settings.lisp b/crates/app/src/public/html/communities/settings.lisp index 29b171e..d55a324 100644 --- a/crates/app/src/public/html/communities/settings.lisp +++ b/crates/app/src/public/html/communities/settings.lisp @@ -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 diff --git a/crates/app/src/routes/api/v1/communities/communities.rs b/crates/app/src/routes/api/v1/communities/communities.rs index 0d6d703..27ec0fb 100644 --- a/crates/app/src/routes/api/v1/communities/communities.rs +++ b/crates/app/src/routes/api/v1/communities/communities.rs @@ -264,6 +264,41 @@ pub async fn update_owner_request( } } +pub async fn update_is_forum_request( + jar: CookieJar, + Extension(data): Extension, + Path(id): Path, +) -> 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, diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs index fa6b913..694ad29 100644 --- a/crates/app/src/routes/api/v1/mod.rs +++ b/crates/app/src/routes/api/v1/mod.rs @@ -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), diff --git a/crates/core/src/database/communities.rs b/crates/core/src/database/communities.rs index 3b22917..1290bdd 100644 --- a/crates/core/src/database/communities.rs +++ b/crates/core/src/database/communities.rs @@ -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)@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);