Commit Graph
4 Commits
Author SHA1 Message Date
4grayandClaude Opus 5.5 23a1860119 fix(ui): destructive confirmations, verb labels and provider icons (#1783)
* fix(ui): destructive confirmations, verb labels and provider icons

Confirmations: ConfirmDialogData.confirmLabel is required, so no dialog can
fall back to "Yes"/"No"; the dismiss defaults to "Cancel" and
`tone: 'destructive'` styles the confirm with .app-destructive-button. Every
caller names its action ("Remove playlist", "Clear", "Refresh playlist",
"Cancel download" with a "Close" dismiss). The confirm button has the
confirm-dialog-confirm test id and drops its no-op color="primary".

The no-op `warn` color input becomes .app-destructive-button on the EPG
mapping, playlist item, error view, EPG/reset settings, delete-all and source
cleanup buttons, and on the unsaved-changes dialog's Discard.

Provider icons come from SOURCE_TYPE_ICONS in shared/interfaces (Xtream
cloud, Stalker cast, M3U playlist_play / link / description / subject) in the
add dialog, auto-import, empty state, playlist switcher, playlist rows,
dashboard source rail, command palette, Sources filters and both reset
summaries. Stalker no longer borrows the Dashboard icon, and Xtream no longer
shares a glyph with M3U URL playlists.

The playlist error view removed a playlist through the stale
PlaylistActions.removePlaylist: it dropped the playlist from state before the
delete ran, swallowed failures, skipped the source activity guard and showed
no toast. It now uses PlaylistDeleteActionService like every other removal,
commits only a completed delete, toasts and goes home. The unused action and
its effect are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): one provider icon per playlist row, imperative Korean remove label

A restored Stalker or Xtream playlist can also carry a URL, and the row's
independent checks then showed the M3U URL icon next to the provider icon.
The row now switches on resolvePlaylistSourceIconKey(), the precedence every
other surface uses, so each source shows exactly one icon.

HOME.PLAYLISTS.REMOVE now names the confirm button and the row's delete
tooltip; in Korean it read "the playlist has been removed". It now says
"remove playlist", like every other locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): keep the auto-refresh badge on playlist rows with one provider icon

Showing one provider icon per row moved the auto-refresh badge into the M3U
branches only, so a restored Stalker playlist with a URL and auto-refresh
lost it although the URL is still re-fetched. The row now renders one icon
container: the provider icon from the shared precedence, then the badge for
any row with a URL or a local M3U, exactly the rows that showed it before.
The Xtream portal-status dot, used without source health, keeps that corner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): let the playlist row's cancel action render in the error color

The row's action buttons set `color: inherit`, and the selected row does so
again with more specific selectors. Both beat Material's token-driven icon
color, so the .app-destructive-button cancel action kept the row color
(selection blue on the active row). Pin the cancel button to
--mat-sys-error in both row states.

The large-deletion Electron E2E now checks the cancel color in both themes;
without this rule it reads rgb(47, 123, 255) instead of the error red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ui): give the dialog service spec the now-required confirm labels

ConfirmDialogData.confirmLabel became required, and the spec still built
confirmations without one. Jest only transpiles, so the suite stayed green,
but the "Typecheck Jest spec programs" CI step rejected it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:36:16 +02:00
ea51cae3db feat(settings): search settings from the header and the command palette (#1714)
* feat(settings): search settings from the header and the command palette

The header search on the Settings page was shown but disabled. It now
searches a shared index of all 56 settings rows by translated title,
description and English synonyms, replaces the section page with ranked
results, and opens a result by scrolling to, focusing and briefly
highlighting its row. Enter opens the best match, and the section
navigation shows per-section match counts.

The command palette gains a "Settings" group that lists the best six
matches for a non-empty query, so any setting is one Ctrl/Cmd+K away.
Rows hidden by the current form state fall back to the control that
reveals them; rows the runtime cannot render are never returned.

The index ships through a new @iptvnator/workspace/shell/util/settings-search
sub-entrypoint so it stays out of the eager bundle, and a registry spec
keeps it in step with the section templates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(settings): let search reveals win over pending input and gate embedded MPV rows

- A reveal (result click, command palette, Enter) now cancels a search
  keystroke still waiting for its debounce, so its q navigation can no
  longer supersede the reveal and leave the results open.
- Embedded MPV extra options and auto-reconnect require a lazily probed
  embedded MPV capability; frame copy also needs frameCopyAvailable, so
  search never offers a row the settings page cannot render.
- Keyboard users keep a focus-visible ring on the revealed row after the
  highlight fades.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(workspace): wait for palette probes without Promise.allSettled

The web tsconfig lib predates Promise.allSettled; use Promise.all over
rejection-safe probes instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:42:53 +02:00
4grayandClaude Fable 5 1a6af75761 feat(settings): per-section pages with unsaved-changes bar (#1384)
* feat(settings): split settings into per-section pages with an unsaved-changes bar

Replace the single scrolling settings page with routed section pages
(/workspace/settings/:section): the context-panel rail links each section,
only the active section renders, and unknown or capability-gated sections
redirect to General. The shared form lives on the parent component, so
staged edits survive section switches; a floating unsaved-changes bar
(Save/Discard) replaces the always-visible footer Save button. Rail links
navigate with replaceUrl so Back still leaves settings in one step.

Along the way:
- delete the unreachable settings dialog mode and the dead
  AppPortalNavigationActionsService with both of its never-injected DI
  tokens (PORTAL_NAVIGATION_ACTIONS, PLAYLIST_PLAYER_ACTIONS)
- delete the scroll-spy directive and pendingScrollTarget plumbing
- revive the EPG panel's "Open EPG settings" empty-state button as a deep
  link to /workspace/settings/epg; the M3U player now reports
  m3u-needs-setup only when the channel has no programmes and no EPG
  source exists in settings or on the playlist itself
- load TMDB cache stats when the Metadata page opens (the section
  component now only exists while its page is open)
- add SETTINGS.UNSAVED_CHANGES / SETTINGS.DISCARD_CHANGES to all 19 locales

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(settings): confirm before leaving with unsaved changes

Add settingsUnsavedChangesGuard (canDeactivate on the :section route) with
a three-action dialog: save and leave, leave without saving, keep editing.
The guard only intercepts leaving the settings AREA — section switches
share the one settings form and pass unconditionally, so the dialog can
never nag while moving between pages. A failed save cancels the navigation
instead of silently dropping the edits it promised to keep; leaving
without saving also reverts the live theme preview. Save-and-leave is
disabled while the form is invalid, with a hint explaining why.

New SETTINGS.UNSAVED_DIALOG_* keys in all 19 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(settings): stage cover size and EPG view mode; adapt e2e to section pages

Cover size and EPG view mode were the only two controls that persisted
eagerly on click, which made Discard (and leave-without-saving) unable to
revert them: hydrateFromStore() faithfully reloaded the just-persisted
edit. They now stage in the form like every other setting and reach the
store on Save. Review finding by Greptile (P1) and Codex.

E2E suites that walk through settings are updated for one-section-page
rendering (epg, backup-roundtrip, xtream-epg, remote-control) and for the
staged cover size (downloads asserts the dataset after Save); the EPG icon
fallback test saves before leaving settings so the new unsaved-changes
dialog does not block its navigation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 09:34:03 +02:00
4gray c51d8161ea refactor(web): split settings component into sections
Entire-Checkpoint: c2cea9c530e6
2026-05-01 20:16:32 +02:00