Skip to content

Commit

Permalink
Reduce network/memory load by not pre-fetching programs (#57)
Browse files Browse the repository at this point in the history
* lazy load programs

* Do not preload programs

* Re-implementation of adding a program as fav from program details

* ProgramFavoritesComponent

* Program favorites

* test

* test

* Bugfix title on audioplayer
  • Loading branch information
LarsBergqvist authored Nov 25, 2023
1 parent 8a8a446 commit 42d8d7b
Show file tree
Hide file tree
Showing 18 changed files with 308 additions and 160 deletions.
1 change: 0 additions & 1 deletion .github/workflows/buildanddeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ jobs:
deploy:
needs: test-and-build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
4 changes: 3 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { LoggingService } from './services/logging.service';
import { MessageBrokerService } from './services/message-broker.service';
import { TranslatePipe } from './translations/translate.pipe';
import { SelectButtonModule } from 'primeng/selectbutton';
import { ProgramFavoritesComponent } from './components/programs/program-favorites.component';

registerLocaleData(locale);

Expand All @@ -54,7 +55,8 @@ registerLocaleData(locale);
ProgramDetailsComponent,
EpisodesListComponent,
EpisodesTableComponent,
EpisodesBookmarksComponent
EpisodesBookmarksComponent,
ProgramFavoritesComponent
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="audio-wrapper">
<div *ngIf="currentUrlToPlay; else empty" [ngClass]="{'scroll-left': isPlaying === true }" class="now-playing"
<div *ngIf="currentUrlToPlay; else empty" class="now-playing"
(click)="onOpenDetails()">
<span class="clickable">
{{this.prefixText}}<b>{{currentStation}}</b>
Expand Down
1 change: 1 addition & 0 deletions src/app/components/audio-player/audio-player.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class AudioPlayerComponent implements OnInit {
)
.subscribe(async (message: PlayAudioMessage) => {
if (!message) return;
if (!message.url) return;
this.episodeId = message.episodeId;
this.channelId = message.channelId;
if (this.episodeId) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/channels/channel-schedule.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class ChannelScheduleComponent implements OnInit, OnDestroy {
endtimeDate: convertFromJSONstring(s?.endtimeutc),
program: s.program,
imageurltemplate: s.imageurltemplate,
imageurl: s.imageurltemplate ? s.imageurltemplate + SRApiService.DefaultImagePreset : this.srApiService.getProgramImageUrlFromId(s.program.id)
imageurl: s.imageurltemplate ? s.imageurltemplate + SRApiService.DefaultImagePreset : this.srApiService.getChannelImageUrlFromId(channelId)
}));
}

Expand Down
9 changes: 6 additions & 3 deletions src/app/components/programs/program-details.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { MessageBrokerService } from 'src/app/services/message-broker.service';
import { SRApiService } from 'src/app/services/srapi.service';
import { EpisodeViewModel } from '../episodes/episode-viewmodel';
import { EpisodesLoadLazyArgs } from '../episodes/episodes-table.component';
import { ProgramsService } from 'src/app/services/programs.service';

@Component({
selector: 'app-program-details',
Expand All @@ -26,7 +27,8 @@ export class ProgramDetailsComponent implements OnInit, OnDestroy {
private readonly service: EpisodesService,
private readonly srApiService: SRApiService,
private readonly activatedRoute: ActivatedRoute,
private readonly broker: MessageBrokerService
private readonly broker: MessageBrokerService,
private readonly programsService: ProgramsService,
) {}

async ngOnInit() {
Expand All @@ -36,7 +38,7 @@ export class ProgramDetailsComponent implements OnInit, OnDestroy {
map((route) => route.id)
)
.subscribe(async (id: string) => {
const program = await this.srApiService.getProgramFromId(parseInt(id));
const program = await this.programsService.fetchProgramWithId(parseInt(id));
if (program) {
await this.show(program);
}
Expand Down Expand Up @@ -80,9 +82,10 @@ export class ProgramDetailsComponent implements OnInit, OnDestroy {

onAddToFavorites(programId: number, programName: string) {
this.srApiService.addProgramToFavorites(programId, programName);
this.program.fav = true;
}

onRemoveFromFavorites(programId: number, programName: string) {
this.srApiService.removeProgramFromFavorites(programId, programName);
this.program.fav = false;
}
}
52 changes: 52 additions & 0 deletions src/app/components/programs/program-favorites.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<p-accordion>
<p-accordionTab header="{{ 'Favorites' | translate }}">
<p-table #dt2 [value]="programs" responsiveLayout="stack" [lazy]="false"
[globalFilterFields]="['name','channel.name','description']" styleClass="sr-table">
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="name">{{'ProgramNameTitle' | translate | uppercase }}<p-sortIcon field="name">
</p-sortIcon>
</th>
<th pSortableColumn="channel.name">{{'ChannelNameTitle' | translate | uppercase }}<p-sortIcon
field="channel.name">
</p-sortIcon>
</th>

<th>{{'Description' | translate | uppercase}}</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-program>
<tr>
<td>
<div class="p-grid" style="width:100%">
<span (click)="onProgramDetails(program)" class="clickable p-col-8">
<img src={{program.programimage}} height="50" width="50"
style="padding: 4px; vertical-align:middle" />
<span *ngIf="program?.channel" class="prg-chan">
{{program.channel.name}}
</span>
<span class="prg-title">
{{program.name}}
</span>
</span>
<span class="p-col-4" style="float:right;padding-top:0px">
<span class="clickable" (click)="onProgramDetails(program)"><i
class="pi pi-info-circle details-btn"></i>
</span>
</span>
</div>
</td>
<td class="prg-chan-td">
<div *ngIf="program?.channel">
{{program.channel.name}}
</div>
</td>
<td class="description-td" style="border:none">
{{program.description}}
</td>
</tr>
</ng-template>
</p-table>

</p-accordionTab>
</p-accordion>
11 changes: 11 additions & 0 deletions src/app/components/programs/program-favorites.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.category {
margin-top: 10px;
}
:host ::ng-deep {
.p-datatable .p-datatable-header {
display: none;
}
.ep-desc {
display: none !important;
}
}
47 changes: 47 additions & 0 deletions src/app/components/programs/program-favorites.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { FavoriteChangedMessage } from 'src/app/messages/favorite-changed.message';
import { ShowProgramDetailsMessage } from 'src/app/messages/show-programdetails.message';
import { Program } from 'src/app/models/program';
import { MessageBrokerService } from 'src/app/services/message-broker.service';
import { ProgramsService } from 'src/app/services/programs.service';

@Component({
selector: 'app-program-favorites',
templateUrl: './program-favorites.component.html',
styleUrls: ['./program-favorites.component.scss']
})
export class ProgramFavoritesComponent implements OnInit {
programs: Program[];
totalHits = 0;
pageSize = 100;
private unsubscribe$ = new Subject();

constructor(
private readonly service: ProgramsService,
private readonly broker: MessageBrokerService
) {}

ngOnInit(): void {
const messages = this.broker.getMessage();
messages
.pipe(
takeUntil(this.unsubscribe$),
filter((message) => message instanceof FavoriteChangedMessage)
)
.subscribe((message: FavoriteChangedMessage) => {
this.fetch();
});

this.fetch();
}

async fetch() {
this.programs = await this.service.fetchAllFavoritePrograms();
}

onProgramDetails(program: Program) {
this.broker.sendMessage(new ShowProgramDetailsMessage(program.id));
}
}
28 changes: 5 additions & 23 deletions src/app/components/programs/programs-list.component.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
<app-program-favorites></app-program-favorites>

<div class="programs-list">
<p-table #dt2 [value]="programs" responsiveLayout="stack" [paginator]="true" [rows]="10"
<p-table #dt2 [value]="programs" responsiveLayout="stack" [paginator]="true" [rows]="pageSize" [totalRecords]="totalHits" (onLazyLoad)="loadLazy($event)" [lazy]="true"
[globalFilterFields]="['name','channel.name','description']" styleClass="sr-table">
<ng-template pTemplate="caption">
<span class="p-d-flex">
<div [ngStyle]="{'margin-bottom': '10px' }">
<span class="fav-chk">
<p-checkbox (onChange)="onFilterFavClicked($event, dt2)" [(ngModel)]="localState.showOnlyFavs"
[binary]="true" label="{{'OnlyFavs' | translate }}" class="fav-checkbox"></p-checkbox>
</span>
<span class="p-input-icon-left p-ml-auto">
<i class="pi pi-search"></i>
<input pInputText class="p-inputtext-sm kw-input" type="text" [(ngModel)]="localState.searchString"
(input)="dt2.filterGlobal($event.target.value, 'contains')"
placeholder="{{'SearchKeyword' | translate | uppercase}}" />
</span>
</div>

<p-accordion [activeIndex]="0">
<p-accordionTab header="{{'Categories' | translate}}">
<p-selectButton [options]="categoryOptions" [(ngModel)]="localState.selectedCategory" optionLabel="label" optionValue="value"
Expand Down Expand Up @@ -43,7 +32,7 @@
<tr>
<td>
<div class="p-grid" style="width:100%">
<span (click)="onShowEpisodes(program)" class="clickable p-col-8">
<span (click)="onProgramDetails(program)" class="clickable p-col-8">
<img src={{program.programimage}} height="50" width="50"
style="padding: 4px; vertical-align:middle" />
<span *ngIf="program?.channel" class="prg-chan">
Expand All @@ -54,16 +43,9 @@
</span>
</span>
<span class="p-col-4" style="float:right;padding-top:0px">
<span class="clickable" (click)="onShowEpisodes(program)"><i
<span class="clickable" (click)="onProgramDetails(program)"><i
class="pi pi-info-circle details-btn"></i>
</span>
<span *ngIf="!program.fav" class="clickable"
(click)="onAddToFavorites(program.id, program.name)"><i class="pi pi-star fav-but details-btn"></i>
</span>
<span *ngIf="program.fav" class="clickable"
(click)="onRemoveFromFavorites(program.id, program.name)"><i
class="pi pi-star-fill yellow-fav details-btn"></i>
</span>
</span>
</div>
</td>
Expand Down
60 changes: 20 additions & 40 deletions src/app/components/programs/programs-list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,30 @@ import { MessageBrokerService } from 'src/app/services/message-broker.service';
import { SRApiService } from 'src/app/services/srapi.service';
import { TranslationService } from 'src/app/services/translation.service';
import { Program } from '../../models/program';
import { ProgramsService } from 'src/app/services/programs.service';

@Component({
selector: 'app-programs-list',
templateUrl: './programs-list.component.html',
styleUrls: ['../common/datatable-styling.scss', './programs-list.component.scss']
})
export class ProgramsListComponent implements OnInit, OnDestroy, AfterViewInit {
totalHits = 0;
pageSize = 10;
programs: Program[];
categoryOptions: SelectItem[];
private unsubscribe$ = new Subject();

private readonly storageId = 'ProgramsListState';
localState = {
showOnlyFavs: false,
selectedCategory: null,
searchString: ''
};

@ViewChild(Table) tableComponent: Table;

constructor(
private readonly srApiService: SRApiService,
private readonly programsService: ProgramsService,
private readonly translationService: TranslationService,
private readonly broker: MessageBrokerService,
private readonly storage: LocalStorageService
Expand All @@ -43,12 +45,6 @@ export class ProgramsListComponent implements OnInit, OnDestroy, AfterViewInit {
this.localState = oldState;
}

this.srApiService.programs$.pipe(takeUntil(this.unsubscribe$)).subscribe((values: Program[]) => {
if (values) {
this.programs = [];
this.programs.push(...values);
}
});
this.srApiService.programCategories$.pipe(takeUntil(this.unsubscribe$)).subscribe((values: ProgramCategory[]) => {
if (values) {
this.categoryOptions = [];
Expand All @@ -57,24 +53,24 @@ export class ProgramsListComponent implements OnInit, OnDestroy, AfterViewInit {
this.categoryOptions.push(...categories);
}
});

await this.fetch(0);
}

async loadLazy(event: any) {
await this.fetch(event.first);
}

ngAfterViewInit(): void {
if (this.tableComponent) {
if (this.localState.showOnlyFavs) {
this.tableComponent.filter(true, 'fav', 'equals');
} else {
this.tableComponent.filter([true, false], 'fav', 'in');
}
async fetch(first: number) {
const page = first / this.pageSize + 1;
let result = await this.programsService.fetchPrograms(page,this.pageSize, this.localState.selectedCategory);
this.totalHits = result.pagination.totalhits;
this.programs = result.programs;
}

if (this.localState.selectedCategory) {
this.tableComponent.filter(this.localState.selectedCategory, 'programcategory.id', 'equals');
}

if (this.localState.searchString) {
this.tableComponent.filterGlobal(this.localState.searchString, 'contains');
}
}
ngAfterViewInit(): void {

}

ngOnDestroy() {
Expand All @@ -83,30 +79,14 @@ export class ProgramsListComponent implements OnInit, OnDestroy, AfterViewInit {
this.unsubscribe$.complete();
}

onShowEpisodes(program: Program) {
onProgramDetails(program: Program) {
this.broker.sendMessage(new ShowProgramDetailsMessage(program.id));
}

onFilterFavClicked(event, dt) {
this.localState.showOnlyFavs = event.checked;
if (this.localState.showOnlyFavs) {
dt.filter(true, 'fav', 'equals');
} else {
dt.filter([true, false], 'fav', 'in');
}
}

onAddToFavorites(programId: number, programName: string) {
this.srApiService.addProgramToFavorites(programId, programName);
}

onRemoveFromFavorites(programId: number, programName: string) {
this.srApiService.removeProgramFromFavorites(programId, programName);
}

onCategoryChanged(event, dt) {
if (event.value !== '') {
dt.filter(event.value, 'programcategory.id', 'equals');
dt.filter(event.value);
}
}
}
15 changes: 15 additions & 0 deletions src/app/messages/favorite-changed.message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Message } from './message';

export class FavoriteChangedMessage extends Message {
programId: number;
isFavorite: boolean;
constructor(programId: number, isFavorite: boolean) {
super();
this.programId = programId;
this.isFavorite = isFavorite;
}

get Type(): string {
return 'FavoriteChangedMessage';
}
}
Loading

0 comments on commit 42d8d7b

Please sign in to comment.