Changeset 6901f8b for src/Clients
- Timestamp:
- 11/04/21 17:01:30 (3 years ago)
- Branches:
- dev
- Children:
- caaf82d
- Parents:
- 1e0d869 (diff), b9d7ae5 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Location:
- src/Clients/Angular/finki-chattery/src
- Files:
-
- 1 added
- 16 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts
r1e0d869 r6901f8b 24 24 public user: ApplicationUser | null = null; 25 25 public oidcUser: User | null = null; 26 public selfUser: SelfUserResponse | null = null; 26 27 27 28 constructor(private baseApi: BaseApiService) { … … 35 36 user?.profile.isVerified 36 37 ); 38 39 this.selfUserDto().subscribe((selfUser) => { 40 if (selfUser) { 41 this.selfUser = selfUser; 42 } 43 }); 37 44 }); 38 45 } … … 86 93 user.profile.isVerified 87 94 ); 95 96 if (!this.selfUser) { 97 this.selfUserDto().subscribe((selfUser) => { 98 if (selfUser) { 99 this.selfUser = selfUser; 100 } 101 }); 102 } 88 103 }); 89 104 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts
r1e0d869 r6901f8b 2 2 import { Injectable } from '@angular/core'; 3 3 import { Action, Store } from '@ngrx/store'; 4 import { Observable, Subject , throwError} from 'rxjs';5 import { catchError,filter, map } from 'rxjs/operators';4 import { Observable, Subject } from 'rxjs'; 5 import { filter, map } from 'rxjs/operators'; 6 6 7 7 import { … … 9 9 PreviewQuestionViewModel, 10 10 QuestionStateViewModel, 11 SearchQuestionsQueryViewModel 11 SearchQuestionsQueryViewModel, 12 VoteType 12 13 } from 'src/app/shared-app/models'; 14 import { AuthService } from '../services'; 13 15 import { 14 16 EffectStartedWorking, … … 16 18 GetPreviewQuestionsPopular, 17 19 GetQuestionState, 18 GetSearchQuestions 20 GetSearchQuestions, 21 SetCorrectAnswer, 22 VoteAnswer 19 23 } from './question-state/question.actions'; 20 24 import { questionStateQuery } from './question-state/question.selectors'; … … 29 33 effectWorking$: Observable<boolean | HttpErrorResponse>; 30 34 31 constructor(private store: Store<QuestionState>) { 32 this.effectWorking$ = this.store.select(questionStateQuery.effectWorking).pipe( 33 filter((effect) => effect !== null), 34 map((effect) => { 35 if (effect instanceof HttpErrorResponse) { 36 throw effect; 37 } else { 38 return effect; 39 } 40 }), 41 catchError((err) => { 42 return throwError(err); 43 }) 44 ); 35 constructor(private store: Store<QuestionState>, private auth: AuthService) { 36 this.effectWorking$ = this.store.select(questionStateQuery.effectWorking).pipe(filter((effect) => effect !== null)); 37 } 38 39 public currentQuestionOwnedByCurrentUser(): Observable<boolean> { 40 return this.getQuestion().pipe(map((question) => this.auth.selfUser?.student?.uid === question.student.uid)); 45 41 } 46 42 … … 81 77 } 82 78 79 public setCorrectAnswer(questionUid: string, answerUid: string): void { 80 this.dispatchEffect(new SetCorrectAnswer(questionUid, answerUid)); 81 } 82 83 public voteAnswer(answerUid: string, questionUid: string, voteType: VoteType): void { 84 this.dispatchEffect(new VoteAnswer(questionUid, answerUid, voteType)); 85 } 86 83 87 public fetchQuestion(questionUid: string): void { 84 88 this.dispatchEffect(new GetQuestionState(questionUid)); -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question-state-response.models.ts
r1e0d869 r6901f8b 1 import { VoteType } from 'src/app/shared-app/models'; 2 1 3 export class QuestionStateResponse { 2 4 public uid!: string; … … 34 36 public correctAnswer!: boolean; 35 37 public createdOn!: moment.Moment; 36 public upvotesCount!: number;38 public votesCount!: number; 37 39 public studentResponse!: AnswerStudentQuestionStateResponse; 38 40 public answerResponsesResponse!: AnswerResponseQuestionStateResponse[]; … … 73 75 public text!: string; 74 76 } 77 78 export class VoteAnswerResponse { 79 public answerUid!: string; 80 public voteUid!: string; 81 public voteType!: VoteType; 82 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts
r1e0d869 r6901f8b 2 2 import { Action } from '@ngrx/store'; 3 3 4 import { PreviewQuestionViewModel, QuestionStateViewModel, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models'; 4 import { 5 PreviewQuestionViewModel, 6 QuestionStateViewModel, 7 SearchQuestionsQueryViewModel, 8 VoteAnswerViewModel, 9 VoteType 10 } from 'src/app/shared-app/models'; 5 11 6 12 export enum QuestionActionTypes { 7 13 GetQuestionState = '[Question] Get state', 8 14 GetQuestionStateSuccess = '[Question] Get state success', 15 SetCorrectAnswer = '[Question] Set Correct Answer', 16 SetCorrectAnswerSuccess = '[Question] Set Correct Answer success', 9 17 GetPreviewQuestionsLatest = '[Question] Get preview questions Latest', 10 18 GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success', … … 13 21 GetSearchQuestions = '[Question] Get search questions', 14 22 GetSearchQuestionsSuccess = '[Question] Get search questions Success', 23 VoteAnswer = '[Question] Vote answer', 24 VoteAnswerSuccess = '[Question] Vote answer Success', 15 25 EffectStartedWorking = '[Question] Effect Started Working', 16 26 EffectFinishedWorking = '[Question] Effect Finished Working', … … 28 38 29 39 constructor(public payload: QuestionStateViewModel) {} 40 } 41 42 export class SetCorrectAnswer implements Action { 43 readonly type = QuestionActionTypes.SetCorrectAnswer; 44 45 constructor(public questionUid: string, public answerUid: string) {} 46 } 47 48 export class SetCorrectAnswerSuccess implements Action { 49 readonly type = QuestionActionTypes.SetCorrectAnswerSuccess; 50 51 constructor(public payload: string) {} 30 52 } 31 53 … … 66 88 } 67 89 90 export class VoteAnswer implements Action { 91 readonly type = QuestionActionTypes.VoteAnswer; 92 93 constructor(public questionUid: string, public answerUid: string, public voteType: VoteType) {} 94 } 95 96 export class VoteAnswerSuccess implements Action { 97 readonly type = QuestionActionTypes.VoteAnswerSuccess; 98 99 constructor(public payload: VoteAnswerViewModel) {} 100 } 101 68 102 export class EffectStartedWorking implements Action { 69 103 readonly type = QuestionActionTypes.EffectStartedWorking; … … 89 123 | GetPreviewQuestionsPopularSuccess 90 124 | GetSearchQuestionsSuccess 125 | VoteAnswerSuccess 126 | SetCorrectAnswerSuccess 91 127 | EffectStartedWorking 92 128 | EffectFinishedWorking -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts
r1e0d869 r6901f8b 1 1 import { Injectable } from '@angular/core'; 2 2 import { act, Actions, createEffect, ofType } from '@ngrx/effects'; 3 import { catchError, filter, mergeMap, switchMap, withLatestFrom } from 'rxjs/operators';3 import { catchError, filter, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators'; 4 4 import { PreviewQuestionsOrderEnum, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models'; 5 5 import { TranslateFromJsonService } from 'src/app/shared-app/services'; 6 6 7 7 import { BaseApiService } from 'src/app/shared-app/services/base-api.service'; 8 import { NotificationService } from '../../services/notification.service'; 8 9 import { QuestionFacadeService } from '../question-facade.service'; 9 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models'; 10 import { VoteAnswerRequest } from './question-state-request.models'; 11 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models'; 10 12 import { 11 13 EffectFinishedWorking, … … 19 21 GetSearchQuestions, 20 22 GetSearchQuestionsSuccess, 21 QuestionActionTypes 23 QuestionActionTypes, 24 SetCorrectAnswer, 25 SetCorrectAnswerSuccess, 26 VoteAnswer, 27 VoteAnswerSuccess 22 28 } from './question.actions'; 23 29 import { QuestionMapper } from './question.mapper'; … … 31 37 private api: BaseApiService, 32 38 private translate: TranslateFromJsonService, 33 private facade: QuestionFacadeService 39 private facade: QuestionFacadeService, 40 private notification: NotificationService 34 41 ) {} 35 42 … … 103 110 ); 104 111 }); 112 113 voteAnswer$ = createEffect(() => { 114 return this.actions$.pipe( 115 ofType<VoteAnswer>(QuestionActionTypes.VoteAnswer), 116 mergeMap((action) => { 117 const body = new VoteAnswerRequest(action.voteType); 118 return this.api.post<VoteAnswerResponse>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/votes`, body).pipe( 119 tap((state) => this.notification.successNotification('sucess-vote')), 120 switchMap((state) => [new VoteAnswerSuccess(QuestionMapper.ToVoteAnswerViewModel(state)), new EffectFinishedWorking()]), 121 catchError((err) => [new EffectFinishedWorkingError(err)]) 122 ); 123 }) 124 ); 125 }); 126 127 setCorrectAnswer$ = createEffect(() => { 128 return this.actions$.pipe( 129 ofType<SetCorrectAnswer>(QuestionActionTypes.SetCorrectAnswer), 130 mergeMap((action) => { 131 return this.api.put<string>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/correct`).pipe( 132 tap((state) => this.notification.successNotification('success-correct-answer')), 133 switchMap((state) => [new SetCorrectAnswerSuccess(state), new EffectFinishedWorking()]), 134 catchError((err) => [new EffectFinishedWorkingError(err)]) 135 ); 136 }) 137 ); 138 }); 105 139 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts
r1e0d869 r6901f8b 10 10 QuestionStateViewModel, 11 11 StudentQuestionStateViewModel, 12 TeamQuestionStateViewModel 12 TeamQuestionStateViewModel, 13 VoteAnswerViewModel 13 14 } from 'src/app/shared-app/models'; 14 15 import { TranslateFromJsonService } from 'src/app/shared-app/services'; 15 import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';16 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models'; 16 17 17 18 export class QuestionMapper { … … 51 52 x.correctAnswer, 52 53 moment(x.createdOn), 53 x. upvotesCount,54 x.votesCount, 54 55 answerStudent, 55 56 answerResponses … … 113 114 return questions; 114 115 } 116 117 public static ToVoteAnswerViewModel(response: VoteAnswerResponse): VoteAnswerViewModel { 118 return new VoteAnswerViewModel(response.answerUid, response.voteUid, response.voteType); 119 } 115 120 } -
src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts
r1e0d869 r6901f8b 1 import { AnswerQuestionStateViewModel, VoteType } from 'src/app/shared-app/models'; 1 2 import { QuestionAction, QuestionActionTypes } from './question.actions'; 2 3 import { initialState, QuestionState } from './question.state'; … … 25 26 searchQuestionsQuery: action.query 26 27 }; 28 case QuestionActionTypes.VoteAnswerSuccess: { 29 if (state.question) { 30 return { 31 ...state, 32 question: { 33 ...state.question, 34 answers: state.question.answers.map((x) => { 35 if (x.uid === action.payload.answerUid) { 36 let votesCountNew = x.votesCount; 37 38 switch (action.payload.voteType) { 39 case VoteType.Upvote: 40 votesCountNew++; 41 break; 42 case VoteType.Downvote: 43 votesCountNew--; 44 break; 45 } 46 47 return { 48 ...x, 49 votesCount: votesCountNew 50 }; 51 } 52 53 return x; 54 }) 55 } 56 }; 57 } 58 59 return { 60 ...state 61 }; 62 } 63 case QuestionActionTypes.SetCorrectAnswerSuccess: { 64 if (state.question) { 65 return { 66 ...state, 67 question: { 68 ...state.question, 69 answers: state.question.answers.map((x) => { 70 if (x.correctAnswer) { 71 return { 72 ...x, 73 correctAnswer: false 74 }; 75 } 76 if (x.uid === action.payload) { 77 return { 78 ...x, 79 correctAnswer: true 80 }; 81 } 82 return x; 83 }) 84 } 85 }; 86 } 87 88 return { 89 ...state 90 }; 91 } 27 92 case QuestionActionTypes.EffectStartedWorking: { 28 93 return { -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.html
r1e0d869 r6901f8b 1 <div >1 <div class="main-div"> 2 2 <div class="vote-icons" fxLayout="column" fxLayoutAlign="center center"> 3 3 <mat-icon class="vote-icon" (click)="voted(VoteType.Upvote)" [inline]="true">arrow_drop_up</mat-icon> … … 7 7 >check</mat-icon 8 8 > 9 <mat-icon 10 *ngIf="canSetCorrectAnswer && !correct" 11 [inline]="true" 12 class="text-center text-bold green set-correct-answer" 13 (click)="setCorrectAnswer.emit()" 14 >check</mat-icon 15 > 9 16 </div> 10 17 </div> -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.scss
r1e0d869 r6901f8b 14 14 cursor: pointer; 15 15 } 16 17 .show-on-hover { 18 display: none; 19 } 20 21 .set-correct-answer { 22 opacity: 0.3; 23 visibility: hidden; 24 } 25 26 .main-div:hover > .vote-icons > .set-correct-answer { 27 visibility: visible; 28 } 29 30 .main-div { 31 height: 100%; 32 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.ts
r1e0d869 r6901f8b 1 1 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 import { VoteType } from 'src/app/shared-app/models'; 2 3 import { ButtonType } from '../button/button.models'; 3 4 export enum VoteType {5 Upvote,6 Downvote7 }8 4 9 5 @Component({ … … 14 10 export class VoteComponent implements OnInit { 15 11 @Input() voteCount: number | undefined; 12 @Input() canSetCorrectAnswer: boolean | null = false; 16 13 @Input() correct = false; 17 14 @Output() voteClicked = new EventEmitter<VoteType>(); 15 @Output() setCorrectAnswer = new EventEmitter<void>(); 18 16 19 17 VoteType = VoteType; -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html
r1e0d869 r6901f8b 14 14 <mat-card-content> 15 15 <div fxLayout="row wrap" fxLayoutAlign="space-around none"> 16 <app-vote [voteCount]="answer.upvotesCount" [correct]="answer.correctAnswer" fxFlex="6%"></app-vote> 16 <app-vote 17 [canSetCorrectAnswer]="canSetCorrectAnswer | async" 18 [voteCount]="answer.votesCount" 19 [correct]="answer.correctAnswer" 20 (voteClicked)="answerVoted($event, answer.uid, question.uid)" 21 (setCorrectAnswer)="setCorrectAnswer(question.uid, answer.uid)" 22 fxFlex="6%" 23 ></app-vote> 17 24 <div fxFlex="92%"> 18 25 <app-text-editor -
src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts
r1e0d869 r6901f8b 1 import { HttpErrorResponse } from '@angular/common/http'; 1 2 import { Component, OnInit } from '@angular/core'; 3 import { NotificationService } from 'src/app/core/services/notification.service'; 2 4 import { QuestionFacadeService } from 'src/app/core/state/question-facade.service'; 3 5 4 import { QuestionStateViewModel } from 'src/app/shared-app/models';6 import { QuestionStateViewModel, VoteType } from 'src/app/shared-app/models'; 5 7 import { ButtonType } from '../../generic/button/button.models'; 6 8 … … 11 13 }) 12 14 export class QuestionPreviewComponent implements OnInit { 15 canSetCorrectAnswer = this.questionFacade.currentQuestionOwnedByCurrentUser(); 13 16 question!: QuestionStateViewModel; 14 17 working = true; 15 18 ButtonType = ButtonType; 16 constructor(private questionFacade: QuestionFacadeService ) {}19 constructor(private questionFacade: QuestionFacadeService, private notification: NotificationService) {} 17 20 18 21 ngOnInit(): void { … … 21 24 this.working = false; 22 25 }); 26 27 this.questionFacade.effectWorking$.subscribe((effect) => { 28 if (effect instanceof HttpErrorResponse) { 29 this.notification.handleErrorsNotification(effect.error); 30 } 31 }); 32 } 33 34 answerVoted(voteType: VoteType, answerUid: string, questionUid: string): void { 35 this.questionFacade.voteAnswer(answerUid, questionUid, voteType); 36 } 37 38 setCorrectAnswer(questionUid: string, answerUid: string): void { 39 this.questionFacade.setCorrectAnswer(questionUid, answerUid); 23 40 } 24 41 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-enums.models.ts
r1e0d869 r6901f8b 3 3 Popular 4 4 } 5 6 export enum VoteType { 7 Upvote, 8 Downvote 9 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-view-models.models.ts
r1e0d869 r6901f8b 1 import { VoteType } from '.'; 2 1 3 export class QuestionStateViewModel { 2 4 constructor( … … 32 34 public correctAnswer: boolean, 33 35 public createdOn: moment.Moment, 34 public upvotesCount: number,36 public votesCount: number, 35 37 public student: AnswerStudentQuestionStateViewModel, 36 38 public answerResponses: AnswerResponseQuestionStateViewModel[] … … 73 75 constructor(public text: string) {} 74 76 } 77 78 export class VoteAnswerViewModel { 79 constructor(public answerUid: string, public voteUid: string, public voteType: VoteType) {} 80 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/models/user.models.ts
r1e0d869 r6901f8b 9 9 } 10 10 11 export class SelfUserResponse {12 uid!: string;13 }14 15 11 export enum ApplicationUserType { 16 12 Student = 'Student', … … 19 15 Guest = 'Guest' 20 16 } 17 18 export class SelfUserResponse { 19 public student?: StudentSelfResponse | null; 20 public teacher?: TeacherSelfResponse | null; 21 public moderator?: ModeratorSelfResponse | null; 22 } 23 24 export class StudentSelfResponse { 25 public uid!: string; 26 public applicationUserId!: number; 27 public index!: string; 28 public reputation!: number; 29 public imageUrl!: string; 30 public questions!: StudentQuestionResponse[]; 31 public teams!: StudentTeamResponse[]; 32 } 33 34 export class StudentQuestionResponse { 35 public questionUid!: string; 36 public title!: string; 37 } 38 39 export class StudentTeamResponse { 40 public teamUid!: string; 41 public name!: string; 42 } 43 44 export class ModeratorSelfResponse { 45 public uid!: string; 46 public applicationUserId!: number; 47 } 48 49 export class TeacherSelfResponse { 50 public uid!: string; 51 public applicationUserId!: number; 52 public teams!: TeacherTeamResponse[]; 53 } 54 55 export class TeacherTeamResponse { 56 public teamUid!: string; 57 public name!: string; 58 } -
src/Clients/Angular/finki-chattery/src/app/shared-app/services/base-api.service.ts
r1e0d869 r6901f8b 19 19 20 20 public getSelfUser(): Observable<SelfUserResponse> { 21 return this.get<SelfUserResponse>(' self');21 return this.get<SelfUserResponse>('v1/self'); 22 22 } 23 23 -
src/Clients/Angular/finki-chattery/src/assets/translations/en.json
r1e0d869 r6901f8b 53 53 "ask-question-stepper-ask": "Ask question", 54 54 "ask-question-ask-button-back": "Edit question", 55 "ask-question-ask-button": "Ask question" 55 "sucess-vote": "Successfully voted answer", 56 "success-correct-answer": "Successfully set correct answer", 57 "ask-question-ask-button": "Ask question", 58 "AnswerAlreadyUpvoted": "You have already upvoted this answer", 59 "AnswerAlreadyDownvoted": "You have already downvoted this answer", 60 "StudentHasBadReputation": "You have bad reputation and can not vote" 56 61 }
Note:
See TracChangeset
for help on using the changeset viewer.