Adds Messaging streaming

This commit is contained in:
Jonathan Bourdon
2024-07-19 00:45:00 -04:00
parent 89ac0f8bc4
commit 8afa037fbe
8 changed files with 185 additions and 52 deletions

64
src/internal_time_ago.js Normal file
View File

@@ -0,0 +1,64 @@
export function time_ago(time) {
const time_date = new Date(time)
const now_date = new Date(Date.now())
const ago = now_date - time_date
return internal_time_ago(ago)
}
function internal_time_ago(time) {
switch (typeof time) {
case 'number':
break;
case 'string':
time = +new Date(time);
break;
case 'object':
if (time.constructor === Date) time = time.getTime();
break;
default:
time = +new Date();
}
const time_formats = [
[60, 'seconds', 1], // 60
[120, '1 minute ago', '1 minute from now'], // 60*2
[3600, 'minutes', 60], // 60*60, 60
[7200, '1 hour ago', '1 hour from now'], // 60*60*2
[86400, 'hours', 3600], // 60*60*24, 60*60
[172800, 'Yesterday', 'Tomorrow'], // 60*60*24*2
[604800, 'days', 86400], // 60*60*24*7, 60*60*24
[1209600, 'Last week', 'Next week'], // 60*60*24*7*4*2
[2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7
[4838400, 'Last month', 'Next month'], // 60*60*24*7*4*2
[29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4
[58060800, 'Last year', 'Next year'], // 60*60*24*7*4*12*2
[2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12
[5806080000, 'Last century', 'Next century'], // 60*60*24*7*4*12*100*2
[58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100
];
let seconds = time / 1000
let token = 'ago'
let list_choice = 1
if (seconds === 0) {
return 'Just now'
}
if (seconds < 0) {
seconds = Math.abs(seconds);
token = 'from now';
list_choice = 2;
}
let i = 0, format;
while (format = time_formats[i++])
if (seconds < format[0]) {
if (typeof format[2] == 'string')
return format[list_choice];
else
return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token;
}
return time;
}

View File

@@ -2,7 +2,12 @@
<div class="shadow-lg rounded-lg">
<div class="text-lg font-bold">{{ props.content.title }}</div>
<div class="text-lg font-bold">
{{ props.content.title }}
<span class="text-md-caption">
{{ time_ago(props.content.createdAt) }}
</span>
</div>
<div class="h-48 object-cover bg-purple rounded-md">
@@ -39,11 +44,11 @@
<div class="border-b p-6">
<h2 class="font-sans font-semibold">Commentaires</h2>
<MessageList :content-id="props.content.id">
<MessageList :subject-id="props.content.id">
</MessageList>
</div>
<div class="border-b-2 p-6">
<PostMessage :content-id="props.content.id">
<PostMessage :subject-id="props.content.id">
</PostMessage>
</div>
</div>
@@ -57,6 +62,7 @@
import {defineProps, computed} from 'vue';
import MessageList from "@/views/messages/MessageList.vue";
import PostMessage from "@/views/messages/PostMessage.vue";
import {time_ago} from "@/internal_time_ago.js";
const isHttpUrl = computed(() => props.content?.uri?.startsWith('http'))

View File

@@ -37,13 +37,13 @@ const props = defineProps({
const client = useClient()
const contents = ref([])
const max_items = 10
const page_size = 10
const errorMessage = ref()
let last_id = null
async function load({done}) {
try {
let uri = `/api/contents/user/${props.creatorId}?max_items=${max_items}`
let uri = `/api/contents/user/${props.creatorId}?page_size=${page_size}`
if (last_id !== null) uri = uri + `&last_id=${last_id}`
console.log(`Fetching content at: ${uri}`)
@@ -59,7 +59,7 @@ async function load({done}) {
last_id = last_content.id
}
if (contentCount < max_items)
if (contentCount < page_size)
done('empty')
else
done('ok')

View File

@@ -1,5 +1,5 @@
<template>
<seize-indicator></seize-indicator>
<size-indicator></size-indicator>
<!-- Bannière-->
<div class="relative bottom-4 z-20 m">
@@ -82,8 +82,8 @@
<script setup>
import CreatePostButton from "@/views/contents/CreatePostButton.vue";
import {defineProps, ref} from "vue";
import SeizeIndicator from "@/views/tools/SeizeIndicator.vue";
import {defineProps} from "vue";
import SizeIndicator from "@/views/tools/SizeIndicator.vue";
import AboutYou from "@/views/creators/AboutYou.vue";
const props = defineProps({

View File

@@ -13,7 +13,7 @@
<div class="flex justify-between items-center">
<div>
<span class="font-semibold font-sans mr-2">{{ message.createdBy }}</span>
<span class="text-sm font-sans text-gray-700">il y a 3 heures</span>
<span class="text-sm font-sans text-gray-700">{{ time_ago(message.createdAt) }}</span>
</div>
<v-menu>
<template v-slot:activator="{ props }">
@@ -43,28 +43,32 @@
</div>
</template>
<script>
export default {
props: {
message: {
type: Object,
required: true
}
},
methods: {
editMessage(message) {
// Logic for editing the message
console.log('Edit message', message);
},
deleteMessage(message) {
// Logic for deleting the message
console.log('Delete message', message);
},
reportMessage(message) {
// Logic for reporting the message
console.log('Report message', message);
}
<script setup>
import {defineProps} from "vue"
import {time_ago} from "@/internal_time_ago.js";
const props = defineProps({
message: {
type: Object,
required: true
}
});
function editMessage(message) {
// Logic for editing the message
console.log('Edit message', message);
}
function deleteMessage(message) {
// Logic for deleting the message
console.log('Delete message', message);
}
function reportMessage(message) {
// Logic for reporting the message
console.log('Report message', message);
}
</script>

View File

@@ -1,40 +1,99 @@
<template>
<div v-for="(message, index) in messages" :key="index">
<Message :message="message"></Message>
</div>
<v-infinite-scroll :items="messages"
:onLoad="load"
mode="manual"
class="bg-teal justify-items-center">
<template v-for="(message, index) in messages" :key="index">
<Message
:message="message"
class="my-2 p-4 bg-amber-200 w-full"
>
</Message>
</template>
<template v-slot:load-more="{ props }">
<v-btn
size="small"
variant="flat"
v-bind="props"
>Voir plus de commentaires
</v-btn>
</template>
<template v-slot:empty>
Il n'y a pas plus de commentaires
</template>
<template v-slot:error>
<v-alert type="error">{{ errorMessage }}</v-alert>
</template>
</v-infinite-scroll>
</template>
<script setup>
import Message from "@/views/messages/Message.vue";
// import posts from "@/views/posts/posts.json";
import {useClient} from '@/plugins/api.js';
import {defineProps, onBeforeMount, ref} from 'vue';
const props = defineProps({
contentId: {
subjectId: {
type: String,
required: true
}
});
const errorMessage = ref()
let last_id = null
const client = useClient();
const messages = ref();
const messages = ref([]);
onBeforeMount(async () => {
if (props.contentId == null) return
try {
const response = await client.get(`/api/messages/${props.contentId}`)
if (response.status >= 200 && response.status < 300) {
messages.value = response.data
if (props.subjectId == null) return
await load({
page_size: 2,
done: function (status) {
console.log(`Loading status: ${status}`)
}
} catch (error) {
console.error("Failed to fetch messages", error);
}
})
})
async function load({done, page_size}) {
if (props.subjectId == null) return
if (page_size === undefined) page_size = 10
try {
let uri = `/api/messages/${props.subjectId}?page_size=${page_size}`
if (last_id !== null) uri = uri + `&last_id=${last_id}`
console.log(`Fetching messages at: ${uri}`)
const response = await client.get(uri)
if (response.status >= 200 && response.status < 300) {
const messageCount = response.data.length
if (messageCount > 0) {
messages.value.push(...response.data)
const [last_content] = response.data.slice(-1)
last_id = last_content.id
}
if (messageCount < page_size)
done('empty')
else
done('ok')
}
} catch (error) {
console.error("Failed to fetch posts", error);
errorMessage.value = error
done('error')
}
}
</script>

View File

@@ -39,7 +39,7 @@ import {useClient} from '@/plugins/api.js';
import {defineProps, ref} from 'vue';
const props = defineProps({
contentId: {
subjectId: {
type: String,
required: true
}
@@ -52,7 +52,7 @@ const publish = async () => {
await client.post(
`/api/messages/`,
{
"subjectId": props.contentId,
"subjectId": props.subjectId,
"message": message.value
});
}