diff --git a/client/lib/screens/contact_page.dart b/client/lib/screens/contact_page.dart index ed22d99..fe80357 100644 --- a/client/lib/screens/contact_page.dart +++ b/client/lib/screens/contact_page.dart @@ -8,6 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../l10n/app_text.dart'; import '../pangolin_theme.dart'; import '../services/channel_launch.dart'; +import '../services/contact_channels.dart'; import '../state/app_providers.dart'; import '../widgets/pangolin_icons.dart'; @@ -19,11 +20,19 @@ class ContactPage extends ConsumerWidget { final c = context.pangolin; final t = ref.watch(appTextProvider); - final channels = <({IconData icon, String name, String sub, bool accent})>[ - (icon: PangolinIcons.send, name: 'Telegram', sub: '@pangolin_app', accent: true), - (icon: PangolinIcons.mail, name: t.contactEmail, sub: 'support@yanmeiai.com', accent: false), - (icon: PangolinIcons.globe, name: t.contactWebsite, sub: 'pangolin.yanmeiai.com', accent: false), - ]; + final channels = kContactChannels + .map((ch) => ( + icon: ch.icon, + name: switch (ch.nameKey) { + 'telegram' => 'Telegram', + 'email' => t.contactEmail, + 'website' => t.contactWebsite, + _ => ch.nameKey, + }, + sub: ch.sub, + accent: ch.accent, + )) + .toList(); return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(32, 16, 32, 24), diff --git a/client/lib/services/contact_channels.dart b/client/lib/services/contact_channels.dart new file mode 100644 index 0000000..c131f68 --- /dev/null +++ b/client/lib/services/contact_channels.dart @@ -0,0 +1,23 @@ +// contact_channels.dart — 联系渠道单源常量(Telegram/邮箱/官网) +// +// 三处引用点:screens/contact_page.dart(桌面联系页)、 +// widgets/account_screens.dart 的 ContactScreen / RedeemScreen。 +// name 用 nameKey 占位,页面侧按 t 映射(telegram→字面量,email/website→AppText), +// 避免把 AppText 塞进常量。点击行为仍由 services/channel_launch.dart::launchChannel 处理。 +import 'package:flutter/widgets.dart'; +import '../widgets/pangolin_icons.dart'; + +class ContactChannel { + const ContactChannel({required this.icon, required this.name, required this.sub, this.accent = false}); + final IconData icon; + final String name; // 展示名(Telegram 用字面量;邮箱/官网用 l10n,在页面侧传入) + final String sub; // handle:@x / a@b / domain(launchChannel 据此推导 URL) + final bool accent; +} + +// name 用 key 占位,页面按 t 映射;Telegram 固定字面量 +const List<({IconData icon, String nameKey, String sub, bool accent})> kContactChannels = [ + (icon: PangolinIcons.send, nameKey: 'telegram', sub: '@pangolin_app', accent: true), + (icon: PangolinIcons.mail, nameKey: 'email', sub: 'support@yanmeiai.com', accent: false), + (icon: PangolinIcons.globe, nameKey: 'website', sub: 'pangolin.yanmeiai.com', accent: false), +]; diff --git a/client/lib/widgets/account_screens.dart b/client/lib/widgets/account_screens.dart index 8836d2d..3079469 100644 --- a/client/lib/widgets/account_screens.dart +++ b/client/lib/widgets/account_screens.dart @@ -11,6 +11,7 @@ import '../l10n/app_text.dart'; import '../models/device.dart'; import '../pangolin_theme.dart'; import '../services/channel_launch.dart'; +import '../services/contact_channels.dart'; import '../screens/settings_page.dart'; import '../services/device_identity.dart'; import 'adaptive_menu.dart'; @@ -374,11 +375,6 @@ class _RedeemScreenState extends ConsumerState { Widget build(BuildContext context) { final c = context.pangolin; final t = widget.t; - final channels = [ - (icon: PangolinIcons.send, name: 'Telegram', sub: '@pangolin_app', accent: true), - (icon: PangolinIcons.mail, name: t.chEmail, sub: 'support@yanmeiai.com', accent: false), - (icon: PangolinIcons.globe, name: t.contactWebsite, sub: 'pangolin.yanmeiai.com', accent: false), - ]; return _SubScaffold( title: t.redeemTitle, onBack: widget.onBack, @@ -417,36 +413,9 @@ class _RedeemScreenState extends ConsumerState { ], ]), ), - const SizedBox(height: 22), - Text(t.buyTitle, style: PangolinText.sm.copyWith(color: c.fg1, fontWeight: FontWeight.w700, fontSize: 14.5)), - const SizedBox(height: 6), - Text(t.buySub, style: PangolinText.caption.copyWith(color: c.fg3, fontWeight: FontWeight.w400, height: 1.5)), - const SizedBox(height: 14), - for (final ch in channels) Padding(padding: const EdgeInsets.only(bottom: 10), child: _channel(c, ch.icon, ch.name, ch.sub, ch.accent)), ]), ); } - - Widget _channel(PangolinScheme c, IconData icon, String name, String sub, bool accent) { - return InkWell( - borderRadius: BorderRadius.circular(PangolinRadius.lg), - onTap: () => launchChannel(sub), - child: Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration(color: accent ? c.accentSubtle : c.surface, borderRadius: BorderRadius.circular(PangolinRadius.lg), border: Border.all(color: accent ? c.accentBorder : c.border), boxShadow: PangolinShadow.sm), - child: Row(children: [ - Container(width: 40, height: 40, decoration: BoxDecoration(color: accent ? c.accent : c.bgSubtle, borderRadius: BorderRadius.circular(PangolinRadius.md)), - child: Icon(icon, size: 20, color: accent ? PangolinColors.white : c.accent)), - const SizedBox(width: 13), - Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(name, style: PangolinText.body.copyWith(color: c.fg1, fontWeight: FontWeight.w600)), - Text(sub, style: PangolinText.caption.copyWith(color: c.fg3, fontWeight: FontWeight.w400)), - ])), - Icon(PangolinIcons.externalLink, size: 16, color: c.fg3), - ]), - ), - ); - } } /// ── 联系我们 ── @@ -458,11 +427,19 @@ class ContactScreen extends StatelessWidget { @override Widget build(BuildContext context) { final c = context.pangolin; - final channels = [ - (icon: PangolinIcons.send, name: 'Telegram', sub: '@pangolin_app', accent: true), - (icon: PangolinIcons.mail, name: t.contactEmail, sub: 'support@yanmeiai.com', accent: false), - (icon: PangolinIcons.globe, name: t.contactWebsite, sub: 'pangolin.yanmeiai.com', accent: false), - ]; + final channels = kContactChannels + .map((ch) => ( + icon: ch.icon, + name: switch (ch.nameKey) { + 'telegram' => 'Telegram', + 'email' => t.contactEmail, + 'website' => t.contactWebsite, + _ => ch.nameKey, + }, + sub: ch.sub, + accent: ch.accent, + )) + .toList(); return _SubScaffold( title: t.contactTitle, onBack: onBack,