Changeset 6738cc0


Ignore:
Timestamp:
01/19/22 19:14:27 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Parents:
f3c4950
Message:

Added notifications

Location:
src
Files:
11 added
25 edited

Legend:

Unmodified
Added
Removed
  • src/Clients/Angular/finki-chattery/package-lock.json

    rf3c4950 r6738cc0  
    78017801      }
    78027802    },
     7803    "ngx-pipes": {
     7804      "version": "2.7.6",
     7805      "resolved": "https://registry.npmjs.org/ngx-pipes/-/ngx-pipes-2.7.6.tgz",
     7806      "integrity": "sha512-FAeMd1mI8jCnnrhUuNLXs+PaVT1ujhA0QD3KRBDuzGFcbnP7NMXR2EJ5KYbV39LDNuRCluhpfwZudQu/NvrVuA==",
     7807      "requires": {
     7808        "tslib": "^2.0.0"
     7809      }
     7810    },
    78037811    "ngx-toastr": {
    78047812      "version": "13.2.1",
  • src/Clients/Angular/finki-chattery/package.json

    rf3c4950 r6738cc0  
    3333    "ng2-file-upload": "^1.4.0",
    3434    "ngx-material-timepicker": "^5.5.3",
     35    "ngx-pipes": "^2.7.6",
    3536    "ngx-toastr": "^13.2.1",
    3637    "oidc-client": "^1.11.5",
  • src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts

    rf3c4950 r6738cc0  
    107107    });
    108108  }
     109
     110  public studentCheckedNotifications(): Observable<void> {
     111    return this.baseApi.post('v1/students/checked-notifications');
     112  }
    109113}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/header/header.component.html

    rf3c4950 r6738cc0  
    1919      'header-student-questions' | translate
    2020    }}</app-button>
     21    <app-button
     22      matBadge="{{ auth.selfUser?.student?.notifications?.length }}"
     23      matBadgeHidden="{{ studentCheckedNotifications || auth.selfUser?.student?.notifications?.length === 0 }}"
     24      [matMenuTriggerFor]="notificationMenu"
     25      class="margin-y-xs"
     26      *ngIf="auth.isStudent()"
     27      (click)="studentCheckedNotificationsClick()"
     28      [buttonType]="ButtonType.Basic"
     29    >
     30      <mat-icon>notifications</mat-icon>
     31    </app-button>
    2132    <app-button class="margin-y-xs" *ngIf="auth.isLoggedIn()" (action)="logout()" [buttonType]="ButtonType.Basic">{{
    2233      'header-logout' | translate
     
    3445  </button>
    3546</mat-menu>
     47
     48<mat-menu #notificationMenu="matMenu">
     49  <div (click)="goToQuestion(q.questionUid)" mat-menu-item *ngFor="let q of auth.selfUser?.student?.notifications">
     50    {{ q?.text }} <span class="time-ago">{{ q?.createdOn | timeAgo }}</span>
     51  </div>
     52</mat-menu>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/header/header.component.scss

    rf3c4950 r6738cc0  
    1414  }
    1515}
     16
     17::ng-deep.mat-menu-panel {
     18  max-width: none !important;
     19}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/generic/header/header.component.ts

    rf3c4950 r6738cc0  
    1111export class HeaderComponent implements OnInit {
    1212  ButtonType = ButtonType;
     13
     14  public studentCheckedNotifications = false;
    1315
    1416  constructor(public auth: AuthService, private router: Router) {}
     
    3638    this.router.navigateByUrl(`questioning/${questionUid}`);
    3739  }
     40
     41  studentCheckedNotificationsClick(): void {
     42    if (!this.studentCheckedNotifications) {
     43      this.auth.studentCheckedNotifications().subscribe();
     44    }
     45    this.studentCheckedNotifications = true;
     46  }
    3847}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/user.models.ts

    rf3c4950 r6738cc0  
     1import * as moment from 'moment';
     2
    13export class ApplicationUser {
    24  constructor(
     
    3032  public questions!: StudentQuestionResponse[];
    3133  public teams!: StudentTeamResponse[];
     34  public notifications!: StudentNotificationResponse[];
     35}
     36
     37export class StudentNotificationResponse {
     38  public uid!: string;
     39  public createdOn!: moment.Moment;
     40  public questionUid!: string;
     41  public text!: string;
     42
     43  constructor(uid: string, createdOn: moment.Moment, questionUid: string, text: string) {
     44    this.uid = uid;
     45    this.createdOn = createdOn;
     46    this.questionUid = questionUid;
     47    this.text = text;
     48  }
    3249}
    3350
  • src/Clients/Angular/finki-chattery/src/app/shared-app/services/base-api.service.ts

    rf3c4950 r6738cc0  
    44
    55import { environment } from '@env/environment';
    6 import { SelfUserResponse } from '../models';
     6import { SelfUserResponse, StudentNotificationResponse } from '../models';
     7import { map } from 'rxjs/operators';
     8import * as moment from 'moment';
    79
    810@Injectable({
     
    1921
    2022  public getSelfUser(): Observable<SelfUserResponse> {
    21     return this.get<SelfUserResponse>('v1/self');
     23    return this.get<SelfUserResponse>('v1/self').pipe(
     24      map((x) => {
     25        if (x.student) {
     26          x.student.notifications = x.student.notifications.map(
     27            (y) => new StudentNotificationResponse(y.uid, moment(y.createdOn), y.questionUid, y.text)
     28          );
     29        }
     30
     31        return x;
     32      })
     33    );
    2234  }
    2335
  • src/Clients/Angular/finki-chattery/src/app/shared-app/shared-app.module.ts

    rf3c4950 r6738cc0  
    1414import { SERVICES } from './services/services';
    1515import { PIPES } from './pipes/pipes';
     16import { NgPipesModule } from 'ngx-pipes';
    1617
    1718@NgModule({
     
    2728    FileUploadModule,
    2829    NgxMaterialTimepickerModule,
    29     EditorModule
     30    EditorModule,
     31    NgPipesModule
    3032  ],
    3133  exports: [
     
    4143    PIPES,
    4244    SharedMaterialModule,
    43     EditorModule
     45    EditorModule,
     46    NgPipesModule
    4447  ]
    4548})
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/User/Mapper/SelfUserMapper.cs

    rf3c4950 r6738cc0  
    1717                                                      dto.StudentSelf.ImageUrl,
    1818                                                      dto.StudentSelf.Questions.Select(x => new StudentQuestionResponse(x.QuestionUid, x.Title)),
    19                                                       dto.StudentSelf.Teams.Select(x => new StudentTeamResponse(x.TeamUid, x.Name)));
     19                                                      dto.StudentSelf.Teams.Select(x => new StudentTeamResponse(x.TeamUid, x.Name)),
     20                                                      dto.StudentSelf.Notifications.Select(x => new StudentNotificationResponse(x.Uid, x.Text, x.CreatedOn, x.QuestionUid)));
    2021
    2122                return new SelfUserResponse(student);
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/MarkAnswerCorrect/AnswerMarkedAsCorrectEvent.cs

    rf3c4950 r6738cc0  
    66    public class AnswerMarkedAsCorrectEvent : IEvent
    77    {
    8         public AnswerMarkedAsCorrectEvent(Guid questionUid, Guid answerUid)
     8        public AnswerMarkedAsCorrectEvent(Guid questionUid, Guid answerUid, long studentFk)
    99        {
    1010            QuestionUid = questionUid;
    1111            AnswerUid = answerUid;
     12            StudentFk = studentFk;
    1213        }
    1314
    1415        public Guid QuestionUid { get; }
    1516        public Guid AnswerUid { get; }
     17        public long StudentFk { get; }
    1618    }
    1719}
  • src/FinkiChattery/FinkiChattery.Commands/Questioning/MarkAnswerCorrect/MarkAnswerCorrectCommand.cs

    rf3c4950 r6738cc0  
    3838            await UnitOfWork.SaveAsync();
    3939
    40             EventService.Enqueue(new AnswerMarkedAsCorrectEvent(request.QuestionUid, request.AnswerUid));
     40            EventService.Enqueue(new AnswerMarkedAsCorrectEvent(request.QuestionUid, request.AnswerUid, answer.StudentFk));
    4141
    4242            return answer.Uid;
  • src/FinkiChattery/FinkiChattery.Contracts/User/GetSelfUser/SelfUserResponse.cs

    rf3c4950 r6738cc0  
    2323    public class StudentSelfResponse
    2424    {
    25         public StudentSelfResponse(Guid uid, long applicationUserId, string index, long reputation, string imageUrl, IEnumerable<StudentQuestionResponse> questions, IEnumerable<StudentTeamResponse> teams)
     25        public StudentSelfResponse(Guid uid, long applicationUserId, string index, long reputation, string imageUrl, IEnumerable<StudentQuestionResponse> questions, IEnumerable<StudentTeamResponse> teams, IEnumerable<StudentNotificationResponse> notifications)
    2626        {
    2727            Uid = uid;
     
    3232            Questions = questions;
    3333            Teams = teams;
     34            Notifications = notifications;
    3435        }
    3536
     
    4142        public IEnumerable<StudentQuestionResponse> Questions { get; }
    4243        public IEnumerable<StudentTeamResponse> Teams { get; }
     44        public IEnumerable<StudentNotificationResponse> Notifications { get; }
     45    }
     46
     47    public class StudentNotificationResponse
     48    {
     49        public StudentNotificationResponse(Guid uid, string text, DateTime createdOn, Guid questionUid)
     50        {
     51            Uid = uid;
     52            Text = text;
     53            CreatedOn = createdOn;
     54            QuestionUid = questionUid;
     55        }
     56
     57        public Guid Uid { get; }
     58        public string Text { get; }
     59        public DateTime CreatedOn { get; }
     60        public Guid QuestionUid { get; }
    4361    }
    4462
  • src/FinkiChattery/FinkiChattery.Database/FinkiChattery.Database.sqlproj

    rf3c4950 r6738cc0  
    7777    <Folder Include="dbo\Tables\Vote" />
    7878    <Folder Include="dbo\Tables\Moderator" />
     79    <Folder Include="dbo\Tables\StudentNotification" />
    7980  </ItemGroup>
    8081  <ItemGroup>
     
    103104    <Build Include="dbo\Tables\Moderator\Moderator.sql" />
    104105    <None Include="dbo\Tables\Moderator\Moderator.Debug.Seed.sql" />
     106    <Build Include="dbo\Tables\StudentNotification\StudentNotification.sql" />
    105107  </ItemGroup>
    106108  <ItemGroup>
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Student/Student.sql

    rf3c4950 r6738cc0  
    77    [ReportReputation]  BIGINT           DEFAULT (CONVERT([bigint],(0))) NOT NULL,
    88    [ImageUrl]          NVARCHAR (1000)     NOT NULL,
     9    [LastCheckedNotifications] SMALLDATETIME NOT NULL DEFAULT GETUTCDATE(),
    910    CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ([Id] ASC),
    1011    CONSTRAINT [FK_Student_AspNetUsers_ApplicationUserFk] FOREIGN KEY ([ApplicationUserFk]) REFERENCES [dbo].[AspNetUsers] ([Id])
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/StudentConfig.cs

    rf3c4950 r6738cc0  
    1 using Microsoft.EntityFrameworkCore;
     1using FinkiChattery.Persistence.Models;
     2using Microsoft.EntityFrameworkCore;
    23using Microsoft.EntityFrameworkCore.Metadata.Builders;
    34using System;
    4 using System.Collections.Generic;
    5 using System.Linq;
    6 using System.Text;
    7 using System.Threading.Tasks;
    8 using FinkiChattery.Persistence.Models;
    95
    106namespace FinkiChattery.Persistence.Configurations
     
    2723            builder.Property(x => x.ReportReputation).HasColumnName(@"ReportReputation").HasColumnType("bigint").IsRequired().HasDefaultValue(0);
    2824            builder.Property(x => x.ImageUrl).HasColumnName(@"ImageUrl").HasColumnType("nvarchar").IsRequired().HasMaxLength(1000);
     25            builder.Property(x => x.LastCheckedNotifications).HasColumnName(@"LastCheckedNotifications").HasColumnType("smalldatetime").IsRequired().HasConversion(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
    2926
    3027            builder.HasOne(x => x.ApplicationUser).WithOne().HasForeignKey<Student>(x => x.ApplicationUserFk).OnDelete(DeleteBehavior.Restrict);
  • src/FinkiChattery/FinkiChattery.Persistence/Context/ApplicationDbContext.cs

    rf3c4950 r6738cc0  
    2424        public DbSet<Team> Teams { get; set; }
    2525        public DbSet<Vote> Votes { get; set; }
     26        public DbSet<StudentNotification> StudentNotifications { get; set; }
    2627
    2728        protected override void OnModelCreating(ModelBuilder builder)
     
    4344            builder.ApplyConfiguration(new TeamConfig(schema));
    4445            builder.ApplyConfiguration(new VoteConfig(schema));
     46            builder.ApplyConfiguration(new StudentNotificationConfig(schema));
    4547        }
    4648    }
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Student.cs

    rf3c4950 r6738cc0  
    1 using System.Collections.Generic;
     1using System;
     2using System.Collections.Generic;
    23
    34namespace FinkiChattery.Persistence.Models
     
    56    public class Student : BaseEntity
    67    {
     8        public DateTime LastCheckedNotifications { get; set; }
     9
    710        public long ApplicationUserFk { get; set; }
    811
     
    2225
    2326        public virtual ICollection<StudentTeam> StudentTeams { get; set; }
     27
     28        public virtual ICollection<StudentNotification> StudentNotifications { get; set; }
    2429    }
    2530}
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Team.cs

    rf3c4950 r6738cc0  
    1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Threading.Tasks;
     1using System.Collections.Generic;
    62
    73namespace FinkiChattery.Persistence.Models
     
    139        public string Description { get; set; }
    1410
    15         public virtual ICollection<Question> Questions{ get; set; }
     11        public virtual ICollection<Question> Questions { get; set; }
    1612
    1713        public virtual ICollection<StudentTeam> TeamStudents { get; set; }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/IRepository.cs

    rf3c4950 r6738cc0  
    1111        Task<T> GetByUidAsync(Guid uid);
    1212
    13         Task<T> GetByIdAsync(int id);
     13        Task<T> GetByIdAsync(long id);
    1414
    1515        void Delete(T entity);
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/Repository.cs

    rf3c4950 r6738cc0  
    2424        }
    2525
    26         public async Task<T> GetByIdAsync(int id)
     26        public async Task<T> GetByIdAsync(long id)
    2727        {
    2828            return await All().FirstOrDefaultAsync(f => f.Id == id);
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/Student/StudentSelfDto.cs

    rf3c4950 r6738cc0  
    11using System;
    22using System.Collections.Generic;
     3using System.Linq;
    34
    45namespace FinkiChattery.Persistence.Repositories.Contracts
     
    67    public class StudentSelfDto
    78    {
    8         public StudentSelfDto(Guid uid, long applicationUserId, string index, long reputation, string imageUrl, IEnumerable<StudentQuestionDto> questions, IEnumerable<StudentTeamDto> teams)
     9        public StudentSelfDto(Guid uid, long applicationUserId, string index, long reputation, string imageUrl, DateTime lastCheckedNotifications, IEnumerable<StudentQuestionDto> questions, IEnumerable<StudentTeamDto> teams, IEnumerable<StudentSelfNotificationDto> notifications = null)
    910        {
    1011            Uid = uid;
     
    1314            Reputation = reputation;
    1415            ImageUrl = imageUrl;
     16            LastCheckedNotifications = lastCheckedNotifications;
    1517            Questions = questions;
    1618            Teams = teams;
     19            Notifications = notifications;
    1720        }
    1821
     
    2225        public long Reputation { get; }
    2326        public string ImageUrl { get; }
     27        public DateTime LastCheckedNotifications { get; }
    2428        public IEnumerable<StudentQuestionDto> Questions { get; }
    2529        public IEnumerable<StudentTeamDto> Teams { get; }
     30        public IEnumerable<StudentSelfNotificationDto> Notifications { get; set; }
    2631    }
    2732}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/StudentRepo.cs

    rf3c4950 r6738cc0  
    2121        public async Task<StudentSelfDto> GetStudentSelfDto(long applicationUserFk)
    2222        {
    23             return await DbSet
     23            var user = await DbSet
    2424                .AsNoTracking()
    2525                .Include(x => x.Questions)
     
    3131                                                x.Reputation,
    3232                                                x.ImageUrl,
     33                                                x.LastCheckedNotifications,
    3334                                                x.Questions.Select(y => new StudentQuestionDto(y.Uid, y.Title)),
    34                                                 x.StudentTeams.Select(y => new StudentTeamDto(y.Team.Uid, y.Team.Name))))
     35                                                x.StudentTeams.Select(y => new StudentTeamDto(y.Team.Uid, y.Team.Name)),
     36                                                null))
    3537                .FirstOrDefaultAsync();
     38
     39            user.Notifications = await DbContext.StudentNotifications
     40                .Where(x => x.CreatedOn > user.LastCheckedNotifications)
     41                .Select(x => new StudentSelfNotificationDto(x.Uid, x.Text, x.CreatedOn, x.QuestionUid))
     42                .ToListAsync();
     43
     44            return user;
    3645        }
    3746    }
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Contracts/IUnitOfWork.cs

    rf3c4950 r6738cc0  
    1616        IModeratorRepo Moderators { get; }
    1717        IAnswerResponseRepo AnswerResponses { get; }
     18        IStudentNotificationRepo StudentNotifications { get; }
    1819        Task<int> SaveAsync();
    1920    }
  • src/FinkiChattery/FinkiChattery.Persistence/UnitOfWork/Implementations/UnitOfWork.cs

    rf3c4950 r6738cc0  
    1717        private TeacherRepo _teachers;
    1818        private AnswerResponseRepo _answerResponses;
     19        private StudentNotificationRepo _studentNotifications;
    1920
    2021        public UnitOfWork(ApplicationDbContext dbContext)
     
    3334
    3435                return _answerResponses;
     36            }
     37        }
     38
     39        public IStudentNotificationRepo StudentNotifications
     40        {
     41            get
     42            {
     43                if (_studentNotifications == null)
     44                {
     45                    _studentNotifications = new StudentNotificationRepo(DbContext);
     46                }
     47
     48                return _studentNotifications;
    3549            }
    3650        }
Note: See TracChangeset for help on using the changeset viewer.