Skip to content

Commit

Permalink
move some vote logic to backend
Browse files Browse the repository at this point in the history
Signed-off-by: dartcafe <github@dartcafe.de>
  • Loading branch information
dartcafe committed Jan 2, 2025
1 parent 515e701 commit c5d8ac7
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 42 deletions.
16 changes: 13 additions & 3 deletions lib/Controller/PublicController.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ public function getVotes(): JSONResponse {
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'DELETE', url: '/s/{token}/user')]
public function deleteUser(): JSONResponse {
$pollId = $this->userSession->getShare()->getPollId();
$this->voteService->deleteUserFromPoll($pollId);
return $this->response(fn () => [
'deleted' => $this->voteService->deleteUserFromPoll($this->userSession->getShare()->getPollId())
'poll' => $this->pollService->get($pollId),
'options' => $this->optionService->list($pollId),
'votes' => $this->voteService->list($pollId)
]);
}

Expand All @@ -175,8 +179,12 @@ public function deleteUser(): JSONResponse {
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'DELETE', url: '/s/{token}/votes/orphaned')]
public function deleteOrphanedVotes(): JSONResponse {
$pollId = $this->userSession->getShare()->getPollId();
$this->voteService->deleteUserFromPoll($pollId, deleteOnlyOrphaned: true);
return $this->response(fn () => [
'deleted' => $this->voteService->deleteUserFromPoll($this->userSession->getShare()->getPollId(), deleteOnlyOrphaned: true)
'poll' => $this->pollService->get($pollId),
'options' => $this->optionService->list($pollId),
'votes' => $this->voteService->list($pollId)
]);
}

