steid

@jamesgill /

1//! The profile page — `/{handle}`.
2//!
3//! Public. This is the first page that renders for anonymous visitors, so everything it
4//! shows comes from [`PublicProfile`](crate::application::PublicProfile), which has no
5//! private fields to leak.
6
7use topcoat::{
8 Result,
9 context::Cx,
10 router::{
11 error::{RouterErrorExt, not_found},
12 page, path_param,
13 },
14 view::view,
15};
16
17use crate::{
18 application::{PublicProfile, view_profile},
19 domain::OrgName,
20};
21
22use super::context::{current_actor, memberships, orgs, server_error};
23
24/// `{handle}` from the path. The struct name snake-cased is the parameter name.
25///
26/// Declared as `str` so the raw segment arrives unparsed — validation is `OrgName`'s
27/// job, and a handle that fails it is a page that does not exist rather than a bad
28/// request.
29#[path_param]
30struct Handle(str);
31
32/// Resolves `{handle}` from the path into a profile, or 404.
33///
34/// A malformed handle 404s rather than erroring: `/Not A Handle` is a page that does
35/// not exist, not a bad request.
36pub(super) async fn profile_for(cx: &Cx) -> Result<PublicProfile> {
37 let raw = path_param::<Handle>(cx);
38 let handle = OrgName::new(raw).map_err(|_| not_found())?;
39 let actor = current_actor(cx).await?;
40
41 Ok(view_profile(&handle, &actor, &orgs(cx), &memberships(cx))
42 .await
43 .map_err(server_error)?
44 .ok_or_not_found()?)
45}
46
47#[page("/{handle}")]
48async fn profile(cx: &Cx) -> Result {
49 let profile = profile_for(cx).await?;
50
51 view! {
52 <header>
53 <h1>(&profile.label)</h1>
54 <p><code>"@" (profile.handle.as_str())</code></p>
55 ({
56 match &profile.bio {
57 Some(bio) => view! { <p>(bio)</p> },
58 None => view! {},
59 }
60 }?)
61 ({
62 if profile.viewer_is_owner {
63 view! {
64 <p><a href=(format!("/{}/settings", profile.handle))>"Edit profile"</a></p>
65 }
66 } else {
67 view! {}
68 }
69 }?)
70 </header>
71
72 <section>
73 <h2>"Repositories"</h2>
74 <p>"Nothing here yet."</p>
75 </section>
76
77 <section>
78 <h2>"Writing"</h2>
79 <p>"Nothing here yet."</p>
80 </section>
81
82 <section>
83 <h2>"Projects"</h2>
84 <p>"Nothing here yet."</p>
85 </section>
86 }
87}