feat(sub): add Copy All Configs button to subscription page (#5163)

* feat(sub): add Copy All Configs button to subscription page

* fix(sub): include links in copyAll dependency array

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* chore: fmt

* fix(sub): drop module-level links from copyAll deps to satisfy exhaustive-deps

links is derived from window.__SUB_PAGE_DATA__ at module scope, so listing it in the useCallback dependency array triggers a react-hooks/exhaustive-deps warning (outer-scope value). Matches the existing single-link copy callback's deps.

---------

Co-authored-by: nikan <nikan>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Nikan Zeyaei
2026-06-11 14:30:37 +03:30
committed by GitHub
parent 89b1137b00
commit ffde2f7ebf
14 changed files with 58 additions and 13 deletions
+19
View File
@@ -111,6 +111,13 @@ export default function SubPage() {
if (ok) messageApi.success(t('copied'));
}, [t, messageApi]);
const copyAll = useCallback(async () => {
if (links.length === 0) return;
const allLinks = links.join('\n');
const ok = await ClipboardManager.copyText(allLinks);
if (ok) messageApi.success(t('subscription.copyAllConfigsCopied'));
}, [t, messageApi]);
const open = useCallback((url: string) => {
if (!url) return;
window.open(url, '_blank');
@@ -393,6 +400,18 @@ export default function SubPage() {
<>
<Divider>{t('pages.inbounds.copyLink')}</Divider>
<div className="links-section">
<div className="sub-link-row">
<span className="sub-link-title">{t('subscription.copyAllConfigs')}</span>
<div className="sub-link-actions">
<Button
size="small"
icon={<CopyOutlined />}
onClick={copyAll}
aria-label={t('subscription.copyAllConfigs')}
title={t('subscription.copyAllConfigs')}
/>
</div>
</div>
{links.map((link, idx) => {
const parts = parseLinkParts(link, linkEmails[idx] || '');
const fallback = `Link ${idx + 1}`;
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "نشط",
"inactive": "غير نشط",
"unlimited": "غير محدود",
"noExpiry": "بدون انتهاء"
"noExpiry": "بدون انتهاء",
"copyAllConfigs": "نسخ جميع الإعدادات",
"copyAllConfigsCopied": "تم نسخ جميع الإعدادات"
},
"menu": {
"theme": "الثيم",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Active",
"inactive": "Inactive",
"unlimited": "Unlimited",
"noExpiry": "No expiry"
"noExpiry": "No expiry",
"copyAllConfigs": "Copy All Configs",
"copyAllConfigsCopied": "All configs copied"
},
"menu": {
"theme": "Theme",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Activo",
"inactive": "Inactivo",
"unlimited": "Ilimitado",
"noExpiry": "Sin caducidad"
"noExpiry": "Sin caducidad",
"copyAllConfigs": "Copiar Todas las Configuraciones",
"copyAllConfigsCopied": "Todas las configuraciones copiadas"
},
"menu": {
"theme": "Tema",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "فعال",
"inactive": "غیرفعال",
"unlimited": "نامحدود",
"noExpiry": "بدون انقضا"
"noExpiry": "بدون انقضا",
"copyAllConfigs": "کپی همه کانفیگ‌ها",
"copyAllConfigsCopied": "همه کانفیگ‌ها کپی شدند"
},
"menu": {
"theme": "تم",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Aktif",
"inactive": "Nonaktif",
"unlimited": "Tanpa batas",
"noExpiry": "Tanpa kedaluwarsa"
"noExpiry": "Tanpa kedaluwarsa",
"copyAllConfigs": "Salin Semua Konfigurasi",
"copyAllConfigsCopied": "Semua konfigurasi tersalin"
},
"menu": {
"theme": "Tema",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "有効",
"inactive": "無効",
"unlimited": "無制限",
"noExpiry": "期限なし"
"noExpiry": "期限なし",
"copyAllConfigs": "すべての設定をコピー",
"copyAllConfigsCopied": "すべての設定をコピーしました"
},
"menu": {
"theme": "テーマ",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Ativo",
"inactive": "Inativo",
"unlimited": "Ilimitado",
"noExpiry": "Sem validade"
"noExpiry": "Sem validade",
"copyAllConfigs": "Copiar Todas as Configurações",
"copyAllConfigsCopied": "Todas as configurações copiadas"
},
"menu": {
"theme": "Tema",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Активна",
"inactive": "Неактивна",
"unlimited": "Неограниченно",
"noExpiry": "Бессрочно"
"noExpiry": "Бессрочно",
"copyAllConfigs": "Копировать все конфигурации",
"copyAllConfigsCopied": "Все конфигурации скопированы"
},
"menu": {
"theme": "Тема",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Aktif",
"inactive": "Pasif",
"unlimited": "Sınırsız",
"noExpiry": "Süresiz"
"noExpiry": "Süresiz",
"copyAllConfigs": "Tüm Yapılandırmaları Kopyala",
"copyAllConfigsCopied": "Tüm yapılandırmalar kopyalandı"
},
"menu": {
"theme": "Tema",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Активна",
"inactive": "Неактивна",
"unlimited": "Безліміт",
"noExpiry": "Без строку"
"noExpiry": "Без строку",
"copyAllConfigs": "Копіювати всі конфігурації",
"copyAllConfigsCopied": "Всі конфігурації скопійовано"
},
"menu": {
"theme": "Тема",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "Hoạt động",
"inactive": "Không hoạt động",
"unlimited": "Không giới hạn",
"noExpiry": "Không hết hạn"
"noExpiry": "Không hết hạn",
"copyAllConfigs": "Sao chép tất cả cấu hình",
"copyAllConfigsCopied": "Đã sao chép tất cả cấu hình"
},
"menu": {
"theme": "Chủ đề",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "启用",
"inactive": "停用",
"unlimited": "无限制",
"noExpiry": "无到期"
"noExpiry": "无到期",
"copyAllConfigs": "复制全部配置",
"copyAllConfigsCopied": "已复制全部配置"
},
"menu": {
"theme": "主题",
+3 -1
View File
@@ -95,7 +95,9 @@
"active": "啟用",
"inactive": "停用",
"unlimited": "無限制",
"noExpiry": "無到期"
"noExpiry": "無到期",
"copyAllConfigs": "複製全部配置",
"copyAllConfigsCopied": "已複製全部配置"
},
"menu": {
"theme": "主題",