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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
### Added
- Show locale date as tooltip for relative date time information
- Add popover to counter to show voters by answer
- Add forced display mode to poll configuration

## [8.5.0] - 2025-10-03
### Fixed
Expand Down
11 changes: 11 additions & 0 deletions lib/Db/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ public function getStatusArray(): array {
'countParticipants' => $this->getIsAllowed(self::PERMISSION_POLL_RESULTS_VIEW) ? $this->getParticipantsCount() : 0,
'maxVotes' => $this->getVoteLimit(),
'maxOptionVotes' => $this->getOptionLimit(),
'forcedViewMode' => $this->getForcedDisplayMode() === 'user-pref' ? null : $this->getForcedDisplayMode(),
];
}
public function getConfigurationArray(): array {
Expand All @@ -257,6 +258,7 @@ public function getConfigurationArray(): array {
'description' => $this->getDescription(),
'expire' => $this->getExpire(),
'forceConfidentialComments' => $this->getForceConfidentialComments(),
'forcedDisplayMode' => $this->getForcedDisplayMode(),
'hideBookedUp' => boolval($this->getHideBookedUp()),
'maxVotesPerOption' => $this->getOptionLimit(),
'maxVotesPerUser' => $this->getVoteLimit(),
Expand Down Expand Up @@ -328,6 +330,7 @@ public function deserializeArray(array $pollConfiguration): self {
$this->setHideBookedUp($pollConfiguration['hideBookedUp'] ?? $this->getHideBookedUp());
$this->setProposalsExpire($pollConfiguration['proposalsExpire'] ?? $this->getProposalsExpire());
$this->setShowResults($pollConfiguration['showResults'] ?? $this->getShowResults());
$this->setForcedDisplayMode($pollConfiguration['forcedDisplayMode'] ?? $this->getForcedDisplayMode());
$this->setUseNo($pollConfiguration['useNo'] ?? $this->getUseNo());
$this->setOptionLimit($pollConfiguration['maxVotesPerOption'] ?? $this->getOptionLimit());
$this->setVoteLimit($pollConfiguration['maxVotesPerUser'] ?? $this->getVoteLimit());
Expand Down Expand Up @@ -421,6 +424,14 @@ public function getForceConfidentialComments(): bool {
return $this->getMiscSettingsArray()['forceConfidentialComments'] ?? false;
}

private function setForcedDisplayMode(string $value): void {
$this->setMiscSettingsByKey('forcedDisplayMode', $value);
}

public function getForcedDisplayMode(): string {
return $this->getMiscSettingsArray()['forcedDisplayMode'] ?? 'user-pref';
}

private function setCollapseDescription(bool|int $value): void {
$this->setMiscSettingsByKey('collapseDescription', (bool)$value);
}
Expand Down
38 changes: 38 additions & 0 deletions src/components/Configuration/ConfigForceViewMode.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!--
- SPDX-FileCopyrightText: 2021 Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import RadioGroupDiv from '../Base/modules/RadioGroupDiv.vue'
import { t } from '@nextcloud/l10n'
import { usePollStore } from '../../stores/poll'

const emit = defineEmits(['change'])

const pollStore = usePollStore()

const pollForcedViewOptions = [
{
value: 'user-pref',
label: t('polls', 'User preference'),
},
{
value: 'table-view',
label: t('polls', 'Table view'),
},
{
value: 'list-view',
label: t('polls', 'List view'),
},
]
</script>

<template>
<div>
<RadioGroupDiv
v-model="pollStore.configuration.forcedDisplayMode"
:options="pollForcedViewOptions"
@update="emit('change')" />
</div>
</template>
16 changes: 16 additions & 0 deletions src/components/SideBar/SideBarTabConfiguration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import LockedIcon from 'vue-material-design-icons/LockOutline.vue'
import UnlockedIcon from 'vue-material-design-icons/LockOpenVariantOutline.vue'
import ShowResultsIcon from 'vue-material-design-icons/Monitor.vue'
import HideResultsUntilClosedIcon from 'vue-material-design-icons/MonitorLock.vue'
import UserPreferenceIcon from 'vue-material-design-icons/AccountCogOutline.vue'
import ShowResultsNeverIcon from 'vue-material-design-icons/MonitorOff.vue'
import ListViewIcon from 'vue-material-design-icons/ViewListOutline.vue'
import TableViewIcon from 'vue-material-design-icons/Table.vue'

import CardDiv from '../Base/modules/CardDiv.vue'
import ConfigBox from '../Base/modules/ConfigBox.vue'
Expand All @@ -32,6 +35,7 @@ import ConfigVoteLimit from '../Configuration/ConfigVoteLimit.vue'
import { usePollStore } from '../../stores/poll'
import { useVotesStore } from '../../stores/votes'
import ConfigDangerArea from '../Configuration/ConfigDangerArea.vue'
import ConfigForceViewMode from '../Configuration/ConfigForceViewMode.vue'

const pollStore = usePollStore()
const votesStore = useVotesStore()
Expand Down Expand Up @@ -95,6 +99,18 @@ const votesStore = useVotesStore()
<ConfigShowResults @change="pollStore.write" />
</ConfigBox>

<ConfigBox :name="t('polls', 'Forced display mode')">
<template #icon>
<UserPreferenceIcon
v-if="pollStore.configuration.forcedDisplayMode === 'user-pref'" />
<TableViewIcon
v-if="pollStore.configuration.forcedDisplayMode === 'table-view'" />
<ListViewIcon
v-if="pollStore.configuration.forcedDisplayMode === 'list-view'" />
</template>
<ConfigForceViewMode @change="pollStore.write" />
</ConfigBox>

<ConfigBox :name="t('polls', 'Deletion and owner')">
<template #icon>
<DeletePollIcon />
Expand Down
2 changes: 1 addition & 1 deletion src/components/VoteTable/VoteTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function isVotable(participant: User, option: Option) {
grid-auto-flow: column;
overflow: scroll;
margin: auto;
& > div {
&.table-view > div {
border-inline-start: 1px solid var(--color-border);
&.participant {
border-inline-start: none;
Expand Down
14 changes: 12 additions & 2 deletions src/stores/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const usePollStore = defineStore('poll', {
state: (): PollStore => ({
id: 0,
type: 'datePoll',
voteVariant: 'simple',
votingVariant: 'simple',
descriptionSafe: '',
configuration: {
title: '',
Expand All @@ -63,6 +63,7 @@ export const usePollStore = defineStore('poll', {
collapseDescription: true,
expire: 0,
forceConfidentialComments: false,
forcedDisplayMode: 'user-pref',
hideBookedUp: false,
proposalsExpire: 0,
showResults: 'always',
Expand All @@ -86,6 +87,7 @@ export const usePollStore = defineStore('poll', {
countParticipants: 0,
maxVotes: 0,
maxOptionVotes: 0,
forcedViewMode: null,
},
currentUserStatus: {
groupInvitations: [],
Expand Down Expand Up @@ -138,6 +140,10 @@ export const usePollStore = defineStore('poll', {
getters: {
viewMode(state): ViewMode {
const sessionStore = useSessionStore()
if (sessionStore.sessionSettings.viewModeForced) {
return sessionStore.sessionSettings.viewModeForced
}

if (state.type === 'textPoll') {
return sessionStore.viewModeTextPoll
}
Expand Down Expand Up @@ -242,7 +248,7 @@ export const usePollStore = defineStore('poll', {

setViewMode(viewMode: ViewMode): void {
const sessionStore = useSessionStore()
sessionStore.setViewMode(this.type, viewMode)
sessionStore.setViewMode(viewMode, this.type)
},

async load(isChanging: boolean = false): Promise<void> {
Expand All @@ -267,6 +273,10 @@ export const usePollStore = defineStore('poll', {

this.$patch(response.data.poll)

sessionStore.setViewMode(
response.data.poll.configuration.forcedDisplayMode,
)

this.meta.status = 'loaded'
} catch (error) {
if ((error as AxiosError)?.code === 'ERR_CANCELED') {
Expand Down
9 changes: 6 additions & 3 deletions src/stores/poll.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { Chunking, StatusResults, User, UserType } from '../Types'
import { ViewMode } from './preferences.types'

export type PollType = 'textPoll' | 'datePoll'

Expand Down Expand Up @@ -33,6 +34,7 @@ export type PollConfiguration = {
description: string
expire: number
forceConfidentialComments: boolean
forcedDisplayMode: ViewMode | 'user-pref'
hideBookedUp: boolean
maxVotesPerOption: number
maxVotesPerUser: number
Expand All @@ -56,6 +58,7 @@ export type PollStatus = {
countParticipants: number
maxVotes: number
maxOptionVotes: number
forcedViewMode: ViewMode | null
}

export type PollPermissions = {
Expand Down Expand Up @@ -100,14 +103,14 @@ export type CurrentUserStatus = {
export type Poll = {
id: number
type: PollType
voteVariant: VoteVariant
descriptionSafe: string
votingVariant: VoteVariant
configuration: PollConfiguration
descriptionSafe: string
owner: User
pollGroups: number[]
status: PollStatus
currentUserStatus: CurrentUserStatus
permissions: PollPermissions
pollGroups: number[]
revealParticipants: boolean
sortParticipants: SortParticipants
meta: Meta
Expand Down
18 changes: 17 additions & 1 deletion src/stores/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const useSessionStore = defineStore('session', {
sessionSettings: {
viewModeDatePoll: '',
viewModeTextPoll: '',
viewModeForced: null,
},
appSettings: {
allAccessGroups: [],
Expand Down Expand Up @@ -195,11 +196,26 @@ export const useSessionStore = defineStore('session', {
return 'list-view'
},

setViewMode(pollType: PollType, viewMode: ViewMode): void {
setViewMode(
viewMode: ViewMode | 'user-pref',
pollType: PollType | null = null,
): void {
if (viewMode === 'user-pref') {
this.sessionSettings.viewModeForced = null
return
}
if (pollType === null) {
this.sessionSettings.viewModeForced = viewMode
return
}

if (pollType === 'datePoll') {
this.sessionSettings.viewModeForced = null
this.sessionSettings.viewModeDatePoll = viewMode
return
}
if (pollType === 'textPoll') {
this.sessionSettings.viewModeForced = null
this.sessionSettings.viewModeTextPoll = viewMode
}
},
Expand Down
1 change: 1 addition & 0 deletions src/stores/session.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type UserStatus = {
export type SessionSettings = {
viewModeDatePoll: '' | ViewMode
viewModeTextPoll: '' | ViewMode
viewModeForced: null | ViewMode
}

export type Watcher = {
Expand Down
2 changes: 1 addition & 1 deletion src/stores/votes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const useVotesStore = defineStore('votes', {

// sort participants by votes for the selected option if sortByOption is set
// TODO: Future usage: This is only valid for simple votes (not ranked)
if (state.sortByOption > 0 && pollStore.voteVariant === 'simple') {
if (state.sortByOption > 0 && pollStore.votingVariant === 'simple') {
participants.sort((aUser, bUser) => {
// find the votes for the selected option and the users to compare
const aAnswer =
Expand Down