replace vue-timeago with custom timeago

This commit is contained in:
shpuld 2019-06-09 14:07:54 +03:00
parent 88c3103755
commit e2dc0d85fc
28 changed files with 565 additions and 1259 deletions

View File

@ -33,7 +33,6 @@
"vue-popperjs": "^2.0.3",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.3.4",
"vue-timeago": "^3.1.2",
"vuelidate": "^0.7.4",
"vuex": "^3.0.1",
"whatwg-fetch": "^2.0.3"

View File

@ -182,7 +182,43 @@ input, textarea, .select {
flex: 1;
}
&[type=radio],
&[type=radio] {
display: none;
&:checked + label::before {
box-shadow: 0px 0px 2px black inset, 0px 0px 0px 4px $fallback--fg inset;
box-shadow: var(--inputShadow), 0px 0px 0px 4px var(--fg, $fallback--fg) inset;
background-color: var(--link, $fallback--link);
}
&:disabled {
&,
& + label,
& + label::before {
opacity: .5;
}
}
+ label::before {
display: inline-block;
content: '';
transition: box-shadow 200ms;
width: 1.1em;
height: 1.1em;
border-radius: 100%; // Radio buttons should always be circle
box-shadow: 0px 0px 2px black inset;
box-shadow: var(--inputShadow);
margin-right: .5em;
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
vertical-align: top;
text-align: center;
line-height: 1.1em;
font-size: 1.1em;
box-sizing: border-box;
color: transparent;
overflow: hidden;
box-sizing: border-box;
}
}
&[type=checkbox] {
display: none;
&:checked + label::before {

View File

@ -1,6 +1,7 @@
import Status from '../status/status.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import UserCard from '../user_card/user_card.vue'
import Timeago from '../timeago/timeago.vue'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -13,7 +14,10 @@ const Notification = {
},
props: [ 'notification' ],
components: {
Status, UserAvatar, UserCard
Status,
UserAvatar,
UserCard,
Timeago
},
methods: {
toggleUserExpanded () {

View File

@ -30,12 +30,12 @@
</div>
<div class="timeago" v-if="notification.type === 'follow'">
<span class="faint">
<timeago :since="notification.created_at" :auto-update="240"></timeago>
<Timeago :time="notification.created_at" :auto-update="240"></Timeago>
</span>
</div>
<div class="timeago" v-else>
<router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" class="faint-link">
<timeago :since="notification.created_at" :auto-update="240"></timeago>
<Timeago :time="notification.created_at" :auto-update="240"></Timeago>
</router-link>
</div>
</span>

View File

@ -3,13 +3,11 @@
v-if="currentUserHasVoted"
:poll="poll"
:status-id="statusId"
v-on:poll-refreshed="handlePollUpdate"
/>
<poll-vote
v-else
:poll="poll"
:status-id="statusId"
v-on:user-has-voted="handlePollUpdate"
/>
</template>
@ -26,17 +24,11 @@ export default {
},
computed: {
currentUserHasVoted () {
console.log('currentUserHasVoted poll', this.poll)
return this.poll.voted
},
voted () {
return this.poll.voted
}
},
methods: {
handlePollUpdate (poll) {
// this.poll = poll
}
}
}
</script>

View File

@ -3,19 +3,24 @@
<div class="votes">
<div
class="poll-option"
v-for="(pollOption, index) in poll.options"
:key="index">
<div class="col">{{percentageForOption(pollOption.votes_count)}}%</div>
<div class="col">{{pollOption.title}}</div>
<div class="col"><progress :max="totalVotesCount" :value="pollOption.votes_count"></progress></div>
v-for="(option, index) in poll.options"
:key="index"
:title="`${option.votes_count}/${totalVotesCount} ${$t('polls.votes')}`"
>
<div class="vote-label">
<span>{{percentageForOption(option.votes_count)}}%</span>
<span>{{option.title}}</span>
</div>
<div class="fill" :style="{ 'width': `${percentageForOption(option.votes_count)}%` }"></div>
</div>
</div>
<footer>
<div class="refresh">
<a href="#" @click.stop.prevent="fetchPoll(poll.id)">Refresh</a>&nbsp;·&nbsp;
</div>
<div class="total">
{{totalVotesCount}} {{ $t("polls.votes") }}
{{totalVotesCount}} {{ $t("polls.votes") }}&nbsp;·&nbsp;
</div>
<div class="refresh">
<a href="#" @click.stop.prevent="fetchPoll(poll.id)">Refresh</a>
</div>
</footer>
</div>
@ -25,9 +30,6 @@
export default {
name: 'PollResults',
props: ['poll', 'statusId'],
created () {
console.log(this.poll)
},
computed: {
totalVotesCount () {
return this.poll.votes_count
@ -35,7 +37,7 @@ export default {
},
methods: {
percentageForOption (count) {
return (this.totalVotesCount === 0 ? 0 : (count / this.totalVotesCount * 100)).toFixed(1)
return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100)
},
fetchPoll () {
this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id })
@ -45,18 +47,39 @@ export default {
</script>
<style lang="scss">
@import '../../../_variables.scss';
.poll-results {
margin: 0.7em 0;
.votes {
display: table;
width: 100%;
display: flex;
flex-direction: column;
margin: 0 0 0.5em;
}
.poll-option {
display: table-row;
.col {
display: table-cell;
padding: 0.7em 0.5em;
position: relative;
display: flex;
flex-direction: row;
margin-top: 0.25em;
margin-bottom: 0.25em;
}
.fill {
height: 100%;
position: absolute;
background-color: $fallback--lightBg;
background-color: var(--faintLink, $fallback--lightBg);
border-radius: $fallback--panelRadius;
border-radius: var(--panelRadius, $fallback--panelRadius);
top: 0;
left: 0;
transition: width 0.5s;
}
.vote-label {
display: flex;
align-items: center;
padding: 0.1em 0.25em;
z-index: 1;
span {
margin-right: 0.5em;
}
}
footer {

View File

@ -2,50 +2,103 @@
<form class="poll-vote" v-bind:class="containerClass">
<div
class="poll-choice"
v-for="(pollOption, index) in poll.options"
v-for="(option, index) in poll.options"
:key="index"
>
<input
v-if="poll.multiple"
type="checkbox"
name="choice"
:id="index"
:disabled="loading"
:value="pollOption.title"
v-model="checks[index]"
:value="option.title"
v-model="multipleChoices[index]"
>
<input
v-else
type="radio"
name="choice"
:id="index"
:disabled="loading"
:value="index"
v-model="singleChoiceIndex"
>
<label :for="index">
{{pollOption.title}}
{{option.title}}
</label>
</div>
<button class="btn btn-default poll-vote-button" type="button" @click="vote">{{$t('polls.vote')}}</button>
<div class="footer">
<button
class="btn btn-default poll-vote-button"
type="button"
@click="vote"
:disabled="isDisabled"
>
{{$t('polls.vote')}}
</button>
<Timeago :time="this.poll.expires_at" :auto-update="1"></Timeago>
</div>
</form>
</template>
<script>
import Timeago from '../../timeago/timeago.vue'
export default {
name: 'PollVote',
props: ['poll', 'statusId'],
components: { Timeago },
data () {
return {
loading: false,
checks: []
multipleChoices: [],
singleChoiceIndex: undefined
}
},
computed: {
containerClass: function () {
expired () {
return new Date() > this.poll.expires_at
},
timeleft () {
const expiresAt = new Date(this.poll.expires_at)
return expiresAt
},
expiresAt () {
return Date.parse(this.poll.expires_at).toLocaleString()
},
containerClass () {
return {
loading: this.loading
}
},
choiceIndices () {
return this.multipleChoices.map((entry, index) => index).filter(value => typeof value === 'number')
},
isDisabled () {
const noChoice = this.poll.multiple ? this.choiceIndices.length === 0 : this.singleChoiceIndex === undefined
return this.loading || noChoice
}
},
methods: {
vote () {
this.loading = true
const choices = this.checks.map((entry, index) => index).filter(value => typeof value === 'number')
this.$store.dispatch('votePoll', { id: this.statusId, pollId: this.poll.id, choices }).then(poll => {
if (this.poll.multiple) {
if (this.choiceIndices.length === 0) return
this.$store.dispatch(
'votePoll',
{ id: this.statusId, pollId: this.poll.id, choices: this.choiceIndices }
).then(poll => {
this.loading = false
})
} else {
if (this.singleChoiceIndex === undefined) return
this.$store.dispatch(
'votePoll',
{ id: this.statusId, pollId: this.poll.id, choices: [this.singleChoiceIndex] }
).then(poll => {
this.loading = false
})
}
}
}
}

View File

@ -9,6 +9,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue'
import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
import AvatarList from '../avatar_list/avatar_list.vue'
import Timeago from '../timeago/timeago.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import fileType from 'src/services/file_type/file_type.service'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
@ -291,7 +292,8 @@ const Status = {
UserAvatar,
Gallery,
LinkPreview,
AvatarList
AvatarList,
Timeago
},
methods: {
visibilityIcon (visibility) {
@ -342,7 +344,6 @@ const Status = {
}
},
toggleReplying () {
console.log(this.status)
this.replying = !this.replying
},
gotoOriginal (id) {

View File

@ -52,7 +52,7 @@
<span class="heading-right">
<router-link class="timeago faint-link" :to="{ name: 'conversation', params: { id: status.id } }">
<timeago :since="status.created_at" :auto-update="60"></timeago>
<Timeago :time="status.created_at" :auto-update="60"></Timeago>
</router-link>
<div class="button-icon visibility-icon" v-if="status.visibility">
<i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i>

View File

@ -0,0 +1,52 @@
<template>
<time :datetime="time" :title="localeDateString">
{{ $t(relativeTime.key, [relativeTime.num]) }}
</time>
</template>
<script>
import * as DateUtils from 'src/services/date_utils/date_utils.js'
export default {
name: 'Timeago',
props: ['time', 'autoUpdate', 'longFormat'],
data () {
return {
relativeTime: { key: 'time.now', num: 0 },
interval: null
}
},
created () {
this.refreshRelativeTimeObject()
},
destroyed () {
clearTimeout(this.interval)
},
computed: {
localeDateString () {
return typeof this.time === 'string'
? new Date(Date.parse(this.time)).toLocaleString()
: this.time.toLocaleString()
},
relativeTimeObject () {
return this.longFormat
? DateUtils.relativeTime(this.time)
: DateUtils.relativeTimeShort(this.time)
}
},
methods: {
refreshRelativeTimeObject () {
this.relativeTime = this.longFormat
? DateUtils.relativeTime(this.time)
: DateUtils.relativeTimeShort(this.time)
if (this.autoUpdate) {
this.interval = setTimeout(
this.refreshRelativeTimeObject,
1000 * this.autoUpdate
)
}
}
}
}
</script>

View File

@ -168,6 +168,40 @@
"true": "sí"
}
},
"time": {
"day": "{0} dia",
"days": "{0} dies",
"day_short": "{0} dia",
"days_short": "{0} dies",
"hour": "{0} hour",
"hours": "{0} hours",
"hour_short": "{0}h",
"hours_short": "{0}h",
"in_future": "in {0}",
"in_past": "fa {0}",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} mes",
"months": "{0} mesos",
"month_short": "{0} mes",
"months_short": "{0} mesos",
"now": "ara mateix",
"now_short": "ara mateix",
"second": "{0} second",
"seconds": "{0} seconds",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} setm.",
"weeks": "{0} setm.",
"week_short": "{0} setm.",
"weeks_short": "{0} setm.",
"year": "{0} any",
"years": "{0} anys",
"year_short": "{0} any",
"years_short": "{0} anys"
},
"timeline": {
"collapse": "Replega",
"conversation": "Conversa",

View File

@ -350,6 +350,40 @@
}
}
},
"time": {
"day": "{0} day",
"days": "{0} days",
"day_short": "{0}d",
"days_short": "{0}d",
"hour": "{0} hour",
"hours": "{0} hours",
"hour_short": "{0}h",
"hours_short": "{0}h",
"in_future": "in {0}",
"in_past": "{0} ago",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} měs",
"months": "{0} měs",
"month_short": "{0} měs",
"months_short": "{0} měs",
"now": "teď",
"now_short": "teď",
"second": "{0} second",
"seconds": "{0} seconds",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} týd",
"weeks": "{0} týd",
"week_short": "{0} týd",
"weeks_short": "{0} týd",
"year": "{0} r",
"years": "{0} l",
"year_short": "{0}r",
"years_short": "{0}l"
},
"timeline": {
"collapse": "Zabalit",
"conversation": "Konverzace",

View File

@ -417,6 +417,40 @@
"frontend_version": "Frontend Version"
}
},
"time": {
"day": "{0} day",
"days": "{0} days",
"day_short": "{0}d",
"days_short": "{0}d",
"hour": "{0} hour",
"hours": "{0} hours",
"hour_short": "{0}h",
"hours_short": "{0}h",
"in_future": "in {0}",
"in_past": "{0} ago",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} month",
"months": "{0} months",
"month_short": "{0}mo",
"months_short": "{0}mo",
"now": "just now",
"now_short": "now",
"second": "{0} second",
"seconds": "{0} seconds",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} week",
"weeks": "{0} weeks",
"week_short": "{0}w",
"weeks_short": "{0}w",
"year": "{0} year",
"years": "{0} years",
"year_short": "{0}y",
"years_short": "{0}y"
},
"timeline": {
"collapse": "Collapse",
"conversation": "Conversation",

View File

@ -210,6 +210,40 @@
"true": "päällä"
}
},
"time": {
"day": "{0} päivä",
"days": "{0} päivää",
"day_short": "{0}pv",
"days_short": "{0}pv",
"hour": "{0} tunti",
"hours": "{0} tuntia",
"hour_short": "{0}t",
"hours_short": "{0}t",
"in_future": "{0} tulevaisuudessa",
"in_past": "{0} sitten",
"minute": "{0} minuutti",
"minutes": "{0} minuuttia",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} kuukausi",
"months": "{0} kuukautta",
"month_short": "{0}kk",
"months_short": "{0}kk",
"now": "nyt",
"now_short": "juuri nyt",
"second": "{0} sekunti",
"seconds": "{0} sekuntia",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} viikko",
"weeks": "{0} viikkoa",
"week_short": "{0}vk",
"weeks_short": "{0}vk",
"year": "{0} vuosi",
"years": "{0} vuotta",
"year_short": "{0}v",
"years_short": "{0}v"
},
"timeline": {
"collapse": "Sulje",
"conversation": "Keskustelu",

View File

@ -170,6 +170,40 @@
"true": "tá"
}
},
"time": {
"day": "{0} lá",
"days": "{0} lá",
"day_short": "{0}l",
"days_short": "{0}l",
"hour": "{0} uair",
"hours": "{0} uair",
"hour_short": "{0}u",
"hours_short": "{0}u",
"in_future": "in {0}",
"in_past": "{0} ago",
"minute": "{0} nóimeád",
"minutes": "{0} nóimeád",
"minute_short": "{0}n",
"minutes_short": "{0}n",
"month": "{0} mí",
"months": "{0} mí",
"month_short": "{0}m",
"months_short": "{0}m",
"now": "Anois",
"now_short": "Anois",
"second": "{0} s",
"seconds": "{0} s",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} seachtain",
"weeks": "{0} seachtaine",
"week_short": "{0}se",
"weeks_short": "{0}se",
"year": "{0} bliainta",
"years": "{0} bliainta",
"year_short": "{0}b",
"years_short": "{0}b"
},
"timeline": {
"collapse": "Folaigh",
"conversation": "Cómhra",

View File

@ -327,6 +327,40 @@
}
}
},
"time": {
"day": "{0}日",
"days": "{0}日",
"day_short": "{0}日",
"days_short": "{0}日",
"hour": "{0}時間",
"hours": "{0}時間",
"hour_short": "{0}時間",
"hours_short": "{0}時間",
"in_future": "{0}で",
"in_past": "{0}前",
"minute": "{0}分",
"minutes": "{0}分",
"minute_short": "{0}分",
"minutes_short": "{0}分",
"month": "{0}ヶ月前",
"months": "{0}ヶ月前",
"month_short": "{0}ヶ月前",
"months_short": "{0}ヶ月前",
"now": "たった今",
"now_short": "たった今",
"second": "{0}秒",
"seconds": "{0}秒",
"second_short": "{0}秒",
"seconds_short": "{0}秒",
"week": "{0}週間",
"weeks": "{0}週間",
"week_short": "{0}週間",
"weeks_short": "{0}週間",
"year": "{0}年",
"years": "{0}年",
"year_short": "{0}年",
"years_short": "{0}年"
},
"timeline": {
"collapse": "たたむ",
"conversation": "スレッド",

View File

@ -327,6 +327,40 @@
}
}
},
"time": {
"day": "{0}日",
"days": "{0}日",
"day_short": "{0}日",
"days_short": "{0}日",
"hour": "{0}時間",
"hours": "{0}時間",
"hour_short": "{0}時間",
"hours_short": "{0}時間",
"in_future": "{0}で",
"in_past": "{0}前",
"minute": "{0}分",
"minutes": "{0}分",
"minute_short": "{0}分",
"minutes_short": "{0}分",
"month": "{0}ヶ月前",
"months": "{0}ヶ月前",
"month_short": "{0}ヶ月前",
"months_short": "{0}ヶ月前",
"now": "たった今",
"now_short": "たった今",
"second": "{0}秒",
"seconds": "{0}秒",
"second_short": "{0}秒",
"seconds_short": "{0}秒",
"week": "{0}週間",
"weeks": "{0}週間",
"week_short": "{0}週間",
"weeks_short": "{0}週間",
"year": "{0}年",
"years": "{0}年",
"year_short": "{0}年",
"years_short": "{0}年"
},
"timeline": {
"collapse": "たたむ",
"conversation": "スレッド",

View File

@ -381,6 +381,40 @@
"frontend_version": "Version Frontend"
}
},
"time": {
"day": "{0} jorn",
"days": "{0} jorns",
"day_short": "{0} jorn",
"days_short": "{0} jorns",
"hour": "{0} hour",
"hours": "{0} hours",
"hour_short": "{0}h",
"hours_short": "{0}h",
"in_future": "in {0}",
"in_past": "fa {0}",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} mes",
"months": "{0} meses",
"month_short": "{0} mes",
"months_short": "{0} meses",
"now": "ara meteis",
"now_short": "ara meteis",
"second": "{0} second",
"seconds": "{0} seconds",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} setm.",
"weeks": "{0} setm.",
"week_short": "{0} setm.",
"weeks_short": "{0} setm.",
"year": "{0} an",
"years": "{0} ans",
"year_short": "{0} an",
"years_short": "{0} ans"
},
"timeline": {
"collapse": "Tampar",
"conversation": "Conversacion",

View File

@ -15,7 +15,6 @@ import oauthTokensModule from './modules/oauth_tokens.js'
import pollModule from './modules/poll.js'
import reportsModule from './modules/reports.js'
import VueTimeago from 'vue-timeago'
import VueI18n from 'vue-i18n'
import createPersistedState from './lib/persisted_state.js'
@ -32,14 +31,6 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0]
Vue.use(Vuex)
Vue.use(VueRouter)
Vue.use(VueTimeago, {
locale: currentLocale === 'cs' ? 'cs' : currentLocale === 'ja' ? 'ja' : 'en',
locales: {
'cs': require('../static/timeago-cs.json'),
'en': require('../static/timeago-en.json'),
'ja': require('../static/timeago-ja.json')
}
})
Vue.use(VueI18n)
Vue.use(VueChatScroll)
Vue.use(VueClickOutside)

