Files
iptvnator/apps/web/src/app/shared/components/header/header.component.html
T
4gray 3e2afe11e3 feat(ui): add remote-control URLs + fix header typos and env
Add support for displaying local remote-control URLs and QR code in the
Settings UI, including styles for URL list, row layout, monospaced links,
and a QR container. Expose a new global API signature getLocalIpAddresses
for retrieving local IPs.

Fix several UI issues:
- Correct typo in header method name (opedAddPlaylistDialog -> openAddPlaylistDialog).
- Fix malformed GitHub URL and collapse img tag formatting.
- Reformat imports and viewChild usage for readability.

Change local development backend URL to localhost:3000 in the local
environment to point to the running local API during development.

These changes improve developer ergonomics (local backend), add a remote
control discovery UX for users on the same network, and tidy up header
bugs and formatting.
2025-12-06 21:46:35 +01:00

200 lines
7.9 KiB
HTML

<div class="modern-header">
<div class="brand-block">
<div class="logo-pulse">
<img src="./assets/icons/icon-tv-256.png" alt="iptvnator logo" />
</div>
<div class="brand-text">
<div class="title">{{ title() }}</div>
</div>
</div>
@if (isHome) {
<div class="search-container">
<mat-icon class="search-icon">search</mat-icon>
<input
#searchInput
type="search"
class="search-input"
spellcheck="false"
autocomplete="off"
[placeholder]="'HOME.PLAYLISTS.SEARCH_PLAYLISTS' | translate"
(input)="onSearchQueryUpdate(searchInput.value)"
/>
</div>
<div class="menu">
<button
mat-icon-button
class="add-playlist-btn"
[matMenuTriggerFor]="addPlaylistMenuComponent().menu()"
[matTooltip]="'HOME.URL_UPLOAD.ADD_PLAYLIST' | translate"
data-test-id="add-playlist"
>
<mat-icon>add</mat-icon>
</button>
<app-add-playlist-menu
(playlistTypeSelected)="openAddPlaylistDialog($event)"
/>
<!-- Sort button -->
<button
mat-icon-button
[matMenuTriggerFor]="sortMenu"
[matTooltip]="'HOME.SORT_PLAYLISTS' | translate"
data-test-id="sort-playlists"
>
<mat-icon>sort</mat-icon>
</button>
<button
mat-icon-button
[matMenuTriggerFor]="filterPlaylistMenu"
[matTooltip]="'HOME.FILTER_BY_TYPE' | translate"
data-test-id="filter-playlist-by-type"
>
<mat-icon>filter_alt</mat-icon>
</button>
@if (isDesktop) {
<button
mat-icon-button
(click)="openSettings()"
[matTooltip]="'MENU.SETTINGS' | translate"
data-test-id="open-settings"
>
<mat-icon>settings</mat-icon>
</button>
} @else {
<button
mat-icon-button
[matMenuTriggerFor]="menu"
[attr.aria-label]="'MENU.OPEN' | translate"
data-test-id="pwa-menu"
>
<mat-icon>menu</mat-icon>
</button>
<mat-menu #menu="matMenu">
@if (isHome) {
<button
mat-menu-item
[attr.aria-label]="'MENU.SETTINGS_ARIA' | translate"
(click)="openSettings()"
data-test-id="pwa-open-settings"
>
<mat-icon>settings</mat-icon>
<span>{{ 'MENU.SETTINGS' | translate }}</span>
</button>
<mat-divider></mat-divider>
}
<button
mat-menu-item
[attr.aria-label]="'MENU.SUPPORT_ARIA' | translate"
(click)="openUrl('https://github.com/4gray/iptvnator')"
>
<mat-icon>recommend</mat-icon>
<span>GitHub</span>
</button>
<button
mat-menu-item
[attr.aria-label]="'MENU.BUG_REPORT' | translate"
(click)="
openUrl('https://github.com/4gray/iptvnator/issues')
"
>
<mat-icon>bug_report</mat-icon>
<span>{{ 'MENU.BUG_REPORT' | translate }}</span>
</button>
<mat-divider />
<button
mat-menu-item
[attr.aria-label]="'MENU.SUPPORT_PROJECT' | translate"
(click)="openUrl('https://github.com/sponsors/4gray')"
>
<mat-icon>savings</mat-icon>
<span>{{ 'MENU.SUPPORT_PROJECT' | translate }}</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
[attr.aria-label]="'MENU.ABOUT_ARIA' | translate"
(click)="openAboutDialog()"
>
<mat-icon>info</mat-icon>
<span>{{ 'MENU.ABOUT' | translate }}</span>
</button>
</mat-menu>
}
<!-- Sort menu -->
<mat-menu #sortMenu="matMenu">
<div
class="dropdown-menu-header"
(click)="$event.stopPropagation()"
>
{{ 'HOME.SORT_BY' | translate }}
</div>
<button
mat-menu-item
(click)="setSortOptions(SortBy.NAME, SortOrder.ASC)"
[class.active-sort]="
isSortActive(SortBy.NAME, SortOrder.ASC)
"
>
<mat-icon>sort_by_alpha</mat-icon>
<span>{{ 'HOME.SORT_OPTIONS.NAME_ASC' | translate }}</span>
</button>
<button
mat-menu-item
(click)="setSortOptions(SortBy.NAME, SortOrder.DESC)"
[class.active-sort]="
isSortActive(SortBy.NAME, SortOrder.DESC)
"
>
<mat-icon>sort_by_alpha</mat-icon>
<span>{{ 'HOME.SORT_OPTIONS.NAME_DESC' | translate }}</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
(click)="setSortOptions(SortBy.DATE_ADDED, SortOrder.DESC)"
[class.active-sort]="
isSortActive(SortBy.DATE_ADDED, SortOrder.DESC)
"
>
<mat-icon>date_range</mat-icon>
<span>{{ 'HOME.SORT_OPTIONS.NEWEST' | translate }}</span>
</button>
<button
mat-menu-item
(click)="setSortOptions(SortBy.DATE_ADDED, SortOrder.ASC)"
[class.active-sort]="
isSortActive(SortBy.DATE_ADDED, SortOrder.ASC)
"
>
<mat-icon>date_range</mat-icon>
<span>{{ 'HOME.SORT_OPTIONS.OLDEST' | translate }}</span>
</button>
</mat-menu>
<mat-menu #filterPlaylistMenu="matMenu">
<section
(click)="$event.stopPropagation()"
class="filter-container"
>
<div
class="dropdown-menu-header"
(click)="$event.stopPropagation()"
>
{{ 'HOME.FILTER_BY_TYPE' | translate }}
</div>
@for (type of playlistTypes; track $index) {
<p>
<mat-checkbox
(click)="$event.stopPropagation()"
[checked]="true"
(change)="onPlaylistFilterChange()"
[(ngModel)]="type.checked"
>{{ type.title }}</mat-checkbox
>
</p>
}
</section>
</mat-menu>
</div>
}
</div>