Ignore:
Timestamp:
10/27/21 19:40:58 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
d2b1fa6
Parents:
81e1ed6 (diff), 7899209 (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 dev

Location:
src/Clients/Angular/finki-chattery
Files:
63 added
1 deleted
23 edited

Legend:

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

    r81e1ed6 r79ae621  
    9898            "tsConfig": "tsconfig.spec.json",
    9999            "karmaConfig": "karma.conf.js",
    100             "assets": ["src/favicon.ico", "src/assets"],
    101             "styles": ["./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.scss"],
     100            "assets": [
     101              "src/favicon.ico",
     102              "src/assets"
     103            ],
     104            "styles": [
     105              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
     106              "src/styles.scss"
     107            ],
    102108            "scripts": []
    103109          }
     
    106112          "builder": "@angular-devkit/build-angular:tslint",
    107113          "options": {
    108             "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
    109             "exclude": ["**/node_modules/**"]
     114            "tsConfig": [
     115              "tsconfig.app.json",
     116              "tsconfig.spec.json",
     117              "e2e/tsconfig.json"
     118            ],
     119            "exclude": [
     120              "**/node_modules/**"
     121            ]
    110122          }
    111123        },
     
    125137    }
    126138  },
    127   "defaultProject": "finki-chattery"
     139  "defaultProject": "finki-chattery",
     140  "cli": {
     141    "analytics": false
     142  }
    128143}
  • src/Clients/Angular/finki-chattery/src/app/app-routing.module.ts

    r81e1ed6 r79ae621  
    11import { NgModule } from '@angular/core';
    22import { Routes, RouterModule } from '@angular/router';
     3import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
     4import { AuthorizedGuard } from './core/guards/authorized.guard';
    35
    46const routes: Routes = [
    57  {
    6     path: '**',
    7     redirectTo: 'public/home'
     8    path: 'auth-callback',
     9    component: AuthCallbackComponent
    810  },
    911  {
    10     path: 'questions',
     12    path: 'questioning',
     13    canActivate: [AuthorizedGuard],
    1114    loadChildren: () => import('./modules/questioning/questioning.module').then((x) => x.QuestioningModule)
     15  },
     16  {
     17    path: '**',
     18    redirectTo: 'questioning/preview'
    1219  }
    1320];
  • src/Clients/Angular/finki-chattery/src/app/app.component.html

    r81e1ed6 r79ae621  
    11<main>
    22  <mat-progress-bar class="global-loader" [class.hidden]="!(loader.isLoading | async)" mode="indeterminate"></mat-progress-bar>
     3  <app-header></app-header>
    34  <router-outlet></router-outlet>
    45</main>
  • src/Clients/Angular/finki-chattery/src/app/app.module.ts

    r81e1ed6 r79ae621  
    1010import { CoreModule } from './core/core.module';
    1111import { translateConfiguration, TranslateFromJsonService } from './shared-app/services';
    12 import { SharedMaterialModule } from './shared-material/shared-material.module';
     12import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
    1313
    1414@NgModule({
    15   declarations: [AppComponent],
     15  declarations: [AppComponent, AuthCallbackComponent],
    1616  imports: [
    1717    BrowserModule,
    1818    AppRoutingModule,
    1919    CoreModule,
    20     SharedMaterialModule,
    2120    BrowserAnimationsModule,
    2221    ToastrModule.forRoot(),
  • src/Clients/Angular/finki-chattery/src/app/core/core.module.ts

    r81e1ed6 r79ae621  
    1111import { GUARDS } from './guards/guards';
    1212import { LoaderInterceptor } from './interceptors/loader.interceptor';
    13 import { SERVICES } from './services/services';
    1413import { reducers } from './state';
    1514import { TokenInterceptor } from './interceptors/token.interceptor';
     15import { EffectsModule } from '@ngrx/effects';
     16import { QuestionEffects } from './state/question-state/question.effects';
     17import { CategoriesEffects } from './state/category-state/category.effects';
    1618
    1719@NgModule({
    1820  declarations: [COMPONENTS],
    1921  providers: [
    20     SERVICES,
    2122    GUARDS,
    2223    { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true },
     
    3334      maxAge: 25,
    3435      logOnly: !environment.production
    35     })
     36    }),
     37    EffectsModule.forRoot([QuestionEffects, CategoriesEffects])
    3638  ],
    37   exports: [HttpClientModule, COMPONENTS]
     39  exports: [HttpClientModule, SharedAppModule, COMPONENTS]
    3840})
    3941export class CoreModule {}
  • src/Clients/Angular/finki-chattery/src/app/core/interceptors/token.interceptor.ts

    r81e1ed6 r79ae621  
    22import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
    33import { Observable } from 'rxjs';
    4 import { switchMap } from 'rxjs/operators';
    54
    65import { AuthService } from '../services';
     
    1615    }
    1716
    18     return this.auth.currentUserToken().pipe(
    19       switchMap((token) => {
    20         const requestToForward = request.clone({
    21           setHeaders: { Authorization: `Bearer ${token}` }
    22         });
    23         return next.handle(requestToForward);
    24       })
    25     );
     17    const requestToForward = request.clone({
     18      setHeaders: { Authorization: `Bearer ${this.auth.currentUserToken()}` }
     19    });
     20
     21    return next.handle(requestToForward);
    2622  }
    2723}
  • src/Clients/Angular/finki-chattery/src/app/core/services/auth.service.ts

    r81e1ed6 r79ae621  
    11import { Injectable } from '@angular/core';
    2 import { UserManager } from 'oidc-client';
    3 import { Observable, from, of } from 'rxjs';
    4 import { map, switchMap } from 'rxjs/operators';
     2import { User, UserManager } from 'oidc-client';
     3import { Observable, of } from 'rxjs';
    54
    65import { environment } from '@env/environment';
     
    1514    authority: environment.identityRoute,
    1615    client_id: environment.identityClientId,
    17     redirect_uri: `${window.location.origin}`,
     16    redirect_uri: `${window.location.origin}/auth-callback`,
    1817    response_type: 'id_token token',
    1918    scope: 'openid app.api.finki-chattery profile',
    2019    post_logout_redirect_uri: window.location.origin
    2120  });
     21
     22  public user: ApplicationUser | null = null;
     23  public oidcUser: User | null = null;
    2224
    2325  constructor(private baseApi: BaseApiService) {}
     
    3133  }
    3234
    33   public isLoggedIn(): Observable<boolean> {
    34     return from(this.userManager.getUser()).pipe(
    35       map((user) => {
    36         if (user) {
    37           return true;
    38         }
    39 
    40         return false;
    41       })
    42     );
     35  public isLoggedIn(): boolean {
     36    if (this.oidcUser) {
     37      return !this.oidcUser.expired;
     38    }
     39    return false;
    4340  }
    4441
    45   public currentUser(): Observable<ApplicationUser | null> {
    46     return from(this.userManager.getUser()).pipe(
    47       map((user) => {
    48         if (!user) {
    49           return null;
    50         }
    51 
    52         return new ApplicationUser(
    53           user.profile.id,
    54           user.profile.userType,
    55           user.profile.emailAddress,
    56           user.profile.username,
    57           user.profile.isVerified
    58         );
    59       })
    60     );
     42  public currentUser(): ApplicationUser | null {
     43    return this.user;
    6144  }
    6245
    63   public currentUserToken(): Observable<string> {
    64     return from(this.userManager.getUser()).pipe(
    65       map((user) => {
    66         if (user?.access_token) {
    67           return user.access_token;
    68         }
     46  public currentUserToken(): string {
     47    if (this.oidcUser) {
     48      return this.oidcUser.access_token;
     49    }
    6950
    70         return '';
    71       })
    72     );
     51    return '';
    7352  }
    7453
    7554  public selfUserDto(): Observable<SelfUserResponse | null> {
    76     return this.isLoggedIn().pipe(
    77       switchMap((loggedIn) => {
    78         if (loggedIn) {
    79           return this.baseApi.getSelfUser();
    80         }
    81         return of(null);
    82       })
    83     );
     55    if (this.isLoggedIn()) {
     56      return this.baseApi.getSelfUser();
     57    }
     58    return of(null);
    8459  }
    8560
    86   public signupCallback(): Observable<boolean> {
    87     return from(this.userManager.signinRedirectCallback()).pipe(map((user) => user !== null));
     61  public async completeAuthentication(): Promise<void> {
     62    return await this.userManager.signinRedirectCallback().then((user: User) => {
     63      this.oidcUser = user;
     64      this.user = new ApplicationUser(
     65        user.profile.id,
     66        user.profile.userType,
     67        user.profile.emailAddress,
     68        user.profile.username,
     69        user.profile.isVerified
     70      );
     71    });
    8872  }
    8973}
  • src/Clients/Angular/finki-chattery/src/app/core/services/notification.service.ts

    r81e1ed6 r79ae621  
    2222
    2323  public successNotification(title: string, description?: string): void {
    24     this.toastr.success(this.translate.instant(description), this.translate.instant(title));
     24    if (description) {
     25      this.toastr.success(this.translate.instant(description), this.translate.instant(title));
     26    }
     27    this.toastr.success(this.translate.instant(title));
    2528  }
    2629}
  • src/Clients/Angular/finki-chattery/src/app/core/services/redirect.service.ts

    r81e1ed6 r79ae621  
    11import { Injectable } from '@angular/core';
    22import { Router } from '@angular/router';
    3 import { switchMap } from 'rxjs/operators';
    43
    54import { ApplicationUserType } from 'src/app/shared-app/models';
     
    1312
    1413  public redirectLoggedInUser(): void {
    15     this.auth
    16       .signupCallback()
    17       .pipe(switchMap(() => this.auth.currentUser()))
    18       .subscribe((currentUser) => {
    19         if (currentUser) {
    20           switch (currentUser.userType) {
    21             case ApplicationUserType.Student:
    22               break;
    23             case ApplicationUserType.Teacher:
    24               break;
    25           }
    26         }
    27       });
     14    const currentUser = this.auth.user;
     15
     16    if (currentUser) {
     17      switch (currentUser.userType) {
     18        case ApplicationUserType.Student:
     19          this.router.navigateByUrl(`questioning/preview`);
     20          break;
     21        case ApplicationUserType.Teacher:
     22          break;
     23      }
     24    }
    2825  }
    2926}
  • src/Clients/Angular/finki-chattery/src/app/core/services/services.ts

    r81e1ed6 r79ae621  
    1 export const SERVICES: any[] = [];
  • src/Clients/Angular/finki-chattery/src/app/core/state/index.ts

    r81e1ed6 r79ae621  
    11import { ActionReducerMap } from '@ngrx/store';
     2import { QuestionState } from './question-state/question.state';
     3import { reducer as questionReducer } from './question-state/question.reducers';
     4import { CategoryState } from './category-state/category.state';
     5import { reducer as categoryReducer } from './category-state/category.reducers';
    26
    3 export interface State {}
     7export interface State {
     8  question: QuestionState;
     9  category: CategoryState;
     10}
    411
    5 export const reducers: ActionReducerMap<State, any> = {};
     12export const reducers: ActionReducerMap<State, any> = {
     13  question: questionReducer,
     14  category: categoryReducer
     15};
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/components/questioning-components.ts

    r81e1ed6 r79ae621  
     1import { QuestionPreviewGeneralComponent } from './question-preview-general/question-preview-general.component';
     2import { QuestioningGeneralComponent } from './questioning-general/questioning-general.component';
     3import { QuestionsPreviewGeneralComponent } from './questions-preview-general/questions-preview-general.component';
     4import { QuestionsSearchComponent } from './questions-search/questions-search.component';
    15import { AskQuestionComponent } from './ask-question/ask-question.component';
    26
    3 export const QUESTIONING_COMPONENTS: any[] = [AskQuestionComponent];
     7export const QUESTIONING_COMPONENTS: any[] = [
     8  QuestionPreviewGeneralComponent,
     9  QuestionsPreviewGeneralComponent,
     10  QuestioningGeneralComponent,
     11  QuestionsSearchComponent,
     12  AskQuestionComponent
     13];
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.module.ts

    r81e1ed6 r79ae621  
    11import { NgModule } from '@angular/core';
    22
     3import { QuestioningRoutingModule } from './questioning.routes';
    34import { QUESTIONING_COMPONENTS } from './components/questioning-components';
    45import { SharedAppModule } from 'src/app/shared-app/shared-app.module';
    5 import { QuestioningRoutingModule } from './questioning.routes';
    6 
    76@NgModule({
    87  declarations: [QUESTIONING_COMPONENTS],
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.routes.ts

    r81e1ed6 r79ae621  
    11import { NgModule } from '@angular/core';
    22import { Routes, RouterModule } from '@angular/router';
     3import { QuestionPreviewGeneralComponent } from './components/question-preview-general/question-preview-general.component';
     4import { QuestioningGeneralComponent } from './components/questioning-general/questioning-general.component';
     5import { QuestionsPreviewGeneralComponent } from './components/questions-preview-general/questions-preview-general.component';
     6import { QuestionsSearchComponent } from './components/questions-search/questions-search.component';
    37import { AuthorizedStudentGuard } from 'src/app/core/guards/authorized-student.guard';
    48import { AskQuestionComponent } from './components/ask-question/ask-question.component';
     
    610const routes: Routes = [
    711  {
    8     path: 'ask',
    9     component: AskQuestionComponent,
    10     canActivate: [AuthorizedStudentGuard]
     12    component: QuestioningGeneralComponent,
     13    path: '',
     14    children: [
     15      {
     16        path: 'preview',
     17        pathMatch: 'full',
     18        component: QuestionsPreviewGeneralComponent
     19      },
     20      {
     21        path: 'search',
     22        pathMatch: 'full',
     23        component: QuestionsSearchComponent
     24      },
     25      {
     26        path: 'ask',
     27        pathMatch: 'full',
     28        component: AskQuestionComponent,
     29        canActivate: [AuthorizedStudentGuard]
     30      }
     31      {
     32        path: ':questionUid',
     33        component: QuestionPreviewGeneralComponent
     34      }
     35    ]
    1136  }
    1237];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/directives/directives.ts

    r81e1ed6 r79ae621  
    1 import { HandleInputFormErrorsDirective, HoverElevationDirective, LoaderDirective, HandleSelectFormErrorsDirective } from '.';
     1import {
     2  HandleInputFormErrorsDirective,
     3  HoverElevationDirective,
     4  LoaderDirective,
     5  HandleSelectFormErrorsDirective,
     6  ShareLinkDirective
     7} from '.';
    28
    39export const DIRECTIVES: any[] = [
     
    511  LoaderDirective,
    612  HoverElevationDirective,
    7   HandleSelectFormErrorsDirective
     13  HandleSelectFormErrorsDirective,
     14  ShareLinkDirective
    815];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/directives/index.ts

    r81e1ed6 r79ae621  
    33export * from './hover-elevation.directive';
    44export * from './handle-select-form-errors.directive';
     5export * from './share-link.directive';
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/index.ts

    r81e1ed6 r79ae621  
    11export * from './error.models';
    22export * from './user.models';
     3export * from './question-state-view-models.models';
     4export * from './category-state-view-models.models';
     5export * from './question-state-enums.models';
    36export * from './categories.models';
  • src/Clients/Angular/finki-chattery/src/app/shared-app/pipes/moment-date.pipe.ts

    r81e1ed6 r79ae621  
    66})
    77export class MomentDatePipe implements PipeTransform {
    8   transform(value: moment.Moment, dateFormat: string): any {
     8  transform(value: moment.Moment | undefined | null, dateFormat: string): any {
    99    return moment(value).format(dateFormat);
    1010  }
  • src/Clients/Angular/finki-chattery/src/app/shared-app/services/translate-from-json.service.ts

    r81e1ed6 r79ae621  
    4343  }
    4444
    45   public instant(key?: string): string | undefined {
    46     if (key) {
    47       return this.translateService.instant(key);
    48     }
    49     return undefined;
     45  public instant(key: string): string {
     46    return this.translateService.instant(key);
    5047  }
    5148}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/shared-app.module.ts

    r81e1ed6 r79ae621  
    99import { EditorModule } from '@tinymce/tinymce-angular';
    1010
    11 import { COMPONENTS } from './components/generic/components';
     11import { COMPONENTS } from './components/components';
    1212import { SharedMaterialModule } from '../shared-material/shared-material.module';
    1313import { DIRECTIVES } from './directives/directives';
    1414import { SERVICES } from './services/services';
    1515import { PIPES } from './pipes/pipes';
    16 import { FileUploadComponent } from './components/generic/file-upload/file-upload.component';
    17 import { HandleSelectFormErrorsDirective } from './directives/handle-select-form-errors.directive';
    1816
    1917@NgModule({
    20   declarations: [COMPONENTS, DIRECTIVES, PIPES, FileUploadComponent, HandleSelectFormErrorsDirective],
     18  declarations: [COMPONENTS, DIRECTIVES, PIPES],
    2119  providers: [SERVICES],
    2220  imports: [
  • src/Clients/Angular/finki-chattery/src/app/shared-material/shared-material.module.ts

    r81e1ed6 r79ae621  
    2020import { MatDatepickerModule } from '@angular/material/datepicker';
    2121import { MatNativeDateModule } from '@angular/material/core';
     22import { MatChipsModule } from '@angular/material/chips';
     23import { MatTooltipModule } from '@angular/material/tooltip';
     24import { MatButtonToggleModule } from '@angular/material/button-toggle';
     25import { MatBadgeModule } from '@angular/material/badge';
     26
    2227@NgModule({
    2328  imports: [
     
    4045    MatTableModule,
    4146    MatDatepickerModule,
    42     MatNativeDateModule
     47    MatNativeDateModule,
     48    MatChipsModule,
     49    MatTooltipModule,
     50    MatButtonToggleModule,
     51    MatBadgeModule
    4352  ],
    4453  exports: [
     
    6069    MatTableModule,
    6170    MatDatepickerModule,
    62     MatNativeDateModule
     71    MatNativeDateModule,
     72    MatChipsModule,
     73    MatTooltipModule,
     74    MatButtonToggleModule,
     75    MatBadgeModule
    6376  ]
    6477})
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r81e1ed6 r79ae621  
    1414  "password-not-match": "Passwords don't match",
    1515  "code-date-passed": "The code date has passed",
    16   "not-found": "Not found"
     16  "not-found": "Not found",
     17  "question-preview-subtitle": "Asked <b>{{createdOn}}</b>, Last active <b>{{lastActive}}</b>, Viewed <b>{{views}}</b> times",
     18  "share-link": "Share",
     19  "share-link-success": "Successfully copied link for sharing",
     20  "question-asked-by-subtitle": "Asked on: {{date}}",
     21  "question-answers": "{{answerCount}} Answers",
     22  "question-answered-by-subtitle": "Answered on: {{date}}",
     23  "student-reputation": "{{reputation}} reputation",
     24  "vote-correct-answer": "This has been accepted as the correct answer by the owner of the question",
     25  "answer-sort-oldest": "Oldest",
     26  "answer-sort-votes": "Votes",
     27  "internet-techologies": "Internet technologies",
     28  "software-engineering": "Software engineering",
     29  "visual-programming": "Visual programming",
     30  "operating-systems": "Operating systems",
     31  "internet-programming": "Internet programming",
     32  "object-oriented-programming": "Object oriented programming",
     33  "calculus": "Calculus",
     34  "discrete-mathematics": "Discrete mathematics",
     35  "web-programming": "Web programming",
     36  "advanced-programming": "Advanced programming",
     37  "questions-preview-find-question": "Search for a question",
     38  "questions-preview-find-question-categories": "Search in categories",
     39  "questions-search": "Search",
     40  "questions-preview": "Preview questions",
     41  "question-sort-newest": "Newest",
     42  "question-sort-popular": "Most popular",
     43  "questions-preview-question-subtitle": "Asked on: {{date}}",
     44  "questions-preview-question-answers": "Answers",
     45  "questions-preview-question-views": "Views",
     46  "questions-search-title": "Search results for: {{searchQuery}}"
    1747}
  • src/Clients/Angular/finki-chattery/src/styles.scss

    r81e1ed6 r79ae621  
    264264
    265265.avatar-image {
    266   width: 100px;
    267   height: 100px;
     266  width: 80px;
     267  height: 80px;
    268268  display: block;
    269269  border-radius: 50%;
     
    285285  z-index: 1100 !important;
    286286}
     287
     288.full-width {
     289  width: 100%;
     290}
Note: See TracChangeset for help on using the changeset viewer.