View File

@ -0,0 +1,45 @@
export const SECOND = 1000
export const MINUTE = 60 * SECOND
export const HOUR = 60 * MINUTE
export const DAY = 24 * HOUR
export const WEEK = 7 * DAY
export const MONTH = 30 * DAY
export const YEAR = 365.25 * DAY
export const relativeTime = date => {
if (typeof date === 'string') date = Date.parse(date)
const round = Date.now() > date ? Math.floor : Math.ceil
const d = Math.abs(Date.now() - date)
let r = { num: round(d / YEAR), key: 'time.years' }
if (d < 30 * SECOND) {
r.num = 0
r.key = 'time.now'
} else if (d < MINUTE) {
r.num = round(d / SECOND)
r.key = 'time.seconds'
} else if (d < HOUR) {
r.num = round(d / MINUTE)
r.key = 'time.minutes'
} else if (d < DAY) {
r.num = round(d / HOUR)
r.key = 'time.hours'
} else if (d < WEEK) {
r.num = round(d / DAY)
r.key = 'time.days'
} else if (d < MONTH) {
r.num = round(d / WEEK)
r.key = 'time.weeks'
} else if (d < YEAR) {
r.num = round(d / MONTH)
r.key = 'time.months'
}
// Remove plural form when singular
if (r.num === 1) r.key = r.key.slice(0, -1)
return r
}
export const relativeTimeShort = date => {
const r = relativeTime(date)
r.key += '_short'
return r
}

