MR App 0.9.5 - Big bugfix update (#3585)

* Add launcher_feature_version to Profile

* Misc fixes

- Add typing to theme and settings stuff
- Push instance route on creation from installing a modpack
- Fixed servers not reloading properly when first added

* Make old instances scan the logs folder for joined servers on launcher startup

* Create AttachedWorldData

* Change AttachedWorldData interface

* Rename WorldType::World to WorldType::Singleplayer

* Implement world display status system

* Fix Minecraft font

* Fix set_world_display_status Tauri error

* Add 'Play instance' option

* Add option to disable worlds showing in Home

* Fixes

- Fix available server filter only showing if there are some available
- Fixed server and singleplayer filters sometimes showing when there are only servers or singleplayer worlds
- Fixed new worlds not being automatically added when detected
- Rephrased Jump back into worlds option description

* Fixed sometimes more than 6 items showing up in Jump back in

* Fix servers.dat issue with instances you haven't played before

* Fix too large of bulk requests being made, limit max to 800 #3430

* Add hiding from home page, add types to Mods.vue

* Make recent worlds go into grid when display is huge

* Fix lint

* Remove redundant media query

* Fix protocol version on home page, and home page being blocked by pinging servers

* Clippy fix

* More Clippy fixes

* Fix Prettier lints

* Undo `from_string` changes

---------

Co-authored-by: Josiah Glosson <soujournme@gmail.com>
Co-authored-by: Alejandro González <me@alegon.dev>
This commit is contained in:
Prospector
2025-05-01 16:13:13 -07:00
committed by GitHub
parent 4a2605bc1e
commit 3dad6b317f
123 changed files with 1622 additions and 744 deletions

View File

@@ -71,7 +71,7 @@ impl ApiV2 {
};
let req = test::TestRequest::get()
.uri(&format!("/v2/search?{}{}", query_field, facets_field))
.uri(&format!("/v2/search?{query_field}{facets_field}"))
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
@@ -99,7 +99,7 @@ impl ApiProject for ApiV2 {
// Approve as a moderator.
let req = TestRequest::patch()
.uri(&format!("/v2/project/{}", slug))
.uri(&format!("/v2/project/{slug}"))
.append_pat(MOD_USER_PAT)
.set_json(json!(
{
@@ -114,7 +114,7 @@ impl ApiProject for ApiV2 {
// Get project's versions
let req = TestRequest::get()
.uri(&format!("/v2/project/{}/version", slug))
.uri(&format!("/v2/project/{slug}/version"))
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
@@ -217,7 +217,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v2/user/{}/projects", user_id_or_username))
.uri(&format!("/v2/user/{user_id_or_username}/projects"))
.append_pat(pat)
.to_request();
self.call(req).await
@@ -260,7 +260,7 @@ impl ApiProject for ApiV2 {
) -> ServiceResponse {
let projects_str = ids_or_slugs
.iter()
.map(|s| format!("\"{}\"", s))
.map(|s| format!("\"{s}\""))
.collect::<Vec<_>>()
.join(",");
let req = test::TestRequest::patch()
@@ -490,13 +490,13 @@ impl ApiProject for ApiV2 {
featured = featured
);
if let Some(title) = title {
url.push_str(&format!("&title={}", title));
url.push_str(&format!("&title={title}"));
}
if let Some(description) = description {
url.push_str(&format!("&description={}", description));
url.push_str(&format!("&description={description}"));
}
if let Some(ordering) = ordering {
url.push_str(&format!("&ordering={}", ordering));
url.push_str(&format!("&ordering={ordering}"));
}
let req = test::TestRequest::post()
@@ -542,10 +542,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!(
"/v2/project/{id_or_slug}/gallery?url={url}",
url = url
))
.uri(&format!("/v2/project/{id_or_slug}/gallery?url={url}"))
.append_pat(pat)
.to_request();

View File

@@ -11,7 +11,7 @@ impl ApiUser for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v2/user/{}", user_id_or_username))
.uri(&format!("/v2/user/{user_id_or_username}"))
.append_pat(pat)
.to_request();
self.call(req).await
@@ -32,7 +32,7 @@ impl ApiUser for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::patch()
.uri(&format!("/v2/user/{}", user_id_or_username))
.uri(&format!("/v2/user/{user_id_or_username}"))
.append_pat(pat)
.set_json(patch)
.to_request();
@@ -46,7 +46,7 @@ impl ApiUser for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v2/user/{}", user_id_or_username))
.uri(&format!("/v2/user/{user_id_or_username}"))
.append_pat(pat)
.to_request();

View File

@@ -399,18 +399,18 @@ impl ApiVersion for ApiV2 {
));
}
if let Some(featured) = featured {
query_string.push_str(&format!("&featured={}", featured));
query_string.push_str(&format!("&featured={featured}"));
}
if let Some(version_type) = version_type {
query_string.push_str(&format!("&version_type={}", version_type));
query_string.push_str(&format!("&version_type={version_type}"));
}
if let Some(limit) = limit {
let limit = limit.to_string();
query_string.push_str(&format!("&limit={}", limit));
query_string.push_str(&format!("&limit={limit}"));
}
if let Some(offset) = offset {
let offset = offset.to_string();
query_string.push_str(&format!("&offset={}", offset));
query_string.push_str(&format!("&offset={offset}"));
}
let req = test::TestRequest::get()
@@ -480,7 +480,7 @@ impl ApiVersion for ApiV2 {
) -> ServiceResponse {
let ids = url_encode_json_serialized_vec(&version_ids);
let request = test::TestRequest::get()
.uri(&format!("/v2/versions?ids={}", ids))
.uri(&format!("/v2/versions?ids={ids}"))
.append_pat(pat)
.to_request();
self.call(request).await

View File

@@ -157,7 +157,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/user/{}/collections", user_id_or_username))
.uri(&format!("/v3/user/{user_id_or_username}/collections"))
.append_pat(pat)
.to_request();
self.call(req).await

View File

@@ -104,8 +104,7 @@ impl ApiV3 {
code: auth_code,
redirect_uri: original_redirect_uri,
client_id: serde_json::from_str(&format!(
"\"{}\"",
client_id
"\"{client_id}\""
))
.unwrap(),
})

View File

@@ -47,7 +47,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<OAuthClient> {
let req = TestRequest::get()
.uri(&format!("/v3/user/{}/oauth_apps", user_id))
.uri(&format!("/v3/user/{user_id}/oauth_apps"))
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
@@ -62,7 +62,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = TestRequest::get()
.uri(&format!("/_internal/oauth/app/{}", client_id))
.uri(&format!("/_internal/oauth/app/{client_id}"))
.append_pat(pat)
.to_request();
@@ -93,7 +93,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = TestRequest::delete()
.uri(&format!("/_internal/oauth/app/{}", client_id))
.uri(&format!("/_internal/oauth/app/{client_id}"))
.append_pat(pat)
.to_request();

View File

@@ -53,7 +53,7 @@ impl ApiProject for ApiV3 {
// Approve as a moderator.
let req = TestRequest::patch()
.uri(&format!("/v3/project/{}", slug))
.uri(&format!("/v3/project/{slug}"))
.append_pat(MOD_USER_PAT)
.set_json(json!(
{
@@ -69,7 +69,7 @@ impl ApiProject for ApiV3 {
// Get project's versions
let req = TestRequest::get()
.uri(&format!("/v3/project/{}/version", slug))
.uri(&format!("/v3/project/{slug}/version"))
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
@@ -172,7 +172,7 @@ impl ApiProject for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/user/{}/projects", user_id_or_username))
.uri(&format!("/v3/user/{user_id_or_username}/projects"))
.append_pat(pat)
.to_request();
self.call(req).await
@@ -215,7 +215,7 @@ impl ApiProject for ApiV3 {
) -> ServiceResponse {
let projects_str = ids_or_slugs
.iter()
.map(|s| format!("\"{}\"", s))
.map(|s| format!("\"{s}\""))
.collect::<Vec<_>>()
.join(",");
let req = test::TestRequest::patch()
@@ -363,13 +363,13 @@ impl ApiProject for ApiV3 {
featured = featured
);
if let Some(title) = title {
url.push_str(&format!("&title={}", title));
url.push_str(&format!("&title={title}"));
}
if let Some(description) = description {
url.push_str(&format!("&description={}", description));
url.push_str(&format!("&description={description}"));
}
if let Some(ordering) = ordering {
url.push_str(&format!("&ordering={}", ordering));
url.push_str(&format!("&ordering={ordering}"));
}
let req = test::TestRequest::post()
@@ -416,10 +416,7 @@ impl ApiProject for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!(
"/v3/project/{id_or_slug}/gallery?url={url}",
url = url
))
.uri(&format!("/v3/project/{id_or_slug}/gallery?url={url}"))
.append_pat(pat)
.to_request();
@@ -562,7 +559,7 @@ impl ApiV3 {
};
let req = test::TestRequest::get()
.uri(&format!("/v3/search?{}{}", query_field, facets_field))
.uri(&format!("/v3/search?{query_field}{facets_field}"))
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
@@ -583,12 +580,12 @@ impl ApiV3 {
let version_string: String =
serde_json::to_string(&id_or_slugs).unwrap();
let version_string = urlencoding::encode(&version_string);
format!("version_ids={}", version_string)
format!("version_ids={version_string}")
} else {
let projects_string: String =
serde_json::to_string(&id_or_slugs).unwrap();
let projects_string = urlencoding::encode(&projects_string);
format!("project_ids={}", projects_string)
format!("project_ids={projects_string}")
};
let mut extra_args = String::new();
@@ -605,10 +602,8 @@ impl ApiV3 {
extra_args.push_str(&format!("&end_date={end_date}"));
}
if let Some(resolution_minutes) = resolution_minutes {
extra_args.push_str(&format!(
"&resolution_minutes={}",
resolution_minutes
));
extra_args
.push_str(&format!("&resolution_minutes={resolution_minutes}"));
}
let req = test::TestRequest::get()

View File

@@ -76,7 +76,7 @@ impl ApiV3 {
loader_field: &str,
) -> ServiceResponse {
let req = TestRequest::get()
.uri(&format!("/v3/loader_field?loader_field={}", loader_field))
.uri(&format!("/v3/loader_field?loader_field={loader_field}"))
.append_pat(ADMIN_USER_PAT)
.to_request();
self.call(req).await

View File

@@ -13,7 +13,7 @@ impl ApiUser for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/user/{}", user_id_or_username))
.uri(&format!("/v3/user/{user_id_or_username}"))
.append_pat(pat)
.to_request();
self.call(req).await
@@ -34,7 +34,7 @@ impl ApiUser for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::patch()
.uri(&format!("/v3/user/{}", user_id_or_username))
.uri(&format!("/v3/user/{user_id_or_username}"))
.append_pat(pat)
.set_json(patch)
.to_request();
@@ -48,7 +48,7 @@ impl ApiUser for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v3/user/{}", user_id_or_username))
.uri(&format!("/v3/user/{user_id_or_username}"))
.append_pat(pat)
.to_request();
self.call(req).await

View File

@@ -432,18 +432,18 @@ impl ApiVersion for ApiV3 {
));
}
if let Some(featured) = featured {
query_string.push_str(&format!("&featured={}", featured));
query_string.push_str(&format!("&featured={featured}"));
}
if let Some(version_type) = version_type {
query_string.push_str(&format!("&version_type={}", version_type));
query_string.push_str(&format!("&version_type={version_type}"));
}
if let Some(limit) = limit {
let limit = limit.to_string();
query_string.push_str(&format!("&limit={}", limit));
query_string.push_str(&format!("&limit={limit}"));
}
if let Some(offset) = offset {
let offset = offset.to_string();
query_string.push_str(&format!("&offset={}", offset));
query_string.push_str(&format!("&offset={offset}"));
}
let req = test::TestRequest::get()
@@ -513,7 +513,7 @@ impl ApiVersion for ApiV3 {
) -> ServiceResponse {
let ids = url_encode_json_serialized_vec(&version_ids);
let request = test::TestRequest::get()
.uri(&format!("/v3/versions?ids={}", ids))
.uri(&format!("/v3/versions?ids={ids}"))
.append_pat(pat)
.to_request();
self.call(request).await
@@ -546,10 +546,7 @@ impl ApiVersion for ApiV3 {
Some(file),
);
let request = test::TestRequest::post()
.uri(&format!(
"/v3/version/{version_id}/file",
version_id = version_id
))
.uri(&format!("/v3/version/{version_id}/file"))
.append_pat(pat)
.set_multipart(m)
.to_request();
@@ -562,10 +559,7 @@ impl ApiVersion for ApiV3 {
pat: Option<&str>,
) -> ServiceResponse {
let request = test::TestRequest::delete()
.uri(&format!(
"/v3/version/{version_id}",
version_id = version_id
))
.uri(&format!("/v3/version/{version_id}"))
.append_pat(pat)
.to_request();
self.call(request).await

View File

@@ -137,7 +137,7 @@ impl TemporaryDatabase {
dotenvy::var("DATABASE_URL").expect("No database URL");
let mut template_url =
Url::parse(&url).expect("Invalid database URL");
template_url.set_path(&format!("/{}", TEMPLATE_DATABASE_NAME));
template_url.set_path(&format!("/{TEMPLATE_DATABASE_NAME}"));
let pool = PgPool::connect(template_url.as_str())
.await

View File

@@ -470,8 +470,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != ProjectPermissions::empty() {
return Err(format!(
"Test 1 failed. Expected no permissions, got {:?}",
p
"Test 1 failed. Expected no permissions, got {p:?}"
));
}
@@ -511,8 +510,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != ProjectPermissions::empty() {
return Err(format!(
"Test 2 failed. Expected no permissions, got {:?}",
p
"Test 2 failed. Expected no permissions, got {p:?}"
));
}
@@ -561,8 +559,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != failure_project_permissions {
return Err(format!(
"Test 3 failed. Expected {:?}, got {:?}",
failure_project_permissions, p
"Test 3 failed. Expected {failure_project_permissions:?}, got {p:?}"
));
}
@@ -607,8 +604,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != success_permissions {
return Err(format!(
"Test 4 failed. Expected {:?}, got {:?}",
success_permissions, p
"Test 4 failed. Expected {success_permissions:?}, got {p:?}"
));
}
@@ -666,8 +662,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != failure_project_permissions {
return Err(format!(
"Test 5 failed. Expected {:?}, got {:?}",
failure_project_permissions, p
"Test 5 failed. Expected {failure_project_permissions:?}, got {p:?}"
));
}
@@ -721,8 +716,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != success_permissions {
return Err(format!(
"Test 6 failed. Expected {:?}, got {:?}",
success_permissions, p
"Test 6 failed. Expected {success_permissions:?}, got {p:?}"
));
}
@@ -790,8 +784,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != failure_project_permissions {
return Err(format!(
"Test 7 failed. Expected {:?}, got {:?}",
failure_project_permissions, p
"Test 7 failed. Expected {failure_project_permissions:?}, got {p:?}"
));
}
@@ -856,8 +849,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != success_permissions {
return Err(format!(
"Test 8 failed. Expected {:?}, got {:?}",
success_permissions, p
"Test 8 failed. Expected {success_permissions:?}, got {p:?}"
));
}
@@ -927,8 +919,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != OrganizationPermissions::empty() {
return Err(format!(
"Test 1 failed. Expected no permissions, got {:?}",
p
"Test 1 failed. Expected no permissions, got {p:?}"
));
}
Ok(())
@@ -976,8 +967,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != failure_organization_permissions {
return Err(format!(
"Test 2 failed. Expected {:?}, got {:?}",
failure_organization_permissions, p
"Test 2 failed. Expected {failure_organization_permissions:?}, got {p:?}"
));
}
Ok(())
@@ -1021,8 +1011,7 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if p != success_permissions {
return Err(format!(
"Test 3 failed. Expected {:?}, got {:?}",
success_permissions, p
"Test 3 failed. Expected {success_permissions:?}, got {p:?}"
));
}
Ok(())

View File

@@ -487,7 +487,7 @@ async fn test_multi_get_redis_cache() {
// Create 5 modpacks
let mut modpacks = Vec::new();
for i in 0..5 {
let slug = format!("test-modpack-{}", i);
let slug = format!("test-modpack-{i}");
let creation_data = get_public_project_creation_data(
&slug,
@@ -503,7 +503,7 @@ async fn test_multi_get_redis_cache() {
// Create 5 mods
let mut mods = Vec::new();
for i in 0..5 {
let slug = format!("test-mod-{}", i);
let slug = format!("test-mod-{i}");
let creation_data = get_public_project_creation_data(
&slug,

View File

@@ -28,7 +28,7 @@ async fn oauth_flow_happy_path() {
} = &env.dummy.oauth_client_alpha;
// Initiate authorization
let redirect_uri = format!("{}?foo=bar", base_redirect_uri);
let redirect_uri = format!("{base_redirect_uri}?foo=bar");
let original_state = "1234";
let resp = env
.api

View File

@@ -81,7 +81,7 @@ pub async fn pat_full_test() {
// Change scopes and test again
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"scopes": 0,
@@ -93,7 +93,7 @@ pub async fn pat_full_test() {
// Change scopes back, and set expiry to the past, and test again
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"scopes": Scopes::COLLECTION_CREATE,
@@ -109,21 +109,21 @@ pub async fn pat_full_test() {
// Change everything back to normal and test again
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"expires": Utc::now() + Duration::days(1), // no longer expired!
}))
.to_request();
println!("PAT ID FOR TEST: {}", id);
println!("PAT ID FOR TEST: {id}");
let resp = test_env.call(req).await;
assert_status!(&resp, StatusCode::NO_CONTENT);
assert_eq!(mock_pat_test(access_token).await, 200); // Works again
// Patching to a bad expiry should fail
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"expires": Utc::now() - Duration::days(1), // Past
@@ -140,7 +140,7 @@ pub async fn pat_full_test() {
}
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"scopes": scope.bits(),
@@ -156,7 +156,7 @@ pub async fn pat_full_test() {
// Delete PAT
let req = test::TestRequest::delete()
.append_pat(USER_USER_PAT)
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.to_request();
let resp = test_env.call(req).await;
assert_status!(&resp, StatusCode::NO_CONTENT);
@@ -260,7 +260,7 @@ pub async fn bad_pats() {
// Patching to a bad expiry should fail
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"expires": Utc::now() - Duration::days(1), // Past
@@ -277,7 +277,7 @@ pub async fn bad_pats() {
}
let req = test::TestRequest::patch()
.uri(&format!("/_internal/pat/{}", id))
.uri(&format!("/_internal/pat/{id}"))
.append_pat(USER_USER_PAT)
.set_json(json!({
"scopes": scope.bits(),

View File

@@ -114,7 +114,7 @@ async fn search_projects() {
let num_hits = projects.total_hits;
expected_project_ids.sort();
found_project_ids.sort();
println!("Facets: {:?}", facets);
println!("Facets: {facets:?}");
assert_eq!(found_project_ids, expected_project_ids);
assert_eq!(num_hits, { expected_project_ids.len() });
}

View File

@@ -328,7 +328,7 @@ async fn search_projects() {
.collect();
expected_project_ids.sort();
found_project_ids.sort();
println!("Facets: {:?}", facets);
println!("Facets: {facets:?}");
assert_eq!(found_project_ids, expected_project_ids);
}
})