mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding account editing popup and displaying profile picture in the top right corner when logged in
This commit is contained in:
parent
c8e145d2e5
commit
8154b93f9b
10
angular.json
10
angular.json
@ -3,7 +3,7 @@
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"Kyoo": {
|
||||
"kyoo": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
@ -71,18 +71,18 @@
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "Kyoo:build"
|
||||
"browserTarget": "kyoo:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "Kyoo:build:production"
|
||||
"browserTarget": "kyoo:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "Kyoo:build"
|
||||
"browserTarget": "kyoo:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@ -100,5 +100,5 @@
|
||||
}
|
||||
}
|
||||
}},
|
||||
"defaultProject": "Kyoo"
|
||||
"defaultProject": "kyoo"
|
||||
}
|
||||
|
4159
package-lock.json
generated
4159
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
@ -9,41 +9,41 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^8.2.14",
|
||||
"@angular/cdk": "^8.2.3",
|
||||
"@angular/common": "^8.2.14",
|
||||
"@angular/compiler": "^8.2.14",
|
||||
"@angular/core": "^8.2.14",
|
||||
"@angular/forms": "^8.2.14",
|
||||
"@angular/material": "^8.2.3",
|
||||
"@angular/platform-browser": "^8.2.14",
|
||||
"@angular/platform-browser-dynamic": "^8.2.14",
|
||||
"@angular/router": "^8.2.14",
|
||||
"@angular/animations": "^9.0.6",
|
||||
"@angular/cdk": "^9.1.2",
|
||||
"@angular/common": "^9.0.6",
|
||||
"@angular/compiler": "^9.0.6",
|
||||
"@angular/core": "^9.0.6",
|
||||
"@angular/forms": "^9.0.6",
|
||||
"@angular/material": "^9.1.2",
|
||||
"@angular/platform-browser": "^9.0.6",
|
||||
"@angular/platform-browser-dynamic": "^9.0.6",
|
||||
"@angular/router": "^9.0.6",
|
||||
"angular-auth-oidc-client": "^10.0.15",
|
||||
"bootstrap": "^4.4.1",
|
||||
"detect-browser": "^4.8.0",
|
||||
"detect-browser": "^5.0.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"hls.js": "^0.12.4",
|
||||
"hls.js": "^0.13.2",
|
||||
"jquery": "^3.4.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"zone.js": "~0.9.1"
|
||||
"zone.js": "~0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^0.803.25",
|
||||
"@angular/cli": "^8.3.25",
|
||||
"@angular/compiler-cli": "^8.2.14",
|
||||
"@angular/language-service": "^8.2.14",
|
||||
"@angular-devkit/build-angular": "^0.900.6",
|
||||
"@angular/cli": "^9.0.6",
|
||||
"@angular/compiler-cli": "^9.0.6",
|
||||
"@angular/language-service": "^9.0.6",
|
||||
"@types/bootstrap": "^4.3.1",
|
||||
"@types/hls.js": "^0.12.5",
|
||||
"@types/jasmine": "~3.3.8",
|
||||
"@types/jasmine": "~3.5.9",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/jquery": "^3.3.32",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jquery": "^3.3.33",
|
||||
"@types/node": "~13.9.1",
|
||||
"@types/video.js": "^7.3.3",
|
||||
"codelyzer": "^5.2.1",
|
||||
"protractor": "^5.4.3",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.15.0",
|
||||
"typescript": "~3.5.3"
|
||||
"ts-node": "~8.6.2",
|
||||
"tslint": "^5.0.0",
|
||||
"typescript": "~3.7.5"
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,27 @@
|
||||
<h1 mat-dialog-title>Account</h1>
|
||||
<div mat-dialog-content>
|
||||
<mat-form-field>
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput name="accountEmail" #accountEmail="ngModel" [(ngModel)]="account.email" required email>
|
||||
</mat-form-field>
|
||||
<br/>
|
||||
<mat-form-field>
|
||||
<mat-label>Username</mat-label>
|
||||
<input matInput name="accountUsername" #accountUsername="ngModel" [(ngModel)]="account.username">
|
||||
</mat-form-field>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<mat-form-field class="w-75">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput name="accountEmail" [(ngModel)]="account.email" required email>
|
||||
</mat-form-field>
|
||||
<br/>
|
||||
<mat-form-field class="w-75">
|
||||
<mat-label>Username</mat-label>
|
||||
<input matInput name="accountUsername" [(ngModel)]="account.username">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<input type="file" class="d-none" (change)="onPictureSelected($event)" #fileInput/>
|
||||
<div class="w-100 profilePicture">
|
||||
<img [src]="account.picture" alt="Profile picture" fallback="account.svg" #accountImg/>
|
||||
</div>
|
||||
<button mat-icon-button class="upload_picture" matTooltipPosition="above" matTooltip="Upload picture" (click)="fileInput.click()">
|
||||
<mat-icon>photo_camera</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions fxFlexAlign="end" align="end" style="text-align: end">
|
||||
<button mat-button (click)="cancel()">Cancel</button>
|
||||
<button mat-button [mat-dialog-close]="this.account" cdkFocusInitial>Ok</button>
|
||||
<button mat-button (click)="finish()" cdkFocusInitial>Ok</button>
|
||||
</div>
|
||||
|
@ -0,0 +1,38 @@
|
||||
*
|
||||
{
|
||||
box-sizing: border-box;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
*:before, *:after
|
||||
{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload_picture
|
||||
{
|
||||
position: absolute;
|
||||
bottom: 2%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.profilePicture
|
||||
{
|
||||
padding-top: 100%;
|
||||
height: 0;
|
||||
position: relative;
|
||||
|
||||
> img
|
||||
{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {Component, ElementRef, Inject, ViewChild} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {Account} from "../../models/account";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -10,10 +11,37 @@ import {Account} from "../../models/account";
|
||||
})
|
||||
export class AccountComponent
|
||||
{
|
||||
constructor(public dialogRef: MatDialogRef<AccountComponent>, @Inject(MAT_DIALOG_DATA) public account: Account) {}
|
||||
selectedPicture: File;
|
||||
@ViewChild("accountImg") accountImg: ElementRef;
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<AccountComponent>, @Inject(MAT_DIALOG_DATA) public account: Account, private http: HttpClient) {}
|
||||
|
||||
finish()
|
||||
{
|
||||
let data = new FormData();
|
||||
data.append("email", this.account.email);
|
||||
data.append("username", this.account.username);
|
||||
data.append("picture", this.selectedPicture);
|
||||
|
||||
this.http.post("api/account/update", data).subscribe(() =>
|
||||
{
|
||||
this.dialogRef.close(this.account);
|
||||
});
|
||||
}
|
||||
|
||||
cancel()
|
||||
{
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onPictureSelected(event: any)
|
||||
{
|
||||
this.selectedPicture = event.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () =>
|
||||
{
|
||||
this.accountImg.nativeElement.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(this.selectedPicture);
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ import { ShowResolverService } from './services/show-resolver.service';
|
||||
import { StreamResolverService } from "./services/stream-resolver.service";
|
||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
||||
import {LoginComponent} from "./login/login.component";
|
||||
import {AccountComponent} from "./account/account.component";
|
||||
import {AuthenticatedGuard} from "./guards/authenticated-guard.service";
|
||||
import {UnauthorizedComponent} from "./unauthorized/unauthorized.component";
|
||||
import {LogoutComponent} from "./logout/logout.component";
|
||||
|
||||
|
@ -25,14 +25,12 @@
|
||||
</a>
|
||||
</li>
|
||||
<ng-template #accountDrop>
|
||||
<li class="nav-item">
|
||||
<button mat-icon-button [matMenuTriggerFor]="accountMenu" class="icon" matTooltipPosition="below" [matTooltip]="authManager.user.name">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<li class="nav-item icon" style="opacity: 1 !important;">
|
||||
<img matRipple [src]="authManager.user.picture" [matMenuTriggerFor]="accountMenu" class="profilePicture" matTooltipPosition="below" [matTooltip]="authManager.user.name" />
|
||||
</li>
|
||||
<mat-menu #accountMenu="matMenu">
|
||||
<button mat-menu-item (click)="this.openAccountDialog()">Settings</button>
|
||||
<button mat-menu-item (click)="this.authManager.logout()">Logout</button>
|
||||
<button class="dropButton" mat-menu-item (click)="this.openAccountDialog()">Settings</button>
|
||||
<button class="dropButton" mat-menu-item (click)="this.authManager.logout()">Logout</button>
|
||||
</mat-menu>
|
||||
</ng-template>
|
||||
</ul>
|
||||
|
@ -94,3 +94,17 @@ input::-webkit-search-cancel-button
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.profilePicture
|
||||
{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dropButton
|
||||
{
|
||||
outline: none;
|
||||
}
|
@ -19,7 +19,7 @@ export class AppComponent
|
||||
isLoading: boolean = false;
|
||||
account: Account;
|
||||
|
||||
constructor(http: HttpClient, private router: Router, private location: Location, private authManager: AuthService, public dialog: MatDialog)
|
||||
constructor(private http: HttpClient, private router: Router, private location: Location, private authManager: AuthService, public dialog: MatDialog)
|
||||
{
|
||||
http.get<Library[]>("api/libraries").subscribe(result =>
|
||||
{
|
||||
@ -81,7 +81,11 @@ export class AppComponent
|
||||
|
||||
openAccountDialog()
|
||||
{
|
||||
this.dialog.open(AccountComponent, {width: "500px", data: this.account});
|
||||
const dialog = this.dialog.open(AccountComponent, {width: "500px", data: this.account});
|
||||
dialog.afterClosed().subscribe((result: Account) =>
|
||||
{
|
||||
this.account = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,10 +38,11 @@ import {
|
||||
OpenIdConfiguration
|
||||
} from "angular-auth-oidc-client";
|
||||
import { AccountComponent } from './account/account.component';
|
||||
import {AuthenticatedGuard} from "./guards/authenticated-guard.service";
|
||||
import { UnauthorizedComponent } from './unauthorized/unauthorized.component';
|
||||
import { LogoutComponent } from './logout/logout.component';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {FallbackDirective} from "./misc/fallback.directive";
|
||||
import {AuthenticatedGuard} from "./misc/guards/authenticated-guard.service";
|
||||
|
||||
export function loadConfig(oidcConfigService: OidcConfigService)
|
||||
{
|
||||
@ -64,7 +65,8 @@ export function loadConfig(oidcConfigService: OidcConfigService)
|
||||
PasswordValidator,
|
||||
AccountComponent,
|
||||
UnauthorizedComponent,
|
||||
LogoutComponent
|
||||
LogoutComponent,
|
||||
FallbackDirective
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
18
src/app/misc/fallback.directive.ts
Normal file
18
src/app/misc/fallback.directive.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {Directive, ElementRef, HostListener, Input} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: 'img[fallback]'
|
||||
})
|
||||
export class FallbackDirective
|
||||
{
|
||||
@Input() fallback: string;
|
||||
|
||||
constructor(private img: ElementRef) { }
|
||||
|
||||
@HostListener("error")
|
||||
onError()
|
||||
{
|
||||
const html: HTMLImageElement = this.img.nativeElement;
|
||||
html.src = this.fallback;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import {
|
||||
Router
|
||||
} from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import {AuthService} from "../services/auth.service";
|
||||
import {AuthService} from "../../services/auth.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
@ -57,6 +57,6 @@ export class AuthService
|
||||
if (!this.isAuthenticated)
|
||||
return null;
|
||||
console.log(this.user);
|
||||
return {email: this.user.email, username: this.user.username};
|
||||
return {email: this.user.email, username: this.user.username, picture: this.user.picture};
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ export interface Account
|
||||
{
|
||||
username: string;
|
||||
email: string;
|
||||
picture: string;
|
||||
}
|
||||
|
20
src/test.ts
20
src/test.ts
@ -1,20 +0,0 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
1
static/account.svg
Normal file
1
static/account.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
After Width: | Height: | Size: 365 B |
Loading…
x
Reference in New Issue
Block a user