зеркало из https://github.com/nextcloud/spreed.git
Add a date separator between messages
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Родитель
bc6c1fb8f7
Коммит
96a16df589
|
@ -1663,7 +1663,6 @@
|
|||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
|
||||
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
|
@ -1807,6 +1806,39 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@nextcloud/moment": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-0.2.2.tgz",
|
||||
"integrity": "sha512-ZnZ8hsXuj/smzO4aykR4LCROfmqZfxpceVIbRwsCFXbvHzxshmle/QRPP9mlzXxoBuh2IS+LPm8x7neeZXYVMQ==",
|
||||
"requires": {
|
||||
"@nextcloud/l10n": "0.2.0",
|
||||
"core-js": "3.2.1",
|
||||
"i18next": "17.0.18",
|
||||
"moment": "2.24.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextcloud/l10n": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-0.2.0.tgz",
|
||||
"integrity": "sha512-UyOWCaL5yhsGlR6wtVBrUJoCMR06u58UjMgWyJsofmN6ayyaTJMxqwDAoJQDC0oMj01orMMF7H8dX1vQfvtbvw==",
|
||||
"requires": {
|
||||
"core-js": "3.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.4.tgz",
|
||||
"integrity": "sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
|
||||
"integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nextcloud/router": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-0.1.0.tgz",
|
||||
|
@ -6077,6 +6109,14 @@
|
|||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||
"dev": true
|
||||
},
|
||||
"i18next": {
|
||||
"version": "17.0.18",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-17.0.18.tgz",
|
||||
"integrity": "sha512-FpLbLwzYV/qen9ZTA+mOHiNzLxESnF+sQCe5wT7UShssfDiW/df5JdXuxnPsPQiUd+M1kpB3Jaawcn9RN6atig==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -7259,6 +7299,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"@nextcloud/axios": "^0.5.0",
|
||||
"@nextcloud/initial-state": "^0.2.0",
|
||||
"@nextcloud/l10n": "^0.2.1",
|
||||
"@nextcloud/moment": "^0.2.2",
|
||||
"@nextcloud/router": "^0.1.0",
|
||||
"@nextcloud/vue": "^1.2.0",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
|
|
|
@ -124,7 +124,7 @@ export default {
|
|||
* The message id.
|
||||
*/
|
||||
id: {
|
||||
type: Number,
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
|
|
|
@ -20,22 +20,27 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="messages__avatar">
|
||||
<AuthorAvatar v-if="!isSystemMessage"
|
||||
:author-type="actorType"
|
||||
:author-id="actorId"
|
||||
:display-name="actorDisplayName" />
|
||||
<div class="message-group">
|
||||
<div v-if="dateSeparator" class="message-group__date-header">
|
||||
<span class="date">{{ dateSeparator }}</span>
|
||||
</div>
|
||||
<div class="messages">
|
||||
<Message
|
||||
v-for="(message, index) of messages"
|
||||
:key="message.id"
|
||||
v-bind="message"
|
||||
:is-first-message="index === 0"
|
||||
:actor-display-name="actorDisplayName"
|
||||
:show-author="!isSystemMessage"
|
||||
:is-temporary="message.timestamp === 0" />
|
||||
<div class="wrapper">
|
||||
<div class="messages__avatar">
|
||||
<AuthorAvatar v-if="!isSystemMessage"
|
||||
:author-type="actorType"
|
||||
:author-id="actorId"
|
||||
:display-name="actorDisplayName" />
|
||||
</div>
|
||||
<div class="messages">
|
||||
<Message
|
||||
v-for="(message, index) of messages"
|
||||
:key="message.id"
|
||||
v-bind="message"
|
||||
:is-first-message="index === 0"
|
||||
:actor-display-name="actorDisplayName"
|
||||
:show-author="!isSystemMessage"
|
||||
:is-temporary="message.timestamp === 0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -55,7 +60,7 @@ export default {
|
|||
* The message id.
|
||||
*/
|
||||
id: {
|
||||
type: Number,
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
|
@ -89,6 +94,13 @@ export default {
|
|||
actorId() {
|
||||
return this.messages[0].actorId
|
||||
},
|
||||
/**
|
||||
* The message date.
|
||||
* @returns {string}
|
||||
*/
|
||||
dateSeparator() {
|
||||
return this.messages[0].dateSeparator || ''
|
||||
},
|
||||
/**
|
||||
* The message actor display name.
|
||||
* @returns {string}
|
||||
|
@ -120,6 +132,33 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
@import '../../../assets/variables';
|
||||
|
||||
.message-group {
|
||||
&__date-header {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
margin: 40px 15px 0;
|
||||
border-top: 1px solid var(--color-border);
|
||||
padding-top: 20px;
|
||||
position: relative;
|
||||
|
||||
.date {
|
||||
content: attr(data-date);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
padding: 0 7px 0 7px;
|
||||
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
|
||||
color: var(--color-text-maxcontrast);
|
||||
background-color: var(--color-main-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
max-width: $message-max-width;
|
||||
display: flex;
|
||||
|
|
|
@ -45,6 +45,7 @@ the Vue virtual scroll list component, whose docs you can find [here.](https://g
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import moment from '@nextcloud/moment'
|
||||
import virtualList from 'vue-virtual-scroll-list'
|
||||
import MessagesGroup from './MessagesGroup/MessagesGroup'
|
||||
import { fetchMessages, lookForNewMessages } from '../../services/messagesService'
|
||||
|
@ -120,6 +121,11 @@ export default {
|
|||
let lastMessage = null
|
||||
for (const message of this.messagesList) {
|
||||
if (!this.messagesShouldBeGrouped(message, lastMessage)) {
|
||||
// Add the date separator for different days
|
||||
if (this.messagesHaveDifferentDate(message, lastMessage)) {
|
||||
message.dateSeparator = this.generateDateSeparator(message.timestamp)
|
||||
}
|
||||
|
||||
groups.push([message])
|
||||
lastMessage = message
|
||||
} else {
|
||||
|
@ -164,11 +170,13 @@ export default {
|
|||
* @param {string} message1.actorType Actor type of the new message
|
||||
* @param {string} message1.actorId Actor id of the new message
|
||||
* @param {string} message1.systemMessage System message content of the new message
|
||||
* @param {int} message1.timestamp Timestamp of the new message
|
||||
* @param {null|object} message2 The previous message
|
||||
* @param {string} message2.actorType Actor type of the previous message
|
||||
* @param {string} message2.actorId Actor id of the previous message
|
||||
* @param {string} message2.systemMessage System message content of the previous message
|
||||
* @returns {bool} Boolean if the messages should be grouped or not
|
||||
* @param {int} message2.timestamp Timestamp of the second message
|
||||
* @returns {boolean} Boolean if the messages should be grouped or not
|
||||
*/
|
||||
messagesShouldBeGrouped(message1, message2) {
|
||||
return message2 // Is there a previous message
|
||||
|
@ -178,6 +186,54 @@ export default {
|
|||
&& (message1.systemMessage.length === 0) === (message2.systemMessage.length === 0) // Only group system messages with each others
|
||||
&& message1.actorType === message2.actorType // To have the same author, the type
|
||||
&& message1.actorId === message2.actorId // and the id of the author must be the same
|
||||
&& !this.messagesHaveDifferentDate(message1, message2) // Posted on the same day
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if 2 messages are from the same date
|
||||
*
|
||||
* @param {object} message1 The new message
|
||||
* @param {int} message1.timestamp Timestamp of the new message
|
||||
* @param {null|object} message2 The previous message
|
||||
* @param {int} message2.timestamp Timestamp of the second message
|
||||
* @returns {boolean} Boolean if the messages have the same date
|
||||
*/
|
||||
messagesHaveDifferentDate(message1, message2) {
|
||||
return !message2 // There is no previous message
|
||||
|| moment.unix(message1.timestamp).format('YYYY-MM-DD') !== moment.unix(message2.timestamp).format('YYYY-MM-DD')
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the date header between the messages
|
||||
*
|
||||
* @param {int} timestamp The timestamp of the message
|
||||
* @returns {string} Translated string of "<Today>, <November 11th, 2019>", "<3 days ago>, <November 8th, 2019>"
|
||||
*/
|
||||
generateDateSeparator(timestamp) {
|
||||
const date = moment.unix(timestamp)
|
||||
const dayOfYear = date.format('YYYY-DDD')
|
||||
let relativePrefix = date.fromNow()
|
||||
|
||||
// Use the relative day for today and yesterday
|
||||
const dayOfYearToday = moment().format('YYYY-DDD')
|
||||
if (dayOfYear === dayOfYearToday) {
|
||||
relativePrefix = t('spreed', 'Today')
|
||||
} else {
|
||||
const dayOfYearYesterday = moment().subtract(1, 'days').format('YYYY-DDD')
|
||||
if (dayOfYear === dayOfYearYesterday) {
|
||||
relativePrefix = t('spreed', 'Yesterday')
|
||||
}
|
||||
}
|
||||
|
||||
// <Today>, <November 11th, 2019>
|
||||
return t('spreed', '{relativeDate}, {absoluteDate}', {
|
||||
relativeDate: relativePrefix,
|
||||
// 'LL' formats a localized date including day of month, month
|
||||
// name and year
|
||||
absoluteDate: date.format('LL'),
|
||||
}, undefined, {
|
||||
escape: false, // French "Today" has a ' in it
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче