summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-09 01:43:34 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-09 11:45:31 -0400
commit2f0b77e8fd02a137047c8975a573626cd76310ff (patch)
tree481d82e99cf8aad8fe256d8186ae72bcee23bf9f
parentba96974bdebd6d4ec345907d49944b5ee644ed47 (diff)
Return a flat message list on boot, not nested lists by channel.
This is a bit easier to compute, and sets us up nicely for pulling message boot out of the `/api/boot` response entirely.
-rw-r--r--.sqlx/query-191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d.json50
-rw-r--r--docs/api.md17
-rw-r--r--hi-ui/src/apiServer.js2
-rw-r--r--hi-ui/src/routes/+page.svelte14
-rw-r--r--hi-ui/src/store/logins.js3
-rw-r--r--hi-ui/src/store/messages.js16
-rw-r--r--src/boot/app.rs42
-rw-r--r--src/boot/mod.rs63
-rw-r--r--src/message/repo.rs35
9 files changed, 128 insertions, 114 deletions
diff --git a/.sqlx/query-191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d.json b/.sqlx/query-191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d.json
new file mode 100644
index 0000000..fe443f9
--- /dev/null
+++ b/.sqlx/query-191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "\n select\n channel as \"channel: channel::Id\",\n sender as \"sender: login::Id\",\n id as \"id: Id\",\n body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n where coalesce(sent_sequence <= $2, true)\n order by sent_sequence\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "channel: channel::Id",
+ "ordinal": 0,
+ "type_info": "Text"
+ },
+ {
+ "name": "sender: login::Id",
+ "ordinal": 1,
+ "type_info": "Text"
+ },
+ {
+ "name": "id: Id",
+ "ordinal": 2,
+ "type_info": "Text"
+ },
+ {
+ "name": "body",
+ "ordinal": 3,
+ "type_info": "Text"
+ },
+ {
+ "name": "sent_at: DateTime",
+ "ordinal": 4,
+ "type_info": "Text"
+ },
+ {
+ "name": "sent_sequence: Sequence",
+ "ordinal": 5,
+ "type_info": "Integer"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d"
+}
diff --git a/docs/api.md b/docs/api.md
index 4145aa8..7414ccf 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -35,14 +35,15 @@ Returns information needed to boot the client. Also the recommended way to check
{
"name": "nonsense and such",
"id": "C1234abcd",
- "messages": [
- {
- "at": "2024-09-27T23:19:10.208147Z",
- "sender": "L1234abcd",
- "id": "M1312acab",
- "body": "beep"
- }
- ]
+ }
+ ],
+ "messages": [
+ {
+ "at": "2024-09-27T23:19:10.208147Z",
+ "channel": "C1234abcd",
+ "sender": "L1234abcd",
+ "id": "M1312acab",
+ "body": "beep"
}
]
}
diff --git a/hi-ui/src/apiServer.js b/hi-ui/src/apiServer.js
index 4421ef5..3aa3f1b 100644
--- a/hi-ui/src/apiServer.js
+++ b/hi-ui/src/apiServer.js
@@ -92,7 +92,7 @@ function onChannelEvent(data) {
function onMessageEvent(data) {
switch (data.event) {
case 'sent':
- messages.update((value) => value.addMessage(data.channel, data.at, data.sender, data.body));
+ messages.update((value) => value.addMessage(data.channel, data.id, data.at, data.sender, data.body));
break;
case 'deleted':
messages.update((value) => value.deleteMessage(data.id));
diff --git a/hi-ui/src/routes/+page.svelte b/hi-ui/src/routes/+page.svelte
index 39f8b62..dd5f2f7 100644
--- a/hi-ui/src/routes/+page.svelte
+++ b/hi-ui/src/routes/+page.svelte
@@ -18,20 +18,13 @@
});
function onBooted(boot) {
- logins.update((value) => value.addLogins(boot.logins));
currentUser.update(() => ({
id: boot.login.id,
username: boot.login.name,
}));
- let channels = boot.channels.map((channel) => ({
- id: channel.id,
- name: channel.name,
- }));
- channelsList.update((value) => value.setChannels(channels));
- let bootMessages = boot.channels.map((channel) => [channel.id, channel.messages]);
- for (let [channel, channelMessages] of bootMessages) {
- messages.update((value) => value.addMessages(channel, channelMessages));
- }
+ logins.update((value) => value.setLogins(boot.logins));
+ channelsList.update((value) => value.setChannels(boot.channels));
+ messages.update((value) => value.setMessages(boot.messages));
}
onMount(async () => {
@@ -39,6 +32,7 @@
let response = await boot();
switch (response.status) {
case 200:
+ debugger;
onBooted(response.data);
subscribeToEvents(response.data.resume_point);
break;
diff --git a/hi-ui/src/store/logins.js b/hi-ui/src/store/logins.js
index 207b757..5b45206 100644
--- a/hi-ui/src/store/logins.js
+++ b/hi-ui/src/store/logins.js
@@ -8,7 +8,8 @@ export class Logins {
return this;
}
- addLogins(logins) {
+ setLogins(logins) {
+ this.logins = {};
for (let { id, name } of logins) {
this.addLogin(id, name);
}
diff --git a/hi-ui/src/store/messages.js b/hi-ui/src/store/messages.js
index f9e0856..931b8fb 100644
--- a/hi-ui/src/store/messages.js
+++ b/hi-ui/src/store/messages.js
@@ -4,16 +4,22 @@ export class Messages {
}
inChannel(channel) {
- return this.channels[channel] || [];
+ return this.channels[channel] = (this.channels[channel] || []);
}
- addMessage(channel, at, sender, body) {
- this.updateChannel(channel, (messages) => [...messages, { at, sender, body }]);
+ addMessage(channel, id, at, sender, body) {
+ this.updateChannel(channel, (messages) => [...messages, { id, at, sender, body }]);
return this;
}
- addMessages(channel, payloads) {
- this.updateChannel(channel, (messages) => [...messages, ...payloads]);
+ setMessages(messages) {
+ this.channels = {};
+ for (let { channel, id, at, sender, body } of messages) {
+ this.inChannel(channel).push({ id, at, sender, body, });
+ }
+ for (let channel in this.channels) {
+ this.channels[channel].sort((a, b) => a.at - b.at);
+ }
return this;
}
diff --git a/src/boot/app.rs b/src/boot/app.rs
index 03e7230..ef48b2f 100644
--- a/src/boot/app.rs
+++ b/src/boot/app.rs
@@ -1,6 +1,6 @@
use sqlx::sqlite::SqlitePool;
-use super::{Channel, Snapshot};
+use super::Snapshot;
use crate::{
channel::repo::Provider as _, event::repo::Provider as _, login::repo::Provider as _,
message::repo::Provider as _,
@@ -20,43 +20,31 @@ impl<'a> Boot<'a> {
let resume_point = tx.sequence().current().await?;
let logins = tx.logins().all(resume_point.into()).await?;
+ let channels = tx.channels().all(resume_point.into()).await?;
+ let messages = tx.messages().all(resume_point.into()).await?;
+
+ tx.commit().await?;
+
let logins = logins
.into_iter()
.filter_map(|login| login.as_of(resume_point))
.collect();
- let channels = tx.channels().all(resume_point.into()).await?;
- let channels = {
- let mut snapshots = Vec::with_capacity(channels.len());
-
- let channels = channels.into_iter().filter_map(|channel| {
- channel
- .as_of(resume_point)
- .map(|snapshot| (channel, snapshot))
- });
-
- for (channel, snapshot) in channels {
- let messages = tx
- .messages()
- .in_channel(&channel, resume_point.into())
- .await?;
-
- let messages = messages
- .into_iter()
- .filter_map(|message| message.as_of(resume_point));
-
- snapshots.push(Channel::new(snapshot, messages));
- }
-
- snapshots
- };
+ let channels = channels
+ .into_iter()
+ .filter_map(|channel| channel.as_of(resume_point))
+ .collect();
- tx.commit().await?;
+ let messages = messages
+ .into_iter()
+ .filter_map(|message| message.as_of(resume_point))
+ .collect();
Ok(Snapshot {
resume_point,
logins,
channels,
+ messages,
})
}
}
diff --git a/src/boot/mod.rs b/src/boot/mod.rs
index 1f94106..ed4764a 100644
--- a/src/boot/mod.rs
+++ b/src/boot/mod.rs
@@ -1,12 +1,7 @@
pub mod app;
mod routes;
-use crate::{
- channel,
- event::{Instant, Sequence},
- login::{self, Login},
- message,
-};
+use crate::{channel::Channel, event::Sequence, login::Login, message::Message};
pub use self::routes::router;
@@ -15,61 +10,5 @@ pub struct Snapshot {
pub resume_point: Sequence,
pub logins: Vec<Login>,
pub channels: Vec<Channel>,
-}
-
-#[derive(serde::Serialize)]
-pub struct Channel {
- pub id: channel::Id,
- pub name: String,
pub messages: Vec<Message>,
}
-
-impl Channel {
- fn new(
- channel: channel::Channel,
- messages: impl IntoIterator<Item = message::Message>,
- ) -> Self {
- // The declarations are like this to guarantee that we aren't omitting any important fields from the corresponding types.
- let channel::Channel { id, name } = channel;
-
- Self {
- id,
- name,
- messages: messages.into_iter().map(Message::from).collect(),
- }
- }
-}
-
-#[derive(serde::Serialize)]
-pub struct Message {
- #[serde(flatten)]
- pub sent: Instant,
- pub sender: login::Id,
- pub id: message::Id,
- pub body: String,
-}
-
-impl From<message::Message> for Message {
- fn from(message: message::Message) -> Self {
- let message::Message {
- sent,
- channel: _,
- sender,
- id,
- body,
- } = message;
-
- Self {
- sent,
- sender,
- id,
- body,
- }
- }
-}
-
-#[derive(serde::Serialize)]
-pub struct Body {
- id: message::Id,
- body: String,
-}
diff --git a/src/message/repo.rs b/src/message/repo.rs
index 0560f4a..71c6d10 100644
--- a/src/message/repo.rs
+++ b/src/message/repo.rs
@@ -112,6 +112,41 @@ impl<'c> Messages<'c> {
Ok(messages)
}
+ pub async fn all(&mut self, resume_at: ResumePoint) -> Result<Vec<History>, sqlx::Error> {
+ let messages = sqlx::query!(
+ r#"
+ select
+ channel as "channel: channel::Id",
+ sender as "sender: login::Id",
+ id as "id: Id",
+ body,
+ sent_at as "sent_at: DateTime",
+ sent_sequence as "sent_sequence: Sequence"
+ from message
+ where coalesce(sent_sequence <= $2, true)
+ order by sent_sequence
+ "#,
+ resume_at,
+ )
+ .map(|row| History {
+ message: Message {
+ sent: Instant {
+ at: row.sent_at,
+ sequence: row.sent_sequence,
+ },
+ channel: row.channel,
+ sender: row.sender,
+ id: row.id,
+ body: row.body,
+ },
+ deleted: None,
+ })
+ .fetch_all(&mut *self.0)
+ .await?;
+
+ Ok(messages)
+ }
+
async fn by_id(&mut self, message: &Id) -> Result<History, sqlx::Error> {
let message = sqlx::query!(
r#"