From 8a04889bbbf68f91c5561130980a6e53a31ed658 Mon Sep 17 00:00:00 2001 From: Daniel Klingmann Date: Thu, 19 Aug 2021 12:48:02 +0200 Subject: [PATCH] feat(oauth): add sign in with discord Implements #3 --- src/app/app-routing.module.ts | 12 ++- src/app/app.module.ts | 5 +- src/app/core/resolvers/user.resolver.spec.ts | 16 ++++ src/app/core/resolvers/user.resolver.ts | 18 +++++ .../modules/api/interfaces/oauth-client.ts | 6 ++ src/app/modules/api/interfaces/user.ts | 5 +- src/app/modules/api/services/api.service.ts | 40 ++++++---- .../modules/toolbar/toolbar.component.html | 10 +-- src/app/modules/toolbar/toolbar.component.ts | 7 +- .../components/profile/profile.component.html | 8 +- .../components/profile/profile.component.ts | 7 +- .../components/sign-in/sign-in.component.html | 21 +++++- .../components/sign-in/sign-in.component.scss | 61 ++++++++++++++- .../components/sign-in/sign-in.component.ts | 32 ++++++-- src/app/modules/user/services/user.service.ts | 66 ++++++++++++---- src/app/modules/user/user.component.html | 8 -- src/app/modules/user/user.component.scss | 6 -- src/app/modules/user/user.component.ts | 21 ------ src/app/modules/user/user.module.ts | 22 +++--- .../pages/browser/browser-routing.module.ts | 40 ++++++++-- .../oauth-provider-user-badge.component.html | 14 ++++ .../oauth-provider-user-badge.component.scss | 30 ++++++++ ...auth-provider-user-badge.component.spec.ts | 25 ++++++ .../oauth-provider-user-badge.component.ts | 20 +++++ src/app/pages/oauth/oauth-routing.module.ts | 55 ++++++++++++++ src/app/pages/oauth/oauth.component.html | 41 ++++++++++ src/app/pages/oauth/oauth.component.scss | 41 ++++++++++ .../oauth/oauth.component.spec.ts} | 12 +-- src/app/pages/oauth/oauth.component.ts | 71 ++++++++++++++++++ src/app/pages/oauth/oauth.module.ts | 20 +++++ src/assets/images/social/discord.png | Bin 0 -> 981 bytes src/assets/images/xivrh-logo.png | Bin 0 -> 56655 bytes src/styles/_buttons.scss | 10 +++ 33 files changed, 629 insertions(+), 121 deletions(-) create mode 100644 src/app/core/resolvers/user.resolver.spec.ts create mode 100644 src/app/core/resolvers/user.resolver.ts create mode 100644 src/app/modules/api/interfaces/oauth-client.ts delete mode 100644 src/app/modules/user/user.component.html delete mode 100644 src/app/modules/user/user.component.scss delete mode 100644 src/app/modules/user/user.component.ts create mode 100644 src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.html create mode 100644 src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.scss create mode 100644 src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.spec.ts create mode 100644 src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.ts create mode 100644 src/app/pages/oauth/oauth-routing.module.ts create mode 100644 src/app/pages/oauth/oauth.component.html create mode 100644 src/app/pages/oauth/oauth.component.scss rename src/app/{modules/user/user.component.spec.ts => pages/oauth/oauth.component.spec.ts} (57%) create mode 100644 src/app/pages/oauth/oauth.component.ts create mode 100644 src/app/pages/oauth/oauth.module.ts create mode 100644 src/assets/images/social/discord.png create mode 100644 src/assets/images/xivrh-logo.png diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 6443a6a..3e5a4aa 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,6 +1,7 @@ import {NgModule} from "@angular/core"; import {RouterModule, Routes} from "@angular/router"; import {GameDataResolver} from "./core/resolvers/game-data.resolver"; +import {UserResolver} from "./core/resolvers/user.resolver"; const rootRoutes: Routes = [ { @@ -10,6 +11,13 @@ const rootRoutes: Routes = [ gameData: GameDataResolver } }, + { + path: 'oauth', + loadChildren: () => import('./pages/oauth/oauth.module').then(m => m.OauthModule), + resolve: { + user: UserResolver + } + }, { path: 'embed', loadChildren: () => import('./pages/embedded/embedded.module').then(m => m.EmbeddedModule), @@ -29,6 +37,7 @@ const rootRoutes: Routes = [ redirectTo: 'browser', pathMatch: 'full' }, + {path: '**', redirectTo: 'browser'} ]; @NgModule({ @@ -41,4 +50,5 @@ const rootRoutes: Routes = [ RouterModule ] }) -export class AppRoutingModule {} +export class AppRoutingModule { +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 589a44d..dcc221a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -14,11 +14,12 @@ import {ReactiveFormsModule} from "@angular/forms"; BrowserModule, HttpClientModule, AppRoutingModule, - ReactiveFormsModule + ReactiveFormsModule, ], providers: [ communicationLayerServiceProvider ], bootstrap: [AppComponent] }) -export class AppModule { } +export class AppModule { +} diff --git a/src/app/core/resolvers/user.resolver.spec.ts b/src/app/core/resolvers/user.resolver.spec.ts new file mode 100644 index 0000000..d6d6fe6 --- /dev/null +++ b/src/app/core/resolvers/user.resolver.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserResolver } from './user.resolver'; + +describe('UserResolver', () => { + let resolver: UserResolver; + + beforeEach(() => { + TestBed.configureTestingModule({}); + resolver = TestBed.inject(UserResolver); + }); + + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); +}); diff --git a/src/app/core/resolvers/user.resolver.ts b/src/app/core/resolvers/user.resolver.ts new file mode 100644 index 0000000..a7bd4d3 --- /dev/null +++ b/src/app/core/resolvers/user.resolver.ts @@ -0,0 +1,18 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router'; +import {Observable} from 'rxjs'; +import {User} from "../../modules/api/interfaces/user"; +import {UserService} from "../../modules/user/services/user.service"; +import {take} from "rxjs/operators"; + +@Injectable({ + providedIn: 'root' +}) +export class UserResolver implements Resolve { + public constructor(private readonly userService: UserService) { + } + + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.userService.signedInUser$.pipe(take(1)); + } +} diff --git a/src/app/modules/api/interfaces/oauth-client.ts b/src/app/modules/api/interfaces/oauth-client.ts new file mode 100644 index 0000000..3cec718 --- /dev/null +++ b/src/app/modules/api/interfaces/oauth-client.ts @@ -0,0 +1,6 @@ +export interface OauthClient { + oauthProvider: string; + providerUserId: string; + providerUsername: string; + providerUserDiscriminator?: string; +} diff --git a/src/app/modules/api/interfaces/user.ts b/src/app/modules/api/interfaces/user.ts index 060eddb..3fd92df 100644 --- a/src/app/modules/api/interfaces/user.ts +++ b/src/app/modules/api/interfaces/user.ts @@ -1,6 +1,9 @@ import {UserShort} from "./user-short"; +import {OauthClient} from "./oauth-client"; export interface User extends UserShort { uniqueToken: string, - email: string + email: string, + isOAuthUser: boolean, + oauthClients: OauthClient[] } diff --git a/src/app/modules/api/services/api.service.ts b/src/app/modules/api/services/api.service.ts index b59e39b..00ea800 100644 --- a/src/app/modules/api/services/api.service.ts +++ b/src/app/modules/api/services/api.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import {Injectable} from '@angular/core'; import {RotationCreate} from "../interfaces/rotation-create"; import {RotationUpdate} from "../interfaces/rotation-update"; import {Rotation} from "../interfaces/rotation"; @@ -9,7 +9,6 @@ import {User} from "../interfaces/user"; import {environment} from "../../../../environments/environment"; import {HttpClient} from "@angular/common/http"; import {Observable} from "rxjs"; -import {map} from "rxjs/operators"; @Injectable({ providedIn: 'root' @@ -17,15 +16,16 @@ import {map} from "rxjs/operators"; export class ApiService { private readonly API_BASE_URL = environment.apiBaseUrl; - public constructor(private readonly httpClient: HttpClient) {} + public constructor(private readonly httpClient: HttpClient) { + } // AUTH signIn(email: string, password: string): Observable { - return this.request('/auth/login', 'POST', JSON.stringify({ email, password })) as Observable; + return this.request('/auth/login', 'POST', JSON.stringify({email, password})) as Observable; } signUp(email: string, username: string, password: string) { - return this.request('/auth/signup', 'POST', JSON.stringify({ email, password, username })); + return this.request('/auth/signup', 'POST', JSON.stringify({email, password, username})); } me(): Observable { @@ -80,7 +80,7 @@ export class ApiService { const paramString = paramArray.length ? `?${paramArray.join('&')}` : ''; - return this.request(`/rotation/${ paramString }`, 'GET') as Observable>; + return this.request(`/rotation/${paramString}`, 'GET') as Observable>; } // USER @@ -92,6 +92,14 @@ export class ApiService { return this.request('/user/rotations', 'GET') as Observable>; } + usernameTaken(username: string): Observable { + return this.request(`/user/name-taken/${username}`, 'GET') as Observable; + } + + changeUsername(username: string): Observable { + return this.request(`/user/name`, 'POST', {username}) as Observable; + } + // Token userTokenFavourites(token: string): Observable> { return this.request(`/token/${token}/favourites`, 'GET') as Observable>; @@ -101,17 +109,17 @@ export class ApiService { return this.request(`/token/${token}/rotations`, 'GET') as Observable>; } - private request(url: string, method: 'POST' | 'PATCH' | 'GET' | 'DELETE', body?: string) { + private request(url: string, method: 'POST' | 'PATCH' | 'GET' | 'DELETE', body?: string | Record) { return this.httpClient.request( - method, - `${this.API_BASE_URL}${url}`, - { - headers: { - 'Content-Type': 'application/json' - }, - withCredentials: true, - body - } + method, + `${this.API_BASE_URL}${url}`, + { + headers: { + 'Content-Type': 'application/json' + }, + withCredentials: true, + body + } ); } } diff --git a/src/app/modules/toolbar/toolbar.component.html b/src/app/modules/toolbar/toolbar.component.html index 8d4586f..a6c2ae1 100644 --- a/src/app/modules/toolbar/toolbar.component.html +++ b/src/app/modules/toolbar/toolbar.component.html @@ -1,7 +1,7 @@ + [classJobs]="classJobs$ | async" + [currentClassJob]="currentClassJob$ | async" + (selectClassJob)="selectClassJob($event)"> Rotation Hero @@ -19,9 +19,9 @@ + [user]="(user$ | async)!"> diff --git a/src/app/modules/toolbar/toolbar.component.ts b/src/app/modules/toolbar/toolbar.component.ts index e80fc81..6b387d2 100644 --- a/src/app/modules/toolbar/toolbar.component.ts +++ b/src/app/modules/toolbar/toolbar.component.ts @@ -35,11 +35,12 @@ export class ToolbarComponent { public readonly classJobs$ = this.gameDataService.classJobs$; public readonly currentClassJob$ = this.appStateService.currentClassJob$ public readonly user$ = this.userService.signedInUser$; + public readonly displayedUserName$ = this.userService.displayedUserName$; constructor( - public readonly gameDataService: GameDataService, - private readonly appStateService: AppStateService, - private readonly userService: UserService + public readonly gameDataService: GameDataService, + private readonly appStateService: AppStateService, + private readonly userService: UserService ) { } diff --git a/src/app/modules/user/components/profile/profile.component.html b/src/app/modules/user/components/profile/profile.component.html index 604c98a..c397819 100644 --- a/src/app/modules/user/components/profile/profile.component.html +++ b/src/app/modules/user/components/profile/profile.component.html @@ -1,13 +1,13 @@