View File

@ -1,10 +0,0 @@
[
"ara mateix",
["fa %s s", "fa %s s"],
["fa %s min", "fa %s min"],
["fa %s h", "fa %s h"],
["fa %s dia", "fa %s dies"],
["fa %s setm.", "fa %s setm."],
["fa %s mes", "fa %s mesos"],
["fa %s any", "fa %s anys"]
]

View File

@ -1,10 +0,0 @@
[
"teď",
["%s s", "%s s"],
["%s min", "%s min"],
["%s h", "%s h"],
["%s d", "%s d"],
["%s týd", "%s týd"],
["%s měs", "%s měs"],
["%s r", "%s l"]
]

View File

@ -1,10 +0,0 @@
[
"now",
["%ss", "%ss"],
["%smin", "%smin"],
["%sh", "%sh"],
["%sd", "%sd"],
["%sw", "%sw"],
["%smo", "%smo"],
["%sy", "%sy"]
]

View File

@ -1,10 +0,0 @@
[
"Anois",
["%s s", "%s s"],
["%s n", "%s nóimeád"],
["%s u", "%s uair"],
["%s l", "%s lá"],
["%s se", "%s seachtaine"],
["%s m", "%s mí"],
["%s b", "%s bliainta"]
]

View File

@ -1,10 +0,0 @@
[
"たった今",
"%s 秒前",
"%s 分前",
"%s 時間前",
"%s 日前",
"%s 週間前",
"%s ヶ月前",
"%s 年前"
]

