replace vue-timeago with custom timeago
This commit is contained in:
parent
88c3103755
commit
e2dc0d85fc
28 changed files with 565 additions and 1259 deletions
|
@ -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"
|
||||
|
|
38
src/App.scss
38
src/App.scss
|
@ -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 {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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> ·
|
||||
</div>
|
||||
<div class="total">
|
||||
{{totalVotesCount}} {{ $t("polls.votes") }}
|
||||
{{totalVotesCount}} {{ $t("polls.votes") }} ·
|
||||
</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 {
|
||||
|
|
|
@ -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 => {
|
||||
this.loading = false
|
||||
})
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
52
src/components/timeago/timeago.vue
Normal file
52
src/components/timeago/timeago.vue
Normal 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>
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "スレッド",
|
||||
|
|
|
@ -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": "スレッド",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
45
src/services/date_utils/date_utils.js
Normal file
45
src/services/date_utils/date_utils.js
Normal 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
|
||||
}
|
|
@ -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"]
|
||||
]
|
|
@ -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"]
|
||||
]
|
|
@ -1,10 +0,0 @@
|
|||
[
|
||||
"now",
|
||||
["%ss", "%ss"],
|
||||
["%smin", "%smin"],
|
||||
["%sh", "%sh"],
|
||||
["%sd", "%sd"],
|
||||
["%sw", "%sw"],
|
||||
["%smo", "%smo"],
|
||||
["%sy", "%sy"]
|
||||
]
|
|
@ -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"]
|
||||
]
|
|
@ -1,10 +0,0 @@
|
|||
[
|
||||
"たった今",
|
||||
"%s 秒前",
|
||||
"%s 分前",
|
||||
"%s 時間前",
|
||||
"%s 日前",
|
||||
"%s 週間前",
|
||||
"%s ヶ月前",
|
||||
"%s 年前"
|
||||
]
|
|
@ -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"]
|
||||
]
|
40
test/unit/specs/services/date_utils/date_utils.spec.js
Normal file
40
test/unit/specs/services/date_utils/date_utils.spec.js
Normal 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' })
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue