mirror of
https://github.com/fergalmoran/podnoms.git
synced 2025-12-25 18:58:12 +00:00
Added right side overlay
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,5 @@ export class SidebarComponent {
|
||||
this._store.dispatch(new fromPodcastActions.GetAction(podcast.slug));
|
||||
return false;
|
||||
}
|
||||
deletePodcast(podcast) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>(
|
||||
|
||||
14
client/src/app/services/ui-state.service.ts
Normal file
14
client/src/app/services/ui-state.service.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user