Changeset dd264cb


Ignore:
Timestamp:
11/04/21 22:10:07 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
cbdbb49
Parents:
91bfcf4 (diff), 68d02ca (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.
Message:

Merged feature/respond-to-answer into dev

Location:
src
Files:
11 added
16 edited

Legend:

Unmodified
Added
Removed
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts

    r91bfcf4 rdd264cb  
    1919  GetQuestionState,
    2020  GetSearchQuestions,
     21  RespondToAnswer,
    2122  SetCorrectAnswer,
    2223  VoteAnswer
     
    8586  }
    8687
     88  public respondToAnswer(answerUid: string, questionUid: string, text: string): void {
     89    this.dispatchEffect(new RespondToAnswer(questionUid, answerUid, text));
     90  }
     91
    8792  public fetchQuestion(questionUid: string): void {
    8893    this.dispatchEffect(new GetQuestionState(questionUid));
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question-state-request.models.ts

    r91bfcf4 rdd264cb  
    44  constructor(public voteType: VoteType) {}
    55}
     6
     7export class RespondToAnswerRequest {
     8  constructor(public text: string) {}
     9}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts

    r91bfcf4 rdd264cb  
    33
    44import {
     5  AnswerResponseQuestionStateViewModel,
    56  PreviewQuestionViewModel,
    67  QuestionStateViewModel,
     
    1314  GetQuestionState = '[Question] Get state',
    1415  GetQuestionStateSuccess = '[Question] Get state success',
     16  RespondToAnswer = '[Question] RespondToAnswer',
     17  RespondToAnswerSuccess = '[Question] RespondToAnswer success',
    1518  SetCorrectAnswer = '[Question] Set Correct Answer',
    1619  SetCorrectAnswerSuccess = '[Question] Set Correct Answer success',
     
    100103}
    101104
     105export class RespondToAnswer implements Action {
     106  readonly type = QuestionActionTypes.RespondToAnswer;
     107
     108  constructor(public questionUid: string, public answerUid: string, public text: string) {}
     109}
     110
     111export class RespondToAnswerSuccess implements Action {
     112  readonly type = QuestionActionTypes.RespondToAnswerSuccess;
     113
     114  constructor(public payload: AnswerResponseQuestionStateViewModel, public answerUid: string) {}
     115}
     116
    102117export class EffectStartedWorking implements Action {
    103118  readonly type = QuestionActionTypes.EffectStartedWorking;
     
    125140  | VoteAnswerSuccess
    126141  | SetCorrectAnswerSuccess
     142  | RespondToAnswerSuccess
    127143  | EffectStartedWorking
    128144  | EffectFinishedWorking
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts

    r91bfcf4 rdd264cb  
    88import { NotificationService } from '../../services/notification.service';
    99import { QuestionFacadeService } from '../question-facade.service';
    10 import { VoteAnswerRequest } from './question-state-request.models';
    11 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models';
     10import { RespondToAnswerRequest, VoteAnswerRequest } from './question-state-request.models';
     11import {
     12  AnswerResponseQuestionStateResponse,
     13  PreviewQuestionResponse,
     14  QuestionStateResponse,
     15  VoteAnswerResponse
     16} from './question-state-response.models';
    1217import {
    1318  EffectFinishedWorking,
     
    2227  GetSearchQuestionsSuccess,
    2328  QuestionActionTypes,
     29  RespondToAnswer,
     30  RespondToAnswerSuccess,
    2431  SetCorrectAnswer,
    2532  SetCorrectAnswerSuccess,
     
    137144    );
    138145  });
     146
     147  respondToAnswer$ = createEffect(() => {
     148    return this.actions$.pipe(
     149      ofType<RespondToAnswer>(QuestionActionTypes.RespondToAnswer),
     150      mergeMap((action) => {
     151        return this.api
     152          .post<AnswerResponseQuestionStateResponse>(
     153            `v1/questions/${action.questionUid}/answers/${action.answerUid}/answerresponses`,
     154            new RespondToAnswerRequest(action.text)
     155          )
     156          .pipe(
     157            tap((state) => this.notification.successNotification('success-answer-response')),
     158            switchMap((state) => [
     159              new RespondToAnswerSuccess(QuestionMapper.ToAnswerResponseQuestionStateViewModel(state), action.answerUid),
     160              new EffectFinishedWorking()
     161            ]),
     162            catchError((err) => [new EffectFinishedWorkingError(err)])
     163          );
     164      })
     165    );
     166  });
    139167}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts

    r91bfcf4 rdd264cb  
    1414} from 'src/app/shared-app/models';
    1515import { TranslateFromJsonService } from 'src/app/shared-app/services';
    16 import { PreviewQuestionResponse, QuestionStateResponse, VoteAnswerResponse } from './question-state-response.models';
     16import {
     17  AnswerResponseQuestionStateResponse,
     18  PreviewQuestionResponse,
     19  QuestionStateResponse,
     20  VoteAnswerResponse
     21} from './question-state-response.models';
    1722
    1823export class QuestionMapper {
     
    118123    return new VoteAnswerViewModel(response.answerUid, response.voteUid, response.voteType);
    119124  }
     125
     126  public static ToAnswerResponseQuestionStateViewModel(
     127    response: AnswerResponseQuestionStateResponse
     128  ): AnswerResponseQuestionStateViewModel {
     129    const answerResponseStudent = new AnswerResponseStudentQuestionStateViewModel(
     130      response.studentResponse.uid,
     131      response.studentResponse.index,
     132      response.studentResponse.imageUrl,
     133      response.studentResponse.reputation
     134    );
     135
     136    return new AnswerResponseQuestionStateViewModel(response.uid, response.text, moment(response.createdOn), answerResponseStudent);
     137  }
    120138}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts

    r91bfcf4 rdd264cb  
    9090      };
    9191    }
     92    case QuestionActionTypes.RespondToAnswerSuccess: {
     93      if (state.question) {
     94        return {
     95          ...state,
     96          question: {
     97            ...state.question,
     98            answers: state.question.answers.map((x) => {
     99              if (x.uid === action.answerUid) {
     100                return {
     101                  ...x,
     102                  answerResponses: [...x.answerResponses, action.payload]
     103                };
     104              }
     105
     106              return x;
     107            })
     108          }
     109        };
     110      }
     111
     112      return {
     113        ...state
     114      };
     115    }
    92116    case QuestionActionTypes.EffectStartedWorking: {
    93117      return {
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/components.ts

    r91bfcf4 rdd264cb  
    99import { PreviewQuestionFullComponent } from './question/preview-question-full/preview-question-full.component';
    1010import { QuestionPreviewComponent } from './question/question-preview/question-preview.component';
     11import { RespondToAnswerDialogComponent } from './question/respond-to-answer-dialog/respond-to-answer-dialog.component';
    1112import { SearchQuestionComponent } from './question/search-question/search-question.component';
    1213import { StudentCardComponent } from './question/student-card/student-card.component';
     
    2425  AskQuestionSharedComponent,
    2526  PreviewQuestionFullComponent,
    26   TextEditorComponent
     27  TextEditorComponent,
     28  RespondToAnswerDialogComponent
    2729];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html

    r91bfcf4 rdd264cb  
    4545            <div *ngFor="let answerResponse of answer.answerResponses">
    4646              {{ answerResponse.text }}
    47               <mat-chip class="cursor" selected>{{ answerResponse.student.index }}</mat-chip> -
     47              <mat-chip class="cursor">{{ answerResponse.student.index }}</mat-chip> -
    4848              {{ answerResponse.createdOn | momentDate: 'LL' }}
    4949              <hr />
     
    5252        </div>
    5353      </mat-card-content>
    54       <mat-card-actions> </mat-card-actions>
     54      <mat-card-actions>
     55        <app-button (action)="openRespondToAnswerDialog(answer.uid)" [buttonType]="ButtonType.Basic">{{
     56          'question-preview-respond-to-answer-button' | translate
     57        }}</app-button>
     58      </mat-card-actions>
    5559    </mat-card>
    5660  </ng-container>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts

    r91bfcf4 rdd264cb  
    55
    66import { QuestionStateViewModel, VoteType } from 'src/app/shared-app/models';
     7import { SharedDialogService } from 'src/app/shared-app/services/shared-dialog.service';
    78import { ButtonType } from '../../generic/button/button.models';
    89
     
    1718  working = true;
    1819  ButtonType = ButtonType;
    19   constructor(private questionFacade: QuestionFacadeService, private notification: NotificationService) {}
     20  constructor(
     21    private questionFacade: QuestionFacadeService,
     22    private notification: NotificationService,
     23    private dialog: SharedDialogService
     24  ) {}
    2025
    2126  ngOnInit(): void {
     
    3944    this.questionFacade.setCorrectAnswer(questionUid, answerUid);
    4045  }
     46
     47  openRespondToAnswerDialog(answerUid: string): void {
     48    this.dialog.respondToAnswer(this.question.uid, answerUid);
     49  }
    4150}
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r91bfcf4 rdd264cb  
    5555  "sucess-vote": "Successfully voted answer",
    5656  "success-correct-answer": "Successfully set correct answer",
     57  "success-answer-response": "Successfully responded to answer",
    5758  "ask-question-ask-button": "Ask question",
     59  "question-preview-respond-to-answer-button": "Respond to answer",
     60  "respond-to-answer-title": "Respond to answer",
     61  "respond-to-answer-title-text-placeholder": "Response text",
     62  "close-button": "Close",
     63  "submit-button": "Submit",
    5864  "AnswerAlreadyUpvoted": "You have already upvoted this answer",
    5965  "AnswerAlreadyDownvoted": "You have already downvoted this answer",
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/QuestionMapper.cs

    r91bfcf4 rdd264cb  
    4848                            var answerResponseStudent = new AnswerResponseStudentQuestionStateResponse(y.StudentDto.Id, y.StudentDto.Uid, y.StudentDto.Index, y.StudentDto.ImageUrl, y.StudentDto.Reputation);
    4949
    50                             return new AnswerResponseQuestionStateResponse(y.Id, y.Uid, y.Text, y.CreatedOn, answerResponseStudent);
     50                            return ToAnswerResponseQuestionStateResponse(y);
    5151                        });
    5252                    }
     
    7676            return new QuestionStateResponse(questionState.Id, questionState.Uid, questionState.Title, questionState.Text, questionState.CreatedOn, questionState.Views, questionState.LastActiveOn, student, answers, questionCategories, team);
    7777        }
     78
     79        public static AnswerResponseQuestionStateResponse ToAnswerResponseQuestionStateResponse(this AnswerResponseQuestionStateDto dto)
     80        {
     81            var answerResponseStudent = new AnswerResponseStudentQuestionStateResponse(dto.StudentDto.Id, dto.StudentDto.Uid, dto.StudentDto.Index, dto.StudentDto.ImageUrl, dto.StudentDto.Reputation);
     82
     83            return new AnswerResponseQuestionStateResponse(dto.Id, dto.Uid, dto.Text, dto.CreatedOn, answerResponseStudent);
     84        }
    7885    }
    7986}
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/QuestioningErrorCodes.cs

    r91bfcf4 rdd264cb  
    77        public const string QuestionTitleLengthInvalid = "QuestionTitleLengthInvalid";
    88        public const string QuestionTextLengthInvalid = "QuestionTextLengthInvalid";
     9        public const string AnswerResponseTextLengthInvalid = "AnswerResponseTextLengthInvalid";
    910        public const string CategoriesDontExist = "CategoriesDontExist";
    1011        public const string TeamDontExist = "TeamDontExist";
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/Validators/QuestioningFluentValidationRules.cs

    r91bfcf4 rdd264cb  
    1616        }
    1717
     18        public static IRuleBuilderOptions<T, string> AnswerResponseTextValidate<T>(this IRuleBuilder<T, string> ruleBuilder)
     19        {
     20            return ruleBuilder.NotNull().WithMessage(QuestioningErrorCodes.CantBeNull).MaximumLength(4000).WithMessage(QuestioningErrorCodes.AnswerResponseTextLengthInvalid);
     21        }
     22
    1823        public static IRuleBuilderOptions<T, string> AnswerTextValidate<T>(this IRuleBuilder<T, string> ruleBuilder)
    1924        {
  • src/FinkiChattery/FinkiChattery.Persistence/Models/AnswerResponse.cs

    r91bfcf4 rdd264cb  
    11using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Threading.Tasks;
    62
    73namespace FinkiChattery.Persistence.Models
     
    95    public class AnswerResponse : BaseEntity
    106    {
     7        public AnswerResponse() : base()
     8        {
     9            CreatedOn = DateTime.UtcNow;
     10        }
     11
    1112        public string Text { get; set; }
    1213
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs

    r91bfcf4 rdd264cb  
    1515        ITeacherRepo Teachers { get; }
    1616        IModeratorRepo Moderators { get; }
     17        IAnswerResponseRepo AnswerResponses { get; }
    1718        Task<int> SaveAsync();
    1819    }
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs

    r91bfcf4 rdd264cb  
    1616        private ModeratorRepo _moderators;
    1717        private TeacherRepo _teachers;
     18        private AnswerResponseRepo _answerResponses;
    1819
    1920        public UnitOfWork(ApplicationDbContext dbContext)
    2021        {
    2122            DbContext = dbContext;
     23        }
     24
     25        public IAnswerResponseRepo AnswerResponses
     26        {
     27            get
     28            {
     29                if (_answerResponses == null)
     30                {
     31                    _answerResponses = new AnswerResponseRepo(DbContext);
     32                }
     33
     34                return _answerResponses;
     35            }
    2236        }
    2337
Note: See TracChangeset for help on using the changeset viewer.