fix(profiles): label a Plex Home parent connection as an account

A Plex Home profile's chip rendered the parent connection's displayLabel,
which for a Plex account is the account owner's username. The owner's name
appeared directly beneath the Home user's own, reading as the wrong user
being signed in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fdzda9t7kQtq7LoQ2v5nVF
This commit is contained in:
Jack Danger
2026-08-04 10:11:38 -07:00
co-authored by Claude Opus 5
parent ddc3a4d77c
commit dcc06768e0
5 changed files with 83 additions and 4 deletions
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Delete this profile?",
"deleteThisProfileMessage": "Remove ${displayName}. Connections aren't affected.",
"active": "Active",
"viaPlexAccount": "via ${name}",
"manage": "Manage",
"delete": "Delete",
"signOut": "Sign out",
+1 -1
View File
@@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 22
/// Strings: 32946 (1497 per locale)
/// Strings: 32947 (1497 per locale)
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+6 -2
View File
@@ -2296,6 +2296,9 @@ class Translations$profiles$en {
/// en: 'Active'
String get active => 'Active';
/// en: 'via ${name}'
String viaPlexAccount({required Object name}) => 'via ${name}';
/// en: 'Manage'
String get manage => 'Manage';
@@ -6751,6 +6754,7 @@ extension on Translations {
'profiles.deleteThisProfileTitle' => 'Delete this profile?',
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Remove ${displayName}. Connections aren\'t affected.',
'profiles.active' => 'Active',
'profiles.viaPlexAccount' => ({required Object name}) => 'via ${name}',
'profiles.manage' => 'Manage',
'profiles.delete' => 'Delete',
'profiles.signOut' => 'Sign out',
@@ -7059,9 +7063,9 @@ extension on Translations {
'explore.stats.onHold' => ({required Object n}) => '${n} on hold',
'explore.stats.dropped' => ({required Object n}) => '${n} dropped',
'explore.season.winter' => 'Winter',
'explore.season.spring' => 'Spring',
_ => null,
} ?? switch (path) {
'explore.season.spring' => 'Spring',
'explore.season.summer' => 'Summer',
'explore.season.fall' => 'Fall',
'explore.season.withYear' => ({required Object season, required Object year}) => '${season} ${year}',
@@ -7573,9 +7577,9 @@ extension on Translations {
'externalPlayer.playerPackage' => 'Package Name',
'externalPlayer.playerUrlScheme' => 'URL Scheme',
'externalPlayer.off' => 'Off',
'externalPlayer.launchFailed' => 'Failed to open external player',
_ => null,
} ?? switch (path) {
'externalPlayer.launchFailed' => 'Failed to open external player',
'externalPlayer.appNotInstalled' => ({required Object name}) => '${name} is not installed',
'externalPlayer.playInExternalPlayer' => 'Play in External Player',
'metadataEdit.editMetadata' => 'Edit...',
+11 -1
View File
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../connection/connection.dart';
import '../../connection/connection_registry.dart';
import '../../focus/focusable_wrapper.dart';
import '../../i18n/strings.g.dart';
@@ -320,7 +321,16 @@ class _ProfileSwitchScreenState extends State<ProfileSwitchScreen> with MountedS
final parentId = profile.parentConnectionId;
if (parentId != null) {
final conn = view.connectionsById[parentId];
if (conn != null) chips.add(_ChipData(backend: conn.backend, label: conn.displayLabel));
if (conn != null) {
chips.add(
_ChipData(
backend: conn.backend,
label: conn is PlexAccountConnection
? t.profiles.viaPlexAccount(name: conn.accountLabel)
: conn.displayLabel,
),
);
}
}
}
final pcs = visibleProfileConnections(
@@ -216,6 +216,70 @@ void main() {
await tester.pumpWidget(const SizedBox.shrink());
await tester.pump();
});
testWidgets('labels a Plex Home parent connection as an account, not as the profile', (tester) async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
final profile = Profile.plexHome(
id: 'home-alice',
displayName: 'Alice',
parentConnectionId: 'plex-account',
plexHomeUserUuid: 'alice-uuid',
createdAt: DateTime(2026, 1, 1),
);
final profiles = _FakeProfileRegistry(db, [profile]);
final connections = _FakeConnectionRegistry(db, [
PlexAccountConnection(
id: 'plex-account',
accountToken: 'token',
clientIdentifier: 'client',
accountLabel: 'Bob',
createdAt: DateTime(2026, 1, 1),
),
]);
final profileConnections = _FakeProfileConnectionRegistry(db);
final storage = await StorageService.getInstance();
final plexHome = PlexHomeService(
connections: connections,
profileConnections: profileConnections,
storage: storage,
plexHomeUserFetcher: (_) async => const [],
);
final activeProfile = ActiveProfileProvider(
registry: profiles,
plexHome: plexHome,
connections: connections,
profileConnections: profileConnections,
storage: storage,
);
addTearDown(() async {
activeProfile.dispose();
await plexHome.dispose();
await db.close();
});
await tester.pumpWidget(
TranslationProvider(
child: MultiProvider(
providers: [
Provider<ProfileRegistry>.value(value: profiles),
Provider<ProfileConnectionRegistry>.value(value: profileConnections),
Provider<ConnectionRegistry>.value(value: connections),
Provider<PlexHomeService>.value(value: plexHome),
ChangeNotifierProvider<ActiveProfileProvider>.value(value: activeProfile),
],
child: MaterialApp(theme: monoTheme(dark: true), home: const ProfileSwitchScreen()),
),
),
);
await tester.pumpAndSettle();
expect(find.text('Alice'), findsOneWidget);
expect(find.text(t.profiles.viaPlexAccount(name: 'Bob')), findsOneWidget);
expect(find.text('Bob'), findsNothing);
await tester.pumpWidget(const SizedBox.shrink());
await tester.pump();
});
}
class _FakeProfileRegistry extends ProfileRegistry {