Changes in / [b9d7ae5:80e2fe0]


Ignore:
Location:
src
Files:
23 deleted
24 edited

Legend:

Unmodified
Added
Removed
  • src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts

    rb9d7ae5 r80e2fe0  
    2424  public user: ApplicationUser | null = null;
    2525  public oidcUser: User | null = null;
    26   public selfUser: SelfUserResponse | null = null;
    2726
    2827  constructor(private baseApi: BaseApiService) {
     
    3635        user?.profile.isVerified
    3736      );
    38 
    39       this.selfUserDto().subscribe((selfUser) => {
    40         if (selfUser) {
    41           this.selfUser = selfUser;
    42         }
    43       });
    4437    });
    4538  }
     
    9386        user.profile.isVerified
    9487      );
    95 
    96       if (!this.selfUser) {
    97         this.selfUserDto().subscribe((selfUser) => {
    98           if (selfUser) {
    99             this.selfUser = selfUser;
    100           }
    101         });
    102       }
    10388    });
    10489  }
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts

    rb9d7ae5 r80e2fe0  
    33import { Action, Store } from '@ngrx/store';
    44import { Observable, Subject } from 'rxjs';
    5 import { filter, map } from 'rxjs/operators';
     5import { filter } from 'rxjs/operators';
    66
    77import {
     
    1212  VoteType
    1313} from 'src/app/shared-app/models';
    14 import { AuthService } from '../services';
    1514import {
    1615  EffectStartedWorking,
     
    1918  GetQuestionState,
    2019  GetSearchQuestions,
    21   SetCorrectAnswer,
    2220  VoteAnswer
    2321} from './question-state/question.actions';
     
    3331  effectWorking$: Observable<boolean | HttpErrorResponse>;
    3432
    35   constructor(private store: Store<QuestionState>, private auth: AuthService) {
     33  constructor(private store: Store<QuestionState>) {
    3634    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));
    4135  }
    4236
     
    7771  }
    7872
    79   public setCorrectAnswer(questionUid: string, answerUid: string): void {
    80     this.dispatchEffect(new SetCorrectAnswer(questionUid, answerUid));
    81   }
    82 
    8373  public voteAnswer(answerUid: string, questionUid: string, voteType: VoteType): void {
    8474    this.dispatchEffect(new VoteAnswer(questionUid, answerUid, voteType));
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts

    rb9d7ae5 r80e2fe0  
    1313  GetQuestionState = '[Question] Get state',
    1414  GetQuestionStateSuccess = '[Question] Get state success',
    15   SetCorrectAnswer = '[Question] Set Correct Answer',
    16   SetCorrectAnswerSuccess = '[Question] Set Correct Answer success',
    1715  GetPreviewQuestionsLatest = '[Question] Get preview questions Latest',
    1816  GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success',
     
    3836
    3937  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) {}
    5238}
    5339
     
    124110  | GetSearchQuestionsSuccess
    125111  | VoteAnswerSuccess
    126   | SetCorrectAnswerSuccess
    127112  | EffectStartedWorking
    128113  | EffectFinishedWorking
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts

    rb9d7ae5 r80e2fe0  
    11import { Injectable } from '@angular/core';
    22import { act, Actions, createEffect, ofType } from '@ngrx/effects';
    3 import { catchError, filter, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
     3import { catchError, filter, mergeMap, switchMap, withLatestFrom } from 'rxjs/operators';
    44import { PreviewQuestionsOrderEnum, SearchQuestionsQueryViewModel } from 'src/app/shared-app/models';
    55import { TranslateFromJsonService } from 'src/app/shared-app/services';
    66
    77import { BaseApiService } from 'src/app/shared-app/services/base-api.service';
    8 import { NotificationService } from '../../services/notification.service';
    98import { QuestionFacadeService } from '../question-facade.service';
    109import { VoteAnswerRequest } from './question-state-request.models';
     
    2221  GetSearchQuestionsSuccess,
    2322  QuestionActionTypes,
    24   SetCorrectAnswer,
    25   SetCorrectAnswerSuccess,
    2623  VoteAnswer,
    2724  VoteAnswerSuccess
     
    3734    private api: BaseApiService,
    3835    private translate: TranslateFromJsonService,
    39     private facade: QuestionFacadeService,
    40     private notification: NotificationService
     36    private facade: QuestionFacadeService
    4137  ) {}
    4238
     
    117113        const body = new VoteAnswerRequest(action.voteType);
    118114        return this.api.post<VoteAnswerResponse>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/votes`, body).pipe(
    119           tap((state) => this.notification.successNotification('sucess-vote')),
    120115          switchMap((state) => [new VoteAnswerSuccess(QuestionMapper.ToVoteAnswerViewModel(state)), new EffectFinishedWorking()]),
    121116          catchError((err) => [new EffectFinishedWorkingError(err)])
     
    124119    );
    125120  });
    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   });
    139121}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts

    rb9d7ae5 r80e2fe0  
    1 import { AnswerQuestionStateViewModel, VoteType } from 'src/app/shared-app/models';
     1import { VoteType } from 'src/app/shared-app/models';
    22import { QuestionAction, QuestionActionTypes } from './question.actions';
    33import { initialState, QuestionState } from './question.state';
     
    6161      };
    6262    }
    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     }
    9263    case QuestionActionTypes.EffectStartedWorking: {
    9364      return {
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.html

    rb9d7ae5 r80e2fe0  
    1 <div class="main-div">
     1<div>
    22  <div class="vote-icons" fxLayout="column" fxLayoutAlign="center center">
    33    <mat-icon class="vote-icon" (click)="voted(VoteType.Upvote)" [inline]="true">arrow_drop_up</mat-icon>
     
    77      >check</mat-icon
    88    >
    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     >
    169  </div>
    1710</div>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/vote/vote.component.scss

    rb9d7ae5 r80e2fe0  
    1414  cursor: pointer;
    1515}
    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

    rb9d7ae5 r80e2fe0  
    1010export class VoteComponent implements OnInit {
    1111  @Input() voteCount: number | undefined;
    12   @Input() canSetCorrectAnswer: boolean | null = false;
    1312  @Input() correct = false;
    1413  @Output() voteClicked = new EventEmitter<VoteType>();
    15   @Output() setCorrectAnswer = new EventEmitter<void>();
    1614
    1715  VoteType = VoteType;
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html

    rb9d7ae5 r80e2fe0  
    1515        <div fxLayout="row wrap" fxLayoutAlign="space-around none">
    1616          <app-vote
    17             [canSetCorrectAnswer]="canSetCorrectAnswer | async"
    1817            [voteCount]="answer.votesCount"
    1918            [correct]="answer.correctAnswer"
    2019            (voteClicked)="answerVoted($event, answer.uid, question.uid)"
    21             (setCorrectAnswer)="setCorrectAnswer(question.uid, answer.uid)"
    2220            fxFlex="6%"
    2321          ></app-vote>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts

    rb9d7ae5 r80e2fe0  
    1313})
    1414export class QuestionPreviewComponent implements OnInit {
    15   canSetCorrectAnswer = this.questionFacade.currentQuestionOwnedByCurrentUser();
    1615  question!: QuestionStateViewModel;
    1716  working = true;
     
    3534    this.questionFacade.voteAnswer(answerUid, questionUid, voteType);
    3635  }
    37 
    38   setCorrectAnswer(questionUid: string, answerUid: string): void {
    39     this.questionFacade.setCorrectAnswer(questionUid, answerUid);
    40   }
    4136}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/user.models.ts

    rb9d7ae5 r80e2fe0  
    99}
    1010
     11export class SelfUserResponse {
     12  uid!: string;
     13}
     14
    1115export enum ApplicationUserType {
    1216  Student = 'Student',
     
    1519  Guest = 'Guest'
    1620}
    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

    rb9d7ae5 r80e2fe0  
    1919
    2020  public getSelfUser(): Observable<SelfUserResponse> {
    21     return this.get<SelfUserResponse>('v1/self');
     21    return this.get<SelfUserResponse>('self');
    2222  }
    2323
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    rb9d7ae5 r80e2fe0  
    5353  "ask-question-stepper-ask": "Ask question",
    5454  "ask-question-ask-button-back": "Edit question",
    55   "sucess-vote": "Successfully voted answer",
    56   "success-correct-answer": "Successfully set correct answer",
    5755  "ask-question-ask-button": "Ask question",
    5856  "AnswerAlreadyUpvoted": "You have already upvoted this answer",
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/AnswersController.cs

    rb9d7ae5 r80e2fe0  
    3030            return Ok(answerUid);
    3131        }
    32 
    33         [HttpPut("{answerUid:Guid}/correct")]
    34         [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Student)]
    35         public async Task<IActionResult> MarkAnswerCorrect([FromRoute] Guid questionUid, [FromRoute] Guid answerUid)
    36         {
    37             await MediatorService.SendAsync(new MarkAnswerCorrectCommand(questionUid, answerUid));
    38             return Ok(answerUid);
    39         }
    4032    }
    4133}
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/QuestioningErrorCodes.cs

    rb9d7ae5 r80e2fe0  
    1313        public const string StudentHasBadReputation = "StudentHasBadReputation";
    1414        public const string AnswerTextLengthInvalid = "AnswerTextLengthInvalid";
    15         public const string QuestionNotFound = "QuestionNotFound";
    16         public const string AnswerInQuestionNotFound = "AnswerInQuestionNotFound";
    17         public const string StudentDoesNotOwnQuestion = "StudentDoesNotOwnQuestion";
    18         public const string AnswerIsAlreadyMarkedAsCorrect = "AnswerIsAlreadyMarkedAsCorrect";
     15        public const string QuestionNotFound = "QuestionNotFound";
    1916    }
    2017}
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Moderator.cs

    rb9d7ae5 r80e2fe0  
    1 namespace FinkiChattery.Persistence.Models
     1using System;
     2using System.Collections.Generic;
     3using System.Linq;
     4using System.Text;
     5using System.Threading.Tasks;
     6
     7namespace FinkiChattery.Persistence.Models
    28{
    39    public class Moderator : BaseEntity
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IAnswerRepo.cs

    rb9d7ae5 r80e2fe0  
    11using FinkiChattery.Persistence.Models;
    2 using System;
    3 using System.Threading.Tasks;
    42
    53namespace FinkiChattery.Persistence.Repositories
     
    75    public interface IAnswerRepo : IRepository<Answer>
    86    {
    9         Task<bool> AnswerInQuestionExists(Guid questionUid, Guid answerUid);
    107    }
    118}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs

    rb9d7ae5 r80e2fe0  
    1616     
    1717        Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular();
    18 
    19         Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId);
    20 
    21         Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid);
    2218    }
    2319}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IStudentRepo.cs

    rb9d7ae5 r80e2fe0  
    11using FinkiChattery.Persistence.Models;
    2 using FinkiChattery.Persistence.Repositories.Contracts;
    32using System.Threading.Tasks;
    43
     
    87    {
    98        public Task<Student> GetStudent(long applicationUserFk);
    10         public Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk);
    119    }
    1210}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/AnswerRepo.cs

    rb9d7ae5 r80e2fe0  
    11using FinkiChattery.Persistence.Context;
    22using FinkiChattery.Persistence.Models;
    3 using Microsoft.EntityFrameworkCore;
    4 using System;
    5 using System.Linq;
    6 using System.Threading.Tasks;
    73
    84namespace FinkiChattery.Persistence.Repositories
     
    139        {
    1410        }
    15 
    16         public async Task<bool> AnswerInQuestionExists(Guid questionUid, Guid answerUid)
    17         {
    18             return await DbSet
    19                 .AsNoTracking()
    20                 .Where(x => x.Question.Uid == questionUid && x.Uid == answerUid)
    21                 .AnyAsync();
    22         }
    2311    }
    2412}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs

    rb9d7ae5 r80e2fe0  
    144144                .ToListAsync();
    145145        }
    146 
    147         public async Task<bool> QuestionIsOwnedByStudent(Guid questionUid, long applicationUserId)
    148         {
    149             return await DbSet
    150                 .Where(x => x.Uid == questionUid && x.Student.ApplicationUserFk == applicationUserId)
    151                 .AnyAsync();
    152         }
    153 
    154         public async Task<Question> GetQuestionWithAnswersAndStudents(Guid questionUid)
    155         {
    156             return await DbSet
    157                 .Include(x => x.Answers).ThenInclude(x => x.Student)
    158                 .FirstOrDefaultAsync(x => x.Uid == questionUid);
    159         }
    160146    }
    161147}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/StudentRepo.cs

    rb9d7ae5 r80e2fe0  
    11using FinkiChattery.Persistence.Context;
    22using FinkiChattery.Persistence.Models;
    3 using FinkiChattery.Persistence.Repositories.Contracts;
    43using Microsoft.EntityFrameworkCore;
    5 using System.Linq;
    64using System.Threading.Tasks;
    75
     
    1816            return await DbSet.FirstOrDefaultAsync(x => x.ApplicationUserFk == applicationUserFk);
    1917        }
    20 
    21         public async Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk)
    22         {
    23             return await DbSet
    24                 .AsNoTracking()
    25                 .Include(x => x.Questions)
    26                 .Include(x => x.StudentTeams).ThenInclude(x => x.Team)
    27                 .Where(x => x.ApplicationUserFk == applicationUserFk)
    28                 .Select(x => new StudentSelfDto(x.Uid,
    29                                                 x.ApplicationUserFk,
    30                                                 x.IndexNumber,
    31                                                 x.Reputation,
    32                                                 x.ImageUrl,
    33                                                 x.Questions.Select(y => new StudentQuestionDto(y.Uid, y.Title)),
    34                                                 x.StudentTeams.Select(y => new StudentTeamDto(y.Team.Uid, y.Team.Name))))
    35                 .FirstOrDefaultAsync();
    36         }
    3718    }
    3819}
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs

    rb9d7ae5 r80e2fe0  
    1313        IVoteRepo Votes { get; }
    1414        IAnswerRepo Answers { get; }
    15         ITeacherRepo Teachers { get; }
    16         IModeratorRepo Moderators { get; }
    1715        Task<int> SaveAsync();
    1816    }
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs

    rb9d7ae5 r80e2fe0  
    1414        private VoteRepo _votes;
    1515        private AnswerRepo _answers;
    16         private ModeratorRepo _moderators;
    17         private TeacherRepo _teachers;
    1816
    1917        public UnitOfWork(ApplicationDbContext dbContext)
    2018        {
    2119            DbContext = dbContext;
    22         }
    23 
    24         public IModeratorRepo Moderators
    25         {
    26             get
    27             {
    28                 if (_moderators == null)
    29                 {
    30                     _moderators = new ModeratorRepo(DbContext);
    31                 }
    32 
    33                 return _moderators;
    34             }
    35         }
    36 
    37         public ITeacherRepo Teachers
    38         {
    39             get
    40             {
    41                 if (_teachers == null)
    42                 {
    43                     _teachers = new TeacherRepo(DbContext);
    44                 }
    45 
    46                 return _teachers;
    47             }
    4820        }
    4921
Note: See TracChangeset for help on using the changeset viewer.