feat: dynamic library tabs and shared library navigation

- Replace hardcoded 4-tab system with data-driven visible tab list
- Shared libraries show only Browse + Playlists tabs
- Fix dpad navigation for shared libraries (no hidden tab targets)
- Switch to TickerProviderStateMixin to support tab controller recreation
- Navigate to library screen when tapping shared library sections
- Persist tab selection by name instead of index
This commit is contained in:
edde746
2026-03-15 21:36:17 +01:00
parent bbc7f0f665
commit 551be5bbef
15 changed files with 322 additions and 189 deletions
+7
View File
@@ -26,6 +26,10 @@ class PlexLibrary with MultiServerFields {
@JsonKey(includeFromJson: false, includeToJson: false)
final String? serverName;
/// Whether this is a shared library (individually shared items, not a real section)
@JsonKey(includeFromJson: false, includeToJson: false)
final bool isShared;
/// Global unique identifier across all servers (serverId:key)
String get globalKey => serverId != null ? buildGlobalKey(serverId!, key) : key;
@@ -42,6 +46,7 @@ class PlexLibrary with MultiServerFields {
this.hidden,
this.serverId,
this.serverName,
this.isShared = false,
});
factory PlexLibrary.fromJson(Map<String, dynamic> json) => _$PlexLibraryFromJson(json);
@@ -62,6 +67,7 @@ class PlexLibrary with MultiServerFields {
int? hidden,
String? serverId,
String? serverName,
bool? isShared,
}) {
return PlexLibrary(
key: key ?? this.key,
@@ -76,6 +82,7 @@ class PlexLibrary with MultiServerFields {
hidden: hidden ?? this.hidden,
serverId: serverId ?? this.serverId,
serverName: serverName ?? this.serverName,
isShared: isShared ?? this.isShared,
);
}
}
+12
View File
@@ -133,6 +133,18 @@ class PlexMetadata with MultiServerFields {
/// Global unique identifier across all servers (serverId:ratingKey)
String get globalKey => serverId != null ? buildGlobalKey(serverId!, ratingKey) : ratingKey;
/// Whether this item represents a library section (shared whole-library, not a media item).
/// These have keys like `/library/sections/5/all` instead of `/library/metadata/12345`.
bool get isLibrarySection => key != null && key!.startsWith('/library/sections/');
/// Extract the library section ID from a library-section item's key.
/// Returns null if this is not a library section item.
String? get librarySectionKey {
if (!isLibrarySection) return null;
final match = RegExp(r'/library/sections/(\d+)').firstMatch(key!);
return match?.group(1);
}
/// Parsed media type enum for type-safe comparisons
PlexMediaType get mediaType {
if (type == null) return PlexMediaType.unknown;