Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# Changelog
All notable changes to this project will be documented in this file.

## [unreleased]
### Added
- Show locale date as tooltip for relative date time information

## [8.5.0] - 2025-10-03
### Fixed
- Archived polls could not get entered anymore
Expand Down
10 changes: 6 additions & 4 deletions src/components/Activity/ActivityItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ const props = defineProps({
},
})

const dateActivityRelative = computed(() =>
DateTime.fromISO(props.activity.datetime).toRelative(),
)
const dateActivity = computed(() => DateTime.fromISO(props.activity.datetime))

const message = computed(() => {
const subject = props.activity.subject_rich[0]
Expand Down Expand Up @@ -97,7 +95,11 @@ const message = computed(() => {
<template>
<div class="activity-item">
<div class="activity-item__content">
<span class="activity-item__date">{{ dateActivityRelative }}</span>
<span
:title="dateActivity.toLocaleString(DateTime.DATETIME_SHORT)"
class="activity-item__date"
>{{ dateActivity.toRelative() }}</span
>
<NcRichText :text="message.subject" :arguments="message.parameters" />
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Cards/CardAddProposals.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const optionAddDatesModalProps = {
<p v-if="pollStore.isProposalExpirySet && !pollStore.isProposalExpired">
{{
t('polls', 'The proposal period ends {timeRelative}.', {
timeRelative: pollStore.proposalsExpireRelative,
timeRelative:
pollStore.getProposalExpirationDateTime.toRelative() as string,
})
}}
</p>
Expand Down
10 changes: 6 additions & 4 deletions src/components/Comments/CommentItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ const preferencesStore = usePreferencesStore()

const { comment } = defineProps<{ comment: CommentsGrouped }>()

const dateCommentedRelative = computed(() =>
DateTime.fromSeconds(comment.timestamp).toRelative(),
)
const commentedDateTime = computed(() => DateTime.fromSeconds(comment.timestamp))

const isCurrentUser = computed(
() => sessionStore.currentUser?.id === comment.user.id,
Expand Down Expand Up @@ -103,7 +101,11 @@ const deletable = computed(

<UserBubble v-else-if="!isCurrentUser" :user="comment.user" />

<span class="comment-item__date">{{ dateCommentedRelative }}</span>
<span
class="comment-item__date"
:title="commentedDateTime.toLocaleString(DateTime.DATETIME_SHORT)">
{{ commentedDateTime.toRelative() }}
</span>

<span v-if="isConfidential" class="comment-item__confidential">
{{ confidentialRecipient }}
Expand Down
45 changes: 29 additions & 16 deletions src/components/Poll/PollInfoLine.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import closedPollIcon from 'vue-material-design-icons/LockOutline.vue'
import creationIcon from 'vue-material-design-icons/ClockOutline.vue'
import ProposalsIcon from 'vue-material-design-icons/HandExtendedOutline.vue'
import ExpirationIcon from 'vue-material-design-icons/CalendarEndOutline.vue'
import { DateTime } from 'luxon'

const pollStore = usePollStore()
const sharesStore = useSharesStore()
Expand All @@ -36,6 +37,7 @@ const subTexts = computed(() => {
text: t('polls', 'Archived'),
class: 'archived',
iconComponent: archivedPollIcon,
title: '',
})
return subTexts
}
Expand All @@ -46,6 +48,7 @@ const subTexts = computed(() => {
text: [t('polls', 'Unpublished')].join('. '),
class: 'unpublished',
iconComponent: unpublishedIcon,
title: '',
})
return subTexts
}
Expand All @@ -58,6 +61,7 @@ const subTexts = computed(() => {
}),
class: '',
iconComponent: null,
title: '',
})
} else {
subTexts.push({
Expand All @@ -67,15 +71,19 @@ const subTexts = computed(() => {
}),
class: '',
iconComponent: null,
title: '',
})
}

if (pollStore.isClosed) {
subTexts.push({
id: 'closed',
text: timeExpirationRelative.value,
text: pollStore.getExpirationDateTime.toRelative() as string,
class: 'closed',
iconComponent: closedPollIcon,
title: pollStore.getExpirationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
return subTexts
}
Expand All @@ -84,10 +92,14 @@ const subTexts = computed(() => {
subTexts.push({
id: 'expiring',
text: t('polls', 'Closing {relativeExpirationTime}', {
relativeExpirationTime: timeExpirationRelative.value,
relativeExpirationTime:
pollStore.getExpirationDateTime.toRelative() as string,
}),
class: closeToClosing.value ? 'closing' : 'open',
iconComponent: ExpirationIcon,
title: pollStore.getExpirationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
return subTexts
}
Expand All @@ -96,10 +108,14 @@ const subTexts = computed(() => {
subTexts.push({
id: 'expired',
text: t('polls', 'Proposal period ended {timeRelative}', {
timeRelative: pollStore.proposalsExpireRelative,
timeRelative:
pollStore.getProposalExpirationDateTime.toRelative() as string,
}),
class: 'proposal',
iconComponent: ProposalsIcon,
title: pollStore.getProposalExpirationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
return subTexts
}
Expand All @@ -108,49 +124,46 @@ const subTexts = computed(() => {
subTexts.push({
id: 'proposal-open',
text: t('polls', 'Proposal period ends {timeRelative}', {
timeRelative: pollStore.proposalsExpireRelative,
timeRelative:
pollStore.getProposalExpirationDateTime.toRelative() as string,
}),
class: 'proposal',
iconComponent: ProposalsIcon,
title: pollStore.getProposalExpirationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
return subTexts
}

if (subTexts.length < 2) {
subTexts.push({
id: 'created',
text: dateCreatedRelative.value,
text: pollStore.getCreationDateTime.toRelative(),
class: 'created',
iconComponent: creationIcon,
title: pollStore.getCreationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
}
return subTexts
})

const dateCreatedRelative = computed(() =>
pollStore.getCreationDateTime.toRelative(),
)

const closeToClosing = computed(
() =>
!pollStore.isClosed
&& pollStore.configuration.expire
&& pollStore.getExpirationDateTime.diffNow().as('days') < 1,
)

const timeExpirationRelative = computed(() => {
if (pollStore.configuration.expire) {
return pollStore.getExpirationDateTime.toRelative() as string
}
return t('polls', 'never')
})
</script>

<template>
<div class="poll-info-line">
<span
v-for="subText in subTexts"
:key="subText.id"
:title="subText.title"
:class="['sub-text', subText.class]">
<Component :is="subText.iconComponent" :size="16" />
<span class="sub-text">{{ subText.text }}</span>
Expand Down
39 changes: 27 additions & 12 deletions src/components/Poll/PollInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { usePollStore } from '../../stores/poll'
import { useSubscriptionStore } from '../../stores/subscription'
import { useOptionsStore } from '../../stores/options'
import { useVotesStore } from '../../stores/votes'
import { DateTime } from 'luxon'

const pollStore = usePollStore()
const sessionStore = useSessionStore()
Expand All @@ -46,12 +47,14 @@ const proposalsStatus = computed(() => {
}
if (pollStore.isProposalExpirySet && !pollStore.isProposalExpired) {
return t('polls', 'Proposal period ends {timeRelative}', {
timeRelative: pollStore.proposalsExpireRelative,
timeRelative:
pollStore.getProposalExpirationDateTime.toRelative() as string,
})
}
if (pollStore.isProposalExpirySet && pollStore.isProposalExpired) {
return t('polls', 'Proposal period ended {timeRelative}', {
timeRelative: pollStore.proposalsExpireRelative,
timeRelative:
pollStore.getProposalExpirationDateTime.toRelative() as string,
})
}
return t('polls', 'No proposals are allowed')
Expand All @@ -75,12 +78,7 @@ const accessCaption = computed(() =>
? t('polls', 'Private poll')
: t('polls', 'Openly accessible poll'),
)
const dateCreatedRelative = computed(
() => pollStore.getCreationDateTime.toRelative() as string,
)
const dateExpiryRelative = computed(
() => pollStore.getCreationDateTime.toRelative() as string,
)

const currentTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
const countAllYesVotes = computed(() => votesStore.countAllVotesByAnswer('yes'))
const countAllMaybeVotes = computed(() => votesStore.countAllVotesByAnswer('maybe'))
Expand Down Expand Up @@ -113,23 +111,40 @@ const countUsedVotes = computed(
</template>
{{ accessCaption }}
</BadgeDiv>
<BadgeDiv>
<BadgeDiv
:title="
t('polls', 'Created {dateTime}', {
dateTime: pollStore.getCreationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
">
<template #icon>
<CreationIcon />
</template>
{{
t('polls', 'Created {dateRelative}', {
dateRelative: dateCreatedRelative,
dateRelative:
pollStore.getCreationDateTime.toRelative() as string,
})
}}
</BadgeDiv>
<BadgeDiv v-if="pollStore.configuration.expire">
<BadgeDiv
v-if="pollStore.configuration.expire"
:title="
t('polls', 'Closing: {dateTime}', {
dateTime: pollStore.getCreationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
})
">
<template #icon>
<ClosedIcon />
</template>
{{
t('polls', 'Closing: {dateRelative}', {
dateRelative: dateExpiryRelative,
dateRelative:
pollStore.getCreationDateTime.toRelative() as string,
})
}}
</BadgeDiv>
Expand Down
53 changes: 34 additions & 19 deletions src/components/PollList/PollItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,26 @@ const { poll, noLink = false } = defineProps<Props>()

const pollStore = usePollStore()
const preferencesStore = usePreferencesStore()

const expirationDateTime = computed(() =>
DateTime.fromMillis(poll.configuration.expire * 1000),
)

const createdDateTime = computed(() =>
DateTime.fromMillis(poll.status.created * 1000),
)

const archiveDateTime = computed(() =>
DateTime.fromMillis(poll.status.archivedDate * 1000),
)

const closeToClosing = computed(
() =>
!poll.status.isExpired
&& poll.configuration.expire
&& DateTime.fromMillis(poll.configuration.expire * 1000).diffNow('hours')
.hours < 36,
&& expirationDateTime.value.diffNow('hours').hours < 36,
)

const timeExpirationRelative = computed(() => {
if (poll.configuration.expire) {
return DateTime.fromMillis(poll.configuration.expire * 1000).toRelative()
}
return t('polls', 'never')
})

const expiryClass = computed(() => {
if (poll.status.isExpired) {
return 'error'
Expand All @@ -70,10 +75,6 @@ const expiryClass = computed(() => {
return 'success'
})

const timeCreatedRelative = computed(
() => DateTime.fromMillis(poll.status.created * 1000).toRelative() as string,
)

const descriptionLine = computed(() => {
if (preferencesStore.user.verbosePollsList) {
if (poll.configuration.description) {
Expand All @@ -84,15 +85,13 @@ const descriptionLine = computed(() => {

if (poll.status.isArchived) {
return t('polls', 'Archived {relativeTime}', {
relativeTime: DateTime.fromMillis(
poll.status.archivedDate * 1000,
).toRelative() as string,
relativeTime: archiveDateTime.value.toRelative() as string,
})
}

return t('polls', 'Started {relativeTime} from {ownerName}', {
ownerName: poll.owner.displayName,
relativeTime: timeCreatedRelative.value,
relativeTime: createdDateTime.value.toRelative() as string,
})
})
</script>
Expand Down Expand Up @@ -259,13 +258,29 @@ const descriptionLine = computed(() => {
<BadgeSmallDiv
v-if="poll.configuration.expire"
:class="expiryClass"
:title="t('polls', 'Expiration')">
:title="
t(
'polls',
poll.status.isExpired
? 'Expired {dateTime}'
: 'Expires {dateTime}',
{
dateTime: expirationDateTime.toLocaleString(
DateTime.DATETIME_SHORT,
) as string,
},
)
">
>
<template #icon>
<ClosedPollsIcon v-if="poll.status.isExpired" :size="16" />
<ExpirationIcon v-else :size="16" />
</template>
{{ timeExpirationRelative }}
{{
expirationDateTime
? expirationDateTime.toRelative()
: t('polls', 'never')
}}
</BadgeSmallDiv>
</div>

Expand Down
Loading