diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 0b7cab3e..3adec194 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 7 -/// Strings: 3764 (537 per locale) +/// Strings: 3771 (538 per locale) /// -/// Built on 2026-01-15 at 19:06 UTC +/// Built on 2026-01-16 at 11:07 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -962,6 +962,7 @@ class _StringsWatchTogetherEn { String get joinInstructions => 'Enter the session code shared by the host to join their watch session.'; String get failedToCreate => 'Failed to create session'; String get failedToJoin => 'Failed to join session'; + String get sessionCodeCopied => 'Session code copied to clipboard'; } // Path: downloads @@ -1899,6 +1900,7 @@ class _StringsWatchTogetherDe implements _StringsWatchTogetherEn { @override String get joinInstructions => 'Gib den vom Host geteilten Sitzungscode ein, um seiner Schausitzung beizutreten.'; @override String get failedToCreate => 'Sitzung konnte nicht erstellt werden'; @override String get failedToJoin => 'Sitzung konnte nicht beigetreten werden'; + @override String get sessionCodeCopied => 'Sitzungscode in Zwischenablage kopiert'; } // Path: hotkeys.actions @@ -2813,6 +2815,7 @@ class _StringsWatchTogetherIt implements _StringsWatchTogetherEn { @override String get joinInstructions => 'Inserisci il codice sessione condiviso dall\'host per unirti alla loro sessione di visione.'; @override String get failedToCreate => 'Impossibile creare la sessione'; @override String get failedToJoin => 'Impossibile unirsi alla sessione'; + @override String get sessionCodeCopied => 'Codice sessione copiato negli appunti'; } // Path: hotkeys.actions @@ -3704,6 +3707,7 @@ class _StringsWatchTogetherKo implements _StringsWatchTogetherEn { @override String get joinInstructions => '호스트가 공유한 세션 코드를 입력하여 시청 세션에 참여하세요.'; @override String get failedToCreate => '세션 생성 실패'; @override String get failedToJoin => '세션 참여 실패'; + @override String get sessionCodeCopied => '세션 코드가 클립보드에 복사되었습니다'; } // Path: downloads @@ -4641,6 +4645,7 @@ class _StringsWatchTogetherNl implements _StringsWatchTogetherEn { @override String get joinInstructions => 'Voer de sessiecode in die door de host is gedeeld om deel te nemen aan hun kijksessie.'; @override String get failedToCreate => 'Sessie maken mislukt'; @override String get failedToJoin => 'Sessie deelnemen mislukt'; + @override String get sessionCodeCopied => 'Sessiecode gekopieerd naar klembord'; } // Path: hotkeys.actions @@ -5555,6 +5560,7 @@ class _StringsWatchTogetherSv implements _StringsWatchTogetherEn { @override String get joinInstructions => 'Ange sessionskoden som delats av värden för att gå med i deras tittarsession.'; @override String get failedToCreate => 'Det gick inte att skapa session'; @override String get failedToJoin => 'Det gick inte att gå med i session'; + @override String get sessionCodeCopied => 'Sessionskod kopierad till urklipp'; } // Path: hotkeys.actions @@ -6469,6 +6475,7 @@ class _StringsWatchTogetherZh implements _StringsWatchTogetherEn { @override String get joinInstructions => '输入主持人分享的会话代码以加入他们的观看会话。'; @override String get failedToCreate => '创建会话失败'; @override String get failedToJoin => '加入会话失败'; + @override String get sessionCodeCopied => '会话代码已复制到剪贴板'; } // Path: hotkeys.actions @@ -7053,6 +7060,7 @@ extension on Translations { case 'watchTogether.joinInstructions': return 'Enter the session code shared by the host to join their watch session.'; case 'watchTogether.failedToCreate': return 'Failed to create session'; case 'watchTogether.failedToJoin': return 'Failed to join session'; + case 'watchTogether.sessionCodeCopied': return 'Session code copied to clipboard'; case 'downloads.title': return 'Downloads'; case 'downloads.manage': return 'Manage'; case 'downloads.tvShows': return 'TV Shows'; @@ -7613,6 +7621,7 @@ extension on _StringsDe { case 'watchTogether.joinInstructions': return 'Gib den vom Host geteilten Sitzungscode ein, um seiner Schausitzung beizutreten.'; case 'watchTogether.failedToCreate': return 'Sitzung konnte nicht erstellt werden'; case 'watchTogether.failedToJoin': return 'Sitzung konnte nicht beigetreten werden'; + case 'watchTogether.sessionCodeCopied': return 'Sitzungscode in Zwischenablage kopiert'; default: return null; } } @@ -8159,6 +8168,7 @@ extension on _StringsIt { case 'watchTogether.joinInstructions': return 'Inserisci il codice sessione condiviso dall\'host per unirti alla loro sessione di visione.'; case 'watchTogether.failedToCreate': return 'Impossibile creare la sessione'; case 'watchTogether.failedToJoin': return 'Impossibile unirsi alla sessione'; + case 'watchTogether.sessionCodeCopied': return 'Codice sessione copiato negli appunti'; default: return null; } } @@ -8691,6 +8701,7 @@ extension on _StringsKo { case 'watchTogether.joinInstructions': return '호스트가 공유한 세션 코드를 입력하여 시청 세션에 참여하세요.'; case 'watchTogether.failedToCreate': return '세션 생성 실패'; case 'watchTogether.failedToJoin': return '세션 참여 실패'; + case 'watchTogether.sessionCodeCopied': return '세션 코드가 클립보드에 복사되었습니다'; case 'downloads.title': return '다운로드'; case 'downloads.manage': return '관리'; case 'downloads.tvShows': return 'TV 프로그램'; @@ -9251,6 +9262,7 @@ extension on _StringsNl { case 'watchTogether.joinInstructions': return 'Voer de sessiecode in die door de host is gedeeld om deel te nemen aan hun kijksessie.'; case 'watchTogether.failedToCreate': return 'Sessie maken mislukt'; case 'watchTogether.failedToJoin': return 'Sessie deelnemen mislukt'; + case 'watchTogether.sessionCodeCopied': return 'Sessiecode gekopieerd naar klembord'; default: return null; } } @@ -9797,6 +9809,7 @@ extension on _StringsSv { case 'watchTogether.joinInstructions': return 'Ange sessionskoden som delats av värden för att gå med i deras tittarsession.'; case 'watchTogether.failedToCreate': return 'Det gick inte att skapa session'; case 'watchTogether.failedToJoin': return 'Det gick inte att gå med i session'; + case 'watchTogether.sessionCodeCopied': return 'Sessionskod kopierad till urklipp'; default: return null; } } @@ -10343,6 +10356,7 @@ extension on _StringsZh { case 'watchTogether.joinInstructions': return '输入主持人分享的会话代码以加入他们的观看会话。'; case 'watchTogether.failedToCreate': return '创建会话失败'; case 'watchTogether.failedToJoin': return '加入会话失败'; + case 'watchTogether.sessionCodeCopied': return '会话代码已复制到剪贴板'; default: return null; } } diff --git a/lib/i18n/strings.i18n.json b/lib/i18n/strings.i18n.json index dc62f498..86196b2f 100644 --- a/lib/i18n/strings.i18n.json +++ b/lib/i18n/strings.i18n.json @@ -587,7 +587,8 @@ "codeMustBe8Chars": "Session code must be 8 characters", "joinInstructions": "Enter the session code shared by the host to join their watch session.", "failedToCreate": "Failed to create session", - "failedToJoin": "Failed to join session" + "failedToJoin": "Failed to join session", + "sessionCodeCopied": "Session code copied to clipboard" }, "downloads": { "title": "Downloads", diff --git a/lib/i18n/strings_de.i18n.json b/lib/i18n/strings_de.i18n.json index 9ef0c9e2..00426547 100644 --- a/lib/i18n/strings_de.i18n.json +++ b/lib/i18n/strings_de.i18n.json @@ -605,6 +605,7 @@ "codeMustBe8Chars": "Sitzungscode muss 8 Zeichen haben", "joinInstructions": "Gib den vom Host geteilten Sitzungscode ein, um seiner Schausitzung beizutreten.", "failedToCreate": "Sitzung konnte nicht erstellt werden", - "failedToJoin": "Sitzung konnte nicht beigetreten werden" + "failedToJoin": "Sitzung konnte nicht beigetreten werden", + "sessionCodeCopied": "Sitzungscode in Zwischenablage kopiert" } } diff --git a/lib/i18n/strings_it.i18n.json b/lib/i18n/strings_it.i18n.json index f7dbd287..c2016066 100644 --- a/lib/i18n/strings_it.i18n.json +++ b/lib/i18n/strings_it.i18n.json @@ -605,6 +605,7 @@ "codeMustBe8Chars": "Il codice sessione deve essere di 8 caratteri", "joinInstructions": "Inserisci il codice sessione condiviso dall'host per unirti alla loro sessione di visione.", "failedToCreate": "Impossibile creare la sessione", - "failedToJoin": "Impossibile unirsi alla sessione" + "failedToJoin": "Impossibile unirsi alla sessione", + "sessionCodeCopied": "Codice sessione copiato negli appunti" } } diff --git a/lib/i18n/strings_ko.i18n.json b/lib/i18n/strings_ko.i18n.json index f5a6f295..d509c0c9 100644 --- a/lib/i18n/strings_ko.i18n.json +++ b/lib/i18n/strings_ko.i18n.json @@ -589,7 +589,8 @@ "codeMustBe8Chars": "세션 코드는 반드시 8자리여야 합니다", "joinInstructions": "호스트가 공유한 세션 코드를 입력하여 시청 세션에 참여하세요.", "failedToCreate": "세션 생성 실패", - "failedToJoin": "세션 참여 실패" + "failedToJoin": "세션 참여 실패", + "sessionCodeCopied": "세션 코드가 클립보드에 복사되었습니다" }, "downloads": { "title": "다운로드", diff --git a/lib/i18n/strings_nl.i18n.json b/lib/i18n/strings_nl.i18n.json index aefbbe9c..e4869100 100644 --- a/lib/i18n/strings_nl.i18n.json +++ b/lib/i18n/strings_nl.i18n.json @@ -605,6 +605,7 @@ "codeMustBe8Chars": "Sessiecode moet 8 tekens zijn", "joinInstructions": "Voer de sessiecode in die door de host is gedeeld om deel te nemen aan hun kijksessie.", "failedToCreate": "Sessie maken mislukt", - "failedToJoin": "Sessie deelnemen mislukt" + "failedToJoin": "Sessie deelnemen mislukt", + "sessionCodeCopied": "Sessiecode gekopieerd naar klembord" } } diff --git a/lib/i18n/strings_sv.i18n.json b/lib/i18n/strings_sv.i18n.json index 0fd3796e..e69acbe9 100644 --- a/lib/i18n/strings_sv.i18n.json +++ b/lib/i18n/strings_sv.i18n.json @@ -605,6 +605,7 @@ "codeMustBe8Chars": "Sessionskod måste vara 8 tecken", "joinInstructions": "Ange sessionskoden som delats av värden för att gå med i deras tittarsession.", "failedToCreate": "Det gick inte att skapa session", - "failedToJoin": "Det gick inte att gå med i session" + "failedToJoin": "Det gick inte att gå med i session", + "sessionCodeCopied": "Sessionskod kopierad till urklipp" } } diff --git a/lib/i18n/strings_zh.i18n.json b/lib/i18n/strings_zh.i18n.json index 3a8a15af..cb945569 100644 --- a/lib/i18n/strings_zh.i18n.json +++ b/lib/i18n/strings_zh.i18n.json @@ -605,6 +605,7 @@ "codeMustBe8Chars": "会话代码必须是8个字符", "joinInstructions": "输入主持人分享的会话代码以加入他们的观看会话。", "failedToCreate": "创建会话失败", - "failedToJoin": "加入会话失败" + "failedToJoin": "加入会话失败", + "sessionCodeCopied": "会话代码已复制到剪贴板" } } diff --git a/lib/watch_together/screens/watch_together_screen.dart b/lib/watch_together/screens/watch_together_screen.dart index 7eb24532..78ca2424 100644 --- a/lib/watch_together/screens/watch_together_screen.dart +++ b/lib/watch_together/screens/watch_together_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; @@ -218,13 +219,7 @@ class _ActiveSessionContent extends StatelessWidget { watchTogether.isHost ? t.watchTogether.hostingSession : t.watchTogether.inSession, style: theme.textTheme.titleMedium, ), - Text( - '${t.watchTogether.sessionCode}: ${session.sessionId}', - style: theme.textTheme.bodySmall?.copyWith( - fontFamily: 'monospace', - color: theme.colorScheme.onSurfaceVariant, - ), - ), + _SessionCodeRow(sessionId: session.sessionId), ], ), ), @@ -360,3 +355,48 @@ class _ActiveSessionContent extends StatelessWidget { } } } + +/// Tappable session code row with copy functionality +class _SessionCodeRow extends StatelessWidget { + final String sessionId; + + const _SessionCodeRow({required this.sessionId}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return InkWell( + onTap: () => _copySessionCode(context), + borderRadius: BorderRadius.circular(4), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${t.watchTogether.sessionCode}: $sessionId', + style: theme.textTheme.bodySmall?.copyWith( + fontFamily: 'monospace', + color: theme.colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(width: 4), + Icon( + Symbols.content_copy_rounded, + size: 14, + color: theme.colorScheme.onSurfaceVariant, + ), + ], + ), + ), + ); + } + + void _copySessionCode(BuildContext context) { + Clipboard.setData(ClipboardData(text: sessionId)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(t.watchTogether.sessionCodeCopied)), + ); + } +} diff --git a/lib/watch_together/widgets/watch_together_overlay.dart b/lib/watch_together/widgets/watch_together_overlay.dart index 3042c075..71bee8f8 100644 --- a/lib/watch_together/widgets/watch_together_overlay.dart +++ b/lib/watch_together/widgets/watch_together_overlay.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; @@ -168,6 +169,46 @@ class _SessionMenuSheet extends StatelessWidget { ], ), + // Session code with copy button + if (provider.sessionId != null) ...[ + const SizedBox(height: 12), + InkWell( + onTap: () => _copySessionCode(context, provider.sessionId!), + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: theme.colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${t.watchTogether.sessionCode}: ', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + Text( + provider.sessionId!, + style: theme.textTheme.bodySmall?.copyWith( + fontFamily: 'monospace', + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 8), + Icon( + Symbols.content_copy_rounded, + size: 16, + color: theme.colorScheme.onSurfaceVariant, + ), + ], + ), + ), + ), + ], + const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), @@ -218,6 +259,13 @@ class _SessionMenuSheet extends StatelessWidget { ); } + void _copySessionCode(BuildContext context, String sessionId) { + Clipboard.setData(ClipboardData(text: sessionId)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(t.watchTogether.sessionCodeCopied)), + ); + } + void _confirmLeave(BuildContext context) { showDialog( context: context,