Adds Messaging streaming
This commit is contained in:
64
src/internal_time_ago.js
Normal file
64
src/internal_time_ago.js
Normal 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;
|
||||
}
|
||||
@@ -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'))
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,29 +43,33 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
<script setup>
|
||||
|
||||
import {defineProps} from "vue"
|
||||
import {time_ago} from "@/internal_time_ago.js";
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editMessage(message) {
|
||||
});
|
||||
|
||||
|
||||
function editMessage(message) {
|
||||
// Logic for editing the message
|
||||
console.log('Edit message', message);
|
||||
},
|
||||
deleteMessage(message) {
|
||||
}
|
||||
|
||||
function deleteMessage(message) {
|
||||
// Logic for deleting the message
|
||||
console.log('Delete message', message);
|
||||
},
|
||||
reportMessage(message) {
|
||||
}
|
||||
|
||||
function reportMessage(message) {
|
||||
// Logic for reporting the message
|
||||
console.log('Report message', message);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch messages", error);
|
||||
if (props.subjectId == null) return
|
||||
await load({
|
||||
page_size: 2,
|
||||
done: function (status) {
|
||||
console.log(`Loading status: ${status}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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>
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user