add: 8 more achievements

This commit is contained in:
trisua 2025-06-27 14:21:42 -04:00
parent 8d70f65863
commit a799c777ea
11 changed files with 121 additions and 18 deletions

View file

@ -1084,10 +1084,12 @@
("href" "/journals/0/0") ("href" "/journals/0/0")
(icon (text "notebook")) (icon (text "notebook"))
(str (text "general:link.journals"))) (str (text "general:link.journals")))
(text "{% if not user.settings.disable_achievements -%}")
(a (a
("href" "/achievements") ("href" "/achievements")
(icon (text "award")) (icon (text "award"))
(str (text "general:link.achievements"))) (str (text "general:link.achievements")))
(text "{%- endif %}")
(a (a
("href" "/settings") ("href" "/settings")
(text "{{ icon \"settings\" }}") (text "{{ icon \"settings\" }}")

View file

@ -1553,6 +1553,11 @@
\"{{ profile.settings.disable_gpa_fun }}\", \"{{ profile.settings.disable_gpa_fun }}\",
\"checkbox\", \"checkbox\",
], ],
[
[\"disable_achievements\", \"Disable achievements\"],
\"{{ profile.settings.disable_achievements }}\",
\"checkbox\",
],
], ],
settings, settings,
); );

View file

@ -979,7 +979,13 @@
self.define( self.define(
"timestamp", "timestamp",
({ $ }, updated_, progress_ms_, duration_ms_, display = "full") => { async (
{ $ },
updated_,
progress_ms_,
duration_ms_,
display = "full",
) => {
if (duration_ms_ === "0") { if (duration_ms_ === "0") {
return; return;
} }
@ -1003,7 +1009,7 @@
} }
if (display === "full") { if (display === "full") {
return `${$.ms_time_text(progress_ms)}/${$.ms_time_text(duration_ms)} <span class="fade">(${Math.floor((progress_ms / duration_ms) * 100)}%)</span>`; return `${await $.ms_time_text(progress_ms)}/${await $.ms_time_text(duration_ms)} <span class="fade">(${Math.floor((progress_ms / duration_ms) * 100)}%)</span>`;
} }
if (display === "left") { if (display === "left") {

View file

@ -292,11 +292,10 @@ pub async fn create_membership(
}; };
match data match data
.create_membership(CommunityMembership::new( .create_membership(
user.id, CommunityMembership::new(user.id, id, CommunityPermission::default()),
id, &user,
CommunityPermission::default(), )
))
.await .await
{ {
Ok(m) => Json(ApiReturn { Ok(m) => Json(ApiReturn {

View file

@ -4,7 +4,7 @@ use axum::{
Extension, Json, Extension, Json,
}; };
use axum_extra::extract::CookieJar; use axum_extra::extract::CookieJar;
use tetratto_core::model::{communities::PostDraft, oauth, ApiReturn, Error}; use tetratto_core::model::{auth::AchievementName, communities::PostDraft, oauth, ApiReturn, Error};
use crate::{ use crate::{
get_user_from_token, get_user_from_token,
routes::{ routes::{
@ -20,11 +20,20 @@ pub async fn create_request(
Json(req): Json<CreatePostDraft>, Json(req): Json<CreatePostDraft>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let data = &(data.read().await).0; let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateDrafts) { let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateDrafts) {
Some(ua) => ua, Some(ua) => ua,
None => return Json(Error::NotAllowed.into()), None => return Json(Error::NotAllowed.into()),
}; };
// award achievement
if let Err(e) = data
.add_achievement(&mut user, AchievementName::CreateDraft.into())
.await
{
return Json(e.into());
}
// ...
match data match data
.create_draft(PostDraft::new(req.content, user.id)) .create_draft(PostDraft::new(req.content, user.id))
.await .await

View file

@ -341,11 +341,20 @@ pub async fn update_content_request(
Json(req): Json<UpdatePostContent>, Json(req): Json<UpdatePostContent>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let data = &(data.read().await).0; let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserEditPosts) { let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserEditPosts) {
Some(ua) => ua, Some(ua) => ua,
None => return Json(Error::NotAllowed.into()), None => return Json(Error::NotAllowed.into()),
}; };
// award achievement
if let Err(e) = data
.add_achievement(&mut user, AchievementName::EditPost.into())
.await
{
return Json(e.into());
}
// ...
match data.update_post_content(id, user, req.content).await { match data.update_post_content(id, user, req.content).await {
Ok(_) => Json(ApiReturn { Ok(_) => Json(ApiReturn {
ok: true, ok: true,

View file

@ -713,6 +713,10 @@ impl DataManager {
/// ///
/// Still returns `Ok` if the user already has the achievement. /// Still returns `Ok` if the user already has the achievement.
pub async fn add_achievement(&self, user: &mut User, achievement: Achievement) -> Result<()> { pub async fn add_achievement(&self, user: &mut User, achievement: Achievement) -> Result<()> {
if user.settings.disable_achievements {
return Ok(());
}
if user if user
.achievements .achievements
.iter() .iter()

View file

@ -299,11 +299,10 @@ impl DataManager {
} }
// add community owner as admin // add community owner as admin
self.create_membership(CommunityMembership::new( self.create_membership(
data.owner, CommunityMembership::new(data.owner, data.id, CommunityPermission::ADMINISTRATOR),
data.id, &owner,
CommunityPermission::ADMINISTRATOR, )
))
.await .await
.unwrap(); .unwrap();

View file

@ -1,4 +1,5 @@
use oiseau::cache::Cache; use oiseau::cache::Cache;
use crate::model::auth::AchievementName;
use crate::model::communities::Community; use crate::model::communities::Community;
use crate::model::requests::{ActionRequest, ActionType}; use crate::model::requests::{ActionRequest, ActionType};
use crate::model::{ use crate::model::{
@ -169,7 +170,11 @@ impl DataManager {
/// # Arguments /// # Arguments
/// * `data` - a mock [`CommunityMembership`] object to insert /// * `data` - a mock [`CommunityMembership`] object to insert
#[async_recursion::async_recursion] #[async_recursion::async_recursion]
pub async fn create_membership(&self, data: CommunityMembership) -> Result<String> { pub async fn create_membership(
&self,
data: CommunityMembership,
user: &User,
) -> Result<String> {
// make sure membership doesn't already exist // make sure membership doesn't already exist
if self if self
.get_membership_by_owner_community_no_void(data.owner, data.community) .get_membership_by_owner_community_no_void(data.owner, data.community)
@ -199,7 +204,7 @@ impl DataManager {
.await?; .await?;
// ... // ...
return self.create_membership(data).await; return self.create_membership(data, user).await;
} }
} }
_ => (), _ => (),
@ -237,6 +242,9 @@ impl DataManager {
Ok(if data.role.check(CommunityPermission::REQUESTED) { Ok(if data.role.check(CommunityPermission::REQUESTED) {
"Join request sent".to_string() "Join request sent".to_string()
} else { } else {
self.add_achievement(&mut user.clone(), AchievementName::JoinCommunity.into())
.await?;
"Community joined".to_string() "Community joined".to_string()
}) })
} }

View file

@ -265,6 +265,33 @@ impl DataManager {
self.add_achievement(&mut other_user, AchievementName::FollowedByStaff.into()) self.add_achievement(&mut other_user, AchievementName::FollowedByStaff.into())
.await?; .await?;
} }
// other achivements
self.add_achievement(&mut other_user, AchievementName::Get1Follower.into())
.await?;
if other_user.follower_count >= 9 {
self.add_achievement(&mut other_user, AchievementName::Get10Followers.into())
.await?;
}
if other_user.follower_count >= 49 {
self.add_achievement(&mut other_user, AchievementName::Get50Followers.into())
.await?;
}
if other_user.follower_count >= 99 {
self.add_achievement(&mut other_user, AchievementName::Get100Followers.into())
.await?;
}
if initiator.following_count >= 9 {
self.add_achievement(
&mut initiator.clone(),
AchievementName::Follow10Users.into(),
)
.await?;
}
} }
// ... // ...

View file

@ -261,6 +261,9 @@ pub struct UserSettings {
/// Increase the text size of buttons and paragraphs. /// Increase the text size of buttons and paragraphs.
#[serde(default)] #[serde(default)]
pub large_text: bool, pub large_text: bool,
/// Disable achievements.
#[serde(default)]
pub disable_achievements: bool,
} }
fn mime_avif() -> String { fn mime_avif() -> String {
@ -478,7 +481,7 @@ pub struct ExternalConnectionData {
} }
/// The total number of achievements needed to 100% Tetratto! /// The total number of achievements needed to 100% Tetratto!
pub const ACHIEVEMENTS: usize = 16; pub const ACHIEVEMENTS: usize = 24;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AchievementName { pub enum AchievementName {
@ -498,6 +501,14 @@ pub enum AchievementName {
Get50Likes, Get50Likes,
Get100Likes, Get100Likes,
Get25Dislikes, Get25Dislikes,
Get1Follower,
Get10Followers,
Get50Followers,
Get100Followers,
Follow10Users,
JoinCommunity,
CreateDraft,
EditPost,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -526,6 +537,14 @@ impl AchievementName {
Self::Get50Likes => "banger post follow for more", Self::Get50Likes => "banger post follow for more",
Self::Get100Likes => "everyone liked that", Self::Get100Likes => "everyone liked that",
Self::Get25Dislikes => "Sorry...", Self::Get25Dislikes => "Sorry...",
Self::Get1Follower => "Friends?",
Self::Get10Followers => "Friends!",
Self::Get50Followers => "50 WHOLE FOLLOWERS??",
Self::Get100Followers => "Everyone is my friend!",
Self::Follow10Users => "Big fan",
Self::JoinCommunity => "A sense of community...",
Self::CreateDraft => "Maybe later!",
Self::EditPost => "Grammar police?",
} }
} }
@ -547,6 +566,14 @@ impl AchievementName {
Self::Get50Likes => "Get 50 likes on one post.", Self::Get50Likes => "Get 50 likes on one post.",
Self::Get100Likes => "Get 100 likes on one post.", Self::Get100Likes => "Get 100 likes on one post.",
Self::Get25Dislikes => "Get 25 dislikes on one post... :(", Self::Get25Dislikes => "Get 25 dislikes on one post... :(",
Self::Get1Follower => "Get 1 follow. Cool!",
Self::Get10Followers => "Get 10 followers. You're getting popular!",
Self::Get50Followers => "Get 50 followers. Okay, you're fairly popular!",
Self::Get100Followers => "Get 100 followers. You might be famous..?",
Self::Follow10Users => "Follow 10 other users. I'm sure people appreciate it!",
Self::JoinCommunity => "Join a community. Welcome!",
Self::CreateDraft => "Save a post as a draft.",
Self::EditPost => "Edit a post.",
} }
} }
@ -570,6 +597,14 @@ impl AchievementName {
Self::Get50Likes => Uncommon, Self::Get50Likes => Uncommon,
Self::Get100Likes => Rare, Self::Get100Likes => Rare,
Self::Get25Dislikes => Uncommon, Self::Get25Dislikes => Uncommon,
Self::Get1Follower => Common,
Self::Get10Followers => Common,
Self::Get50Followers => Uncommon,
Self::Get100Followers => Rare,
Self::Follow10Users => Common,
Self::JoinCommunity => Common,
Self::CreateDraft => Common,
Self::EditPost => Common,
} }
} }
} }