Merge pull request #5392 from tsiegleauq/vote-await-server-answer
Wait for server while voting
This commit is contained in:
commit
0275df6ab2
@ -1,5 +1,5 @@
|
||||
<ng-container *ngIf="poll">
|
||||
<ng-container *ngIf="vmanager.canVote(poll) && !alreadyVoted; else cannotVote">
|
||||
<ng-container *ngIf="vmanager.canVote(poll) && !alreadyVoted && !deliveringVote; else cannotVote">
|
||||
<!-- Poll hint -->
|
||||
<p *ngIf="pollHint">
|
||||
<i>{{ pollHint }}</i>
|
||||
@ -9,9 +9,7 @@
|
||||
<h4 *ngIf="poll.pollmethod === AssignmentPollMethod.Votes && poll.votes_amount > 1">
|
||||
{{ 'Available votes' | translate }}:
|
||||
|
||||
<b>
|
||||
{{ getVotesAvailable() }}/{{ poll.votes_amount }}
|
||||
</b>
|
||||
<b> {{ getVotesAvailable() }}/{{ poll.votes_amount }} </b>
|
||||
</h4>
|
||||
|
||||
<!-- Options and Actions -->
|
||||
@ -39,6 +37,7 @@
|
||||
class="vote-button"
|
||||
mat-raised-button
|
||||
(click)="saveSingleVote(option.id, action.vote)"
|
||||
[disabled]="deliveringVote"
|
||||
[ngClass]="
|
||||
voteRequestData.votes[option.id] === action.vote ||
|
||||
voteRequestData.votes[option.id] === 1
|
||||
@ -67,6 +66,7 @@
|
||||
mat-raised-button
|
||||
(click)="saveGlobalVote('N')"
|
||||
[ngClass]="voteRequestData.global === 'N' ? 'voted-no' : ''"
|
||||
[disabled]="deliveringVote"
|
||||
>
|
||||
<mat-icon> thumb_down </mat-icon>
|
||||
</button>
|
||||
@ -103,13 +103,19 @@
|
||||
|
||||
<ng-template #cannotVote>
|
||||
<div class="centered-button-wrapper">
|
||||
<div>
|
||||
<div *ngIf="!deliveringVote">
|
||||
<mat-icon class="vote-submitted">
|
||||
check_circle
|
||||
</mat-icon>
|
||||
<br />
|
||||
<span>{{ 'Voting successful.' | translate }}</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="deliveringVote" class="submit-vote-indicator">
|
||||
<mat-spinner class="small-spinner"></mat-spinner>
|
||||
<br />
|
||||
<span>{{ 'Delivering vote... Please wait!' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
@ -67,3 +67,10 @@
|
||||
.mat-divider-horizontal {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.submit-vote-indicator {
|
||||
text-align: center;
|
||||
.mat-spinner {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
@ -29,7 +29,8 @@ interface VoteActions {
|
||||
@Component({
|
||||
selector: 'os-assignment-poll-vote',
|
||||
templateUrl: './assignment-poll-vote.component.html',
|
||||
styleUrls: ['./assignment-poll-vote.component.scss']
|
||||
styleUrls: ['./assignment-poll-vote.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssignmentPoll> implements OnInit {
|
||||
public AssignmentPollMethod = AssignmentPollMethod;
|
||||
@ -47,7 +48,8 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
operator: OperatorService,
|
||||
public vmanager: VotingService,
|
||||
private pollRepo: AssignmentPollRepositoryService,
|
||||
private promptService: PromptService
|
||||
private promptService: PromptService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
super(title, translate, matSnackbar, operator);
|
||||
}
|
||||
@ -58,6 +60,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
this.defineVoteOptions();
|
||||
} else {
|
||||
this.alreadyVoted = true;
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,19 +107,23 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
return !!this.voteRequestData.global;
|
||||
}
|
||||
|
||||
public submitVote(): void {
|
||||
public async submitVote(): Promise<void> {
|
||||
const title = this.translate.instant('Submit selection now?');
|
||||
const content = this.translate.instant('Your decision cannot be changed afterwards.');
|
||||
this.promptService.open(title, content).then(confirmed => {
|
||||
if (confirmed) {
|
||||
this.pollRepo
|
||||
.vote(this.voteRequestData, this.poll.id)
|
||||
.then(() => {
|
||||
this.alreadyVoted = true;
|
||||
})
|
||||
.catch(this.raiseError);
|
||||
}
|
||||
});
|
||||
const confirmed = await this.promptService.open(title, content);
|
||||
if (confirmed) {
|
||||
this.deliveringVote = true;
|
||||
this.cd.markForCheck();
|
||||
this.pollRepo
|
||||
.vote(this.voteRequestData, this.poll.id)
|
||||
.then(() => {
|
||||
this.alreadyVoted = true;
|
||||
})
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.deliveringVote = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveSingleVote(optionId: number, vote: VoteValue): void {
|
||||
|
@ -42,10 +42,16 @@
|
||||
mat-stroked-button
|
||||
[ngClass]="pollStateActions[poll.state].css"
|
||||
(click)="changeState(poll.nextState)"
|
||||
[disabled]="stateChangePending"
|
||||
>
|
||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||
<span class="next-state-label">
|
||||
{{ poll.nextStateActionVerbose | translate }}
|
||||
<ng-container *ngIf="!stateChangePending">
|
||||
{{ poll.nextStateActionVerbose | translate }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="stateChangePending">
|
||||
{{ 'In progress, please wait...' | translate }}
|
||||
</ng-container>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -2,19 +2,26 @@
|
||||
<os-poll-progress [poll]="poll"></os-poll-progress>
|
||||
</div>
|
||||
<ng-container *ngIf="poll && !poll.user_has_voted; else userHasVotes">
|
||||
<div *ngIf="vmanager.canVote(poll)" class="vote-button-grid">
|
||||
<div *ngIf="vmanager.canVote(poll) && !deliveringVote" class="vote-button-grid">
|
||||
<!-- Voting -->
|
||||
<div class="vote-button" *ngFor="let option of voteOptions">
|
||||
<button
|
||||
mat-raised-button
|
||||
(click)="saveVote(option.vote)"
|
||||
[ngClass]="currentVote && currentVote.vote === option.vote ? option.css : ''"
|
||||
[disabled]="deliveringVote"
|
||||
>
|
||||
<mat-icon> {{ option.icon }}</mat-icon>
|
||||
</button>
|
||||
<span class="vote-label"> {{ option.label | translate }} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="deliveringVote" class="submit-vote-indicator">
|
||||
<mat-spinner class="small-spinner"></mat-spinner>
|
||||
<br />
|
||||
<span>{{ 'Delivering vote... Please wait!' | translate }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #userHasVotes>
|
||||
|
@ -8,6 +8,14 @@
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
}
|
||||
|
||||
.submit-vote-indicator {
|
||||
margin-top: 1em;
|
||||
text-align: center;
|
||||
.mat-spinner {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.vote-button {
|
||||
display: inline-grid;
|
||||
grid-gap: 1em;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
@ -22,7 +22,8 @@ interface VoteOption {
|
||||
@Component({
|
||||
selector: 'os-motion-poll-vote',
|
||||
templateUrl: './motion-poll-vote.component.html',
|
||||
styleUrls: ['./motion-poll-vote.component.scss']
|
||||
styleUrls: ['./motion-poll-vote.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MotionPollVoteComponent extends BasePollVoteComponent<ViewMotionPoll> {
|
||||
public currentVote: VoteOption = {};
|
||||
@ -54,19 +55,28 @@ export class MotionPollVoteComponent extends BasePollVoteComponent<ViewMotionPol
|
||||
operator: OperatorService,
|
||||
public vmanager: VotingService,
|
||||
private pollRepo: MotionPollRepositoryService,
|
||||
private promptService: PromptService
|
||||
private promptService: PromptService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
super(title, translate, matSnackbar, operator);
|
||||
}
|
||||
|
||||
public saveVote(vote: VoteValue): void {
|
||||
public async saveVote(vote: VoteValue): Promise<void> {
|
||||
this.currentVote.vote = vote;
|
||||
const title = this.translate.instant('Submit selection now?');
|
||||
const content = this.translate.instant('Your decision cannot be changed afterwards.');
|
||||
this.promptService.open(title, content).then(confirmed => {
|
||||
if (confirmed) {
|
||||
this.pollRepo.vote(vote, this.poll.id).catch(this.raiseError);
|
||||
}
|
||||
});
|
||||
const confirmed = await this.promptService.open(title, content);
|
||||
|
||||
if (confirmed) {
|
||||
this.deliveringVote = true;
|
||||
this.cd.markForCheck();
|
||||
|
||||
this.pollRepo
|
||||
.vote(vote, this.poll.id)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.deliveringVote = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +42,15 @@
|
||||
|
||||
<!-- Change state button -->
|
||||
<div *osPerms="'motions.can_manage_polls'; and: !hideChangeState">
|
||||
<button mat-stroked-button [ngClass]="pollStateActions[poll.state].css" (click)="changeState(poll.nextState)">
|
||||
<button mat-stroked-button [ngClass]="pollStateActions[poll.state].css" (click)="changeState(poll.nextState)" [disabled]="stateChangePending">
|
||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||
<span class="next-state-label">
|
||||
{{ poll.nextStateActionVerbose | translate }}
|
||||
<ng-container *ngIf="!stateChangePending">
|
||||
{{ poll.nextStateActionVerbose | translate }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="stateChangePending">
|
||||
{{ 'In progress, please wait...' | translate }}
|
||||
</ng-container>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -16,6 +16,8 @@ export abstract class BasePollVoteComponent<V extends ViewBasePoll> extends Base
|
||||
|
||||
public votingErrors = VotingError;
|
||||
|
||||
public deliveringVote = false;
|
||||
|
||||
protected user: ViewUser;
|
||||
|
||||
public constructor(
|
||||
|
@ -15,6 +15,8 @@ import { PollService } from '../services/poll.service';
|
||||
import { ViewBasePoll } from '../models/view-base-poll';
|
||||
|
||||
export abstract class BasePollComponent<V extends ViewBasePoll, S extends PollService> extends BaseViewComponent {
|
||||
public stateChangePending = false;
|
||||
|
||||
public chartDataSubject: BehaviorSubject<ChartData> = new BehaviorSubject([]);
|
||||
|
||||
protected _poll: V;
|
||||
@ -55,10 +57,22 @@ export abstract class BasePollComponent<V extends ViewBasePoll, S extends PollSe
|
||||
const title = this.translate.instant('Are you sure you want to reset this vote?');
|
||||
const content = this.translate.instant('All votes will be lost.');
|
||||
if (await this.promptService.open(title, content)) {
|
||||
this.repo.resetPoll(this._poll).catch(this.raiseError);
|
||||
this.stateChangePending = true;
|
||||
this.repo
|
||||
.resetPoll(this._poll)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.stateChangePending = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.repo.changePollState(this._poll).catch(this.raiseError);
|
||||
this.stateChangePending = true;
|
||||
this.repo
|
||||
.changePollState(this._poll)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.stateChangePending = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,6 +778,17 @@ button.mat-menu-item.selected {
|
||||
display: none !important; /* hide scrollbars in webkit browsers */
|
||||
}
|
||||
|
||||
.small-spinner {
|
||||
// 24px is the size of a normal icon
|
||||
$spinner-size: 24px;
|
||||
height: $spinner-size !important;
|
||||
height: $spinner-size !important;
|
||||
svg {
|
||||
height: $spinner-size !important;
|
||||
height: $spinner-size !important;
|
||||
}
|
||||
}
|
||||
|
||||
.import-table {
|
||||
.table-container {
|
||||
width: 100%;
|
||||
|
Loading…
Reference in New Issue
Block a user