feat: let subscriptions be renamed from the list (#1044)

A subscription's name is captured once at subscribe time from the feed's own
title, so playlists — particularly the UULF-prefixed channel-uploads playlists
— all come back named "Videos" and stay that way. Adds an inline editor on the
Name cell, mirroring the existing title-filter edit next to it.

The update route already whitelisted `name` and update_subscription already
applied it, so this is mostly the missing UI. The backend side is validation:
the old `str(changes["name"])` accepted any type, any length and any
whitespace, for a value that is persisted and broadcast to every connected
client. validate_subscription_name now requires a string, collapses interior
whitespace to keep the label single-line, and caps it at 200 characters.

The name is display-only — it is used for the subscription list and log lines,
never for download paths — so renaming cannot move where files land.
This commit is contained in:
Alex Shnitman
2026-07-27 21:34:09 +03:00
parent 2744f36b44
commit d66b04ccf5
5 changed files with 192 additions and 4 deletions
+31
View File
@@ -102,6 +102,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
skipSubscriberOnly = false;
editingTitleRegexId: string | null = null;
titleRegexEditDraft = '';
editingNameId: string | null = null;
nameEditDraft = '';
readonly subscriptionNameMaxLength = 200;
cachedSubs: [string, SubscriptionRow][] = [];
selectedSubscriptionIds = new Set<string>();
checkingSubscriptionIds = new Set<string>();
@@ -663,6 +666,34 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
});
}
beginEditName(id: string, current: string | undefined) {
this.editingNameId = id;
this.nameEditDraft = current ?? '';
this.cdr.markForCheck();
}
cancelEditName() {
this.editingNameId = null;
this.nameEditDraft = '';
this.cdr.markForCheck();
}
saveName(id: string) {
const name = (this.nameEditDraft || '').trim();
if (!name) {
this.toasts.error('Subscription name must not be empty');
return;
}
this.subscriptionsSvc.update(id, { name }).subscribe((res) => {
const error = this.getStatusError(res);
if (error) {
this.toasts.error(error || 'Update subscription failed');
return;
}
this.cancelEditName();
});
}
deleteSubscription(id: string) {
this.subscriptionsSvc.delete([id]).subscribe((res) => {
const error = this.getStatusError(res);