summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-04 23:38:21 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-04 23:38:21 -0400
commit6366fb3c96e4ed281e233279c85bbfd90ab3ecbc (patch)
treebb46e2f9d42d6085517767ed7d53a47885a30345 /src
parentbeeb40acdc07d5652bf2128ecb8f71a1116993ae (diff)
Support joining channels.
Diffstat (limited to 'src')
-rw-r--r--src/channel/repo.rs28
-rw-r--r--src/channel/routes.rs18
-rw-r--r--src/index.rs43
3 files changed, 79 insertions, 10 deletions
diff --git a/src/channel/repo.rs b/src/channel/repo.rs
index bb39d6e..a255305 100644
--- a/src/channel/repo.rs
+++ b/src/channel/repo.rs
@@ -62,7 +62,7 @@ impl<'c> Channels<'c> {
Ok(())
}
- pub async fn for_login(&mut self, login: &LoginId) -> Result<Vec<Channel>, BoxedError> {
+ pub async fn joined(&mut self, login: &LoginId) -> Result<Vec<Channel>, BoxedError> {
let channels = sqlx::query_as!(
Channel,
r#"
@@ -83,6 +83,32 @@ impl<'c> Channels<'c> {
Ok(channels)
}
+ pub async fn unjoined(&mut self, login: &LoginId) -> Result<Vec<Channel>, BoxedError> {
+ let channels = sqlx::query_as!(
+ Channel,
+ r#"
+ select
+ channel.id as "id: Id",
+ channel.name
+ from channel
+ except
+ select
+ channel.id as "id: Id",
+ channel.name
+ from channel
+ join channel_member
+ on (channel.id = channel_member.channel)
+ where channel_member.login = $1
+ order by channel.name
+ "#,
+ login,
+ )
+ .fetch_all(&mut *self.0)
+ .await?;
+
+ Ok(channels)
+ }
+
/// Unenrol a login from a channel.
pub async fn leave(&mut self, channel: &Id, login: &LoginId) -> Result<(), BoxedError> {
sqlx::query_scalar!(
diff --git a/src/channel/routes.rs b/src/channel/routes.rs
index 4453a1e..3dc2b1a 100644
--- a/src/channel/routes.rs
+++ b/src/channel/routes.rs
@@ -12,6 +12,7 @@ use crate::{error::InternalError, login::repo::logins::Login};
pub fn router() -> Router<SqlitePool> {
Router::new()
.route("/create", post(on_create))
+ .route("/join", post(on_join))
.route("/:channel/leave", post(on_leave))
}
@@ -33,6 +34,23 @@ async fn on_create(
Ok(Redirect::to("/"))
}
+#[derive(serde::Deserialize)]
+struct JoinRequest {
+ channel: ChannelId,
+}
+
+async fn on_join(
+ State(db): State<SqlitePool>,
+ login: Login,
+ Form(req): Form<JoinRequest>,
+) -> Result<impl IntoResponse, InternalError> {
+ let mut tx = db.begin().await?;
+ tx.channels().join(&req.channel, &login.id).await?;
+ tx.commit().await?;
+
+ Ok(Redirect::to("/"))
+}
+
async fn on_leave(
State(db): State<SqlitePool>,
login: Login,
diff --git a/src/index.rs b/src/index.rs
index 605d9f6..9de91d5 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -16,10 +16,15 @@ async fn index(
async fn index_authenticated(db: SqlitePool, login: Login) -> Result<Markup, InternalError> {
let mut tx = db.begin().await?;
- let channels = tx.channels().for_login(&login.id).await?;
+ let joined_channels = tx.channels().joined(&login.id).await?;
+ let unjoined_channels = tx.channels().unjoined(&login.id).await?;
tx.commit().await?;
- Ok(templates::authenticated(login, &channels))
+ Ok(templates::authenticated(
+ login,
+ &joined_channels,
+ &unjoined_channels,
+ ))
}
pub fn router() -> Router<SqlitePool> {
@@ -33,7 +38,8 @@ mod templates {
pub fn authenticated<'c>(
login: Login,
- channels: impl IntoIterator<Item = &'c Channel>,
+ joined_channels: impl IntoIterator<Item = &'c Channel>,
+ unjoined_channels: impl IntoIterator<Item = &'c Channel>,
) -> Markup {
html! {
(DOCTYPE)
@@ -42,7 +48,8 @@ mod templates {
}
body {
section {
- (channel_list(channels))
+ (channel_list(joined_channels))
+ (join_channel(unjoined_channels))
(create_channel())
}
section {
@@ -52,21 +59,21 @@ mod templates {
}
}
- fn channel_list<'c>(channels: impl IntoIterator<Item = &'c Channel>) -> Markup {
+ fn channel_list<'c>(joined_channels: impl IntoIterator<Item = &'c Channel>) -> Markup {
html! {
ul {
- @for channel in channels {
- (channel_entry(&channel))
+ @for channel in joined_channels {
+ (joined_channel_entry(&channel))
}
}
}
}
- fn channel_entry(channel: &Channel) -> Markup {
+ fn joined_channel_entry(channel: &Channel) -> Markup {
let leave_url = format!("/{}/leave", channel.id);
html! {
li {
- (channel.name) "(" (channel.id) ")"
+ (channel.name) " (" (channel.id) ")"
form action=(leave_url) method="post" {
button {
"leave"
@@ -76,6 +83,24 @@ mod templates {
}
}
+ fn join_channel<'c>(unjoined_channels: impl IntoIterator<Item = &'c Channel>) -> Markup {
+ html! {
+ form action="join" method="post" {
+ select name="channel" required {
+ option value="" { "channel" }
+ @for channel in unjoined_channels {
+ option value=(channel.id) {
+ (channel.name) " (" (channel.id) ")"
+ }
+ }
+ }
+ button {
+ "join"
+ }
+ }
+ }
+ }
+
fn create_channel() -> Markup {
html! {
form action="/create" method="post" {