- ID:
+ ID:
{{ user?.id }}

- Username:
+ Username:
{{ user?.username }}

-

- ACT Overlay URL:
+

+ ACT Overlay URL:
{{ user?.uniqueToken }}

diff --git a/src/app/modules/user/components/profile/profile.component.ts b/src/app/modules/user/components/profile/profile.component.ts index e32c92c..9eb6e03 100644 --- a/src/app/modules/user/components/profile/profile.component.ts +++ b/src/app/modules/user/components/profile/profile.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit, ChangeDetectionStrategy, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {UserService} from "../../services/user.service"; import {User} from "../../../api/interfaces/user"; @@ -10,9 +10,10 @@ import {User} from "../../../api/interfaces/user"; }) export class ProfileComponent { - @Input() user: User | null = null; + @Input() user!: User; - constructor(private readonly userService: UserService) { } + constructor(private readonly userService: UserService) { + } logout() { this.userService.signOutSubject$.next(); diff --git a/src/app/modules/user/components/sign-in/sign-in.component.html b/src/app/modules/user/components/sign-in/sign-in.component.html index ae9d870..a41ef45 100644 --- a/src/app/modules/user/components/sign-in/sign-in.component.html +++ b/src/app/modules/user/components/sign-in/sign-in.component.html @@ -5,13 +5,28 @@ (ngSubmit)="onSubmit(signInFormGroup.value)"> + [disabled]="!signInFormGroup.valid">Sign in + + + + + + + diff --git a/src/app/modules/user/components/sign-in/sign-in.component.scss b/src/app/modules/user/components/sign-in/sign-in.component.scss index 838dce3..d21878f 100644 --- a/src/app/modules/user/components/sign-in/sign-in.component.scss +++ b/src/app/modules/user/components/sign-in/sign-in.component.scss @@ -1,4 +1,6 @@ -:host { display: block; } +:host { + display: block; +} .rh-sign-in { &__title { @@ -22,10 +24,10 @@ &__input { display: block; color: white; - background: rgba(255,255,255,0.1); + background: rgba(255, 255, 255, 0.1); border: 0; outline: 0; - border-bottom: 2px solid rgba(255,255,255,0.2); + border-bottom: 2px solid rgba(255, 255, 255, 0.2); line-height: 1.5; width: 100%; padding: 6px 8px; @@ -48,4 +50,57 @@ border-color: gray; } } + + &__separator { + background: linear-gradient(to right, transparent, white, transparent) no-repeat center center; + background-size: 100% 1px; + color: white; + text-align: center; + margin: 8px 0; + + span { + display: inline-block; + background: #323232; + font-size: 12px; + padding: 0 6px; + } + } + + &__social-button { + cursor: pointer; + font-size: inherit; + width: 100%; + border: 0; + padding: 0; + text-align: left; + display: flex; + align-items: stretch; + + &::before { + content: ''; + display: block; + width: 40px; + } + + &--discord { + background: #8c9eff; + color: white; + + &::before { + background: white url("/assets/images/social/discord.png") no-repeat center center; + background-size: 75%; + } + } + } + + &__social-button-label { + padding: 8px 12px; + font-size: 16px; + font-weight: 700; + } + + &__social-auth-label { + color: white; + font-size: 12px; + } } diff --git a/src/app/modules/user/components/sign-in/sign-in.component.ts b/src/app/modules/user/components/sign-in/sign-in.component.ts index 1a321e1..415d8bb 100644 --- a/src/app/modules/user/components/sign-in/sign-in.component.ts +++ b/src/app/modules/user/components/sign-in/sign-in.component.ts @@ -1,6 +1,13 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {FormControl, FormGroup, Validators} from "@angular/forms"; import {UserService} from "../../services/user.service"; +import {environment} from "../../../../../environments/environment"; + +interface OAuthProvider { + name: string, + url: string, + className: string +} @Component({ selector: 'rh-sign-in', @@ -10,14 +17,27 @@ import {UserService} from "../../services/user.service"; }) export class SignInComponent { - public signInFormGroup = new FormGroup({ - email: new FormControl('', [ Validators.required, Validators.email ]), - password: new FormControl('', [ Validators.required ]) + public readonly oauthProviders: OAuthProvider[] = [ + { + name: 'Discord', + className: 'discord', + url: environment.apiBaseUrl + '/auth/provider/discord' + } + ]; + + public readonly signInFormGroup = new FormGroup({ + email: new FormControl('', [Validators.required, Validators.email]), + password: new FormControl('', [Validators.required]) }); - public constructor(private readonly userService: UserService) {} + public constructor(private readonly userService: UserService) { + } - public onSubmit(credentials: { email: string, password: string}) { + public onSubmit(credentials: { email: string, password: string }) { this.userService.signInSubject$.next(credentials); } + + public signInWithProvider(provider: OAuthProvider): void { + window.location.href = provider.url; + } } diff --git a/src/app/modules/user/services/user.service.ts b/src/app/modules/user/services/user.service.ts index 60d8db5..bffbbd5 100644 --- a/src/app/modules/user/services/user.service.ts +++ b/src/app/modules/user/services/user.service.ts @@ -1,31 +1,67 @@ -import { Injectable } from '@angular/core'; +import {Injectable} from '@angular/core'; import {ApiService} from "../../api/services/api.service"; -import {of, Subject} from "rxjs"; -import {catchError, shareReplay, startWith, switchMap, switchMapTo, tap} from "rxjs/operators"; +import {Observable, of, ReplaySubject, Subject} from "rxjs"; +import {catchError, map, share, switchMap, take} from "rxjs/operators"; +import {User} from "../../api/interfaces/user"; +import {Router} from "@angular/router"; @Injectable({ providedIn: 'root' }) export class UserService { + public readonly signedInUser$: ReplaySubject = new ReplaySubject(1); public readonly signInSubject$: Subject<{ email: string, password: string }> = new Subject(); public readonly signOutSubject$: Subject = new Subject(); - public readonly signInRequests$ = this.signInSubject$.pipe( - switchMap(({ email, password}) => - this.api.signIn(email, password) - .pipe(catchError(() => of(null))) - ), - startWith(null) - ); + public readonly displayedUserName$: Observable = this.signedInUser$ + .pipe( + map((user) => { + if (!user) { + return ''; + } + + return user.username; + }) + ); - public readonly signedInUser$ = this.signOutSubject$.pipe( - startWith(null), - switchMapTo(this.signInRequests$), - shareReplay(1) + private readonly signInRequests$ = this.signInSubject$.pipe( + switchMap(({email, password}) => + this.api.signIn(email, password) + .pipe(catchError(() => of(null))) + ) ); - public constructor(private readonly api: ApiService) { + private readonly signOutRequests$ = this.signOutSubject$.pipe( + switchMap(() => + this.api.logout() + .pipe(catchError(() => of(null))) + ), + share() + ) + + public constructor(private readonly api: ApiService, + private readonly router: Router) { this.signedInUser$.subscribe(); + this.signInRequests$.subscribe((user) => this.signedInUser$.next(user)); + this.signOutRequests$.subscribe(() => this.signedInUser$.next(null)); + + this.loadCurrentUser(); + } + + public loadCurrentUser() { + this.api.me().subscribe((user) => this.signedInUser$.next(user)); + } + + public logout() { + this.signOutRequests$ + .pipe( + take(1) + ) + .subscribe( + () => this.router.navigate(['/browser']) + ); + + this.signOutSubject$.next(); } } diff --git a/src/app/modules/user/user.component.html b/src/app/modules/user/user.component.html deleted file mode 100644 index 8e3b85b..0000000 --- a/src/app/modules/user/user.component.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/app/modules/user/user.component.scss b/src/app/modules/user/user.component.scss deleted file mode 100644 index 9be3c5b..0000000 --- a/src/app/modules/user/user.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -:host { - width: 300px; - z-index: 100; - padding: 16px; - background: rgba(36, 36, 36, 0.6); -} diff --git a/src/app/modules/user/user.component.ts b/src/app/modules/user/user.component.ts deleted file mode 100644 index 6539d13..0000000 --- a/src/app/modules/user/user.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; -import {ActiveUserView} from "./enums/active-user-view"; -import {UserService} from "./services/user.service"; - -@Component({ - selector: 'rh-user', - templateUrl: './user.component.html', - styleUrls: ['./user.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class UserComponent { - - public user$ = this.userService.signedInUser$; - - public activeView: ActiveUserView = ActiveUserView.SignIn; - - public readonly ActiveUserView = ActiveUserView; - - constructor(private readonly userService: UserService) { } - -} diff --git a/src/app/modules/user/user.module.ts b/src/app/modules/user/user.module.ts index 01adc7a..ba1c41f 100644 --- a/src/app/modules/user/user.module.ts +++ b/src/app/modules/user/user.module.ts @@ -1,26 +1,21 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SignInComponent } from './components/sign-in/sign-in.component'; -import { SignUpComponent } from './components/sign-up/sign-up.component'; -import { ProfileComponent } from './components/profile/profile.component'; -import { UserComponent } from './user.component'; +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {SignInComponent} from './components/sign-in/sign-in.component'; +import {SignUpComponent} from './components/sign-up/sign-up.component'; +import {ProfileComponent} from './components/profile/profile.component'; import {ReactiveFormsModule} from "@angular/forms"; -import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component'; -import { UserOverlayComponent } from './components/user-overlay/user-overlay.component'; - - +import {ForgotPasswordComponent} from './components/forgot-password/forgot-password.component'; +import {UserOverlayComponent} from './components/user-overlay/user-overlay.component'; @NgModule({ declarations: [ SignInComponent, SignUpComponent, ProfileComponent, - UserComponent, ForgotPasswordComponent, UserOverlayComponent ], exports: [ - UserComponent, UserOverlayComponent, ProfileComponent ], @@ -29,4 +24,5 @@ import { UserOverlayComponent } from './components/user-overlay/user-overlay.com ReactiveFormsModule ] }) -export class UserModule { } +export class UserModule { +} diff --git a/src/app/pages/browser/browser-routing.module.ts b/src/app/pages/browser/browser-routing.module.ts index 86cae33..8b63f7c 100644 --- a/src/app/pages/browser/browser-routing.module.ts +++ b/src/app/pages/browser/browser-routing.module.ts @@ -1,20 +1,50 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import {RouterModule, Routes} from "@angular/router"; +import {Injectable, NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import { + ActivatedRouteSnapshot, + CanActivate, + Router, + RouterModule, + RouterStateSnapshot, + Routes, + UrlTree +} from "@angular/router"; import {BrowserComponent} from "./browser.component"; +import {ApiService} from "../../modules/api/services/api.service"; +import {Observable} from "rxjs"; +import {map} from "rxjs/operators"; + +@Injectable() +class IsLoggedOutOrHasUsername implements CanActivate { + public constructor( + private readonly apiService: ApiService, + private readonly router: Router + ) { + } + + public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.apiService.me() + .pipe( + map((user) => Boolean(!user || user.username !== null) ? true : this.router.createUrlTree(['/oauth'])) + ); + } +} const routes: Routes = [ { path: '', - component: BrowserComponent + component: BrowserComponent, + canActivate: [IsLoggedOutOrHasUsername] } ] @NgModule({ declarations: [], + providers: [IsLoggedOutOrHasUsername], imports: [ CommonModule, RouterModule.forChild(routes) ] }) -export class BrowserRoutingModule { } +export class BrowserRoutingModule { +} diff --git a/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.html b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.html new file mode 100644 index 0000000..eedd494 --- /dev/null +++ b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.html @@ -0,0 +1,14 @@ +
+ +
+
+
+ {{ user.oauthClients[0].providerUsername}} + + #{{user.oauthClients[0].providerUserDiscriminator}} + +
+
{{ user.oauthClients[0].oauthProvider }}
+
diff --git a/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.scss b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.scss new file mode 100644 index 0000000..8d4ed46 --- /dev/null +++ b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.scss @@ -0,0 +1,30 @@ +:host { + display: flex; + border-radius: 10px; + overflow: hidden; + + &.discord .rh-oauth-provider-user-badge__container { + background: #8c9eff; + color: white; + } +} + +.rh-oauth-provider-user-badge { + &__provider-icon { + background: white; + } + + &__user { + font-weight: 700; + } + + &__user-discriminator { + opacity: .7; + font-weight: 300; + } + + &__container { + padding: 6px; + flex-grow: 1; + } +} \ No newline at end of file diff --git a/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.spec.ts b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.spec.ts new file mode 100644 index 0000000..9b75b1c --- /dev/null +++ b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OauthProviderUserBadgeComponent } from './oauth-provider-user-badge.component'; + +describe('OauthProviderUserBadgeComponent', () => { + let component: OauthProviderUserBadgeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ OauthProviderUserBadgeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(OauthProviderUserBadgeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.ts b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.ts new file mode 100644 index 0000000..91523b4 --- /dev/null +++ b/src/app/pages/oauth/oauth-provider-user-badge/oauth-provider-user-badge.component.ts @@ -0,0 +1,20 @@ +import {ChangeDetectionStrategy, Component, HostBinding, Input} from '@angular/core'; +import {User} from "../../../modules/api/interfaces/user"; + +@Component({ + selector: 'rh-oauth-provider-user-badge', + templateUrl: './oauth-provider-user-badge.component.html', + styleUrls: ['./oauth-provider-user-badge.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OauthProviderUserBadgeComponent { + + @Input() public user!: User; + + @HostBinding('class') + public provider: string = ''; + + public ngOnChanges() { + this.provider = this.user?.oauthClients[0].oauthProvider; + } +} diff --git a/src/app/pages/oauth/oauth-routing.module.ts b/src/app/pages/oauth/oauth-routing.module.ts new file mode 100644 index 0000000..d8cb171 --- /dev/null +++ b/src/app/pages/oauth/oauth-routing.module.ts @@ -0,0 +1,55 @@ +import {Injectable, NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import { + ActivatedRouteSnapshot, + CanActivate, + Router, + RouterModule, + RouterStateSnapshot, + Routes, + UrlTree +} from "@angular/router"; +import {OauthComponent} from "./oauth.component"; +import {Observable} from "rxjs"; +import {map} from "rxjs/operators"; +import {ApiService} from "../../modules/api/services/api.service"; + +@Injectable() +class IsOauthUserWithoutName implements CanActivate { + public constructor( + private readonly apiService: ApiService, + private readonly router: Router + ) { + } + + public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + return this.apiService.me() + .pipe( + map((user) => Boolean(user && user.isOAuthUser && user.username === null) ? true : this.router.createUrlTree(['/browser'])) + ); + } +} + +const routes: Routes = [ + { + path: '', + component: OauthComponent, + canActivate: [IsOauthUserWithoutName] + } +]; + +@NgModule({ + declarations: [], + providers: [ + IsOauthUserWithoutName + ], + imports: [ + CommonModule, + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class OauthRoutingModule { +} diff --git a/src/app/pages/oauth/oauth.component.html b/src/app/pages/oauth/oauth.component.html new file mode 100644 index 0000000..7cb6226 --- /dev/null +++ b/src/app/pages/oauth/oauth.component.html @@ -0,0 +1,41 @@ +
+ + +
+ + +
+

Choose a username

+ + + + This username is already taken :( + + + + +
+
+
diff --git a/src/app/pages/oauth/oauth.component.scss b/src/app/pages/oauth/oauth.component.scss new file mode 100644 index 0000000..a4244ab --- /dev/null +++ b/src/app/pages/oauth/oauth.component.scss @@ -0,0 +1,41 @@ +.rh-oauth { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #545561; + height: 100%; + + &__panel { + max-width: 400px; + padding: 16px; + border-radius: 16px; + background: #323232; + color: white; + font-size: 14px; + margin-bottom: 15%; + box-shadow: inset 0 2px 5px black; + border: 2px solid gray; + } + + &__logo { + height: 100px; + } + + &__username-input { + display: block; + color: #fff; + background: #ffffff1a; + border: 0; + outline: 0; + border-bottom: 2px solid #fff3; + line-height: 1.5; + width: 100%; + padding: 6px 8px; + box-sizing: border-box; + } +} + +button { + margin-top: 8px; +} diff --git a/src/app/modules/user/user.component.spec.ts b/src/app/pages/oauth/oauth.component.spec.ts similarity index 57% rename from src/app/modules/user/user.component.spec.ts rename to src/app/pages/oauth/oauth.component.spec.ts index e6bf596..ed653b4 100644 --- a/src/app/modules/user/user.component.spec.ts +++ b/src/app/pages/oauth/oauth.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { UserComponent } from './user.component'; +import { OauthComponent } from './oauth.component'; -describe('UserComponent', () => { - let component: UserComponent; - let fixture: ComponentFixture; +describe('OauthComponent', () => { + let component: OauthComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ UserComponent ] + declarations: [ OauthComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(UserComponent); + fixture = TestBed.createComponent(OauthComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/pages/oauth/oauth.component.ts b/src/app/pages/oauth/oauth.component.ts new file mode 100644 index 0000000..c9d4cbd --- /dev/null +++ b/src/app/pages/oauth/oauth.component.ts @@ -0,0 +1,71 @@ +import {ChangeDetectionStrategy, Component} from '@angular/core'; +import {UserService} from "../../modules/user/services/user.service"; +import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, Validators} from "@angular/forms"; +import {filter, map, switchMap, take, tap} from "rxjs/operators"; +import {Observable, timer} from "rxjs"; +import {ApiService} from "../../modules/api/services/api.service"; +import {Router} from "@angular/router"; + +function usernameIsNotTaken(apiService: ApiService) { + return (control: AbstractControl): Promise | Observable => { + return timer(500).pipe( + switchMap(() => apiService.usernameTaken(control.value)), + tap(v => console.log(v, v === true)), + map((res) => res ? {usernameIsTaken: true} : null) + ) + }; +} + +@Component({ + selector: 'rh-oauth', + templateUrl: './oauth.component.html', + styleUrls: ['./oauth.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OauthComponent { + + public readonly signedInUser$ = this.userService.signedInUser$; + + public readonly form: FormGroup; + public readonly usernameControl: FormControl; + + public constructor(private readonly userService: UserService, + private readonly fb: FormBuilder, + private readonly apiService: ApiService, + private readonly router: Router) { + this.usernameControl = this.fb.control('', [Validators.required], [usernameIsNotTaken(apiService)]); + + this.form = this.fb.group({ + username: this.usernameControl + }); + + this.signedInUser$.pipe( + filter((value => !!value)), + take(1) + ) + .subscribe((user) => { + if (!user) { + return; + } + + this.form.patchValue({username: user.oauthClients[0].providerUsername + user.oauthClients[0].providerUserDiscriminator}); + }); + } + + onSubmitForm() { + const username = this.usernameControl.value; + + this.apiService.changeUsername(username) + .subscribe((res) => { + this.userService.signedInUser$.next(res); + + if (res) { + this.router.navigate(['/browser']) + } + }); + } + + onLogout() { + this.userService.logout(); + } +} diff --git a/src/app/pages/oauth/oauth.module.ts b/src/app/pages/oauth/oauth.module.ts new file mode 100644 index 0000000..4e0db07 --- /dev/null +++ b/src/app/pages/oauth/oauth.module.ts @@ -0,0 +1,20 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {OauthComponent} from './oauth.component'; +import {OauthRoutingModule} from "./oauth-routing.module"; +import {OauthProviderUserBadgeComponent} from './oauth-provider-user-badge/oauth-provider-user-badge.component'; +import {ReactiveFormsModule} from "@angular/forms"; + +@NgModule({ + declarations: [ + OauthComponent, + OauthProviderUserBadgeComponent + ], + imports: [ + CommonModule, + OauthRoutingModule, + ReactiveFormsModule + ] +}) +export class OauthModule { +} diff --git a/src/assets/images/social/discord.png b/src/assets/images/social/discord.png new file mode 100644 index 0000000000000000000000000000000000000000..396e497ae53e20cf76866d9ee6206d8a298074b7 GIT binary patch literal 981 zcmV;`11kK9P)6UI~No9*ztZpSmY-R5X*^T}H z^)D#3Ui3a%`t=tiyKjPEiy$GYU5bKAbT=wOEW#uyEKBo8d9CL3;%U-o&h4JL%~j)m zV3@(@Jm-ABbLN>j#|s%UWSG{dM8U(&x(3*zpc0sq9!UU<0Bu@vtf^L=OVrS4;Apcx zq+$Ozmu8FRU}L>HUCvnN13)@aykig_APP!XIQ zUsRd`rWu$UkPyG*m;xpP(}s+J$!2EC9n65z^Dm$^boD^T zLwNT-)zMQhpcMT46RaGxq`B1*fyIpofj#wTn`%(VZ`vUHFK}DI&+?h*$RroZpv{1yv z$6!sVef|&g<%o|-KT8Xt`-vYVK8v`rOwQpfe}lTX`EGxHur?6OlWG5!6c2iMq1=X*$fs!Tu1`4WQt0K;hKz`Ia*4qaySLAxf7AewXVlD--@z<^mRx?moAn$)K z{<#4ILvs9kBl(z%QczT2qeieYkRLP0UJQ(1_w^?qOSXR#b_NWO55W8hu?5lOV-raf zGht;QH^DBUx=NN3(OlS8m3%DO{!LgF@to}O>-ZSVnkfr7kix9r<(+4WIS#yiH~vAJ zJKIK$U^N4w39Z9f(AgymImfXZ)Nv%Qv+MJ(dC14Qz-k8mz5u^(hn$c9AzED`i@B36 zQhxf04&$Q@f* zEDN$bop9~H#P9J)7^>F7)(!Ib0latM-UGPWE{|oP20#Y6bRBNqme2pu*3)#cZ6 z3u1D9{8aV_9S>pnwY_i`-}`}~5oo(BN3v)Qr_TEw(VJ<%C(am{Y)l4*)2bo`Oa|K1 zsv-qE4QR!&H0wAHjQUCRG|*J5&Y?M&k|YbXX3&9+z$4pSV-Yvjs{;yl0+$K6CX{0L zzB=`P$e{wT`cfqPjchW|TB{>}vQNnq@aYCO)_Ly*r`&5)DW#ig)gMYz4Lti=`zdF@ zrJ-J3{`l)?Yoc~j)&M|5oq9a1_*E%ROtzK`-z^^i4~ywYvhpOI00000NkvXXu0mjf D#w5}7 literal 0 HcmV?d00001 diff --git a/src/assets/images/xivrh-logo.png b/src/assets/images/xivrh-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ab967f767d5f233b572e6b272c1d4d58c2a427f1 GIT binary patch literal 56655 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ⅇ{&LP0QRpK}fj4CE*m6Mb-P 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⪙~E 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≥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 literal 0 HcmV?d00001 diff --git a/src/styles/_buttons.scss b/src/styles/_buttons.scss index 8b3ec28..4ceba58 100644 --- a/src/styles/_buttons.scss +++ b/src/styles/_buttons.scss @@ -33,6 +33,16 @@ a, text-align: center; } + &--filled { + background: #2f79a3; + color: white; + border: 1px solid #2f79a3; + + &:hover { + color: rgba(255, 255, 255, .8); + } + } + &--warning { color: darkred; border-color: darkred;