From 53cf75b53cb1d712479178cb7c2d662acd49e44c Mon Sep 17 00:00:00 2001 From: trisua Date: Wed, 2 Apr 2025 18:44:31 -0400 Subject: [PATCH] add: ability to edit community title through ui add: finish README --- README.md | 63 ++++++++++++++++--- crates/app/src/langs/en-US.toml | 3 + crates/app/src/public/css/style.css | 22 ++++++- .../src/public/html/communities/settings.html | 60 ++++++++++++++++++ crates/app/src/public/html/components.html | 24 ++++--- crates/app/src/public/html/macros.html | 17 +++-- crates/app/src/public/html/root.html | 8 ++- crates/app/src/routes/api/v1/util.rs | 2 +- crates/app/src/routes/pages/communities.rs | 10 ++- crates/app/src/routes/pages/profile.rs | 39 +++++------- rustfmt.toml | 10 +++ 11 files changed, 203 insertions(+), 55 deletions(-) create mode 100644 rustfmt.toml diff --git a/README.md b/README.md index d7efa63..a796e9e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,59 @@ -# 🐐 tetratto! +
+

🐇 tetratto!

+

Tetratto is a super simple community-oriented website where users can create various communities and share posts in them!

+
-Tetratto is your personal journal! +!["Docs" workflow badge](https://github.com/trisuaso/tetratto/workflows/Docs/badge.svg) +![GitHub commit activity](https://img.shields.io/github/commit-activity/m/trisuaso/tetratto) +![GitHub last commit](https://img.shields.io/github/last-commit/trisuaso/tetratto) +[![GitHub License](https://img.shields.io/github/license/trisuaso/tetratto)](https://github.com/trisuaso/tetratto/blob/master/LICENSE) -## Features +# Usage -- Create new pages in your journal (essentially just posts) -- Create new pages in your journal where people can post messages (essentially message boards that you control) -- Follow other people and see their (public) journal entries - - Journal entries can either be public, unlisted (only accessible via link), and fully private (only accessible to moderators and the owner) +Everything Tetratto needs will be built into the main binary. You can build Tetratto with the following command: + +```bash +cargo build -r --no-default-features features=redis,sqlite +``` + +You can replace `sqlite` in the above command with `postgres`, if you'd like. It's also acceptable to remove the `redis` part if you don't want to use a cache. I wouldn't recomment removing cache, though + +You can then take the binary and place it somewhere else (highly recommended; the binary will create a fair number of files!). You can do this to move it to a directory just called "tetratto" in the parent directory: + +```bash +mkdir tetratto +mv ./target/release/tetratto ../tetratto +cd ../tetratto +``` + +Your first start of Tetratto might be a little slow as it's going to download all icon SVGs required for the HTML templates to render properly. These icons will be stored on disk, so there's no need to worry about this time _every_ restart. + +## Configuration + +In the directory you're running Tetratto from, you should create a `tetratto.toml` file. This file follows the configuration schema defined [here](https://trisuaso.github.io/tetratto/tetratto/config/struct.Config.html)! + +## Usage (as a user) + +Tetratto is very simple once you get the hang of it! At the top of the page (or bottom if you're on mobile), you'll see the navigation bar. Once logged in, you'll be able to access "Home", "Popular", and "Communities" from there! You can also press your profile picture (on the right) to view your own profile, settings, or log out! + +All Tetratto instances support reports for communities and posts through the UI. You can just find the ellipsis icon on either and then press "Report" to file a report! + +# Updating + +When bumping versions, you _might_ need to run a few SQL scripts in order to get your database up to what the next commit expects. It is recommended to only update from GitHub release, which will list the SQL scripts you need to run to migrate your database. + +# Developing + +All you really need to develop Tetratto is [Rust](https://rustup.rs/) and [Just](https://just.systems/). + +You can fix a lot of weird style issues and stuff using `just fix`. You need [Clippy](https://doc.rust-lang.org/stable/clippy/installation.html) for this + +You can also automatically bump all dependencies _and_ point out unused ones with `just clean-deps`! You need [cargo-edit](https://github.com/killercup/cargo-edit) and [cargo-machete](https://github.com/bnjbvr/cargo-machete) for this + +# Contributing + +Read the ["Contribution Guidelines"](./.github/CONTRIBUTING.md) before contributing! + +# License + +Tetratto is licensed under the [AGPL-3.0](./LICENSE). diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml index 5af0496..da55be7 100644 --- a/crates/app/src/langs/en-US.toml +++ b/crates/app/src/langs/en-US.toml @@ -8,6 +8,7 @@ version = "1.0.0" "general:link.next" = "Next" "general:link.previous" = "Previous" "general:link.source_code" = "Source code" +"general:link.reference" = "Reference" "general:link.audit_log" = "Audit log" "general:link.reports" = "Reports" "general:action.save" = "Save" @@ -64,6 +65,8 @@ version = "1.0.0" "communities:label.user_id" = "User ID" "communities:label.danger_zone" = "Danger zone" "communities:label.delete_community" = "Delete community" +"communities:label.change_title" = "Change title" +"communities:label.new_title" = "New title" "notifs:action.mark_as_read" = "Mark as read" "notifs:action.mark_as_unread" = "Mark as unread" diff --git a/crates/app/src/public/css/style.css b/crates/app/src/public/css/style.css index 93a1c19..7c2ad8e 100644 --- a/crates/app/src/public/css/style.css +++ b/crates/app/src/public/css/style.css @@ -102,6 +102,17 @@ article { article { margin-top: 0; } + + main { + padding: 0; + } + + body .card:not(.card *), + body .pillmenu:not(.card *) > a, + body .card-nest:not(.card *) > .card, + body .banner { + border-radius: 0 !important; + } } .content_container { @@ -138,13 +149,19 @@ article { } /* typo */ -pre { - font-family: monospace; +pre, +code { + font-family: "Jetbrains Mono", "Fire Code", monospace; width: 100%; max-width: 100%; overflow: auto; background: var(--color-lowered); border-radius: var(--radius); + padding: 0.25rem; + font-size: 0.8rem; +} + +pre { padding: 1rem; } @@ -765,6 +782,7 @@ dialog::backdrop { display: none; position: absolute; background: var(--color-raised); + border: solid 1px var(--color-super-lowered); z-index: 2; border-radius: var(--radius); top: calc(100% + 5px); diff --git a/crates/app/src/public/html/communities/settings.html b/crates/app/src/public/html/communities/settings.html index f471c2f..7daadb2 100644 --- a/crates/app/src/public/html/communities/settings.html +++ b/crates/app/src/public/html/communities/settings.html @@ -98,6 +98,36 @@ + +
+
+ {{ text "communities:label.change_title" }} +
+ +
+
+ + +
+ + +
+
@@ -412,6 +442,35 @@ }); }; + globalThis.change_title = async (e) => { + e.preventDefault(); + + if ( + !(await trigger("atto::confirm", [ + "Are you sure you would like to do this?", + ])) + ) { + return; + } + + fetch("/api/v1/communities/{{ community.id }}/title", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: e.target.new_title.value, + }), + }) + .then((res) => res.json()) + .then((res) => { + trigger("atto::toast", [ + res.ok ? "success" : "error", + res.message, + ]); + }); + }; + globalThis.delete_community = async () => { if ( !(await trigger("atto::confirm", [ @@ -437,6 +496,7 @@ "read_access", "join_access", "write_access", + "change_title", "change_avatar", "change_banner", ]); diff --git a/crates/app/src/public/html/components.html b/crates/app/src/public/html/components.html index e2d28ba..2d6d40f 100644 --- a/crates/app/src/public/html/components.html +++ b/crates/app/src/public/html/components.html @@ -88,6 +88,22 @@ community %} {{ dislikes }} {% endif %} +{%- endmacro %} {% macro full_username(user) -%} +
+ + {{ components::username(user=user) }} + + + {{ components::online_indicator(user=user) }} {% if user.is_verified %} + + {{ icon "badge-check" }} + + {% endif %} +
{%- endmacro %} {% macro post(post, owner, secondary=false, community=false, show_community=true) -%} {% if community and show_community %}
@@ -113,13 +129,7 @@ show_community=true) -%} {% if community and show_community %}
-
- {{ components::username(user=owner) }} - - {{ components::online_indicator(user=owner) }} -
+ {{ components::full_username(user=owner) }} {{ post.created }} diff --git a/crates/app/src/public/html/macros.html b/crates/app/src/public/html/macros.html index f9c7412..4969c27 100644 --- a/crates/app/src/public/html/macros.html +++ b/crates/app/src/public/html/macros.html @@ -75,11 +75,6 @@ show_lhs=true) -%} {{ text "auth:link.settings" }} - - {{ icon "code" }} - {{ text "general:link.source_code" }} - - {% if is_helper %} {{ text "general:label.mod" }} @@ -94,6 +89,18 @@ show_lhs=true) -%} {% endif %} + {{ config.name }} + + + {{ icon "code" }} + {{ text "general:link.source_code" }} + + + + {{ icon "book" }} + {{ text "general:link.reference" }} + +
diff --git a/crates/app/src/routes/api/v1/util.rs b/crates/app/src/routes/api/v1/util.rs index 30d9dbb..44057ab 100644 --- a/crates/app/src/routes/api/v1/util.rs +++ b/crates/app/src/routes/api/v1/util.rs @@ -66,7 +66,7 @@ pub async fn proxy_request( if let Some(ct) = stream.headers().get("Content-Type") { let ct = ct.to_str().unwrap(); - let bad_ct = vec!["text/html", "text/plain"]; + let bad_ct = ["text/html", "text/plain"]; if (!ct.starts_with("image/") && !ct.starts_with("font/")) | bad_ct.contains(&ct) { // if we got html, return default banner (likely an error page) return ( diff --git a/crates/app/src/routes/pages/communities.rs b/crates/app/src/routes/pages/communities.rs index b11c011..34c6df6 100644 --- a/crates/app/src/routes/pages/communities.rs +++ b/crates/app/src/routes/pages/communities.rs @@ -233,12 +233,10 @@ pub async fn settings_request( Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)), }; - if user.id != community.owner { - if !user.permissions.check(FinePermission::MANAGE_COMMUNITIES) { - return Err(Html( - render_error(Error::NotAllowed, &jar, &data, &None).await, - )); - } + if user.id != community.owner && !user.permissions.check(FinePermission::MANAGE_COMMUNITIES) { + return Err(Html( + render_error(Error::NotAllowed, &jar, &data, &None).await, + )); } // init context diff --git a/crates/app/src/routes/pages/profile.rs b/crates/app/src/routes/pages/profile.rs index 22e72d7..aff32e9 100644 --- a/crates/app/src/routes/pages/profile.rs +++ b/crates/app/src/routes/pages/profile.rs @@ -121,17 +121,14 @@ pub async fn posts_request( // check for private profile if other_user.settings.private_profile { if let Some(ref ua) = user { - if (ua.id != other_user.id) && !ua.permissions.check(FinePermission::MANAGE_USERS) { - if data + if (ua.id != other_user.id) && !ua.permissions.check(FinePermission::MANAGE_USERS) && data .0 .get_userfollow_by_initiator_receiver(other_user.id, ua.id) .await - .is_err() - { - return Err(Html( - render_error(Error::NotAllowed, &jar, &data, &user).await, - )); - } + .is_err() { + return Err(Html( + render_error(Error::NotAllowed, &jar, &data, &user).await, + )); } } else { return Err(Html( @@ -246,17 +243,14 @@ pub async fn following_request( // check for private profile if other_user.settings.private_profile { if let Some(ref ua) = user { - if ua.id != other_user.id { - if data + if ua.id != other_user.id && data .0 .get_userfollow_by_initiator_receiver(other_user.id, ua.id) .await - .is_err() - { - return Err(Html( - render_error(Error::NotAllowed, &jar, &data, &user).await, - )); - } + .is_err() { + return Err(Html( + render_error(Error::NotAllowed, &jar, &data, &user).await, + )); } } else { return Err(Html( @@ -373,17 +367,14 @@ pub async fn followers_request( // check for private profile if other_user.settings.private_profile { if let Some(ref ua) = user { - if ua.id != other_user.id { - if data + if ua.id != other_user.id && data .0 .get_userfollow_by_initiator_receiver(other_user.id, ua.id) .await - .is_err() - { - return Err(Html( - render_error(Error::NotAllowed, &jar, &data, &user).await, - )); - } + .is_err() { + return Err(Html( + render_error(Error::NotAllowed, &jar, &data, &user).await, + )); } } else { return Err(Html( diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..48f9b0f --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,10 @@ +# indent_style = "Block" +reorder_imports = false +hard_tabs = false +tab_spaces = 4 +# enum_discrim_align_threshold = 20 +# struct_field_align_threshold = 20 +fn_params_layout = "Tall" +max_width = 100 +newline_style = "Unix" +use_field_init_shorthand = true