@@ -704,6 +704,10 @@
|
||||
"sessionCodeCopied": "Sitzungscode in Zwischenablage kopiert",
|
||||
"relayUnreachable": "Der Relay-Server ist nicht erreichbar. Möglicherweise blockiert dein Internetanbieter die Verbindung. Du kannst es trotzdem versuchen, aber Watch Together funktioniert eventuell nicht.",
|
||||
"reconnectingToHost": "Verbindung zum Host wird wiederhergestellt...",
|
||||
"currentPlayback": "Aktuelle Wiedergabe",
|
||||
"joinCurrentPlayback": "Aktuelle Wiedergabe beitreten",
|
||||
"joinCurrentPlaybackDescription": "Springe zurück zu dem, was der Host gerade ansieht",
|
||||
"failedToOpenCurrentPlayback": "Aktuelle Wiedergabe konnte nicht geöffnet werden",
|
||||
"participantJoined": "${name} ist beigetreten",
|
||||
"participantLeft": "${name} hat die Sitzung verlassen"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,10 @@
|
||||
"sessionCodeCopied": "Session code copied to clipboard",
|
||||
"relayUnreachable": "The relay server is unreachable. This may be caused by your ISP blocking the connection. You can still try, but Watch Together may not work.",
|
||||
"reconnectingToHost": "Reconnecting to host...",
|
||||
"currentPlayback": "Current Playback",
|
||||
"joinCurrentPlayback": "Join Current Playback",
|
||||
"joinCurrentPlaybackDescription": "Jump back into what the host is currently watching",
|
||||
"failedToOpenCurrentPlayback": "Failed to open current playback",
|
||||
"participantJoined": "${name} joined",
|
||||
"participantLeft": "${name} left"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,10 @@
|
||||
"sessionCodeCopied": "Código de sesión copiado al portapapeles",
|
||||
"relayUnreachable": "El servidor de retransmisión no está disponible. Esto puede deberse a que tu proveedor de internet bloquea la conexión. Puedes intentarlo de todos modos, pero Watch Together podría no funcionar.",
|
||||
"reconnectingToHost": "Reconectando con el anfitrión...",
|
||||
"currentPlayback": "Reproducción actual",
|
||||
"joinCurrentPlayback": "Unirse a la reproducción actual",
|
||||
"joinCurrentPlaybackDescription": "Vuelve a lo que el anfitrión está viendo ahora mismo",
|
||||
"failedToOpenCurrentPlayback": "No se pudo abrir la reproducción actual",
|
||||
"participantJoined": "${name} se unió",
|
||||
"participantLeft": "${name} se fue"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,10 @@
|
||||
"sessionCodeCopied": "Code de session copié dans le presse-papiers",
|
||||
"relayUnreachable": "Le serveur relais est inaccessible. Cela peut être dû au blocage de la connexion par votre fournisseur d'accès. Vous pouvez quand même essayer, mais Watch Together pourrait ne pas fonctionner.",
|
||||
"reconnectingToHost": "Reconnexion à l'hôte...",
|
||||
"currentPlayback": "Lecture en cours",
|
||||
"joinCurrentPlayback": "Rejoindre la lecture en cours",
|
||||
"joinCurrentPlaybackDescription": "Revenez à ce que l'hôte regarde actuellement",
|
||||
"failedToOpenCurrentPlayback": "Impossible d'ouvrir la lecture en cours",
|
||||
"participantJoined": "${name} a rejoint",
|
||||
"participantLeft": "${name} est parti"
|
||||
},
|
||||
|
||||
@@ -704,6 +704,10 @@
|
||||
"sessionCodeCopied": "Codice sessione copiato negli appunti",
|
||||
"relayUnreachable": "Il server di inoltro non è raggiungibile. Questo potrebbe essere causato dal blocco della connessione da parte del tuo provider. Puoi comunque provare, ma Watch Together potrebbe non funzionare.",
|
||||
"reconnectingToHost": "Riconnessione all'host...",
|
||||
"currentPlayback": "Riproduzione corrente",
|
||||
"joinCurrentPlayback": "Unisciti alla riproduzione corrente",
|
||||
"joinCurrentPlaybackDescription": "Torna a ciò che l'host sta guardando in questo momento",
|
||||
"failedToOpenCurrentPlayback": "Impossibile aprire la riproduzione corrente",
|
||||
"participantJoined": "${name} si è unito",
|
||||
"participantLeft": "${name} se ne è andato"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,10 @@
|
||||
"sessionCodeCopied": "세션 코드가 클립보드에 복사되었습니다",
|
||||
"relayUnreachable": "릴레이 서버에 연결할 수 없습니다. 인터넷 제공업체가 연결을 차단하고 있을 수 있습니다. 시도해 볼 수 있지만 함께 보기가 작동하지 않을 수 있습니다.",
|
||||
"reconnectingToHost": "호스트에 재연결 중...",
|
||||
"currentPlayback": "현재 재생",
|
||||
"joinCurrentPlayback": "현재 재생 참여",
|
||||
"joinCurrentPlaybackDescription": "호스트가 현재 보고 있는 항목으로 돌아갑니다",
|
||||
"failedToOpenCurrentPlayback": "현재 재생을 열 수 없습니다",
|
||||
"participantJoined": "${name}님이 참여했습니다",
|
||||
"participantLeft": "${name}님이 나갔습니다"
|
||||
},
|
||||
|
||||
@@ -704,6 +704,10 @@
|
||||
"sessionCodeCopied": "Sessiecode gekopieerd naar klembord",
|
||||
"relayUnreachable": "De relayserver is niet bereikbaar. Dit kan worden veroorzaakt doordat je internetprovider de verbinding blokkeert. Je kunt het toch proberen, maar Watch Together werkt mogelijk niet.",
|
||||
"reconnectingToHost": "Opnieuw verbinden met host...",
|
||||
"currentPlayback": "Huidige weergave",
|
||||
"joinCurrentPlayback": "Deelnemen aan huidige weergave",
|
||||
"joinCurrentPlaybackDescription": "Ga terug naar wat de host nu kijkt",
|
||||
"failedToOpenCurrentPlayback": "Huidige weergave kon niet worden geopend",
|
||||
"participantJoined": "${name} is toegetreden",
|
||||
"participantLeft": "${name} heeft de sessie verlaten"
|
||||
},
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 9
|
||||
/// Strings: 7254 (806 per locale)
|
||||
/// Strings: 7290 (810 per locale)
|
||||
///
|
||||
/// Built on 2026-03-05 at 11:22 UTC
|
||||
/// Built on 2026-03-06 at 19:39 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -972,6 +972,10 @@ class _TranslationsWatchTogetherDe implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Sitzungscode in Zwischenablage kopiert';
|
||||
@override String get relayUnreachable => 'Der Relay-Server ist nicht erreichbar. Möglicherweise blockiert dein Internetanbieter die Verbindung. Du kannst es trotzdem versuchen, aber Watch Together funktioniert eventuell nicht.';
|
||||
@override String get reconnectingToHost => 'Verbindung zum Host wird wiederhergestellt...';
|
||||
@override String get currentPlayback => 'Aktuelle Wiedergabe';
|
||||
@override String get joinCurrentPlayback => 'Aktuelle Wiedergabe beitreten';
|
||||
@override String get joinCurrentPlaybackDescription => 'Springe zurück zu dem, was der Host gerade ansieht';
|
||||
@override String get failedToOpenCurrentPlayback => 'Aktuelle Wiedergabe konnte nicht geöffnet werden';
|
||||
@override String participantJoined({required Object name}) => '${name} ist beigetreten';
|
||||
@override String participantLeft({required Object name}) => '${name} hat die Sitzung verlassen';
|
||||
}
|
||||
@@ -1937,6 +1941,10 @@ extension on TranslationsDe {
|
||||
'watchTogether.sessionCodeCopied' => 'Sitzungscode in Zwischenablage kopiert',
|
||||
'watchTogether.relayUnreachable' => 'Der Relay-Server ist nicht erreichbar. Möglicherweise blockiert dein Internetanbieter die Verbindung. Du kannst es trotzdem versuchen, aber Watch Together funktioniert eventuell nicht.',
|
||||
'watchTogether.reconnectingToHost' => 'Verbindung zum Host wird wiederhergestellt...',
|
||||
'watchTogether.currentPlayback' => 'Aktuelle Wiedergabe',
|
||||
'watchTogether.joinCurrentPlayback' => 'Aktuelle Wiedergabe beitreten',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Springe zurück zu dem, was der Host gerade ansieht',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Aktuelle Wiedergabe konnte nicht geöffnet werden',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} ist beigetreten',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} hat die Sitzung verlassen',
|
||||
'shaders.title' => 'Shader',
|
||||
|
||||
@@ -2108,6 +2108,18 @@ class TranslationsWatchTogetherEn {
|
||||
/// en: 'Reconnecting to host...'
|
||||
String get reconnectingToHost => 'Reconnecting to host...';
|
||||
|
||||
/// en: 'Current Playback'
|
||||
String get currentPlayback => 'Current Playback';
|
||||
|
||||
/// en: 'Join Current Playback'
|
||||
String get joinCurrentPlayback => 'Join Current Playback';
|
||||
|
||||
/// en: 'Jump back into what the host is currently watching'
|
||||
String get joinCurrentPlaybackDescription => 'Jump back into what the host is currently watching';
|
||||
|
||||
/// en: 'Failed to open current playback'
|
||||
String get failedToOpenCurrentPlayback => 'Failed to open current playback';
|
||||
|
||||
/// en: '${name} joined'
|
||||
String participantJoined({required Object name}) => '${name} joined';
|
||||
|
||||
@@ -3538,6 +3550,10 @@ extension on Translations {
|
||||
'watchTogether.sessionCodeCopied' => 'Session code copied to clipboard',
|
||||
'watchTogether.relayUnreachable' => 'The relay server is unreachable. This may be caused by your ISP blocking the connection. You can still try, but Watch Together may not work.',
|
||||
'watchTogether.reconnectingToHost' => 'Reconnecting to host...',
|
||||
'watchTogether.currentPlayback' => 'Current Playback',
|
||||
'watchTogether.joinCurrentPlayback' => 'Join Current Playback',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Jump back into what the host is currently watching',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Failed to open current playback',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} joined',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} left',
|
||||
'downloads.title' => 'Downloads',
|
||||
|
||||
@@ -945,6 +945,10 @@ class _TranslationsWatchTogetherEs implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Código de sesión copiado al portapapeles';
|
||||
@override String get relayUnreachable => 'El servidor de retransmisión no está disponible. Esto puede deberse a que tu proveedor de internet bloquea la conexión. Puedes intentarlo de todos modos, pero Watch Together podría no funcionar.';
|
||||
@override String get reconnectingToHost => 'Reconectando con el anfitrión...';
|
||||
@override String get currentPlayback => 'Reproducción actual';
|
||||
@override String get joinCurrentPlayback => 'Unirse a la reproducción actual';
|
||||
@override String get joinCurrentPlaybackDescription => 'Vuelve a lo que el anfitrión está viendo ahora mismo';
|
||||
@override String get failedToOpenCurrentPlayback => 'No se pudo abrir la reproducción actual';
|
||||
@override String participantJoined({required Object name}) => '${name} se unió';
|
||||
@override String participantLeft({required Object name}) => '${name} se fue';
|
||||
}
|
||||
@@ -1919,6 +1923,10 @@ extension on TranslationsEs {
|
||||
'watchTogether.sessionCodeCopied' => 'Código de sesión copiado al portapapeles',
|
||||
'watchTogether.relayUnreachable' => 'El servidor de retransmisión no está disponible. Esto puede deberse a que tu proveedor de internet bloquea la conexión. Puedes intentarlo de todos modos, pero Watch Together podría no funcionar.',
|
||||
'watchTogether.reconnectingToHost' => 'Reconectando con el anfitrión...',
|
||||
'watchTogether.currentPlayback' => 'Reproducción actual',
|
||||
'watchTogether.joinCurrentPlayback' => 'Unirse a la reproducción actual',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Vuelve a lo que el anfitrión está viendo ahora mismo',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'No se pudo abrir la reproducción actual',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} se unió',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} se fue',
|
||||
'downloads.title' => 'Descargas',
|
||||
|
||||
@@ -945,6 +945,10 @@ class _TranslationsWatchTogetherFr implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Code de session copié dans le presse-papiers';
|
||||
@override String get relayUnreachable => 'Le serveur relais est inaccessible. Cela peut être dû au blocage de la connexion par votre fournisseur d\'accès. Vous pouvez quand même essayer, mais Watch Together pourrait ne pas fonctionner.';
|
||||
@override String get reconnectingToHost => 'Reconnexion à l\'hôte...';
|
||||
@override String get currentPlayback => 'Lecture en cours';
|
||||
@override String get joinCurrentPlayback => 'Rejoindre la lecture en cours';
|
||||
@override String get joinCurrentPlaybackDescription => 'Revenez à ce que l\'hôte regarde actuellement';
|
||||
@override String get failedToOpenCurrentPlayback => 'Impossible d\'ouvrir la lecture en cours';
|
||||
@override String participantJoined({required Object name}) => '${name} a rejoint';
|
||||
@override String participantLeft({required Object name}) => '${name} est parti';
|
||||
}
|
||||
@@ -1919,6 +1923,10 @@ extension on TranslationsFr {
|
||||
'watchTogether.sessionCodeCopied' => 'Code de session copié dans le presse-papiers',
|
||||
'watchTogether.relayUnreachable' => 'Le serveur relais est inaccessible. Cela peut être dû au blocage de la connexion par votre fournisseur d\'accès. Vous pouvez quand même essayer, mais Watch Together pourrait ne pas fonctionner.',
|
||||
'watchTogether.reconnectingToHost' => 'Reconnexion à l\'hôte...',
|
||||
'watchTogether.currentPlayback' => 'Lecture en cours',
|
||||
'watchTogether.joinCurrentPlayback' => 'Rejoindre la lecture en cours',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Revenez à ce que l\'hôte regarde actuellement',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Impossible d\'ouvrir la lecture en cours',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} a rejoint',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} est parti',
|
||||
'downloads.title' => 'Téléchargements',
|
||||
|
||||
@@ -972,6 +972,10 @@ class _TranslationsWatchTogetherIt implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Codice sessione copiato negli appunti';
|
||||
@override String get relayUnreachable => 'Il server di inoltro non è raggiungibile. Questo potrebbe essere causato dal blocco della connessione da parte del tuo provider. Puoi comunque provare, ma Watch Together potrebbe non funzionare.';
|
||||
@override String get reconnectingToHost => 'Riconnessione all\'host...';
|
||||
@override String get currentPlayback => 'Riproduzione corrente';
|
||||
@override String get joinCurrentPlayback => 'Unisciti alla riproduzione corrente';
|
||||
@override String get joinCurrentPlaybackDescription => 'Torna a ciò che l\'host sta guardando in questo momento';
|
||||
@override String get failedToOpenCurrentPlayback => 'Impossibile aprire la riproduzione corrente';
|
||||
@override String participantJoined({required Object name}) => '${name} si è unito';
|
||||
@override String participantLeft({required Object name}) => '${name} se ne è andato';
|
||||
}
|
||||
@@ -1937,6 +1941,10 @@ extension on TranslationsIt {
|
||||
'watchTogether.sessionCodeCopied' => 'Codice sessione copiato negli appunti',
|
||||
'watchTogether.relayUnreachable' => 'Il server di inoltro non è raggiungibile. Questo potrebbe essere causato dal blocco della connessione da parte del tuo provider. Puoi comunque provare, ma Watch Together potrebbe non funzionare.',
|
||||
'watchTogether.reconnectingToHost' => 'Riconnessione all\'host...',
|
||||
'watchTogether.currentPlayback' => 'Riproduzione corrente',
|
||||
'watchTogether.joinCurrentPlayback' => 'Unisciti alla riproduzione corrente',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Torna a ciò che l\'host sta guardando in questo momento',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Impossibile aprire la riproduzione corrente',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} si è unito',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} se ne è andato',
|
||||
'shaders.title' => 'Shader',
|
||||
|
||||
@@ -945,6 +945,10 @@ class _TranslationsWatchTogetherKo implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => '세션 코드가 클립보드에 복사되었습니다';
|
||||
@override String get relayUnreachable => '릴레이 서버에 연결할 수 없습니다. 인터넷 제공업체가 연결을 차단하고 있을 수 있습니다. 시도해 볼 수 있지만 함께 보기가 작동하지 않을 수 있습니다.';
|
||||
@override String get reconnectingToHost => '호스트에 재연결 중...';
|
||||
@override String get currentPlayback => '현재 재생';
|
||||
@override String get joinCurrentPlayback => '현재 재생 참여';
|
||||
@override String get joinCurrentPlaybackDescription => '호스트가 현재 보고 있는 항목으로 돌아갑니다';
|
||||
@override String get failedToOpenCurrentPlayback => '현재 재생을 열 수 없습니다';
|
||||
@override String participantJoined({required Object name}) => '${name}님이 참여했습니다';
|
||||
@override String participantLeft({required Object name}) => '${name}님이 나갔습니다';
|
||||
}
|
||||
@@ -1919,6 +1923,10 @@ extension on TranslationsKo {
|
||||
'watchTogether.sessionCodeCopied' => '세션 코드가 클립보드에 복사되었습니다',
|
||||
'watchTogether.relayUnreachable' => '릴레이 서버에 연결할 수 없습니다. 인터넷 제공업체가 연결을 차단하고 있을 수 있습니다. 시도해 볼 수 있지만 함께 보기가 작동하지 않을 수 있습니다.',
|
||||
'watchTogether.reconnectingToHost' => '호스트에 재연결 중...',
|
||||
'watchTogether.currentPlayback' => '현재 재생',
|
||||
'watchTogether.joinCurrentPlayback' => '현재 재생 참여',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => '호스트가 현재 보고 있는 항목으로 돌아갑니다',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => '현재 재생을 열 수 없습니다',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name}님이 참여했습니다',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name}님이 나갔습니다',
|
||||
'downloads.title' => '다운로드',
|
||||
|
||||
@@ -972,6 +972,10 @@ class _TranslationsWatchTogetherNl implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Sessiecode gekopieerd naar klembord';
|
||||
@override String get relayUnreachable => 'De relayserver is niet bereikbaar. Dit kan worden veroorzaakt doordat je internetprovider de verbinding blokkeert. Je kunt het toch proberen, maar Watch Together werkt mogelijk niet.';
|
||||
@override String get reconnectingToHost => 'Opnieuw verbinden met host...';
|
||||
@override String get currentPlayback => 'Huidige weergave';
|
||||
@override String get joinCurrentPlayback => 'Deelnemen aan huidige weergave';
|
||||
@override String get joinCurrentPlaybackDescription => 'Ga terug naar wat de host nu kijkt';
|
||||
@override String get failedToOpenCurrentPlayback => 'Huidige weergave kon niet worden geopend';
|
||||
@override String participantJoined({required Object name}) => '${name} is toegetreden';
|
||||
@override String participantLeft({required Object name}) => '${name} heeft de sessie verlaten';
|
||||
}
|
||||
@@ -1937,6 +1941,10 @@ extension on TranslationsNl {
|
||||
'watchTogether.sessionCodeCopied' => 'Sessiecode gekopieerd naar klembord',
|
||||
'watchTogether.relayUnreachable' => 'De relayserver is niet bereikbaar. Dit kan worden veroorzaakt doordat je internetprovider de verbinding blokkeert. Je kunt het toch proberen, maar Watch Together werkt mogelijk niet.',
|
||||
'watchTogether.reconnectingToHost' => 'Opnieuw verbinden met host...',
|
||||
'watchTogether.currentPlayback' => 'Huidige weergave',
|
||||
'watchTogether.joinCurrentPlayback' => 'Deelnemen aan huidige weergave',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Ga terug naar wat de host nu kijkt',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Huidige weergave kon niet worden geopend',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} is toegetreden',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} heeft de sessie verlaten',
|
||||
'shaders.title' => 'Shaders',
|
||||
|
||||
@@ -972,6 +972,10 @@ class _TranslationsWatchTogetherSv implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => 'Sessionskod kopierad till urklipp';
|
||||
@override String get relayUnreachable => 'Reläservern kan inte nås. Detta kan bero på att din internetleverantör blockerar anslutningen. Du kan fortfarande försöka, men Watch Together kanske inte fungerar.';
|
||||
@override String get reconnectingToHost => 'Återansluter till värd...';
|
||||
@override String get currentPlayback => 'Aktuell uppspelning';
|
||||
@override String get joinCurrentPlayback => 'Gå med i aktuell uppspelning';
|
||||
@override String get joinCurrentPlaybackDescription => 'Hoppa tillbaka till det värden tittar på just nu';
|
||||
@override String get failedToOpenCurrentPlayback => 'Kunde inte öppna aktuell uppspelning';
|
||||
@override String participantJoined({required Object name}) => '${name} gick med';
|
||||
@override String participantLeft({required Object name}) => '${name} lämnade';
|
||||
}
|
||||
@@ -1937,6 +1941,10 @@ extension on TranslationsSv {
|
||||
'watchTogether.sessionCodeCopied' => 'Sessionskod kopierad till urklipp',
|
||||
'watchTogether.relayUnreachable' => 'Reläservern kan inte nås. Detta kan bero på att din internetleverantör blockerar anslutningen. Du kan fortfarande försöka, men Watch Together kanske inte fungerar.',
|
||||
'watchTogether.reconnectingToHost' => 'Återansluter till värd...',
|
||||
'watchTogether.currentPlayback' => 'Aktuell uppspelning',
|
||||
'watchTogether.joinCurrentPlayback' => 'Gå med i aktuell uppspelning',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => 'Hoppa tillbaka till det värden tittar på just nu',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => 'Kunde inte öppna aktuell uppspelning',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} gick med',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} lämnade',
|
||||
'shaders.title' => 'Shaders',
|
||||
|
||||
@@ -972,6 +972,10 @@ class _TranslationsWatchTogetherZh implements TranslationsWatchTogetherEn {
|
||||
@override String get sessionCodeCopied => '会话代码已复制到剪贴板';
|
||||
@override String get relayUnreachable => '无法连接到中继服务器。这可能是由于您的网络运营商屏蔽了连接。您仍然可以尝试,但一起观看功能可能无法正常使用。';
|
||||
@override String get reconnectingToHost => '正在重新连接到主持人...';
|
||||
@override String get currentPlayback => '当前播放';
|
||||
@override String get joinCurrentPlayback => '加入当前播放';
|
||||
@override String get joinCurrentPlaybackDescription => '回到房主当前正在观看的内容';
|
||||
@override String get failedToOpenCurrentPlayback => '无法打开当前播放';
|
||||
@override String participantJoined({required Object name}) => '${name} 加入了';
|
||||
@override String participantLeft({required Object name}) => '${name} 离开了';
|
||||
}
|
||||
@@ -1937,6 +1941,10 @@ extension on TranslationsZh {
|
||||
'watchTogether.sessionCodeCopied' => '会话代码已复制到剪贴板',
|
||||
'watchTogether.relayUnreachable' => '无法连接到中继服务器。这可能是由于您的网络运营商屏蔽了连接。您仍然可以尝试,但一起观看功能可能无法正常使用。',
|
||||
'watchTogether.reconnectingToHost' => '正在重新连接到主持人...',
|
||||
'watchTogether.currentPlayback' => '当前播放',
|
||||
'watchTogether.joinCurrentPlayback' => '加入当前播放',
|
||||
'watchTogether.joinCurrentPlaybackDescription' => '回到房主当前正在观看的内容',
|
||||
'watchTogether.failedToOpenCurrentPlayback' => '无法打开当前播放',
|
||||
'watchTogether.participantJoined' => ({required Object name}) => '${name} 加入了',
|
||||
'watchTogether.participantLeft' => ({required Object name}) => '${name} 离开了',
|
||||
'shaders.title' => '着色器',
|
||||
|
||||
@@ -704,6 +704,10 @@
|
||||
"sessionCodeCopied": "Sessionskod kopierad till urklipp",
|
||||
"relayUnreachable": "Reläservern kan inte nås. Detta kan bero på att din internetleverantör blockerar anslutningen. Du kan fortfarande försöka, men Watch Together kanske inte fungerar.",
|
||||
"reconnectingToHost": "Återansluter till värd...",
|
||||
"currentPlayback": "Aktuell uppspelning",
|
||||
"joinCurrentPlayback": "Gå med i aktuell uppspelning",
|
||||
"joinCurrentPlaybackDescription": "Hoppa tillbaka till det värden tittar på just nu",
|
||||
"failedToOpenCurrentPlayback": "Kunde inte öppna aktuell uppspelning",
|
||||
"participantJoined": "${name} gick med",
|
||||
"participantLeft": "${name} lämnade"
|
||||
},
|
||||
|
||||
@@ -704,6 +704,10 @@
|
||||
"sessionCodeCopied": "会话代码已复制到剪贴板",
|
||||
"relayUnreachable": "无法连接到中继服务器。这可能是由于您的网络运营商屏蔽了连接。您仍然可以尝试,但一起观看功能可能无法正常使用。",
|
||||
"reconnectingToHost": "正在重新连接到主持人...",
|
||||
"currentPlayback": "当前播放",
|
||||
"joinCurrentPlayback": "加入当前播放",
|
||||
"joinCurrentPlaybackDescription": "回到房主当前正在观看的内容",
|
||||
"failedToOpenCurrentPlayback": "无法打开当前播放",
|
||||
"participantJoined": "${name} 加入了",
|
||||
"participantLeft": "${name} 离开了"
|
||||
},
|
||||
|
||||
@@ -43,7 +43,6 @@ import 'livetv/live_tv_screen.dart';
|
||||
import 'search_screen.dart';
|
||||
import 'downloads/downloads_screen.dart';
|
||||
import 'settings/settings_screen.dart';
|
||||
import 'video_player_screen.dart';
|
||||
import 'profile/profile_switch_screen.dart';
|
||||
import '../services/watch_next_service.dart';
|
||||
import '../watch_together/watch_together.dart';
|
||||
@@ -374,27 +373,7 @@ class _MainScreenState extends State<MainScreen> with RouteAware, WindowListener
|
||||
if (!mounted) return; // Check before any context usage
|
||||
|
||||
try {
|
||||
final multiServer = context.read<MultiServerProvider>();
|
||||
final client = multiServer.getClientForServer(serverId);
|
||||
|
||||
if (client == null) {
|
||||
appLogger.w('WatchTogether: Server $serverId not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the metadata for the new media
|
||||
final metadata = await client.getMetadataWithImages(ratingKey);
|
||||
|
||||
if (metadata == null || !mounted) return;
|
||||
|
||||
// Use push to preserve WatchTogetherScreen in navigation stack
|
||||
// VideoPlayerScreen handles its own replacement via onPlayerMediaSwitched
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
MaterialPageRoute(
|
||||
settings: const RouteSettings(name: kVideoPlayerRouteName),
|
||||
builder: (_) => VideoPlayerScreen(metadata: metadata),
|
||||
),
|
||||
);
|
||||
await navigateToWatchTogetherPlayback(context, ratingKey: ratingKey, serverId: serverId);
|
||||
} catch (e) {
|
||||
appLogger.e('WatchTogether: Failed to navigate to media', error: e);
|
||||
}
|
||||
@@ -1114,9 +1093,7 @@ class _MainScreenState extends State<MainScreen> with RouteAware, WindowListener
|
||||
builder: (context, settingsProvider, child) {
|
||||
final hideLabels = !settingsProvider.showNavBarLabels;
|
||||
return NavigationBarTheme(
|
||||
data: NavigationBarTheme.of(context).copyWith(
|
||||
height: hideLabels ? 56 : null,
|
||||
),
|
||||
data: NavigationBarTheme.of(context).copyWith(height: hideLabels ? 56 : null),
|
||||
child: NavigationBar(
|
||||
selectedIndex: _currentIndex,
|
||||
onDestinationSelected: _selectTab,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -6,6 +8,7 @@ import '../mpv/mpv.dart';
|
||||
import '../models/plex_metadata.dart';
|
||||
import '../models/plex_video_playback_data.dart';
|
||||
import '../providers/download_provider.dart';
|
||||
import '../providers/multi_server_provider.dart';
|
||||
import '../screens/video_player_screen.dart';
|
||||
import '../services/external_player_service.dart';
|
||||
import '../services/settings_service.dart';
|
||||
@@ -14,6 +17,15 @@ import 'app_logger.dart';
|
||||
|
||||
const String kVideoPlayerRouteName = '/video_player';
|
||||
|
||||
class WatchTogetherPlaybackNavigationException implements Exception {
|
||||
final String message;
|
||||
|
||||
const WatchTogetherPlaybackNavigationException(this.message);
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
||||
|
||||
/// Navigates to the VideoPlayerScreen with instant transitions to prevent white flash.
|
||||
///
|
||||
/// This utility function provides a consistent way to navigate to the video player
|
||||
@@ -175,3 +187,28 @@ Future<bool?> navigateToVideoPlayerWithRefresh(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Resolves the current Watch Together media and opens the video player.
|
||||
Future<void> navigateToWatchTogetherPlayback(
|
||||
BuildContext context, {
|
||||
required String ratingKey,
|
||||
required String serverId,
|
||||
VoidCallback? onBeforeNavigate,
|
||||
}) async {
|
||||
final multiServer = context.read<MultiServerProvider>();
|
||||
final client = multiServer.getClientForServer(serverId);
|
||||
|
||||
if (client == null) {
|
||||
throw const WatchTogetherPlaybackNavigationException('Watch Together server is unavailable');
|
||||
}
|
||||
|
||||
final metadata = await client.getMetadataWithImages(ratingKey);
|
||||
if (metadata == null) {
|
||||
throw const WatchTogetherPlaybackNavigationException('Current Watch Together media is unavailable');
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
onBeforeNavigate?.call();
|
||||
unawaited(navigateToVideoPlayer(context, metadata: metadata));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
final List<Participant> _participants = [];
|
||||
bool _isSyncing = false;
|
||||
String _displayName = 'User';
|
||||
String? _lastHandledCurrentPlaybackKey;
|
||||
|
||||
// Host reconnect grace period
|
||||
Timer? _hostReconnectTimer;
|
||||
@@ -79,12 +80,79 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
String? get currentMediaRatingKey => _session?.mediaRatingKey;
|
||||
String? get currentMediaServerId => _session?.mediaServerId;
|
||||
String? get currentMediaTitle => _session?.mediaTitle;
|
||||
bool get hasCurrentPlayback =>
|
||||
currentMediaRatingKey != null && currentMediaServerId != null && currentMediaTitle != null;
|
||||
|
||||
/// Set the display name for this user
|
||||
void setDisplayName(String name) {
|
||||
_displayName = name;
|
||||
}
|
||||
|
||||
String? _buildPlaybackKey(String? ratingKey, String? serverId) {
|
||||
if (ratingKey == null || serverId == null) return null;
|
||||
return '$serverId:$ratingKey';
|
||||
}
|
||||
|
||||
void _updateCurrentPlaybackSnapshot({
|
||||
required String ratingKey,
|
||||
required String serverId,
|
||||
required String mediaTitle,
|
||||
}) {
|
||||
_session = _session?.copyWith(mediaRatingKey: ratingKey, mediaServerId: serverId, mediaTitle: mediaTitle);
|
||||
}
|
||||
|
||||
void _clearCurrentPlaybackSnapshot() {
|
||||
final session = _session;
|
||||
if (session == null) return;
|
||||
|
||||
_session = WatchSession(
|
||||
sessionId: session.sessionId,
|
||||
role: session.role,
|
||||
controlMode: session.controlMode,
|
||||
state: session.state,
|
||||
participants: session.participants,
|
||||
errorMessage: session.errorMessage,
|
||||
hostPeerId: session.hostPeerId,
|
||||
);
|
||||
_lastHandledCurrentPlaybackKey = null;
|
||||
}
|
||||
|
||||
void _dispatchCurrentPlayback({
|
||||
required String ratingKey,
|
||||
required String serverId,
|
||||
required String mediaTitle,
|
||||
required String source,
|
||||
}) {
|
||||
final callback = onPlayerMediaSwitched ?? onMediaSwitched;
|
||||
if (callback == null) {
|
||||
appLogger.d('WatchTogether: No media switch callback set, keeping snapshot from $source only');
|
||||
return;
|
||||
}
|
||||
|
||||
_lastHandledCurrentPlaybackKey = _buildPlaybackKey(ratingKey, serverId);
|
||||
appLogger.d('WatchTogether: Dispatching current playback from $source: $mediaTitle');
|
||||
callback(ratingKey, serverId, mediaTitle);
|
||||
}
|
||||
|
||||
void markCurrentPlaybackHandled({required String ratingKey, required String serverId}) {
|
||||
_lastHandledCurrentPlaybackKey = _buildPlaybackKey(ratingKey, serverId);
|
||||
}
|
||||
|
||||
void requestCurrentPlaybackSnapshot() {
|
||||
if (isHost || _peerService == null || _session == null || _peerService!.myPeerId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final request = SyncMessage.requestSessionConfig(peerId: _peerService!.myPeerId);
|
||||
if (_session!.hostPeerId != null) {
|
||||
appLogger.d('WatchTogether: Requesting current playback snapshot from host');
|
||||
_peerService!.sendTo(_session!.hostPeerId!, request);
|
||||
} else {
|
||||
appLogger.d('WatchTogether: Host peer unknown, broadcasting current playback snapshot request');
|
||||
_peerService!.broadcast(request);
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire up sync manager's state change callback to update provider state
|
||||
void _wireSyncStateChanges() {
|
||||
_syncManager!.onSyncStateChanged = (isSyncing) {
|
||||
@@ -102,6 +170,7 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
}) async {
|
||||
// Clean up any existing session
|
||||
await leaveSession();
|
||||
_lastHandledCurrentPlaybackKey = null;
|
||||
|
||||
appLogger.d('WatchTogether: Creating session with control mode: $controlMode');
|
||||
|
||||
@@ -148,6 +217,7 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
Future<void> joinSession(String sessionId) async {
|
||||
// Clean up any existing session
|
||||
await leaveSession();
|
||||
_lastHandledCurrentPlaybackKey = null;
|
||||
|
||||
appLogger.d('WatchTogether: Joining session: $sessionId');
|
||||
|
||||
@@ -185,6 +255,7 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
|
||||
// Announce join to other participants
|
||||
_syncManager!.announceJoin(_displayName);
|
||||
requestCurrentPlaybackSnapshot();
|
||||
|
||||
notifyListeners();
|
||||
appLogger.d('WatchTogether: Joined session successfully');
|
||||
@@ -230,6 +301,7 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
_session = null;
|
||||
_participants.clear();
|
||||
_isSyncing = false;
|
||||
_lastHandledCurrentPlaybackKey = null;
|
||||
|
||||
notifyListeners();
|
||||
appLogger.d('WatchTogether: Session left');
|
||||
@@ -266,6 +338,10 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
_cancelHostReconnectGracePeriod();
|
||||
}
|
||||
|
||||
if (!isHost && peerId == _session?.hostPeerId) {
|
||||
requestCurrentPlaybackSnapshot();
|
||||
}
|
||||
|
||||
// Peer will announce themselves with a join message
|
||||
notifyListeners();
|
||||
});
|
||||
@@ -413,16 +489,25 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// If config contains media info that differs from our current state,
|
||||
// trigger a media switch so the guest navigates to the correct content.
|
||||
// This handles the case where a guest missed a mediaSwitch broadcast
|
||||
// (e.g., host switched episodes while guest was popping out of the player).
|
||||
if (message.ratingKey != null &&
|
||||
message.serverId != null &&
|
||||
message.mediaTitle != null &&
|
||||
message.ratingKey != _session?.mediaRatingKey) {
|
||||
appLogger.d('WatchTogether: Session config contains different media, triggering switch');
|
||||
_handleMediaSwitch(message);
|
||||
if (message.ratingKey != null && message.serverId != null && message.mediaTitle != null) {
|
||||
final playbackKey = _buildPlaybackKey(message.ratingKey, message.serverId);
|
||||
final shouldDispatch = playbackKey != _lastHandledCurrentPlaybackKey;
|
||||
|
||||
_updateCurrentPlaybackSnapshot(
|
||||
ratingKey: message.ratingKey!,
|
||||
serverId: message.serverId!,
|
||||
mediaTitle: message.mediaTitle!,
|
||||
);
|
||||
notifyListeners();
|
||||
|
||||
if (shouldDispatch) {
|
||||
_dispatchCurrentPlayback(
|
||||
ratingKey: message.ratingKey!,
|
||||
serverId: message.serverId!,
|
||||
mediaTitle: message.mediaTitle!,
|
||||
source: 'session config',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,37 +555,33 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
void _handleMediaSwitch(SyncMessage message) {
|
||||
if (isHost) return; // Host doesn't need to handle their own switch
|
||||
|
||||
// Skip if already playing this media (prevents duplicate navigation from duplicate messages)
|
||||
if (_session?.mediaRatingKey == message.ratingKey) {
|
||||
appLogger.d('WatchTogether: Ignoring duplicate media switch for ${message.ratingKey}');
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.ratingKey == null || message.serverId == null || message.mediaTitle == null) {
|
||||
appLogger.w('WatchTogether: Received incomplete media switch message');
|
||||
return;
|
||||
}
|
||||
|
||||
appLogger.d('WatchTogether: Received media switch: ${message.mediaTitle}');
|
||||
final playbackKey = _buildPlaybackKey(message.ratingKey, message.serverId);
|
||||
final shouldDispatch = playbackKey != _lastHandledCurrentPlaybackKey;
|
||||
|
||||
// Dispatch callback BEFORE updating session state.
|
||||
// If the callback fails silently (e.g., player disposed mid-animation),
|
||||
// the ratingKey stays unchanged so the next positionSync or re-sent
|
||||
// message can retry instead of being treated as a duplicate.
|
||||
if (onPlayerMediaSwitched != null) {
|
||||
onPlayerMediaSwitched!(message.ratingKey!, message.serverId!, message.mediaTitle!);
|
||||
} else {
|
||||
onMediaSwitched?.call(message.ratingKey!, message.serverId!, message.mediaTitle!);
|
||||
_updateCurrentPlaybackSnapshot(
|
||||
ratingKey: message.ratingKey!,
|
||||
serverId: message.serverId!,
|
||||
mediaTitle: message.mediaTitle!,
|
||||
);
|
||||
notifyListeners();
|
||||
|
||||
if (!shouldDispatch) {
|
||||
appLogger.d('WatchTogether: Ignoring duplicate media switch for ${message.ratingKey}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update local session state after successful dispatch
|
||||
_session = _session?.copyWith(
|
||||
mediaRatingKey: message.ratingKey,
|
||||
mediaServerId: message.serverId,
|
||||
mediaTitle: message.mediaTitle,
|
||||
appLogger.d('WatchTogether: Received media switch: ${message.mediaTitle}');
|
||||
_dispatchCurrentPlayback(
|
||||
ratingKey: message.ratingKey!,
|
||||
serverId: message.serverId!,
|
||||
mediaTitle: message.mediaTitle!,
|
||||
source: 'media switch',
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Notify guests that host is exiting the video player
|
||||
@@ -522,23 +603,13 @@ class WatchTogetherProvider with ChangeNotifier {
|
||||
|
||||
appLogger.d('WatchTogether: Host exited player, callback set: ${onHostExitedPlayer != null}');
|
||||
|
||||
// Clear media info so the next mediaSwitch (even same ratingKey) isn't
|
||||
// treated as a duplicate by the guard in _handleMediaSwitch.
|
||||
// copyWith uses ?? so it can't set fields to null — construct directly.
|
||||
_session = WatchSession(
|
||||
sessionId: _session!.sessionId,
|
||||
role: _session!.role,
|
||||
controlMode: _session!.controlMode,
|
||||
state: _session!.state,
|
||||
participants: _session!.participants,
|
||||
errorMessage: _session!.errorMessage,
|
||||
hostPeerId: _session!.hostPeerId,
|
||||
);
|
||||
_clearCurrentPlaybackSnapshot();
|
||||
|
||||
// Clear the player callback BEFORE popping so that any mediaSwitch message
|
||||
// arriving during the pop animation routes to MainScreen's handler instead
|
||||
// of the dying VideoPlayerScreen.
|
||||
onPlayerMediaSwitched = null;
|
||||
notifyListeners();
|
||||
|
||||
// Trigger callback for the app to navigate guest out of player
|
||||
if (onHostExitedPlayer != null) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import '../../focus/focusable_button.dart';
|
||||
import '../../focus/focusable_wrapper.dart';
|
||||
import '../../utils/app_logger.dart';
|
||||
import '../../utils/dialogs.dart';
|
||||
import '../../utils/snackbar_helper.dart';
|
||||
import '../../utils/video_player_navigation.dart';
|
||||
import '../../widgets/focused_scroll_scaffold.dart';
|
||||
import '../models/watch_session.dart';
|
||||
import '../providers/watch_together_provider.dart';
|
||||
@@ -391,11 +393,16 @@ class _ActiveSessionContent extends StatelessWidget {
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
if (!watchTogether.isHost && watchTogether.hasCurrentPlayback) ...[
|
||||
_JoinCurrentPlaybackCard(watchTogether: watchTogether),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
// Leave/End Session Button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FocusableButton(
|
||||
autofocus: true,
|
||||
autofocus: watchTogether.isHost || !watchTogether.hasCurrentPlayback,
|
||||
onPressed: () => _leaveSession(context),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _leaveSession(context),
|
||||
@@ -427,6 +434,103 @@ class _ActiveSessionContent extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _JoinCurrentPlaybackCard extends StatefulWidget {
|
||||
final WatchTogetherProvider watchTogether;
|
||||
|
||||
const _JoinCurrentPlaybackCard({required this.watchTogether});
|
||||
|
||||
@override
|
||||
State<_JoinCurrentPlaybackCard> createState() => _JoinCurrentPlaybackCardState();
|
||||
}
|
||||
|
||||
class _JoinCurrentPlaybackCardState extends State<_JoinCurrentPlaybackCard> {
|
||||
bool _isJoining = false;
|
||||
|
||||
Future<void> _joinCurrentPlayback() async {
|
||||
final ratingKey = widget.watchTogether.currentMediaRatingKey;
|
||||
final serverId = widget.watchTogether.currentMediaServerId;
|
||||
if (ratingKey == null || serverId == null) return;
|
||||
|
||||
setState(() => _isJoining = true);
|
||||
|
||||
try {
|
||||
await navigateToWatchTogetherPlayback(
|
||||
context,
|
||||
ratingKey: ratingKey,
|
||||
serverId: serverId,
|
||||
onBeforeNavigate: () {
|
||||
widget.watchTogether.markCurrentPlaybackHandled(ratingKey: ratingKey, serverId: serverId);
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e('WatchTogether: Failed to open current playback', error: e, stackTrace: stackTrace);
|
||||
if (mounted) {
|
||||
showErrorSnackBar(context, t.watchTogether.failedToOpenCurrentPlayback);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isJoining = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final mediaTitle = widget.watchTogether.currentMediaTitle;
|
||||
if (widget.watchTogether.isHost || mediaTitle == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Symbols.play_circle_rounded, color: theme.colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(t.watchTogether.currentPlayback, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
t.watchTogether.joinCurrentPlaybackDescription,
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(mediaTitle, style: theme.textTheme.bodyLarge),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FocusableButton(
|
||||
autofocus: true,
|
||||
onPressed: _isJoining ? null : _joinCurrentPlayback,
|
||||
child: FilledButton.icon(
|
||||
onPressed: _isJoining ? null : _joinCurrentPlayback,
|
||||
icon: _isJoining
|
||||
? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: const Icon(Symbols.play_arrow_rounded),
|
||||
label: Text(_isJoining ? t.watchTogether.joining : t.watchTogether.joinCurrentPlayback),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tappable session code row with copy functionality
|
||||
class _SessionCodeRow extends StatelessWidget {
|
||||
final String sessionId;
|
||||
|
||||
Reference in New Issue
Block a user