From e796324240b3d1cc6f81419e666bc5164b735d56 Mon Sep 17 00:00:00 2001 From: Sarina Date: Sun, 15 Oct 2023 06:07:42 +0330 Subject: [PATCH 01/27] add: setup inlang translation --- assets/translations/en.json | 4 ++++ project.inlang.json | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 assets/translations/en.json create mode 100644 project.inlang.json diff --git a/assets/translations/en.json b/assets/translations/en.json new file mode 100644 index 00000000..9cc21234 --- /dev/null +++ b/assets/translations/en.json @@ -0,0 +1,4 @@ +{ + "appName": "Hiddify Next", + "Error": "Error" +} diff --git a/project.inlang.json b/project.inlang.json new file mode 100644 index 00000000..9b62c6d6 --- /dev/null +++ b/project.inlang.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "sourceLanguageTag": "en", + "languageTags": [ + "en", + "fa", + "ru-RU", + "zh-CN" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js" + ], + "plugin.inlang.i18next": { + "pathPattern": "./assets/translations/{languageTag}.json", + "variableReferencePattern": [ + "@:" + ] + } +} + From 2faa17f66d242da59175841aebf5a5af1a10ed83 Mon Sep 17 00:00:00 2001 From: Sarina Date: Sun, 15 Oct 2023 08:08:37 +0330 Subject: [PATCH 02/27] add: inlang translation files(incomplete) --- assets/translations/en.json | 260 ++++++++++++++++++++++++++++++++- assets/translations/fa.json | 260 +++++++++++++++++++++++++++++++++ assets/translations/ru-RU.json | 260 +++++++++++++++++++++++++++++++++ assets/translations/zh-CN.json | 260 +++++++++++++++++++++++++++++++++ 4 files changed, 1038 insertions(+), 2 deletions(-) create mode 100644 assets/translations/fa.json create mode 100644 assets/translations/ru-RU.json create mode 100644 assets/translations/zh-CN.json diff --git a/assets/translations/en.json b/assets/translations/en.json index 9cc21234..b89ecfe9 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,4 +1,260 @@ { - "appName": "Hiddify Next", - "Error": "Error" + "general": { + "appTitle": "Hiddify Next", + "reset": "Reset", + "toggle": { + "enabled": "Enabled", + "disabled": "Disabled" + }, + "state": { + "disable": "Disable" + }, + "sort": "Sort", + "sortBy": "Sort by", + "addToClipboard": "Add to clipboard" + }, + "intro": { + "termsAndPolicyCaution(rich)": "by continuing you agree with ${tap(@:about.termsAndConditions)}", + "start": "Start" + }, + "home": { + "pageTitle": "Home", + "emptyProfilesMsg": "Begin by adding a subscription profile", + "noActiveProfileMsg": "Choose a profile", + "connection": { + "tapToConnect": "Tap to Connect", + "connecting": "Connecting", + "disconnecting": "Disconnecting", + "connected": "Connected" + }, + "stats": { + "traffic": "Live Traffic", + "trafficTotal": "Total Traffic", + "uplink": "Uplink", + "downlink": "Downlink" + } + }, + "profile": { + "overviewPageTitle": "Profiles", + "detailsPageTitle": "Profile", + "activeProfileNameSemanticLabel": "Active profile name: \"${name}\".", + "activeProfileBtnSemanticLabel": "View all profiles.", + "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as active profile.", + "subscription": { + "traffic": "Traffic", + "updatedTimeAgo": "Updated ${timeago}", + "remainingDuration": "${duration} Days Remaining", + "remainingTrafficSemanticLabel": "${consumed} of ${total} traffic consumed.", + "expired": "Expired", + "noTraffic": "No more traffic" + }, + "sortBy": { + "lastUpdate": "Recently updated", + "name": "Alphabetically" + }, + "add": { + "buttonText": "New Profile", + "shortBtnTxt": "New Profile", + "fromClipboard": "Add From Clipboard", + "scanQr": "Scan QR code", + "manually": "Manual Entry", + "addingProfileMsg": "Adding Profile", + "failureMsg": "Failed to add profile" + }, + "update": { + "buttonTxt": "Update", + "tooltip": "Update Profile", + "failureMsg": "Failed to update profile", + "successMsg": "Profile updated successfully" + }, + "edit": { + "buttonTxt": "Edit", + "selectActiveTxt": "Select active profile" + }, + "delete": { + "buttonTxt": "Delete", + "confirmationMsg": "Delete profile permanently?", + "successMsg": "Profile deleted successfully" + }, + "save": { + "buttonText": "Save", + "successMsg": "Profile saved successfully", + "failureMsg": "Failed to save profile" + }, + "detailsForm": { + "nameLabel": "Name", + "nameHint": "Profile name", + "urlLabel": "URL", + "urlHint": "Full config URL", + "emptyNameMsg": "Name is required", + "invalidUrlMsg": "Invalid URL", + "lastUpdate": "Last Update", + "updateInterval": "Auto Update", + "updateIntervalDialogTitle": "Auto Update Interval (in hours)" + } + }, + "proxies": { + "pageTitle": "Proxies", + "emptyProxiesMsg": "No proxies available", + "delayTestTooltip": "Test Delay", + "sortTooltip": "Sort Proxies", + "sortOptions": { + "unsorted": "Default", + "name": "Alphabetically", + "delay": "By Delay" + } + }, + "logs": { + "pageTitle": "Logs", + "clearLogsButtonText": "Clear Logs", + "filterHint": "Filter", + "allLevelsFilter": "All", + "shareCoreLogs": "Share Core Logs", + "shareAppLogs": "Share App logs" + }, + "settings": { + "pageTitle": "Settings", + "requiresRestartMsg": "For this to take effect restart the app", + "general": { + "sectionTitle": "General", + "locale": "Language", + "region": "Region", + "regionMsg": "Helps set default options to bypass domestic addresses", + "regions": { + "ir": "Iran (ir)", + "cn": "China (cn)", + "other": "Other" + }, + "themeMode": "Theme Mode", + "themeModes": { + "system": "Follow system theme", + "dark": "Dark mode", + "light": "Light mode" + }, + "enableAnalytics": "Enable Analytics", + "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", + "trueBlack": "Pure Black", + "autoStart": "Start on Boot", + "silentStart": "Silent Start", + "openWorkingDir": "Open Working Directory", + "ignoreBatteryOptimizations": "Disable Battery Optimization", + "ignoreBatteryOptimizationsMsg": "Remove restrictions for optimal VPN performance" + }, + "advanced": { + "sectionTitle": "Advanced", + "debugMode": "Debug Mode", + "debugModeMsg": "Restart the app for applying this change" + }, + "network": { + "perAppProxyPageTitle": "Per-app Proxy", + "perAppProxyModes": { + "off": "All", + "offMsg": "Proxy all apps", + "include": "Proxy", + "includeMsg": "Proxy only selected apps", + "exclude": "Bypass", + "excludeMsg": "Do not proxy selected apps" + }, + "showSystemApps": "Show system apps", + "hideSystemApps": "Hide system apps", + "clearSelection": "Clear selection" + }, + "config": { + "section": { + "route": "Route Options", + "dns": "DNS Options", + "inbound": "Inbound Options", + "misc": "Misc Options" + }, + "pageTitle": "Config Options", + "executeConfigAsIs": "Execute Config As Is", + "executeConfigAsIsMsg": "Executes the SingBox configs with minimal changes. Can cause problems.", + "logLevel": "Log Level", + "resolveDestination": "Resolve Destination", + "ipv6Mode": "IPv6 Route", + "ipv6Modes": { + "disable": "Disable", + "enable": "Enable", + "prefer": "Preferred", + "only": "Exclusive" + }, + "remoteDnsAddress": "Remote DNS", + "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", + "directDnsAddress": "Direct DNS", + "directDnsDomainStrategy": "Direct DNS Domain Strategy", + "mixedPort": "Mixed Port", + "localDnsPort": "Local DNS Port", + "tunImplementation": "TUN Implementation", + "mtu": "MTU", + "connectionTestUrl": "Connection Test URL", + "urlTestInterval": "URL Test Interval", + "enableClashApi": "Enable Clash API", + "clashApiPort": "Clash API Port", + "enableTun": "Enable TUN", + "setSystemProxy": "Set System Proxy" + } + }, + "about": { + "pageTitle": "About", + "version": "Version", + "sourceCode": "Source Code", + "telegramChannel": "Telegram Channel", + "checkForUpdate": "Check for update", + "privacyPolicy": "Privacy policy", + "termsAndConditions": "Terms and conditions" + }, + "appUpdate": { + "notAvailableMsg": "Already using the latest version", + "dialogTitle": "Update Available", + "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", + "currentVersionLbl": "Current Version", + "newVersionLbl": "New Version", + "updateNowBtnTxt": "Update Now", + "laterBtnTxt": "Later", + "ignoreBtnTxt": "Ignore" + }, + "tray": { + "dashboard": "Dashboard", + "quit": "Quit", + "systemProxy": "System Proxy" + }, + "failure": { + "unexpected": "Unexpected Error", + "clash": { + "unexpected": "Unexpected Error", + "core": "Clash Error ${reason}" + }, + "singbox": { + "unexpected": "Unexpected Service Error", + "serviceNotRunning": "Service is not running", + "invalidConfigOptions": "Invalid configuration options", + "invalidConfig": "Invalid Configuration", + "create": "Service creation error", + "start": "Service startup error" + }, + "connectivity": { + "unexpected": "Unexpected Failure", + "missingVpnPermission": "Missing VPN Permission", + "missingNotificationPermission": "Missing Notification Permission", + "core": "Core Error" + }, + "profiles": { + "unexpected": "Unexpected Error", + "notFound": "Profile Not Found", + "invalidConfig": "Invalid Configs", + "invalidUrl": "Invalid URL" + }, + "connection": { + "unexpected": "Unexpected connection error", + "timeout": "Connection timeout", + "badResponse": "Bad response", + "connectionError": "Connection error", + "badCertificate": "Bad certificate" + } + }, + "play": { + "title": "Hiddify Next (Preview)", + "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", + "full_description": "The key goal of HiddifyNext is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe support servers with:\n- Normal V2ray/Xray Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - more and more\n\nSupport:\n- All Protocols supported by Sing-Box \n- VLESS + xtls reality, vision\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n- V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source sing-box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." + } } diff --git a/assets/translations/fa.json b/assets/translations/fa.json new file mode 100644 index 00000000..9fe3206c --- /dev/null +++ b/assets/translations/fa.json @@ -0,0 +1,260 @@ +{ + "general": { + "appTitle": "هیدیفای نکست", + "reset": "بازنشانی", + "toggle": { + "enabled": "فعال", + "disabled": "غیر فعال" + }, + "state": { + "disable": "غیر فعال" + }, + "sort": "مرتب‌سازی", + "sortBy": "مرتب‌سازی براساس", + "addToClipboard": "به کلیپ بورد اضافه کنید" + }, + "intro": { + "termsAndPolicyCaution(rich)": "در صورت ادامه با ${tap(@:about.termsAndConditions)} موافقت میکنید", + "start": "شروع" + }, + "home": { + "pageTitle": "خانه", + "emptyProfilesMsg": "با افزودن پروفایل شروع کنید", + "noActiveProfileMsg": "یک پروفایل انتخاب کنید", + "connection": { + "tapToConnect": "برای اتصال ضربه بزنید", + "connecting": "در حال اتصال", + "disconnecting": "در حال قطع اتصال", + "connected": "متصل" + }, + "stats": { + "traffic": "مصرف لحظه‌ای", + "trafficTotal": "مصرف کل", + "uplink": "ارسال", + "downlink": "دریافت" + } + }, + "profile": { + "overviewPageTitle": "پروفایل‌ها", + "detailsPageTitle": "پروفایل", + "activeProfileNameSemanticLabel": "نام پروفایل فعال: ${name}", + "activeProfileBtnSemanticLabel": "همه‌ی پروفایل‌ها", + "nonActiveProfileBtnSemanticLabel": "انتخاب ${name} به عنوان پروفایل فعال", + "subscription": { + "traffic": "ترافیک", + "updatedTimeAgo": "بروزرسانی شده در ${timeago}", + "remainingDuration": "${duration} روز باقی مانده", + "remainingTrafficSemanticLabel": "${consumed} از ${total} ترافیک مصرف شده", + "expired": "منقضی شده", + "noTraffic": "پایان ترافیک" + }, + "sortBy": { + "lastUpdate": "اخیرا بروز شده", + "name": "براساس نام" + }, + "add": { + "buttonText": "افزودن پروفایل جدید", + "shortBtnTxt": "افزودن", + "fromClipboard": "افزودن از کلیپ‌بورد", + "scanQr": "اسکن QR کد", + "manually": "افزودن دستی", + "addingProfileMsg": "در حال افزودن پروفایل", + "failureMsg": "در افزودن پروفایل خطایی رخ داد" + }, + "update": { + "buttonTxt": "بروزرسانی", + "tooltip": "بروزرسانی پروفایل", + "failureMsg": "در بروزرسانی پروفایل خطایی رخ داد", + "successMsg": "پروفایل با موفقیت بروزرسانی شد" + }, + "edit": { + "buttonTxt": "ویرایش", + "selectActiveTxt": "انتخاب پروفایل فعال" + }, + "delete": { + "buttonTxt": "حذف", + "confirmationMsg": "حذف پروفایل برای همیشه؟", + "successMsg": "پروفایل با موفقیت حذف شد" + }, + "save": { + "buttonText": "ذخیره", + "successMsg": "پروفایل با موفقیت ذخیره شد", + "failureMsg": "خطا در ذخیره پروفایل" + }, + "detailsForm": { + "nameLabel": "نام", + "nameHint": "نام پروفایل", + "urlLabel": "لینک", + "urlHint": "آدرس کامل کانفیگ", + "emptyNameMsg": "نام نمی‌تواند خالی باشد", + "invalidUrlMsg": "لینک نامعتبر", + "lastUpdate": "آخرین بروزرسانی", + "updateInterval": "بروزرسانی خودکار", + "updateIntervalDialogTitle": "فاصله زمانی بروزرسانی خودکار (ساعت)" + } + }, + "proxies": { + "pageTitle": "پراکسی‌ها", + "emptyProxiesMsg": "پراکسی وجود ندارد", + "delayTestTooltip": "تست تاخیر", + "sortTooltip": "مرتب‌سازی پراکسی‌ها", + "sortOptions": { + "unsorted": "پیش‌فرض", + "name": "براساس نام", + "delay": "براساس تاخیر" + } + }, + "logs": { + "pageTitle": "لاگ‌ها", + "clearLogsButtonText": "پاک‌سازی", + "filterHint": "فیلتر", + "allLevelsFilter": "همه", + "shareCoreLogs": "اشتراک‌گذاری لاگ هسته", + "shareAppLogs": "اشتراک‌گذاری لاگ برنامه" + }, + "settings": { + "pageTitle": "تنظیمات", + "requiresRestartMsg": "برای اعمال این تنظیم برنامه را بازنشانی کنید", + "general": { + "sectionTitle": "عمومی", + "locale": "زبان", + "region": "منطقه", + "regionMsg": "به انتخاب تنظیمات پیش‌فرض برای دورزدن آدرس‌های داخلی کمک میکند", + "regions": { + "ir": "ایران (ir)", + "cn": "چین (cn)", + "other": "سایر" + }, + "themeMode": "تم مود", + "themeModes": { + "system": "پیروی از تم دستگاه", + "dark": "تم تیره", + "light": "تم روشن" + }, + "enableAnalytics": "فعال‌سازی آنالیتیکز", + "enableAnalyticsMsg": "ارائه دسترسی آنالیز و گزارش خطا برای بهبود عملکرد برنامه", + "trueBlack": "کاملا سیاه", + "autoStart": "اجرا با روشن شدن سیستم", + "silentStart": "اجرای ساکت", + "openWorkingDir": "باز کردن دایرکتوری کاری", + "ignoreBatteryOptimizations": "غیرفعال کردن بهینه‌سازی باتری", + "ignoreBatteryOptimizationsMsg": "حذف محدودیت‌ها برای عملکرد بهتر VPN" + }, + "advanced": { + "sectionTitle": "پیشرفته", + "debugMode": "دیباگ مود", + "debugModeMsg": "برای اعمال این تغییر اپ را ری‌استارت کنید" + }, + "network": { + "perAppProxyPageTitle": "پراکسی برنامه‌ها", + "perAppProxyModes": { + "off": "همه", + "offMsg": "همه برنامه‌ها پراکسی میشوند", + "include": "پراکسی", + "includeMsg": "تنها برنامه‌های انتخاب شده پراکسی میشوند", + "exclude": "بایپس", + "excludeMsg": "همه بجز برنامه‌های انتخاب شده پراکسی میشوند" + }, + "showSystemApps": "نمایش برنامه‌های سیستمی", + "hideSystemApps": "مخفی کردن برنامه‌های سیستمی", + "clearSelection": "حذف انتخاب‌ها" + }, + "config": { + "section": { + "route": "تنظیمات مسیریاب", + "dns": "تنظیمات DNS", + "inbound": "تنظیمات ورودی", + "misc": "تنظیمات متفرقه" + }, + "pageTitle": "تنظیمات کانفیگ", + "executeConfigAsIs": "اجرای کانفیگ بدون تغییر", + "executeConfigAsIsMsg": "کانفیگ موجود را با تغییرات حداقلی اجرا می‌کند. فقط برای کانفیگ‌های سینگ‌باکس کار می‌کند", + "logLevel": "سطح لاگ", + "resolveDestination": "جایگذاری IP مقصد", + "ipv6Mode": "مسیریابی IPV6 در تونل", + "ipv6Modes": { + "disable": "غیرفعال", + "enable": "فعال", + "prefer": "ترجیح", + "only": "فقط" + }, + "remoteDnsAddress": "DNS خارجی", + "remoteDnsDomainStrategy": "حالت DNS خارجی", + "directDnsAddress": "DNS مستقیم", + "directDnsDomainStrategy": "حالت DNS مستقیم", + "mixedPort": "پورت همه منظوره", + "localDnsPort": "پورت DNS داخلی", + "tunImplementation": "مدل تونل", + "mtu": "سایز بسته‌ها", + "connectionTestUrl": "لینک تست اتصال", + "urlTestInterval": "زمان تست اتصال", + "enableClashApi": "فعالسازی کلش", + "clashApiPort": "پورت کلش", + "enableTun": "فعال سازی تونل", + "setSystemProxy": "تنظیم پروکسی سیستم" + } + }, + "about": { + "pageTitle": "درباره", + "version": "ورژن", + "sourceCode": "سورس کد", + "telegramChannel": "کانال تلگرام", + "checkForUpdate": "بررسی آپدیت جدید", + "privacyPolicy": "سیاست حفظ حریم خصوصی", + "termsAndConditions": "شرایط و ضوابط استفاده" + }, + "appUpdate": { + "notAvailableMsg": "نسخه جدیدی یافت نشد", + "dialogTitle": "نسخه جدید موجود است", + "updateMsg": "نسخه جدیدی از @:general.appTitle موجود است! الان بروزرسانی شود؟", + "currentVersionLbl": "نسخه فعلی", + "newVersionLbl": "نسخه جدید", + "updateNowBtnTxt": "بروزرسانی", + "laterBtnTxt": "بعدا", + "ignoreBtnTxt": "نادیده‌گرفتن" + }, + "tray": { + "dashboard": "داشبورد", + "quit": "خروج", + "systemProxy": "پراکسی سیستم" + }, + "failure": { + "unexpected": "خطای غیرمنتظره", + "clash": { + "unexpected": "خطای غیرمنتظره", + "core": "خطای کلش ${reason}" + }, + "singbox": { + "unexpected": "خطای غیرمنتظره در سرویس", + "serviceNotRunning": "سرویس در حال اجرا نیست", + "invalidConfigOptions": "تنظیمات کانفیگ نامعتبر", + "invalidConfig": "کانفیگ غیر معتبر", + "create": "در ایجاد سرویس خطایی رخ داده", + "start": "در راه‌اندازی سرویس خطایی رخ داده" + }, + "connectivity": { + "unexpected": "خطای غیرمنتظره", + "missingVpnPermission": "نیازمند دسترسی VPN", + "missingNotificationPermission": "نیازمند دسترسی اعلانات", + "core": "خطای هسته" + }, + "profiles": { + "unexpected": "خطای غیرمنتظره", + "notFound": "پروفایل یافت نشد", + "invalidConfig": "کانفیگ غیر معتبر", + "invalidUrl": "لینک نامعتبر" + }, + "connection": { + "unexpected": " خطای غیرمنتظره در اتصال", + "timeout": "درخواست بیش از حد مجاز زمان برد", + "badResponse": "پاسخ نامعتبر", + "connectionError": "خطای اتصال", + "badCertificate": "خطای اعتبار سنجی" + } + }, + "play": { + "title": "هیدیفای نکست Hiddify Next", + "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", + "full_description": "هدف اصلی HiddifyNext ارائه یک کلاینت تونل زنی ایمن، کاربرپسند و کارآمد است. این به شما امکان می دهد تا با استفاده از مجوز VPN-Service، تمام ترافیک یا ترافیک برنامه انتخابی را به یک سرور راه دور مورد نظر خود هدایت کنید.\n\nتوجه: ما هیچ سروری ارائه نمی دهیم. کاربران موظفند با استفاده از سرورهای خود میزبان یا سرورهای مورد اعتماد، فعالیت‌های آنلاین خود را خصوصی نگه دارند.\n \nما از سرورهایی با موارد زیر پشتیبانی می کنیم:\n- لینک اشتراک V2ray/Xray معمولی\n- لینک اشتراک کلش\n- لینک اشتراک Sing-Box\n\nویژگی های منحصر به فرد ما چیست؟\n - کاربر پسند\n - بهینه و سریع\n - به طور خودکار LowestPing را انتخاب کنید\n - نمایش اطلاعات استفاده کاربر\n - به راحتی لینک فرعی را با یک کلیک با استفاده از دیپ لینک وارد کنید\n - رایگان و بدون تبلیغات\n - به راحتی پیوندهای فرعی کاربر را تغییر دهید\n - بیشتر و بیشتر\n\nحمایت کردن:\n- تمام پروتکل های پشتیبانی شده توسط Sing-Box\n- VLESS + xtls \n- VMESS\n- تروجان\n- ShoadowSocks\n- ریالیتی\n- V2ray\n- هیستریا 2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nکد منبع در https://github.com/hiddify/Hiddify-Next وجود دارد\nهسته برنامه بر اساس sing-box منبع باز است.\n\nتوضیحات مجوز:\n- سرویس VPN: از آنجا که هدف این برنامه ارائه یک کلاینت تونل زنی ایمن، کاربر پسند و کارآمد است، ما به این مجوز نیاز داریم تا بتوانیم ترافیک را از طریق تونل به سرور راه دور هدایت کنیم.\n- QUERY ALL PACKAGES: این مجوز برای اجازه دادن به کاربران برای گنجاندن یا حذف برنامه های کاربردی خاص برای تونل زدن استفاده می شود.\n- دریافت بوت تکمیل شد: این مجوز را می توان از تنظیمات برنامه فعال یا غیرفعال کرد تا این برنامه پس از بوت شدن دستگاه فعال شود.\n- اعلان های ارسالی: این مجوز ضروری است زیرا ما از یک سرویس پیش زمینه برای اطمینان از عملکرد مداوم سرویس VPN استفاده می کنیم.\n- این برنامه بدون تبلیغات است. تجزیه و تحلیل و داده های اشکال فقط با رضایت صریح کاربر در اولین استفاده از برنامه اتفاق می افتد." + } +} diff --git a/assets/translations/ru-RU.json b/assets/translations/ru-RU.json new file mode 100644 index 00000000..2541f7ef --- /dev/null +++ b/assets/translations/ru-RU.json @@ -0,0 +1,260 @@ +{ + "general": { + "appTitle": "HiddifyNext", + "reset": "Сброс", + "toggle": { + "enabled": "Включено", + "disabled": "Отключено" + }, + "state": { + "disable": "Отключить" + }, + "sort": "Сортировка", + "sortBy": "Сортировка", + "addToClipboard": "Копировать в буфер обмена" + }, + "intro": { + "termsAndPolicyCaution(rich)": "Продолжая, вы соглашаетесь с ${tap(@:about.termsAndConditions)}", + "start": "Начать" + }, + "home": { + "pageTitle": "Главная", + "emptyProfilesMsg": "Начните с добавления профиля подписки", + "noActiveProfileMsg": "Выберите профиль", + "connection": { + "tapToConnect": "Нажмите для подключения", + "connecting": "Подключение", + "disconnecting": "Отключение", + "connected": "Подключено" + }, + "stats": { + "traffic": "Скорость", + "trafficTotal": "Трафик", + "uplink": "Восходящий канал", + "downlink": "Нисходящий канал" + } + }, + "profile": { + "overviewPageTitle": "Профили", + "detailsPageTitle": "Профиль", + "activeProfileNameSemanticLabel": "Имя активного профиля: «${name}».", + "activeProfileBtnSemanticLabel": "Посмотреть все профили.", + "nonActiveProfileBtnSemanticLabel": "Выберите «${name}» в качестве активного профиля.", + "subscription": { + "traffic": "Трафик", + "updatedTimeAgo": "Обновлено ${timeago}", + "remainingDuration": "Ещё ${duration} дн.", + "remainingTrafficSemanticLabel": "${consumed} из ${total} использованного трафика.", + "expired": "Истекло", + "noTraffic": "Нет доступного трафика" + }, + "sortBy": { + "lastUpdate": "Последнее обновление", + "name": "По алфавиту" + }, + "add": { + "buttonText": "Новый профиль", + "shortBtnTxt": "Новый профиль", + "fromClipboard": "Добавить из буфера обмена", + "scanQr": "Сканировать QR-код", + "manually": "Ручной ввод", + "addingProfileMsg": "Добавление профиля", + "failureMsg": "Невозможно добавить профиль" + }, + "update": { + "buttonTxt": "Обновить", + "tooltip": "Обновить профиль", + "failureMsg": "Ошибка обновления", + "successMsg": "Профиль успешно обновлён" + }, + "edit": { + "buttonTxt": "Изменить", + "selectActiveTxt": "Выберите активный профиль" + }, + "delete": { + "buttonTxt": "Удалить", + "confirmationMsg": "Безвозвратно удалить профиль?", + "successMsg": "Профиль успешно удалён" + }, + "save": { + "buttonText": "Сохранить", + "successMsg": "Профиль успешно сохранен", + "failureMsg": "Невозможно сохранить профиль" + }, + "detailsForm": { + "nameLabel": "Имя", + "nameHint": "Имя профиля", + "urlLabel": "URL", + "urlHint": "Полный URL конфигурации", + "emptyNameMsg": "Поле «Имя» обязательно", + "invalidUrlMsg": "Неправильный URL", + "lastUpdate": "Последнее обновление", + "updateInterval": "Автоматическое обновление", + "updateIntervalDialogTitle": "Интервал обновления (в часах)" + } + }, + "proxies": { + "pageTitle": "Прокси", + "emptyProxiesMsg": "Нет доступных прокси", + "delayTestTooltip": "Тестирование задержки", + "sortTooltip": "Сортировка прокси", + "sortOptions": { + "unsorted": "По умолчанию", + "name": "По алфавиту", + "delay": "По задержке" + } + }, + "logs": { + "pageTitle": "Журналы", + "clearLogsButtonText": "Очистить журналы", + "filterHint": "Фильтр", + "allLevelsFilter": "Все", + "shareCoreLogs": "Поделиться журналами ядра", + "shareAppLogs": "Поделиться журналами приложения" + }, + "settings": { + "pageTitle": "Настройки", + "requiresRestartMsg": "Для применения перезапустите приложение.", + "general": { + "sectionTitle": "Основные", + "locale": "Язык", + "region": "Регион", + "regionMsg": "Помогает установить параметры по умолчанию для обхода внутренних адресов.", + "regions": { + "ir": "Иран (ir)", + "cn": "Китай (cn)", + "other": "Другой" + }, + "themeMode": "Оформление", + "themeModes": { + "system": "Системная тема", + "dark": "Тёмная тема", + "light": "Светлая тема" + }, + "enableAnalytics": "Сбор аналитики", + "enableAnalyticsMsg": "Сбор аналитических данных и отправка отчётов о сбоях для улучшения приложения.", + "trueBlack": "Чистый чёрный цвет", + "autoStart": "Запуск при загрузке", + "silentStart": "Тихий запуск", + "openWorkingDir": "Открыть рабочую папку", + "ignoreBatteryOptimizations": "Отключить оптимизацию батареи", + "ignoreBatteryOptimizationsMsg": "Отключение ограничений для оптимальной производительности VPN." + }, + "advanced": { + "sectionTitle": "Расширенные", + "debugMode": "Режим отладки", + "debugModeMsg": "Для применения перезапустите приложение." + }, + "network": { + "perAppProxyPageTitle": "Раздельное проксирование", + "perAppProxyModes": { + "off": "Все", + "offMsg": "Проксировать все приложения", + "include": "Прокси", + "includeMsg": "Проксировать выбранные приложения", + "exclude": "Обход", + "excludeMsg": "Не проксировать выбранные приложения" + }, + "showSystemApps": "Показать системные приложения", + "hideSystemApps": "Скрыть системные приложения", + "clearSelection": "Очистить выбор" + }, + "config": { + "section": { + "route": "Варианты маршрутизации", + "dns": "Параметры DNS", + "inbound": "Входящие параметры", + "misc": "Разные параметры" + }, + "pageTitle": "Параметры конфигурации", + "executeConfigAsIs": "Использовать конфигурацию как есть", + "executeConfigAsIsMsg": "Использовать конфигурации SingBox с минимальными изменениями.", + "logLevel": "Подробность журналирования", + "resolveDestination": "Определять назначение", + "ipv6Mode": "Маршрутизация IPv6", + "ipv6Modes": { + "disable": "Отключено", + "enable": "Включено", + "prefer": "Предпочтительно", + "only": "Эксклюзивно" + }, + "remoteDnsAddress": "Удалённая DNS", + "remoteDnsDomainStrategy": "Стратегия удалённого домена DNS", + "directDnsAddress": "Прямая DNS", + "directDnsDomainStrategy": "Стратегия прямого домена DNS", + "mixedPort": "Смешанный порт", + "localDnsPort": "Локальный порт DNS", + "tunImplementation": "Реализация TUN", + "mtu": "MTU", + "connectionTestUrl": "URL проверки подключения", + "urlTestInterval": "Интервал проверки URL", + "enableClashApi": "Использовать Clash API", + "clashApiPort": "Порт Clash API", + "enableTun": "Использовать TUN", + "setSystemProxy": "Установить системный прокси" + } + }, + "about": { + "pageTitle": "Информация", + "version": "Версия", + "sourceCode": "Исходный код", + "telegramChannel": "Канал Telegram", + "checkForUpdate": "Проверка обновления", + "privacyPolicy": "Политика конфиденциальности", + "termsAndConditions": "Условия и положения" + }, + "appUpdate": { + "notAvailableMsg": "У вас установлена последняя версия", + "dialogTitle": "Доступно обновление", + "updateMsg": "Доступна новая версия @:general.appTitle. Обновить сейчас?", + "currentVersionLbl": "Текущая версия", + "newVersionLbl": "Новая версия", + "updateNowBtnTxt": "Обновить сейчас", + "laterBtnTxt": "Позже", + "ignoreBtnTxt": "Игнорировать" + }, + "tray": { + "dashboard": "Панель", + "quit": "Выход", + "systemProxy": "Системный прокси" + }, + "failure": { + "unexpected": "Неожиданная ошибка", + "clash": { + "unexpected": "Неожиданная ошибка (Clash)", + "core": "Ошибка ${reason}" + }, + "singbox": { + "unexpected": "Неожиданная ошибка (SingBox)", + "serviceNotRunning": "Сервис не запущен", + "invalidConfigOptions": "Неправильные параметры конфигурации", + "invalidConfig": "Неправильная конфигурация", + "create": "Ошибка создания сервиса", + "start": "Ошибка запуска сервиса" + }, + "connectivity": { + "unexpected": "Неожиданная ошибка", + "missingVpnPermission": "Отсутствует разрешение VPN", + "missingNotificationPermission": "Отсутствует разрешение на уведомление", + "core": "Ошибка ядра" + }, + "profiles": { + "unexpected": "Неожиданная ошибка", + "notFound": "Профиль не найден", + "invalidConfig": "Неправильная конфигурация", + "invalidUrl": "Неправильный URL" + }, + "connection": { + "unexpected": "Неожиданная ошибка подключения", + "timeout": "Истекло время подключения", + "badResponse": "Неправильный ответ", + "connectionError": "Ошибка подключения", + "badCertificate": "Неправильный сертификат" + } + }, + "play": { + "title": "Hiddify Next (Preview)", + "short_description": "Автовыбор, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", + "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n- ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." + } +} diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json new file mode 100644 index 00000000..065f8798 --- /dev/null +++ b/assets/translations/zh-CN.json @@ -0,0 +1,260 @@ +{ + "general": { + "appTitle": "Hiddify Next", + "reset": "重置", + "toggle": { + "enabled": "启用", + "disabled": "停用" + }, + "state": { + "disable": "停用" + }, + "sort": "排序", + "sortBy": "排序方式", + "addToClipboard": "添加到剪贴板" + }, + "intro": { + "termsAndPolicyCaution(rich)": "继续即表示您同意 ${tap(@:about.termsAndConditions)}", + "start": "开始" + }, + "home": { + "pageTitle": "主页", + "emptyProfilesMsg": "首先添加订阅配置文件", + "noActiveProfileMsg": "选择配置文件", + "connection": { + "tapToConnect": "点击连接", + "connecting": "正在连接", + "disconnecting": "断开连接", + "connected": "连接成功" + }, + "stats": { + "traffic": "实际流量", + "trafficTotal": "总流量", + "uplink": "上行链路", + "downlink": "下行" + } + }, + "profile": { + "overviewPageTitle": "配置文件", + "detailsPageTitle": "配置文件", + "activeProfileNameSemanticLabel": "活动配置文件名称:“${name}”。", + "activeProfileBtnSemanticLabel": "查看所有配置文件", + "nonActiveProfileBtnSemanticLabel": "选择“${name}”作为活动配置文件。", + "subscription": { + "traffic": "流量", + "updatedTimeAgo": "更新${timeago}", + "remainingDuration": "剩余 ${duration} 天", + "remainingTrafficSemanticLabel": "已消耗 ${consumed} 流量,共 ${total} 流量。", + "expired": "已到期", + "noTraffic": "无流量" + }, + "sortBy": { + "lastUpdate": "最近更新", + "name": "按字母顺序" + }, + "add": { + "buttonText": "新的配置文件", + "shortBtnTxt": "新的配置文件", + "fromClipboard": "从剪贴板添加", + "scanQr": "扫二维码", + "manually": "手动输入", + "addingProfileMsg": "添加配置文件", + "failureMsg": "无法添加配置文件" + }, + "update": { + "buttonTxt": "更新", + "tooltip": "更新配置文件", + "failureMsg": "无法更新配置文件", + "successMsg": "配置文件已成功更新" + }, + "edit": { + "buttonTxt": "编辑", + "selectActiveTxt": "选择活动配置文件" + }, + "delete": { + "buttonTxt": "删除", + "confirmationMsg": "永久删除配置文件吗?", + "successMsg": "配置文件删除成功" + }, + "save": { + "buttonText": "节省", + "successMsg": "个人资料保存成功", + "failureMsg": "保存配置文件失败" + }, + "detailsForm": { + "nameLabel": "姓名", + "nameHint": "个人资料名称", + "urlLabel": "网址", + "urlHint": "完整配置网址", + "emptyNameMsg": "姓名为必填项", + "invalidUrlMsg": "无效的网址", + "lastUpdate": "最后更新", + "updateInterval": "自动更新", + "updateIntervalDialogTitle": "自动更新间隔(以小时为单位)" + } + }, + "proxies": { + "pageTitle": "代理", + "emptyProxiesMsg": "没有可用的代理", + "delayTestTooltip": "测试延迟", + "sortTooltip": "对代理进行排序", + "sortOptions": { + "unsorted": "默认", + "name": "按字母顺序", + "delay": "延迟" + } + }, + "logs": { + "pageTitle": "日志", + "clearLogsButtonText": "清除日志", + "filterHint": "筛选", + "allLevelsFilter": "全部", + "shareCoreLogs": "分享核心日志", + "shareAppLogs": "共享应用程序日志" + }, + "settings": { + "pageTitle": "设置", + "requiresRestartMsg": "要使其生效,请重新启动应用程序", + "general": { + "sectionTitle": "一般的", + "locale": "语言", + "region": "地区", + "regionMsg": "帮助设置默认选项以绕过国内地址", + "regions": { + "ir": "伊朗 (ir)", + "cn": "中国 (cn)", + "other": "其他" + }, + "themeMode": "主题模式", + "themeModes": { + "system": "遵循系统主题", + "dark": "深色模式", + "light": "灯光模式" + }, + "enableAnalytics": "启用分析", + "enableAnalyticsMsg": "授予收集分析并发送崩溃报告以改进应用程序的权限", + "trueBlack": "纯黑", + "autoStart": "开机启动", + "silentStart": "无声启动", + "openWorkingDir": "打开工作目录", + "ignoreBatteryOptimizations": "禁用电池优化", + "ignoreBatteryOptimizationsMsg": "消除限制以获得最佳 VPN 性能" + }, + "advanced": { + "sectionTitle": "先进的", + "debugMode": "调试模式", + "debugModeMsg": "重新启动应用程序以应用此更改" + }, + "network": { + "perAppProxyPageTitle": "每个应用程序代理", + "perAppProxyModes": { + "off": "全部", + "offMsg": "代理所有应用程序", + "include": "代理人", + "includeMsg": "仅代理选定的应用程序", + "exclude": "旁路", + "excludeMsg": "不代理选定的应用程序" + }, + "showSystemApps": "显示系统应用程序", + "hideSystemApps": "隐藏系统应用程序", + "clearSelection": "清空选项" + }, + "config": { + "section": { + "route": "路线选项", + "dns": "DNS 选项", + "inbound": "入境选项", + "misc": "其他选项" + }, + "pageTitle": "配置选项", + "executeConfigAsIs": "按原样执行配置", + "executeConfigAsIsMsg": "只需进行最少的更改即可执行 SingBox 配置。", + "logLevel": "日志级别", + "resolveDestination": "解决目的地", + "ipv6Mode": "IPv6路由", + "ipv6Modes": { + "disable": "禁用", + "enable": "使能够", + "prefer": "首选", + "only": "独家的" + }, + "remoteDnsAddress": "远程域名解析", + "remoteDnsDomainStrategy": "远程 DNS 域策略", + "directDnsAddress": "直接域名解析", + "directDnsDomainStrategy": "直接 DNS 域策略", + "mixedPort": "混合港口", + "localDnsPort": "本地 DNS 端口", + "tunImplementation": "TUN实施", + "mtu": "MTU", + "connectionTestUrl": "连接测试网址", + "urlTestInterval": "URL 测试间隔", + "enableClashApi": "启用Clash API", + "clashApiPort": "Clash API 端口", + "enableTun": "启用TUN", + "setSystemProxy": "设置系统代理" + } + }, + "about": { + "pageTitle": "关于", + "version": "版本", + "sourceCode": "源代码", + "telegramChannel": "电报频道", + "checkForUpdate": "检查更新", + "privacyPolicy": "隐私政策", + "termsAndConditions": "条款和条件" + }, + "appUpdate": { + "notAvailableMsg": "已经使用最新版本", + "dialogTitle": "可用更新", + "updateMsg": "@:general.appTitle 的新版本现已推出。您想现在更新吗?", + "currentVersionLbl": "当前版本", + "newVersionLbl": "新版本", + "updateNowBtnTxt": "现在更新", + "laterBtnTxt": "之后", + "ignoreBtnTxt": "忽略" + }, + "tray": { + "dashboard": "控制面板", + "quit": "退出", + "systemProxy": "系统代理" + }, + "failure": { + "unexpected": "意外错误", + "clash": { + "unexpected": "意外错误", + "core": "Clash错误${reason}" + }, + "singbox": { + "unexpected": "意外服务错误", + "serviceNotRunning": "服务未运行", + "invalidConfigOptions": "配置选项无效", + "invalidConfig": "无效配置", + "create": "服务创建错误", + "start": "服务启动错误" + }, + "connectivity": { + "unexpected": "意外失败", + "missingVpnPermission": "缺少 VPN 权限", + "missingNotificationPermission": "缺少通知权限", + "core": "核心错误" + }, + "profiles": { + "unexpected": "意外错误", + "notFound": "未找到配置文件", + "invalidConfig": "无效配置", + "invalidUrl": "网址无效" + }, + "connection": { + "unexpected": "意外连接错误", + "timeout": "连接超时", + "badResponse": "反应不佳", + "connectionError": "连接错误", + "badCertificate": "证书无效" + } + }, + "play": { + "title": "Hiddify Next(预览)", + "short_description": "自动,SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", + "full_description": "HiddifyNext 的主要目标是提供安全、用户友好且高效的隧道客户端。它使您能够利用 VPN 服务权限将所有流量或选定的应用程序流量路由到您选择的远程服务器。\n\n注:我们不提供任何服务器;用户需要使用自己的自托管服务器或受信任的服务器来确保其在线活动的私密性。\n \n我们支持以下类型的服务器:\n- 普通V2ray/Xray订阅链接\n- Clash订阅链接\n- Sing-Box 订阅链接\n\n我们的独特特点是什么?\n\n-用户友好\n-优化和高速\n-自动选择最低延迟\n-显示用户使用信息\n-通过一键深度链接轻松导入子链接\n-免费且无广告\n-轻松切换用户子链接\n-等等\n\n支持:\n- Sing-Box 支持的所有协议\n- VLESS + xtls 现实、愿景\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n-V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\n源代码位于https://github.com/hiddify/Hiddify-Next\n应用程序核心基于开源的Sing-Box。\n\n权限说明:\n\nVPN服务:由于此应用程序的目标是提供安全、用户友好和高效的隧道客户端,我们需要此权限以能够通过隧道将流量路由到远程服务器。\n查询所有包:此权限用于允许用户包括或排除特定应用程序以进行隧道传输。\n接收启动完成:此权限可以从应用程序设置中启用或禁用,以在设备启动时激活此应用程序。\n发送通知:此权限是必需的,因为我们使用前台服务来确保VPN服务的持续运行。\n此应用程序没有广告。分析和崩溃数据仅在用户在首次使用应用程序时明确同意的情况下发生。" + } +} From 29919b3939b8af0738e859828899dc2434e9b5cc Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:09:47 +0330 Subject: [PATCH 03/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab7c639c..64398647 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.de ## Installation and tutorials -Please find tutorial information on the [wiki page](https://github.com/hiddify/hiddify-server/wiki). +Please find tutorial information on the [wiki page](https://github.com/hiddify/hiddify-next/wiki). ## Acknowledgements From 129f6c2ca4349a8319fc07508cd773803a408d06 Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:10:42 +0330 Subject: [PATCH 04/27] Update README_cn.md --- README_cn.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README_cn.md b/README_cn.md index d905e4f1..7583a9b6 100644 --- a/README_cn.md +++ b/README_cn.md @@ -91,7 +91,8 @@ ## 安装和教程 -请在 [wiki 页面](https://github.com/hiddify/hiddify-server/wiki) 上查找教程信息。 +请在 [wiki 页面](https://github.com/hiddify/hiddify-next/wiki) 上查找教程信息。 + ## 致谢 - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box 适用于安卓](https://github.com/SagerNet/sing-box-for-android) From 7b965a98203116bf272ad3c035f7ea0dd2239e0e Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:10:56 +0330 Subject: [PATCH 05/27] Update README_ru.md --- README_ru.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_ru.md b/README_ru.md index b4052cca..3d1c2161 100644 --- a/README_ru.md +++ b/README_ru.md @@ -87,7 +87,7 @@ ## Установка и руководства -Учебную информацию можно найти на [вики-странице](https://github.com/hiddify/hiddify-server/wiki). +Учебную информацию можно найти на [вики-странице](https://github.com/hiddify/hiddify-next/wiki). ## Благодарности From fbe522e9e293f2b4cff0269c52e6032d34423991 Mon Sep 17 00:00:00 2001 From: Elshad Guseynov <66727826+lifeindarkside@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:03:43 +0300 Subject: [PATCH 06/27] Update strings_ru.i18n.json Update some wrong interpretation --- assets/translations/strings_ru.i18n.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 2541f7ef..d798d217 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -30,8 +30,8 @@ "stats": { "traffic": "Скорость", "trafficTotal": "Трафик", - "uplink": "Восходящий канал", - "downlink": "Нисходящий канал" + "uplink": "Входящий канал", + "downlink": "Исходящий канал" } }, "profile": { @@ -195,10 +195,10 @@ } }, "about": { - "pageTitle": "Информация", + "pageTitle": "О программе", "version": "Версия", "sourceCode": "Исходный код", - "telegramChannel": "Канал Telegram", + "telegramChannel": "Telegram канал", "checkForUpdate": "Проверка обновления", "privacyPolicy": "Политика конфиденциальности", "termsAndConditions": "Условия и положения" @@ -211,7 +211,7 @@ "newVersionLbl": "Новая версия", "updateNowBtnTxt": "Обновить сейчас", "laterBtnTxt": "Позже", - "ignoreBtnTxt": "Игнорировать" + "ignoreBtnTxt": "Пропустить" }, "tray": { "dashboard": "Панель", From 2d2d5446baee78500ceab5bb2fb1186fb1db4950 Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 16 Oct 2023 17:04:56 +0330 Subject: [PATCH 07/27] add: french language --- assets/translations/fr-FR.json | 0 project.inlang.json | 1 + 2 files changed, 1 insertion(+) create mode 100644 assets/translations/fr-FR.json diff --git a/assets/translations/fr-FR.json b/assets/translations/fr-FR.json new file mode 100644 index 00000000..e69de29b diff --git a/project.inlang.json b/project.inlang.json index 9b62c6d6..2f2322e1 100644 --- a/project.inlang.json +++ b/project.inlang.json @@ -5,6 +5,7 @@ "en", "fa", "ru-RU", + "fr-FR", "zh-CN" ], "modules": [ From ee5be508c8f8cfd392892a58f865c890927917de Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 16 Oct 2023 17:07:05 +0330 Subject: [PATCH 08/27] update: fr_FR.json --- assets/translations/fr-FR.json | 260 +++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/assets/translations/fr-FR.json b/assets/translations/fr-FR.json index e69de29b..4a6a9a96 100644 --- a/assets/translations/fr-FR.json +++ b/assets/translations/fr-FR.json @@ -0,0 +1,260 @@ +{ + "general": { + "appTitle": "", + "reset": "", + "toggle": { + "enabled": "", + "disabled": "" + }, + "state": { + "disable": "" + }, + "sort": "", + "sortBy": "", + "addToClipboard": "" + }, + "intro": { + "termsAndPolicyCaution(rich)": "", + "start": "" + }, + "home": { + "pageTitle": "", + "emptyProfilesMsg": "", + "noActiveProfileMsg": "", + "connection": { + "tapToConnect": "", + "connecting": "", + "disconnecting": "", + "connected": "" + }, + "stats": { + "traffic": "", + "trafficTotal": "", + "uplink": "", + "downlink": "" + } + }, + "profile": { + "overviewPageTitle": "", + "detailsPageTitle": "", + "activeProfileNameSemanticLabel": "", + "activeProfileBtnSemanticLabel": "", + "nonActiveProfileBtnSemanticLabel": "", + "subscription": { + "traffic": "", + "updatedTimeAgo": "", + "remainingDuration": "", + "remainingTrafficSemanticLabel": "", + "expired": "", + "noTraffic": "" + }, + "sortBy": { + "lastUpdate": "", + "name": "" + }, + "add": { + "buttonText": "", + "shortBtnTxt": "", + "fromClipboard": "", + "scanQr": "", + "manually": "", + "addingProfileMsg": "", + "failureMsg": "" + }, + "update": { + "buttonTxt": "", + "tooltip": "", + "failureMsg": "", + "successMsg": "" + }, + "edit": { + "buttonTxt": "", + "selectActiveTxt": "" + }, + "delete": { + "buttonTxt": "", + "confirmationMsg": "", + "successMsg": "" + }, + "save": { + "buttonText": "", + "successMsg": "", + "failureMsg": "" + }, + "detailsForm": { + "nameLabel": "", + "nameHint": "", + "urlLabel": "", + "urlHint": "", + "emptyNameMsg": "", + "invalidUrlMsg": "", + "lastUpdate": "", + "updateInterval": "", + "updateIntervalDialogTitle": "" + } + }, + "proxies": { + "pageTitle": "", + "emptyProxiesMsg": "", + "delayTestTooltip": "", + "sortTooltip": "", + "sortOptions": { + "unsorted": "", + "name": "", + "delay": "" + } + }, + "logs": { + "pageTitle": "", + "clearLogsButtonText": "", + "filterHint": "", + "allLevelsFilter": "", + "shareCoreLogs": "", + "shareAppLogs": "" + }, + "settings": { + "pageTitle": "", + "requiresRestartMsg": "", + "general": { + "sectionTitle": "", + "locale": "", + "region": "", + "regionMsg": "", + "regions": { + "ir": "", + "cn": "", + "other": "" + }, + "themeMode": "", + "themeModes": { + "system": "", + "dark": "", + "light": "" + }, + "enableAnalytics": "", + "enableAnalyticsMsg": "", + "trueBlack": "", + "autoStart": "", + "silentStart": "", + "openWorkingDir": "", + "ignoreBatteryOptimizations": "", + "ignoreBatteryOptimizationsMsg": "" + }, + "advanced": { + "sectionTitle": "", + "debugMode": "", + "debugModeMsg": "" + }, + "network": { + "perAppProxyPageTitle": "", + "perAppProxyModes": { + "off": "", + "offMsg": "", + "include": "", + "includeMsg": "", + "exclude": "", + "excludeMsg": "" + }, + "showSystemApps": "", + "hideSystemApps": "", + "clearSelection": "" + }, + "config": { + "section": { + "route": "", + "dns": "", + "inbound": "", + "misc": "" + }, + "pageTitle": "", + "executeConfigAsIs": "", + "executeConfigAsIsMsg": "", + "logLevel": "", + "resolveDestination": "", + "ipv6Mode": "", + "ipv6Modes": { + "disable": "", + "enable": "", + "prefer": "", + "only": "" + }, + "remoteDnsAddress": "", + "remoteDnsDomainStrategy": "", + "directDnsAddress": "", + "directDnsDomainStrategy": "", + "mixedPort": "", + "localDnsPort": "", + "tunImplementation": "", + "mtu": "", + "connectionTestUrl": "", + "urlTestInterval": "", + "enableClashApi": "", + "clashApiPort": "", + "enableTun": "", + "setSystemProxy": "" + } + }, + "about": { + "pageTitle": "", + "version": "", + "sourceCode": "", + "telegramChannel": "", + "checkForUpdate": "", + "privacyPolicy": "", + "termsAndConditions": "" + }, + "appUpdate": { + "notAvailableMsg": "", + "dialogTitle": "", + "updateMsg": "", + "currentVersionLbl": "", + "newVersionLbl": "", + "updateNowBtnTxt": "", + "laterBtnTxt": "", + "ignoreBtnTxt": "" + }, + "tray": { + "dashboard": "", + "quit": "", + "systemProxy": "" + }, + "failure": { + "unexpected": "", + "clash": { + "unexpected": "", + "core": "" + }, + "singbox": { + "unexpected": "", + "serviceNotRunning": "", + "invalidConfigOptions": "", + "invalidConfig": "", + "create": "", + "start": "" + }, + "connectivity": { + "unexpected": "", + "missingVpnPermission": "", + "missingNotificationPermission": "", + "core": "" + }, + "profiles": { + "unexpected": "", + "notFound": "", + "invalidConfig": "", + "invalidUrl": "" + }, + "connection": { + "unexpected": "", + "timeout": "", + "badResponse": "", + "connectionError": "", + "badCertificate": "" + } + }, + "play": { + "title": "", + "short_description": "", + "full_description": "" + } +} From 1f95b44376105bcbdd260b6b7ff64c6e6636be53 Mon Sep 17 00:00:00 2001 From: Hiddify Date: Wed, 18 Oct 2023 11:49:18 +0200 Subject: [PATCH 09/27] add inlang project and remove localize --- .github/auto_translator.py | 4 +- .github/sync_translate.sh | 28 +- assets/translations/en.json | 260 ------------------ assets/translations/fa.json | 260 ------------------ assets/translations/fr-FR.json | 260 ------------------ assets/translations/ru-RU.json | 260 ------------------ ...strings.i18n.json => strings_en.i18n.json} | 0 assets/translations/zh-CN.json | 260 ------------------ project.inlang.json | 7 +- 9 files changed, 19 insertions(+), 1320 deletions(-) delete mode 100644 assets/translations/en.json delete mode 100644 assets/translations/fa.json delete mode 100644 assets/translations/fr-FR.json delete mode 100644 assets/translations/ru-RU.json rename assets/translations/{strings.i18n.json => strings_en.i18n.json} (100%) delete mode 100644 assets/translations/zh-CN.json diff --git a/.github/auto_translator.py b/.github/auto_translator.py index e6843e9c..68c8125b 100644 --- a/.github/auto_translator.py +++ b/.github/auto_translator.py @@ -6,8 +6,8 @@ import os def get_path(lang): - if lang == 'en': - return f'../assets/translations/strings.i18n.json' + # if lang == 'en': + # return f'../assets/translations/strings.i18n.json' return f'../assets/translations/strings_{lang}.i18n.json' diff --git a/.github/sync_translate.sh b/.github/sync_translate.sh index 8e9a757e..d67c2b09 100644 --- a/.github/sync_translate.sh +++ b/.github/sync_translate.sh @@ -1,9 +1,9 @@ -key="FRu3eopQWgsvWmnycBXxv2eWpbUwGOu2" -wget -O ../assets/translations/strings.i18n.json "https://localise.biz/api/export/locale/en-US.json?index=id&format=i18next4&key=$key" -wget -O ../assets/translations/strings_fa.i18n.json "https://localise.biz/api/export/locale/fa.json?index=id&format=i18next4&key=$key" -wget -O ../assets/translations/strings_zh.i18n.json "https://localise.biz/api/export/locale/zh.json?index=id&format=i18next4&key=$key" -# # wget -O ../assets/translations/strings_pt.i18n.json "https://localise.biz/api/export/locale/pt.json?index=id&format=i18next4&key=$key" -wget -O ../assets/translations/strings_ru.i18n.json "https://localise.biz/api/export/locale/ru.json?index=id&format=i18next4&key=$key" +# key="FRu3eopQWgsvWmnycBXxv2eWpbUwGOu2" +# wget -O ../assets/translations/strings_en.i18n.json "https://localise.biz/api/export/locale/en-US.json?index=id&format=i18next4&key=$key" +# wget -O ../assets/translations/strings_fa.i18n.json "https://localise.biz/api/export/locale/fa.json?index=id&format=i18next4&key=$key" +# wget -O ../assets/translations/strings_zh.i18n.json "https://localise.biz/api/export/locale/zh.json?index=id&format=i18next4&key=$key" +# # # wget -O ../assets/translations/strings_pt.i18n.json "https://localise.biz/api/export/locale/pt.json?index=id&format=i18next4&key=$key" +# wget -O ../assets/translations/strings_ru.i18n.json "https://localise.biz/api/export/locale/ru.json?index=id&format=i18next4&key=$key" @@ -20,9 +20,9 @@ python3 auto_translator.py en ru function update_localise(){ lang=$1 pat="../assets/translations/strings_${lang}.i18n.json" - if [[ $lang == 'en' ]];then - pat="../assets/translations/strings.i18n.json" - fi + # if [[ $lang == 'en' ]];then + # pat="../assets/translations/strings.i18n.json" + # fi # curl -X POST "https://localise.biz/api/import/json?locale=$lang&key=$LOCALIZ_KEY" \ curl "https://localise.biz/api/import/json?format=i18next4&delete-absent=false&ignore-existing=false&locale=$lang&flag-new=Provisional&key=$LOCALIZ_KEY" \ -H 'Accept: application/json' \ @@ -30,8 +30,8 @@ curl "https://localise.biz/api/import/json?format=i18next4&delete-absent=false&i } -update_localise en -update_localise fa -update_localise zh -# # # update_localise pt -update_localise ru \ No newline at end of file +# update_localise en +# update_localise fa +# update_localise zh +# # # # update_localise pt +# update_localise ru \ No newline at end of file diff --git a/assets/translations/en.json b/assets/translations/en.json deleted file mode 100644 index b89ecfe9..00000000 --- a/assets/translations/en.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "general": { - "appTitle": "Hiddify Next", - "reset": "Reset", - "toggle": { - "enabled": "Enabled", - "disabled": "Disabled" - }, - "state": { - "disable": "Disable" - }, - "sort": "Sort", - "sortBy": "Sort by", - "addToClipboard": "Add to clipboard" - }, - "intro": { - "termsAndPolicyCaution(rich)": "by continuing you agree with ${tap(@:about.termsAndConditions)}", - "start": "Start" - }, - "home": { - "pageTitle": "Home", - "emptyProfilesMsg": "Begin by adding a subscription profile", - "noActiveProfileMsg": "Choose a profile", - "connection": { - "tapToConnect": "Tap to Connect", - "connecting": "Connecting", - "disconnecting": "Disconnecting", - "connected": "Connected" - }, - "stats": { - "traffic": "Live Traffic", - "trafficTotal": "Total Traffic", - "uplink": "Uplink", - "downlink": "Downlink" - } - }, - "profile": { - "overviewPageTitle": "Profiles", - "detailsPageTitle": "Profile", - "activeProfileNameSemanticLabel": "Active profile name: \"${name}\".", - "activeProfileBtnSemanticLabel": "View all profiles.", - "nonActiveProfileBtnSemanticLabel": "Select \"${name}\" as active profile.", - "subscription": { - "traffic": "Traffic", - "updatedTimeAgo": "Updated ${timeago}", - "remainingDuration": "${duration} Days Remaining", - "remainingTrafficSemanticLabel": "${consumed} of ${total} traffic consumed.", - "expired": "Expired", - "noTraffic": "No more traffic" - }, - "sortBy": { - "lastUpdate": "Recently updated", - "name": "Alphabetically" - }, - "add": { - "buttonText": "New Profile", - "shortBtnTxt": "New Profile", - "fromClipboard": "Add From Clipboard", - "scanQr": "Scan QR code", - "manually": "Manual Entry", - "addingProfileMsg": "Adding Profile", - "failureMsg": "Failed to add profile" - }, - "update": { - "buttonTxt": "Update", - "tooltip": "Update Profile", - "failureMsg": "Failed to update profile", - "successMsg": "Profile updated successfully" - }, - "edit": { - "buttonTxt": "Edit", - "selectActiveTxt": "Select active profile" - }, - "delete": { - "buttonTxt": "Delete", - "confirmationMsg": "Delete profile permanently?", - "successMsg": "Profile deleted successfully" - }, - "save": { - "buttonText": "Save", - "successMsg": "Profile saved successfully", - "failureMsg": "Failed to save profile" - }, - "detailsForm": { - "nameLabel": "Name", - "nameHint": "Profile name", - "urlLabel": "URL", - "urlHint": "Full config URL", - "emptyNameMsg": "Name is required", - "invalidUrlMsg": "Invalid URL", - "lastUpdate": "Last Update", - "updateInterval": "Auto Update", - "updateIntervalDialogTitle": "Auto Update Interval (in hours)" - } - }, - "proxies": { - "pageTitle": "Proxies", - "emptyProxiesMsg": "No proxies available", - "delayTestTooltip": "Test Delay", - "sortTooltip": "Sort Proxies", - "sortOptions": { - "unsorted": "Default", - "name": "Alphabetically", - "delay": "By Delay" - } - }, - "logs": { - "pageTitle": "Logs", - "clearLogsButtonText": "Clear Logs", - "filterHint": "Filter", - "allLevelsFilter": "All", - "shareCoreLogs": "Share Core Logs", - "shareAppLogs": "Share App logs" - }, - "settings": { - "pageTitle": "Settings", - "requiresRestartMsg": "For this to take effect restart the app", - "general": { - "sectionTitle": "General", - "locale": "Language", - "region": "Region", - "regionMsg": "Helps set default options to bypass domestic addresses", - "regions": { - "ir": "Iran (ir)", - "cn": "China (cn)", - "other": "Other" - }, - "themeMode": "Theme Mode", - "themeModes": { - "system": "Follow system theme", - "dark": "Dark mode", - "light": "Light mode" - }, - "enableAnalytics": "Enable Analytics", - "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", - "trueBlack": "Pure Black", - "autoStart": "Start on Boot", - "silentStart": "Silent Start", - "openWorkingDir": "Open Working Directory", - "ignoreBatteryOptimizations": "Disable Battery Optimization", - "ignoreBatteryOptimizationsMsg": "Remove restrictions for optimal VPN performance" - }, - "advanced": { - "sectionTitle": "Advanced", - "debugMode": "Debug Mode", - "debugModeMsg": "Restart the app for applying this change" - }, - "network": { - "perAppProxyPageTitle": "Per-app Proxy", - "perAppProxyModes": { - "off": "All", - "offMsg": "Proxy all apps", - "include": "Proxy", - "includeMsg": "Proxy only selected apps", - "exclude": "Bypass", - "excludeMsg": "Do not proxy selected apps" - }, - "showSystemApps": "Show system apps", - "hideSystemApps": "Hide system apps", - "clearSelection": "Clear selection" - }, - "config": { - "section": { - "route": "Route Options", - "dns": "DNS Options", - "inbound": "Inbound Options", - "misc": "Misc Options" - }, - "pageTitle": "Config Options", - "executeConfigAsIs": "Execute Config As Is", - "executeConfigAsIsMsg": "Executes the SingBox configs with minimal changes. Can cause problems.", - "logLevel": "Log Level", - "resolveDestination": "Resolve Destination", - "ipv6Mode": "IPv6 Route", - "ipv6Modes": { - "disable": "Disable", - "enable": "Enable", - "prefer": "Preferred", - "only": "Exclusive" - }, - "remoteDnsAddress": "Remote DNS", - "remoteDnsDomainStrategy": "Remote DNS Domain Strategy", - "directDnsAddress": "Direct DNS", - "directDnsDomainStrategy": "Direct DNS Domain Strategy", - "mixedPort": "Mixed Port", - "localDnsPort": "Local DNS Port", - "tunImplementation": "TUN Implementation", - "mtu": "MTU", - "connectionTestUrl": "Connection Test URL", - "urlTestInterval": "URL Test Interval", - "enableClashApi": "Enable Clash API", - "clashApiPort": "Clash API Port", - "enableTun": "Enable TUN", - "setSystemProxy": "Set System Proxy" - } - }, - "about": { - "pageTitle": "About", - "version": "Version", - "sourceCode": "Source Code", - "telegramChannel": "Telegram Channel", - "checkForUpdate": "Check for update", - "privacyPolicy": "Privacy policy", - "termsAndConditions": "Terms and conditions" - }, - "appUpdate": { - "notAvailableMsg": "Already using the latest version", - "dialogTitle": "Update Available", - "updateMsg": "A new version of @:general.appTitle is available. Would you like to update now?", - "currentVersionLbl": "Current Version", - "newVersionLbl": "New Version", - "updateNowBtnTxt": "Update Now", - "laterBtnTxt": "Later", - "ignoreBtnTxt": "Ignore" - }, - "tray": { - "dashboard": "Dashboard", - "quit": "Quit", - "systemProxy": "System Proxy" - }, - "failure": { - "unexpected": "Unexpected Error", - "clash": { - "unexpected": "Unexpected Error", - "core": "Clash Error ${reason}" - }, - "singbox": { - "unexpected": "Unexpected Service Error", - "serviceNotRunning": "Service is not running", - "invalidConfigOptions": "Invalid configuration options", - "invalidConfig": "Invalid Configuration", - "create": "Service creation error", - "start": "Service startup error" - }, - "connectivity": { - "unexpected": "Unexpected Failure", - "missingVpnPermission": "Missing VPN Permission", - "missingNotificationPermission": "Missing Notification Permission", - "core": "Core Error" - }, - "profiles": { - "unexpected": "Unexpected Error", - "notFound": "Profile Not Found", - "invalidConfig": "Invalid Configs", - "invalidUrl": "Invalid URL" - }, - "connection": { - "unexpected": "Unexpected connection error", - "timeout": "Connection timeout", - "badResponse": "Bad response", - "connectionError": "Connection error", - "badCertificate": "Bad certificate" - } - }, - "play": { - "title": "Hiddify Next (Preview)", - "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "The key goal of HiddifyNext is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe support servers with:\n- Normal V2ray/Xray Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - more and more\n\nSupport:\n- All Protocols supported by Sing-Box \n- VLESS + xtls reality, vision\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n- V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source sing-box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." - } -} diff --git a/assets/translations/fa.json b/assets/translations/fa.json deleted file mode 100644 index 9fe3206c..00000000 --- a/assets/translations/fa.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "general": { - "appTitle": "هیدیفای نکست", - "reset": "بازنشانی", - "toggle": { - "enabled": "فعال", - "disabled": "غیر فعال" - }, - "state": { - "disable": "غیر فعال" - }, - "sort": "مرتب‌سازی", - "sortBy": "مرتب‌سازی براساس", - "addToClipboard": "به کلیپ بورد اضافه کنید" - }, - "intro": { - "termsAndPolicyCaution(rich)": "در صورت ادامه با ${tap(@:about.termsAndConditions)} موافقت میکنید", - "start": "شروع" - }, - "home": { - "pageTitle": "خانه", - "emptyProfilesMsg": "با افزودن پروفایل شروع کنید", - "noActiveProfileMsg": "یک پروفایل انتخاب کنید", - "connection": { - "tapToConnect": "برای اتصال ضربه بزنید", - "connecting": "در حال اتصال", - "disconnecting": "در حال قطع اتصال", - "connected": "متصل" - }, - "stats": { - "traffic": "مصرف لحظه‌ای", - "trafficTotal": "مصرف کل", - "uplink": "ارسال", - "downlink": "دریافت" - } - }, - "profile": { - "overviewPageTitle": "پروفایل‌ها", - "detailsPageTitle": "پروفایل", - "activeProfileNameSemanticLabel": "نام پروفایل فعال: ${name}", - "activeProfileBtnSemanticLabel": "همه‌ی پروفایل‌ها", - "nonActiveProfileBtnSemanticLabel": "انتخاب ${name} به عنوان پروفایل فعال", - "subscription": { - "traffic": "ترافیک", - "updatedTimeAgo": "بروزرسانی شده در ${timeago}", - "remainingDuration": "${duration} روز باقی مانده", - "remainingTrafficSemanticLabel": "${consumed} از ${total} ترافیک مصرف شده", - "expired": "منقضی شده", - "noTraffic": "پایان ترافیک" - }, - "sortBy": { - "lastUpdate": "اخیرا بروز شده", - "name": "براساس نام" - }, - "add": { - "buttonText": "افزودن پروفایل جدید", - "shortBtnTxt": "افزودن", - "fromClipboard": "افزودن از کلیپ‌بورد", - "scanQr": "اسکن QR کد", - "manually": "افزودن دستی", - "addingProfileMsg": "در حال افزودن پروفایل", - "failureMsg": "در افزودن پروفایل خطایی رخ داد" - }, - "update": { - "buttonTxt": "بروزرسانی", - "tooltip": "بروزرسانی پروفایل", - "failureMsg": "در بروزرسانی پروفایل خطایی رخ داد", - "successMsg": "پروفایل با موفقیت بروزرسانی شد" - }, - "edit": { - "buttonTxt": "ویرایش", - "selectActiveTxt": "انتخاب پروفایل فعال" - }, - "delete": { - "buttonTxt": "حذف", - "confirmationMsg": "حذف پروفایل برای همیشه؟", - "successMsg": "پروفایل با موفقیت حذف شد" - }, - "save": { - "buttonText": "ذخیره", - "successMsg": "پروفایل با موفقیت ذخیره شد", - "failureMsg": "خطا در ذخیره پروفایل" - }, - "detailsForm": { - "nameLabel": "نام", - "nameHint": "نام پروفایل", - "urlLabel": "لینک", - "urlHint": "آدرس کامل کانفیگ", - "emptyNameMsg": "نام نمی‌تواند خالی باشد", - "invalidUrlMsg": "لینک نامعتبر", - "lastUpdate": "آخرین بروزرسانی", - "updateInterval": "بروزرسانی خودکار", - "updateIntervalDialogTitle": "فاصله زمانی بروزرسانی خودکار (ساعت)" - } - }, - "proxies": { - "pageTitle": "پراکسی‌ها", - "emptyProxiesMsg": "پراکسی وجود ندارد", - "delayTestTooltip": "تست تاخیر", - "sortTooltip": "مرتب‌سازی پراکسی‌ها", - "sortOptions": { - "unsorted": "پیش‌فرض", - "name": "براساس نام", - "delay": "براساس تاخیر" - } - }, - "logs": { - "pageTitle": "لاگ‌ها", - "clearLogsButtonText": "پاک‌سازی", - "filterHint": "فیلتر", - "allLevelsFilter": "همه", - "shareCoreLogs": "اشتراک‌گذاری لاگ هسته", - "shareAppLogs": "اشتراک‌گذاری لاگ برنامه" - }, - "settings": { - "pageTitle": "تنظیمات", - "requiresRestartMsg": "برای اعمال این تنظیم برنامه را بازنشانی کنید", - "general": { - "sectionTitle": "عمومی", - "locale": "زبان", - "region": "منطقه", - "regionMsg": "به انتخاب تنظیمات پیش‌فرض برای دورزدن آدرس‌های داخلی کمک میکند", - "regions": { - "ir": "ایران (ir)", - "cn": "چین (cn)", - "other": "سایر" - }, - "themeMode": "تم مود", - "themeModes": { - "system": "پیروی از تم دستگاه", - "dark": "تم تیره", - "light": "تم روشن" - }, - "enableAnalytics": "فعال‌سازی آنالیتیکز", - "enableAnalyticsMsg": "ارائه دسترسی آنالیز و گزارش خطا برای بهبود عملکرد برنامه", - "trueBlack": "کاملا سیاه", - "autoStart": "اجرا با روشن شدن سیستم", - "silentStart": "اجرای ساکت", - "openWorkingDir": "باز کردن دایرکتوری کاری", - "ignoreBatteryOptimizations": "غیرفعال کردن بهینه‌سازی باتری", - "ignoreBatteryOptimizationsMsg": "حذف محدودیت‌ها برای عملکرد بهتر VPN" - }, - "advanced": { - "sectionTitle": "پیشرفته", - "debugMode": "دیباگ مود", - "debugModeMsg": "برای اعمال این تغییر اپ را ری‌استارت کنید" - }, - "network": { - "perAppProxyPageTitle": "پراکسی برنامه‌ها", - "perAppProxyModes": { - "off": "همه", - "offMsg": "همه برنامه‌ها پراکسی میشوند", - "include": "پراکسی", - "includeMsg": "تنها برنامه‌های انتخاب شده پراکسی میشوند", - "exclude": "بایپس", - "excludeMsg": "همه بجز برنامه‌های انتخاب شده پراکسی میشوند" - }, - "showSystemApps": "نمایش برنامه‌های سیستمی", - "hideSystemApps": "مخفی کردن برنامه‌های سیستمی", - "clearSelection": "حذف انتخاب‌ها" - }, - "config": { - "section": { - "route": "تنظیمات مسیریاب", - "dns": "تنظیمات DNS", - "inbound": "تنظیمات ورودی", - "misc": "تنظیمات متفرقه" - }, - "pageTitle": "تنظیمات کانفیگ", - "executeConfigAsIs": "اجرای کانفیگ بدون تغییر", - "executeConfigAsIsMsg": "کانفیگ موجود را با تغییرات حداقلی اجرا می‌کند. فقط برای کانفیگ‌های سینگ‌باکس کار می‌کند", - "logLevel": "سطح لاگ", - "resolveDestination": "جایگذاری IP مقصد", - "ipv6Mode": "مسیریابی IPV6 در تونل", - "ipv6Modes": { - "disable": "غیرفعال", - "enable": "فعال", - "prefer": "ترجیح", - "only": "فقط" - }, - "remoteDnsAddress": "DNS خارجی", - "remoteDnsDomainStrategy": "حالت DNS خارجی", - "directDnsAddress": "DNS مستقیم", - "directDnsDomainStrategy": "حالت DNS مستقیم", - "mixedPort": "پورت همه منظوره", - "localDnsPort": "پورت DNS داخلی", - "tunImplementation": "مدل تونل", - "mtu": "سایز بسته‌ها", - "connectionTestUrl": "لینک تست اتصال", - "urlTestInterval": "زمان تست اتصال", - "enableClashApi": "فعالسازی کلش", - "clashApiPort": "پورت کلش", - "enableTun": "فعال سازی تونل", - "setSystemProxy": "تنظیم پروکسی سیستم" - } - }, - "about": { - "pageTitle": "درباره", - "version": "ورژن", - "sourceCode": "سورس کد", - "telegramChannel": "کانال تلگرام", - "checkForUpdate": "بررسی آپدیت جدید", - "privacyPolicy": "سیاست حفظ حریم خصوصی", - "termsAndConditions": "شرایط و ضوابط استفاده" - }, - "appUpdate": { - "notAvailableMsg": "نسخه جدیدی یافت نشد", - "dialogTitle": "نسخه جدید موجود است", - "updateMsg": "نسخه جدیدی از @:general.appTitle موجود است! الان بروزرسانی شود؟", - "currentVersionLbl": "نسخه فعلی", - "newVersionLbl": "نسخه جدید", - "updateNowBtnTxt": "بروزرسانی", - "laterBtnTxt": "بعدا", - "ignoreBtnTxt": "نادیده‌گرفتن" - }, - "tray": { - "dashboard": "داشبورد", - "quit": "خروج", - "systemProxy": "پراکسی سیستم" - }, - "failure": { - "unexpected": "خطای غیرمنتظره", - "clash": { - "unexpected": "خطای غیرمنتظره", - "core": "خطای کلش ${reason}" - }, - "singbox": { - "unexpected": "خطای غیرمنتظره در سرویس", - "serviceNotRunning": "سرویس در حال اجرا نیست", - "invalidConfigOptions": "تنظیمات کانفیگ نامعتبر", - "invalidConfig": "کانفیگ غیر معتبر", - "create": "در ایجاد سرویس خطایی رخ داده", - "start": "در راه‌اندازی سرویس خطایی رخ داده" - }, - "connectivity": { - "unexpected": "خطای غیرمنتظره", - "missingVpnPermission": "نیازمند دسترسی VPN", - "missingNotificationPermission": "نیازمند دسترسی اعلانات", - "core": "خطای هسته" - }, - "profiles": { - "unexpected": "خطای غیرمنتظره", - "notFound": "پروفایل یافت نشد", - "invalidConfig": "کانفیگ غیر معتبر", - "invalidUrl": "لینک نامعتبر" - }, - "connection": { - "unexpected": " خطای غیرمنتظره در اتصال", - "timeout": "درخواست بیش از حد مجاز زمان برد", - "badResponse": "پاسخ نامعتبر", - "connectionError": "خطای اتصال", - "badCertificate": "خطای اعتبار سنجی" - } - }, - "play": { - "title": "هیدیفای نکست Hiddify Next", - "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "هدف اصلی HiddifyNext ارائه یک کلاینت تونل زنی ایمن، کاربرپسند و کارآمد است. این به شما امکان می دهد تا با استفاده از مجوز VPN-Service، تمام ترافیک یا ترافیک برنامه انتخابی را به یک سرور راه دور مورد نظر خود هدایت کنید.\n\nتوجه: ما هیچ سروری ارائه نمی دهیم. کاربران موظفند با استفاده از سرورهای خود میزبان یا سرورهای مورد اعتماد، فعالیت‌های آنلاین خود را خصوصی نگه دارند.\n \nما از سرورهایی با موارد زیر پشتیبانی می کنیم:\n- لینک اشتراک V2ray/Xray معمولی\n- لینک اشتراک کلش\n- لینک اشتراک Sing-Box\n\nویژگی های منحصر به فرد ما چیست؟\n - کاربر پسند\n - بهینه و سریع\n - به طور خودکار LowestPing را انتخاب کنید\n - نمایش اطلاعات استفاده کاربر\n - به راحتی لینک فرعی را با یک کلیک با استفاده از دیپ لینک وارد کنید\n - رایگان و بدون تبلیغات\n - به راحتی پیوندهای فرعی کاربر را تغییر دهید\n - بیشتر و بیشتر\n\nحمایت کردن:\n- تمام پروتکل های پشتیبانی شده توسط Sing-Box\n- VLESS + xtls \n- VMESS\n- تروجان\n- ShoadowSocks\n- ریالیتی\n- V2ray\n- هیستریا 2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nکد منبع در https://github.com/hiddify/Hiddify-Next وجود دارد\nهسته برنامه بر اساس sing-box منبع باز است.\n\nتوضیحات مجوز:\n- سرویس VPN: از آنجا که هدف این برنامه ارائه یک کلاینت تونل زنی ایمن، کاربر پسند و کارآمد است، ما به این مجوز نیاز داریم تا بتوانیم ترافیک را از طریق تونل به سرور راه دور هدایت کنیم.\n- QUERY ALL PACKAGES: این مجوز برای اجازه دادن به کاربران برای گنجاندن یا حذف برنامه های کاربردی خاص برای تونل زدن استفاده می شود.\n- دریافت بوت تکمیل شد: این مجوز را می توان از تنظیمات برنامه فعال یا غیرفعال کرد تا این برنامه پس از بوت شدن دستگاه فعال شود.\n- اعلان های ارسالی: این مجوز ضروری است زیرا ما از یک سرویس پیش زمینه برای اطمینان از عملکرد مداوم سرویس VPN استفاده می کنیم.\n- این برنامه بدون تبلیغات است. تجزیه و تحلیل و داده های اشکال فقط با رضایت صریح کاربر در اولین استفاده از برنامه اتفاق می افتد." - } -} diff --git a/assets/translations/fr-FR.json b/assets/translations/fr-FR.json deleted file mode 100644 index 4a6a9a96..00000000 --- a/assets/translations/fr-FR.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "general": { - "appTitle": "", - "reset": "", - "toggle": { - "enabled": "", - "disabled": "" - }, - "state": { - "disable": "" - }, - "sort": "", - "sortBy": "", - "addToClipboard": "" - }, - "intro": { - "termsAndPolicyCaution(rich)": "", - "start": "" - }, - "home": { - "pageTitle": "", - "emptyProfilesMsg": "", - "noActiveProfileMsg": "", - "connection": { - "tapToConnect": "", - "connecting": "", - "disconnecting": "", - "connected": "" - }, - "stats": { - "traffic": "", - "trafficTotal": "", - "uplink": "", - "downlink": "" - } - }, - "profile": { - "overviewPageTitle": "", - "detailsPageTitle": "", - "activeProfileNameSemanticLabel": "", - "activeProfileBtnSemanticLabel": "", - "nonActiveProfileBtnSemanticLabel": "", - "subscription": { - "traffic": "", - "updatedTimeAgo": "", - "remainingDuration": "", - "remainingTrafficSemanticLabel": "", - "expired": "", - "noTraffic": "" - }, - "sortBy": { - "lastUpdate": "", - "name": "" - }, - "add": { - "buttonText": "", - "shortBtnTxt": "", - "fromClipboard": "", - "scanQr": "", - "manually": "", - "addingProfileMsg": "", - "failureMsg": "" - }, - "update": { - "buttonTxt": "", - "tooltip": "", - "failureMsg": "", - "successMsg": "" - }, - "edit": { - "buttonTxt": "", - "selectActiveTxt": "" - }, - "delete": { - "buttonTxt": "", - "confirmationMsg": "", - "successMsg": "" - }, - "save": { - "buttonText": "", - "successMsg": "", - "failureMsg": "" - }, - "detailsForm": { - "nameLabel": "", - "nameHint": "", - "urlLabel": "", - "urlHint": "", - "emptyNameMsg": "", - "invalidUrlMsg": "", - "lastUpdate": "", - "updateInterval": "", - "updateIntervalDialogTitle": "" - } - }, - "proxies": { - "pageTitle": "", - "emptyProxiesMsg": "", - "delayTestTooltip": "", - "sortTooltip": "", - "sortOptions": { - "unsorted": "", - "name": "", - "delay": "" - } - }, - "logs": { - "pageTitle": "", - "clearLogsButtonText": "", - "filterHint": "", - "allLevelsFilter": "", - "shareCoreLogs": "", - "shareAppLogs": "" - }, - "settings": { - "pageTitle": "", - "requiresRestartMsg": "", - "general": { - "sectionTitle": "", - "locale": "", - "region": "", - "regionMsg": "", - "regions": { - "ir": "", - "cn": "", - "other": "" - }, - "themeMode": "", - "themeModes": { - "system": "", - "dark": "", - "light": "" - }, - "enableAnalytics": "", - "enableAnalyticsMsg": "", - "trueBlack": "", - "autoStart": "", - "silentStart": "", - "openWorkingDir": "", - "ignoreBatteryOptimizations": "", - "ignoreBatteryOptimizationsMsg": "" - }, - "advanced": { - "sectionTitle": "", - "debugMode": "", - "debugModeMsg": "" - }, - "network": { - "perAppProxyPageTitle": "", - "perAppProxyModes": { - "off": "", - "offMsg": "", - "include": "", - "includeMsg": "", - "exclude": "", - "excludeMsg": "" - }, - "showSystemApps": "", - "hideSystemApps": "", - "clearSelection": "" - }, - "config": { - "section": { - "route": "", - "dns": "", - "inbound": "", - "misc": "" - }, - "pageTitle": "", - "executeConfigAsIs": "", - "executeConfigAsIsMsg": "", - "logLevel": "", - "resolveDestination": "", - "ipv6Mode": "", - "ipv6Modes": { - "disable": "", - "enable": "", - "prefer": "", - "only": "" - }, - "remoteDnsAddress": "", - "remoteDnsDomainStrategy": "", - "directDnsAddress": "", - "directDnsDomainStrategy": "", - "mixedPort": "", - "localDnsPort": "", - "tunImplementation": "", - "mtu": "", - "connectionTestUrl": "", - "urlTestInterval": "", - "enableClashApi": "", - "clashApiPort": "", - "enableTun": "", - "setSystemProxy": "" - } - }, - "about": { - "pageTitle": "", - "version": "", - "sourceCode": "", - "telegramChannel": "", - "checkForUpdate": "", - "privacyPolicy": "", - "termsAndConditions": "" - }, - "appUpdate": { - "notAvailableMsg": "", - "dialogTitle": "", - "updateMsg": "", - "currentVersionLbl": "", - "newVersionLbl": "", - "updateNowBtnTxt": "", - "laterBtnTxt": "", - "ignoreBtnTxt": "" - }, - "tray": { - "dashboard": "", - "quit": "", - "systemProxy": "" - }, - "failure": { - "unexpected": "", - "clash": { - "unexpected": "", - "core": "" - }, - "singbox": { - "unexpected": "", - "serviceNotRunning": "", - "invalidConfigOptions": "", - "invalidConfig": "", - "create": "", - "start": "" - }, - "connectivity": { - "unexpected": "", - "missingVpnPermission": "", - "missingNotificationPermission": "", - "core": "" - }, - "profiles": { - "unexpected": "", - "notFound": "", - "invalidConfig": "", - "invalidUrl": "" - }, - "connection": { - "unexpected": "", - "timeout": "", - "badResponse": "", - "connectionError": "", - "badCertificate": "" - } - }, - "play": { - "title": "", - "short_description": "", - "full_description": "" - } -} diff --git a/assets/translations/ru-RU.json b/assets/translations/ru-RU.json deleted file mode 100644 index 2541f7ef..00000000 --- a/assets/translations/ru-RU.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "general": { - "appTitle": "HiddifyNext", - "reset": "Сброс", - "toggle": { - "enabled": "Включено", - "disabled": "Отключено" - }, - "state": { - "disable": "Отключить" - }, - "sort": "Сортировка", - "sortBy": "Сортировка", - "addToClipboard": "Копировать в буфер обмена" - }, - "intro": { - "termsAndPolicyCaution(rich)": "Продолжая, вы соглашаетесь с ${tap(@:about.termsAndConditions)}", - "start": "Начать" - }, - "home": { - "pageTitle": "Главная", - "emptyProfilesMsg": "Начните с добавления профиля подписки", - "noActiveProfileMsg": "Выберите профиль", - "connection": { - "tapToConnect": "Нажмите для подключения", - "connecting": "Подключение", - "disconnecting": "Отключение", - "connected": "Подключено" - }, - "stats": { - "traffic": "Скорость", - "trafficTotal": "Трафик", - "uplink": "Восходящий канал", - "downlink": "Нисходящий канал" - } - }, - "profile": { - "overviewPageTitle": "Профили", - "detailsPageTitle": "Профиль", - "activeProfileNameSemanticLabel": "Имя активного профиля: «${name}».", - "activeProfileBtnSemanticLabel": "Посмотреть все профили.", - "nonActiveProfileBtnSemanticLabel": "Выберите «${name}» в качестве активного профиля.", - "subscription": { - "traffic": "Трафик", - "updatedTimeAgo": "Обновлено ${timeago}", - "remainingDuration": "Ещё ${duration} дн.", - "remainingTrafficSemanticLabel": "${consumed} из ${total} использованного трафика.", - "expired": "Истекло", - "noTraffic": "Нет доступного трафика" - }, - "sortBy": { - "lastUpdate": "Последнее обновление", - "name": "По алфавиту" - }, - "add": { - "buttonText": "Новый профиль", - "shortBtnTxt": "Новый профиль", - "fromClipboard": "Добавить из буфера обмена", - "scanQr": "Сканировать QR-код", - "manually": "Ручной ввод", - "addingProfileMsg": "Добавление профиля", - "failureMsg": "Невозможно добавить профиль" - }, - "update": { - "buttonTxt": "Обновить", - "tooltip": "Обновить профиль", - "failureMsg": "Ошибка обновления", - "successMsg": "Профиль успешно обновлён" - }, - "edit": { - "buttonTxt": "Изменить", - "selectActiveTxt": "Выберите активный профиль" - }, - "delete": { - "buttonTxt": "Удалить", - "confirmationMsg": "Безвозвратно удалить профиль?", - "successMsg": "Профиль успешно удалён" - }, - "save": { - "buttonText": "Сохранить", - "successMsg": "Профиль успешно сохранен", - "failureMsg": "Невозможно сохранить профиль" - }, - "detailsForm": { - "nameLabel": "Имя", - "nameHint": "Имя профиля", - "urlLabel": "URL", - "urlHint": "Полный URL конфигурации", - "emptyNameMsg": "Поле «Имя» обязательно", - "invalidUrlMsg": "Неправильный URL", - "lastUpdate": "Последнее обновление", - "updateInterval": "Автоматическое обновление", - "updateIntervalDialogTitle": "Интервал обновления (в часах)" - } - }, - "proxies": { - "pageTitle": "Прокси", - "emptyProxiesMsg": "Нет доступных прокси", - "delayTestTooltip": "Тестирование задержки", - "sortTooltip": "Сортировка прокси", - "sortOptions": { - "unsorted": "По умолчанию", - "name": "По алфавиту", - "delay": "По задержке" - } - }, - "logs": { - "pageTitle": "Журналы", - "clearLogsButtonText": "Очистить журналы", - "filterHint": "Фильтр", - "allLevelsFilter": "Все", - "shareCoreLogs": "Поделиться журналами ядра", - "shareAppLogs": "Поделиться журналами приложения" - }, - "settings": { - "pageTitle": "Настройки", - "requiresRestartMsg": "Для применения перезапустите приложение.", - "general": { - "sectionTitle": "Основные", - "locale": "Язык", - "region": "Регион", - "regionMsg": "Помогает установить параметры по умолчанию для обхода внутренних адресов.", - "regions": { - "ir": "Иран (ir)", - "cn": "Китай (cn)", - "other": "Другой" - }, - "themeMode": "Оформление", - "themeModes": { - "system": "Системная тема", - "dark": "Тёмная тема", - "light": "Светлая тема" - }, - "enableAnalytics": "Сбор аналитики", - "enableAnalyticsMsg": "Сбор аналитических данных и отправка отчётов о сбоях для улучшения приложения.", - "trueBlack": "Чистый чёрный цвет", - "autoStart": "Запуск при загрузке", - "silentStart": "Тихий запуск", - "openWorkingDir": "Открыть рабочую папку", - "ignoreBatteryOptimizations": "Отключить оптимизацию батареи", - "ignoreBatteryOptimizationsMsg": "Отключение ограничений для оптимальной производительности VPN." - }, - "advanced": { - "sectionTitle": "Расширенные", - "debugMode": "Режим отладки", - "debugModeMsg": "Для применения перезапустите приложение." - }, - "network": { - "perAppProxyPageTitle": "Раздельное проксирование", - "perAppProxyModes": { - "off": "Все", - "offMsg": "Проксировать все приложения", - "include": "Прокси", - "includeMsg": "Проксировать выбранные приложения", - "exclude": "Обход", - "excludeMsg": "Не проксировать выбранные приложения" - }, - "showSystemApps": "Показать системные приложения", - "hideSystemApps": "Скрыть системные приложения", - "clearSelection": "Очистить выбор" - }, - "config": { - "section": { - "route": "Варианты маршрутизации", - "dns": "Параметры DNS", - "inbound": "Входящие параметры", - "misc": "Разные параметры" - }, - "pageTitle": "Параметры конфигурации", - "executeConfigAsIs": "Использовать конфигурацию как есть", - "executeConfigAsIsMsg": "Использовать конфигурации SingBox с минимальными изменениями.", - "logLevel": "Подробность журналирования", - "resolveDestination": "Определять назначение", - "ipv6Mode": "Маршрутизация IPv6", - "ipv6Modes": { - "disable": "Отключено", - "enable": "Включено", - "prefer": "Предпочтительно", - "only": "Эксклюзивно" - }, - "remoteDnsAddress": "Удалённая DNS", - "remoteDnsDomainStrategy": "Стратегия удалённого домена DNS", - "directDnsAddress": "Прямая DNS", - "directDnsDomainStrategy": "Стратегия прямого домена DNS", - "mixedPort": "Смешанный порт", - "localDnsPort": "Локальный порт DNS", - "tunImplementation": "Реализация TUN", - "mtu": "MTU", - "connectionTestUrl": "URL проверки подключения", - "urlTestInterval": "Интервал проверки URL", - "enableClashApi": "Использовать Clash API", - "clashApiPort": "Порт Clash API", - "enableTun": "Использовать TUN", - "setSystemProxy": "Установить системный прокси" - } - }, - "about": { - "pageTitle": "Информация", - "version": "Версия", - "sourceCode": "Исходный код", - "telegramChannel": "Канал Telegram", - "checkForUpdate": "Проверка обновления", - "privacyPolicy": "Политика конфиденциальности", - "termsAndConditions": "Условия и положения" - }, - "appUpdate": { - "notAvailableMsg": "У вас установлена последняя версия", - "dialogTitle": "Доступно обновление", - "updateMsg": "Доступна новая версия @:general.appTitle. Обновить сейчас?", - "currentVersionLbl": "Текущая версия", - "newVersionLbl": "Новая версия", - "updateNowBtnTxt": "Обновить сейчас", - "laterBtnTxt": "Позже", - "ignoreBtnTxt": "Игнорировать" - }, - "tray": { - "dashboard": "Панель", - "quit": "Выход", - "systemProxy": "Системный прокси" - }, - "failure": { - "unexpected": "Неожиданная ошибка", - "clash": { - "unexpected": "Неожиданная ошибка (Clash)", - "core": "Ошибка ${reason}" - }, - "singbox": { - "unexpected": "Неожиданная ошибка (SingBox)", - "serviceNotRunning": "Сервис не запущен", - "invalidConfigOptions": "Неправильные параметры конфигурации", - "invalidConfig": "Неправильная конфигурация", - "create": "Ошибка создания сервиса", - "start": "Ошибка запуска сервиса" - }, - "connectivity": { - "unexpected": "Неожиданная ошибка", - "missingVpnPermission": "Отсутствует разрешение VPN", - "missingNotificationPermission": "Отсутствует разрешение на уведомление", - "core": "Ошибка ядра" - }, - "profiles": { - "unexpected": "Неожиданная ошибка", - "notFound": "Профиль не найден", - "invalidConfig": "Неправильная конфигурация", - "invalidUrl": "Неправильный URL" - }, - "connection": { - "unexpected": "Неожиданная ошибка подключения", - "timeout": "Истекло время подключения", - "badResponse": "Неправильный ответ", - "connectionError": "Ошибка подключения", - "badCertificate": "Неправильный сертификат" - } - }, - "play": { - "title": "Hiddify Next (Preview)", - "short_description": "Автовыбор, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n- ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." - } -} diff --git a/assets/translations/strings.i18n.json b/assets/translations/strings_en.i18n.json similarity index 100% rename from assets/translations/strings.i18n.json rename to assets/translations/strings_en.i18n.json diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json deleted file mode 100644 index 065f8798..00000000 --- a/assets/translations/zh-CN.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "general": { - "appTitle": "Hiddify Next", - "reset": "重置", - "toggle": { - "enabled": "启用", - "disabled": "停用" - }, - "state": { - "disable": "停用" - }, - "sort": "排序", - "sortBy": "排序方式", - "addToClipboard": "添加到剪贴板" - }, - "intro": { - "termsAndPolicyCaution(rich)": "继续即表示您同意 ${tap(@:about.termsAndConditions)}", - "start": "开始" - }, - "home": { - "pageTitle": "主页", - "emptyProfilesMsg": "首先添加订阅配置文件", - "noActiveProfileMsg": "选择配置文件", - "connection": { - "tapToConnect": "点击连接", - "connecting": "正在连接", - "disconnecting": "断开连接", - "connected": "连接成功" - }, - "stats": { - "traffic": "实际流量", - "trafficTotal": "总流量", - "uplink": "上行链路", - "downlink": "下行" - } - }, - "profile": { - "overviewPageTitle": "配置文件", - "detailsPageTitle": "配置文件", - "activeProfileNameSemanticLabel": "活动配置文件名称:“${name}”。", - "activeProfileBtnSemanticLabel": "查看所有配置文件", - "nonActiveProfileBtnSemanticLabel": "选择“${name}”作为活动配置文件。", - "subscription": { - "traffic": "流量", - "updatedTimeAgo": "更新${timeago}", - "remainingDuration": "剩余 ${duration} 天", - "remainingTrafficSemanticLabel": "已消耗 ${consumed} 流量,共 ${total} 流量。", - "expired": "已到期", - "noTraffic": "无流量" - }, - "sortBy": { - "lastUpdate": "最近更新", - "name": "按字母顺序" - }, - "add": { - "buttonText": "新的配置文件", - "shortBtnTxt": "新的配置文件", - "fromClipboard": "从剪贴板添加", - "scanQr": "扫二维码", - "manually": "手动输入", - "addingProfileMsg": "添加配置文件", - "failureMsg": "无法添加配置文件" - }, - "update": { - "buttonTxt": "更新", - "tooltip": "更新配置文件", - "failureMsg": "无法更新配置文件", - "successMsg": "配置文件已成功更新" - }, - "edit": { - "buttonTxt": "编辑", - "selectActiveTxt": "选择活动配置文件" - }, - "delete": { - "buttonTxt": "删除", - "confirmationMsg": "永久删除配置文件吗?", - "successMsg": "配置文件删除成功" - }, - "save": { - "buttonText": "节省", - "successMsg": "个人资料保存成功", - "failureMsg": "保存配置文件失败" - }, - "detailsForm": { - "nameLabel": "姓名", - "nameHint": "个人资料名称", - "urlLabel": "网址", - "urlHint": "完整配置网址", - "emptyNameMsg": "姓名为必填项", - "invalidUrlMsg": "无效的网址", - "lastUpdate": "最后更新", - "updateInterval": "自动更新", - "updateIntervalDialogTitle": "自动更新间隔(以小时为单位)" - } - }, - "proxies": { - "pageTitle": "代理", - "emptyProxiesMsg": "没有可用的代理", - "delayTestTooltip": "测试延迟", - "sortTooltip": "对代理进行排序", - "sortOptions": { - "unsorted": "默认", - "name": "按字母顺序", - "delay": "延迟" - } - }, - "logs": { - "pageTitle": "日志", - "clearLogsButtonText": "清除日志", - "filterHint": "筛选", - "allLevelsFilter": "全部", - "shareCoreLogs": "分享核心日志", - "shareAppLogs": "共享应用程序日志" - }, - "settings": { - "pageTitle": "设置", - "requiresRestartMsg": "要使其生效,请重新启动应用程序", - "general": { - "sectionTitle": "一般的", - "locale": "语言", - "region": "地区", - "regionMsg": "帮助设置默认选项以绕过国内地址", - "regions": { - "ir": "伊朗 (ir)", - "cn": "中国 (cn)", - "other": "其他" - }, - "themeMode": "主题模式", - "themeModes": { - "system": "遵循系统主题", - "dark": "深色模式", - "light": "灯光模式" - }, - "enableAnalytics": "启用分析", - "enableAnalyticsMsg": "授予收集分析并发送崩溃报告以改进应用程序的权限", - "trueBlack": "纯黑", - "autoStart": "开机启动", - "silentStart": "无声启动", - "openWorkingDir": "打开工作目录", - "ignoreBatteryOptimizations": "禁用电池优化", - "ignoreBatteryOptimizationsMsg": "消除限制以获得最佳 VPN 性能" - }, - "advanced": { - "sectionTitle": "先进的", - "debugMode": "调试模式", - "debugModeMsg": "重新启动应用程序以应用此更改" - }, - "network": { - "perAppProxyPageTitle": "每个应用程序代理", - "perAppProxyModes": { - "off": "全部", - "offMsg": "代理所有应用程序", - "include": "代理人", - "includeMsg": "仅代理选定的应用程序", - "exclude": "旁路", - "excludeMsg": "不代理选定的应用程序" - }, - "showSystemApps": "显示系统应用程序", - "hideSystemApps": "隐藏系统应用程序", - "clearSelection": "清空选项" - }, - "config": { - "section": { - "route": "路线选项", - "dns": "DNS 选项", - "inbound": "入境选项", - "misc": "其他选项" - }, - "pageTitle": "配置选项", - "executeConfigAsIs": "按原样执行配置", - "executeConfigAsIsMsg": "只需进行最少的更改即可执行 SingBox 配置。", - "logLevel": "日志级别", - "resolveDestination": "解决目的地", - "ipv6Mode": "IPv6路由", - "ipv6Modes": { - "disable": "禁用", - "enable": "使能够", - "prefer": "首选", - "only": "独家的" - }, - "remoteDnsAddress": "远程域名解析", - "remoteDnsDomainStrategy": "远程 DNS 域策略", - "directDnsAddress": "直接域名解析", - "directDnsDomainStrategy": "直接 DNS 域策略", - "mixedPort": "混合港口", - "localDnsPort": "本地 DNS 端口", - "tunImplementation": "TUN实施", - "mtu": "MTU", - "connectionTestUrl": "连接测试网址", - "urlTestInterval": "URL 测试间隔", - "enableClashApi": "启用Clash API", - "clashApiPort": "Clash API 端口", - "enableTun": "启用TUN", - "setSystemProxy": "设置系统代理" - } - }, - "about": { - "pageTitle": "关于", - "version": "版本", - "sourceCode": "源代码", - "telegramChannel": "电报频道", - "checkForUpdate": "检查更新", - "privacyPolicy": "隐私政策", - "termsAndConditions": "条款和条件" - }, - "appUpdate": { - "notAvailableMsg": "已经使用最新版本", - "dialogTitle": "可用更新", - "updateMsg": "@:general.appTitle 的新版本现已推出。您想现在更新吗?", - "currentVersionLbl": "当前版本", - "newVersionLbl": "新版本", - "updateNowBtnTxt": "现在更新", - "laterBtnTxt": "之后", - "ignoreBtnTxt": "忽略" - }, - "tray": { - "dashboard": "控制面板", - "quit": "退出", - "systemProxy": "系统代理" - }, - "failure": { - "unexpected": "意外错误", - "clash": { - "unexpected": "意外错误", - "core": "Clash错误${reason}" - }, - "singbox": { - "unexpected": "意外服务错误", - "serviceNotRunning": "服务未运行", - "invalidConfigOptions": "配置选项无效", - "invalidConfig": "无效配置", - "create": "服务创建错误", - "start": "服务启动错误" - }, - "connectivity": { - "unexpected": "意外失败", - "missingVpnPermission": "缺少 VPN 权限", - "missingNotificationPermission": "缺少通知权限", - "core": "核心错误" - }, - "profiles": { - "unexpected": "意外错误", - "notFound": "未找到配置文件", - "invalidConfig": "无效配置", - "invalidUrl": "网址无效" - }, - "connection": { - "unexpected": "意外连接错误", - "timeout": "连接超时", - "badResponse": "反应不佳", - "connectionError": "连接错误", - "badCertificate": "证书无效" - } - }, - "play": { - "title": "Hiddify Next(预览)", - "short_description": "自动,SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "HiddifyNext 的主要目标是提供安全、用户友好且高效的隧道客户端。它使您能够利用 VPN 服务权限将所有流量或选定的应用程序流量路由到您选择的远程服务器。\n\n注:我们不提供任何服务器;用户需要使用自己的自托管服务器或受信任的服务器来确保其在线活动的私密性。\n \n我们支持以下类型的服务器:\n- 普通V2ray/Xray订阅链接\n- Clash订阅链接\n- Sing-Box 订阅链接\n\n我们的独特特点是什么?\n\n-用户友好\n-优化和高速\n-自动选择最低延迟\n-显示用户使用信息\n-通过一键深度链接轻松导入子链接\n-免费且无广告\n-轻松切换用户子链接\n-等等\n\n支持:\n- Sing-Box 支持的所有协议\n- VLESS + xtls 现实、愿景\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n-V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\n源代码位于https://github.com/hiddify/Hiddify-Next\n应用程序核心基于开源的Sing-Box。\n\n权限说明:\n\nVPN服务:由于此应用程序的目标是提供安全、用户友好和高效的隧道客户端,我们需要此权限以能够通过隧道将流量路由到远程服务器。\n查询所有包:此权限用于允许用户包括或排除特定应用程序以进行隧道传输。\n接收启动完成:此权限可以从应用程序设置中启用或禁用,以在设备启动时激活此应用程序。\n发送通知:此权限是必需的,因为我们使用前台服务来确保VPN服务的持续运行。\n此应用程序没有广告。分析和崩溃数据仅在用户在首次使用应用程序时明确同意的情况下发生。" - } -} diff --git a/project.inlang.json b/project.inlang.json index 2f2322e1..8f748ec1 100644 --- a/project.inlang.json +++ b/project.inlang.json @@ -4,9 +4,8 @@ "languageTags": [ "en", "fa", - "ru-RU", - "fr-FR", - "zh-CN" + "ru", + "zh" ], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@4/dist/index.js", @@ -16,7 +15,7 @@ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js" ], "plugin.inlang.i18next": { - "pathPattern": "./assets/translations/{languageTag}.json", + "pathPattern": "./assets/translations/strings_{languageTag}.i18n.json", "variableReferencePattern": [ "@:" ] From a22a5c088f64208fdf0b1cd6811039e7ab191160 Mon Sep 17 00:00:00 2001 From: Hiddify Date: Wed, 18 Oct 2023 11:53:31 +0200 Subject: [PATCH 10/27] add inlang translations --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 64398647..c4dc0dfd 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ [![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-next/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-next/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-next/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-next/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5) + + +
+English Demo + +
## What is Hiddify-Next? @@ -19,11 +25,15 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.dev). For more information you can read through our [Contribution Guidelines](https://github.com/hiddify/hiddify-next/blob/main/CONTRIBUTING.md) for development. -
-English Demo +## Improve Translations +You can easily contribute to this project by using the following links to improve the translations: + - [English](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en) +- [Persian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=fa) +- [Russian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=ru) +- [Chinese](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=zh) + -
## 🚀 Main features From 3e051679ac63b57d15b64340476d4471462db465 Mon Sep 17 00:00:00 2001 From: leic4u <32786903+leic4u@users.noreply.github.com> Date: Fri, 20 Oct 2023 02:57:03 +0800 Subject: [PATCH 11/27] Update README_cn.md --- README_cn.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README_cn.md b/README_cn.md index 7583a9b6..1bf5f6d5 100644 --- a/README_cn.md +++ b/README_cn.md @@ -13,7 +13,7 @@ -## 什么是 Hiddify-Next? +## Hiddify-Next 是什么? 基于 [Sing-box](https://github.com/SagerNet/sing-box) 的多平台客户端,用作通用代理工具链。 该应用程序提供了广泛的功能,如下所列。 它还支持大量协议。 该应用程序免费使用、无广告且开源。 它提供了一个安全且私密的工具来访问免费互联网。 该应用程序是使用 [Flutter](https://flutter.dev/) 和 [Go](https://go.dev/) 开发的。 欲了解更多信息,您可以阅读我们的开发贡献指南。 @@ -31,9 +31,9 @@ 🔍 基于延迟的自动选择 -🟡 广泛的协议:ECH、Sing-box、V2ray、Xray、Vless、Vmess、Reality、TUIC、Hysteria、ShadowTLS、SSH、Clash、Clash meta +🟡 广泛的协议支持:ECH、Sing-box、V2ray、Xray、Vless、Vmess、Trojan、Trojan with websocket、Reality、TUIC、Hysteria、Hysteria2、ShadowTLS、SSH、Clash、Clash meta -🟡 订阅链接:Clash、Clash meta、Sing-box 和 Shadowsocks +🟡 支持多种订阅链接导入:Clash、Clash meta、Sing-box 和 Shadowsocks 🔄 自动订阅更新 @@ -45,9 +45,9 @@ 🌙 深色和浅色模式 -⚙ 与所有代理管理面板兼容 +⚙ 与所有代理管理面板的节点兼容 -⭐ 适合伊朗、中国、俄罗斯等国家配置 +⭐ 适用于伊朗、中国、俄罗斯等国家配置 📱 可在 Google Play 上获取 @@ -63,7 +63,7 @@ - 安卓 + Android


@@ -72,13 +72,13 @@ - 视窗 + Windows
- 苹果系统 + macOS @@ -95,16 +95,16 @@ ## 致谢 - [Sing-box](https://github.com/SagerNet/sing-box) -- [Sing-box 适用于安卓](https://github.com/SagerNet/sing-box-for-android) +- [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) -- [其他的](./pubspec.yaml) +- [其他](./pubspec.yaml) ## 捐赠与支持 -支持我们的最简单方法是点击本页顶部的星号 (⭐)。 +支持我们的最简单方法是单击此页面顶部的 Star (⭐)。 -我们的服务还需要财政支持。 我们所有的活动都是自愿进行的,财政支持将用于项目的开发。 您可以[此处](https://github.com/hiddify/hiddify-server/wiki/support)查看我们的支持地址 +我们的服务也需要资金支持。我们所有的活动都是自愿进行的,资金支持将用于项目的开发和维护。您可以在 [此处](https://github.com/hiddify/hiddify-manager/wiki/support) 查看我们的支持地址。
@@ -119,7 +119,7 @@

- 我们感谢所有参与该项目的人。 这里有一些人,还有 Github 之外的更多人。 这对我们来说意义重大。 ♥ + 感谢所有参与该项目的人。包括以下列出的人,和更多其他来自 Github 的人。你们对我们的意义非常重大。 ♥

@@ -128,7 +128,7 @@

- 制作与 Contrib.Rocks + 使用 Contrib.Rocks 制作

From cdb1f0ae95541641a8b4304ea970d7849235e6cb Mon Sep 17 00:00:00 2001 From: hiddify-com <114227601+hiddify-com@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:12:05 +0200 Subject: [PATCH 12/27] inlang: update translations --- assets/translations/strings_en.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index ddf1dbca..e548a8eb 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -46,7 +46,7 @@ "remainingDuration": "${duration} Days Remaining", "remainingTrafficSemanticLabel": "${consumed} of ${total} traffic consumed.", "expired": "Expired", - "noTraffic": "No more traffic" + "noTraffic": "Out of Quota" }, "sortBy": { "lastUpdate": "Recently updated", From 7a332023ed736436ee2a9fee6e032100beb0230f Mon Sep 17 00:00:00 2001 From: Hiddify <114227601+hiddify-com@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:24:20 +0200 Subject: [PATCH 13/27] Update LICENSE.md --- LICENSE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LICENSE.md b/LICENSE.md index fd7de532..cac21c22 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,6 +2,9 @@ # Attribution-NonCommercial-ShareAlike 4.0 International ## Summary: +- The forks of the app are not allowed to be listed on F-Droid or other app stores under the original name or original design. +- Any forks should be published open-source under the same license. +- You need prior consent to publish a fork or use any part of this code in an application published in Apple Store. - You are free to: - Share — copy and redistribute the material in any medium or format - Adapt — remix, transform, and build upon the material From e75f39a9afb149b1ce3357ad6e3342d043e5890e Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Fri, 20 Oct 2023 19:10:40 +0330 Subject: [PATCH 14/27] Add rules --- lib/data/repository/config_options_store.dart | 29 ++++++++++++++- lib/domain/singbox/config_options.dart | 8 +++-- lib/domain/singbox/rules.dart | 35 +++++++++++++++++++ .../widgets/advanced_setting_tiles.dart | 2 ++ lib/services/singbox/ffi_singbox_service.dart | 2 +- libcore | 2 +- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/lib/data/repository/config_options_store.dart b/lib/data/repository/config_options_store.dart index afc10987..e846d46c 100644 --- a/lib/data/repository/config_options_store.dart +++ b/lib/data/repository/config_options_store.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_manual_providers_as_generated_provider_dependency import 'package:hiddify/core/prefs/prefs.dart'; -import 'package:hiddify/domain/singbox/config_options.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/utils/pref_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -67,6 +67,32 @@ final enableTunStore = PrefNotifier.provider("enable-tun", _default.enableTun); final setSystemProxyStore = PrefNotifier.provider("set-system-proxy", _default.setSystemProxy); +// HACK temporary +@riverpod +List rules(RulesRef ref) => switch (ref.watch(regionNotifierProvider)) { + Region.ir => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.ir", + ip: "geoip:ir", + outbound: RuleOutbound.bypass, + ), + ], + Region.cn => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.cn,geosite:cn", + ip: "geoip:cn", + outbound: RuleOutbound.bypass, + ), + ], + _ => [], + }; + @riverpod ConfigOptions configOptions(ConfigOptionsRef ref) => ConfigOptions( executeConfigAsIs: @@ -88,4 +114,5 @@ ConfigOptions configOptions(ConfigOptionsRef ref) => ConfigOptions( clashApiPort: ref.watch(clashApiPortStore), enableTun: ref.watch(enableTunStore), setSystemProxy: ref.watch(setSystemProxyStore), + rules: ref.watch(rulesProvider), ); diff --git a/lib/domain/singbox/config_options.dart b/lib/domain/singbox/config_options.dart index 1890d8a9..bfae62f4 100644 --- a/lib/domain/singbox/config_options.dart +++ b/lib/domain/singbox/config_options.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/prefs/prefs.dart'; +import 'package:hiddify/domain/singbox/rules.dart'; import 'package:hiddify/utils/platform_utils.dart'; part 'config_options.freezed.dart'; @@ -19,13 +20,13 @@ class ConfigOptions with _$ConfigOptions { @Default(IPv6Mode.disable) IPv6Mode ipv6Mode, @Default("tcp://8.8.8.8") String remoteDnsAddress, @Default(DomainStrategy.auto) DomainStrategy remoteDnsDomainStrategy, - @Default("8.8.8.8") String directDnsAddress, + @Default("local") String directDnsAddress, @Default(DomainStrategy.auto) DomainStrategy directDnsDomainStrategy, @Default(2334) int mixedPort, @Default(6450) int localDnsPort, @Default(TunImplementation.mixed) TunImplementation tunImplementation, @Default(9000) int mtu, - @Default("https://www.gstatic.com/generate_204") String connectionTestUrl, + @Default("http://cp.cloudflare.com/") String connectionTestUrl, @IntervalConverter() @Default(Duration(minutes: 10)) Duration urlTestInterval, @@ -33,6 +34,9 @@ class ConfigOptions with _$ConfigOptions { @Default(6756) int clashApiPort, @Default(false) bool enableTun, @Default(true) bool setSystemProxy, + @Default(false) bool bypassLan, + @Default(false) bool enableFakeDns, + List? rules, }) = _ConfigOptions; static ConfigOptions initial = ConfigOptions( diff --git a/lib/domain/singbox/rules.dart b/lib/domain/singbox/rules.dart index f30f024f..80db6ee7 100644 --- a/lib/domain/singbox/rules.dart +++ b/lib/domain/singbox/rules.dart @@ -1,5 +1,40 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/prefs/locale_prefs.dart'; +part 'rules.freezed.dart'; +part 'rules.g.dart'; + +@freezed +class Rule with _$Rule { + @JsonSerializable(fieldRename: FieldRename.kebab) + const factory Rule({ + required String id, + required String name, + @Default(false) bool enabled, + String? domains, + String? ip, + String? port, + String? protocol, + @Default(RuleNetwork.tcpAndUdp) RuleNetwork network, + @Default(RuleOutbound.proxy) RuleOutbound outbound, + }) = _Rule; + + factory Rule.fromJson(Map json) => _$RuleFromJson(json); +} + +enum RuleOutbound { proxy, bypass, block } + +@JsonEnum(valueField: 'key') +enum RuleNetwork { + tcpAndUdp(""), + tcp("tcp"), + udp("udp"); + + const RuleNetwork(this.key); + + final String? key; +} + enum PerAppProxyMode { off, include, diff --git a/lib/features/settings/widgets/advanced_setting_tiles.dart b/lib/features/settings/widgets/advanced_setting_tiles.dart index 82b94bdb..64274c21 100644 --- a/lib/features/settings/widgets/advanced_setting_tiles.dart +++ b/lib/features/settings/widgets/advanced_setting_tiles.dart @@ -6,6 +6,7 @@ import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/core/router/routes/routes.dart'; import 'package:hiddify/domain/singbox/singbox.dart'; +import 'package:hiddify/features/common/common.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class AdvancedSettingTiles extends HookConsumerWidget { @@ -20,6 +21,7 @@ class AdvancedSettingTiles extends HookConsumerWidget { return Column( children: [ + const RegionPrefTile(), ListTile( title: Text(t.settings.config.pageTitle), leading: const Icon(Icons.edit_document), diff --git a/lib/services/singbox/ffi_singbox_service.dart b/lib/services/singbox/ffi_singbox_service.dart index e2f75d9c..47dc4e65 100644 --- a/lib/services/singbox/ffi_singbox_service.dart +++ b/lib/services/singbox/ffi_singbox_service.dart @@ -8,7 +8,7 @@ import 'package:combine/combine.dart'; import 'package:ffi/ffi.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/domain/connectivity/connectivity.dart'; -import 'package:hiddify/domain/singbox/config_options.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/gen/singbox_generated_bindings.dart'; import 'package:hiddify/services/singbox/shared.dart'; import 'package:hiddify/services/singbox/singbox_service.dart'; diff --git a/libcore b/libcore index d410fe1c..0480a5d3 160000 --- a/libcore +++ b/libcore @@ -1 +1 @@ -Subproject commit d410fe1c4b0d90f545716737d53e0002be9504cd +Subproject commit 0480a5d3ec0571b7a0662625679a277219c51d0f From f89c4c21c1f2b03419d4e1ee4198e96e3f64835f Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Fri, 20 Oct 2023 23:50:45 +0330 Subject: [PATCH 15/27] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4dc0dfd..5afee5ef 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,8 @@ You can easily contribute to this project by using the following links to improv Android

-
-
+
+
From 886b860e68daee8ecb32e1239bb231a45267525d Mon Sep 17 00:00:00 2001 From: solokot Date: Sat, 21 Oct 2023 14:20:07 +0300 Subject: [PATCH 16/27] Russian translation: fix some mistakes --- assets/translations/strings_ru.i18n.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index d798d217..77aa313d 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -30,8 +30,8 @@ "stats": { "traffic": "Скорость", "trafficTotal": "Трафик", - "uplink": "Входящий канал", - "downlink": "Исходящий канал" + "uplink": "Исходящий канал", + "downlink": "Входящий канал" } }, "profile": { @@ -198,7 +198,7 @@ "pageTitle": "О программе", "version": "Версия", "sourceCode": "Исходный код", - "telegramChannel": "Telegram канал", + "telegramChannel": "Telegram-канал", "checkForUpdate": "Проверка обновления", "privacyPolicy": "Политика конфиденциальности", "termsAndConditions": "Условия и положения" @@ -255,6 +255,6 @@ "play": { "title": "Hiddify Next (Preview)", "short_description": "Автовыбор, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n- ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." + "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n— ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." } } From a8e75dfb28517487dd634d4fe25f0a27adcb21a4 Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:23:38 +0330 Subject: [PATCH 17/27] Update README.md --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5afee5ef..e3ea3826 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,6 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.dev). For more information you can read through our [Contribution Guidelines](https://github.com/hiddify/hiddify-next/blob/main/CONTRIBUTING.md) for development. -## Improve Translations -You can easily contribute to this project by using the following links to improve the translations: - - [English](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en) -- [Persian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=fa) -- [Russian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=ru) -- [Chinese](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=zh) - - ## 🚀 Main features @@ -108,6 +100,15 @@ You can easily contribute to this project by using the following links to improv ## Installation and tutorials Please find tutorial information on the [wiki page](https://github.com/hiddify/hiddify-next/wiki). +## Improve Translations +You can easily contribute to this project by using the following links to improve the translations: + - [English](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en) +- [Persian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=fa) +- [Russian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=ru) +- [Chinese](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=zh) + + + ## Acknowledgements - [Sing-box](https://github.com/SagerNet/sing-box) From c9d75d73bc11a06c85dc7a45204a945e2a057973 Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:27:00 +0330 Subject: [PATCH 18/27] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e3ea3826..fca6f2a7 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,6 @@ -
-English Demo - - -
## What is Hiddify-Next? @@ -25,6 +20,11 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.dev). For more information you can read through our [Contribution Guidelines](https://github.com/hiddify/hiddify-next/blob/main/CONTRIBUTING.md) for development. +
+English Demo + + +
## 🚀 Main features From 1bfc8f0d688d5aafcac0b475780a179f40559d8d Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:49:50 +0330 Subject: [PATCH 19/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fca6f2a7..743713a3 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.de


- + From 7b23bb94eda2b82c03e13982168f351034a536ed Mon Sep 17 00:00:00 2001 From: lymanjre <125398461+lymanjre@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:53:05 +0330 Subject: [PATCH 20/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 743713a3..934a8192 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.de Linux - + From 3e054df31abebb45404295b6a168bb17af367b04 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Sat, 21 Oct 2023 20:11:53 +0330 Subject: [PATCH 21/27] Change theme prefs --- assets/translations/strings_en.i18n.json | 6 +-- assets/translations/strings_fa.i18n.json | 6 +-- assets/translations/strings_ru.i18n.json | 6 +-- assets/translations/strings_zh.i18n.json | 6 +-- lib/core/app/app_view.dart | 2 +- lib/core/core_providers.dart | 1 - lib/core/prefs/app_theme.dart | 30 +++++++++-- lib/core/prefs/theme_prefs.dart | 24 ++------- .../widgets/general_setting_tiles.dart | 44 ++++++++-------- .../widgets/theme_mode_switch_button.dart | 51 ------------------- 10 files changed, 67 insertions(+), 109 deletions(-) delete mode 100644 lib/features/settings/widgets/theme_mode_switch_button.dart diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index e548a8eb..1b6157d8 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -129,11 +129,11 @@ "themeModes": { "system": "Follow system theme", "dark": "Dark mode", - "light": "Light mode" + "light": "Light mode", + "black": "Black mode" }, "enableAnalytics": "Enable Analytics", "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", - "trueBlack": "Pure Black", "autoStart": "Start on Boot", "silentStart": "Silent Start", "openWorkingDir": "Open Working Directory", @@ -257,4 +257,4 @@ "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "The key goal of HiddifyNext is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe support servers with:\n- Normal V2ray/Xray Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - more and more\n\nSupport:\n- All Protocols supported by Sing-Box \n- VLESS + xtls reality, vision\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n- V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source sing-box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 9fe3206c..d52f50ba 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -129,11 +129,11 @@ "themeModes": { "system": "پیروی از تم دستگاه", "dark": "تم تیره", - "light": "تم روشن" + "light": "تم روشن", + "black": "تم سیاه" }, "enableAnalytics": "فعال‌سازی آنالیتیکز", "enableAnalyticsMsg": "ارائه دسترسی آنالیز و گزارش خطا برای بهبود عملکرد برنامه", - "trueBlack": "کاملا سیاه", "autoStart": "اجرا با روشن شدن سیستم", "silentStart": "اجرای ساکت", "openWorkingDir": "باز کردن دایرکتوری کاری", @@ -257,4 +257,4 @@ "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "هدف اصلی HiddifyNext ارائه یک کلاینت تونل زنی ایمن، کاربرپسند و کارآمد است. این به شما امکان می دهد تا با استفاده از مجوز VPN-Service، تمام ترافیک یا ترافیک برنامه انتخابی را به یک سرور راه دور مورد نظر خود هدایت کنید.\n\nتوجه: ما هیچ سروری ارائه نمی دهیم. کاربران موظفند با استفاده از سرورهای خود میزبان یا سرورهای مورد اعتماد، فعالیت‌های آنلاین خود را خصوصی نگه دارند.\n \nما از سرورهایی با موارد زیر پشتیبانی می کنیم:\n- لینک اشتراک V2ray/Xray معمولی\n- لینک اشتراک کلش\n- لینک اشتراک Sing-Box\n\nویژگی های منحصر به فرد ما چیست؟\n - کاربر پسند\n - بهینه و سریع\n - به طور خودکار LowestPing را انتخاب کنید\n - نمایش اطلاعات استفاده کاربر\n - به راحتی لینک فرعی را با یک کلیک با استفاده از دیپ لینک وارد کنید\n - رایگان و بدون تبلیغات\n - به راحتی پیوندهای فرعی کاربر را تغییر دهید\n - بیشتر و بیشتر\n\nحمایت کردن:\n- تمام پروتکل های پشتیبانی شده توسط Sing-Box\n- VLESS + xtls \n- VMESS\n- تروجان\n- ShoadowSocks\n- ریالیتی\n- V2ray\n- هیستریا 2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nکد منبع در https://github.com/hiddify/Hiddify-Next وجود دارد\nهسته برنامه بر اساس sing-box منبع باز است.\n\nتوضیحات مجوز:\n- سرویس VPN: از آنجا که هدف این برنامه ارائه یک کلاینت تونل زنی ایمن، کاربر پسند و کارآمد است، ما به این مجوز نیاز داریم تا بتوانیم ترافیک را از طریق تونل به سرور راه دور هدایت کنیم.\n- QUERY ALL PACKAGES: این مجوز برای اجازه دادن به کاربران برای گنجاندن یا حذف برنامه های کاربردی خاص برای تونل زدن استفاده می شود.\n- دریافت بوت تکمیل شد: این مجوز را می توان از تنظیمات برنامه فعال یا غیرفعال کرد تا این برنامه پس از بوت شدن دستگاه فعال شود.\n- اعلان های ارسالی: این مجوز ضروری است زیرا ما از یک سرویس پیش زمینه برای اطمینان از عملکرد مداوم سرویس VPN استفاده می کنیم.\n- این برنامه بدون تبلیغات است. تجزیه و تحلیل و داده های اشکال فقط با رضایت صریح کاربر در اولین استفاده از برنامه اتفاق می افتد." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 77aa313d..52782e8c 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -129,11 +129,11 @@ "themeModes": { "system": "Системная тема", "dark": "Тёмная тема", - "light": "Светлая тема" + "light": "Светлая тема", + "black": "Черный режим" }, "enableAnalytics": "Сбор аналитики", "enableAnalyticsMsg": "Сбор аналитических данных и отправка отчётов о сбоях для улучшения приложения.", - "trueBlack": "Чистый чёрный цвет", "autoStart": "Запуск при загрузке", "silentStart": "Тихий запуск", "openWorkingDir": "Открыть рабочую папку", @@ -257,4 +257,4 @@ "short_description": "Автовыбор, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n— ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_zh.i18n.json b/assets/translations/strings_zh.i18n.json index d7610172..95971a12 100644 --- a/assets/translations/strings_zh.i18n.json +++ b/assets/translations/strings_zh.i18n.json @@ -129,11 +129,11 @@ "themeModes": { "system": "遵循系统主题", "dark": "深色模式", - "light": "灯光模式" + "light": "灯光模式", + "black": "黑色模式" }, "enableAnalytics": "启用分析", "enableAnalyticsMsg": "授予收集分析并发送崩溃报告以改进应用程序的权限", - "trueBlack": "纯黑", "autoStart": "开机启动", "silentStart": "无声启动", "openWorkingDir": "打开工作目录", @@ -257,4 +257,4 @@ "short_description": "自动,SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "HiddifyNext 的主要目标是提供安全、用户友好且高效的隧道客户端。它使您能够利用 VPN 服务权限将所有流量或选定的应用程序流量路由到您选择的远程服务器。\n\n注:我们不提供任何服务器;用户需要使用自己的自托管服务器或受信任的服务器来确保其在线活动的私密性。\n \n我们支持以下类型的服务器:\n- 普通V2ray/Xray订阅链接\n- Clash订阅链接\n- Sing-Box 订阅链接\n\n我们的独特特点是什么?\n\n-用户友好\n-优化和高速\n-自动选择最低延迟\n-显示用户使用信息\n-通过一键深度链接轻松导入子链接\n-免费且无广告\n-轻松切换用户子链接\n-等等\n\n支持:\n- Sing-Box 支持的所有协议\n- VLESS + xtls 现实、愿景\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n-V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\n源代码位于https://github.com/hiddify/Hiddify-Next\n应用程序核心基于开源的Sing-Box。\n\n权限说明:\n\nVPN服务:由于此应用程序的目标是提供安全、用户友好和高效的隧道客户端,我们需要此权限以能够通过隧道将流量路由到远程服务器。\n查询所有包:此权限用于允许用户包括或排除特定应用程序以进行隧道传输。\n接收启动完成:此权限可以从应用程序设置中启用或禁用,以在设备启动时激活此应用程序。\n发送通知:此权限是必需的,因为我们使用前台服务来确保VPN服务的持续运行。\n此应用程序没有广告。分析和崩溃数据仅在用户在首次使用应用程序时明确同意的情况下发生。" } -} +} \ No newline at end of file diff --git a/lib/core/app/app_view.dart b/lib/core/app/app_view.dart index 69549dc6..35129467 100644 --- a/lib/core/app/app_view.dart +++ b/lib/core/app/app_view.dart @@ -33,7 +33,7 @@ class AppView extends HookConsumerWidget with PresLogger { supportedLocales: AppLocaleUtils.supportedLocales, localizationsDelegates: GlobalMaterialLocalizations.delegates, debugShowCheckedModeBanner: false, - themeMode: theme.mode, + themeMode: theme.mode.flutterThemeMode, theme: theme.light(), darkTheme: theme.dark(), title: Constants.appName, diff --git a/lib/core/core_providers.dart b/lib/core/core_providers.dart index 9cd598ff..35ce17e4 100644 --- a/lib/core/core_providers.dart +++ b/lib/core/core_providers.dart @@ -19,6 +19,5 @@ TranslationsEn translations(TranslationsRef ref) => @Riverpod(keepAlive: true) AppTheme theme(ThemeRef ref) => AppTheme( ref.watch(themeModeNotifierProvider), - ref.watch(trueBlackThemeNotifierProvider), ref.watch(localeNotifierProvider).preferredFontFamily, ); diff --git a/lib/core/prefs/app_theme.dart b/lib/core/prefs/app_theme.dart index 8e6c8601..786be4f5 100644 --- a/lib/core/prefs/app_theme.dart +++ b/lib/core/prefs/app_theme.dart @@ -1,16 +1,38 @@ import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; +import 'package:hiddify/core/prefs/locale_prefs.dart'; + +enum AppThemeMode { + system, + light, + dark, + black; + + String present(TranslationsEn t) => switch (this) { + system => t.settings.general.themeModes.system, + light => t.settings.general.themeModes.light, + dark => t.settings.general.themeModes.dark, + black => t.settings.general.themeModes.black, + }; + + ThemeMode get flutterThemeMode => switch (this) { + system => ThemeMode.system, + light => ThemeMode.light, + dark => ThemeMode.dark, + black => ThemeMode.dark, + }; + + bool get trueBlack => this == black; +} // mostly exact copy of flex color scheme 7.1's fabulous 12 theme class AppTheme { AppTheme( this.mode, - this.trueBlack, this.fontFamily, ); - final ThemeMode mode; - final bool trueBlack; + final AppThemeMode mode; final String fontFamily; ThemeData light() { @@ -81,7 +103,7 @@ class AppTheme { useMaterial3: true, swapLegacyOnMaterial3: true, useMaterial3ErrorColors: true, - darkIsTrueBlack: trueBlack, + darkIsTrueBlack: mode.trueBlack, surfaceMode: FlexSurfaceMode.highScaffoldLowSurface, // blendLevel: 1, subThemesData: const FlexSubThemesData( diff --git a/lib/core/prefs/theme_prefs.dart b/lib/core/prefs/theme_prefs.dart index 8a0eca45..a9f21b3b 100644 --- a/lib/core/prefs/theme_prefs.dart +++ b/lib/core/prefs/theme_prefs.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:hiddify/core/prefs/app_theme.dart'; import 'package:hiddify/data/data_providers.dart'; import 'package:hiddify/utils/pref_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -10,29 +10,15 @@ class ThemeModeNotifier extends _$ThemeModeNotifier { late final _pref = Pref( ref.watch(sharedPreferencesProvider), "theme_mode", - ThemeMode.system, - mapFrom: ThemeMode.values.byName, + AppThemeMode.system, + mapFrom: AppThemeMode.values.byName, mapTo: (value) => value.name, ); @override - ThemeMode build() => _pref.getValue(); + AppThemeMode build() => _pref.getValue(); - Future update(ThemeMode value) { - state = value; - return _pref.update(value); - } -} - -@Riverpod(keepAlive: true) -class TrueBlackThemeNotifier extends _$TrueBlackThemeNotifier { - late final _pref = - Pref(ref.watch(sharedPreferencesProvider), "true_black_theme", false); - - @override - bool build() => _pref.getValue(); - - Future update(bool value) { + Future update(AppThemeMode value) { state = value; return _pref.update(value); } diff --git a/lib/features/settings/widgets/general_setting_tiles.dart b/lib/features/settings/widgets/general_setting_tiles.dart index f8b76249..a66cb81e 100644 --- a/lib/features/settings/widgets/general_setting_tiles.dart +++ b/lib/features/settings/widgets/general_setting_tiles.dart @@ -3,7 +3,6 @@ import 'package:go_router/go_router.dart'; import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/features/common/common.dart'; -import 'package:hiddify/features/settings/widgets/theme_mode_switch_button.dart'; import 'package:hiddify/services/auto_start_service.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -44,31 +43,34 @@ class GeneralSettingTiles extends HookConsumerWidget { ), ListTile( title: Text(t.settings.general.themeMode), - subtitle: Text( - switch (theme.mode) { - ThemeMode.system => t.settings.general.themeModes.system, - ThemeMode.light => t.settings.general.themeModes.light, - ThemeMode.dark => t.settings.general.themeModes.dark, - }, - ), - trailing: ThemeModeSwitch( - themeMode: theme.mode, - onChanged: ref.read(themeModeNotifierProvider.notifier).update, - ), + subtitle: Text(theme.mode.present(t)), leading: const Icon(Icons.light_mode), onTap: () async { - await ref.read(themeModeNotifierProvider.notifier).update( - Theme.of(context).brightness == Brightness.light - ? ThemeMode.dark - : ThemeMode.light, + final selectedThemeMode = await showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: Text(t.settings.general.themeMode), + children: AppThemeMode.values + .map( + (e) => RadioListTile( + title: Text(e.present(t)), + value: e, + groupValue: theme.mode, + onChanged: (e) => context.pop(e), + ), + ) + .toList(), ); + }, + ); + if (selectedThemeMode != null) { + await ref + .read(themeModeNotifierProvider.notifier) + .update(selectedThemeMode); + } }, ), - SwitchListTile( - title: Text(t.settings.general.trueBlack), - value: theme.trueBlack, - onChanged: ref.read(trueBlackThemeNotifierProvider.notifier).update, - ), if (PlatformUtils.isDesktop) ...[ SwitchListTile( title: Text(t.settings.general.autoStart), diff --git a/lib/features/settings/widgets/theme_mode_switch_button.dart b/lib/features/settings/widgets/theme_mode_switch_button.dart deleted file mode 100644 index 580a633b..00000000 --- a/lib/features/settings/widgets/theme_mode_switch_button.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hiddify/core/core_providers.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -class ThemeModeSwitch extends HookConsumerWidget { - const ThemeModeSwitch({ - super.key, - required this.themeMode, - required this.onChanged, - }); - final ThemeMode themeMode; - final ValueChanged onChanged; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final t = ref.watch(translationsProvider); - - final List isSelected = [ - themeMode == ThemeMode.light, - themeMode == ThemeMode.system, - themeMode == ThemeMode.dark, - ]; - - return ToggleButtons( - isSelected: isSelected, - onPressed: (int newIndex) { - if (newIndex == 0) { - onChanged(ThemeMode.light); - } else if (newIndex == 1) { - onChanged(ThemeMode.system); - } else { - onChanged(ThemeMode.dark); - } - }, - children: [ - Icon( - Icons.wb_sunny, - semanticLabel: t.settings.general.themeModes.light, - ), - Icon( - Icons.phone_iphone, - semanticLabel: t.settings.general.themeModes.system, - ), - Icon( - Icons.bedtime, - semanticLabel: t.settings.general.themeModes.dark, - ), - ], - ); - } -} From 87cf7348259b1bb6e3fa54224eee3dbe05f5de01 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Sat, 21 Oct 2023 20:28:37 +0330 Subject: [PATCH 22/27] Add russia region --- assets/translations/strings_en.i18n.json | 1 + assets/translations/strings_fa.i18n.json | 1 + assets/translations/strings_ru.i18n.json | 1 + assets/translations/strings_zh.i18n.json | 1 + lib/data/repository/config_options_store.dart | 10 ++++++++++ lib/domain/singbox/rules.dart | 2 ++ 6 files changed, 16 insertions(+) diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index 1b6157d8..186966de 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -123,6 +123,7 @@ "regions": { "ir": "Iran (ir)", "cn": "China (cn)", + "ru": "Russia (ru)", "other": "Other" }, "themeMode": "Theme Mode", diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index d52f50ba..520be7ad 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -123,6 +123,7 @@ "regions": { "ir": "ایران (ir)", "cn": "چین (cn)", + "ru": "روسیه (ru)", "other": "سایر" }, "themeMode": "تم مود", diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 52782e8c..7bcda9c2 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -123,6 +123,7 @@ "regions": { "ir": "Иран (ir)", "cn": "Китай (cn)", + "ru": "Россия (ru)", "other": "Другой" }, "themeMode": "Оформление", diff --git a/assets/translations/strings_zh.i18n.json b/assets/translations/strings_zh.i18n.json index 95971a12..168f9da1 100644 --- a/assets/translations/strings_zh.i18n.json +++ b/assets/translations/strings_zh.i18n.json @@ -123,6 +123,7 @@ "regions": { "ir": "伊朗 (ir)", "cn": "中国 (cn)", + "ru": "俄罗斯 (ru)", "other": "其他" }, "themeMode": "主题模式", diff --git a/lib/data/repository/config_options_store.dart b/lib/data/repository/config_options_store.dart index e846d46c..fb7b9432 100644 --- a/lib/data/repository/config_options_store.dart +++ b/lib/data/repository/config_options_store.dart @@ -90,6 +90,16 @@ List rules(RulesRef ref) => switch (ref.watch(regionNotifierProvider)) { outbound: RuleOutbound.bypass, ), ], + Region.ru => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.ru", + ip: "geoip:ru", + outbound: RuleOutbound.bypass, + ), + ], _ => [], }; diff --git a/lib/domain/singbox/rules.dart b/lib/domain/singbox/rules.dart index 80db6ee7..96c53c9e 100644 --- a/lib/domain/singbox/rules.dart +++ b/lib/domain/singbox/rules.dart @@ -61,11 +61,13 @@ enum PerAppProxyMode { enum Region { ir, cn, + ru, other; String present(TranslationsEn t) => switch (this) { ir => t.settings.general.regions.ir, cn => t.settings.general.regions.cn, + ru => t.settings.general.regions.ru, other => t.settings.general.regions.other, }; } From 88c7f8d2c7efd8ee413d6a294e29dd5ee1473a94 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Sat, 21 Oct 2023 21:49:26 +0330 Subject: [PATCH 23/27] Change changelog workflow --- CHANGELOG.md | 22 + Makefile | 3 +- changelog.md | 1265 -------------------------------------------------- pubspec.yaml | 5 + 4 files changed, 28 insertions(+), 1267 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 changelog.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..fab9bd51 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Added + +- Basic region based routing rules +- Russian region + +### Changed + +- Theme preferences + +### Fixed + +- Localization mistakes in Russian from [solokot](https://github.com/solokot) +- Localization mistakes in Russian from [Elshad Guseynov](https://github.com/lifeindarkside) diff --git a/Makefile b/Makefile index 445cc656..29f3f1f0 100644 --- a/Makefile +++ b/Makefile @@ -116,9 +116,8 @@ release: # Create a new tag for release. echo "version: $${VERSION_STR}+$${BUILD_NUMBER}" && \ sed -i "s/version: .*/version: $${VERSION_STR}\+$${BUILD_NUMBER}/g" pubspec.yaml && \ git tag $${TAG} > /dev/null && \ - gitchangelog > changelog.md || { git tag -d $${TAG}; echo "Please run pip install git gitchangelog pystache mustache markdown"; exit 2; } && \ git tag -d $${TAG} > /dev/null && \ - git add pubspec.yaml changelog.md && \ + git add pubspec.yaml CHANGELOG.md && \ make sync_translate && \ git add assets/translations/* && \ git commit -m "release: version $${TAG}" && \ diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 1ead2d96..00000000 --- a/changelog.md +++ /dev/null @@ -1,1265 +0,0 @@ -# Changelog - - -## 0.9.2 (2023-10-15) - -#### Other - -* Fix ndk setup. - -* Fix android arm bug. - - - -## v0.9.1 (2023-10-15) - -#### Other - -* Update README_cn.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Delete docs/file. - -* Google play badge. - -* Delete docs/google-play-badge1.png. - -* Upload google play badge. - -* Create file. - -* Delete docs/google-play-badge.png. - -* Fix ci. - -* Change ndk setup. - -* Update dependencies. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_ru.md. - -* Update README_ru.md. - -* Change desktop error handling. - -* Fix android bugs. - -* Update README.md. - -* Update README_cn.md. - -* Update README_ru.md. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Delete Russian_Flag.png. - -* Add Russian flag. - -* Delete docs/file. - -* Create README_ru.md. - -* Delete REAMME_ru.md. - -* Create REAMME_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_cn.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update file. - -* Uplaod a badge for google play. - -* Delete google-play-badge.png. - -* Add files via upload. - -* Create file. - -* Delete docs/google-play-badge.png. - -* Delete google-play-badge.png. - -* Add files via upload. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Create Chinese README_cn.md. - - - -## v0.8.12 (2023-10-13) - -#### Fix - -* Typo. - -* Bug. - -* Release names. - -#### Other - -* Update readme. - -* Update core. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Merge pull request #56 from solokot/main. - _Improvement of Russian translation_ - -* Improvement of Russian translation. - _Basically a replacement for machine automatic translation_ - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Fix bugs. - -* Change android signing. - -* Update README.md. - -* Update readme. - -* Update contribution guide. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update release_message.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update release template. - - - -## v0.8.11 (2023-10-08) - -#### Changes - -* Remove auto release message. - - - -## v0.8.10 (2023-10-08) - -#### Fix - -* Release changelog. - - - -## v0.8.9 (2023-10-08) - -#### Fix - -* Missing libs. - - - -## v0.8.8 (2023-10-08) - -#### Fix - -* Release bug. - - - -## v0.8.7 (2023-10-08) - -#### Fix - -* Release message. - - - -## v0.8.6 (2023-10-08) - -#### Fix - -* Windows build. - -* Build issue. - -#### Other - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Update release_message.md. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Update release_message.md. - -* Update README.md. - -* Update README_fa.md. - -* Delete docs/note. - -* Add files via upload. - -* Create note. - -* Update contribute.md. - -* Update README.md. - -* Delete assets/images/google-play-badge.png. - -* Update README_fa.md. - -* Update README.md. - -* Update release_message.md. - -* Update release_message.md. - -* Update release_message.md. - -* Fix build. - - - -## v0.8.5 (2023-10-07) - -#### Fix - -* Bug. - - - -## v0.8.4 (2023-10-07) - -#### Fix - -* Translate. - - - -## v0.8.3 (2023-10-07) - -#### Other - -* Add release message and help. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Fix bugs. - -* Update release_message.md. - -* Update release_message.md. - -* Update README_fa.md. - -* Update README.md. - -* Update release_message.md. - -* Update release_message.md. - -* Create release_message.md. - -* Add google play badge to assets. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Create contribute.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Add feature request template. - -* Fix issue template. - -* Add issue template. - - - -## v0.8.2 (2023-10-07) - -#### Fix - -* Hysteria2 and some links. - -#### Other - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Create README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - _Add some features to the readme_ - -* Add debug export to clipboard. - - - -## v0.8.1 (2023-10-06) - -#### New - -* Add chinese lang. - -#### Fix - -* Chinese translation. - -#### Other - -* Update core 0.5.1. - -* Fix floating number sub info header. - -* Add russian. - -* Add google play descriptions. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - - - -## v0.8.0 (2023-10-05) - -#### New - -* Add russian lang. - -#### Other - -* Add proxy tag sanitization. - -* Fix bugs. - -* Add ignore app update version. - -* Add new protocols to link parser. - -* Auto update translations on release. - -* Update translation. - -* Remove param in ru translation. - - - -## v0.7.2 (2023-10-04) - -#### Other - -* Fix bugs. - - - -## v0.7.1 (2023-10-03) - -#### Other - -* Fix log and analytics bugs. - - - -## v0.7.0 (2023-10-03) - -#### New - -* Add support of some exception panel with zero usage. - -#### Other - -* Fix translation bug. - -* Improve error handling and presentation. - -* Add retry for network ops. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Add local profile. - -* Add auto translator. - -* Add auto translate. - - - -## v0.6.0 (2023-09-30) - -#### Other - -* Fix minor bugs. - -* Add scheduled profile update. - -* Update dependencies. - -* Refactor profile details page. - - - -## v0.5.11 (2023-09-22) - -#### Other - -* Fix android bundle abi. - -* Bump core version. - - - -## v0.5.10 (2023-09-22) - -#### Other - -* Fix minor bugs. - -* Fix profile update bug. - - - -## v0.5.9 (2023-09-22) - -#### Other - -* Add build number. - - - -## v0.5.8 (2023-09-22) - -#### New - -* Add crashlytics. - -* Add support for base64 sublink for header from content. - -* Add profile headers from comments in response! good for hosting in github and show information. - -* Automated version release. - -* Send release versions only to play market add pre-release version. - -#### Changes - -* Change invalid dns 235.5.5.5 to 8.8.8.8. - -#### Fix - -* Improve routing accessibility and logs. - -* Minor bugs. - -* Prefs persistence. - -* Crashlytics. - -* App update url. - -* Small profiles. - -* Makefile vars. - -* Adaptive icon. - -* Pre-release. - -* Typo in adaptive icon. - -* If .dev is exist in the version do not show update needed. - -* Keep the link as it is. fix the issue with & - -* Dependency issue. - -* Remove extra print. - -* Bug in get headers from body. - -* Bug ini ci to google play. - -* Tag version issue. - -* Ci bug. - -* Remove comments. - -* Bug. - -#### Other - -* Fix ci. - -* Fix false-positive error reports. - -* Change build setup. - -* Fix minor bugs. - -* Refactor app update. - -* Fix sentry dart plugin upload. - -* Fix ci debug symbols upload. - -* Add sentry provider observer. - -* Ci: add sentry debug info upload. - -* Update dependencies and general fixes. - -* Chore: bump agp version. - -* Ci: fix env. - -* Ci: add dsn env. - -* Feat: add region and terms to intro. - -* Update ci.yml. - -* Build: add sentry dsn. - -* Feat: add intro screen. - -* Feat: add sentry. - -* Ci: bump macos version. - -* Feat: update profile when adding preexisting url. - -* Publish draft even with error. - -* Update version of core. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Add firebase. - -* Update translation. - -* Refactor: version presentation. - -* Perf: improve header parser. - -* Feat: remove check for updates in market releases. - -* Better manage the market release. - -* Update. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Improve accessability. - -* Fix per-app proxy selection. - -* Add android per-app proxy. - -* Add basic flavors. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update README.md. - -* Add accessability semantics. - -* Add support for fragment in the url. - - - -## v0.1.0 (2023-09-11) - -#### New - -* Add signing to android. - -* Add android universal build also. - -* Add change in network entitlements. - -* Change logo icon to next. - -* Add cache for speed up build process. - -* Add windows portable version. - -* Add libclash. - -* Add as draft release. - -* Make better ci and building applications. - -#### Changes - -* Change windows logo. - -* Change macos build to flutter_distributor. - -* Remove x86 builds since flutter does not support. - -* Add x64 to the name. - -* Update makefile. - -#### Fix - -* Space bugs in some panels. - -* Aab. - -* Aab build. - -* Universal sign. - -* KeystoreProperties. - -* Name issue. - -* Revert package name change. - -* Name. - -* Geosite download. - -* Windows portable bug. - -* Change name bug. - -* Setup exe files. - -* Libclash.so. - -* Linux AppImage. - -* Error. - -* Category in linux. - -* Add fuse for linux. - -* Icon. - -* Linux logo. - -* Android. - -* Makefile error. - -#### Other - -* Release v0.1.0. - -* Fix ci build. - -* Update ci. - -* Add core version. - -* Fix barrel file. - -* Change proxies lifecycle. - -* Add service restart. - -* Remove notification service. - -* Change android notification permission. - -* Add android service restart. - -* Change mark new profile active. - -* Handle unlimited. - -* Update upload download link stats. - -* Update Translations. - -* Remove // TODO add content disposition parsing. - -* Add content-disposition for profile title. - -* Update README.md. - -* Add hysteria2. - -* Fix android build connection. - -* Add android connection shortcut. - -* Add desktop autostart. - -* Add android boot receiver. - -* Add android proxy service. - -* Add android battery optimizations settings. - -* Change sharedpreferences to unify with android. - -* Add vclibs. - -* Update dependencies. - -* Add submodule. - -* Fix translation code gen. - -* Change prefs. - -* Change default config options. - -* Remove string casing. - -* Update ci.yml. - -* Remove caching. - -* Change core prefs to use code generation. - -* Fix custom lint. - -* Fix general issues. - -* Remove vclibs. - -* Update README.md. - -* Fix blank screen. - -* Add proxies sort. - -* Refactor preferences. - -* Update dependencies. - -* Add more config options to settings. - -* Add tun implementation option. - -* Add android power manager. - -* Add android quick tile. - -* Fix macos dependencies. - -* Fix mac build. - -* Fix build. - -* Change system tray icon. - -* Fix riverpod code generation. - -* Remove unnecessary config options. - -* Add accessability semantics. - -* Update dependencies. - -* Add config options. - -* Add pref utilities. - -* Add stats overview. - -* Update Translations. - -* Fix android outbounds view. - -* Remove unnecessary options. - -* Change proxies flow. - -* Add android command client support. - -* Update README.md. - -* Add status command receiver. - -* Add hiddify deeplink. - -* Add macos deeplink support. - -* Add singbox deeplink. - -* Change error prompts. - -* Fix url parser. - -* Remove unnecessary prefs. - -* Update dependencies. - -* Make AppImage zipped for preserving permission in linux. - -* Add user agent. - -* Fix uri launch. - -* Update ci flutter version. - -* Fix macos silent start. - -* Change proxies page. - -* Fix desktop connection error msg. - -* Add button tooltips. - -* Fix logging. - -* Create dependabot.yml. - -* Create CODE_OF_CONDUCT.md. - -* Fix aab. - -* Add aab file. - -* Change windows-portable name to HiddifyNext-portable. - -* Add debug mode. - -* Add misc settings ui. - -* Remote unnecessary logs. - -* Add misc preferences. - -* Add directory options. - -* Fix linux build. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Fix android builds. - -* Fix android splash screen. - -* Update icons. - -* Fix windows executable build. - -* Remove unnecessary build steps. - -* Remove build number from appbar. - -* Update readme. - -* Change desktop directories. - -* Remove desktop notifications. - -* Force same version code for all platforms. - -* Add more logs. - -* Add log timestamp. - -* Update icons. - -* Remove drawer branding. - -* Add download section in readme. - -* Fix name of universal apk. - -* Fix order. - -* Update readme. - -* Update logging. - -* Update geo assets url. - -* Change linux directories. - -* Update icon. - -* Update icon. - -* Update logo for all platforms. - -* Revert name in macos. - -* Temporary disable app sandbox. - -* Change name. - -* Update Release.entitlements to fix binding issue. - -* Merge pull request #5 from evstegneych/main. - _add macos option_ - -* Add macos option. - -* Update ci flutter version. - -* Update kotlin version. - -* Update other dependencies. - -* Update dependencies. - -* Update flutter version. - -* Update LICENSE.md. - -* Add build option for ios but not tested. - -* Return: build for all. - -* Fix build for macos. - -* Fix ci. - -* Migrate to singbox. - -* Fix routing. - -* Fix connection button text casing. - -* Add update checking. - -* Add separate page for clash overrides. - -* Add version number to appbar. - -* Add more icons. - -* Add sort limited profiles last. - -* Fix profile sort icon. - -* Fix profile traffic ratio. - -* Add profiles sort option. - -* Refactor profile addition flow. - -* Add extra profile metadata. - -* Fix linux deep link service. - -* Refactor profile tile. - -* Add locale based font. - -* Fix linux startup. - -* Update dependencies. - -* Add about page. - -* Fix linux notifications. - -* Build all. - -* Hard coding. - -* Add png. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update Makefile. - -* Fix makefile. - -* Fix actions. - -* Fix actions. - -* Fix actions. - -* Update build setup. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Create LICENSE.md. - -* Add core submodule. - -* Remove core libs. - -* Update Makefile. - -* Update Makefile. - -* Remove c install. - -* Update make. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Add Persian font. - -* Update dependencies. - -* Update to libclash. - -* Add cache. - -* Add: new action. - -* Add write permission. - -* Fix. - -* Modify. - -* Fix. - -* Update. - -* Fix android build. - -* Fix code gen bug. - -* Update ci.yml. - -* Update ci.yml. - -* Fix CI. - -* Fix CI. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Update ci.yml. - -* Update ci.yml. - -* Fix CI. - -* Update ci.yml. - -* Update ci.yml. - -* Add CI. - -* Add silent start for desktop. - -* Update readme. - -* Add headless mode to desktop. - -* Update dependencies. - -* Add distribution setup for windows. - -* Add Farsi(fa) language. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Initial commit. - -* Initial. - - - diff --git a/pubspec.yaml b/pubspec.yaml index 5016d264..e460c9b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -170,3 +170,8 @@ sentry: upload_sources: true log_level: info ignore_missing: true + +cider: + link_template: + tag: https://github.com/hiddify/hiddify-next/releases/tag/%tag% + diff: https://github.com/hiddify/hiddify-next/compare/%from%...%to% From 49901695254666e2e2460597586d2da6b71fb8be Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Mon, 23 Oct 2023 19:39:29 +0330 Subject: [PATCH 24/27] Change logging --- CHANGELOG.md | 8 +- .../kotlin/com/hiddify/hiddify/LogHandler.kt | 9 +- .../com/hiddify/hiddify/MainActivity.kt | 28 +-- .../com/hiddify/hiddify/MethodHandler.kt | 60 +++-- assets/translations/strings_en.i18n.json | 6 +- assets/translations/strings_fa.i18n.json | 6 +- assets/translations/strings_ru.i18n.json | 6 +- assets/translations/strings_zh.i18n.json | 6 +- lib/data/repository/core_facade_impl.dart | 19 +- lib/domain/singbox/box_log.dart | 62 +++++ lib/domain/singbox/config_options.dart | 8 +- lib/domain/singbox/singbox.dart | 1 + lib/domain/singbox/singbox_facade.dart | 4 +- lib/features/common/common_controllers.dart | 5 - lib/features/logs/notifier/logs_notifier.dart | 149 +++++++---- lib/features/logs/notifier/logs_state.dart | 6 +- lib/features/logs/view/logs_page.dart | 233 ++++++++++-------- .../settings/view/config_options_page.dart | 6 +- lib/services/singbox/ffi_singbox_service.dart | 59 +++-- .../singbox/mobile_singbox_service.dart | 17 +- lib/services/singbox/singbox_service.dart | 4 +- libcore | 2 +- pubspec.lock | 2 +- pubspec.yaml | 19 +- 24 files changed, 450 insertions(+), 275 deletions(-) create mode 100644 lib/domain/singbox/box_log.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index fab9bd51..8d56e98b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,20 @@ # Changelog - All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased - ### Added - - Basic region based routing rules - Russian region +- Logs flow control ### Changed - - Theme preferences +- Logs page ### Fixed - - Localization mistakes in Russian from [solokot](https://github.com/solokot) - Localization mistakes in Russian from [Elshad Guseynov](https://github.com/lifeindarkside) +- Logs filtering diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt index d1ae2e46..3fc91860 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt @@ -1,5 +1,6 @@ package com.hiddify.hiddify +import android.util.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel @@ -18,13 +19,15 @@ class LogHandler : FlutterPlugin { logsChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { - MainActivity.instance.serviceLogs.observeForever { - if (it == null) return@observeForever - events?.success(it) + val activity = MainActivity.instance + events?.success(activity.logList) + activity.logCallback = { + events?.success(activity.logList) } } override fun onCancel(arguments: Any?) { + MainActivity.instance.logCallback = null } }) } diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt index f2253b36..f605144c 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt @@ -39,7 +39,6 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback { var logCallback: ((Boolean) -> Unit)? = null val serviceStatus = MutableLiveData(Status.Stopped) val serviceAlerts = MutableLiveData(null) - val serviceLogs = MutableLiveData(null) override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) @@ -102,37 +101,18 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback { serviceAlerts.postValue(ServiceEvent(Status.Stopped, type, message)) } - private var paused = false - override fun onPause() { - super.onPause() - - paused = true - } - - override fun onResume() { - super.onResume() - - paused = false - logCallback?.invoke(true) - } - override fun onServiceWriteLog(message: String?) { - if (paused) { - if (logList.size > 300) { - logList.removeFirst() - } + if (logList.size > 300) { + logList.removeFirst() } logList.addLast(message) - if (!paused) { - logCallback?.invoke(false) - serviceLogs.postValue(message) - } + logCallback?.invoke(false) } override fun onServiceResetLogs(messages: MutableList) { logList.clear() logList.addAll(messages) - if (!paused) logCallback?.invoke(true) + logCallback?.invoke(true) } override fun onDestroy() { diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt index 944c52f7..36e01b2a 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt @@ -29,6 +29,7 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, Restart("restart"), SelectOutbound("select_outbound"), UrlTest("url_test"), + ClearLogs("clear_logs"), } } @@ -63,38 +64,44 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, } Trigger.ChangeConfigOptions.method -> { - result.runCatching { - val args = call.arguments as String - Settings.configOptions = args - success(true) + scope.launch { + result.runCatching { + val args = call.arguments as String + Settings.configOptions = args + success(true) + } } } Trigger.Start.method -> { - result.runCatching { - val args = call.arguments as Map<*, *> - Settings.activeConfigPath = args["path"] as String? ?: "" - val mainActivity = MainActivity.instance - val started = mainActivity.serviceStatus.value == Status.Started - if (started) { - Log.w(TAG, "service is already running") - return success(true) + scope.launch { + result.runCatching { + val args = call.arguments as Map<*, *> + Settings.activeConfigPath = args["path"] as String? ?: "" + val mainActivity = MainActivity.instance + val started = mainActivity.serviceStatus.value == Status.Started + if (started) { + Log.w(TAG, "service is already running") + return@launch success(true) + } + mainActivity.startService() + success(true) } - mainActivity.startService() - success(true) } } Trigger.Stop.method -> { - result.runCatching { - val mainActivity = MainActivity.instance - val started = mainActivity.serviceStatus.value == Status.Started - if (!started) { - Log.w(TAG, "service is not running") - return success(true) + scope.launch { + result.runCatching { + val mainActivity = MainActivity.instance + val started = mainActivity.serviceStatus.value == Status.Started + if (!started) { + Log.w(TAG, "service is not running") + return@launch success(true) + } + BoxService.stop() + success(true) } - BoxService.stop() - success(true) } } @@ -151,6 +158,15 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, } } + Trigger.ClearLogs.method -> { + scope.launch { + result.runCatching { + MainActivity.instance.onServiceResetLogs(mutableListOf()) + success(true) + } + } + } + else -> result.notImplemented() } } diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index 186966de..8a5e9056 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "Logs", - "clearLogsButtonText": "Clear Logs", "filterHint": "Filter", "allLevelsFilter": "All", "shareCoreLogs": "Share Core Logs", - "shareAppLogs": "Share App logs" + "shareAppLogs": "Share App logs", + "pauseTooltip": "Pause", + "resumeTooltip": "Resume", + "clearTooltip": "Clear" }, "settings": { "pageTitle": "Settings", diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 520be7ad..cebc0b0a 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "لاگ‌ها", - "clearLogsButtonText": "پاک‌سازی", "filterHint": "فیلتر", "allLevelsFilter": "همه", "shareCoreLogs": "اشتراک‌گذاری لاگ هسته", - "shareAppLogs": "اشتراک‌گذاری لاگ برنامه" + "shareAppLogs": "اشتراک‌گذاری لاگ برنامه", + "pauseTooltip": "مکث", + "resumeTooltip": "از سرگیری", + "clearTooltip": "پاک‌سازی" }, "settings": { "pageTitle": "تنظیمات", diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 7bcda9c2..12259231 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "Журналы", - "clearLogsButtonText": "Очистить журналы", "filterHint": "Фильтр", "allLevelsFilter": "Все", "shareCoreLogs": "Поделиться журналами ядра", - "shareAppLogs": "Поделиться журналами приложения" + "shareAppLogs": "Поделиться журналами приложения", + "pauseTooltip": "Пауза", + "resumeTooltip": "Резюме", + "clearTooltip": "Прозрачный" }, "settings": { "pageTitle": "Настройки", diff --git a/assets/translations/strings_zh.i18n.json b/assets/translations/strings_zh.i18n.json index 168f9da1..a1ec9bd4 100644 --- a/assets/translations/strings_zh.i18n.json +++ b/assets/translations/strings_zh.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "日志", - "clearLogsButtonText": "清除日志", "filterHint": "筛选", "allLevelsFilter": "全部", "shareCoreLogs": "分享核心日志", - "shareAppLogs": "分享日志" + "shareAppLogs": "分享日志", + "pauseTooltip": "暂停", + "resumeTooltip": "恢复", + "clearTooltip": "清除" }, "settings": { "pageTitle": "设置", diff --git a/lib/data/repository/core_facade_impl.dart b/lib/data/repository/core_facade_impl.dart index b344809f..fa89af87 100644 --- a/lib/data/repository/core_facade_impl.dart +++ b/lib/data/repository/core_facade_impl.dart @@ -179,10 +179,21 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { } @override - Stream> watchLogs() { - return singbox - .watchLogs(filesEditor.coreLogsPath) - .handleExceptions(CoreServiceFailure.unexpected); + Stream>> watchLogs() { + return singbox.watchLogs(filesEditor.coreLogsPath).handleExceptions( + (error, stackTrace) { + loggy.warning("error watching logs", error, stackTrace); + return CoreServiceFailure.unexpected(error, stackTrace); + }, + ); + } + + @override + TaskEither clearLogs() { + return exceptionHandler( + () => singbox.clearLogs().mapLeft(CoreServiceFailure.other).run(), + CoreServiceFailure.unexpected, + ); } @override diff --git a/lib/domain/singbox/box_log.dart b/lib/domain/singbox/box_log.dart new file mode 100644 index 00000000..84ee25c1 --- /dev/null +++ b/lib/domain/singbox/box_log.dart @@ -0,0 +1,62 @@ +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tint/tint.dart'; + +part 'box_log.freezed.dart'; + +enum LogLevel { + trace, + debug, + info, + warn, + error, + fatal, + panic; + + static List get choices => values.takeFirst(4); + + Color? get color => switch (this) { + trace => Colors.lightBlueAccent, + debug => Colors.grey, + info => Colors.lightGreen, + warn => Colors.orange, + error => Colors.redAccent, + fatal => Colors.red, + panic => Colors.red, + }; +} + +@freezed +class BoxLog with _$BoxLog { + const factory BoxLog({ + LogLevel? level, + DateTime? time, + required String message, + }) = _BoxLog; + + factory BoxLog.parse(String log) { + log = log.strip(); + DateTime? time; + if (log.length > 25) { + time = DateTime.tryParse(log.substring(6, 25)); + } + if (time != null) { + log = log.substring(26); + } + final level = LogLevel.values.firstOrNullWhere( + (e) { + if (log.startsWith(e.name.toUpperCase())) { + log = log.removePrefix(e.name.toUpperCase()); + return true; + } + return false; + }, + ); + return BoxLog( + level: level, + time: time, + message: log.trim(), + ); + } +} diff --git a/lib/domain/singbox/config_options.dart b/lib/domain/singbox/config_options.dart index bfae62f4..4206a840 100644 --- a/lib/domain/singbox/config_options.dart +++ b/lib/domain/singbox/config_options.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/prefs/prefs.dart'; +import 'package:hiddify/domain/singbox/box_log.dart'; import 'package:hiddify/domain/singbox/rules.dart'; import 'package:hiddify/utils/platform_utils.dart'; @@ -53,13 +54,6 @@ class ConfigOptions with _$ConfigOptions { _$ConfigOptionsFromJson(json); } -enum LogLevel { - warn, - info, - debug, - trace, -} - @JsonEnum(valueField: 'key') enum IPv6Mode { disable("ipv4_only"), diff --git a/lib/domain/singbox/singbox.dart b/lib/domain/singbox/singbox.dart index 39bb5774..ea665c8f 100644 --- a/lib/domain/singbox/singbox.dart +++ b/lib/domain/singbox/singbox.dart @@ -1,3 +1,4 @@ +export 'box_log.dart'; export 'config_options.dart'; export 'core_status.dart'; export 'outbounds.dart'; diff --git a/lib/domain/singbox/singbox_facade.dart b/lib/domain/singbox/singbox_facade.dart index bcd40f80..6a8d9541 100644 --- a/lib/domain/singbox/singbox_facade.dart +++ b/lib/domain/singbox/singbox_facade.dart @@ -37,5 +37,7 @@ abstract interface class SingboxFacade { Stream> watchCoreStatus(); - Stream> watchLogs(); + Stream>> watchLogs(); + + TaskEither clearLogs(); } diff --git a/lib/features/common/common_controllers.dart b/lib/features/common/common_controllers.dart index 8cfbb413..db6e7e2c 100644 --- a/lib/features/common/common_controllers.dart +++ b/lib/features/common/common_controllers.dart @@ -2,7 +2,6 @@ import 'package:hiddify/core/prefs/general_prefs.dart'; import 'package:hiddify/features/common/app_update_notifier.dart'; import 'package:hiddify/features/common/connectivity/connectivity_controller.dart'; import 'package:hiddify/features/common/window/window_controller.dart'; -import 'package:hiddify/features/logs/notifier/notifier.dart'; import 'package:hiddify/features/profiles/notifier/notifier.dart'; import 'package:hiddify/features/system_tray/controller/system_tray_controller.dart'; import 'package:hiddify/services/service_providers.dart'; @@ -24,10 +23,6 @@ void commonControllers(CommonControllersRef ref) { }, fireImmediately: true, ); - ref.listen( - logsNotifierProvider, - (previous, next) {}, - ); ref.listen( connectivityControllerProvider, (previous, next) {}, diff --git a/lib/features/logs/notifier/logs_notifier.dart b/lib/features/logs/notifier/logs_notifier.dart index 6c7cfd43..b089840d 100644 --- a/lib/features/logs/notifier/logs_notifier.dart +++ b/lib/features/logs/notifier/logs_notifier.dart @@ -1,66 +1,133 @@ import 'dart:async'; -import 'package:dartx/dartx.dart'; import 'package:hiddify/data/data_providers.dart'; -import 'package:hiddify/domain/clash/clash.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/features/logs/notifier/logs_state.dart'; +import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:rxdart/rxdart.dart'; part 'logs_notifier.g.dart'; -// TODO: rewrite -@Riverpod(keepAlive: true) +@riverpod class LogsNotifier extends _$LogsNotifier with AppLogger { - static const maxLength = 1000; - @override - Stream build() { - state = const AsyncData(LogsState()); - return ref.read(coreFacadeProvider).watchLogs().asyncMap( - (event) async { - _logs = [ - event.getOrElse((l) => throw l), - ..._logs.takeFirst(maxLength - 1), - ]; - return switch (state) { - // ignore: unused_result - AsyncData(:final value) => value.copyWith(logs: await _computeLogs()), - _ => LogsState(logs: await _computeLogs()), - }; + LogsState build() { + ref.disposeDelay(const Duration(seconds: 20)); + state = const LogsState(); + ref.onDispose( + () { + loggy.debug("disposing"); + _listener?.cancel(); + _listener = null; }, ); + ref.onCancel( + () { + if (_listener?.isPaused != true) { + loggy.debug("pausing"); + _listener?.pause(); + } + }, + ); + ref.onResume( + () { + if (!state.paused && (_listener?.isPaused ?? false)) { + loggy.debug("resuming"); + _listener?.resume(); + } + }, + ); + + _addListeners(); + return const LogsState(); } - var _logs = []; + StreamSubscription? _listener; + + Future _addListeners() async { + loggy.debug("adding listeners"); + await _listener?.cancel(); + _listener = ref + .read(coreFacadeProvider) + .watchLogs() + .throttle( + (_) => Stream.value(_listener?.isPaused ?? false), + leading: false, + trailing: true, + ) + .throttleTime( + const Duration(milliseconds: 250), + leading: false, + trailing: true, + ) + .asyncMap( + (event) async { + await event.fold( + (f) { + _logs = []; + state = state.copyWith(logs: AsyncError(f, StackTrace.current)); + }, + (a) async { + _logs = a.reversed; + state = state.copyWith(logs: AsyncData(await _computeLogs())); + }, + ); + }, + ).listen((event) {}); + } + + Iterable _logs = []; final _debouncer = CallbackDebouncer(const Duration(milliseconds: 200)); LogLevel? _levelFilter; String _filter = ""; - Future> _computeLogs() async { - if (_levelFilter == null && _filter.isEmpty) return _logs; - return _logs.where((e) { - return _filter.isEmpty || e.contains(_filter); + Future> _computeLogs() async { + final logs = _logs.map(BoxLog.parse); + if (_levelFilter == null && _filter.isEmpty) return logs.toList(); + return logs.where((e) { + return (_filter.isEmpty || e.message.contains(_filter)) && + (_levelFilter == null || + e.level == null || + e.level!.index >= _levelFilter!.index); }).toList(); } - void clear() { - if (state case AsyncData(:final value)) { - state = AsyncData(value.copyWith(logs: [])).copyWithPrevious(state); - } + void pause() { + loggy.debug("pausing"); + _listener?.pause(); + state = state.copyWith(paused: true); + } + + void resume() { + loggy.debug("resuming"); + _listener?.resume(); + state = state.copyWith(paused: false); + } + + Future clear() async { + loggy.debug("clearing"); + await ref.read(coreFacadeProvider).clearLogs().match( + (l) { + loggy.warning("error clearing logs", l); + }, + (_) { + _logs = []; + state = state.copyWith(logs: const AsyncData([])); + }, + ).run(); } void filterMessage(String? filter) { _filter = filter ?? ''; _debouncer( () async { - if (state case AsyncData(:final value)) { - state = AsyncData( - value.copyWith( - filter: _filter, - logs: await _computeLogs(), - ), - ).copyWithPrevious(state); + if (state.logs case AsyncData()) { + state = state.copyWith( + filter: _filter, + logs: AsyncData(await _computeLogs()), + ); } }, ); @@ -68,13 +135,11 @@ class LogsNotifier extends _$LogsNotifier with AppLogger { Future filterLevel(LogLevel? level) async { _levelFilter = level; - if (state case AsyncData(:final value)) { - state = AsyncData( - value.copyWith( - levelFilter: _levelFilter, - logs: await _computeLogs(), - ), - ).copyWithPrevious(state); + if (state.logs case AsyncData()) { + state = state.copyWith( + levelFilter: _levelFilter, + logs: AsyncData(await _computeLogs()), + ); } } } diff --git a/lib/features/logs/notifier/logs_state.dart b/lib/features/logs/notifier/logs_state.dart index ba34a075..4318870a 100644 --- a/lib/features/logs/notifier/logs_state.dart +++ b/lib/features/logs/notifier/logs_state.dart @@ -1,5 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/domain/clash/clash.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'logs_state.freezed.dart'; @@ -8,7 +9,8 @@ class LogsState with _$LogsState { const LogsState._(); const factory LogsState({ - @Default([]) List logs, + @Default(AsyncLoading()) AsyncValue> logs, + @Default(false) bool paused, @Default("") String filter, LogLevel? levelFilter, }) = _LogsState; diff --git a/lib/features/logs/view/logs_page.dart b/lib/features/logs/view/logs_page.dart index af2e7954..7c5d093c 100644 --- a/lib/features/logs/view/logs_page.dart +++ b/lib/features/logs/view/logs_page.dart @@ -1,31 +1,31 @@ -import 'package:dartx/dartx.dart'; + import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fpdart/fpdart.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; -import 'package:hiddify/domain/clash/clash.dart'; import 'package:hiddify/domain/failures.dart'; -import 'package:hiddify/features/common/common.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/features/logs/notifier/notifier.dart'; import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:tint/tint.dart'; - class LogsPage extends HookConsumerWidget with PresLogger { const LogsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); - final asyncState = ref.watch(logsNotifierProvider); + final state = ref.watch(logsNotifierProvider); final notifier = ref.watch(logsNotifierProvider.notifier); final debug = ref.watch(debugModeNotifierProvider); final filesEditor = ref.watch(filesEditorServiceProvider); + final filterController = useTextEditingController(text: state.filter); + final List popupButtons = debug || PlatformUtils.isDesktop ? [ PopupMenuItem( @@ -49,115 +49,146 @@ class LogsPage extends HookConsumerWidget with PresLogger { ] : []; - switch (asyncState) { - case AsyncData(value: final state): - return Scaffold( - appBar: AppBar( - // TODO: fix height - toolbarHeight: 90, - title: Text(t.logs.pageTitle), - actions: [ - if (popupButtons.isNotEmpty) - PopupMenuButton( - itemBuilder: (context) { - return popupButtons; - }, - ), - ], - bottom: PreferredSize( - preferredSize: const Size.fromHeight(36), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - children: [ - Flexible( - child: TextFormField( - onChanged: notifier.filterMessage, - decoration: InputDecoration( - isDense: true, - hintText: t.logs.filterHint, - ), - ), + return Scaffold( + appBar: AppBar( + // TODO: fix height + toolbarHeight: 90, + title: Text(t.logs.pageTitle), + actions: [ + if (state.paused) + IconButton( + onPressed: notifier.resume, + icon: const Icon(Icons.play_arrow), + tooltip: t.logs.resumeTooltip, + ) + else + IconButton( + onPressed: notifier.pause, + icon: const Icon(Icons.pause), + tooltip: t.logs.pauseTooltip, + ), + IconButton( + onPressed: notifier.clear, + icon: const Icon(Icons.clear_all), + tooltip: t.logs.clearTooltip, + ), + if (popupButtons.isNotEmpty) + PopupMenuButton( + itemBuilder: (context) { + return popupButtons; + }, + ), + ], + bottom: PreferredSize( + preferredSize: const Size.fromHeight(36), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Flexible( + child: TextFormField( + controller: filterController, + onChanged: notifier.filterMessage, + decoration: InputDecoration( + isDense: true, + hintText: t.logs.filterHint, ), - const Gap(16), - DropdownButton>( - value: optionOf(state.levelFilter), - onChanged: (v) { - if (v == null) return; - notifier.filterLevel(v.toNullable()); - }, - padding: const EdgeInsets.symmetric(horizontal: 8), - borderRadius: BorderRadius.circular(4), - items: [ - DropdownMenuItem( - value: none(), - child: Text(t.logs.allLevelsFilter), - ), - ...LogLevel.values.takeFirst(3).map( - (e) => DropdownMenuItem( - value: some(e), - child: Text(e.name), - ), - ), - ], + ), + ), + const Gap(16), + DropdownButton>( + value: optionOf(state.levelFilter), + onChanged: (v) { + if (v == null) return; + notifier.filterLevel(v.toNullable()); + }, + padding: const EdgeInsets.symmetric(horizontal: 8), + borderRadius: BorderRadius.circular(4), + items: [ + DropdownMenuItem( + value: none(), + child: Text(t.logs.allLevelsFilter), + ), + ...LogLevel.choices.map( + (e) => DropdownMenuItem( + value: some(e), + child: Text(e.name), + ), ), ], ), - ), + ], ), ), - body: ListView.builder( - itemCount: state.logs.length, - reverse: true, - itemBuilder: (context, index) { - final log = state.logs[index]; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - subtitle: Text(log.strip()), - ), - if (index != 0) - const Divider( - indent: 16, - endIndent: 16, - height: 4, + ), + ), + body: switch (state.logs) { + AsyncData(value: final logs) => SelectionArea( + child: ListView.builder( + itemCount: logs.length, + reverse: true, + itemBuilder: (context, index) { + final log = logs[index]; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (log.level != null) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + log.level!.name.toUpperCase(), + style: Theme.of(context) + .textTheme + .labelMedium + ?.copyWith(color: log.level!.color), + ), + if (log.time != null) + Text( + log.time!.toString(), + style: + Theme.of(context).textTheme.labelSmall, + ), + ], + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), ), - ], - ); - }, + if (index != 0) + const Divider( + indent: 16, + endIndent: 16, + height: 4, + ), + ], + ); + }, + ), ), - ); - - case AsyncError(:final error): - return Scaffold( - body: CustomScrollView( + AsyncError(:final error) => CustomScrollView( slivers: [ - NestedTabAppBar( - title: Text(t.logs.pageTitle), - ), SliverErrorBodyPlaceholder(t.presentShortError(error)), ], ), - ); - - case AsyncLoading(): - return Scaffold( - body: CustomScrollView( + _ => const CustomScrollView( slivers: [ - NestedTabAppBar( - title: Text(t.logs.pageTitle), - ), - const SliverLoadingBodyPlaceholder(), + SliverLoadingBodyPlaceholder(), ], ), - ); - - // TODO: remove - default: - return const Scaffold(); - } + }, + ); } } diff --git a/lib/features/settings/view/config_options_page.dart b/lib/features/settings/view/config_options_page.dart index a677fd7a..87e804f5 100644 --- a/lib/features/settings/view/config_options_page.dart +++ b/lib/features/settings/view/config_options_page.dart @@ -52,13 +52,13 @@ class ConfigOptionsPage extends HookConsumerWidget { ), ListTile( title: Text(t.settings.config.logLevel), - subtitle: Text(options.logLevel.name), + subtitle: Text(options.logLevel.name.toUpperCase()), onTap: () async { final logLevel = await SettingsPickerDialog( title: t.settings.config.logLevel, selected: options.logLevel, - options: LogLevel.values, - getTitle: (e) => e.name, + options: LogLevel.choices, + getTitle: (e) => e.name.toUpperCase(), resetValue: _default.logLevel, ).show(context); if (logLevel == null) return; diff --git a/lib/services/singbox/ffi_singbox_service.dart b/lib/services/singbox/ffi_singbox_service.dart index 47dc4e65..8b9b3aff 100644 --- a/lib/services/singbox/ffi_singbox_service.dart +++ b/lib/services/singbox/ffi_singbox_service.dart @@ -16,6 +16,7 @@ import 'package:hiddify/utils/utils.dart'; import 'package:loggy/loggy.dart'; import 'package:path/path.dart' as p; import 'package:rxdart/rxdart.dart'; +import 'package:watcher/watcher.dart'; final _logger = Loggy('FFISingboxService'); @@ -301,33 +302,47 @@ class FFISingboxService ); } + final _logBuffer = []; + int _logFilePosition = 0; + @override - Stream watchLogs(String path) { - var linesRead = 0; - return Stream.periodic( - const Duration(seconds: 1), - ).asyncMap((_) async { - final result = await _readLogs(path, linesRead); - linesRead = result.$2; - return result.$1; - }).transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) { - for (final item in data) { - sink.add(item); - } - }, - ), - ); + Stream> watchLogs(String path) async* { + yield await _readLogFile(File(path)); + yield* Watcher(path, pollingDelay: const Duration(seconds: 1)) + .events + .asyncMap((event) async { + if (event.type == ChangeType.MODIFY) { + await _readLogFile(File(path)); + } + return _logBuffer; + }); } - Future<(List, int)> _readLogs(String path, int from) async { - return CombineWorker().execute( + @override + TaskEither clearLogs() { + return TaskEither( () async { - final lines = await File(path).readAsLines(); - final to = lines.length; - return (lines.sublist(from), to); + _logBuffer.clear(); + return right(unit); }, ); } + + Future> _readLogFile(File file) async { + if (_logFilePosition == 0 && file.lengthSync() == 0) return []; + final content = + await file.openRead(_logFilePosition).transform(utf8.decoder).join(); + _logFilePosition = file.lengthSync(); + final lines = const LineSplitter().convert(content); + if (lines.length > 300) { + lines.removeRange(0, lines.length - 300); + } + for (final line in lines) { + _logBuffer.add(line); + if (_logBuffer.length > 300) { + _logBuffer.removeAt(0); + } + } + return _logBuffer; + } } diff --git a/lib/services/singbox/mobile_singbox_service.dart b/lib/services/singbox/mobile_singbox_service.dart index 83d1e1cc..aaf2b399 100644 --- a/lib/services/singbox/mobile_singbox_service.dart +++ b/lib/services/singbox/mobile_singbox_service.dart @@ -163,11 +163,18 @@ class MobileSingboxService } @override - Stream watchLogs(String path) { - return _logsChannel.receiveBroadcastStream().map( - (event) { - // loggy.debug("received log: $event"); - return event as String; + Stream> watchLogs(String path) async* { + yield* _logsChannel + .receiveBroadcastStream() + .map((event) => (event as List).map((e) => e as String).toList()); + } + + @override + TaskEither clearLogs() { + return TaskEither( + () async { + await _methodChannel.invokeMethod("clear_logs"); + return right(unit); }, ); } diff --git a/lib/services/singbox/singbox_service.dart b/lib/services/singbox/singbox_service.dart index 5f1c31f4..eb181695 100644 --- a/lib/services/singbox/singbox_service.dart +++ b/lib/services/singbox/singbox_service.dart @@ -48,5 +48,7 @@ abstract interface class SingboxService { Stream watchStats(); - Stream watchLogs(String path); + Stream> watchLogs(String path); + + TaskEither clearLogs(); } diff --git a/libcore b/libcore index 0480a5d3..7b367fe7 160000 --- a/libcore +++ b/libcore @@ -1 +1 @@ -Subproject commit 0480a5d3ec0571b7a0662625679a277219c51d0f +Subproject commit 7b367fe70c9ecbf0dda2b73289905565e2451745 diff --git a/pubspec.lock b/pubspec.lock index 609db473..68535a90 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1555,7 +1555,7 @@ packages: source: hosted version: "11.10.0" watcher: - dependency: transitive + dependency: "direct main" description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" diff --git a/pubspec.yaml b/pubspec.yaml index e460c9b9..f2ef1f46 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,8 +10,6 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.6 - - # internationalization flutter_localizations: sdk: flutter intl: ^0.18.1 @@ -19,28 +17,18 @@ dependencies: slang_flutter: ^3.24.0 timeago: ^3.5.0 flutter_localized_locales: ^2.0.5 - - # data & serialization fpdart: ^1.1.0 freezed_annotation: ^2.4.1 json_annotation: ^4.8.1 - - # state management hooks_riverpod: ^2.4.3 flutter_hooks: ^0.20.3 riverpod_annotation: ^2.2.0 rxdart: ^0.27.7 - - # persistence drift: ^2.12.1 sqlite3_flutter_libs: ^0.5.16 shared_preferences: ^2.2.2 - - # networking dio: ^5.3.3 web_socket_channel: ^2.4.0 - - # native ffi: ^2.1.0 path_provider: ^2.1.1 flutter_local_notifications: ^15.1.1 @@ -54,13 +42,9 @@ dependencies: url_launcher: ^6.1.14 vclibs: ^0.1.0 launch_at_startup: ^0.2.2 - - # analytics sentry_flutter: ^7.10.1 sentry_dart_plugin: ^1.6.2 sentry_dio: ^7.10.1 - - # utils combine: ^0.5.6 path: ^1.8.3 loggy: ^2.0.3 @@ -73,8 +57,7 @@ dependencies: accessibility_tools: ^1.0.0 neat_periodic_task: ^2.0.1 retry: ^3.1.2 - - # widgets + watcher: ^1.1.0 go_router: ^11.1.4 flex_color_scheme: ^7.3.1 flutter_animate: ^4.2.0+1 From a6fc119cf6f8ba0a3fed4c92c0a848a115bc819d Mon Sep 17 00:00:00 2001 From: solokot Date: Tue, 24 Oct 2023 08:56:40 +0300 Subject: [PATCH 25/27] Update Russian Mainly fix google translate of new worlds --- assets/translations/strings_ru.i18n.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 12259231..a5c627c7 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -1,6 +1,6 @@ { "general": { - "appTitle": "HiddifyNext", + "appTitle": "Hiddify Next", "reset": "Сброс", "toggle": { "enabled": "Включено", @@ -110,9 +110,9 @@ "allLevelsFilter": "Все", "shareCoreLogs": "Поделиться журналами ядра", "shareAppLogs": "Поделиться журналами приложения", - "pauseTooltip": "Пауза", - "resumeTooltip": "Резюме", - "clearTooltip": "Прозрачный" + "pauseTooltip": "Приостановить", + "resumeTooltip": "Возобновить", + "clearTooltip": "Очистить" }, "settings": { "pageTitle": "Настройки", @@ -133,7 +133,7 @@ "system": "Системная тема", "dark": "Тёмная тема", "light": "Светлая тема", - "black": "Черный режим" + "black": "Чёрная тема" }, "enableAnalytics": "Сбор аналитики", "enableAnalyticsMsg": "Сбор аналитических данных и отправка отчётов о сбоях для улучшения приложения.", @@ -212,7 +212,7 @@ "updateMsg": "Доступна новая версия @:general.appTitle. Обновить сейчас?", "currentVersionLbl": "Текущая версия", "newVersionLbl": "Новая версия", - "updateNowBtnTxt": "Обновить сейчас", + "updateNowBtnTxt": "Обновить", "laterBtnTxt": "Позже", "ignoreBtnTxt": "Пропустить" }, From 45d3243d9ee7dceb50d975744618b1cc047062b5 Mon Sep 17 00:00:00 2001 From: Hiddify Date: Tue, 24 Oct 2023 11:26:21 +0200 Subject: [PATCH 26/27] new: add ios core library to the project --- .github/workflows/build.yml | 46 +++++++++++++++++++++++------------- Makefile | 9 ++++--- ios/Pods/Frameworks/.gitkeep | 0 3 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 ios/Pods/Frameworks/.gitkeep diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9461bb3..08927886 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,12 +4,12 @@ on: branches: - main tags: - - 'v*' + - "v*" paths-ignore: - - '**.md' - - 'docs/**' - - '.github/**' - - '!.github/workflows/build.yml' + - "**.md" + - "docs/**" + - ".github/**" + - "!.github/workflows/build.yml" pull_request: branches: - main @@ -57,6 +57,7 @@ jobs: # - platform: ios # os: macos-11 # aarch: universal + # filename: hiddify-ios-universal # targets: ipa runs-on: ${{ matrix.os }} @@ -67,15 +68,15 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.13.x' - channel: 'stable' + flutter-version: "3.13.x" + channel: "stable" cache: true - name: Setup Java if: startsWith(matrix.platform,'android') uses: actions/setup-java@v3 with: - distribution: 'zulu' + distribution: "zulu" java-version: 11 - name: Setup NDK @@ -164,7 +165,7 @@ jobs: xcopy /s /v '.\.github\help\mac-windows\' .\tmp_out\ cd tmp_out - Compress-Archive .\ ..\out\hiddify-${{ matrix.platform }}-x64-setup.zip + Compress-Archive -Path .\* -DestinationPath ..\out\hiddify-${{ matrix.platform }}-x64-setup.zip cd .. @@ -205,10 +206,13 @@ jobs: else cp ./.github/help/mac-windows/* tmp_out/ fi - - cd tmp_out - 7z a ${{matrix.filename}}.zip ./ - mv *.zip ../out/ + if [[ "${{matrix.platform}}" == 'ios' ]];then + mv tmp_out/${{matrix.filename}}.ipa bin/${{matrix.filename}}.ipa + else + cd tmp_out + 7z a ${{matrix.filename}}.zip ./ + mv *.zip ../out/ + fi # - name: Copy to out unix # if: matrix.platform == 'linux' || matrix.platform == 'macos' || matrix.platform == 'ios' @@ -259,7 +263,7 @@ jobs: uses: 8Mi-Tech/delete-release-assets-action@main with: github_token: ${{ secrets.GITHUB_TOKEN }} - tag: 'draft' + tag: "draft" deleteOnlyFromDrafts: false - name: Create or Update Draft Release @@ -269,8 +273,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: ./out/* - name: 'draft' - tag_name: 'draft' + name: "draft" + tag_name: "draft" prerelease: true upload-release: @@ -310,7 +314,7 @@ jobs: with: prerelease: ${{ env.CHANNEL == 'dev' }} tag_name: ${{ github.ref_name }} - body_path: './release.md' + body_path: "./release.md" files: ./out/* - name: Create service_account.json @@ -324,3 +328,11 @@ jobs: releaseName: ${{ github.ref }} releaseFiles: ./hiddify-android-market.aab track: internal + + # - name: 'Upload app to TestFlight' + # uses: apple-actions/upload-testflight-build@v1 + # with: + # app-path: './hiddify-ios-universal.ipa' + # issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} + # api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} + # api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} diff --git a/Makefile b/Makefile index 445cc656..47420aab 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ include dependencies.properties BINDIR=./libcore/bin ANDROID_OUT=./android/app/libs +IOS_OUT=./ios/Pods/Frameworks DESKTOP_OUT=./libcore/bin GEO_ASSETS_DIR=./assets/core @@ -78,7 +79,8 @@ macos-libs: ios-libs: #not tested mkdir -p $(DESKTOP_OUT)/ &&\ - curl -L $(CORE_URL)/$(CORE_NAME)-ios-universal.xcframework.gz | gunzip > $(DESKTOP_OUT)/libcore.xcframework + curl -L $(CORE_URL)/$(CORE_NAME)-ios.xcframework.tar.gz | tar xz -C "$(IOS_OUT)" && \ + mv $(IOS_OUT)/$(CORE_NAME)-ios.xcframework $(IOS_OUT)/libcore.xcframework get-geo-assets: curl -L https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db -o $(GEO_ASSETS_DIR)/geoip.db @@ -99,8 +101,9 @@ build-linux-libs: build-macos-libs: make -C libcore -f Makefile macos-universal && mv $(BINDIR)/$(CORE_NAME)-macos-universal.dylib $(DESKTOP_OUT)/libcore.dylib -build-ios-libs: #not tested - make -C libcore -f Makefile ios && mv $(BINDIR)/$(CORE_NAME)-ios.xcframework $(DESKTOP_OUT)/libcore.xcframework +build-ios-libs: + make -C libcore -f Makefile ios && mv $(BINDIR)/$(CORE_NAME)-ios.xcframework $(IOS_OUT)/libcore.xcframework + release: # Create a new tag for release. diff --git a/ios/Pods/Frameworks/.gitkeep b/ios/Pods/Frameworks/.gitkeep new file mode 100644 index 00000000..e69de29b From aa946deebd24d4b594650a29086c4269658b47da Mon Sep 17 00:00:00 2001 From: Hiddify Date: Tue, 24 Oct 2023 11:26:57 +0200 Subject: [PATCH 27/27] Merge branch 'main' of hiddify-github:hiddify/hiddify-next --- CHANGELOG.md | 20 + LICENSE.md | 3 + Makefile | 3 +- README.md | 31 +- README_cn.md | 28 +- .../kotlin/com/hiddify/hiddify/LogHandler.kt | 9 +- .../com/hiddify/hiddify/MainActivity.kt | 28 +- .../com/hiddify/hiddify/MethodHandler.kt | 60 +- assets/translations/strings_en.i18n.json | 15 +- assets/translations/strings_fa.i18n.json | 13 +- assets/translations/strings_ru.i18n.json | 25 +- assets/translations/strings_zh.i18n.json | 13 +- changelog.md | 1265 ----------------- lib/core/app/app_view.dart | 2 +- lib/core/core_providers.dart | 1 - lib/core/prefs/app_theme.dart | 30 +- lib/core/prefs/theme_prefs.dart | 24 +- lib/data/repository/config_options_store.dart | 39 +- lib/data/repository/core_facade_impl.dart | 19 +- lib/domain/singbox/box_log.dart | 62 + lib/domain/singbox/config_options.dart | 16 +- lib/domain/singbox/rules.dart | 37 + lib/domain/singbox/singbox.dart | 1 + lib/domain/singbox/singbox_facade.dart | 4 +- lib/features/common/common_controllers.dart | 5 - lib/features/logs/notifier/logs_notifier.dart | 149 +- lib/features/logs/notifier/logs_state.dart | 6 +- lib/features/logs/view/logs_page.dart | 233 +-- .../settings/view/config_options_page.dart | 6 +- .../widgets/advanced_setting_tiles.dart | 2 + .../widgets/general_setting_tiles.dart | 44 +- .../widgets/theme_mode_switch_button.dart | 51 - lib/services/singbox/ffi_singbox_service.dart | 61 +- .../singbox/mobile_singbox_service.dart | 17 +- lib/services/singbox/singbox_service.dart | 4 +- libcore | 2 +- pubspec.lock | 2 +- pubspec.yaml | 24 +- 38 files changed, 668 insertions(+), 1686 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 changelog.md create mode 100644 lib/domain/singbox/box_log.dart delete mode 100644 lib/features/settings/widgets/theme_mode_switch_button.dart diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8d56e98b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +### Added +- Basic region based routing rules +- Russian region +- Logs flow control + +### Changed +- Theme preferences +- Logs page + +### Fixed +- Localization mistakes in Russian from [solokot](https://github.com/solokot) +- Localization mistakes in Russian from [Elshad Guseynov](https://github.com/lifeindarkside) +- Logs filtering diff --git a/LICENSE.md b/LICENSE.md index fd7de532..cac21c22 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,6 +2,9 @@ # Attribution-NonCommercial-ShareAlike 4.0 International ## Summary: +- The forks of the app are not allowed to be listed on F-Droid or other app stores under the original name or original design. +- Any forks should be published open-source under the same license. +- You need prior consent to publish a fork or use any part of this code in an application published in Apple Store. - You are free to: - Share — copy and redistribute the material in any medium or format - Adapt — remix, transform, and build upon the material diff --git a/Makefile b/Makefile index 47420aab..e1eaf072 100644 --- a/Makefile +++ b/Makefile @@ -119,9 +119,8 @@ release: # Create a new tag for release. echo "version: $${VERSION_STR}+$${BUILD_NUMBER}" && \ sed -i "s/version: .*/version: $${VERSION_STR}\+$${BUILD_NUMBER}/g" pubspec.yaml && \ git tag $${TAG} > /dev/null && \ - gitchangelog > changelog.md || { git tag -d $${TAG}; echo "Please run pip install git gitchangelog pystache mustache markdown"; exit 2; } && \ git tag -d $${TAG} > /dev/null && \ - git add pubspec.yaml changelog.md && \ + git add pubspec.yaml CHANGELOG.md && \ make sync_translate && \ git add assets/translations/* && \ git commit -m "release: version $${TAG}" && \ diff --git a/README.md b/README.md index c4dc0dfd..934a8192 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,6 @@ -
-English Demo - - -
## What is Hiddify-Next? @@ -25,14 +20,11 @@ The app is developed using [Flutter](https://flutter.dev) and [Go](https://go.dev). For more information you can read through our [Contribution Guidelines](https://github.com/hiddify/hiddify-next/blob/main/CONTRIBUTING.md) for development. -## Improve Translations -You can easily contribute to this project by using the following links to improve the translations: - - [English](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en) -- [Persian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=fa) -- [Russian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=ru) -- [Chinese](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=zh) +
+English Demo +
## 🚀 Main features @@ -80,9 +72,9 @@ You can easily contribute to this project by using the following links to improv Android

-
-
- +
+
+ @@ -97,7 +89,7 @@ You can easily contribute to this project by using the following links to improv Linux - + @@ -108,6 +100,15 @@ You can easily contribute to this project by using the following links to improv ## Installation and tutorials Please find tutorial information on the [wiki page](https://github.com/hiddify/hiddify-next/wiki). +## Improve Translations +You can easily contribute to this project by using the following links to improve the translations: + - [English](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en) +- [Persian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=fa) +- [Russian](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=ru) +- [Chinese](https://inlang.com/editor/github.com/hiddify/hiddify-next?lang=en&lang=zh) + + + ## Acknowledgements - [Sing-box](https://github.com/SagerNet/sing-box) diff --git a/README_cn.md b/README_cn.md index 7583a9b6..1bf5f6d5 100644 --- a/README_cn.md +++ b/README_cn.md @@ -13,7 +13,7 @@ -## 什么是 Hiddify-Next? +## Hiddify-Next 是什么? 基于 [Sing-box](https://github.com/SagerNet/sing-box) 的多平台客户端,用作通用代理工具链。 该应用程序提供了广泛的功能,如下所列。 它还支持大量协议。 该应用程序免费使用、无广告且开源。 它提供了一个安全且私密的工具来访问免费互联网。 该应用程序是使用 [Flutter](https://flutter.dev/) 和 [Go](https://go.dev/) 开发的。 欲了解更多信息,您可以阅读我们的开发贡献指南。 @@ -31,9 +31,9 @@ 🔍 基于延迟的自动选择 -🟡 广泛的协议:ECH、Sing-box、V2ray、Xray、Vless、Vmess、Reality、TUIC、Hysteria、ShadowTLS、SSH、Clash、Clash meta +🟡 广泛的协议支持:ECH、Sing-box、V2ray、Xray、Vless、Vmess、Trojan、Trojan with websocket、Reality、TUIC、Hysteria、Hysteria2、ShadowTLS、SSH、Clash、Clash meta -🟡 订阅链接:Clash、Clash meta、Sing-box 和 Shadowsocks +🟡 支持多种订阅链接导入:Clash、Clash meta、Sing-box 和 Shadowsocks 🔄 自动订阅更新 @@ -45,9 +45,9 @@ 🌙 深色和浅色模式 -⚙ 与所有代理管理面板兼容 +⚙ 与所有代理管理面板的节点兼容 -⭐ 适合伊朗、中国、俄罗斯等国家配置 +⭐ 适用于伊朗、中国、俄罗斯等国家配置 📱 可在 Google Play 上获取 @@ -63,7 +63,7 @@ - 安卓 + Android


@@ -72,13 +72,13 @@ - 视窗 + Windows
- 苹果系统 + macOS @@ -95,16 +95,16 @@ ## 致谢 - [Sing-box](https://github.com/SagerNet/sing-box) -- [Sing-box 适用于安卓](https://github.com/SagerNet/sing-box-for-android) +- [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) -- [其他的](./pubspec.yaml) +- [其他](./pubspec.yaml) ## 捐赠与支持 -支持我们的最简单方法是点击本页顶部的星号 (⭐)。 +支持我们的最简单方法是单击此页面顶部的 Star (⭐)。 -我们的服务还需要财政支持。 我们所有的活动都是自愿进行的,财政支持将用于项目的开发。 您可以[此处](https://github.com/hiddify/hiddify-server/wiki/support)查看我们的支持地址 +我们的服务也需要资金支持。我们所有的活动都是自愿进行的,资金支持将用于项目的开发和维护。您可以在 [此处](https://github.com/hiddify/hiddify-manager/wiki/support) 查看我们的支持地址。
@@ -119,7 +119,7 @@

- 我们感谢所有参与该项目的人。 这里有一些人,还有 Github 之外的更多人。 这对我们来说意义重大。 ♥ + 感谢所有参与该项目的人。包括以下列出的人,和更多其他来自 Github 的人。你们对我们的意义非常重大。 ♥

@@ -128,7 +128,7 @@

- 制作与 Contrib.Rocks + 使用 Contrib.Rocks 制作

diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt index d1ae2e46..3fc91860 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt @@ -1,5 +1,6 @@ package com.hiddify.hiddify +import android.util.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel @@ -18,13 +19,15 @@ class LogHandler : FlutterPlugin { logsChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { - MainActivity.instance.serviceLogs.observeForever { - if (it == null) return@observeForever - events?.success(it) + val activity = MainActivity.instance + events?.success(activity.logList) + activity.logCallback = { + events?.success(activity.logList) } } override fun onCancel(arguments: Any?) { + MainActivity.instance.logCallback = null } }) } diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt index f2253b36..f605144c 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt @@ -39,7 +39,6 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback { var logCallback: ((Boolean) -> Unit)? = null val serviceStatus = MutableLiveData(Status.Stopped) val serviceAlerts = MutableLiveData(null) - val serviceLogs = MutableLiveData(null) override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) @@ -102,37 +101,18 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback { serviceAlerts.postValue(ServiceEvent(Status.Stopped, type, message)) } - private var paused = false - override fun onPause() { - super.onPause() - - paused = true - } - - override fun onResume() { - super.onResume() - - paused = false - logCallback?.invoke(true) - } - override fun onServiceWriteLog(message: String?) { - if (paused) { - if (logList.size > 300) { - logList.removeFirst() - } + if (logList.size > 300) { + logList.removeFirst() } logList.addLast(message) - if (!paused) { - logCallback?.invoke(false) - serviceLogs.postValue(message) - } + logCallback?.invoke(false) } override fun onServiceResetLogs(messages: MutableList) { logList.clear() logList.addAll(messages) - if (!paused) logCallback?.invoke(true) + logCallback?.invoke(true) } override fun onDestroy() { diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt index 944c52f7..36e01b2a 100644 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt @@ -29,6 +29,7 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, Restart("restart"), SelectOutbound("select_outbound"), UrlTest("url_test"), + ClearLogs("clear_logs"), } } @@ -63,38 +64,44 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, } Trigger.ChangeConfigOptions.method -> { - result.runCatching { - val args = call.arguments as String - Settings.configOptions = args - success(true) + scope.launch { + result.runCatching { + val args = call.arguments as String + Settings.configOptions = args + success(true) + } } } Trigger.Start.method -> { - result.runCatching { - val args = call.arguments as Map<*, *> - Settings.activeConfigPath = args["path"] as String? ?: "" - val mainActivity = MainActivity.instance - val started = mainActivity.serviceStatus.value == Status.Started - if (started) { - Log.w(TAG, "service is already running") - return success(true) + scope.launch { + result.runCatching { + val args = call.arguments as Map<*, *> + Settings.activeConfigPath = args["path"] as String? ?: "" + val mainActivity = MainActivity.instance + val started = mainActivity.serviceStatus.value == Status.Started + if (started) { + Log.w(TAG, "service is already running") + return@launch success(true) + } + mainActivity.startService() + success(true) } - mainActivity.startService() - success(true) } } Trigger.Stop.method -> { - result.runCatching { - val mainActivity = MainActivity.instance - val started = mainActivity.serviceStatus.value == Status.Started - if (!started) { - Log.w(TAG, "service is not running") - return success(true) + scope.launch { + result.runCatching { + val mainActivity = MainActivity.instance + val started = mainActivity.serviceStatus.value == Status.Started + if (!started) { + Log.w(TAG, "service is not running") + return@launch success(true) + } + BoxService.stop() + success(true) } - BoxService.stop() - success(true) } } @@ -151,6 +158,15 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, } } + Trigger.ClearLogs.method -> { + scope.launch { + result.runCatching { + MainActivity.instance.onServiceResetLogs(mutableListOf()) + success(true) + } + } + } + else -> result.notImplemented() } } diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index ddf1dbca..8a5e9056 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -46,7 +46,7 @@ "remainingDuration": "${duration} Days Remaining", "remainingTrafficSemanticLabel": "${consumed} of ${total} traffic consumed.", "expired": "Expired", - "noTraffic": "No more traffic" + "noTraffic": "Out of Quota" }, "sortBy": { "lastUpdate": "Recently updated", @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "Logs", - "clearLogsButtonText": "Clear Logs", "filterHint": "Filter", "allLevelsFilter": "All", "shareCoreLogs": "Share Core Logs", - "shareAppLogs": "Share App logs" + "shareAppLogs": "Share App logs", + "pauseTooltip": "Pause", + "resumeTooltip": "Resume", + "clearTooltip": "Clear" }, "settings": { "pageTitle": "Settings", @@ -123,17 +125,18 @@ "regions": { "ir": "Iran (ir)", "cn": "China (cn)", + "ru": "Russia (ru)", "other": "Other" }, "themeMode": "Theme Mode", "themeModes": { "system": "Follow system theme", "dark": "Dark mode", - "light": "Light mode" + "light": "Light mode", + "black": "Black mode" }, "enableAnalytics": "Enable Analytics", "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", - "trueBlack": "Pure Black", "autoStart": "Start on Boot", "silentStart": "Silent Start", "openWorkingDir": "Open Working Directory", @@ -257,4 +260,4 @@ "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "The key goal of HiddifyNext is to provide a secure, user-friendly and efficient tunneling client. It enables you to route all traffic or selected app traffic to a remote server of your choose, utilizing VPN-Service permission.\n\nNote: We do not provide any server; users are required to ensure their online activities stay private by using use their own self-hosted server or trusted servers. \n \nWe support servers with:\n- Normal V2ray/Xray Subscription Link\n- Clash Subscription Link\n- Sing-Box Subscription Link\n\nWhat is our unique features?\n - User Friendly\n - Optimized and Fast\n - Automatically select LowestPing \n - Show user usage information\n - Easily import sublink by one click using deeplinking \n - Free and No ADS\n - Easily switch user sublinks\n - more and more\n\nSupport:\n- All Protocols supported by Sing-Box \n- VLESS + xtls reality, vision\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n- V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nThe source code exist in https://github.com/hiddify/Hiddify-Next\nThe application core is based on open-source sing-box.\n\nPermission Description:\n- VPN Service: As the goal of this application is to provide a secure, user-friendly and efficient tunneling client, we need this permission to be able to route the traffic via tunnel to the remote server. \n- QUERY ALL PACKAGES: This permission is used to allow users to include or exclude specific applications for tunneling.\n- RECEIVE BOOT COMPLETED: This permission can be enabled or disabled from app settings to activate this application upon device boot.\n- POST NOTIFICATIONS: This permission is essential as we employ a foreground service to ensure the continuous operation of the VPN service.\n- This application is free from advertisements. The analytics and crash data only occurs with the explicit consent of the user in the first use of application." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 9fe3206c..cebc0b0a 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "لاگ‌ها", - "clearLogsButtonText": "پاک‌سازی", "filterHint": "فیلتر", "allLevelsFilter": "همه", "shareCoreLogs": "اشتراک‌گذاری لاگ هسته", - "shareAppLogs": "اشتراک‌گذاری لاگ برنامه" + "shareAppLogs": "اشتراک‌گذاری لاگ برنامه", + "pauseTooltip": "مکث", + "resumeTooltip": "از سرگیری", + "clearTooltip": "پاک‌سازی" }, "settings": { "pageTitle": "تنظیمات", @@ -123,17 +125,18 @@ "regions": { "ir": "ایران (ir)", "cn": "چین (cn)", + "ru": "روسیه (ru)", "other": "سایر" }, "themeMode": "تم مود", "themeModes": { "system": "پیروی از تم دستگاه", "dark": "تم تیره", - "light": "تم روشن" + "light": "تم روشن", + "black": "تم سیاه" }, "enableAnalytics": "فعال‌سازی آنالیتیکز", "enableAnalyticsMsg": "ارائه دسترسی آنالیز و گزارش خطا برای بهبود عملکرد برنامه", - "trueBlack": "کاملا سیاه", "autoStart": "اجرا با روشن شدن سیستم", "silentStart": "اجرای ساکت", "openWorkingDir": "باز کردن دایرکتوری کاری", @@ -257,4 +260,4 @@ "short_description": "Auto, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "هدف اصلی HiddifyNext ارائه یک کلاینت تونل زنی ایمن، کاربرپسند و کارآمد است. این به شما امکان می دهد تا با استفاده از مجوز VPN-Service، تمام ترافیک یا ترافیک برنامه انتخابی را به یک سرور راه دور مورد نظر خود هدایت کنید.\n\nتوجه: ما هیچ سروری ارائه نمی دهیم. کاربران موظفند با استفاده از سرورهای خود میزبان یا سرورهای مورد اعتماد، فعالیت‌های آنلاین خود را خصوصی نگه دارند.\n \nما از سرورهایی با موارد زیر پشتیبانی می کنیم:\n- لینک اشتراک V2ray/Xray معمولی\n- لینک اشتراک کلش\n- لینک اشتراک Sing-Box\n\nویژگی های منحصر به فرد ما چیست؟\n - کاربر پسند\n - بهینه و سریع\n - به طور خودکار LowestPing را انتخاب کنید\n - نمایش اطلاعات استفاده کاربر\n - به راحتی لینک فرعی را با یک کلیک با استفاده از دیپ لینک وارد کنید\n - رایگان و بدون تبلیغات\n - به راحتی پیوندهای فرعی کاربر را تغییر دهید\n - بیشتر و بیشتر\n\nحمایت کردن:\n- تمام پروتکل های پشتیبانی شده توسط Sing-Box\n- VLESS + xtls \n- VMESS\n- تروجان\n- ShoadowSocks\n- ریالیتی\n- V2ray\n- هیستریا 2\n- TUIC\n- SSH\n- ShadowTLS\n\n\nکد منبع در https://github.com/hiddify/Hiddify-Next وجود دارد\nهسته برنامه بر اساس sing-box منبع باز است.\n\nتوضیحات مجوز:\n- سرویس VPN: از آنجا که هدف این برنامه ارائه یک کلاینت تونل زنی ایمن، کاربر پسند و کارآمد است، ما به این مجوز نیاز داریم تا بتوانیم ترافیک را از طریق تونل به سرور راه دور هدایت کنیم.\n- QUERY ALL PACKAGES: این مجوز برای اجازه دادن به کاربران برای گنجاندن یا حذف برنامه های کاربردی خاص برای تونل زدن استفاده می شود.\n- دریافت بوت تکمیل شد: این مجوز را می توان از تنظیمات برنامه فعال یا غیرفعال کرد تا این برنامه پس از بوت شدن دستگاه فعال شود.\n- اعلان های ارسالی: این مجوز ضروری است زیرا ما از یک سرویس پیش زمینه برای اطمینان از عملکرد مداوم سرویس VPN استفاده می کنیم.\n- این برنامه بدون تبلیغات است. تجزیه و تحلیل و داده های اشکال فقط با رضایت صریح کاربر در اولین استفاده از برنامه اتفاق می افتد." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index d798d217..a5c627c7 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -1,6 +1,6 @@ { "general": { - "appTitle": "HiddifyNext", + "appTitle": "Hiddify Next", "reset": "Сброс", "toggle": { "enabled": "Включено", @@ -30,8 +30,8 @@ "stats": { "traffic": "Скорость", "trafficTotal": "Трафик", - "uplink": "Входящий канал", - "downlink": "Исходящий канал" + "uplink": "Исходящий канал", + "downlink": "Входящий канал" } }, "profile": { @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "Журналы", - "clearLogsButtonText": "Очистить журналы", "filterHint": "Фильтр", "allLevelsFilter": "Все", "shareCoreLogs": "Поделиться журналами ядра", - "shareAppLogs": "Поделиться журналами приложения" + "shareAppLogs": "Поделиться журналами приложения", + "pauseTooltip": "Приостановить", + "resumeTooltip": "Возобновить", + "clearTooltip": "Очистить" }, "settings": { "pageTitle": "Настройки", @@ -123,17 +125,18 @@ "regions": { "ir": "Иран (ir)", "cn": "Китай (cn)", + "ru": "Россия (ru)", "other": "Другой" }, "themeMode": "Оформление", "themeModes": { "system": "Системная тема", "dark": "Тёмная тема", - "light": "Светлая тема" + "light": "Светлая тема", + "black": "Чёрная тема" }, "enableAnalytics": "Сбор аналитики", "enableAnalyticsMsg": "Сбор аналитических данных и отправка отчётов о сбоях для улучшения приложения.", - "trueBlack": "Чистый чёрный цвет", "autoStart": "Запуск при загрузке", "silentStart": "Тихий запуск", "openWorkingDir": "Открыть рабочую папку", @@ -198,7 +201,7 @@ "pageTitle": "О программе", "version": "Версия", "sourceCode": "Исходный код", - "telegramChannel": "Telegram канал", + "telegramChannel": "Telegram-канал", "checkForUpdate": "Проверка обновления", "privacyPolicy": "Политика конфиденциальности", "termsAndConditions": "Условия и положения" @@ -209,7 +212,7 @@ "updateMsg": "Доступна новая версия @:general.appTitle. Обновить сейчас?", "currentVersionLbl": "Текущая версия", "newVersionLbl": "Новая версия", - "updateNowBtnTxt": "Обновить сейчас", + "updateNowBtnTxt": "Обновить", "laterBtnTxt": "Позже", "ignoreBtnTxt": "Пропустить" }, @@ -255,6 +258,6 @@ "play": { "title": "Hiddify Next (Preview)", "short_description": "Автовыбор, SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", - "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n- ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." + "full_description": "Основная цель HiddifyNext — предоставить безопасный, удобный и эффективный клиент туннелирования. Он позволяет направлять весь трафик или трафик выбранного приложения на выбранный вами удалённый сервер, используя разрешение VPN–сервиса.\n\nПримечание: мы не предоставляем серверы, пользователи должны обеспечивать конфиденциальность своих действий в Интернете, используя собственный сервер или доверенные серверы.\n \nПоддерживаемые серверы:\n— Обычная ссылка на подписку V2ray/Xray\n— Ссылка на подписку Clash\n— Ссылка на подписку на Sing–Box\n\nВ чём уникальные особенности?\n — Удобство\n — Оптимизация и скорость\n — Автоматический выбор минимальной задержки\n — Отображение информации об использовании\n — Простой импорт ссылок одним щелчком мыши\n — Бесплатно и без рекламы\n — Простое переключение ссылок\n — …и много больше\n\nПоддержка:\n• Все протоколы, поддерживаемые Sing-Box\n• VLESS + xtls reality, vision\n• VMESS\n• Trojan\n• ShoadowSocks\n• Reality\n• V2ray\n• Hystria2\n• TUIC\n• SSH\n• ShadowTLS\n\n\nИсходный код доступен по адресу https://github.com/hiddify/Hiddify-Next.\nЯдро приложения основано на открытом исходном коде Sing–Box.\n\nОписание разрешений:\n— СЛУЖБА VPN: поскольку целью данного приложения является предоставление безопасного, удобного и эффективного клиента туннелирования, это разрешение необходимо, чтобы иметь возможность направлять трафик через туннель на удалённый сервер.\n— ЗАПРОС ВСЕХ ПАКЕТОВ: это разрешение позволяет включать или исключать определённые приложения для туннелирования.\n— ИНФОРМИРОВАНИЕ О ЗАВЕРШЕНИИ ЗАГРУЗКИ: это разрешение можно включить или отключить в настройках приложения, чтобы (де)активировать запуск приложения при загрузке устройства.\n— ПОСТОЯННОЕ УВЕДОМЛЕНИЕ: это разрешение необходимо, поскольку используется приоритетная служба для обеспечения непрерывной работы службы VPN.\n— Приложение не содержит рекламы. Сбор аналитики и данных о сбоях происходят только с явного согласия пользователя при первом использовании приложения." } -} +} \ No newline at end of file diff --git a/assets/translations/strings_zh.i18n.json b/assets/translations/strings_zh.i18n.json index d7610172..a1ec9bd4 100644 --- a/assets/translations/strings_zh.i18n.json +++ b/assets/translations/strings_zh.i18n.json @@ -106,11 +106,13 @@ }, "logs": { "pageTitle": "日志", - "clearLogsButtonText": "清除日志", "filterHint": "筛选", "allLevelsFilter": "全部", "shareCoreLogs": "分享核心日志", - "shareAppLogs": "分享日志" + "shareAppLogs": "分享日志", + "pauseTooltip": "暂停", + "resumeTooltip": "恢复", + "clearTooltip": "清除" }, "settings": { "pageTitle": "设置", @@ -123,17 +125,18 @@ "regions": { "ir": "伊朗 (ir)", "cn": "中国 (cn)", + "ru": "俄罗斯 (ru)", "other": "其他" }, "themeMode": "主题模式", "themeModes": { "system": "遵循系统主题", "dark": "深色模式", - "light": "灯光模式" + "light": "灯光模式", + "black": "黑色模式" }, "enableAnalytics": "启用分析", "enableAnalyticsMsg": "授予收集分析并发送崩溃报告以改进应用程序的权限", - "trueBlack": "纯黑", "autoStart": "开机启动", "silentStart": "无声启动", "openWorkingDir": "打开工作目录", @@ -257,4 +260,4 @@ "short_description": "自动,SSH, VLESS, Vmess, Trojan, Reality, Sing-Box, Clash, Xray, Shadowsocks", "full_description": "HiddifyNext 的主要目标是提供安全、用户友好且高效的隧道客户端。它使您能够利用 VPN 服务权限将所有流量或选定的应用程序流量路由到您选择的远程服务器。\n\n注:我们不提供任何服务器;用户需要使用自己的自托管服务器或受信任的服务器来确保其在线活动的私密性。\n \n我们支持以下类型的服务器:\n- 普通V2ray/Xray订阅链接\n- Clash订阅链接\n- Sing-Box 订阅链接\n\n我们的独特特点是什么?\n\n-用户友好\n-优化和高速\n-自动选择最低延迟\n-显示用户使用信息\n-通过一键深度链接轻松导入子链接\n-免费且无广告\n-轻松切换用户子链接\n-等等\n\n支持:\n- Sing-Box 支持的所有协议\n- VLESS + xtls 现实、愿景\n- VMESS\n- Trojan\n- ShoadowSocks\n- Reality\n-V2ray\n- Hystria2\n- TUIC\n- SSH\n- ShadowTLS\n\n\n源代码位于https://github.com/hiddify/Hiddify-Next\n应用程序核心基于开源的Sing-Box。\n\n权限说明:\n\nVPN服务:由于此应用程序的目标是提供安全、用户友好和高效的隧道客户端,我们需要此权限以能够通过隧道将流量路由到远程服务器。\n查询所有包:此权限用于允许用户包括或排除特定应用程序以进行隧道传输。\n接收启动完成:此权限可以从应用程序设置中启用或禁用,以在设备启动时激活此应用程序。\n发送通知:此权限是必需的,因为我们使用前台服务来确保VPN服务的持续运行。\n此应用程序没有广告。分析和崩溃数据仅在用户在首次使用应用程序时明确同意的情况下发生。" } -} +} \ No newline at end of file diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 1ead2d96..00000000 --- a/changelog.md +++ /dev/null @@ -1,1265 +0,0 @@ -# Changelog - - -## 0.9.2 (2023-10-15) - -#### Other - -* Fix ndk setup. - -* Fix android arm bug. - - - -## v0.9.1 (2023-10-15) - -#### Other - -* Update README_cn.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Delete docs/file. - -* Google play badge. - -* Delete docs/google-play-badge1.png. - -* Upload google play badge. - -* Create file. - -* Delete docs/google-play-badge.png. - -* Fix ci. - -* Change ndk setup. - -* Update dependencies. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_ru.md. - -* Update README_ru.md. - -* Change desktop error handling. - -* Fix android bugs. - -* Update README.md. - -* Update README_cn.md. - -* Update README_ru.md. - -* Update README_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Delete Russian_Flag.png. - -* Add Russian flag. - -* Delete docs/file. - -* Create README_ru.md. - -* Delete REAMME_ru.md. - -* Create REAMME_ru.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_cn.md. - -* Update README_cn.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update file. - -* Uplaod a badge for google play. - -* Delete google-play-badge.png. - -* Add files via upload. - -* Create file. - -* Delete docs/google-play-badge.png. - -* Delete google-play-badge.png. - -* Add files via upload. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Create Chinese README_cn.md. - - - -## v0.8.12 (2023-10-13) - -#### Fix - -* Typo. - -* Bug. - -* Release names. - -#### Other - -* Update readme. - -* Update core. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Merge pull request #56 from solokot/main. - _Improvement of Russian translation_ - -* Improvement of Russian translation. - _Basically a replacement for machine automatic translation_ - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Fix bugs. - -* Change android signing. - -* Update README.md. - -* Update readme. - -* Update contribution guide. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update release_message.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update release template. - - - -## v0.8.11 (2023-10-08) - -#### Changes - -* Remove auto release message. - - - -## v0.8.10 (2023-10-08) - -#### Fix - -* Release changelog. - - - -## v0.8.9 (2023-10-08) - -#### Fix - -* Missing libs. - - - -## v0.8.8 (2023-10-08) - -#### Fix - -* Release bug. - - - -## v0.8.7 (2023-10-08) - -#### Fix - -* Release message. - - - -## v0.8.6 (2023-10-08) - -#### Fix - -* Windows build. - -* Build issue. - -#### Other - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Update release_message.md. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Update release_message.md. - -* Update README.md. - -* Update README_fa.md. - -* Delete docs/note. - -* Add files via upload. - -* Create note. - -* Update contribute.md. - -* Update README.md. - -* Delete assets/images/google-play-badge.png. - -* Update README_fa.md. - -* Update README.md. - -* Update release_message.md. - -* Update release_message.md. - -* Update release_message.md. - -* Fix build. - - - -## v0.8.5 (2023-10-07) - -#### Fix - -* Bug. - - - -## v0.8.4 (2023-10-07) - -#### Fix - -* Translate. - - - -## v0.8.3 (2023-10-07) - -#### Other - -* Add release message and help. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Fix bugs. - -* Update release_message.md. - -* Update release_message.md. - -* Update README_fa.md. - -* Update README.md. - -* Update release_message.md. - -* Update release_message.md. - -* Create release_message.md. - -* Add google play badge to assets. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Create contribute.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README_fa.md. - -* Add feature request template. - -* Fix issue template. - -* Add issue template. - - - -## v0.8.2 (2023-10-07) - -#### Fix - -* Hysteria2 and some links. - -#### Other - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - -* Create README_fa.md. - -* Update README.md. - -* Update README.md. - -* Update README.md. - _Add some features to the readme_ - -* Add debug export to clipboard. - - - -## v0.8.1 (2023-10-06) - -#### New - -* Add chinese lang. - -#### Fix - -* Chinese translation. - -#### Other - -* Update core 0.5.1. - -* Fix floating number sub info header. - -* Add russian. - -* Add google play descriptions. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - - - -## v0.8.0 (2023-10-05) - -#### New - -* Add russian lang. - -#### Other - -* Add proxy tag sanitization. - -* Fix bugs. - -* Add ignore app update version. - -* Add new protocols to link parser. - -* Auto update translations on release. - -* Update translation. - -* Remove param in ru translation. - - - -## v0.7.2 (2023-10-04) - -#### Other - -* Fix bugs. - - - -## v0.7.1 (2023-10-03) - -#### Other - -* Fix log and analytics bugs. - - - -## v0.7.0 (2023-10-03) - -#### New - -* Add support of some exception panel with zero usage. - -#### Other - -* Fix translation bug. - -* Improve error handling and presentation. - -* Add retry for network ops. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Add local profile. - -* Add auto translator. - -* Add auto translate. - - - -## v0.6.0 (2023-09-30) - -#### Other - -* Fix minor bugs. - -* Add scheduled profile update. - -* Update dependencies. - -* Refactor profile details page. - - - -## v0.5.11 (2023-09-22) - -#### Other - -* Fix android bundle abi. - -* Bump core version. - - - -## v0.5.10 (2023-09-22) - -#### Other - -* Fix minor bugs. - -* Fix profile update bug. - - - -## v0.5.9 (2023-09-22) - -#### Other - -* Add build number. - - - -## v0.5.8 (2023-09-22) - -#### New - -* Add crashlytics. - -* Add support for base64 sublink for header from content. - -* Add profile headers from comments in response! good for hosting in github and show information. - -* Automated version release. - -* Send release versions only to play market add pre-release version. - -#### Changes - -* Change invalid dns 235.5.5.5 to 8.8.8.8. - -#### Fix - -* Improve routing accessibility and logs. - -* Minor bugs. - -* Prefs persistence. - -* Crashlytics. - -* App update url. - -* Small profiles. - -* Makefile vars. - -* Adaptive icon. - -* Pre-release. - -* Typo in adaptive icon. - -* If .dev is exist in the version do not show update needed. - -* Keep the link as it is. fix the issue with & - -* Dependency issue. - -* Remove extra print. - -* Bug in get headers from body. - -* Bug ini ci to google play. - -* Tag version issue. - -* Ci bug. - -* Remove comments. - -* Bug. - -#### Other - -* Fix ci. - -* Fix false-positive error reports. - -* Change build setup. - -* Fix minor bugs. - -* Refactor app update. - -* Fix sentry dart plugin upload. - -* Fix ci debug symbols upload. - -* Add sentry provider observer. - -* Ci: add sentry debug info upload. - -* Update dependencies and general fixes. - -* Chore: bump agp version. - -* Ci: fix env. - -* Ci: add dsn env. - -* Feat: add region and terms to intro. - -* Update ci.yml. - -* Build: add sentry dsn. - -* Feat: add intro screen. - -* Feat: add sentry. - -* Ci: bump macos version. - -* Feat: update profile when adding preexisting url. - -* Publish draft even with error. - -* Update version of core. - -* Merge branch 'main' of hiddify-github:hiddify/hiddify-next. - -* Add firebase. - -* Update translation. - -* Refactor: version presentation. - -* Perf: improve header parser. - -* Feat: remove check for updates in market releases. - -* Better manage the market release. - -* Update. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Improve accessability. - -* Fix per-app proxy selection. - -* Add android per-app proxy. - -* Add basic flavors. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update README.md. - -* Add accessability semantics. - -* Add support for fragment in the url. - - - -## v0.1.0 (2023-09-11) - -#### New - -* Add signing to android. - -* Add android universal build also. - -* Add change in network entitlements. - -* Change logo icon to next. - -* Add cache for speed up build process. - -* Add windows portable version. - -* Add libclash. - -* Add as draft release. - -* Make better ci and building applications. - -#### Changes - -* Change windows logo. - -* Change macos build to flutter_distributor. - -* Remove x86 builds since flutter does not support. - -* Add x64 to the name. - -* Update makefile. - -#### Fix - -* Space bugs in some panels. - -* Aab. - -* Aab build. - -* Universal sign. - -* KeystoreProperties. - -* Name issue. - -* Revert package name change. - -* Name. - -* Geosite download. - -* Windows portable bug. - -* Change name bug. - -* Setup exe files. - -* Libclash.so. - -* Linux AppImage. - -* Error. - -* Category in linux. - -* Add fuse for linux. - -* Icon. - -* Linux logo. - -* Android. - -* Makefile error. - -#### Other - -* Release v0.1.0. - -* Fix ci build. - -* Update ci. - -* Add core version. - -* Fix barrel file. - -* Change proxies lifecycle. - -* Add service restart. - -* Remove notification service. - -* Change android notification permission. - -* Add android service restart. - -* Change mark new profile active. - -* Handle unlimited. - -* Update upload download link stats. - -* Update Translations. - -* Remove // TODO add content disposition parsing. - -* Add content-disposition for profile title. - -* Update README.md. - -* Add hysteria2. - -* Fix android build connection. - -* Add android connection shortcut. - -* Add desktop autostart. - -* Add android boot receiver. - -* Add android proxy service. - -* Add android battery optimizations settings. - -* Change sharedpreferences to unify with android. - -* Add vclibs. - -* Update dependencies. - -* Add submodule. - -* Fix translation code gen. - -* Change prefs. - -* Change default config options. - -* Remove string casing. - -* Update ci.yml. - -* Remove caching. - -* Change core prefs to use code generation. - -* Fix custom lint. - -* Fix general issues. - -* Remove vclibs. - -* Update README.md. - -* Fix blank screen. - -* Add proxies sort. - -* Refactor preferences. - -* Update dependencies. - -* Add more config options to settings. - -* Add tun implementation option. - -* Add android power manager. - -* Add android quick tile. - -* Fix macos dependencies. - -* Fix mac build. - -* Fix build. - -* Change system tray icon. - -* Fix riverpod code generation. - -* Remove unnecessary config options. - -* Add accessability semantics. - -* Update dependencies. - -* Add config options. - -* Add pref utilities. - -* Add stats overview. - -* Update Translations. - -* Fix android outbounds view. - -* Remove unnecessary options. - -* Change proxies flow. - -* Add android command client support. - -* Update README.md. - -* Add status command receiver. - -* Add hiddify deeplink. - -* Add macos deeplink support. - -* Add singbox deeplink. - -* Change error prompts. - -* Fix url parser. - -* Remove unnecessary prefs. - -* Update dependencies. - -* Make AppImage zipped for preserving permission in linux. - -* Add user agent. - -* Fix uri launch. - -* Update ci flutter version. - -* Fix macos silent start. - -* Change proxies page. - -* Fix desktop connection error msg. - -* Add button tooltips. - -* Fix logging. - -* Create dependabot.yml. - -* Create CODE_OF_CONDUCT.md. - -* Fix aab. - -* Add aab file. - -* Change windows-portable name to HiddifyNext-portable. - -* Add debug mode. - -* Add misc settings ui. - -* Remote unnecessary logs. - -* Add misc preferences. - -* Add directory options. - -* Fix linux build. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Fix android builds. - -* Fix android splash screen. - -* Update icons. - -* Fix windows executable build. - -* Remove unnecessary build steps. - -* Remove build number from appbar. - -* Update readme. - -* Change desktop directories. - -* Remove desktop notifications. - -* Force same version code for all platforms. - -* Add more logs. - -* Add log timestamp. - -* Update icons. - -* Remove drawer branding. - -* Add download section in readme. - -* Fix name of universal apk. - -* Fix order. - -* Update readme. - -* Update logging. - -* Update geo assets url. - -* Change linux directories. - -* Update icon. - -* Update icon. - -* Update logo for all platforms. - -* Revert name in macos. - -* Temporary disable app sandbox. - -* Change name. - -* Update Release.entitlements to fix binding issue. - -* Merge pull request #5 from evstegneych/main. - _add macos option_ - -* Add macos option. - -* Update ci flutter version. - -* Update kotlin version. - -* Update other dependencies. - -* Update dependencies. - -* Update flutter version. - -* Update LICENSE.md. - -* Add build option for ios but not tested. - -* Return: build for all. - -* Fix build for macos. - -* Fix ci. - -* Migrate to singbox. - -* Fix routing. - -* Fix connection button text casing. - -* Add update checking. - -* Add separate page for clash overrides. - -* Add version number to appbar. - -* Add more icons. - -* Add sort limited profiles last. - -* Fix profile sort icon. - -* Fix profile traffic ratio. - -* Add profiles sort option. - -* Refactor profile addition flow. - -* Add extra profile metadata. - -* Fix linux deep link service. - -* Refactor profile tile. - -* Add locale based font. - -* Fix linux startup. - -* Update dependencies. - -* Add about page. - -* Fix linux notifications. - -* Build all. - -* Hard coding. - -* Add png. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update ci.yml. - -* Update Makefile. - -* Update ci.yml. - -* Update Makefile. - -* Fix makefile. - -* Fix actions. - -* Fix actions. - -* Fix actions. - -* Update build setup. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Create LICENSE.md. - -* Add core submodule. - -* Remove core libs. - -* Update Makefile. - -* Update Makefile. - -* Remove c install. - -* Update make. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Add Persian font. - -* Update dependencies. - -* Update to libclash. - -* Add cache. - -* Add: new action. - -* Add write permission. - -* Fix. - -* Modify. - -* Fix. - -* Update. - -* Fix android build. - -* Fix code gen bug. - -* Update ci.yml. - -* Update ci.yml. - -* Fix CI. - -* Fix CI. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Update ci.yml. - -* Update ci.yml. - -* Fix CI. - -* Update ci.yml. - -* Update ci.yml. - -* Add CI. - -* Add silent start for desktop. - -* Update readme. - -* Add headless mode to desktop. - -* Update dependencies. - -* Add distribution setup for windows. - -* Add Farsi(fa) language. - -* Merge branch 'main' of https://github.com/hiddify/hiddify-next. - -* Initial commit. - -* Initial. - - - diff --git a/lib/core/app/app_view.dart b/lib/core/app/app_view.dart index 69549dc6..35129467 100644 --- a/lib/core/app/app_view.dart +++ b/lib/core/app/app_view.dart @@ -33,7 +33,7 @@ class AppView extends HookConsumerWidget with PresLogger { supportedLocales: AppLocaleUtils.supportedLocales, localizationsDelegates: GlobalMaterialLocalizations.delegates, debugShowCheckedModeBanner: false, - themeMode: theme.mode, + themeMode: theme.mode.flutterThemeMode, theme: theme.light(), darkTheme: theme.dark(), title: Constants.appName, diff --git a/lib/core/core_providers.dart b/lib/core/core_providers.dart index 9cd598ff..35ce17e4 100644 --- a/lib/core/core_providers.dart +++ b/lib/core/core_providers.dart @@ -19,6 +19,5 @@ TranslationsEn translations(TranslationsRef ref) => @Riverpod(keepAlive: true) AppTheme theme(ThemeRef ref) => AppTheme( ref.watch(themeModeNotifierProvider), - ref.watch(trueBlackThemeNotifierProvider), ref.watch(localeNotifierProvider).preferredFontFamily, ); diff --git a/lib/core/prefs/app_theme.dart b/lib/core/prefs/app_theme.dart index 8e6c8601..786be4f5 100644 --- a/lib/core/prefs/app_theme.dart +++ b/lib/core/prefs/app_theme.dart @@ -1,16 +1,38 @@ import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; +import 'package:hiddify/core/prefs/locale_prefs.dart'; + +enum AppThemeMode { + system, + light, + dark, + black; + + String present(TranslationsEn t) => switch (this) { + system => t.settings.general.themeModes.system, + light => t.settings.general.themeModes.light, + dark => t.settings.general.themeModes.dark, + black => t.settings.general.themeModes.black, + }; + + ThemeMode get flutterThemeMode => switch (this) { + system => ThemeMode.system, + light => ThemeMode.light, + dark => ThemeMode.dark, + black => ThemeMode.dark, + }; + + bool get trueBlack => this == black; +} // mostly exact copy of flex color scheme 7.1's fabulous 12 theme class AppTheme { AppTheme( this.mode, - this.trueBlack, this.fontFamily, ); - final ThemeMode mode; - final bool trueBlack; + final AppThemeMode mode; final String fontFamily; ThemeData light() { @@ -81,7 +103,7 @@ class AppTheme { useMaterial3: true, swapLegacyOnMaterial3: true, useMaterial3ErrorColors: true, - darkIsTrueBlack: trueBlack, + darkIsTrueBlack: mode.trueBlack, surfaceMode: FlexSurfaceMode.highScaffoldLowSurface, // blendLevel: 1, subThemesData: const FlexSubThemesData( diff --git a/lib/core/prefs/theme_prefs.dart b/lib/core/prefs/theme_prefs.dart index 8a0eca45..a9f21b3b 100644 --- a/lib/core/prefs/theme_prefs.dart +++ b/lib/core/prefs/theme_prefs.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:hiddify/core/prefs/app_theme.dart'; import 'package:hiddify/data/data_providers.dart'; import 'package:hiddify/utils/pref_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -10,29 +10,15 @@ class ThemeModeNotifier extends _$ThemeModeNotifier { late final _pref = Pref( ref.watch(sharedPreferencesProvider), "theme_mode", - ThemeMode.system, - mapFrom: ThemeMode.values.byName, + AppThemeMode.system, + mapFrom: AppThemeMode.values.byName, mapTo: (value) => value.name, ); @override - ThemeMode build() => _pref.getValue(); + AppThemeMode build() => _pref.getValue(); - Future update(ThemeMode value) { - state = value; - return _pref.update(value); - } -} - -@Riverpod(keepAlive: true) -class TrueBlackThemeNotifier extends _$TrueBlackThemeNotifier { - late final _pref = - Pref(ref.watch(sharedPreferencesProvider), "true_black_theme", false); - - @override - bool build() => _pref.getValue(); - - Future update(bool value) { + Future update(AppThemeMode value) { state = value; return _pref.update(value); } diff --git a/lib/data/repository/config_options_store.dart b/lib/data/repository/config_options_store.dart index afc10987..fb7b9432 100644 --- a/lib/data/repository/config_options_store.dart +++ b/lib/data/repository/config_options_store.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_manual_providers_as_generated_provider_dependency import 'package:hiddify/core/prefs/prefs.dart'; -import 'package:hiddify/domain/singbox/config_options.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/utils/pref_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -67,6 +67,42 @@ final enableTunStore = PrefNotifier.provider("enable-tun", _default.enableTun); final setSystemProxyStore = PrefNotifier.provider("set-system-proxy", _default.setSystemProxy); +// HACK temporary +@riverpod +List rules(RulesRef ref) => switch (ref.watch(regionNotifierProvider)) { + Region.ir => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.ir", + ip: "geoip:ir", + outbound: RuleOutbound.bypass, + ), + ], + Region.cn => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.cn,geosite:cn", + ip: "geoip:cn", + outbound: RuleOutbound.bypass, + ), + ], + Region.ru => [ + const Rule( + id: "id", + name: "name", + enabled: true, + domains: "domain:.ru", + ip: "geoip:ru", + outbound: RuleOutbound.bypass, + ), + ], + _ => [], + }; + @riverpod ConfigOptions configOptions(ConfigOptionsRef ref) => ConfigOptions( executeConfigAsIs: @@ -88,4 +124,5 @@ ConfigOptions configOptions(ConfigOptionsRef ref) => ConfigOptions( clashApiPort: ref.watch(clashApiPortStore), enableTun: ref.watch(enableTunStore), setSystemProxy: ref.watch(setSystemProxyStore), + rules: ref.watch(rulesProvider), ); diff --git a/lib/data/repository/core_facade_impl.dart b/lib/data/repository/core_facade_impl.dart index b344809f..fa89af87 100644 --- a/lib/data/repository/core_facade_impl.dart +++ b/lib/data/repository/core_facade_impl.dart @@ -179,10 +179,21 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade { } @override - Stream> watchLogs() { - return singbox - .watchLogs(filesEditor.coreLogsPath) - .handleExceptions(CoreServiceFailure.unexpected); + Stream>> watchLogs() { + return singbox.watchLogs(filesEditor.coreLogsPath).handleExceptions( + (error, stackTrace) { + loggy.warning("error watching logs", error, stackTrace); + return CoreServiceFailure.unexpected(error, stackTrace); + }, + ); + } + + @override + TaskEither clearLogs() { + return exceptionHandler( + () => singbox.clearLogs().mapLeft(CoreServiceFailure.other).run(), + CoreServiceFailure.unexpected, + ); } @override diff --git a/lib/domain/singbox/box_log.dart b/lib/domain/singbox/box_log.dart new file mode 100644 index 00000000..84ee25c1 --- /dev/null +++ b/lib/domain/singbox/box_log.dart @@ -0,0 +1,62 @@ +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tint/tint.dart'; + +part 'box_log.freezed.dart'; + +enum LogLevel { + trace, + debug, + info, + warn, + error, + fatal, + panic; + + static List get choices => values.takeFirst(4); + + Color? get color => switch (this) { + trace => Colors.lightBlueAccent, + debug => Colors.grey, + info => Colors.lightGreen, + warn => Colors.orange, + error => Colors.redAccent, + fatal => Colors.red, + panic => Colors.red, + }; +} + +@freezed +class BoxLog with _$BoxLog { + const factory BoxLog({ + LogLevel? level, + DateTime? time, + required String message, + }) = _BoxLog; + + factory BoxLog.parse(String log) { + log = log.strip(); + DateTime? time; + if (log.length > 25) { + time = DateTime.tryParse(log.substring(6, 25)); + } + if (time != null) { + log = log.substring(26); + } + final level = LogLevel.values.firstOrNullWhere( + (e) { + if (log.startsWith(e.name.toUpperCase())) { + log = log.removePrefix(e.name.toUpperCase()); + return true; + } + return false; + }, + ); + return BoxLog( + level: level, + time: time, + message: log.trim(), + ); + } +} diff --git a/lib/domain/singbox/config_options.dart b/lib/domain/singbox/config_options.dart index 1890d8a9..4206a840 100644 --- a/lib/domain/singbox/config_options.dart +++ b/lib/domain/singbox/config_options.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/prefs/prefs.dart'; +import 'package:hiddify/domain/singbox/box_log.dart'; +import 'package:hiddify/domain/singbox/rules.dart'; import 'package:hiddify/utils/platform_utils.dart'; part 'config_options.freezed.dart'; @@ -19,13 +21,13 @@ class ConfigOptions with _$ConfigOptions { @Default(IPv6Mode.disable) IPv6Mode ipv6Mode, @Default("tcp://8.8.8.8") String remoteDnsAddress, @Default(DomainStrategy.auto) DomainStrategy remoteDnsDomainStrategy, - @Default("8.8.8.8") String directDnsAddress, + @Default("local") String directDnsAddress, @Default(DomainStrategy.auto) DomainStrategy directDnsDomainStrategy, @Default(2334) int mixedPort, @Default(6450) int localDnsPort, @Default(TunImplementation.mixed) TunImplementation tunImplementation, @Default(9000) int mtu, - @Default("https://www.gstatic.com/generate_204") String connectionTestUrl, + @Default("http://cp.cloudflare.com/") String connectionTestUrl, @IntervalConverter() @Default(Duration(minutes: 10)) Duration urlTestInterval, @@ -33,6 +35,9 @@ class ConfigOptions with _$ConfigOptions { @Default(6756) int clashApiPort, @Default(false) bool enableTun, @Default(true) bool setSystemProxy, + @Default(false) bool bypassLan, + @Default(false) bool enableFakeDns, + List? rules, }) = _ConfigOptions; static ConfigOptions initial = ConfigOptions( @@ -49,13 +54,6 @@ class ConfigOptions with _$ConfigOptions { _$ConfigOptionsFromJson(json); } -enum LogLevel { - warn, - info, - debug, - trace, -} - @JsonEnum(valueField: 'key') enum IPv6Mode { disable("ipv4_only"), diff --git a/lib/domain/singbox/rules.dart b/lib/domain/singbox/rules.dart index f30f024f..96c53c9e 100644 --- a/lib/domain/singbox/rules.dart +++ b/lib/domain/singbox/rules.dart @@ -1,5 +1,40 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/prefs/locale_prefs.dart'; +part 'rules.freezed.dart'; +part 'rules.g.dart'; + +@freezed +class Rule with _$Rule { + @JsonSerializable(fieldRename: FieldRename.kebab) + const factory Rule({ + required String id, + required String name, + @Default(false) bool enabled, + String? domains, + String? ip, + String? port, + String? protocol, + @Default(RuleNetwork.tcpAndUdp) RuleNetwork network, + @Default(RuleOutbound.proxy) RuleOutbound outbound, + }) = _Rule; + + factory Rule.fromJson(Map json) => _$RuleFromJson(json); +} + +enum RuleOutbound { proxy, bypass, block } + +@JsonEnum(valueField: 'key') +enum RuleNetwork { + tcpAndUdp(""), + tcp("tcp"), + udp("udp"); + + const RuleNetwork(this.key); + + final String? key; +} + enum PerAppProxyMode { off, include, @@ -26,11 +61,13 @@ enum PerAppProxyMode { enum Region { ir, cn, + ru, other; String present(TranslationsEn t) => switch (this) { ir => t.settings.general.regions.ir, cn => t.settings.general.regions.cn, + ru => t.settings.general.regions.ru, other => t.settings.general.regions.other, }; } diff --git a/lib/domain/singbox/singbox.dart b/lib/domain/singbox/singbox.dart index 39bb5774..ea665c8f 100644 --- a/lib/domain/singbox/singbox.dart +++ b/lib/domain/singbox/singbox.dart @@ -1,3 +1,4 @@ +export 'box_log.dart'; export 'config_options.dart'; export 'core_status.dart'; export 'outbounds.dart'; diff --git a/lib/domain/singbox/singbox_facade.dart b/lib/domain/singbox/singbox_facade.dart index bcd40f80..6a8d9541 100644 --- a/lib/domain/singbox/singbox_facade.dart +++ b/lib/domain/singbox/singbox_facade.dart @@ -37,5 +37,7 @@ abstract interface class SingboxFacade { Stream> watchCoreStatus(); - Stream> watchLogs(); + Stream>> watchLogs(); + + TaskEither clearLogs(); } diff --git a/lib/features/common/common_controllers.dart b/lib/features/common/common_controllers.dart index 8cfbb413..db6e7e2c 100644 --- a/lib/features/common/common_controllers.dart +++ b/lib/features/common/common_controllers.dart @@ -2,7 +2,6 @@ import 'package:hiddify/core/prefs/general_prefs.dart'; import 'package:hiddify/features/common/app_update_notifier.dart'; import 'package:hiddify/features/common/connectivity/connectivity_controller.dart'; import 'package:hiddify/features/common/window/window_controller.dart'; -import 'package:hiddify/features/logs/notifier/notifier.dart'; import 'package:hiddify/features/profiles/notifier/notifier.dart'; import 'package:hiddify/features/system_tray/controller/system_tray_controller.dart'; import 'package:hiddify/services/service_providers.dart'; @@ -24,10 +23,6 @@ void commonControllers(CommonControllersRef ref) { }, fireImmediately: true, ); - ref.listen( - logsNotifierProvider, - (previous, next) {}, - ); ref.listen( connectivityControllerProvider, (previous, next) {}, diff --git a/lib/features/logs/notifier/logs_notifier.dart b/lib/features/logs/notifier/logs_notifier.dart index 6c7cfd43..b089840d 100644 --- a/lib/features/logs/notifier/logs_notifier.dart +++ b/lib/features/logs/notifier/logs_notifier.dart @@ -1,66 +1,133 @@ import 'dart:async'; -import 'package:dartx/dartx.dart'; import 'package:hiddify/data/data_providers.dart'; -import 'package:hiddify/domain/clash/clash.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/features/logs/notifier/logs_state.dart'; +import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:rxdart/rxdart.dart'; part 'logs_notifier.g.dart'; -// TODO: rewrite -@Riverpod(keepAlive: true) +@riverpod class LogsNotifier extends _$LogsNotifier with AppLogger { - static const maxLength = 1000; - @override - Stream build() { - state = const AsyncData(LogsState()); - return ref.read(coreFacadeProvider).watchLogs().asyncMap( - (event) async { - _logs = [ - event.getOrElse((l) => throw l), - ..._logs.takeFirst(maxLength - 1), - ]; - return switch (state) { - // ignore: unused_result - AsyncData(:final value) => value.copyWith(logs: await _computeLogs()), - _ => LogsState(logs: await _computeLogs()), - }; + LogsState build() { + ref.disposeDelay(const Duration(seconds: 20)); + state = const LogsState(); + ref.onDispose( + () { + loggy.debug("disposing"); + _listener?.cancel(); + _listener = null; }, ); + ref.onCancel( + () { + if (_listener?.isPaused != true) { + loggy.debug("pausing"); + _listener?.pause(); + } + }, + ); + ref.onResume( + () { + if (!state.paused && (_listener?.isPaused ?? false)) { + loggy.debug("resuming"); + _listener?.resume(); + } + }, + ); + + _addListeners(); + return const LogsState(); } - var _logs = []; + StreamSubscription? _listener; + + Future _addListeners() async { + loggy.debug("adding listeners"); + await _listener?.cancel(); + _listener = ref + .read(coreFacadeProvider) + .watchLogs() + .throttle( + (_) => Stream.value(_listener?.isPaused ?? false), + leading: false, + trailing: true, + ) + .throttleTime( + const Duration(milliseconds: 250), + leading: false, + trailing: true, + ) + .asyncMap( + (event) async { + await event.fold( + (f) { + _logs = []; + state = state.copyWith(logs: AsyncError(f, StackTrace.current)); + }, + (a) async { + _logs = a.reversed; + state = state.copyWith(logs: AsyncData(await _computeLogs())); + }, + ); + }, + ).listen((event) {}); + } + + Iterable _logs = []; final _debouncer = CallbackDebouncer(const Duration(milliseconds: 200)); LogLevel? _levelFilter; String _filter = ""; - Future> _computeLogs() async { - if (_levelFilter == null && _filter.isEmpty) return _logs; - return _logs.where((e) { - return _filter.isEmpty || e.contains(_filter); + Future> _computeLogs() async { + final logs = _logs.map(BoxLog.parse); + if (_levelFilter == null && _filter.isEmpty) return logs.toList(); + return logs.where((e) { + return (_filter.isEmpty || e.message.contains(_filter)) && + (_levelFilter == null || + e.level == null || + e.level!.index >= _levelFilter!.index); }).toList(); } - void clear() { - if (state case AsyncData(:final value)) { - state = AsyncData(value.copyWith(logs: [])).copyWithPrevious(state); - } + void pause() { + loggy.debug("pausing"); + _listener?.pause(); + state = state.copyWith(paused: true); + } + + void resume() { + loggy.debug("resuming"); + _listener?.resume(); + state = state.copyWith(paused: false); + } + + Future clear() async { + loggy.debug("clearing"); + await ref.read(coreFacadeProvider).clearLogs().match( + (l) { + loggy.warning("error clearing logs", l); + }, + (_) { + _logs = []; + state = state.copyWith(logs: const AsyncData([])); + }, + ).run(); } void filterMessage(String? filter) { _filter = filter ?? ''; _debouncer( () async { - if (state case AsyncData(:final value)) { - state = AsyncData( - value.copyWith( - filter: _filter, - logs: await _computeLogs(), - ), - ).copyWithPrevious(state); + if (state.logs case AsyncData()) { + state = state.copyWith( + filter: _filter, + logs: AsyncData(await _computeLogs()), + ); } }, ); @@ -68,13 +135,11 @@ class LogsNotifier extends _$LogsNotifier with AppLogger { Future filterLevel(LogLevel? level) async { _levelFilter = level; - if (state case AsyncData(:final value)) { - state = AsyncData( - value.copyWith( - levelFilter: _levelFilter, - logs: await _computeLogs(), - ), - ).copyWithPrevious(state); + if (state.logs case AsyncData()) { + state = state.copyWith( + levelFilter: _levelFilter, + logs: AsyncData(await _computeLogs()), + ); } } } diff --git a/lib/features/logs/notifier/logs_state.dart b/lib/features/logs/notifier/logs_state.dart index ba34a075..4318870a 100644 --- a/lib/features/logs/notifier/logs_state.dart +++ b/lib/features/logs/notifier/logs_state.dart @@ -1,5 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/domain/clash/clash.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'logs_state.freezed.dart'; @@ -8,7 +9,8 @@ class LogsState with _$LogsState { const LogsState._(); const factory LogsState({ - @Default([]) List logs, + @Default(AsyncLoading()) AsyncValue> logs, + @Default(false) bool paused, @Default("") String filter, LogLevel? levelFilter, }) = _LogsState; diff --git a/lib/features/logs/view/logs_page.dart b/lib/features/logs/view/logs_page.dart index af2e7954..7c5d093c 100644 --- a/lib/features/logs/view/logs_page.dart +++ b/lib/features/logs/view/logs_page.dart @@ -1,31 +1,31 @@ -import 'package:dartx/dartx.dart'; + import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fpdart/fpdart.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; -import 'package:hiddify/domain/clash/clash.dart'; import 'package:hiddify/domain/failures.dart'; -import 'package:hiddify/features/common/common.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/features/logs/notifier/notifier.dart'; import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:tint/tint.dart'; - class LogsPage extends HookConsumerWidget with PresLogger { const LogsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); - final asyncState = ref.watch(logsNotifierProvider); + final state = ref.watch(logsNotifierProvider); final notifier = ref.watch(logsNotifierProvider.notifier); final debug = ref.watch(debugModeNotifierProvider); final filesEditor = ref.watch(filesEditorServiceProvider); + final filterController = useTextEditingController(text: state.filter); + final List popupButtons = debug || PlatformUtils.isDesktop ? [ PopupMenuItem( @@ -49,115 +49,146 @@ class LogsPage extends HookConsumerWidget with PresLogger { ] : []; - switch (asyncState) { - case AsyncData(value: final state): - return Scaffold( - appBar: AppBar( - // TODO: fix height - toolbarHeight: 90, - title: Text(t.logs.pageTitle), - actions: [ - if (popupButtons.isNotEmpty) - PopupMenuButton( - itemBuilder: (context) { - return popupButtons; - }, - ), - ], - bottom: PreferredSize( - preferredSize: const Size.fromHeight(36), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - children: [ - Flexible( - child: TextFormField( - onChanged: notifier.filterMessage, - decoration: InputDecoration( - isDense: true, - hintText: t.logs.filterHint, - ), - ), + return Scaffold( + appBar: AppBar( + // TODO: fix height + toolbarHeight: 90, + title: Text(t.logs.pageTitle), + actions: [ + if (state.paused) + IconButton( + onPressed: notifier.resume, + icon: const Icon(Icons.play_arrow), + tooltip: t.logs.resumeTooltip, + ) + else + IconButton( + onPressed: notifier.pause, + icon: const Icon(Icons.pause), + tooltip: t.logs.pauseTooltip, + ), + IconButton( + onPressed: notifier.clear, + icon: const Icon(Icons.clear_all), + tooltip: t.logs.clearTooltip, + ), + if (popupButtons.isNotEmpty) + PopupMenuButton( + itemBuilder: (context) { + return popupButtons; + }, + ), + ], + bottom: PreferredSize( + preferredSize: const Size.fromHeight(36), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Flexible( + child: TextFormField( + controller: filterController, + onChanged: notifier.filterMessage, + decoration: InputDecoration( + isDense: true, + hintText: t.logs.filterHint, ), - const Gap(16), - DropdownButton>( - value: optionOf(state.levelFilter), - onChanged: (v) { - if (v == null) return; - notifier.filterLevel(v.toNullable()); - }, - padding: const EdgeInsets.symmetric(horizontal: 8), - borderRadius: BorderRadius.circular(4), - items: [ - DropdownMenuItem( - value: none(), - child: Text(t.logs.allLevelsFilter), - ), - ...LogLevel.values.takeFirst(3).map( - (e) => DropdownMenuItem( - value: some(e), - child: Text(e.name), - ), - ), - ], + ), + ), + const Gap(16), + DropdownButton>( + value: optionOf(state.levelFilter), + onChanged: (v) { + if (v == null) return; + notifier.filterLevel(v.toNullable()); + }, + padding: const EdgeInsets.symmetric(horizontal: 8), + borderRadius: BorderRadius.circular(4), + items: [ + DropdownMenuItem( + value: none(), + child: Text(t.logs.allLevelsFilter), + ), + ...LogLevel.choices.map( + (e) => DropdownMenuItem( + value: some(e), + child: Text(e.name), + ), ), ], ), - ), + ], ), ), - body: ListView.builder( - itemCount: state.logs.length, - reverse: true, - itemBuilder: (context, index) { - final log = state.logs[index]; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - subtitle: Text(log.strip()), - ), - if (index != 0) - const Divider( - indent: 16, - endIndent: 16, - height: 4, + ), + ), + body: switch (state.logs) { + AsyncData(value: final logs) => SelectionArea( + child: ListView.builder( + itemCount: logs.length, + reverse: true, + itemBuilder: (context, index) { + final log = logs[index]; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (log.level != null) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + log.level!.name.toUpperCase(), + style: Theme.of(context) + .textTheme + .labelMedium + ?.copyWith(color: log.level!.color), + ), + if (log.time != null) + Text( + log.time!.toString(), + style: + Theme.of(context).textTheme.labelSmall, + ), + ], + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), ), - ], - ); - }, + if (index != 0) + const Divider( + indent: 16, + endIndent: 16, + height: 4, + ), + ], + ); + }, + ), ), - ); - - case AsyncError(:final error): - return Scaffold( - body: CustomScrollView( + AsyncError(:final error) => CustomScrollView( slivers: [ - NestedTabAppBar( - title: Text(t.logs.pageTitle), - ), SliverErrorBodyPlaceholder(t.presentShortError(error)), ], ), - ); - - case AsyncLoading(): - return Scaffold( - body: CustomScrollView( + _ => const CustomScrollView( slivers: [ - NestedTabAppBar( - title: Text(t.logs.pageTitle), - ), - const SliverLoadingBodyPlaceholder(), + SliverLoadingBodyPlaceholder(), ], ), - ); - - // TODO: remove - default: - return const Scaffold(); - } + }, + ); } } diff --git a/lib/features/settings/view/config_options_page.dart b/lib/features/settings/view/config_options_page.dart index a677fd7a..87e804f5 100644 --- a/lib/features/settings/view/config_options_page.dart +++ b/lib/features/settings/view/config_options_page.dart @@ -52,13 +52,13 @@ class ConfigOptionsPage extends HookConsumerWidget { ), ListTile( title: Text(t.settings.config.logLevel), - subtitle: Text(options.logLevel.name), + subtitle: Text(options.logLevel.name.toUpperCase()), onTap: () async { final logLevel = await SettingsPickerDialog( title: t.settings.config.logLevel, selected: options.logLevel, - options: LogLevel.values, - getTitle: (e) => e.name, + options: LogLevel.choices, + getTitle: (e) => e.name.toUpperCase(), resetValue: _default.logLevel, ).show(context); if (logLevel == null) return; diff --git a/lib/features/settings/widgets/advanced_setting_tiles.dart b/lib/features/settings/widgets/advanced_setting_tiles.dart index 82b94bdb..64274c21 100644 --- a/lib/features/settings/widgets/advanced_setting_tiles.dart +++ b/lib/features/settings/widgets/advanced_setting_tiles.dart @@ -6,6 +6,7 @@ import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/core/router/routes/routes.dart'; import 'package:hiddify/domain/singbox/singbox.dart'; +import 'package:hiddify/features/common/common.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class AdvancedSettingTiles extends HookConsumerWidget { @@ -20,6 +21,7 @@ class AdvancedSettingTiles extends HookConsumerWidget { return Column( children: [ + const RegionPrefTile(), ListTile( title: Text(t.settings.config.pageTitle), leading: const Icon(Icons.edit_document), diff --git a/lib/features/settings/widgets/general_setting_tiles.dart b/lib/features/settings/widgets/general_setting_tiles.dart index f8b76249..a66cb81e 100644 --- a/lib/features/settings/widgets/general_setting_tiles.dart +++ b/lib/features/settings/widgets/general_setting_tiles.dart @@ -3,7 +3,6 @@ import 'package:go_router/go_router.dart'; import 'package:hiddify/core/core_providers.dart'; import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/features/common/common.dart'; -import 'package:hiddify/features/settings/widgets/theme_mode_switch_button.dart'; import 'package:hiddify/services/auto_start_service.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -44,31 +43,34 @@ class GeneralSettingTiles extends HookConsumerWidget { ), ListTile( title: Text(t.settings.general.themeMode), - subtitle: Text( - switch (theme.mode) { - ThemeMode.system => t.settings.general.themeModes.system, - ThemeMode.light => t.settings.general.themeModes.light, - ThemeMode.dark => t.settings.general.themeModes.dark, - }, - ), - trailing: ThemeModeSwitch( - themeMode: theme.mode, - onChanged: ref.read(themeModeNotifierProvider.notifier).update, - ), + subtitle: Text(theme.mode.present(t)), leading: const Icon(Icons.light_mode), onTap: () async { - await ref.read(themeModeNotifierProvider.notifier).update( - Theme.of(context).brightness == Brightness.light - ? ThemeMode.dark - : ThemeMode.light, + final selectedThemeMode = await showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: Text(t.settings.general.themeMode), + children: AppThemeMode.values + .map( + (e) => RadioListTile( + title: Text(e.present(t)), + value: e, + groupValue: theme.mode, + onChanged: (e) => context.pop(e), + ), + ) + .toList(), ); + }, + ); + if (selectedThemeMode != null) { + await ref + .read(themeModeNotifierProvider.notifier) + .update(selectedThemeMode); + } }, ), - SwitchListTile( - title: Text(t.settings.general.trueBlack), - value: theme.trueBlack, - onChanged: ref.read(trueBlackThemeNotifierProvider.notifier).update, - ), if (PlatformUtils.isDesktop) ...[ SwitchListTile( title: Text(t.settings.general.autoStart), diff --git a/lib/features/settings/widgets/theme_mode_switch_button.dart b/lib/features/settings/widgets/theme_mode_switch_button.dart deleted file mode 100644 index 580a633b..00000000 --- a/lib/features/settings/widgets/theme_mode_switch_button.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hiddify/core/core_providers.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -class ThemeModeSwitch extends HookConsumerWidget { - const ThemeModeSwitch({ - super.key, - required this.themeMode, - required this.onChanged, - }); - final ThemeMode themeMode; - final ValueChanged onChanged; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final t = ref.watch(translationsProvider); - - final List isSelected = [ - themeMode == ThemeMode.light, - themeMode == ThemeMode.system, - themeMode == ThemeMode.dark, - ]; - - return ToggleButtons( - isSelected: isSelected, - onPressed: (int newIndex) { - if (newIndex == 0) { - onChanged(ThemeMode.light); - } else if (newIndex == 1) { - onChanged(ThemeMode.system); - } else { - onChanged(ThemeMode.dark); - } - }, - children: [ - Icon( - Icons.wb_sunny, - semanticLabel: t.settings.general.themeModes.light, - ), - Icon( - Icons.phone_iphone, - semanticLabel: t.settings.general.themeModes.system, - ), - Icon( - Icons.bedtime, - semanticLabel: t.settings.general.themeModes.dark, - ), - ], - ); - } -} diff --git a/lib/services/singbox/ffi_singbox_service.dart b/lib/services/singbox/ffi_singbox_service.dart index e2f75d9c..8b9b3aff 100644 --- a/lib/services/singbox/ffi_singbox_service.dart +++ b/lib/services/singbox/ffi_singbox_service.dart @@ -8,7 +8,7 @@ import 'package:combine/combine.dart'; import 'package:ffi/ffi.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/domain/connectivity/connectivity.dart'; -import 'package:hiddify/domain/singbox/config_options.dart'; +import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/gen/singbox_generated_bindings.dart'; import 'package:hiddify/services/singbox/shared.dart'; import 'package:hiddify/services/singbox/singbox_service.dart'; @@ -16,6 +16,7 @@ import 'package:hiddify/utils/utils.dart'; import 'package:loggy/loggy.dart'; import 'package:path/path.dart' as p; import 'package:rxdart/rxdart.dart'; +import 'package:watcher/watcher.dart'; final _logger = Loggy('FFISingboxService'); @@ -301,33 +302,47 @@ class FFISingboxService ); } + final _logBuffer = []; + int _logFilePosition = 0; + @override - Stream watchLogs(String path) { - var linesRead = 0; - return Stream.periodic( - const Duration(seconds: 1), - ).asyncMap((_) async { - final result = await _readLogs(path, linesRead); - linesRead = result.$2; - return result.$1; - }).transform( - StreamTransformer.fromHandlers( - handleData: (data, sink) { - for (final item in data) { - sink.add(item); - } - }, - ), - ); + Stream> watchLogs(String path) async* { + yield await _readLogFile(File(path)); + yield* Watcher(path, pollingDelay: const Duration(seconds: 1)) + .events + .asyncMap((event) async { + if (event.type == ChangeType.MODIFY) { + await _readLogFile(File(path)); + } + return _logBuffer; + }); } - Future<(List, int)> _readLogs(String path, int from) async { - return CombineWorker().execute( + @override + TaskEither clearLogs() { + return TaskEither( () async { - final lines = await File(path).readAsLines(); - final to = lines.length; - return (lines.sublist(from), to); + _logBuffer.clear(); + return right(unit); }, ); } + + Future> _readLogFile(File file) async { + if (_logFilePosition == 0 && file.lengthSync() == 0) return []; + final content = + await file.openRead(_logFilePosition).transform(utf8.decoder).join(); + _logFilePosition = file.lengthSync(); + final lines = const LineSplitter().convert(content); + if (lines.length > 300) { + lines.removeRange(0, lines.length - 300); + } + for (final line in lines) { + _logBuffer.add(line); + if (_logBuffer.length > 300) { + _logBuffer.removeAt(0); + } + } + return _logBuffer; + } } diff --git a/lib/services/singbox/mobile_singbox_service.dart b/lib/services/singbox/mobile_singbox_service.dart index 83d1e1cc..aaf2b399 100644 --- a/lib/services/singbox/mobile_singbox_service.dart +++ b/lib/services/singbox/mobile_singbox_service.dart @@ -163,11 +163,18 @@ class MobileSingboxService } @override - Stream watchLogs(String path) { - return _logsChannel.receiveBroadcastStream().map( - (event) { - // loggy.debug("received log: $event"); - return event as String; + Stream> watchLogs(String path) async* { + yield* _logsChannel + .receiveBroadcastStream() + .map((event) => (event as List).map((e) => e as String).toList()); + } + + @override + TaskEither clearLogs() { + return TaskEither( + () async { + await _methodChannel.invokeMethod("clear_logs"); + return right(unit); }, ); } diff --git a/lib/services/singbox/singbox_service.dart b/lib/services/singbox/singbox_service.dart index 5f1c31f4..eb181695 100644 --- a/lib/services/singbox/singbox_service.dart +++ b/lib/services/singbox/singbox_service.dart @@ -48,5 +48,7 @@ abstract interface class SingboxService { Stream watchStats(); - Stream watchLogs(String path); + Stream> watchLogs(String path); + + TaskEither clearLogs(); } diff --git a/libcore b/libcore index d410fe1c..7b367fe7 160000 --- a/libcore +++ b/libcore @@ -1 +1 @@ -Subproject commit d410fe1c4b0d90f545716737d53e0002be9504cd +Subproject commit 7b367fe70c9ecbf0dda2b73289905565e2451745 diff --git a/pubspec.lock b/pubspec.lock index 609db473..68535a90 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1555,7 +1555,7 @@ packages: source: hosted version: "11.10.0" watcher: - dependency: transitive + dependency: "direct main" description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" diff --git a/pubspec.yaml b/pubspec.yaml index 5016d264..f2ef1f46 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,8 +10,6 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.6 - - # internationalization flutter_localizations: sdk: flutter intl: ^0.18.1 @@ -19,28 +17,18 @@ dependencies: slang_flutter: ^3.24.0 timeago: ^3.5.0 flutter_localized_locales: ^2.0.5 - - # data & serialization fpdart: ^1.1.0 freezed_annotation: ^2.4.1 json_annotation: ^4.8.1 - - # state management hooks_riverpod: ^2.4.3 flutter_hooks: ^0.20.3 riverpod_annotation: ^2.2.0 rxdart: ^0.27.7 - - # persistence drift: ^2.12.1 sqlite3_flutter_libs: ^0.5.16 shared_preferences: ^2.2.2 - - # networking dio: ^5.3.3 web_socket_channel: ^2.4.0 - - # native ffi: ^2.1.0 path_provider: ^2.1.1 flutter_local_notifications: ^15.1.1 @@ -54,13 +42,9 @@ dependencies: url_launcher: ^6.1.14 vclibs: ^0.1.0 launch_at_startup: ^0.2.2 - - # analytics sentry_flutter: ^7.10.1 sentry_dart_plugin: ^1.6.2 sentry_dio: ^7.10.1 - - # utils combine: ^0.5.6 path: ^1.8.3 loggy: ^2.0.3 @@ -73,8 +57,7 @@ dependencies: accessibility_tools: ^1.0.0 neat_periodic_task: ^2.0.1 retry: ^3.1.2 - - # widgets + watcher: ^1.1.0 go_router: ^11.1.4 flex_color_scheme: ^7.3.1 flutter_animate: ^4.2.0+1 @@ -170,3 +153,8 @@ sentry: upload_sources: true log_level: info ignore_missing: true + +cider: + link_template: + tag: https://github.com/hiddify/hiddify-next/releases/tag/%tag% + diff: https://github.com/hiddify/hiddify-next/compare/%from%...%to%