diff --git a/Cargo.lock b/Cargo.lock
index a11c634..ce61c71 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3231,7 +3231,7 @@ dependencies = [
[[package]]
name = "tetratto"
-version = "9.0.0"
+version = "10.0.0"
dependencies = [
"ammonia",
"async-stripe",
@@ -3262,7 +3262,7 @@ dependencies = [
[[package]]
name = "tetratto-core"
-version = "9.0.0"
+version = "10.0.0"
dependencies = [
"async-recursion",
"base16ct",
@@ -3284,7 +3284,7 @@ dependencies = [
[[package]]
name = "tetratto-l10n"
-version = "9.0.0"
+version = "10.0.0"
dependencies = [
"pathbufd",
"serde",
@@ -3293,7 +3293,7 @@ dependencies = [
[[package]]
name = "tetratto-shared"
-version = "9.0.0"
+version = "10.0.0"
dependencies = [
"ammonia",
"chrono",
diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml
index e29dcb9..3c66674 100644
--- a/crates/app/Cargo.toml
+++ b/crates/app/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tetratto"
-version = "9.0.0"
+version = "10.0.0"
edition = "2024"
[dependencies]
diff --git a/crates/app/src/public/html/body.lisp b/crates/app/src/public/html/body.lisp
index 82e5fe9..227a8f1 100644
--- a/crates/app/src/public/html/body.lisp
+++ b/crates/app/src/public/html/body.lisp
@@ -1,5 +1,17 @@
(div ("id" "toast_zone"))
+; large text
+(text "{% if user and user.settings.large_text -%}")
+(style
+ (text "button, a, p, span, b, strone, em, i, pre, code {
+ font-size: 18px !important;
+ }
+
+ nav .icon {
+ font-size: 15px !important;
+ }"))
+(text "{%- endif %}")
+
; templates
(template
("id" "loading_skeleton")
diff --git a/crates/app/src/public/html/profile/posts.lisp b/crates/app/src/public/html/profile/posts.lisp
index 6c417a6..6120fea 100644
--- a/crates/app/src/public/html/profile/posts.lisp
+++ b/crates/app/src/public/html/profile/posts.lisp
@@ -49,6 +49,7 @@
(text "setTimeout(async () => {
await trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?user_id={{ profile.id }}&tag={{ tag }}&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
(await ns(\"ui\")).IO_DATA_DISABLE_RELOAD = true;
- }, 500);"))
+ console.log(\"created profile timeline\");
+ }, 1000);"))
(text "{% endblock %}")
diff --git a/crates/app/src/public/html/profile/settings.lisp b/crates/app/src/public/html/profile/settings.lisp
index 2b12aa0..f2280e6 100644
--- a/crates/app/src/public/html/profile/settings.lisp
+++ b/crates/app/src/public/html/profile/settings.lisp
@@ -1407,6 +1407,22 @@
embed_html:
'Muted phrases should all be on new lines.',
}],
+ [[], \"Accessibility\", \"title\"],
+ [
+ [\"large_text\", \"Increase UI text size\"],
+ \"{{ profile.settings.large_text }}\",
+ \"checkbox\",
+ ],
+ [
+ [\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
+ \"{{ profile.settings.paged_timelines }}\",
+ \"checkbox\",
+ ],
+ [
+ [\"auto_clear_notifs\", \"Automatically clear all notifications when you open the notifications page\"],
+ \"{{ profile.settings.auto_clear_notifs }}\",
+ \"checkbox\",
+ ],
],
settings,
{
@@ -1531,16 +1547,6 @@
\"Hides dislikes on all posts. Users will also no longer be able to dislike your posts.\",
\"text\",
],
- [
- [\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
- \"{{ profile.settings.paged_timelines }}\",
- \"checkbox\",
- ],
- [
- [\"auto_clear_notifs\", \"Automatically clear all notifications when you open the notifications page\"],
- \"{{ profile.settings.auto_clear_notifs }}\",
- \"checkbox\",
- ],
[[], \"Fun\", \"title\"],
[
[\"disable_gpa_fun\", \"Disable GPA\"],
diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js
index 0bd8d62..350b7a3 100644
--- a/crates/app/src/public/js/atto.js
+++ b/crates/app/src/public/js/atto.js
@@ -1151,82 +1151,90 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
});
// intersection observer infinite scrolling
- const obs = (await ns("ui")).IO_DATA_OBSERVER;
- if (obs) {
- console.log("get lost old observer");
- obs.disconnect();
- }
+ self.IO_DATA_OBSERVER = null;
- self.IO_DATA_OBSERVER = new IntersectionObserver(
- async (entries) => {
- for (const entry of entries) {
- if (!entry.isIntersecting) {
- continue;
- }
-
- await self.io_load_data();
- break;
+ self.define(
+ "io_data_load",
+ async (_, tmpl, page, paginated_mode = false) => {
+ // remove old
+ const obs = self.IO_DATA_OBSERVER;
+ if (obs) {
+ console.log("get lost old observer");
+ obs.disconnect();
+ self.IO_DATA_OBSERVER = null;
}
- },
- {
- root: document.body,
- rootMargin: "0px",
- threshold: 1,
- },
- );
- self.define("io_data_load", (_, tmpl, page, paginated_mode = false) => {
- self.IO_DATA_MARKER = document.querySelector(
- "[ui_ident=io_data_marker]",
- );
+ self.IO_DATA_OBSERVER = new IntersectionObserver(
+ async (entries) => {
+ for (const entry of entries) {
+ if (!entry.isIntersecting) {
+ continue;
+ }
- self.IO_DATA_ELEMENT = document.querySelector(
- "[ui_ident=io_data_load]",
- );
-
- self.IO_HTML_TMPL = document.getElementById("loading_skeleton");
-
- if (!self.IO_DATA_ELEMENT || !self.IO_DATA_MARKER) {
- console.warn(
- "ui::io_data_load called, but required elements don't exist",
+ await self.io_load_data();
+ break;
+ }
+ },
+ {
+ root: document.body,
+ rootMargin: "0px",
+ threshold: 1,
+ },
);
- return;
- }
+ // ...
+ self.IO_DATA_MARKER = document.querySelector(
+ "[ui_ident=io_data_marker]",
+ );
- self.IO_DATA_TMPL = tmpl;
- self.IO_DATA_PAGE = page;
- self.IO_DATA_SEEN_IDS = [];
- self.IO_DATA_WAITING = false;
- self.IO_HAS_LOADED_AT_LEAST_ONCE = false;
- self.IO_DATA_DISCONNECTED = false;
- self.IO_DATA_DISABLE_RELOAD = false;
+ self.IO_DATA_ELEMENT = document.querySelector(
+ "[ui_ident=io_data_load]",
+ );
- if (!paginated_mode) {
- self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
- } else {
- // immediately load first page
- self.IO_DATA_TMPL = self.IO_DATA_TMPL.replace("&page=", "");
- self.IO_DATA_TMPL += `&paginated=true&page=`;
- self.io_load_data();
- }
+ self.IO_HTML_TMPL = document.getElementById("loading_skeleton");
+
+ if (!self.IO_DATA_ELEMENT || !self.IO_DATA_MARKER) {
+ console.warn(
+ "ui::io_data_load called, but required elements don't exist",
+ );
- setTimeout(() => {
- if (self.IO_DATA_DISABLE_RELOAD) {
- console.log("missing data reload disabled");
return;
}
- if (!self.IO_HAS_LOADED_AT_LEAST_ONCE) {
- // reload
- self.IO_DATA_OBSERVER.disconnect();
- console.log("timeline load fail :(");
- window.location.reload();
- }
- }, 1500);
+ self.IO_DATA_TMPL = tmpl;
+ self.IO_DATA_PAGE = page;
+ self.IO_DATA_SEEN_IDS = [];
+ self.IO_DATA_WAITING = false;
+ self.IO_HAS_LOADED_AT_LEAST_ONCE = false;
+ self.IO_DATA_DISCONNECTED = false;
+ self.IO_DATA_DISABLE_RELOAD = false;
- self.IO_PAGINATED = paginated_mode;
- });
+ if (!paginated_mode) {
+ self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
+ } else {
+ // immediately load first page
+ self.IO_DATA_TMPL = self.IO_DATA_TMPL.replace("&page=", "");
+ self.IO_DATA_TMPL += `&paginated=true&page=`;
+ self.io_load_data();
+ }
+
+ setTimeout(() => {
+ if (self.IO_DATA_DISABLE_RELOAD) {
+ console.log("missing data reload disabled");
+ return;
+ }
+
+ if (!self.IO_HAS_LOADED_AT_LEAST_ONCE) {
+ // reload
+ self.IO_DATA_OBSERVER.disconnect();
+ console.log("timeline load fail :(");
+ window.location.reload();
+ }
+ }, 1500);
+
+ self.IO_PAGINATED = paginated_mode;
+ },
+ );
self.define("io_load_data", async () => {
if (self.IO_DATA_WAITING) {
diff --git a/crates/app/src/routes/api/v1/auth/profile.rs b/crates/app/src/routes/api/v1/auth/profile.rs
index baf2f54..0d2bc49 100644
--- a/crates/app/src/routes/api/v1/auth/profile.rs
+++ b/crates/app/src/routes/api/v1/auth/profile.rs
@@ -116,7 +116,7 @@ pub async fn update_user_settings_request(
Json(mut req): Json,
) -> impl IntoResponse {
let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProfile) {
+ let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProfile) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@@ -153,7 +153,7 @@ pub async fn update_user_settings_request(
// award achievement
if let Err(e) = data
- .add_achievement(&user, AchievementName::EditSettings.into())
+ .add_achievement(&mut user, AchievementName::EditSettings.into())
.await
{
return Json(e.into());
diff --git a/crates/app/src/routes/api/v1/auth/social.rs b/crates/app/src/routes/api/v1/auth/social.rs
index a507e49..53aff80 100644
--- a/crates/app/src/routes/api/v1/auth/social.rs
+++ b/crates/app/src/routes/api/v1/auth/social.rs
@@ -22,7 +22,7 @@ pub async fn follow_request(
Extension(data): Extension,
) -> impl IntoResponse {
let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
+ let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@@ -40,7 +40,7 @@ pub async fn follow_request(
} else {
// create
match data
- .create_userfollow(UserFollow::new(user.id, id), false)
+ .create_userfollow(UserFollow::new(user.id, id), &user, false)
.await
{
Ok(r) => {
@@ -59,13 +59,15 @@ pub async fn follow_request(
return Json(e.into());
};
+ // award achievement
if let Err(e) = data
- .add_achievement(&user, AchievementName::FollowUser.into())
+ .add_achievement(&mut user, AchievementName::FollowUser.into())
.await
{
return Json(e.into());
}
+ // ...
Json(ApiReturn {
ok: true,
message: "User followed".to_string(),
@@ -123,7 +125,7 @@ pub async fn accept_follow_request(
// create follow
match data
- .create_userfollow(UserFollow::new(id, user.id), true)
+ .create_userfollow(UserFollow::new(id, user.id), &user, true)
.await
{
Ok(_) => {
diff --git a/crates/app/src/routes/api/v1/communities/posts.rs b/crates/app/src/routes/api/v1/communities/posts.rs
index e1fb89e..fee1ba8 100644
--- a/crates/app/src/routes/api/v1/communities/posts.rs
+++ b/crates/app/src/routes/api/v1/communities/posts.rs
@@ -37,7 +37,7 @@ pub async fn create_request(
JsonMultipart(images, req): JsonMultipart,
) -> impl IntoResponse {
let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreatePosts) {
+ let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreatePosts) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@@ -181,7 +181,7 @@ pub async fn create_request(
// achievements
if let Err(e) = data
- .add_achievement(&user, AchievementName::CreatePost.into())
+ .add_achievement(&mut user, AchievementName::CreatePost.into())
.await
{
return Json(e.into());
@@ -189,7 +189,7 @@ pub async fn create_request(
if user.post_count >= 49 {
if let Err(e) = data
- .add_achievement(&user, AchievementName::Create50Posts.into())
+ .add_achievement(&mut user, AchievementName::Create50Posts.into())
.await
{
return Json(e.into());
@@ -198,7 +198,7 @@ pub async fn create_request(
if user.post_count >= 99 {
if let Err(e) = data
- .add_achievement(&user, AchievementName::Create100Posts.into())
+ .add_achievement(&mut user, AchievementName::Create100Posts.into())
.await
{
return Json(e.into());
@@ -207,7 +207,7 @@ pub async fn create_request(
if user.post_count >= 999 {
if let Err(e) = data
- .add_achievement(&user, AchievementName::Create1000Posts.into())
+ .add_achievement(&mut user, AchievementName::Create1000Posts.into())
.await
{
return Json(e.into());
diff --git a/crates/app/src/routes/api/v1/communities/questions.rs b/crates/app/src/routes/api/v1/communities/questions.rs
index c469512..ec24c08 100644
--- a/crates/app/src/routes/api/v1/communities/questions.rs
+++ b/crates/app/src/routes/api/v1/communities/questions.rs
@@ -52,12 +52,23 @@ pub async fn create_request(
// award achievement
if let Some(ref user) = user {
+ let mut user = user.clone();
+
if let Err(e) = data
- .add_achievement(user, AchievementName::CreateQuestion.into())
+ .add_achievement(&mut user, AchievementName::CreateQuestion.into())
.await
{
return Json(e.into());
}
+
+ if drawings.len() > 0 {
+ if let Err(e) = data
+ .add_achievement(&mut user, AchievementName::CreateDrawing.into())
+ .await
+ {
+ return Json(e.into());
+ }
+ }
}
// ...
diff --git a/crates/app/src/routes/api/v1/journals.rs b/crates/app/src/routes/api/v1/journals.rs
index e2f9404..95ae3da 100644
--- a/crates/app/src/routes/api/v1/journals.rs
+++ b/crates/app/src/routes/api/v1/journals.rs
@@ -98,7 +98,7 @@ pub async fn create_request(
Json(props): Json,
) -> impl IntoResponse {
let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateJournals) {
+ let mut user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateJournals) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@@ -110,7 +110,7 @@ pub async fn create_request(
Ok(x) => {
// award achievement
if let Err(e) = data
- .add_achievement(&user, AchievementName::CreateJournal.into())
+ .add_achievement(&mut user, AchievementName::CreateJournal.into())
.await
{
return Json(e.into());
diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs
index 44f54aa..3b99c5d 100644
--- a/crates/app/src/routes/pages/misc.rs
+++ b/crates/app/src/routes/pages/misc.rs
@@ -10,7 +10,10 @@ use axum::{
use axum_extra::extract::CookieJar;
use serde::Deserialize;
use tetratto_core::model::{
- auth::DefaultTimelineChoice, permissions::FinePermission, requests::ActionType, Error,
+ auth::{AchievementName, DefaultTimelineChoice},
+ permissions::FinePermission,
+ requests::ActionType,
+ Error,
};
use std::fs::read_to_string;
use pathbufd::PathBufD;
@@ -447,7 +450,7 @@ pub async fn achievements_request(
Extension(data): Extension,
) -> impl IntoResponse {
let data = data.read().await;
- let user = match get_user_from_token!(jar, data.0) {
+ let mut user = match get_user_from_token!(jar, data.0) {
Some(ua) => ua,
None => {
return Err(Html(
@@ -458,6 +461,15 @@ pub async fn achievements_request(
let achievements = data.0.fill_achievements(user.achievements.clone());
+ // award achievement
+ if let Err(e) = data
+ .0
+ .add_achievement(&mut user, AchievementName::OpenAchievements.into())
+ .await
+ {
+ return Err(Html(render_error(e, &jar, &data, &None).await));
+ }
+
// ...
let lang = get_lang!(jar, data.0);
let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await;
diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml
index b2fd135..afcecb0 100644
--- a/crates/core/Cargo.toml
+++ b/crates/core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tetratto-core"
-version = "9.0.0"
+version = "10.0.0"
edition = "2024"
[dependencies]
diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs
index 4baeef0..a34b634 100644
--- a/crates/core/src/database/auth.rs
+++ b/crates/core/src/database/auth.rs
@@ -712,7 +712,7 @@ impl DataManager {
/// Add an achievement to a user.
///
/// Still returns `Ok` if the user already has the achievement.
- pub async fn add_achievement(&self, user: &User, achievement: Achievement) -> Result<()> {
+ pub async fn add_achievement(&self, user: &mut User, achievement: Achievement) -> Result<()> {
if user
.achievements
.iter()
@@ -734,9 +734,8 @@ impl DataManager {
.await?;
// add achievement
- let mut user = user.clone();
user.achievements.push(achievement);
- self.update_user_achievements(user.id, user.achievements)
+ self.update_user_achievements(user.id, user.achievements.to_owned())
.await?;
Ok(())
diff --git a/crates/core/src/database/reactions.rs b/crates/core/src/database/reactions.rs
index 347548f..4d15190 100644
--- a/crates/core/src/database/reactions.rs
+++ b/crates/core/src/database/reactions.rs
@@ -1,9 +1,9 @@
use oiseau::cache::Cache;
use crate::model::{
- Error, Result,
- auth::{Notification, User},
+ auth::{AchievementName, Notification, User},
permissions::FinePermission,
reactions::{AssetType, Reaction},
+ Error, Result,
};
use crate::{auto_method, DataManager};
@@ -148,6 +148,33 @@ impl DataManager {
{
return Err(Error::NotAllowed);
}
+
+ // achievements
+ if user.id != post.owner {
+ let mut owner = self.get_user_by_id(post.owner).await?;
+ self.add_achievement(&mut owner, AchievementName::Get1Like.into())
+ .await?;
+
+ if post.likes >= 9 {
+ self.add_achievement(&mut owner, AchievementName::Get10Likes.into())
+ .await?;
+ }
+
+ if post.likes >= 49 {
+ self.add_achievement(&mut owner, AchievementName::Get50Likes.into())
+ .await?;
+ }
+
+ if post.likes >= 99 {
+ self.add_achievement(&mut owner, AchievementName::Get100Likes.into())
+ .await?;
+ }
+
+ if post.dislikes >= 24 {
+ self.add_achievement(&mut owner, AchievementName::Get25Dislikes.into())
+ .await?;
+ }
+ }
} else if data.asset_type == AssetType::Question {
let question = self.get_question_by_id(data.asset).await?;
diff --git a/crates/core/src/database/userfollows.rs b/crates/core/src/database/userfollows.rs
index 09504c0..3409443 100644
--- a/crates/core/src/database/userfollows.rs
+++ b/crates/core/src/database/userfollows.rs
@@ -1,5 +1,5 @@
use oiseau::cache::Cache;
-use crate::model::auth::FollowResult;
+use crate::model::auth::{AchievementName, FollowResult};
use crate::model::requests::{ActionRequest, ActionType};
use crate::model::{Error, Result, auth::User, auth::UserFollow, permissions::FinePermission};
use crate::{auto_method, DataManager};
@@ -238,9 +238,14 @@ impl DataManager {
/// # Arguments
/// * `data` - a mock [`UserFollow`] object to insert
/// * `force` - if we should skip the request stage
- pub async fn create_userfollow(&self, data: UserFollow, force: bool) -> Result {
+ pub async fn create_userfollow(
+ &self,
+ data: UserFollow,
+ initiator: &User,
+ force: bool,
+ ) -> Result {
if !force {
- let other_user = self.get_user_by_id(data.receiver).await?;
+ let mut other_user = self.get_user_by_id(data.receiver).await?;
if other_user.settings.private_profile {
// send follow request instead
@@ -254,6 +259,12 @@ impl DataManager {
return Ok(FollowResult::Requested);
}
+
+ // check if we're staff
+ if initiator.permissions.check(FinePermission::STAFF_BADGE) {
+ self.add_achievement(&mut other_user, AchievementName::FollowedByStaff.into())
+ .await?;
+ }
}
// ...
diff --git a/crates/core/src/model/auth.rs b/crates/core/src/model/auth.rs
index 4617770..11f015f 100644
--- a/crates/core/src/model/auth.rs
+++ b/crates/core/src/model/auth.rs
@@ -258,6 +258,9 @@ pub struct UserSettings {
/// Automatically clear all notifications when notifications are viewed.
#[serde(default)]
pub auto_clear_notifs: bool,
+ /// Increase the text size of buttons and paragraphs.
+ #[serde(default)]
+ pub large_text: bool,
}
fn mime_avif() -> String {
@@ -475,26 +478,26 @@ pub struct ExternalConnectionData {
}
/// The total number of achievements needed to 100% Tetratto!
-pub const ACHIEVEMENTS: usize = 8;
+pub const ACHIEVEMENTS: usize = 16;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AchievementName {
- /// Create your first post.
CreatePost,
- /// Follow somebody.
FollowUser,
- /// Create your 50th post.
Create50Posts,
- /// Create your 100th post.
Create100Posts,
- /// Create your 1000th post.
Create1000Posts,
- /// Ask your first question.
CreateQuestion,
- /// Edit your settings.
EditSettings,
- /// Create your first journal.
CreateJournal,
+ FollowedByStaff,
+ CreateDrawing,
+ OpenAchievements,
+ Get1Like,
+ Get10Likes,
+ Get50Likes,
+ Get100Likes,
+ Get25Dislikes,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@@ -515,6 +518,14 @@ impl AchievementName {
Self::CreateQuestion => "Big questions...",
Self::EditSettings => "Just how I like it!",
Self::CreateJournal => "Dear diary...",
+ Self::FollowedByStaff => "Big Shrimpin'",
+ Self::CreateDrawing => "Modern art",
+ Self::OpenAchievements => "Welcome!",
+ Self::Get1Like => "Baby steps!",
+ Self::Get10Likes => "WOW! 10 LIKES!",
+ Self::Get50Likes => "banger post follow for more",
+ Self::Get100Likes => "everyone liked that",
+ Self::Get25Dislikes => "Sorry...",
}
}
@@ -528,19 +539,37 @@ impl AchievementName {
Self::CreateQuestion => "Ask your first question!",
Self::EditSettings => "Edit your settings.",
Self::CreateJournal => "Create your first journal.",
+ Self::FollowedByStaff => "Get followed by a staff member!",
+ Self::CreateDrawing => "Include a drawing in a question.",
+ Self::OpenAchievements => "Open the achievements page.",
+ Self::Get1Like => "Get 1 like on a post! Good job!",
+ Self::Get10Likes => "Get 10 likes on one post.",
+ Self::Get50Likes => "Get 50 likes on one post.",
+ Self::Get100Likes => "Get 100 likes on one post.",
+ Self::Get25Dislikes => "Get 25 dislikes on one post... :(",
}
}
pub fn rarity(&self) -> AchievementRarity {
+ // i don't want to write that long ass type name everywhere
+ use AchievementRarity::*;
match self {
- Self::CreatePost => AchievementRarity::Common,
- Self::FollowUser => AchievementRarity::Common,
- Self::Create50Posts => AchievementRarity::Uncommon,
- Self::Create100Posts => AchievementRarity::Uncommon,
- Self::Create1000Posts => AchievementRarity::Rare,
- Self::CreateQuestion => AchievementRarity::Common,
- Self::EditSettings => AchievementRarity::Common,
- Self::CreateJournal => AchievementRarity::Uncommon,
+ Self::CreatePost => Common,
+ Self::FollowUser => Common,
+ Self::Create50Posts => Uncommon,
+ Self::Create100Posts => Uncommon,
+ Self::Create1000Posts => Rare,
+ Self::CreateQuestion => Common,
+ Self::EditSettings => Common,
+ Self::CreateJournal => Uncommon,
+ Self::FollowedByStaff => Rare,
+ Self::CreateDrawing => Common,
+ Self::OpenAchievements => Common,
+ Self::Get1Like => Common,
+ Self::Get10Likes => Common,
+ Self::Get50Likes => Uncommon,
+ Self::Get100Likes => Rare,
+ Self::Get25Dislikes => Uncommon,
}
}
}
diff --git a/crates/l10n/Cargo.toml b/crates/l10n/Cargo.toml
index c50b714..1b3e5e1 100644
--- a/crates/l10n/Cargo.toml
+++ b/crates/l10n/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tetratto-l10n"
-version = "9.0.0"
+version = "10.0.0"
edition = "2024"
authors.workspace = true
repository.workspace = true
diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml
index 8d48901..5fd4230 100644
--- a/crates/shared/Cargo.toml
+++ b/crates/shared/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tetratto-shared"
-version = "9.0.0"
+version = "10.0.0"
edition = "2024"
authors.workspace = true
repository.workspace = true