Show orgs in project card when a project is owned by an org (#5892)
* fix: link to user using id instead of username * feat: show org in project card * fix: account for outdated documents * refactor: use struct to store owner information * fix: default new fields * fix lint
This commit is contained in:
34
apps/labrinth/.sqlx/query-654ca85d28573de9b532d0768e5250baa3a5f200fcf9ae990575073d0a1043e0.json
generated
Normal file
34
apps/labrinth/.sqlx/query-654ca85d28573de9b532d0768e5250baa3a5f200fcf9ae990575073d0a1043e0.json
generated
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT m.id mod_id, u.username, u.id uid\n FROM mods m\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "mod_id",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "username",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "uid",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int8Array"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "654ca85d28573de9b532d0768e5250baa3a5f200fcf9ae990575073d0a1043e0"
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT m.id mod_id, u.username\n FROM mods m\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "mod_id",
|
|
||||||
"type_info": "Int8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 1,
|
|
||||||
"name": "username",
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int8Array"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "80734c33c16aeacca980cf40070bac035931a0bab8c0d0cf63888c8e5616f847"
|
|
||||||
}
|
|
||||||
46
apps/labrinth/.sqlx/query-81b65298ddf4c58b884b15e64f1f4f5aa006944c05980a5b5da366135d17c912.json
generated
Normal file
46
apps/labrinth/.sqlx/query-81b65298ddf4c58b884b15e64f1f4f5aa006944c05980a5b5da366135d17c912.json
generated
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT m.id mod_id, u.username, u.id uid, o.name orgname, o.id oid\n FROM mods m\n INNER JOIN organizations o ON o.id = m.organization_id\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "mod_id",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "username",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "uid",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "orgname",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "oid",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int8Array"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "81b65298ddf4c58b884b15e64f1f4f5aa006944c05980a5b5da366135d17c912"
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT m.id mod_id, u.username\n FROM mods m\n INNER JOIN organizations o ON o.id = m.organization_id\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "mod_id",
|
|
||||||
"type_info": "Int8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 1,
|
|
||||||
"name": "username",
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int8Array"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "e50e308826d1e7fa54cade7daf8120b4ae4068bd086dc08f572b33cfc2476354"
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,12 @@ pub struct LegacyResultSearchProject {
|
|||||||
pub project_type: String,
|
pub project_type: String,
|
||||||
pub slug: Option<String>,
|
pub slug: Option<String>,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub author_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub organization: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub organization_id: Option<String>,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub categories: Vec<String>,
|
pub categories: Vec<String>,
|
||||||
@@ -139,6 +145,9 @@ impl LegacyResultSearchProject {
|
|||||||
project_id: result_search_project.project_id,
|
project_id: result_search_project.project_id,
|
||||||
slug: result_search_project.slug,
|
slug: result_search_project.slug,
|
||||||
author: result_search_project.author,
|
author: result_search_project.author,
|
||||||
|
author_id: result_search_project.author_id,
|
||||||
|
organization: result_search_project.organization,
|
||||||
|
organization_id: result_search_project.organization_id,
|
||||||
title: result_search_project.name,
|
title: result_search_project.name,
|
||||||
description: result_search_project.summary,
|
description: result_search_project.summary,
|
||||||
display_categories,
|
display_categories,
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ use crate::database::models::loader_fields::{
|
|||||||
VersionField,
|
VersionField,
|
||||||
};
|
};
|
||||||
use crate::database::models::{
|
use crate::database::models::{
|
||||||
DBProjectId, DBVersionId, LoaderFieldEnumId, LoaderFieldEnumValueId,
|
DBOrganizationId, DBProjectId, DBUserId, DBVersionId, LoaderFieldEnumId,
|
||||||
LoaderFieldId,
|
LoaderFieldEnumValueId, LoaderFieldId,
|
||||||
};
|
};
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::models::exp;
|
use crate::models::exp;
|
||||||
@@ -34,6 +34,13 @@ fn normalize_for_search(s: &str) -> String {
|
|||||||
SPECIAL_CHARS_RE.replace_all(s, "").to_kebab_case()
|
SPECIAL_CHARS_RE.replace_all(s, "").to_kebab_case()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ProjectOwner {
|
||||||
|
username: String,
|
||||||
|
user_id: DBUserId,
|
||||||
|
org_name: Option<String>,
|
||||||
|
org_id: Option<DBOrganizationId>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn index_local(
|
pub async fn index_local(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
redis: &RedisPool,
|
redis: &RedisPool,
|
||||||
@@ -171,9 +178,9 @@ pub async fn index_local(
|
|||||||
|
|
||||||
info!("Indexing local org owners!");
|
info!("Indexing local org owners!");
|
||||||
|
|
||||||
let mods_org_owners: DashMap<DBProjectId, String> = sqlx::query!(
|
let mods_org_owners: DashMap<DBProjectId, ProjectOwner> = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT m.id mod_id, u.username
|
SELECT m.id mod_id, u.username, u.id uid, o.name orgname, o.id oid
|
||||||
FROM mods m
|
FROM mods m
|
||||||
INNER JOIN organizations o ON o.id = m.organization_id
|
INNER JOIN organizations o ON o.id = m.organization_id
|
||||||
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id
|
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id
|
||||||
@@ -183,17 +190,22 @@ pub async fn index_local(
|
|||||||
&*project_ids,
|
&*project_ids,
|
||||||
)
|
)
|
||||||
.fetch(pool)
|
.fetch(pool)
|
||||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, String>, m| {
|
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, ProjectOwner>, m| {
|
||||||
acc.insert(DBProjectId(m.mod_id), m.username);
|
acc.insert(DBProjectId(m.mod_id), ProjectOwner {
|
||||||
|
username: m.username,
|
||||||
|
user_id: DBUserId(m.uid),
|
||||||
|
org_name: Some(m.orgname),
|
||||||
|
org_id: Some(DBOrganizationId(m.oid)),
|
||||||
|
});
|
||||||
async move { Ok(acc) }
|
async move { Ok(acc) }
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!("Indexing local team owners!");
|
info!("Indexing local team owners!");
|
||||||
|
|
||||||
let mods_team_owners: DashMap<DBProjectId, String> = sqlx::query!(
|
let mods_team_owners: DashMap<DBProjectId, ProjectOwner> = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT m.id mod_id, u.username
|
SELECT m.id mod_id, u.username, u.id uid
|
||||||
FROM mods m
|
FROM mods m
|
||||||
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id
|
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id
|
||||||
INNER JOIN users u ON u.id = tm.user_id
|
INNER JOIN users u ON u.id = tm.user_id
|
||||||
@@ -202,8 +214,13 @@ pub async fn index_local(
|
|||||||
&project_ids,
|
&project_ids,
|
||||||
)
|
)
|
||||||
.fetch(pool)
|
.fetch(pool)
|
||||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, String>, m| {
|
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, ProjectOwner>, m| {
|
||||||
acc.insert(DBProjectId(m.mod_id), m.username);
|
acc.insert(DBProjectId(m.mod_id), ProjectOwner {
|
||||||
|
username: m.username,
|
||||||
|
user_id: DBUserId(m.uid),
|
||||||
|
org_name: None,
|
||||||
|
org_id: None,
|
||||||
|
});
|
||||||
async move { Ok(acc) }
|
async move { Ok(acc) }
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
@@ -262,21 +279,24 @@ pub async fn index_local(
|
|||||||
if count % 1000 == 0 {
|
if count % 1000 == 0 {
|
||||||
info!("projects index prog: {count}/{total_len}");
|
info!("projects index prog: {count}/{total_len}");
|
||||||
}
|
}
|
||||||
|
let Some((
|
||||||
let owner =
|
_,
|
||||||
if let Some((_, org_owner)) = mods_org_owners.remove(&project.id) {
|
ProjectOwner {
|
||||||
org_owner
|
username,
|
||||||
} else if let Some((_, team_owner)) =
|
user_id,
|
||||||
mods_team_owners.remove(&project.id)
|
org_name,
|
||||||
{
|
org_id,
|
||||||
team_owner
|
},
|
||||||
} else {
|
)) = mods_org_owners
|
||||||
warn!(
|
.remove(&project.id)
|
||||||
"org owner not found for project {} id: {}!",
|
.or_else(|| mods_team_owners.remove(&project.id))
|
||||||
project.name, project.id.0
|
else {
|
||||||
);
|
warn!(
|
||||||
continue;
|
"org owner not found for project {} id: {}!",
|
||||||
};
|
project.name, project.id.0
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let license = match project.license.split(' ').next() {
|
let license = match project.license.split(' ').next() {
|
||||||
Some(license) => license.to_string(),
|
Some(license) => license.to_string(),
|
||||||
@@ -444,8 +464,13 @@ pub async fn index_local(
|
|||||||
downloads: project.downloads,
|
downloads: project.downloads,
|
||||||
log_downloads: (project.downloads.max(1) as f64).ln(),
|
log_downloads: (project.downloads.max(1) as f64).ln(),
|
||||||
icon_url: project.icon_url.clone(),
|
icon_url: project.icon_url.clone(),
|
||||||
author: owner.clone(),
|
author: username.clone(),
|
||||||
indexed_author: normalize_for_search(&owner),
|
author_id: ariadne::ids::UserId::from(user_id).to_string(),
|
||||||
|
organization: org_name.clone(),
|
||||||
|
organization_id: org_id.map(|e| {
|
||||||
|
crate::models::ids::OrganizationId::from(e).to_string()
|
||||||
|
}),
|
||||||
|
indexed_author: normalize_for_search(&username),
|
||||||
date_created: project.approved,
|
date_created: project.approved,
|
||||||
created_timestamp: project.approved.timestamp(),
|
created_timestamp: project.approved.timestamp(),
|
||||||
date_modified: project.updated,
|
date_modified: project.updated,
|
||||||
|
|||||||
@@ -228,6 +228,9 @@ pub struct UploadSearchProject {
|
|||||||
pub project_types: Vec<String>,
|
pub project_types: Vec<String>,
|
||||||
pub slug: Option<String>,
|
pub slug: Option<String>,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
|
pub author_id: String,
|
||||||
|
pub organization: Option<String>,
|
||||||
|
pub organization_id: Option<String>,
|
||||||
pub indexed_author: String,
|
pub indexed_author: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub indexed_name: String,
|
pub indexed_name: String,
|
||||||
@@ -279,6 +282,12 @@ pub struct ResultSearchProject {
|
|||||||
pub project_types: Vec<String>,
|
pub project_types: Vec<String>,
|
||||||
pub slug: Option<String>,
|
pub slug: Option<String>,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub author_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub organization: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub organization_id: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub summary: String,
|
pub summary: String,
|
||||||
pub categories: Vec<String>,
|
pub categories: Vec<String>,
|
||||||
@@ -315,6 +324,9 @@ impl From<UploadSearchProject> for ResultSearchProject {
|
|||||||
project_types: source.project_types,
|
project_types: source.project_types,
|
||||||
slug: source.slug,
|
slug: source.slug,
|
||||||
author: source.author,
|
author: source.author,
|
||||||
|
author_id: Some(source.author_id),
|
||||||
|
organization: source.organization,
|
||||||
|
organization_id: source.organization_id,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
summary: source.summary,
|
summary: source.summary,
|
||||||
categories: source.categories,
|
categories: source.categories,
|
||||||
|
|||||||
@@ -1133,6 +1133,9 @@ export namespace Labrinth {
|
|||||||
project_type: string
|
project_type: string
|
||||||
slug: string | null
|
slug: string | null
|
||||||
author: string
|
author: string
|
||||||
|
author_id: string | null
|
||||||
|
organization: string | null
|
||||||
|
organization_id: string | null
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
categories: string[]
|
categories: string[]
|
||||||
@@ -1167,6 +1170,9 @@ export namespace Labrinth {
|
|||||||
project_types: string[]
|
project_types: string[]
|
||||||
slug: string | null
|
slug: string | null
|
||||||
author: string
|
author: string
|
||||||
|
author_id: string | null
|
||||||
|
organization: string | null
|
||||||
|
organization_id: string | null
|
||||||
name: string
|
name: string
|
||||||
summary: string
|
summary: string
|
||||||
categories: string[]
|
categories: string[]
|
||||||
|
|||||||
@@ -190,11 +190,15 @@ const maxResultsOptions = computed<ComboboxOption<number>[]>(() =>
|
|||||||
:title="result.title"
|
:title="result.title"
|
||||||
:icon-url="result.icon_url"
|
:icon-url="result.icon_url"
|
||||||
:author="{
|
:author="{
|
||||||
name: result.author,
|
name: result.organization == null ? result.author : result.organization,
|
||||||
link:
|
link:
|
||||||
ctx.variant === 'web'
|
result.organization_id == null
|
||||||
? `/user/${result.author}`
|
? ctx.variant === 'web'
|
||||||
: `https://modrinth.com/user/${result.author}`,
|
? `/user/${result.author_id ?? result.author}`
|
||||||
|
: `https://modrinth.com/user/${result.author_id ?? result.author}`
|
||||||
|
: ctx.variant === 'web'
|
||||||
|
? `/organization/${result.organization_id}`
|
||||||
|
: `https://modrinth.com/organization/${result.organization_id}`,
|
||||||
}"
|
}"
|
||||||
:date-updated="result.date_modified"
|
:date-updated="result.date_modified"
|
||||||
:date-published="result.date_created"
|
:date-published="result.date_created"
|
||||||
|
|||||||
Reference in New Issue
Block a user