feat: blocking frontend (#6911)

* feat: blocking api interfaces

* feat: block action on user pages + shared instance flows

* feat: safety settings subpage

* feat: interaction source settings

* feat: finish social settings

* feat: profile settings xplat

* fix: prepr

* feat: click on friends

* default instance -> game options & fix feature flag width

* fix: scroll indicators

---------

Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
Calum H.
2026-07-28 20:02:58 +00:00
committed by GitHub
co-authored by Prospector
parent cbb31f31c0
commit f89c116bf8
103 changed files with 2166 additions and 1075 deletions
+113 -1
View File
@@ -1,5 +1,6 @@
use crate::State;
use crate::util::fetch::{fetch_advanced, fetch_json};
use crate::util::fetch::{fetch_advanced, fetch_advanced_bytes, fetch_json};
use bytes::Bytes;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -129,3 +130,114 @@ pub async fn patch_user(user_id: &str, patch: Value) -> crate::Result<()> {
Ok(())
}
#[tracing::instrument(skip(image))]
pub async fn change_user_avatar(
user_id: &str,
image: Bytes,
extension: &str,
) -> crate::Result<()> {
let state = State::get().await?;
let user_id = urlencoding::encode(user_id);
let extension = urlencoding::encode(extension);
fetch_advanced_bytes(
Method::PATCH,
&format!(
"{}user/{}/icon?ext={}",
env!("MODRINTH_API_URL"),
user_id,
extension
),
image,
Some(("Content-Type", "application/octet-stream")),
Some("/v2/user/:id/icon"),
&state.api_semaphore,
&state.pool,
)
.await?;
Ok(())
}
#[tracing::instrument]
pub async fn delete_user_avatar(user_id: &str) -> crate::Result<()> {
let state = State::get().await?;
let user_id = urlencoding::encode(user_id);
fetch_advanced(
Method::DELETE,
&format!("{}user/{}/icon", env!("MODRINTH_API_URL"), user_id),
None,
None,
None,
None,
None,
Some("/v2/user/:id/icon"),
&state.api_semaphore,
&state.pool,
)
.await?;
Ok(())
}
#[tracing::instrument]
pub async fn block_user(user_id: &str) -> crate::Result<()> {
let state = State::get().await?;
let user_id = urlencoding::encode(user_id);
fetch_advanced(
Method::POST,
&format!("{}block/{}", env!("MODRINTH_API_URL_V3"), user_id),
None,
None,
None,
None,
None,
Some("/v3/block/:id"),
&state.api_semaphore,
&state.pool,
)
.await?;
Ok(())
}
#[tracing::instrument]
pub async fn unblock_user(user_id: &str) -> crate::Result<()> {
let state = State::get().await?;
let user_id = urlencoding::encode(user_id);
fetch_advanced(
Method::DELETE,
&format!("{}block/{}", env!("MODRINTH_API_URL_V3"), user_id),
None,
None,
None,
None,
None,
Some("/v3/block/:id"),
&state.api_semaphore,
&state.pool,
)
.await?;
Ok(())
}
#[tracing::instrument]
pub async fn get_blocked_users() -> crate::Result<Vec<String>> {
let state = State::get().await?;
fetch_json(
Method::GET,
&format!("{}blocks", env!("MODRINTH_API_URL_V3")),
None,
None,
Some("/v3/blocks"),
&state.api_semaphore,
&state.pool,
)
.await
}
+38 -1
View File
@@ -428,6 +428,7 @@ pub async fn fetch_with_client_progress(
sha1,
None,
None,
None,
download_meta,
None,
uri_path,
@@ -493,6 +494,35 @@ pub async fn fetch_advanced(
.await
}
#[tracing::instrument(skip(body, semaphore))]
#[allow(clippy::too_many_arguments)]
pub async fn fetch_advanced_bytes(
method: Method,
url: &str,
body: Bytes,
header: Option<(&str, &str)>,
uri_path: Option<&'static str>,
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<Bytes> {
fetch_advanced_with_client_and_progress(
method,
url,
None,
None,
Some(body),
header,
None,
None,
uri_path,
semaphore,
exec,
&INSECURE_REQWEST_CLIENT,
None,
)
.await
}
#[tracing::instrument(skip(json_body, semaphore, progress))]
#[allow(clippy::too_many_arguments)]
pub async fn fetch_advanced_with_progress(
@@ -513,6 +543,7 @@ pub async fn fetch_advanced_with_progress(
url,
sha1,
json_body,
None,
header,
download_meta,
loading_bar,
@@ -546,6 +577,7 @@ pub async fn fetch_advanced_with_client(
url,
sha1,
json_body,
None,
header,
download_meta,
loading_bar,
@@ -558,13 +590,16 @@ pub async fn fetch_advanced_with_client(
.await
}
#[tracing::instrument(skip(json_body, semaphore, client, progress))]
#[tracing::instrument(skip(
json_body, bytes_body, semaphore, client, progress
))]
#[allow(clippy::too_many_arguments)]
async fn fetch_advanced_with_client_and_progress(
method: Method,
url: &str,
sha1: Option<&str>,
json_body: Option<serde_json::Value>,
bytes_body: Option<Bytes>,
header: Option<(&str, &str)>,
download_meta: Option<&DownloadMeta>,
loading_bar: Option<(&LoadingBarId, f64)>,
@@ -611,6 +646,8 @@ async fn fetch_advanced_with_client_and_progress(
if let Some(body) = json_body.clone() {
req = req.json(&body);
} else if let Some(body) = bytes_body.clone() {
req = req.body(body);
}
if let Some(header) = header {