Added right side overlay

This commit is contained in:
Fergal Moran
2018-04-27 00:03:33 +01:00
parent 7df296a5d6
commit f220292e24
19 changed files with 178 additions and 32 deletions

View File

@@ -1,9 +1,9 @@
<div *ngIf="!loggedIn()">
<router-outlet></router-outlet>
</div>
<div id="page-container"
class="sidebar-o side-scroll side-trans-enabled"
<div id="page-container" class="sidebar-o side-scroll side-trans-enabled" [ngClass]="overlayOpen ? 'side-overlay-o' : ''"
*ngIf="loggedIn()">
<app-side-overlay></app-side-overlay>
<app-sidebar></app-sidebar>
<app-navbar></app-navbar>
<main id="main-container">

View File

@@ -7,6 +7,7 @@ import { AppInsightsService } from 'app/services/app-insights.service';
import { SignalRService } from 'app/services/signalr.service';
import { ProfileService } from './services/profile.service';
import { MessagingService } from 'app/services/messaging.service';
import { UiStateService } from 'app/services/ui-state.service';
@Component({
selector: 'app-root',
@@ -14,39 +15,40 @@ import { MessagingService } from 'app/services/messaging.service';
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
overlayOpen: boolean = false;
constructor(
private _authService: PodnomsAuthService,
private _toastyService: ToastyService,
private _signalrService: SignalRService,
private _profileService: ProfileService,
private _messagingService: MessagingService,
private _uiStateService: UiStateService,
_appInsights: AppInsightsService
) {
}
) {}
loggedIn() {
return this._authService.isAuthenticated();
}
ngOnInit() {
// this._pushNotifications.requestPermissions();
this._uiStateService.change.subscribe((r) => (this.overlayOpen = r));
if (this.loggedIn()) {
const user = this._profileService.getProfile().subscribe(u => {
const user = this._profileService.getProfile().subscribe((u) => {
if (u) {
this._messagingService.getPermission();
this._messagingService.receiveMessage();
const chatterChannel = `${u.uid}_chatter`;
this._signalrService
.init('chatter')
.then(r => {
.then((r) => {
this._signalrService.connection.on(
chatterChannel,
result => {
(result) => {
this._toastyService.info(result);
}
);
})
.catch(err => {
.catch((err) => {
console.error(
'app.component',
'Unable to initialise chatter hub',

View File

@@ -67,6 +67,8 @@ import { AudioService } from 'app/services/audio.service';
import { HumaniseTimePipe } from './pipes/humanise-time.pipe';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { PodNomsApiInterceptor } from './interceptors/podnoms-api.interceptor';
import { SideOverlayComponent } from './components/side-overlay/side-overlay.component';
import { UiStateService } from './services/ui-state.service';
let config = new AuthServiceConfig([
{
@@ -106,7 +108,8 @@ export function provideConfig() {
AboutComponent,
FooterComponent,
FooterPlayerComponent,
HumaniseTimePipe
HumaniseTimePipe,
SideOverlayComponent
],
imports: [
BrowserModule,
@@ -146,6 +149,7 @@ export function provideConfig() {
],
providers: [
PodnomsAuthService,
UiStateService,
AuthGuard,
{
provide: HTTP_INTERCEPTORS,

View File

@@ -9,23 +9,25 @@
</a> -->
</div>
<div class="content-header-section" *ngIf="user$ | async; let user; else loading">
<img [src]="user.profileImage"
class="rounded-circle profile-img">
<div class="btn-group"
role="group">
<button type="button"
class="btn btn-rounded btn-dual-secondary"
id="page-header-user-dropdown"
data-toggle="dropdown">
{{user.name}}<i class="fa fa-angle-down ml-5"></i>
<img [src]="user.profileImage" class="rounded-circle profile-img">
<div class="btn-group" role="group">
<button type="button" class="btn btn-rounded btn-dual-secondary" id="page-header-user-dropdown" data-toggle="dropdown">
{{user.name}}
<i class="fa fa-angle-down ml-5"></i>
</button>
<div class="dropdown-menu dropdown-menu-right min-width-150">
<a class="dropdown-item" [routerLink]="['/profile']"><i class="login-user mr-5"></i> Profile</a>
<a class="dropdown-item"
(click)="logout()"><i class="login-logout mr-5"></i> Logout</a>
<a class="dropdown-item" [routerLink]="['/profile']">
<i class="login-user mr-5"></i> Profile</a>
<a class="dropdown-item" (click)="logout()">
<i class="login-logout mr-5"></i> Logout</a>
</div>
</div>
<button type="button" class="btn btn-circle btn-dual-secondary" (click)="toggleOverlay()">
<i class="fa fa-tasks"></i>
</button>
</div>
<ng-template #loading><i class="fa fa-sun-o fa-spin text-white"></i></ng-template>
<ng-template #loading>
<i class="fa fa-sun-o fa-spin text-white"></i>
</ng-template>
</div>
</header>

View File

@@ -3,6 +3,7 @@ import { Component, OnInit } from '@angular/core';
import { PodnomsAuthService } from '../../services/podnoms-auth.service';
import { ProfileService } from '../../services/profile.service';
import { Observable } from 'rxjs/Observable';
import { UiStateService } from '../../services/ui-state.service';
@Component({
selector: 'app-navbar',
@@ -14,7 +15,8 @@ export class NavbarComponent implements OnInit {
constructor(
private _authService: PodnomsAuthService,
private _profileService: ProfileService
private _profileService: ProfileService,
private _uiStateService: UiStateService
) {}
ngOnInit() {
@@ -22,7 +24,9 @@ export class NavbarComponent implements OnInit {
this.user$ = this._profileService.getProfile();
}
}
toggleOverlay() {
this._uiStateService.toggleOverlay();
}
logout() {
this._authService.logout();
}

View File

@@ -0,0 +1,48 @@
<aside id="side-overlay">
<div class="slimScrollDiv">
<div class="content-header content-header-fullrow">
<div class="content-header-section align-parent">
<button type="button" class="btn btn-circle btn-dual-secondary align-v-r" (click)="toggleOverlay()">
<i class="fa fa-times text-danger"></i>
</button>
<div class="content-header-item" *ngIf="user$ | async; let user">
<a class="img-link mr-5" [routerLink]="['/profile']">
<img class="img-avatar img-avatar32" [src]="user.profileImage" alt="">
</a>
<a class="align-middle link-effect text-primary-dark font-w600" [routerLink]="['/profile']">{{user.name}}</a>
</div>
</div>
</div>
<div class="content-side">
<div class="block pull-r-l">
<div class="block-header bg-body-light">
<h3 class="block-title">
<i class="fa fa-fw fa-clock-o font-size-default mr-5"></i>Episodes
</h3>
<div class="block-options">
<button type="button" class="btn-block-option">
<i class="si si-refresh"></i>
</button>
<!-- <button type="button" class="btn-block-option" data-toggle="block-option" data-action="content_toggle">
<i class="si si-arrow-up"></i>
</button> -->
</div>
</div>
<div class="block-content">
<ul class="nav-users push">
<li *ngFor="let entry of entries$ | async| orderBy: '-id'">
<a [routerLink]="['/podcasts', entry.podcast.slug]">
<img class="img-avatar" [src]="entry.imageUrl" alt="">
<i class="fa fa-circle text-success"></i> {{entry.podcast.title}}
<div class="font-w400 font-size-xs text-muted" style="margin-right: 15px;">{{entry.title}}</div>
<button type="button" class="btn btn-circle btn-dual-secondary align-v-r" (click)="playAudio(entry)">
<i class="si si-control-play text-danger"></i>
</button>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>

View File

@@ -0,0 +1,40 @@
import { Component, OnInit } from '@angular/core';
import { UiStateService } from '../../services/ui-state.service';
import { ProfileService } from 'app/services/profile.service';
import { ProfileModel } from 'app/models/profile.model';
import { PodcastEntryModel } from 'app/models/podcasts.models';
import { Observable } from 'rxjs/Observable';
import { PodcastService } from 'app/services/podcast.service';
import { AudioService } from 'app/services/audio.service';
@Component({
selector: 'app-side-overlay',
templateUrl: './side-overlay.component.html',
styleUrls: ['./side-overlay.component.css']
})
export class SideOverlayComponent implements OnInit {
user$: Observable<ProfileModel>;
entries$: Observable<PodcastEntryModel[]>;
constructor(
private _profileService: ProfileService,
private _podcastService: PodcastService,
private _uiStateService: UiStateService,
private _audioService: AudioService
) {}
ngOnInit() {
this._uiStateService.change.subscribe((r) => {
if (r) {
this.user$ = this._profileService.getProfile();
this.entries$ = this._podcastService.getAllEntriesForUser();
}
});
}
toggleOverlay() {
this._uiStateService.toggleOverlay();
}
playAudio(entry: PodcastEntryModel) {
this._audioService.playAudio(entry.audioUrl, entry.title);
}
}

View File

@@ -29,7 +29,5 @@ export class SidebarComponent {
this._store.dispatch(new fromPodcastActions.GetAction(podcast.slug));
return false;
}
deletePodcast(podcast) {
}
}

View File

@@ -1,4 +1,3 @@
export class PodcastModel {
id?: number;
title: string;
@@ -23,7 +22,7 @@ export class PodcastEntryModel {
processed: boolean;
processingStatus?: string;
processingPayload?: string;
podcast?: PodcastModel;
constructor(podcastId?: number, sourceUrl?: string) {
this.podcastId = podcastId;
this.sourceUrl = sourceUrl;

View File

@@ -41,8 +41,14 @@ export class PodcastService {
}
//#endregion
//#region Entries
getEntries(slug: string): any {
return this._http.get(environment.API_HOST + '/entry/all/' + slug);
getAllEntriesForUser(): Observable<PodcastEntryModel[]> {
return this._http.get<PodcastEntryModel[]>(
environment.API_HOST + '/entry/users/'
);
}
getEntries(slug: string): Observable<PodcastEntryModel[]> {
return this._http.get<PodcastEntryModel[]>(environment.API_HOST + '/entry/all/' + slug);
}
addEntry(entry: PodcastEntryModel): Observable<PodcastEntryModel> {
return this._http.post<PodcastEntryModel>(

View File

@@ -0,0 +1,14 @@
import { Injectable, Output, EventEmitter } from '@angular/core';
@Injectable()
export class UiStateService {
overlayOpen: boolean = false;
@Output() change: EventEmitter<boolean> = new EventEmitter();
constructor() {}
toggleOverlay() {
this.overlayOpen = !this.overlayOpen;
this.change.emit(this.overlayOpen);
}
}

View File

@@ -50,6 +50,8 @@ namespace PodNoms.Api.Controllers {
// check the credentials
if (await _userManager.CheckPasswordAsync(userToVerify, password)) {
await _userManager.UpdateAsync(userToVerify);
return await Task.FromResult(_jwtFactory.GenerateClaimsIdentity(userName, userToVerify.Id));
}

View File

@@ -64,6 +64,15 @@ namespace PodNoms.Api.Controllers {
}
}
[HttpGet("users")]
public async Task<IActionResult> GetAllForUser() {
var entries = await _repository.GetAllForUserAsync(_applicationUser.Id);
var results = _mapper.Map<List<PodcastEntry>, List<PodcastEntryViewModel>>(
entries.OrderByDescending(e => e.Id).ToList()
);
return Ok(results);
}
[HttpGet("all/{podcastSlug}")]
public async Task<IActionResult> GetAllForSlug(string podcastSlug) {
var entries = await _repository.GetAllAsync(podcastSlug);

View File

@@ -17,5 +17,6 @@ namespace PodNoms.Api.Models.ViewModels {
public string ProcessingStatus { get; set; }
public bool Processed { get; set; }
public string ProcessingPayload { get; set; }
public PodcastViewModel Podcast { get; set; }
}
}

View File

@@ -37,6 +37,14 @@ namespace PodNoms.Api.Persistence {
.ToListAsync();
return entries;
}
public async Task<IEnumerable<PodcastEntry>> GetAllForUserAsync(string userId) {
var entries = await _context.PodcastEntries
.Where(e => e.Podcast.AppUser.Id == userId)
.Include(e => e.Podcast)
.ToListAsync();
return entries;
}
public async Task<PodcastEntry> AddOrUpdateAsync(PodcastEntry entry) {
if (entry.Id != 0) {
// _context.Entry(entry).State = EntityState.Modified

View File

@@ -8,6 +8,7 @@ namespace PodNoms.Api.Persistence {
Task<PodcastEntry> GetByUidAsync(string uid);
Task<IEnumerable<PodcastEntry>> GetAllAsync(int podcastId);
Task<IEnumerable<PodcastEntry>> GetAllAsync(string podcastSlug);
Task<IEnumerable<PodcastEntry>> GetAllForUserAsync(string userId);
Task<PodcastEntry> AddOrUpdateAsync(PodcastEntry entry);
Task DeleteAsync(int id);
}

View File

@@ -31,14 +31,22 @@ namespace PodNoms.Api.Services.Auth {
}
public override async Task<IdentityResult> CreateAsync(ApplicationUser user) {
_slugify(user);
_checkName(user);
await _imageify(user);
return await base.CreateAsync(user);
}
public override async Task<IdentityResult> UpdateAsync(ApplicationUser user) {
_slugify(user);
_checkName(user);
await _imageify(user);
return await base.UpdateAsync(user);
}
private void _checkName(ApplicationUser user) {
if (string.IsNullOrEmpty(user.FirstName)) {
user.FirstName = "PodNoms";
user.LastName = "User";
}
}
private async Task _imageify(ApplicationUser user) {
if (string.IsNullOrEmpty(user.PictureUrl)) {