init
This commit is contained in:
@@ -17,7 +17,7 @@ const userStore = useUserStore();
|
|||||||
const {renderMarkdown} = useMarkdown()
|
const {renderMarkdown} = useMarkdown()
|
||||||
|
|
||||||
const messageContainer = ref<HTMLElement | null>(null);
|
const messageContainer = ref<HTMLElement | null>(null);
|
||||||
const {messages, loading, loadingBefore, loadingAfter} = storeToRefs(messageStore);
|
const {messages, loading, loadingBefore, loadingAfter, hasMoreAfter, isAtBottom} = storeToRefs(messageStore);
|
||||||
const newMessage = ref('');
|
const newMessage = ref('');
|
||||||
|
|
||||||
const SCROLL_LOAD_THRESHOLD = 120;
|
const SCROLL_LOAD_THRESHOLD = 120;
|
||||||
@@ -26,6 +26,10 @@ const paginationLock = ref<'before' | 'after' | null>(null);
|
|||||||
const paginationLockScrollTop = ref(0);
|
const paginationLockScrollTop = ref(0);
|
||||||
const lastScrollTop = ref(0);
|
const lastScrollTop = ref(0);
|
||||||
|
|
||||||
|
const showRecentMessagesButton = computed(() =>
|
||||||
|
!loading.value && (hasMoreAfter.value || !isAtBottom.value),
|
||||||
|
);
|
||||||
|
|
||||||
interface ScrollAnchor {
|
interface ScrollAnchor {
|
||||||
id: string;
|
id: string;
|
||||||
top: number;
|
top: number;
|
||||||
@@ -36,6 +40,8 @@ const scrollToBottom = async () => {
|
|||||||
if (messageContainer.value) {
|
if (messageContainer.value) {
|
||||||
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
||||||
messageStore.setAtBottom(true);
|
messageStore.setAtBottom(true);
|
||||||
|
paginationLock.value = null;
|
||||||
|
lastScrollTop.value = messageContainer.value.scrollTop;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -153,6 +159,12 @@ const sendMessage = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const returnToRecentMessages = async () => {
|
||||||
|
paginationLock.value = null;
|
||||||
|
await messageStore.fetchMessages(channelId.value);
|
||||||
|
await scrollToBottom();
|
||||||
|
};
|
||||||
|
|
||||||
// Only explicit initial loads and realtime messages received while at the
|
// Only explicit initial loads and realtime messages received while at the
|
||||||
// bottom request an automatic scroll. Pagination restores its own anchor.
|
// bottom request an automatic scroll. Pagination restores its own anchor.
|
||||||
watch(messages, async () => {
|
watch(messages, async () => {
|
||||||
@@ -169,7 +181,7 @@ watch(channelId, async (newChannelId) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-container class="pa-0 fill-height d-flex flex-column" fluid>
|
<v-container class="pa-0 fill-height d-flex flex-column channel-layout" fluid>
|
||||||
<div
|
<div
|
||||||
ref="messageContainer"
|
ref="messageContainer"
|
||||||
class="flex-grow-1 overflow-y-auto w-100 message-container"
|
class="flex-grow-1 overflow-y-auto w-100 message-container"
|
||||||
@@ -211,6 +223,22 @@ watch(channelId, async (newChannelId) => {
|
|||||||
<v-progress-linear v-if="loadingAfter" color="primary" indeterminate />
|
<v-progress-linear v-if="loadingAfter" color="primary" indeterminate />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showRecentMessagesButton" class="recent-messages-button">
|
||||||
|
<v-tooltip location="top" text="Revenir aux messages récents">
|
||||||
|
<template #activator="{ props: tooltipProps }">
|
||||||
|
<v-btn
|
||||||
|
v-bind="tooltipProps"
|
||||||
|
aria-label="Revenir aux messages récents"
|
||||||
|
color="primary"
|
||||||
|
elevation="4"
|
||||||
|
icon="mdi-arrow-down-bold"
|
||||||
|
:loading="loading"
|
||||||
|
@click="returnToRecentMessages"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
<v-sheet class="pa-4 flex-shrink-0" width="100%">
|
<v-sheet class="pa-4 flex-shrink-0" width="100%">
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="newMessage"
|
v-model="newMessage"
|
||||||
@@ -244,6 +272,17 @@ watch(channelId, async (newChannelId) => {
|
|||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-layout {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-messages-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
bottom: 92px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.markdown-content :deep(p) {
|
.markdown-content :deep(p) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
pub use sea_orm_migration::prelude::*;
|
pub use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
mod m20220101_000001_create_table;
|
mod m20220101_000001_create_table;
|
||||||
mod m20260808_000002_add_message_channel_id_id_index;
|
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl MigratorTrait for Migrator {
|
impl MigratorTrait for Migrator {
|
||||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||||
vec![
|
vec![Box::new(m20220101_000001_create_table::Migration)]
|
||||||
Box::new(m20220101_000001_create_table::Migration),
|
|
||||||
Box::new(m20260808_000002_add_message_channel_id_id_index::Migration),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -505,6 +505,12 @@ impl MigrationTrait for Migration {
|
|||||||
.to(Alias::new("message"), Alias::new("id"))
|
.to(Alias::new("message"), Alias::new("id"))
|
||||||
.on_delete(ForeignKeyAction::SetNull),
|
.on_delete(ForeignKeyAction::SetNull),
|
||||||
)
|
)
|
||||||
|
.index(
|
||||||
|
Index::create()
|
||||||
|
.name("idx_message_channel_id_id")
|
||||||
|
.col(Alias::new("channel_id"))
|
||||||
|
.col(Alias::new("id")),
|
||||||
|
)
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
use sea_orm_migration::prelude::*;
|
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
|
||||||
pub struct Migration;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl MigrationTrait for Migration {
|
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
manager
|
|
||||||
.create_index(
|
|
||||||
Index::create()
|
|
||||||
.name("idx_message_channel_id_id")
|
|
||||||
.table(Alias::new("message"))
|
|
||||||
.col(Alias::new("channel_id"))
|
|
||||||
.col(Alias::new("id"))
|
|
||||||
.to_owned(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
||||||
manager
|
|
||||||
.drop_index(
|
|
||||||
Index::drop()
|
|
||||||
.name("idx_message_channel_id_id")
|
|
||||||
.table(Alias::new("message"))
|
|
||||||
.to_owned(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Generate test messages directly in the project's SQLite database."""
|
"""Generate test messages directly in the project's SQLite database."""
|
||||||
|
|
||||||
|
# python3 scripts/generate_messages.py \
|
||||||
|
# --db oxspeak.db \
|
||||||
|
# --channel-id 672e7757-b7df-401b-8e47-8c62e1fb9d7d \
|
||||||
|
# --user-id d327a80b-83d4-4a53-9c0b-140f60cc0caa \
|
||||||
|
# --count 1000 \
|
||||||
|
# --min-words 10 \
|
||||||
|
# --max-words 500
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|||||||
Reference in New Issue
Block a user