generated from t/malachite
add: base chat ui
This commit is contained in:
parent
747a05d649
commit
f53eb3d367
10 changed files with 176 additions and 1031 deletions
|
@ -198,7 +198,12 @@ video {
|
|||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.card_nest .card:nth-child(1) {
|
||||
.card_nest:not(.reverse) .card:nth-child(1) {
|
||||
background: var(--color-super-raised);
|
||||
padding: var(--pad-2) var(--pad-4);
|
||||
}
|
||||
|
||||
.card_nest.reverse .card:nth-child(2) {
|
||||
background: var(--color-super-raised);
|
||||
padding: var(--pad-2) var(--pad-4);
|
||||
}
|
||||
|
|
|
@ -15,11 +15,43 @@
|
|||
("href" "/chats/{{ chat.id }}")
|
||||
(text "{{ components::chat_name(chat=chat, members=members, advanced=true, avatar_size=\"18px\") }}"))))
|
||||
(div
|
||||
("class" "card flex flex_col gap_2")
|
||||
("class" "flex flex_col card_nest reverse")
|
||||
("style" "flex: 1 0 auto")
|
||||
(text "{% if messages|length == 0 -%}")
|
||||
(i ("class" "flex gap_ch items_center fade") (text "{{ icon \"star\" }} This is the start of the chat!"))
|
||||
(text "{%- endif %}"))
|
||||
(div
|
||||
("class" "card flex flex_rev_col gap_2")
|
||||
("style" "flex: 1 0 auto")
|
||||
("id" "messages_stream")
|
||||
(text "{% if chat.last_message_created > 0 -%}")
|
||||
(div
|
||||
("class" "flex gap_ch items_center")
|
||||
("id" "delivered_read_status")
|
||||
(text "{% if chat.last_message_read_by|length <= 1 -%}")
|
||||
; just delivered
|
||||
(text "{{ icon \"check\" }}")
|
||||
(text "{{ chat.last_message_created|int|date(format=\"%Y-%m-%d %H:%M\", timezone=\"Etc/UTC\") }} UTC }}")
|
||||
(text "{%- else -%}")
|
||||
; delivered and read by at least two people
|
||||
(text "{{ icon \"check-check\" }}")
|
||||
(text "{% for uid in chat.last_message_read_by -%}")
|
||||
(text "{{ components::avatar(id=uid) }}")
|
||||
(text "{%- endfor %}")
|
||||
(text "{%- endif %}"))
|
||||
(text "{%- endif %}")
|
||||
|
||||
(text "{% if messages|length == 0 -%}")
|
||||
(i ("class" "flex gap_ch items_center fade") (text "{{ icon \"star\" }} This is the start of the chat!"))
|
||||
(text "{%- endif %}"))
|
||||
(form
|
||||
("class" "card flex flex_row items_center gap_2")
|
||||
(input
|
||||
("type" "text")
|
||||
("class" "w_full")
|
||||
("name" "content")
|
||||
("id" "content")
|
||||
("placeholder" "Send a message to {{ components::chat_name(chat=chat, members=members) }}"))
|
||||
(button
|
||||
("class" "button")
|
||||
(text "{{ icon \"send\" }}"))))
|
||||
|
||||
(script
|
||||
(text ""))
|
||||
|
|
|
@ -40,3 +40,12 @@
|
|||
(text "{%- endif %}")
|
||||
(text "{%- endif %}")
|
||||
(text "{%- endmacro %}")
|
||||
|
||||
(text "{% macro message(message) -%}")
|
||||
(div
|
||||
("class" "flex w_full gap_ch message {%- if user.id == message.owner %} justify_right mine {%- endif %}")
|
||||
(div
|
||||
("class" "inner no_p_margin")
|
||||
(text "{{ message.content|markdown|safe }}"))
|
||||
(text "{{ components::avatar(id=uid) }}"))
|
||||
(text "{%- endmacro %}")
|
||||
|
|
2
app/templates_src/message.lisp
Normal file
2
app/templates_src/message.lisp
Normal file
|
@ -0,0 +1,2 @@
|
|||
(text "{%- import \"components.lisp\" as components -%}")
|
||||
(text "{{ components::message(message) }}")
|
4
app/templates_src/messages.lisp
Normal file
4
app/templates_src/messages.lisp
Normal file
|
@ -0,0 +1,4 @@
|
|||
(text "{%- import \"components.lisp\" as components -%}")
|
||||
(text "{% for message in messages -%}")
|
||||
(text "{{ components::message(message) }}")
|
||||
(text "{%- endfor %}")
|
|
@ -48,7 +48,7 @@
|
|||
(text "home"))
|
||||
(a
|
||||
("class" "button")
|
||||
("href" "https://trisua.com/t/malachite")
|
||||
("href" "https://trisua.com/t/tawny")
|
||||
(text "source"))
|
||||
(hr)
|
||||
(text "{% if not user -%}")
|
||||
|
@ -63,8 +63,8 @@
|
|||
(text "{%- else -%}")
|
||||
(a
|
||||
("class" "button")
|
||||
("href" "/app")
|
||||
(text "app"))
|
||||
("href" "/chats")
|
||||
(text "my chats"))
|
||||
(a
|
||||
("class" "button")
|
||||
("href" "{{ config.service_hosts.tetratto }}/settings")
|
||||
|
|
|
@ -53,6 +53,38 @@ impl DataManager {
|
|||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Get messages by their chat ID (excluding anything after the given date).
|
||||
pub async fn get_messages_by_chat_before(
|
||||
&self,
|
||||
id: usize,
|
||||
before: usize,
|
||||
batch: usize,
|
||||
page: usize,
|
||||
) -> Result<Vec<Message>> {
|
||||
let conn = match self.0.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
"SELECT * FROM t_messages WHERE chat = $1 AND created < $2 LIMIT $3 OFFSET $4",
|
||||
params![
|
||||
&(id as i64),
|
||||
&(before as i64),
|
||||
&(batch as i64),
|
||||
&((page * batch) as i64)
|
||||
],
|
||||
|x| { Self::get_message_from_row(x) }
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Create a new message in the database.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -103,6 +135,12 @@ impl DataManager {
|
|||
return Err(Error::MiscError(e.to_string()));
|
||||
}
|
||||
|
||||
// update chat
|
||||
self.update_chat_last_message_created(data.chat, data.created as i64)
|
||||
.await?;
|
||||
self.update_chat_last_message_read_by(data.chat, Vec::new())
|
||||
.await?;
|
||||
|
||||
// ....
|
||||
Ok(data)
|
||||
}
|
||||
|
|
1025
src/markdown.rs
1025
src/markdown.rs
File diff suppressed because it is too large
Load diff
|
@ -85,3 +85,71 @@ pub async fn chat_request(
|
|||
|
||||
Ok(Html(tera.render("chat.lisp", &ctx).unwrap()))
|
||||
}
|
||||
|
||||
pub async fn single_message_request(
|
||||
jar: CookieJar,
|
||||
Extension(data): Extension<State>,
|
||||
Path(id): Path<usize>,
|
||||
) -> impl IntoResponse {
|
||||
let (ref data, ref tera, ref build_code) = *data.read().await;
|
||||
let user = match get_user_from_token!(jar, data.2) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(render_error(Error::NotAllowed, tera, data.0.0.clone(), None).await);
|
||||
}
|
||||
};
|
||||
|
||||
let message = match data.get_message_by_id(id).await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
return Err(render_error(e, tera, data.0.0.clone(), Some(user)).await);
|
||||
}
|
||||
};
|
||||
|
||||
let mut chat = match data.get_chat_by_id(message.chat).await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
return Err(render_error(e, tera, data.0.0.clone(), Some(user)).await);
|
||||
}
|
||||
};
|
||||
|
||||
if !chat.last_message_read_by.contains(&user.id) {
|
||||
// update chat
|
||||
chat.last_message_read_by.push(user.id);
|
||||
if let Err(e) = data
|
||||
.update_chat_last_message_read_by(user.id, chat.last_message_read_by)
|
||||
.await
|
||||
{
|
||||
return Err(render_error(e, tera, data.0.0.clone(), Some(user)).await);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ctx = default_context(&data.0.0, &build_code, &Some(user));
|
||||
ctx.insert("message", &message);
|
||||
Ok(Html(tera.render("message.lisp", &ctx).unwrap()))
|
||||
}
|
||||
|
||||
pub async fn messages_request(
|
||||
jar: CookieJar,
|
||||
Extension(data): Extension<State>,
|
||||
Path((id, before)): Path<(usize, usize)>,
|
||||
) -> impl IntoResponse {
|
||||
let (ref data, ref tera, ref build_code) = *data.read().await;
|
||||
let user = match get_user_from_token!(jar, data.2) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(render_error(Error::NotAllowed, tera, data.0.0.clone(), None).await);
|
||||
}
|
||||
};
|
||||
|
||||
let messages = match data.get_messages_by_chat_before(id, before, 12, 0).await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
return Err(render_error(e, tera, data.0.0.clone(), Some(user)).await);
|
||||
}
|
||||
};
|
||||
|
||||
let mut ctx = default_context(&data.0.0, &build_code, &Some(user));
|
||||
ctx.insert("messages", &messages);
|
||||
Ok(Html(tera.render("messages.lisp", &ctx).unwrap()))
|
||||
}
|
||||
|
|
|
@ -12,6 +12,14 @@ pub fn routes() -> Router {
|
|||
// chats
|
||||
.route("/chats", get(chats::list_request))
|
||||
.route("/chats/{id}", get(chats::chat_request))
|
||||
.route(
|
||||
"/chats/_templates/message/{id}",
|
||||
get(chats::single_message_request),
|
||||
)
|
||||
.route(
|
||||
"/chats/_templates/chat/{id}/messages/before/{before}",
|
||||
get(chats::messages_request),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue