Skip to content

Commit 757f4b2

Browse files
authored
Merge pull request #43 from byrdsandbytes/polishing/touching-everything
Polishing/touching everything
2 parents 8577863 + 1ac7475 commit 757f4b2

43 files changed

Lines changed: 938 additions & 323 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/app/app.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class AppComponent {
1717
}
1818

1919
initializeApp() {
20+
this.snapcastService.connect();
2021
App.addListener('appStateChange', (state) => {
2122
// NOTE: This is my backup plan if state is still not refreshed
2223
this.ngZone.run(() => {

src/app/app.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { HttpClientModule } from '@angular/common/http';
1313
imports: [BrowserModule, HttpClientModule, IonicModule.forRoot(
1414
{
1515
mode: 'ios', // Set the mode to 'ios' for iOS style
16+
swipeBackEnabled: false, // Disable swipe back gesture
1617

1718
}
1819
), AppRoutingModule],

src/app/components/camilla-dsp/camilla-dsp.component.html

Lines changed: 205 additions & 154 deletions
Large diffs are not rendered by default.

src/app/components/camilla-dsp/camilla-dsp.component.scss

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,26 @@
1919
}
2020

2121
.meter-group {
22-
background: #1a1a1a;
22+
background: var(--ion-color-primary);
2323
padding: 1.5rem;
24-
border: 1px solid #000;
25-
border-radius: 4px;
26-
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.8);
24+
// border: 1px solid #000;
25+
// border-radius: 4px;
26+
// box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.8);
2727
position: relative;
2828

2929
h3 {
3030
font-size: 0.8rem;
31-
text-transform: uppercase;
31+
// text-transform: uppercase;
3232
letter-spacing: 2px;
3333
margin-bottom: 1.5rem;
3434
color: #ccc;
3535
text-align: center;
3636
text-shadow: 0 2px 4px rgba(0, 0, 0, 1);
37-
background-color: #111;
37+
// background-color: #111;
3838
display: inline-block;
3939
padding: 4px 12px;
4040
border-radius: 2px;
41-
border: 1px solid #333;
41+
// border: 1px solid #333;
4242
width: 100%;
4343
}
4444
}
@@ -56,21 +56,21 @@
5656

5757
.channel-name {
5858
margin-bottom: 0.8rem;
59-
font-weight: 700;
59+
// font-weight: 700;
6060
font-size: 0.75rem;
61-
color: #888;
62-
text-transform: uppercase;
61+
// color: #888;
62+
// text-transform: uppercase;
6363
letter-spacing: 1px;
6464
}
6565

6666
.sub-channel {
6767
display: flex;
6868
gap: 12px;
6969
padding: 12px;
70-
background: #0d0d0d;
70+
// background: #0d0d0d;
7171
border-radius: 2px;
72-
box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.05);
73-
border: 1px solid #333;
72+
// box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.05);
73+
// border: 1px solid #333;
7474
}
7575

7676
.meter-column {
@@ -154,3 +154,19 @@
154154
font-weight: bold;
155155
}
156156
}
157+
158+
ion-card {
159+
// border-radius: 0px;
160+
// --ion-border-radius: 0px;
161+
box-shadow: none;
162+
border: 1px solid var(--ion-color-light);
163+
164+
}
165+
166+
ion-list-header {
167+
border-radius: 8px 8px 0 0;
168+
169+
border: 1px solid var(--ion-color-light);
170+
}
171+
172+

src/app/components/player-toolbar/player-toolbar.component.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,9 @@ <h2> {{ stream.properties.metadata.title }}</h2>
4747
</ng-container>
4848
</ng-container>
4949

50+
<div class="spacer">
51+
52+
</div>
53+
5054
</ion-toolbar>
5155
</ion-content>

src/app/components/player-toolbar/player-toolbar.component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@
2424
.volume-modal{
2525
z-index: 1 !important;
2626
}
27+
28+
.spacer{
29+
height: 60px;
30+
}
2731

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,69 @@
11
<div class="snapcast-container">
2-
<h2>Snapcast Server Status</h2>
32

4-
<h3>JSON Server Response:</h3>
3+
<ng-container *ngIf="(displayState | async) as state; else loadingOrError">
4+
<ion-list>
5+
<ion-item>
6+
<ion-label>
7+
<h3>Host Name</h3>
8+
<p>{{ state.server.server.host.name }}</p>
9+
</ion-label>
10+
</ion-item>
11+
<ion-item>
12+
<ion-label>
13+
<h3>IP Address</h3>
14+
<p>{{ state.server.server.host.ip }}</p>
15+
</ion-label>
16+
</ion-item>
17+
<ion-item>
18+
<ion-label>
19+
<h3>OS / Arch</h3>
20+
<p>{{ state.server.server.host.os }} / {{ state.server.server.host.arch }}</p>
21+
</ion-label>
22+
</ion-item>
23+
<ion-item>
24+
<ion-label>
25+
<h3>Snapserver Version</h3>
26+
<p>{{ state.server.server.snapserver.version }}</p>
27+
</ion-label>
28+
</ion-item>
29+
<ion-item>
30+
<ion-label>
31+
<h3>Groups</h3>
32+
<p>{{ state.server.groups.length }} Groups</p>
33+
</ion-label>
34+
</ion-item>
35+
<ion-item>
36+
<ion-label>
37+
<h3>Clients</h3>
38+
<p *ngIf="getClientStats(state) as stats">
39+
{{ stats.total }} Total ({{ stats.online }} Online, {{ stats.offline }} Offline)
40+
</p>
41+
</ion-label>
42+
<ion-button slot="end" fill="outline" (click)="cleanOfflineClients()">Clean Offline Clients</ion-button>
543

6-
<div *ngIf="(displayState |async) as state" class="key-value-container">
44+
</ion-item>
45+
<ion-item>
46+
<ion-label>
47+
<h3>Streams</h3>
48+
<p>{{ state.server.streams.length }} Streams</p>
49+
</ion-label>
50+
</ion-item>
51+
</ion-list>
752

8-
<pre>{{ state| json }}</pre>
9-
10-
</div>
53+
<ion-accordion-group>
54+
<ion-accordion value="json">
55+
<ion-item slot="header" color="light">
56+
<ion-label>JSON Server Response</ion-label>
57+
</ion-item>
58+
<div class="ion-padding" slot="content" style="overflow-x: auto;">
59+
<pre>{{ state | json }}</pre>
60+
</div>
61+
</ion-accordion>
62+
</ion-accordion-group>
63+
</ng-container>
1164

1265
<ng-template #loadingOrError>
1366
<p>Loading Snapcast status or WebSocket not connected...</p>
14-
<button type="button" (click)="snapcastService.connect()">Attempt to Connect</button>
67+
<ion-button (click)="snapcastService.connect()">Attempt to Connect</ion-button>
1568
</ng-template>
1669
</div>

src/app/components/snapcast-status/snapcast-status.component.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
2-
import { Observable, Subscription, EMPTY } from 'rxjs';
3-
import { catchError, tap } from 'rxjs/operators';
2+
import { Observable, Subscription, EMPTY, firstValueFrom } from 'rxjs';
3+
import { catchError, first, tap } from 'rxjs/operators';
44
import { SnapcastService } from 'src/app/services/snapcast.service'; // Adjust path
55
import { Group, Client, Stream, ServerDetail, SnapCastServerStatusResponse } from 'src/app/model/snapcast.model'; // Adjust path
66

@@ -26,6 +26,22 @@ export class SnapcastStatusComponent implements OnInit, OnDestroy {
2626

2727
}
2828

29+
getClientStats(state: SnapCastServerStatusResponse): { total: number, online: number, offline: number } {
30+
if (!state?.server?.groups) return { total: 0, online: 0, offline: 0 };
31+
32+
let total = 0;
33+
let online = 0;
34+
35+
state.server.groups.forEach(group => {
36+
group.clients?.forEach(client => {
37+
total++;
38+
if (client.connected) online++;
39+
});
40+
});
41+
42+
return { total, online, offline: total - online };
43+
}
44+
2945

3046

3147
onSetClientVolumePercent(clientId: string, event: Event): void {
@@ -42,6 +58,32 @@ export class SnapcastStatusComponent implements OnInit, OnDestroy {
4258
).subscribe()
4359
);
4460
}
61+
62+
// Clean offline clients and empty groups if needed. Maybe not the best place to do this, but it works for now.
63+
async cleanOfflineClients(): Promise<void> {
64+
const state = await firstValueFrom(this.displayState!);
65+
if(!state) return;
66+
67+
const offlineClientIds: string[] = [];
68+
69+
state.server.groups.forEach(group => {
70+
group.clients?.forEach(client => {
71+
if (!client.connected) {
72+
offlineClientIds.push(client.id);
73+
}
74+
});
75+
});
76+
77+
for (const clientId of offlineClientIds) {
78+
try {
79+
await this.snapcastService.deleteServerClient(clientId);
80+
console.log(`Deleted offline client ${clientId}`);
81+
} catch (err) {
82+
console.error(`Failed to delete offline client ${clientId}`, err);
83+
}
84+
}
85+
}
86+
4587
ngOnDestroy(): void {
4688
this.subscriptions.unsubscribe();
4789
}

src/app/components/snapcast-status/snapcast-status.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { NgModule } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { SnapcastStatusComponent } from './snapcast-status.component';
4+
import { IonicModule } from '@ionic/angular';
5+
import { FormsModule } from '@angular/forms';
46

57

68

79
@NgModule({
810
declarations: [SnapcastStatusComponent],
911
imports: [
10-
CommonModule
12+
CommonModule,
13+
IonicModule,
14+
FormsModule
1115
],
1216
exports: [SnapcastStatusComponent],
1317
})
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<ion-header>
2+
<ion-toolbar color="light">
3+
<ion-title>Soundcard Picker</ion-title>
4+
</ion-toolbar>
5+
<ion-toolbar color="light">
6+
<ion-searchbar (ionInput)="filterHats($event.target.value)"></ion-searchbar>
7+
8+
</ion-toolbar>
9+
</ion-header>
10+
<ion-content>
11+
12+
13+
<div class="row">
14+
<div class="col-6" *ngFor="let hat of filteredHats">
15+
16+
<ion-img [src]="'assets/soundcards/' + hat.id + '.webp'" />
17+
<!-- make sure image source is valid -->
18+
<ion-card-header>
19+
<ion-card-subtitle>{{ hat.name }}</ion-card-subtitle>
20+
21+
</ion-card-header>
22+
<ion-card-content>
23+
<ion-badge color="primary" *ngIf="hat.testedbyBeatnik">Tested by Beatnik</ion-badge>
24+
<ion-badge color="secondary" *ngIf="hat.testedByCommunity">Tested by Community</ion-badge>
25+
<p>ID: {{ hat.id }}</p>
26+
<p>dt Overlay: {{ hat.overlay}}</p>
27+
<p>Format: {{hat.camilla.format}}</p>
28+
</ion-card-content>
29+
</div>
30+
31+
</div>
32+
</ion-content>

0 commit comments

Comments
 (0)