mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 03:56:15 -08:00
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.
200 lines
7.9 KiB
HTML
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>
|