View File

@ -1,10 +0,0 @@
[
"ara meteis",
["fa %s s", "fa %s s"],
["fa %s min", "fa %s min"],
["fa %s h", "fa %s h"],
["fa %s jorn", "fa %s jorns"],
["fa %s setm.", "fa %s setm."],
["fa %s mes", "fa %s meses"],
["fa %s an", "fa %s ans"]
]

View File

@ -0,0 +1,40 @@
import * as DateUtils from 'src/services/date_utils/date_utils.js'
describe.only('DateUtils', () => {
describe('relativeTime', () => {
it('returns now with low amount of seconds', () => {
const futureTime = Date.now() + 20 * DateUtils.SECOND
const pastTime = Date.now() - 20 * DateUtils.SECOND
expect(DateUtils.relativeTime(futureTime)).to.eql({ num: 0, key: 'time.now' })
expect(DateUtils.relativeTime(pastTime)).to.eql({ num: 0, key: 'time.now' })
})
it('rounds down for past', () => {
const time = Date.now() - 1.8 * DateUtils.HOUR
expect(DateUtils.relativeTime(time)).to.eql({ num: 1, key: 'time.hour' })
})
it('rounds up for future', () => {
const time = Date.now() + 1.8 * DateUtils.HOUR
expect(DateUtils.relativeTime(time)).to.eql({ num: 2, key: 'time.hours' })
})
it('uses plural when necessary', () => {
const time = Date.now() - 3.8 * DateUtils.WEEK
expect(DateUtils.relativeTime(time)).to.eql({ num: 3, key: 'time.weeks' })
})
it('works with date string', () => {
const time = Date.now() - 4 * DateUtils.MONTH
const dateString = new Date(time).toISOString()
expect(DateUtils.relativeTime(dateString)).to.eql({ num: 4, key: 'time.months' })
})
})
describe('relativeTimeShort', () => {
it('returns the short version of the same relative time', () => {
const time = Date.now() + 2 * DateUtils.YEAR
expect(DateUtils.relativeTimeShort(time)).to.eql({ num: 2, key: 'time.years_short' })
})
})
})

1142
yarn.lock

File diff suppressed because it is too large Load Diff