From 9e171096a72d3e63626df7b09970476aba28eb06 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Tue, 8 Oct 2024 22:43:22 -0400 Subject: Use a two-tier hierarchy for events. This will make it much easier to slot in new event types (login events!). --- hi-ui/src/store/messages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'hi-ui/src/store/messages.js') diff --git a/hi-ui/src/store/messages.js b/hi-ui/src/store/messages.js index 560b9e1..cefd725 100644 --- a/hi-ui/src/store/messages.js +++ b/hi-ui/src/store/messages.js @@ -24,8 +24,8 @@ export class Messages { deleteMessage(channel, message) { - let messages = this.messages(channel).filter((msg) => msg.message.id != message); - this.channels[channel] = messages; + this.updateChannel(channel, (messages) => messages.filter((msg) => msg.id != message)); + return this; } deleteChannel(id) { -- cgit v1.2.3 From da1810afc5a627a518131cfb0af0996c5ec60bcf Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Tue, 8 Oct 2024 22:57:39 -0400 Subject: Simplify channel IDs in events. Remove redundant ones. --- ...0061d4bf5755cda35dba473eae37f467cfabb0102e.json | 56 +++++++++++++++++++ ...0bd021892002b7ba7d9f4cac5deea298b6223918aa.json | 56 +++++++++++++++++++ ...4eedb8229360ba78f2607d25e7e2ee5db5c759a5a3.json | 62 ---------------------- ...9cedc6bee1750d28a6176980ed7040b8a3301fc7e5.json | 62 ---------------------- ...4a2137be57b3a45fe38a675262ceaaebb3d346a9ca.json | 62 ---------------------- ...47a7317dda3e429faac15e02c900f62f83c25070a7.json | 56 +++++++++++++++++++ docs/api.md | 9 +--- hi-ui/src/apiServer.js | 4 +- hi-ui/src/store/messages.js | 15 +++--- src/message/event.rs | 6 +-- src/message/history.rs | 1 - src/message/repo.rs | 36 ++++--------- src/message/snapshot.rs | 4 +- 13 files changed, 189 insertions(+), 240 deletions(-) create mode 100644 .sqlx/query-14ca0364c6f0ab084b22af0061d4bf5755cda35dba473eae37f467cfabb0102e.json create mode 100644 .sqlx/query-315a4a16ebc0a99ab745a40bd021892002b7ba7d9f4cac5deea298b6223918aa.json delete mode 100644 .sqlx/query-5c53579fa431b6e184faf94eedb8229360ba78f2607d25e7e2ee5db5c759a5a3.json delete mode 100644 .sqlx/query-6fc4be85527af518da17c49cedc6bee1750d28a6176980ed7040b8a3301fc7e5.json delete mode 100644 .sqlx/query-9606853f2ea9f776f7e4384a2137be57b3a45fe38a675262ceaaebb3d346a9ca.json create mode 100644 .sqlx/query-eb9d7146e615044568f9bb47a7317dda3e429faac15e02c900f62f83c25070a7.json (limited to 'hi-ui/src/store/messages.js') diff --git a/.sqlx/query-14ca0364c6f0ab084b22af0061d4bf5755cda35dba473eae37f467cfabb0102e.json b/.sqlx/query-14ca0364c6f0ab084b22af0061d4bf5755cda35dba473eae37f467cfabb0102e.json new file mode 100644 index 0000000..15ceb24 --- /dev/null +++ b/.sqlx/query-14ca0364c6f0ab084b22af0061d4bf5755cda35dba473eae37f467cfabb0102e.json @@ -0,0 +1,56 @@ +{ + "db_name": "SQLite", + "query": "\n select\n message.channel as \"channel: channel::Id\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join login as sender on message.sender = sender.id\n where coalesce(message.sent_sequence > $1, true)\n ", + "describe": { + "columns": [ + { + "name": "channel: channel::Id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "sender_id: login::Id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "sender_name", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "id: Id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "sent_at: DateTime", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "sent_sequence: Sequence", + "ordinal": 6, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "14ca0364c6f0ab084b22af0061d4bf5755cda35dba473eae37f467cfabb0102e" +} diff --git a/.sqlx/query-315a4a16ebc0a99ab745a40bd021892002b7ba7d9f4cac5deea298b6223918aa.json b/.sqlx/query-315a4a16ebc0a99ab745a40bd021892002b7ba7d9f4cac5deea298b6223918aa.json new file mode 100644 index 0000000..7aeac69 --- /dev/null +++ b/.sqlx/query-315a4a16ebc0a99ab745a40bd021892002b7ba7d9f4cac5deea298b6223918aa.json @@ -0,0 +1,56 @@ +{ + "db_name": "SQLite", + "query": "\n select\n message.channel as \"channel: channel::Id\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join login as sender on message.sender = sender.id\n where message.id = $1\n ", + "describe": { + "columns": [ + { + "name": "channel: channel::Id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "sender_id: login::Id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "sender_name", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "id: Id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "sent_at: DateTime", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "sent_sequence: Sequence", + "ordinal": 6, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "315a4a16ebc0a99ab745a40bd021892002b7ba7d9f4cac5deea298b6223918aa" +} diff --git a/.sqlx/query-5c53579fa431b6e184faf94eedb8229360ba78f2607d25e7e2ee5db5c759a5a3.json b/.sqlx/query-5c53579fa431b6e184faf94eedb8229360ba78f2607d25e7e2ee5db5c759a5a3.json deleted file mode 100644 index 4ca6786..0000000 --- a/.sqlx/query-5c53579fa431b6e184faf94eedb8229360ba78f2607d25e7e2ee5db5c759a5a3.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n select\n channel.id as \"channel_id: channel::Id\",\n channel.name as \"channel_name\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join channel on message.channel = channel.id\n join login as sender on message.sender = sender.id\n where coalesce(message.sent_sequence > $1, true)\n ", - "describe": { - "columns": [ - { - "name": "channel_id: channel::Id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "channel_name", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "sender_id: login::Id", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "sender_name", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "id: Id", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "body", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "sent_at: DateTime", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "sent_sequence: Sequence", - "ordinal": 7, - "type_info": "Integer" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "5c53579fa431b6e184faf94eedb8229360ba78f2607d25e7e2ee5db5c759a5a3" -} diff --git a/.sqlx/query-6fc4be85527af518da17c49cedc6bee1750d28a6176980ed7040b8a3301fc7e5.json b/.sqlx/query-6fc4be85527af518da17c49cedc6bee1750d28a6176980ed7040b8a3301fc7e5.json deleted file mode 100644 index 257e1f6..0000000 --- a/.sqlx/query-6fc4be85527af518da17c49cedc6bee1750d28a6176980ed7040b8a3301fc7e5.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n select\n channel.id as \"channel_id: channel::Id\",\n channel.name as \"channel_name\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join channel on message.channel = channel.id\n join login as sender on message.sender = sender.id\n where message.id = $1\n ", - "describe": { - "columns": [ - { - "name": "channel_id: channel::Id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "channel_name", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "sender_id: login::Id", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "sender_name", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "id: Id", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "body", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "sent_at: DateTime", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "sent_sequence: Sequence", - "ordinal": 7, - "type_info": "Integer" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "6fc4be85527af518da17c49cedc6bee1750d28a6176980ed7040b8a3301fc7e5" -} diff --git a/.sqlx/query-9606853f2ea9f776f7e4384a2137be57b3a45fe38a675262ceaaebb3d346a9ca.json b/.sqlx/query-9606853f2ea9f776f7e4384a2137be57b3a45fe38a675262ceaaebb3d346a9ca.json deleted file mode 100644 index 82246ac..0000000 --- a/.sqlx/query-9606853f2ea9f776f7e4384a2137be57b3a45fe38a675262ceaaebb3d346a9ca.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n select\n channel.id as \"channel_id: channel::Id\",\n channel.name as \"channel_name\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join channel on message.channel = channel.id\n join login as sender on message.sender = sender.id\n where channel.id = $1\n and coalesce(message.sent_sequence <= $2, true)\n order by message.sent_sequence\n ", - "describe": { - "columns": [ - { - "name": "channel_id: channel::Id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "channel_name", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "sender_id: login::Id", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "sender_name", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "id: Id", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "body", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "sent_at: DateTime", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "sent_sequence: Sequence", - "ordinal": 7, - "type_info": "Integer" - } - ], - "parameters": { - "Right": 2 - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "9606853f2ea9f776f7e4384a2137be57b3a45fe38a675262ceaaebb3d346a9ca" -} diff --git a/.sqlx/query-eb9d7146e615044568f9bb47a7317dda3e429faac15e02c900f62f83c25070a7.json b/.sqlx/query-eb9d7146e615044568f9bb47a7317dda3e429faac15e02c900f62f83c25070a7.json new file mode 100644 index 0000000..1fe8a3e --- /dev/null +++ b/.sqlx/query-eb9d7146e615044568f9bb47a7317dda3e429faac15e02c900f62f83c25070a7.json @@ -0,0 +1,56 @@ +{ + "db_name": "SQLite", + "query": "\n select\n message.channel as \"channel: channel::Id\",\n sender.id as \"sender_id: login::Id\",\n sender.name as \"sender_name\",\n message.id as \"id: Id\",\n message.body,\n sent_at as \"sent_at: DateTime\",\n sent_sequence as \"sent_sequence: Sequence\"\n from message\n join login as sender on message.sender = sender.id\n where message.channel = $1\n and coalesce(message.sent_sequence <= $2, true)\n order by message.sent_sequence\n ", + "describe": { + "columns": [ + { + "name": "channel: channel::Id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "sender_id: login::Id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "sender_name", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "id: Id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "sent_at: DateTime", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "sent_sequence: Sequence", + "ordinal": 6, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "eb9d7146e615044568f9bb47a7317dda3e429faac15e02c900f62f83c25070a7" +} diff --git a/docs/api.md b/docs/api.md index 73bfb38..56d361d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -212,10 +212,7 @@ data: { data: "type": "message", data: "event": "sent", data: "at": "2024-09-27T23:19:10.208147Z", -data: "channel": { -data: "id": "C9876cyyz", -data: "name": "example channel 2" -data: }, +data: "channel": "C9876cyyz", data: "sender": { data: "id": "L1234abcd", data: "name": "example username" @@ -229,10 +226,6 @@ data: { data: "type": "message", data: "event": "deleted", data: "at": "2024-09-28T02:44:27.077355Z", -data: "channel": { -data: "id": "C9876cyyz", -data: "name": "example channel 2" -data: }, data: "id": "M1312acab" data: } diff --git a/hi-ui/src/apiServer.js b/hi-ui/src/apiServer.js index 648edf5..e87e2d6 100644 --- a/hi-ui/src/apiServer.js +++ b/hi-ui/src/apiServer.js @@ -81,10 +81,10 @@ function onChannelEvent(data) { function onMessageEvent(data) { switch (data.event) { case 'sent': - messages.update((value) => value.addMessage(data)); + messages.update((value) => value.addMessage(data.channel, data.at, data.sender, data.body)); break; case 'deleted': - messages.update((value) => value.deleteMessage(data.channel.id, data.id)); + messages.update((value) => value.deleteMessage(data.id)); break; } } diff --git a/hi-ui/src/store/messages.js b/hi-ui/src/store/messages.js index cefd725..f9e0856 100644 --- a/hi-ui/src/store/messages.js +++ b/hi-ui/src/store/messages.js @@ -7,13 +7,8 @@ export class Messages { return this.channels[channel] || []; } - addMessage(message) { - let { - channel, - ...payload - } = message; - let channel_id = channel.id; - this.updateChannel(channel_id, (messages) => [...messages, payload]); + addMessage(channel, at, sender, body) { + this.updateChannel(channel, (messages) => [...messages, { at, sender, body }]); return this; } @@ -23,8 +18,10 @@ export class Messages { } - deleteMessage(channel, message) { - this.updateChannel(channel, (messages) => messages.filter((msg) => msg.id != message)); + deleteMessage(message) { + for (let channel in this.channels) { + this.updateChannel(channel, (messages) => messages.filter((msg) => msg.id != message)); + } return this; } diff --git a/src/message/event.rs b/src/message/event.rs index 9f49a32..1cd5847 100644 --- a/src/message/event.rs +++ b/src/message/event.rs @@ -1,8 +1,5 @@ use super::{snapshot::Message, Id}; -use crate::{ - channel::Channel, - event::{Instant, Sequenced}, -}; +use crate::event::{Instant, Sequenced}; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] #[serde(tag = "event", rename_all = "snake_case")] @@ -42,7 +39,6 @@ impl From for Event { pub struct Deleted { #[serde(flatten)] pub instant: Instant, - pub channel: Channel, pub id: Id, } diff --git a/src/message/history.rs b/src/message/history.rs index b5886af..09e69b7 100644 --- a/src/message/history.rs +++ b/src/message/history.rs @@ -45,7 +45,6 @@ impl History { self.deleted.map(|instant| { Deleted { instant, - channel: self.message.channel.clone(), id: self.message.id.clone(), } .into() diff --git a/src/message/repo.rs b/src/message/repo.rs index 5b199a7..e098fb2 100644 --- a/src/message/repo.rs +++ b/src/message/repo.rs @@ -2,7 +2,7 @@ use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use super::{snapshot::Message, History, Id}; use crate::{ - channel::{self, Channel}, + channel, clock::DateTime, event::{Instant, ResumePoint, Sequence}, login::{self, Login}, @@ -50,10 +50,7 @@ impl<'c> Messages<'c> { .map(|row| History { message: Message { sent: *sent, - // Use "as created" here as we don't care about providing a perfectly up-to-date - // representation of the channel. The `name` is informational (and the ID, which is - // normative, is fixed over time). - channel: channel.as_created(), + channel: channel.id().clone(), sender: sender.clone(), id: row.id, body: row.body, @@ -75,8 +72,7 @@ impl<'c> Messages<'c> { let messages = sqlx::query!( r#" select - channel.id as "channel_id: channel::Id", - channel.name as "channel_name", + message.channel as "channel: channel::Id", sender.id as "sender_id: login::Id", sender.name as "sender_name", message.id as "id: Id", @@ -84,9 +80,8 @@ impl<'c> Messages<'c> { sent_at as "sent_at: DateTime", sent_sequence as "sent_sequence: Sequence" from message - join channel on message.channel = channel.id join login as sender on message.sender = sender.id - where channel.id = $1 + where message.channel = $1 and coalesce(message.sent_sequence <= $2, true) order by message.sent_sequence "#, @@ -99,10 +94,7 @@ impl<'c> Messages<'c> { at: row.sent_at, sequence: row.sent_sequence, }, - channel: Channel { - id: row.channel_id, - name: row.channel_name, - }, + channel: row.channel, sender: Login { id: row.sender_id, name: row.sender_name, @@ -122,8 +114,7 @@ impl<'c> Messages<'c> { let message = sqlx::query!( r#" select - channel.id as "channel_id: channel::Id", - channel.name as "channel_name", + message.channel as "channel: channel::Id", sender.id as "sender_id: login::Id", sender.name as "sender_name", message.id as "id: Id", @@ -131,7 +122,6 @@ impl<'c> Messages<'c> { sent_at as "sent_at: DateTime", sent_sequence as "sent_sequence: Sequence" from message - join channel on message.channel = channel.id join login as sender on message.sender = sender.id where message.id = $1 "#, @@ -143,10 +133,7 @@ impl<'c> Messages<'c> { at: row.sent_at, sequence: row.sent_sequence, }, - channel: Channel { - id: row.channel_id, - name: row.channel_name, - }, + channel: row.channel, sender: Login { id: row.sender_id, name: row.sender_name, @@ -207,8 +194,7 @@ impl<'c> Messages<'c> { let messages = sqlx::query!( r#" select - channel.id as "channel_id: channel::Id", - channel.name as "channel_name", + message.channel as "channel: channel::Id", sender.id as "sender_id: login::Id", sender.name as "sender_name", message.id as "id: Id", @@ -216,7 +202,6 @@ impl<'c> Messages<'c> { sent_at as "sent_at: DateTime", sent_sequence as "sent_sequence: Sequence" from message - join channel on message.channel = channel.id join login as sender on message.sender = sender.id where coalesce(message.sent_sequence > $1, true) "#, @@ -228,10 +213,7 @@ impl<'c> Messages<'c> { at: row.sent_at, sequence: row.sent_sequence, }, - channel: Channel { - id: row.channel_id, - name: row.channel_name, - }, + channel: row.channel, sender: Login { id: row.sender_id, name: row.sender_name, diff --git a/src/message/snapshot.rs b/src/message/snapshot.rs index a8cf734..8c1e62a 100644 --- a/src/message/snapshot.rs +++ b/src/message/snapshot.rs @@ -2,13 +2,13 @@ use super::{ event::{Event, Sent}, Id, }; -use crate::{channel::Channel, event::Instant, login::Login}; +use crate::{channel, event::Instant, login::Login}; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct Message { #[serde(flatten)] pub sent: Instant, - pub channel: Channel, + pub channel: channel::Id, pub sender: Login, pub id: Id, pub body: String, -- cgit v1.2.3 From 2f0b77e8fd02a137047c8975a573626cd76310ff Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Wed, 9 Oct 2024 01:43:34 -0400 Subject: 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. --- ...7e56e515d121964d3481e48aae3558b53a5123ce7d.json | 50 +++++++++++++++++ docs/api.md | 17 +++--- hi-ui/src/apiServer.js | 2 +- hi-ui/src/routes/+page.svelte | 14 ++--- hi-ui/src/store/logins.js | 3 +- hi-ui/src/store/messages.js | 16 ++++-- src/boot/app.rs | 42 ++++++--------- src/boot/mod.rs | 63 +--------------------- src/message/repo.rs | 35 ++++++++++++ 9 files changed, 128 insertions(+), 114 deletions(-) create mode 100644 .sqlx/query-191255b9e55c9b36d0fd047e56e515d121964d3481e48aae3558b53a5123ce7d.json (limited to 'hi-ui/src/store/messages.js') 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, pub channels: Vec, -} - -#[derive(serde::Serialize)] -pub struct Channel { - pub id: channel::Id, - pub name: String, pub messages: Vec, } - -impl Channel { - fn new( - channel: channel::Channel, - messages: impl IntoIterator, - ) -> 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 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, 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 { let message = sqlx::query!( r#" -- cgit v1.2.3