Expand Down Expand Up @@ -253,10 +261,12 @@ public function restoreOption(int $optionId): JSONResponse {
#[FrontpageRoute(verb: 'PUT', url: '/s/{token}/vote')]
public function setVote(int $optionId, string $setTo): JSONResponse {
$option = $this->optionService->get($optionId);
$vote = $this->voteService->set($optionId, $setTo);
return $this->response(fn () => [
'vote' => $this->voteService->set($optionId, $setTo),
'vote' => $vote,
'poll' => $this->pollService->get($option->getPollId()),
'options' => $this->optionService->list($option->getPollId()),
'votes' => $this->voteService->list($option->getPollId())
]);
}

Expand Down
9 changes: 5 additions & 4 deletions lib/Controller/VoteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public function list(int $pollId): JSONResponse {
// #[FrontpageRoute(verb: 'PUT', url: '/vote/{optionId}/set/{setTo}')]
public function set(int $optionId, string $setTo): JSONResponse {
$option = $this->optionService->get($optionId);

$vote = $this->voteService->set($optionId, $setTo);
return $this->response(fn () => [
'vote' => $this->voteService->set($optionId, $setTo),
'vote' => $vote,
'poll' => $this->pollService->get($option->getPollId()),
'options' => $this->optionService->list($option->getPollId()),
'votes' => $this->voteService->list($option->getPollId())
]);
}

Expand All @@ -73,8 +74,8 @@ public function set(int $optionId, string $setTo): JSONResponse {
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user/{userId}', postfix: 'named')]
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/user', postfix: 'self')]
public function delete(int $pollId, string $userId = ''): JSONResponse {
$this->voteService->deleteUserFromPoll($pollId, $userId);
return $this->response(fn () => [
'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId),
'poll' => $this->pollService->get($pollId),
'options' => $this->optionService->list($pollId),
'votes' => $this->voteService->list($pollId)
Expand All @@ -90,8 +91,8 @@ public function delete(int $pollId, string $userId = ''): JSONResponse {
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'DELETE', url: '/poll/{pollId}/votes/orphaned')]
public function deleteOrphaned(int $pollId, string $userId = ''): JSONResponse {
$this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true);
return $this->response(fn () => [
'deleted' => $this->voteService->deleteUserFromPoll($pollId, $userId, deleteOnlyOrphaned: true),
'poll' => $this->pollService->get($pollId),
'options' => $this->optionService->list($pollId),
'votes' => $this->voteService->list($pollId)
Expand Down
51 changes: 50 additions & 1 deletion lib/Db/Vote.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
namespace OCA\Polls\Db;

use JsonSerializable;
use OCA\Polls\AppConstants;
use OCA\Polls\Helper\Container;
use OCA\Polls\UserSession;
use OCP\IL10N;
use OCP\L10N\IFactory;

/**
* @psalm-suppress UnusedProperty
Expand Down Expand Up @@ -38,6 +43,10 @@ class Vote extends EntityWithUser implements JsonSerializable {
public const VOTE_NO = 'no';
public const VOTE_EVENTUALLY = 'maybe';

protected IL10N $l10n;
protected UserSession $userSession;
protected IFactory $transFactory;

// schema columns
public $id = null;
protected int $pollId = 0;
Expand All @@ -51,13 +60,51 @@ class Vote extends EntityWithUser implements JsonSerializable {
// joined columns
protected ?int $optionId = null;

public function __construct() {
public function __construct(
) {
$this->userSession = Container::queryClass(UserSession::class);
$this->transFactory = Container::queryClass(IFactory::class);
$this->userSession->getUser()->getLocaleCode();

$languageCode = $this->userSession->getUser()->getLanguageCode() !== '' ? $this->userSession->getUser()->getLanguageCode() : $this->transFactory->findGenericLanguage();

$this->l10n = $this->transFactory->get(
AppConstants::APP_ID,
$languageCode,
$this->userSession->getUser()->getLocaleCode()
);

$this->addType('id', 'integer');
$this->addType('pollId', 'integer');
$this->addType('voteOptionId', 'integer');
$this->addType('deleted', 'integer');
}

private function getAnswerSymbol(): string {
switch ($this->getVoteAnswer()) {
case self::VOTE_YES:
return '';
case self::VOTE_NO:
return '';
case self::VOTE_EVENTUALLY:
return '';
default:
return '';
}
}

private function getAnswerTranslated(): string {
switch ($this->getVoteAnswer()) {
case self::VOTE_YES:
return $this->l10n->t('Yes');
case self::VOTE_NO:
return $this->l10n->t('No');
case self::VOTE_EVENTUALLY:
return $this->l10n->t('Maybe');
default:
return '';
}
}
/**
* @return array
*
Expand All @@ -72,6 +119,8 @@ public function jsonSerialize(): array {
'deleted' => $this->getDeleted(),
'optionId' => $this->getOptionId(),
'user' => $this->getUser(),
'answerSymbol' => $this->getAnswerSymbol(),
'answerTranslated' => $this->getAnswerTranslated(),
];
}
}
8 changes: 4 additions & 4 deletions src/components/Export/ExportPoll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { saveAs } from 'file-saver'
import { t } from '@nextcloud/l10n'
import { showError } from '@nextcloud/dialogs'

import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'

Expand Down Expand Up @@ -147,11 +147,11 @@

optionsStore.list.forEach((option) => {
if (style === 'symbols') {
votesLine.push(votesStore.getVote({ userId: participant.id, option }).answerSymbol ?? '❌')
votesLine.push(votesStore.getVote({ user: participant, option }).answerSymbol ?? '❌')
} else if (style === 'raw') {
votesLine.push(votesStore.getVote({ userId: participant.id, option }).answer)
votesLine.push(votesStore.getVote({ user: participant, option }).answer)
} else {
votesLine.push(votesStore.getVote({ userId: participant.id, option }).answerTranslated ?? t('polls', 'No'))
votesLine.push(votesStore.getVote({ user: participant, option }).answerTranslated ?? t('polls', 'No'))
}
})

Expand Down
2 changes: 1 addition & 1 deletion src/components/VoteTable/VoteItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

const answer = computed(() => votesStore.getVote({
option: props.option,
userId: props.user.id,
user: props.user,
}).answer)

const iconAnswer = computed(() => {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { uniqueArrayOfObjects, uniqueOptions, uniqueParticipants } from './modul
export { groupComments } from './modules/comments.ts'
export { SimpleLink } from './modules/SimpleLink.ts'
export { GuestBubble } from './modules/GuestBubble.ts'
export { StoreHelper } from './modules/StoreHelper.ts'
28 changes: 28 additions & 0 deletions src/helpers/modules/StoreHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { useVotesStore, Vote } from "../../stores/votes";
import { Poll, usePollStore } from "../../stores/poll";
import { Option, useOptionsStore } from "../../stores/options";

const StoreHelper = {
updateStores(data: { poll?: Poll, votes?: Vote[], options?: Option[] }) {
const pollStore = usePollStore()
const votesStore = useVotesStore()
const optionsStore = useOptionsStore()

if (Object.prototype.hasOwnProperty.call(data, 'polls')) {
pollStore.$patch(data.poll)
}
if (Object.prototype.hasOwnProperty.call(data, 'votes')) {
votesStore.list = data.votes
}
if (Object.prototype.hasOwnProperty.call(data, 'options')) {
optionsStore.list = data.options
}
}
}

export { StoreHelper }
48 changes: 19 additions & 29 deletions src/stores/votes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import { defineStore } from 'pinia'
import { PublicAPI, VotesAPI } from '../Api/index.js'
import { User } from '../Types/index.ts'
import { Logger } from '../helpers/index.ts'
import { t } from '@nextcloud/l10n'
import { Option, useOptionsStore } from './options.ts'
import { Logger, StoreHelper } from '../helpers/index.ts'
import { Option } from './options.ts'
import { usePollStore } from './poll.ts'
import { useSessionStore } from './session.ts'

Expand Down Expand Up @@ -55,14 +54,20 @@ export const useVotesStore = defineStore('votes', {
return this.list.filter((vote) => vote.answer === answer).length
},

getVote(payload: { userId: string; option: { text: string } }) {
const found = this.list.find((vote: Vote) => (vote.user.id === payload.userId
getVote(payload: { user: User; option: Option }): Vote {
const found = this.list.find((vote: Vote) => (vote.user.id === payload.user.id
&& vote.optionText === payload.option.text))
if (found === undefined) {
return {
answer: '',
answer: Answer.None,
optionText: payload.option.text,
userId: payload.userId,
user: payload.user,
answerSymbol: AnswerSymbol.None,
answerTranslated: '',
deleted: 0,
id: 0,
optionId: payload.option.id,
pollId: payload.option.pollId,
}
}
return found
Expand All @@ -82,22 +87,7 @@ export const useVotesStore = defineStore('votes', {
return
}

const votes: Vote[] = []
response.data.votes.forEach((vote: Vote) => {
if (vote.answer === Answer.Yes) {
vote.answerTranslated = t('polls', 'Yes')
vote.answerSymbol = AnswerSymbol.Yes
} else if (vote.answer === Answer.Maybe) {
vote.answerTranslated = t('polls', 'Maybe')
vote.answerSymbol = AnswerSymbol.Maybe
} else {
vote.answerTranslated = t('polls', 'No')
vote.answerSymbol = AnswerSymbol.No
}
votes.push(vote)
})

this.list = votes
this.list = response.data.votes
} catch (error) {
if (error?.code === 'ERR_CANCELED') return
this.$reset()
Expand All @@ -119,7 +109,6 @@ export const useVotesStore = defineStore('votes', {

async set(payload: { option: Option; setTo: Answer }) {
const sessionStore = useSessionStore()
const optionsStore = useOptionsStore()
const pollStore = usePollStore()
try {
let response = null
Expand All @@ -128,9 +117,10 @@ export const useVotesStore = defineStore('votes', {
} else {
response = await VotesAPI.setVote(payload.option.id, payload.setTo)
}

this.setItem({ option: payload.option, vote: response.data.vote })
optionsStore.list = response.data.options
pollStore.$patch(response.data.poll)
StoreHelper.updateStores(response.data)

return response
} catch (error) {
if (error?.code === 'ERR_CANCELED') return
Expand All @@ -154,7 +144,7 @@ export const useVotesStore = defineStore('votes', {
} else {
response = await VotesAPI.removeUser(sessionStore.route.params.id)
}
this.list = this.list.filter((vote: Vote) => vote.user.id !== response.data.deleted)
StoreHelper.updateStores(response.data)

} catch (error) {
if (error?.code === 'ERR_CANCELED') return
Expand All @@ -166,8 +156,8 @@ export const useVotesStore = defineStore('votes', {
async deleteUser(payload) {
const sessionStore = useSessionStore()
try {
await VotesAPI.removeUser(sessionStore.route.params.id, payload.userId)
this.list = this.list.filter((vote: Vote) => vote.user.id !== payload.userId)
const response = await VotesAPI.removeUser(sessionStore.route.params.id, payload.userId)
StoreHelper.updateStores(response.data)
} catch (error) {
if (error?.code === 'ERR_CANCELED') return
Logger.error('Error deleting votes', { error, payload })
Expand Down

0 comments on commit c5d8ac7

Please sign in to comment.