diff --git a/arb/intl_en.arb b/arb/intl_en.arb index 8c93cdfeb..4e18fa0ff 100644 --- a/arb/intl_en.arb +++ b/arb/intl_en.arb @@ -138,6 +138,7 @@ "recoveryAll": "Recovery all data", "recoverySuccess": "Recovery success", "backupSuccess": "Backup success", + "copied": "Copied", "noInfo": "No info", "pleaseBindWebDAV": "Please bind WebDAV", "bind": "Bind", diff --git a/arb/intl_ja.arb b/arb/intl_ja.arb index f9b7ff4a9..aef8f0e61 100644 --- a/arb/intl_ja.arb +++ b/arb/intl_ja.arb @@ -138,6 +138,7 @@ "recoveryAll": "全データ復元", "recoverySuccess": "復元成功", "backupSuccess": "バックアップ成功", + "copied": "コピーしました", "noInfo": "情報なし", "pleaseBindWebDAV": "WebDAVをバインドしてください", "bind": "バインド", diff --git a/arb/intl_ru.arb b/arb/intl_ru.arb index 433ed57b8..e4c1e5c53 100644 --- a/arb/intl_ru.arb +++ b/arb/intl_ru.arb @@ -138,6 +138,7 @@ "recoveryAll": "Восстановить все данные", "recoverySuccess": "Восстановление успешно", "backupSuccess": "Резервное копирование успешно", + "copied": "Скопировано", "noInfo": "Нет информации", "pleaseBindWebDAV": "Пожалуйста, привяжите WebDAV", "bind": "Привязать", diff --git a/arb/intl_zh_CN.arb b/arb/intl_zh_CN.arb index abba2bc23..b10a476b2 100644 --- a/arb/intl_zh_CN.arb +++ b/arb/intl_zh_CN.arb @@ -138,6 +138,7 @@ "recoveryAll": "恢复所有数据", "recoverySuccess": "恢复成功", "backupSuccess": "备份成功", + "copied": "已复制", "noInfo": "暂无信息", "pleaseBindWebDAV": "请绑定WebDAV", "bind": "绑定", diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index eb5ae3b40..21c33c3bb 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -171,6 +171,7 @@ class MessageLookup extends MessageLookupByLibrary { "Sync data via WebDAV or file", ), "backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"), + "copied": MessageLookupByLibrary.simpleMessage("Copied"), "basicConfig": MessageLookupByLibrary.simpleMessage("Basic configuration"), "basicConfigDesc": MessageLookupByLibrary.simpleMessage( "Modify the basic configuration globally", diff --git a/lib/l10n/intl/messages_ja.dart b/lib/l10n/intl/messages_ja.dart index fb2a3cf1d..56dce88e0 100644 --- a/lib/l10n/intl/messages_ja.dart +++ b/lib/l10n/intl/messages_ja.dart @@ -122,6 +122,7 @@ class MessageLookup extends MessageLookupByLibrary { "WebDAVまたはファイルでデータを同期", ), "backupSuccess": MessageLookupByLibrary.simpleMessage("バックアップ成功"), + "copied": MessageLookupByLibrary.simpleMessage("コピーしました"), "basicConfig": MessageLookupByLibrary.simpleMessage("基本設定"), "basicConfigDesc": MessageLookupByLibrary.simpleMessage("基本設定をグローバルに変更"), "bind": MessageLookupByLibrary.simpleMessage("バインド"), diff --git a/lib/l10n/intl/messages_ru.dart b/lib/l10n/intl/messages_ru.dart index 9e57f589d..b2b4f423f 100644 --- a/lib/l10n/intl/messages_ru.dart +++ b/lib/l10n/intl/messages_ru.dart @@ -170,6 +170,7 @@ class MessageLookup extends MessageLookupByLibrary { "backupSuccess": MessageLookupByLibrary.simpleMessage( "Резервное копирование успешно", ), + "copied": MessageLookupByLibrary.simpleMessage("Скопировано"), "basicConfig": MessageLookupByLibrary.simpleMessage("Базовая конфигурация"), "basicConfigDesc": MessageLookupByLibrary.simpleMessage( "Глобальное изменение базовых настроек", diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index c1e166293..f0c58b0d8 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -114,6 +114,7 @@ class MessageLookup extends MessageLookupByLibrary { "通过WebDAV或者文件同步数据", ), "backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"), + "copied": MessageLookupByLibrary.simpleMessage("已复制"), "basicConfig": MessageLookupByLibrary.simpleMessage("基本配置"), "basicConfigDesc": MessageLookupByLibrary.simpleMessage("全局修改基本配置"), "bind": MessageLookupByLibrary.simpleMessage("绑定"), diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 7db6f0353..de176e6e1 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -1074,6 +1074,16 @@ class AppLocalizations { ); } + /// `Copied` + String get copied { + return Intl.message( + 'Copied', + name: 'copied', + desc: '', + args: [], + ); + } + /// `No info` String get noInfo { return Intl.message('No info', name: 'noInfo', desc: '', args: []); diff --git a/lib/views/dashboard/widgets/intranet_ip.dart b/lib/views/dashboard/widgets/intranet_ip.dart index 397f80a3a..488c22504 100644 --- a/lib/views/dashboard/widgets/intranet_ip.dart +++ b/lib/views/dashboard/widgets/intranet_ip.dart @@ -1,20 +1,36 @@ import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/providers/app.dart'; +import 'package:fl_clash/providers/config.dart'; import 'package:fl_clash/state.dart'; import 'package:fl_clash/widgets/widgets.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -class IntranetIP extends StatelessWidget { +class IntranetIP extends ConsumerWidget { const IntranetIP({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final localIp = ref.watch(localIpProvider); + final mixedPort = ref.watch( + patchClashConfigProvider.select((state) => state.mixedPort), + ); + return SizedBox( height: getWidgetHeight(1), child: CommonCard( info: Info(label: appLocalizations.intranetIP, iconData: Icons.devices), - onPressed: () {}, + onPressed: localIp != null && localIp.isNotEmpty + ? () async { + final url = 'http://$localIp:$mixedPort'; + await Clipboard.setData(ClipboardData(text: url)); + globalState.showMessage( + title: appLocalizations.tip, + message: TextSpan(text: '${appLocalizations.copied}\n$url'), + ); + } + : null, child: Container( padding: baseInfoEdgeInsets.copyWith(top: 0), child: Column( @@ -23,33 +39,40 @@ class IntranetIP extends StatelessWidget { children: [ SizedBox( height: globalState.measure.bodyMediumHeight + 2, - child: Consumer( - builder: (_, ref, _) { - final localIp = ref.watch(localIpProvider); - return FadeThroughBox( - child: localIp != null - ? TooltipText( - text: Text( - localIp.isNotEmpty - ? localIp - : appLocalizations.noNetwork, - style: context.textTheme.bodyMedium?.toLight - .adjustSize(1), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ) - : Container( - padding: EdgeInsets.all(2), - child: AspectRatio( - aspectRatio: 1, - child: CircularProgressIndicator( - strokeWidth: 2, + child: FadeThroughBox( + child: localIp != null + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: TooltipText( + text: Text( + localIp.isNotEmpty + ? localIp + : appLocalizations.noNetwork, + style: context.textTheme.bodyMedium?.toLight + .adjustSize(1), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ), - ); - }, + if (localIp.isNotEmpty) + Icon( + Icons.content_copy, + size: 16, + color: context.colorScheme.onSurfaceVariant + .withOpacity(0.6), + ), + ], + ) + : Container( + padding: const EdgeInsets.all(2), + child: AspectRatio( + aspectRatio: 1, + child: CircularProgressIndicator(strokeWidth: 2), + ), + ), ), ), ],