Merge pull request #1789 from JackDanger/fix/plex-home-account-chip

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

Conflict resolution: regenerated the Slang outputs against main's
translation set, added the empty locale placeholders the translation
gate requires, and gave the new widget test the StorageService provider
the picker now reads for profile recency.
This commit is contained in:
edde746
2026-08-05 12:31:29 +02:00
26 changed files with 105 additions and 4 deletions
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Bu profil silinsin?",
"deleteThisProfileMessage": "${displayName} silinəcək. Qoşulmalar təsirlənmir.",
"active": "Aktiv",
"viaPlexAccount": "",
"manage": "İdarə et",
"delete": "Sil",
"signOut": "Çıxış et",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Да се изтрие ли този профил?",
"deleteThisProfileMessage": "Премахване на ${displayName}. Връзките не се засягат.",
"active": "Активен",
"viaPlexAccount": "",
"manage": "Управление",
"delete": "Изтрий",
"signOut": "Изход",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Slet denne profil?",
"deleteThisProfileMessage": "Fjern ${displayName}. Forbindelser påvirkes ikke.",
"active": "Aktiv",
"viaPlexAccount": "",
"manage": "Administrer",
"delete": "Slet",
"signOut": "Log ud",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Dieses Profil löschen?",
"deleteThisProfileMessage": "${displayName} entfernen. Verbindungen bleiben unberührt.",
"active": "Aktiv",
"viaPlexAccount": "",
"manage": "Verwalten",
"delete": "Löschen",
"signOut": "Abmelden",
+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
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "¿Eliminar este perfil?",
"deleteThisProfileMessage": "Eliminar ${displayName}. Las conexiones no se verán afectadas.",
"active": "Activo",
"viaPlexAccount": "",
"manage": "Administrar",
"delete": "Eliminar",
"signOut": "Cerrar sesión",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Supprimer ce profil ?",
"deleteThisProfileMessage": "Supprimer ${displayName}. Les connexions ne sont pas affectées.",
"active": "Actif",
"viaPlexAccount": "",
"manage": "Gérer",
"delete": "Supprimer",
"signOut": "Se déconnecter",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Törlöd ezt a profilt?",
"deleteThisProfileMessage": "${displayName} eltávolítása. A kapcsolatokat nem érinti.",
"active": "Aktív",
"viaPlexAccount": "",
"manage": "Kezelés",
"delete": "Törlés",
"signOut": "Kijelentkezés",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Eliminare questo profilo?",
"deleteThisProfileMessage": "Rimuovi ${displayName}. Le connessioni resteranno invariate.",
"active": "Attivo",
"viaPlexAccount": "",
"manage": "Gestisci",
"delete": "Elimina",
"signOut": "Esci",
+1
View File
@@ -762,6 +762,7 @@
"deleteThisProfileTitle": "このプロフィールを削除しますか?",
"deleteThisProfileMessage": "${displayName}を削除します。接続には影響しません。",
"active": "アクティブ",
"viaPlexAccount": "",
"manage": "管理",
"delete": "削除",
"signOut": "サインアウト",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Осы профиль өшірілсін бе?",
"deleteThisProfileMessage": "${displayName} өшіріледі. Қосылымдарға әсер етпейді.",
"active": "Белсенді",
"viaPlexAccount": "",
"manage": "Басқару",
"delete": "Өшіру",
"signOut": "Шығу",
+1
View File
@@ -762,6 +762,7 @@
"deleteThisProfileTitle": "이 프로필을 삭제하시겠습니까?",
"deleteThisProfileMessage": "${displayName}을(를) 제거합니다. 연결에는 영향이 없습니다.",
"active": "활성",
"viaPlexAccount": "",
"manage": "관리",
"delete": "삭제",
"signOut": "로그아웃",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Slett denne profilen?",
"deleteThisProfileMessage": "Fjern ${displayName}. Tilkoblinger påvirkes ikke.",
"active": "Aktiv",
"viaPlexAccount": "",
"manage": "Administrer",
"delete": "Slett",
"signOut": "Logg ut",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Dit profiel verwijderen?",
"deleteThisProfileMessage": "Verwijder ${displayName}. Verbindingen blijven ongewijzigd.",
"active": "Actief",
"viaPlexAccount": "",
"manage": "Beheren",
"delete": "Verwijderen",
"signOut": "Afmelden",
+1
View File
@@ -765,6 +765,7 @@
"deleteThisProfileTitle": "Usunąć ten profil?",
"deleteThisProfileMessage": "Usuń ${displayName}. Połączenia nie zostaną zmienione.",
"active": "Aktywny",
"viaPlexAccount": "",
"manage": "Zarządzaj",
"delete": "Usuń",
"signOut": "Wyloguj się",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Excluir este perfil?",
"deleteThisProfileMessage": "Remover ${displayName}. As conexões não serão afetadas.",
"active": "Ativo",
"viaPlexAccount": "",
"manage": "Gerenciar",
"delete": "Excluir",
"signOut": "Sair",
+1
View File
@@ -765,6 +765,7 @@
"deleteThisProfileTitle": "Удалить этот профиль?",
"deleteThisProfileMessage": "Удалить ${displayName}. Подключения не изменятся.",
"active": "Активный",
"viaPlexAccount": "",
"manage": "Управление",
"delete": "Удалить",
"signOut": "Выйти",
+1 -1
View File
@@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 22
/// Strings: 32737 (1488 per locale)
/// Strings: 32738 (1488 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';
@@ -6754,6 +6757,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',
@@ -7062,9 +7066,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}',
@@ -7576,9 +7580,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...',
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Ta bort denna profil?",
"deleteThisProfileMessage": "Ta bort ${displayName}. Anslutningar påverkas inte.",
"active": "Aktiv",
"viaPlexAccount": "",
"manage": "Hantera",
"delete": "Ta bort",
"signOut": "Logga ut",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Bu profil silinsin mi?",
"deleteThisProfileMessage": "${displayName} kişisi kaldırılacak. Bağlantılar etkilenmez.",
"active": "Aktif",
"viaPlexAccount": "",
"manage": "Yönet",
"delete": "Sil",
"signOut": "Çıkış Yap",
+1
View File
@@ -763,6 +763,7 @@
"deleteThisProfileTitle": "Ushbu profil oʻchirilsinmi?",
"deleteThisProfileMessage": "${displayName} oʻchiriladi. Ulanishlarga taʼsir qilmaydi.",
"active": "Faol",
"viaPlexAccount": "",
"manage": "Boshqarish",
"delete": "Oʻchirish",
"signOut": "Chiqish",
+1
View File
@@ -762,6 +762,7 @@
"deleteThisProfileTitle": "刪除此使用者設定檔?",
"deleteThisProfileMessage": "將移除 ${displayName}。連線資訊將不受影響。",
"active": "使用中",
"viaPlexAccount": "",
"manage": "管理",
"delete": "刪除",
"signOut": "登出",
+1
View File
@@ -762,6 +762,7 @@
"deleteThisProfileTitle": "删除此用户资料?",
"deleteThisProfileMessage": "移除 ${displayName}。连接不会受影响。",
"active": "当前使用",
"viaPlexAccount": "",
"manage": "管理",
"delete": "删除",
"signOut": "退出登录",
+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';
@@ -331,7 +332,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(
@@ -368,6 +368,71 @@ 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<StorageService>.value(value: storage),
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();
});
}
/// Tile labels in painted order. Only the two names the reorder tests seed are