Skip to content

Commit a24deab

Browse files
feat(user page): Recently published
1 parent b65bdf2 commit a24deab

File tree

4 files changed

+165
-10
lines changed

4 files changed

+165
-10
lines changed

api/.sqlx/query-b51c548092ff640f6d210365a4fb79d8d22a7868b9da6896b068e0cc0fa1298d.json

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/src/api/users.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ use crate::util::ApiResult;
1313
use crate::util::RequestIdExt;
1414

1515
use super::ApiError;
16+
use super::ApiPackage;
1617
use super::ApiScope;
1718
use super::ApiUser;
1819

1920
pub fn users_router() -> Router<Body, ApiError> {
2021
Router::builder()
2122
.get("/:id", util::json(get_handler))
2223
.get("/:id/scopes", util::json(get_scopes_handler))
24+
.get("/:id/packages", util::json(get_packages_handler))
2325
.build()
2426
.unwrap()
2527
}
@@ -54,3 +56,25 @@ pub async fn get_scopes_handler(
5456

5557
Ok(scopes.into_iter().map(ApiScope::from).collect())
5658
}
59+
60+
#[instrument(name = "GET /api/users/:id/packages", skip(req), err, fields(id))]
61+
pub async fn get_packages_handler(
62+
req: Request<Body>,
63+
) -> ApiResult<Vec<ApiPackage>> {
64+
let id = req.param_uuid("id")?;
65+
Span::current().record("id", field::display(id));
66+
67+
let db = req.data::<Database>().unwrap();
68+
db.get_user_public(id)
69+
.await?
70+
.ok_or(ApiError::UserNotFound)?;
71+
72+
let packages = db.get_recent_packages_by_user(&id).await?;
73+
74+
Ok(
75+
packages
76+
.into_iter()
77+
.map(|package| ApiPackage::from((package, None, Default::default())))
78+
.collect(),
79+
)
80+
}

api/src/db/database.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4858,6 +4858,39 @@ impl Database {
48584858

48594859
Ok((total_scopes as usize, scopes))
48604860
}
4861+
4862+
pub async fn get_recent_packages_by_user(
4863+
&self,
4864+
user_id: &uuid::Uuid,
4865+
) -> Result<Vec<Package>> {
4866+
let packages = sqlx::query_as!(
4867+
Package,
4868+
r#"
4869+
SELECT DISTINCT ON (packages.scope, packages.name)
4870+
packages.scope as "scope: ScopeName",
4871+
packages.name as "name: PackageName",
4872+
packages.description,
4873+
packages.github_repository_id,
4874+
packages.runtime_compat as "runtime_compat: RuntimeCompat",
4875+
packages.when_featured,
4876+
packages.is_archived,
4877+
packages.updated_at,
4878+
packages.created_at,
4879+
(SELECT COUNT(created_at) FROM package_versions WHERE scope = packages.scope AND name = packages.name) as "version_count!",
4880+
(SELECT version FROM package_versions WHERE scope = packages.scope AND name = packages.name AND version NOT LIKE '%-%' AND is_yanked = false ORDER BY version DESC LIMIT 1) as "latest_version"
4881+
FROM packages
4882+
JOIN scope_members ON packages.scope = scope_members.scope
4883+
WHERE scope_members.user_id = $1
4884+
ORDER BY packages.scope, packages.name, packages.created_at DESC
4885+
LIMIT 10;
4886+
"#,
4887+
user_id
4888+
)
4889+
.fetch_all(&self.pool)
4890+
.await?;
4891+
4892+
Ok(packages)
4893+
}
48614894
}
48624895

48634896
async fn finalize_package_creation(

frontend/routes/user/[id].tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { HttpError } from "fresh";
33
import { define } from "../../util.ts";
44
import { path } from "../../utils/api.ts";
5-
import { FullUser, Scope, User } from "../../utils/api_types.ts";
5+
import { FullUser, Package, Scope, User } from "../../utils/api_types.ts";
66
import { ListPanel } from "../../components/ListPanel.tsx";
77
import { AccountLayout } from "../account/(_components)/AccountLayout.tsx";
88

@@ -32,25 +32,39 @@ export default define.page<typeof handler>(function UserPage({ data, state }) {
3232
</div>
3333
)}
3434

35-
{
36-
/*<div>
37-
<span class="font-semibold">Recently published</span>
38-
<div class="text-tertiary text-base"
39-
TODO: all packages recently published by this user
40-
</div>
41-
</div>*/
42-
}
35+
{data.packages.length > 0
36+
? (
37+
<ListPanel
38+
title="Recently published"
39+
subtitle={state.user?.id === data.user.id
40+
? "Packages you have published."
41+
: "Packages this user has published."}
42+
// deno-lint-ignore jsx-no-children-prop
43+
children={data.packages.map((pkg) => ({
44+
value: `@${pkg.scope}/${pkg.name}`,
45+
href: `/@${pkg.scope}/${pkg.name}`,
46+
}))}
47+
/>
48+
)
49+
: (
50+
<div class="p-3 text-jsr-gray-500 text-center italic">
51+
{state.user?.id === data.user.id ? "You have" : "This user has"}
52+
{" "}
53+
not published any packages recently.
54+
</div>
55+
)}
4356
</div>
4457
</AccountLayout>
4558
);
4659
});
4760

4861
export const handler = define.handlers({
4962
async GET(ctx) {
50-
const [currentUser, userRes, scopesRes] = await Promise.all([
63+
const [currentUser, userRes, scopesRes, packagesRes] = await Promise.all([
5164
ctx.state.userPromise,
5265
ctx.state.api.get<User>(path`/users/${ctx.params.id}`),
5366
ctx.state.api.get<Scope[]>(path`/users/${ctx.params.id}/scopes`),
67+
ctx.state.api.get<Package[]>(path`/users/${ctx.params.id}/packages`),
5468
]);
5569
if (currentUser instanceof Response) return currentUser;
5670

@@ -62,6 +76,7 @@ export const handler = define.handlers({
6276
throw userRes; // gracefully handle errors
6377
}
6478
if (!scopesRes.ok) throw scopesRes; // gracefully handle errors
79+
if (!packagesRes.ok) throw packagesRes; // gracefully handle errors
6580

6681
let user: User | FullUser = userRes.data;
6782
if (ctx.params.id === currentUser?.id) {
@@ -75,6 +90,7 @@ export const handler = define.handlers({
7590
data: {
7691
user,
7792
scopes: scopesRes.data,
93+
packages: packagesRes.data,
7894
},
7995
};
8096
},

0 commit comments

Comments
 (0)