add: policy achievements
This commit is contained in:
parent
d90b08720a
commit
973373426a
7 changed files with 83 additions and 13 deletions
|
@ -1135,15 +1135,13 @@
|
||||||
(icon (text "rabbit"))
|
(icon (text "rabbit"))
|
||||||
(str (text "general:link.reference")))
|
(str (text "general:link.reference")))
|
||||||
|
|
||||||
(a
|
(button
|
||||||
("href" "{{ config.policies.terms_of_service }}")
|
("onclick" "trigger('me::achievement', ['OpenTos']); Turbo.visit('{{ config.policies.terms_of_service }}')")
|
||||||
("class" "button")
|
|
||||||
(icon (text "heart-handshake"))
|
(icon (text "heart-handshake"))
|
||||||
(text "Terms of service"))
|
(text "Terms of service"))
|
||||||
|
|
||||||
(a
|
(button
|
||||||
("href" "{{ config.policies.privacy }}")
|
("onclick" "trigger('me::achievement', ['OpenPrivacyPolicy']); Turbo.visit('{{ config.policies.privacy }}')")
|
||||||
("class" "button")
|
|
||||||
(icon (text "cookie"))
|
(icon (text "cookie"))
|
||||||
(text "Privacy policy"))
|
(text "Privacy policy"))
|
||||||
(b ("class" "title") (str (text "general:label.account")))
|
(b ("class" "title") (str (text "general:label.account")))
|
||||||
|
|
|
@ -1522,7 +1522,7 @@
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
\"hide_associated_blocked_users\",
|
\"hide_associated_blocked_users\",
|
||||||
\"Hide users that you've blocked on your other accounts from timelines.\",
|
\"Hide users that you've blocked on your other accounts from timelines\",
|
||||||
],
|
],
|
||||||
\"{{ profile.settings.hide_associated_blocked_users }}\",
|
\"{{ profile.settings.hide_associated_blocked_users }}\",
|
||||||
\"checkbox\",
|
\"checkbox\",
|
||||||
|
|
|
@ -342,6 +342,27 @@
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.define("achievement", async (_, name) => {
|
||||||
|
fetch("/api/v1/auth/user/me/achievement", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
trigger("atto::toast", [
|
||||||
|
res.ok ? "success" : "error",
|
||||||
|
res.message,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
self.define("report", (_, asset, asset_type) => {
|
self.define("report", (_, asset, asset_type) => {
|
||||||
window.open(
|
window.open(
|
||||||
`/mod_panel/file_report?asset=${asset}&asset_type=${asset_type}`,
|
`/mod_panel/file_report?asset=${asset}&asset_type=${asset_type}`,
|
||||||
|
|
|
@ -3,8 +3,9 @@ use crate::{
|
||||||
get_user_from_token,
|
get_user_from_token,
|
||||||
model::{ApiReturn, Error},
|
model::{ApiReturn, Error},
|
||||||
routes::api::v1::{
|
routes::api::v1::{
|
||||||
AppendAssociations, DeleteUser, DisableTotp, RefreshGrantToken, UpdateSecondaryUserRole,
|
AppendAssociations, AwardAchievement, DeleteUser, DisableTotp, RefreshGrantToken,
|
||||||
UpdateUserIsVerified, UpdateUserPassword, UpdateUserRole, UpdateUserUsername,
|
UpdateSecondaryUserRole, UpdateUserIsVerified, UpdateUserPassword, UpdateUserRole,
|
||||||
|
UpdateUserUsername,
|
||||||
},
|
},
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
|
@ -21,7 +22,7 @@ use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||||
use tetratto_core::{
|
use tetratto_core::{
|
||||||
cache::Cache,
|
cache::Cache,
|
||||||
model::{
|
model::{
|
||||||
auth::{AchievementName, InviteCode, Token, UserSettings},
|
auth::{AchievementName, InviteCode, Token, UserSettings, SELF_SERVE_ACHIEVEMENTS},
|
||||||
moderation::AuditLogEntry,
|
moderation::AuditLogEntry,
|
||||||
oauth,
|
oauth,
|
||||||
permissions::FinePermission,
|
permissions::FinePermission,
|
||||||
|
@ -920,3 +921,31 @@ pub async fn generate_invite_codes_request(
|
||||||
payload: Some((out_string, errors_string)),
|
payload: Some((out_string, errors_string)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Award an achievement to the current user.
|
||||||
|
/// Only works with specific "self-serve" achievements.
|
||||||
|
pub async fn self_serve_achievement_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
Json(req): Json<AwardAchievement>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = &(data.read().await).0;
|
||||||
|
let mut user = match get_user_from_token!(jar, data) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => return Json(Error::NotAllowed.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !SELF_SERVE_ACHIEVEMENTS.contains(&req.name) {
|
||||||
|
return Json(Error::MiscError("Cannot grant this achievement manually".to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// award achievement
|
||||||
|
match data.add_achievement(&mut user, req.name.into()).await {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
|
ok: true,
|
||||||
|
message: "Achievement granted".to_string(),
|
||||||
|
payload: (),
|
||||||
|
}),
|
||||||
|
Err(e) => Json(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use axum::{
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tetratto_core::model::{
|
use tetratto_core::model::{
|
||||||
apps::AppQuota,
|
apps::AppQuota,
|
||||||
|
auth::AchievementName,
|
||||||
communities::{
|
communities::{
|
||||||
CommunityContext, CommunityJoinAccess, CommunityReadAccess, CommunityWriteAccess,
|
CommunityContext, CommunityJoinAccess, CommunityReadAccess, CommunityWriteAccess,
|
||||||
PollOption, PostContext,
|
PollOption, PostContext,
|
||||||
|
@ -387,6 +388,10 @@ pub fn routes() -> Router {
|
||||||
"/auth/user/{id}/grants/{app}/refresh",
|
"/auth/user/{id}/grants/{app}/refresh",
|
||||||
post(auth::profile::refresh_grant_request),
|
post(auth::profile::refresh_grant_request),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/auth/user/me/achievement",
|
||||||
|
post(auth::profile::self_serve_achievement_request),
|
||||||
|
)
|
||||||
// apps
|
// apps
|
||||||
.route("/apps", post(apps::create_request))
|
.route("/apps", post(apps::create_request))
|
||||||
.route("/apps/{id}/title", post(apps::update_title_request))
|
.route("/apps/{id}/title", post(apps::update_title_request))
|
||||||
|
@ -976,7 +981,13 @@ pub struct AddJournalDir {
|
||||||
pub struct RemoveJournalDir {
|
pub struct RemoveJournalDir {
|
||||||
pub dir: String,
|
pub dir: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct UpdateNoteTags {
|
pub struct UpdateNoteTags {
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct AwardAchievement {
|
||||||
|
pub name: AchievementName,
|
||||||
|
}
|
||||||
|
|
|
@ -493,7 +493,10 @@ 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 = 28;
|
pub const ACHIEVEMENTS: usize = 30;
|
||||||
|
/// "self-serve" achievements can be granted by the user through the API.
|
||||||
|
pub const SELF_SERVE_ACHIEVEMENTS: &[AchievementName] =
|
||||||
|
&[AchievementName::OpenTos, AchievementName::OpenPrivacyPolicy];
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub enum AchievementName {
|
pub enum AchievementName {
|
||||||
|
@ -525,6 +528,8 @@ pub enum AchievementName {
|
||||||
EditNote,
|
EditNote,
|
||||||
CreatePostWithTitle,
|
CreatePostWithTitle,
|
||||||
CreateRepost,
|
CreateRepost,
|
||||||
|
OpenTos,
|
||||||
|
OpenPrivacyPolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -565,6 +570,8 @@ impl AchievementName {
|
||||||
Self::EditNote => "I take it back!",
|
Self::EditNote => "I take it back!",
|
||||||
Self::CreatePostWithTitle => "Must declutter",
|
Self::CreatePostWithTitle => "Must declutter",
|
||||||
Self::CreateRepost => "More than a like or comment...",
|
Self::CreateRepost => "More than a like or comment...",
|
||||||
|
Self::OpenTos => "Well informed!",
|
||||||
|
Self::OpenPrivacyPolicy => "Privacy conscious",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,6 +605,8 @@ impl AchievementName {
|
||||||
Self::EditNote => "Edit a note.",
|
Self::EditNote => "Edit a note.",
|
||||||
Self::CreatePostWithTitle => "Create a post with a title.",
|
Self::CreatePostWithTitle => "Create a post with a title.",
|
||||||
Self::CreateRepost => "Create a repost or quote.",
|
Self::CreateRepost => "Create a repost or quote.",
|
||||||
|
Self::OpenTos => "Open the terms of service.",
|
||||||
|
Self::OpenPrivacyPolicy => "Open the privacy policy.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,6 +642,8 @@ impl AchievementName {
|
||||||
Self::EditNote => Uncommon,
|
Self::EditNote => Uncommon,
|
||||||
Self::CreatePostWithTitle => Common,
|
Self::CreatePostWithTitle => Common,
|
||||||
Self::CreateRepost => Common,
|
Self::CreateRepost => Common,
|
||||||
|
Self::OpenTos => Uncommon,
|
||||||
|
Self::OpenPrivacyPolicy => Uncommon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ user = "user"
|
||||||
password = "postgres"
|
password = "postgres"
|
||||||
|
|
||||||
[policies]
|
[policies]
|
||||||
terms_of_service = "/public/tos.html"
|
terms_of_service = "/doc/tos.md"
|
||||||
privacy = "/public/privacy.html"
|
privacy = "/doc/privacy.md"
|
||||||
|
|
||||||
[turnstile]
|
[turnstile]
|
||||||
site_key = "1x00000000000000000000AA"
|
site_key = "1x00000000000000000000AA"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue