diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5ffc68f..c8583e16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,7 @@ env: UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}" TAG_NAME: "${{ inputs.tag-name }}" TARGET_NAME_gz: "Hiddify-Linux-x64-AppImage.tar" + TARGET_NAME_AppImage: "Hiddify-Linux-x64-AppImage" TARGET_NAME_deb: "Hiddify-Debian-x64" # TARGET_NAME_rpm: "Hiddify-rpm-x64" TARGET_NAME_apk: "Hiddify-Android" @@ -64,18 +65,23 @@ jobs: fail-fast: false matrix: include: - # - platform: android-apk - # os: ubuntu-latest - # targets: apk + - platform: android-apk + os: ubuntu-latest + targets: apk - platform: android-aab os: ubuntu-latest targets: aab - # - platform: windows - # os: windows-latest - # aarch: amd64 - # targets: exe,msix,zip + - platform: windows + os: windows-latest + aarch: amd64 + targets: exe,msix,zip + + - platform: linux + os: ubuntu-22.04 + aarch: amd64 + targets: deb,gz,AppImage # - platform: linux-amd64 # os: ubuntu-22.04 @@ -97,10 +103,10 @@ jobs: # aarch: arm64 # targets: deb,gz - # - platform: macos - # os: macos-15 - # aarch: universal - # targets: dmg,pkg + - platform: macos + os: macos-15 + aarch: universal + targets: dmg,pkg # - platform: ios # os: macos-15 @@ -133,21 +139,21 @@ jobs: - - name: Setup Flutter for arm64 - if: ${{ startsWith(matrix.platform,'linux-arm64') }} - uses: hurelhuyag/flutter-arm64-action@HEAD - with: - channel: 'stable' - flutter-version: ${{ env.FLUTTER_VERSION }} + # - name: Setup Flutter for arm64 + # if: ${{ startsWith(matrix.platform,'linux-arm64') }} + # uses: hurelhuyag/flutter-arm64-action@HEAD + # with: + # channel: 'stable' + # flutter-version: ${{ env.FLUTTER_VERSION }} - name: Setup Flutter - if: ${{ !startsWith(matrix.platform, 'linux-arm64') }} uses: subosito/flutter-action@v2.21.0 #issue with 2.13 with: flutter-version: ${{ env.FLUTTER_VERSION }} # flutter-version-file: pubspec.yaml channel: 'stable' cache: true + - name: Clean up disk space if: startsWith(matrix.platform,'android') run: | diff --git a/Makefile b/Makefile index 949363f9..8f31a7f1 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ ios-prepare: common-prepare ios-libs cd ios; pod repo update; pod install;echo "done ios prepare" macos-prepare: common-prepare macos-libs -linux-prepare: common-prepare linux-libs +linux-prepare: common-prepare linux-amd64-libs linux-amd64-prepare: common-prepare linux-amd64-libs @@ -388,6 +388,8 @@ linux-appimage-release: sed -i '/^\[Desktop Entry\]/a StartupWMClass=app.hiddify.com' "squashfs-root/hiddify.desktop"; \ $(BLUE)Removing old AppImage$(DONE); \ rm *.AppImage; \ + $(BLUE)Deleting bundled libstdc++ to fix Arch Linux compatibility...$(DONE); \ + find squashfs-root/usr/lib -name "libstdc++.so.6" -delete; \ $(BLUE)Rebuilding AppImage$(DONE); \ ARCH=x86_64 appimagetool --no-appstream squashfs-root Hiddify.AppImage > /dev/null; \ $(BLUE)Cleaning up squashfs$(DONE); \ @@ -396,8 +398,8 @@ linux-appimage-release: PKG_DIR_NAME="hiddify-linux-appimage"; \ $(BLUE)Creating dir: $$PKG_DIR_NAME$(DONE); \ mkdir -p "$$PKG_DIR_NAME"; \ - $(BLUE)Moving and Renaming to Hiddify.AppImage$(DONE); \ - mv "Hiddify.AppImage" "$$PKG_DIR_NAME/Hiddify.AppImage"; \ + $(BLUE)Moving Hiddify.AppImage$(DONE); \ + cp -p "Hiddify.AppImage" "$$PKG_DIR_NAME/Hiddify.AppImage"; \ $(BLUE)Creating Portable Home directory$(DONE); \ mkdir -p "$$PKG_DIR_NAME/Hiddify.AppImage.home"; \ $(BLUE)Compressing to .tar.gz$(DONE); \ diff --git a/README.md b/README.md index d2ef2d1c..d2e9198a 100644 --- a/README.md +++ b/README.md @@ -131,12 +131,9 @@ Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH etc. ## 🌎 Translations -
-[![inlang status badge](https://inlang.com/badge?url=github.com/hiddify/hiddify-next)](https://inlang.com/editor/github.com/hiddify/hiddify-next?ref=badge) -
+You can improve existing languages or contribute new ones either by editing the JSON files in `/assets/translations` or [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) by using [Inlang online editor](https://fink.inlang.com/github.com/hiddify/hiddify-app). -Improve existing languages or add new ones by manually editing the JSON files located in `/assets/translations` or by using the [Inlang online editor](https://fink.inlang.com/github.com/hiddify/hiddify-next). ## ✏️ Acknowledgements @@ -154,6 +151,8 @@ We would like to express our sincere appreciation to the contributors of the fol ## 🎯 Donation and Support The easiest way to support us is to click on the star (⭐) at the top of this page. + +
@@ -162,7 +161,9 @@ The easiest way to support us is to click on the star (⭐) at the top of this p -We also need financial support for our services. All of our activities are done voluntarily and financial support will be spent on the development of the project. You can view our support addresses [here](https://github.com/hiddify/hiddify-server/wiki/support). +
+ +We also need financial support for our services. All of our activities are done voluntarily and financial support will be spent on the development of the project. You can view our support addresses [here](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Collaboration and Contact Information diff --git a/README_br.md b/README_br.md index 59cd36d9..c547e9c1 100644 --- a/README_br.md +++ b/README_br.md @@ -133,12 +133,8 @@ Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH, etc. ## 🌎 Tradução -
- -[![inlang status badge](https://inlang.com/badge?url=github.com/hiddify/hiddify-next)](https://inlang.com/editor/github.com/hiddify/hiddify-next?ref=badge) -
-Melhore os idiomas existentes ou adicione novos editando manualmente os arquivos JSON localizados em `/assets/translations` ou usando o [editor online Inlang](https://inlang.com/editor/github.com/hiddify/hiddify-next). +Melhore os idiomas existentes ou adicione novos editando manualmente os arquivos JSON localizados em `/assets/translations` ou [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) usando o [editor online Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app). ## ✏️ Agradecimentos @@ -159,11 +155,16 @@ A maneira mais fácil de nos apoiar é clicar na estrela (⭐) no topo desta pá
-Star History Chart + + + + Star History of hiddify/hiddify-app + +
-Também precisamos de apoio financeiro para nossos serviços. Todas as nossas atividades são realizadas voluntariamente e o apoio financeiro será utilizado no desenvolvimento do projeto. Você pode ver nossos links de suporte [aqui](https://github.com/hiddify/hiddify-server/wiki/support). +Também precisamos de apoio financeiro para nossos serviços. Todas as nossas atividades são realizadas voluntariamente e o apoio financeiro será utilizado no desenvolvimento do projeto. Você pode ver nossos links de suporte [aqui](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Colaboração e Informações de Contato diff --git a/README_cn.md b/README_cn.md index da51116b..c7b0d706 100644 --- a/README_cn.md +++ b/README_cn.md @@ -127,13 +127,8 @@ ## 🌎 翻译 -
- -[![inlang status badge](https://inlang.com/badge?url=github.com/hiddify/hiddify-next)](https://inlang.com/editor/github.com/hiddify/hiddify-next?ref=badge) -
- -您可以通过手动编辑位于 `/assets/translations` 中的 JSON 文件,或使用 [Inlang 在线编辑器](https://inlang.com/editor/github.com/hiddify/hiddify-next)来改进现有语言或添加新语言。 +您可以通过手动编辑位于 `/assets/translations` 中的 JSON 文件,或使用 [Inlang 在线编辑器](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)来改进现有语言或添加新语言。 ## ✏️ 致谢 我们谨向以下项目的贡献者表示诚挚的谢意,这些项目打下的坚实基础和开发的创新功能,显着增强了本项目的功能,为本项目的开发带来了成功。 @@ -154,11 +149,16 @@
-Star History Chart + + + + Star History of hiddify/hiddify-app + +
-我们的服务也需要经济支持。我们所有的活动都是志愿性质的,经济支持将被用于项目的发展。您可以在 [这里](https://github.com/hiddify/hiddify-server/wiki/support) 查看我们的支持地址。 +我们的服务也需要经济支持。我们所有的活动都是志愿性质的,经济支持将被用于项目的发展。您可以在 [这里](https://hiddify.com/donation-and-support/) 查看我们的支持地址。 ## 👩‍🏫 合作及联系信息 diff --git a/README_fa.md b/README_fa.md index a4f4858e..08e42392 100644 --- a/README_fa.md +++ b/README_fa.md @@ -132,12 +132,9 @@ Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH, etc. ## 🌎 ترجمه‌ها -
- -![Improve Translations](https://github.com/hiddify/hiddify-app/assets/125398461/c6f1f746-db8e-477d-ae05-b8305af838aa) -
-با ویرایش دستی فایل‌های JSON در assets/translations/ یا با استفاده از [ویرایشگر آنلاین Inlang](https://inlang.com/editor/github.com/hiddify/hiddify-next)، زبان‌های موجود را بهبود بدهید و یا زبان‌های جدید اضافه کنید. + +با ویرایش دستی فایل‌های JSON در assets/translations/ یا با استفاده از [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) [ویرایشگر آنلاین Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)، زبان‌های موجود را بهبود بدهید و یا زبان‌های جدید اضافه کنید. ## ✏️ سپاسگزاری‌ها مایلیم از دست‌اندرکاران پروژه‌های زیر صمیمانه قدردانی کنیم که پایه قوی و ویژگی‌های نوآورانه آنها موفقیت و عملکرد این پروژه را به میزان قابل توجهی افزایش داده است. @@ -157,11 +154,16 @@ Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH, etc.
-Star History Chart + + + + Star History of hiddify/hiddify-app + +
-ما برای سرویس هایمان به کمک مالی هم نیاز داریم. تمامی فعالیت‌های ما به صورت داوطلبانه انجام می‌شود و حمایت‌های مالی صرف توسعه پروژه می‌شود. اطلاعات و آدرس‌های حمایت‌ از ما را در [این لینک](https://github.com/hiddify/hiddify-server/wiki/support) مشاهده فرمایید. +ما برای سرویس هایمان به کمک مالی هم نیاز داریم. تمامی فعالیت‌های ما به صورت داوطلبانه انجام می‌شود و حمایت‌های مالی صرف توسعه پروژه می‌شود. اطلاعات و آدرس‌های حمایت‌ از ما را در [این لینک](https://hiddify.com/fa/donation-and-support/) مشاهده فرمایید. ## 👩‍🏫 راه‌های همکاری و ارتباط با ما diff --git a/README_ja.md b/README_ja.md index 529fb5b2..bb1642bc 100644 --- a/README_ja.md +++ b/README_ja.md @@ -135,12 +135,7 @@ Vless、Vmess、Reality、TUIC、Hysteria、Wireguard、SSH など。 ## 🌎 翻訳 -
- -[![inlang status badge](https://inlang.com/badge?url=github.com/hiddify/hiddify-next)](https://inlang.com/editor/github.com/hiddify/hiddify-next?ref=badge) -
- -インストールとチュートリアル `/assets/translations` にある JSON ファイルを手動で編集するか、[Inlang オンラインエディタ](https://inlang.com/editor/github.com/hiddify/hiddify-next)を使って、既存の言語を改良したり、新しい言語を追加したりすることができます。 +インストールとチュートリアル `/assets/translations` にある JSON ファイルを手動で編集するか、[Inlang オンラインエディタ](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)を使って、既存の言語を改良したり、新しい言語を追加したりすることができます。 ## ✏️ 謝辞 @@ -161,11 +156,16 @@ Vless、Vmess、Reality、TUIC、Hysteria、Wireguard、SSH など。
-Star History Chart + + + + Star History of hiddify/hiddify-app + +
-また、私たちのサービスには財政的な支援も必要です。私たちの活動はすべて自発的に行われており、経済的支援はプロジェクトの発展に費やされます。私たちのサポートアドレスは[こちら](https://github.com/hiddify/hiddify-server/wiki/support)からご覧いただけます。 +また、私たちのサービスには財政的な支援も必要です。私たちの活動はすべて自発的に行われており、経済的支援はプロジェクトの発展に費やされます。私たちのサポートアドレスは[こちら](https://hiddify.com/donation-and-support/)からご覧いただけます。 ## 👩‍🏫 コラボレーションおよび連絡先 diff --git a/README_ru.md b/README_ru.md index 56a4d870..f10ddde3 100644 --- a/README_ru.md +++ b/README_ru.md @@ -119,12 +119,7 @@ ## 🌎 Переводы -
- -[![inlang панель статуса](https://inlang.com/badge?url=github.com/hiddify/hiddify-next)](https://inlang.com/editor/github.com/hiddify/hiddify-next?ref=badge) -
- -Улучшайте существующие языки или добавляйте новые, вручную редактируя JSON-файлы, расположенные в `/assets/translations`, или используя [Онлайн редактор Inlang](https://inlang.com/editor/github.com/hiddify/hiddify-next) +Улучшайте существующие языки или добавляйте новые, вручную редактируя JSON-файлы, расположенные в `/assets/translations`, или используя [Онлайн редактор Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app) ## ✏️ Благодарности @@ -145,12 +140,17 @@
-Star History Chart + + + + Star History of hiddify/hiddify-app + +
-Нам также нужна финансовая поддержка для наших сервисов. Вся наша деятельность осуществляется на добровольных началах, а финансовая поддержка будет направлена на развитие проекта. Вы можете просмотреть адреса нашей поддержки [здесь](https://github.com/hiddify/hiddify-server/wiki/support). +Нам также нужна финансовая поддержка для наших сервисов. Вся наша деятельность осуществляется на добровольных началах, а финансовая поддержка будет направлена на развитие проекта. Вы можете просмотреть адреса нашей поддержки [здесь](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Сотрудничество и контактная информация diff --git a/assets/translations/ar.i18n.json b/assets/translations/ar.i18n.json index 8262298c..05b751dd 100644 --- a/assets/translations/ar.i18n.json +++ b/assets/translations/ar.i18n.json @@ -279,13 +279,13 @@ }, "region": "المنطقة", "regions": { - "ir": "إيران (ir) 🇮🇷", - "cn": "الصين (cn) 🇨🇳", - "ru": "روسيا (ru) 🇷🇺", - "af": "أفغانستان (af) 🇦🇫", - "id": "إندونيسيا (id) 🇮🇩", - "tr": "تركيا (tr) 🇹🇷", - "br": "البرازيل (br) 🇧🇷", + "ir": "إيران (ir)", + "cn": "الصين (cn)", + "ru": "روسيا (ru)", + "af": "أفغانستان (af)", + "id": "إندونيسيا (id)", + "tr": "تركيا (tr)", + "br": "البرازيل (br)", "other": "أخرى" }, "balancerStrategy": { diff --git a/assets/translations/en.i18n.json b/assets/translations/en.i18n.json index 20699050..07b3f341 100644 --- a/assets/translations/en.i18n.json +++ b/assets/translations/en.i18n.json @@ -274,13 +274,13 @@ }, "region": "Region", "regions": { - "ir": "Iran (ir) 🇮🇷", - "cn": "China (cn) 🇨🇳", - "ru": "Russia (ru) 🇷🇺", - "af": "Afghanistan (af) 🇦🇫", - "id": "Indonesia (id) 🇮🇩", - "tr": "Türkiye (tr) 🇹🇷", - "br": "Brazil (br) 🇧🇷", + "ir": "Iran (ir)", + "cn": "China (cn)", + "ru": "Russia (ru)", + "af": "Afghanistan (af)", + "id": "Indonesia (id)", + "tr": "Türkiye (tr)", + "br": "Brazil (br)", "other": "Other" }, "balancerStrategy": { diff --git a/assets/translations/es.i18n.json b/assets/translations/es.i18n.json index 9274d41c..9baa640b 100644 --- a/assets/translations/es.i18n.json +++ b/assets/translations/es.i18n.json @@ -273,13 +273,13 @@ }, "region": "Región", "regions": { - "ir": "Irán (ir) 🇮🇷", - "cn": "China (cn) 🇨🇳", - "ru": "Rusia (ru) 🇷🇺", - "af": "Afganistán (af) 🇦🇫", - "id": "Indonesia (id) 🇮🇩", - "tr": "Turquía (tr) 🇹🇷", - "br": "Brasil (br) 🇧🇷", + "ir": "Irán (ir)", + "cn": "China (cn)", + "ru": "Rusia (ru)", + "af": "Afganistán (af)", + "id": "Indonesia (id)", + "tr": "Turquía (tr)", + "br": "Brasil (br)", "other": "Otro" }, "balancerStrategy": { diff --git a/assets/translations/fa.i18n.json b/assets/translations/fa.i18n.json index ec8de8e8..f3f4729a 100644 --- a/assets/translations/fa.i18n.json +++ b/assets/translations/fa.i18n.json @@ -273,13 +273,13 @@ }, "region": "منطقه", "regions": { - "ir": "ایران (ir) 🇮🇷", - "cn": "چین (cn) 🇨🇳", - "ru": "روسیه (ru) 🇷🇺", - "af": "افغانستان (af) 🇦🇫", - "id": "اندونزی (id) 🇮🇩", - "tr": "ترکیه (tr) 🇹🇷", - "br": "برزیل (br) 🇧🇷", + "ir": "ایران (ir)", + "cn": "چین (cn)", + "ru": "روسیه (ru)", + "af": "افغانستان (af)", + "id": "اندونزی (id)", + "tr": "ترکیه (tr)", + "br": "برزیل (br)", "other": "سایر" }, "balancerStrategy": { diff --git a/assets/translations/fr.i18n.json b/assets/translations/fr.i18n.json index 16a92372..29743ac1 100644 --- a/assets/translations/fr.i18n.json +++ b/assets/translations/fr.i18n.json @@ -273,13 +273,13 @@ }, "region": "Région", "regions": { - "ir": "Iran (ir) 🇮🇷", - "cn": "Chine (cn) 🇨🇳", - "ru": "Russie (ru) 🇷🇺", - "af": "Afghanistan (af) 🇦🇫", - "id": "Indonésie (id) 🇮🇩", - "tr": "Turquie (tr) 🇹🇷", - "br": "Brésil (br) 🇧🇷", + "ir": "Iran (ir)", + "cn": "Chine (cn)", + "ru": "Russie (ru)", + "af": "Afghanistan (af)", + "id": "Indonésie (id)", + "tr": "Turquie (tr)", + "br": "Brésil (br)", "other": "Autre" }, "balancerStrategy": { diff --git a/assets/translations/id.i18n.json b/assets/translations/id.i18n.json index 76402eff..41b7ce1b 100644 --- a/assets/translations/id.i18n.json +++ b/assets/translations/id.i18n.json @@ -273,13 +273,13 @@ }, "region": "Wilayah", "regions": { - "ir": "Iran (ir) 🇮🇷", - "cn": "Tiongkok (cn) 🇨🇳", - "ru": "Rusia (ru) 🇷🇺", - "af": "Afghanistan (af) 🇦🇫", - "id": "Indonesia (id) 🇮🇩", - "tr": "Turki (tr) 🇹🇷", - "br": "Brasil (br) 🇧🇷", + "ir": "Iran (ir)", + "cn": "Tiongkok (cn)", + "ru": "Rusia (ru)", + "af": "Afghanistan (af)", + "id": "Indonesia (id)", + "tr": "Turki (tr)", + "br": "Brasil (br)", "other": "Lainnya" }, "balancerStrategy": { diff --git a/assets/translations/pt-BR.i18n.json b/assets/translations/pt-BR.i18n.json index 2558c01d..c3f536bc 100644 --- a/assets/translations/pt-BR.i18n.json +++ b/assets/translations/pt-BR.i18n.json @@ -273,13 +273,13 @@ }, "region": "Região", "regions": { - "ir": "Irã (ir) 🇮🇷", - "cn": "China (cn) 🇨🇳", - "ru": "Rússia (ru) 🇷🇺", - "af": "Afeganistão (af) 🇦🇫", - "id": "Indonésia (id) 🇮🇩", - "tr": "Turquia (tr) 🇹🇷", - "br": "Brasil (br) 🇧🇷", + "ir": "Irã (ir)", + "cn": "China (cn)", + "ru": "Rússia (ru)", + "af": "Afeganistão (af)", + "id": "Indonésia (id)", + "tr": "Turquia (tr)", + "br": "Brasil (br)", "other": "Outro" }, "balancerStrategy": { diff --git a/assets/translations/ru.i18n.json b/assets/translations/ru.i18n.json index ed798abe..a6adacbf 100644 --- a/assets/translations/ru.i18n.json +++ b/assets/translations/ru.i18n.json @@ -277,13 +277,13 @@ }, "region": "Регион", "regions": { - "ir": "Иран (ir) 🇮🇷", - "cn": "Китай (cn) 🇨🇳", - "ru": "Россия (ru) 🇷🇺", - "af": "Афганистан (af) 🇦🇫", - "id": "Индонезия (id) 🇮🇩", - "tr": "Турция (tr) 🇹🇷", - "br": "Бразилия (br) 🇧🇷", + "ir": "Иран (ir)", + "cn": "Китай (cn)", + "ru": "Россия (ru)", + "af": "Афганистан (af)", + "id": "Индонезия (id)", + "tr": "Турция (tr)", + "br": "Бразилия (br)", "other": "Другой" }, "balancerStrategy": { diff --git a/assets/translations/tr.i18n.json b/assets/translations/tr.i18n.json index 9b20e38f..6cf98919 100644 --- a/assets/translations/tr.i18n.json +++ b/assets/translations/tr.i18n.json @@ -273,13 +273,13 @@ }, "region": "Bölge", "regions": { - "ir": "İran (ir) 🇮🇷", - "cn": "Çin (cn) 🇨🇳", - "ru": "Rusya (ru) 🇷🇺", - "af": "Afganistan (af) 🇦🇫", - "id": "Endonezya (id) 🇮🇩", - "tr": "Türkiye (tr) 🇹🇷", - "br": "Brezilya (br) 🇧🇷", + "ir": "İran (ir)", + "cn": "Çin (cn)", + "ru": "Rusya (ru)", + "af": "Afganistan (af)", + "id": "Endonezya (id)", + "tr": "Türkiye (tr)", + "br": "Brezilya (br)", "other": "Diğer" }, "balancerStrategy": { diff --git a/assets/translations/zh-CN.i18n.json b/assets/translations/zh-CN.i18n.json index c1e1a97c..35b35747 100644 --- a/assets/translations/zh-CN.i18n.json +++ b/assets/translations/zh-CN.i18n.json @@ -273,13 +273,13 @@ }, "region": "地区", "regions": { - "ir": "伊朗 (ir) 🇮🇷", - "cn": "中国 (cn) 🇨🇳", - "ru": "俄罗斯 (ru) 🇷🇺", - "af": "阿富汗 (af) 🇦🇫", - "id": "印度尼西亚 (id) 🇮🇩", - "tr": "土耳其 (tr) 🇹🇷", - "br": "巴西 (br) 🇧🇷", + "ir": "伊朗 (ir)", + "cn": "中国 (cn)", + "ru": "俄罗斯 (ru)", + "af": "阿富汗 (af)", + "id": "印度尼西亚 (id)", + "tr": "土耳其 (tr)", + "br": "巴西 (br)", "other": "其他" }, "balancerStrategy": { diff --git a/assets/translations/zh-TW.i18n.json b/assets/translations/zh-TW.i18n.json index 0f477738..6ee0b033 100644 --- a/assets/translations/zh-TW.i18n.json +++ b/assets/translations/zh-TW.i18n.json @@ -273,13 +273,13 @@ }, "region": "地區", "regions": { - "ir": "伊朗 (ir) 🇮🇷", - "cn": "中國 (cn) 🇨🇳", - "ru": "俄羅斯 (ru) 🇷🇺", - "af": "阿富汗 (af) 🇦🇫", - "id": "印尼 (id) 🇮🇩", - "tr": "土耳其 (tr) 🇹🇷", - "br": "巴西 (br) 🇧🇷", + "ir": "伊朗 (ir)", + "cn": "中國 (cn)", + "ru": "俄羅斯 (ru)", + "af": "阿富汗 (af)", + "id": "印尼 (id)", + "tr": "土耳其 (tr)", + "br": "巴西 (br)", "other": "其他" }, "balancerStrategy": { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d6176529..9213d46c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -46,58 +46,11 @@ PODS: - Flutter - flutter_native_splash (2.4.3): - Flutter - - flutter_timezone (0.0.1): - - Flutter - - GoogleDataTransport (10.1.0): - - nanopb (~> 3.30910.0) - - PromisesObjC (~> 2.4) - - GoogleMLKit/BarcodeScanning (7.0.0): - - GoogleMLKit/MLKitCore - - MLKitBarcodeScanning (~> 6.0.0) - - GoogleMLKit/MLKitCore (7.0.0): - - MLKitCommon (~> 12.0.0) - - GoogleToolboxForMac/Defines (4.2.1) - - GoogleToolboxForMac/Logger (4.2.1): - - GoogleToolboxForMac/Defines (= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleUtilities/Environment (8.1.0): - - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.1.0): - - GoogleUtilities/Environment - - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.1.0) - - GoogleUtilities/UserDefaults (8.1.0): - - GoogleUtilities/Logger - - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (3.5.0) - in_app_review (2.0.0): - Flutter - - MLImage (1.0.0-beta6) - - MLKitBarcodeScanning (6.0.0): - - MLKitCommon (~> 12.0) - - MLKitVision (~> 8.0) - - MLKitCommon (12.0.0): - - GoogleDataTransport (~> 10.0) - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleUtilities/Logger (~> 8.0) - - GoogleUtilities/UserDefaults (~> 8.0) - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitVision (8.0.0): - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLImage (= 1.0.0-beta6) - - MLKitCommon (~> 12.0) - - mobile_scanner (6.0.2): + - mobile_scanner (7.0.0): - Flutter - - GoogleMLKit/BarcodeScanning (~> 7.0.0) - - nanopb (3.30910.0): - - nanopb/decode (= 3.30910.0) - - nanopb/encode (= 3.30910.0) - - nanopb/decode (3.30910.0) - - nanopb/encode (3.30910.0) + - FlutterMacOS - network_info_plus (0.0.1): - Flutter - objective_c (0.0.1): @@ -109,7 +62,6 @@ PODS: - FlutterMacOS - pointer_interceptor_ios (0.0.1): - Flutter - - PromisesObjC (2.4.0) - SDWebImage (5.21.6): - SDWebImage/Core (= 5.21.6) - SDWebImage/Core (5.21.6) @@ -155,9 +107,8 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - - flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`) - - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) + - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`) - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - objective_c (from `.symlinks/plugins/objective_c/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) @@ -174,17 +125,6 @@ SPEC REPOS: - DKImagePickerController - DKPhotoGallery - EasyPermissionX - - GoogleDataTransport - - GoogleMLKit - - GoogleToolboxForMac - - GoogleUtilities - - GTMSessionFetcher - - MLImage - - MLKitBarcodeScanning - - MLKitCommon - - MLKitVision - - nanopb - - PromisesObjC - SDWebImage - Sentry - sqlite3 @@ -205,12 +145,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" - flutter_timezone: - :path: ".symlinks/plugins/flutter_timezone/ios" in_app_review: :path: ".symlinks/plugins/in_app_review/ios" mobile_scanner: - :path: ".symlinks/plugins/mobile_scanner/ios" + :path: ".symlinks/plugins/mobile_scanner/darwin" network_info_plus: :path: ".symlinks/plugins/network_info_plus/ios" objective_c: @@ -243,25 +181,13 @@ SPEC CHECKSUMS: Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 - flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282 - GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318 - GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 - GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 - MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56 - MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2 - MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d - MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e - mobile_scanner: fd0054c52ede661e80bf5a4dea477a2467356bee - nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e network_info_plus: 6613d9d7cdeb0e6f366ed4dbe4b3c51c52d567a9 objective_c: 77e887b5ba1827970907e10e832eec1683f3431d package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 pointer_interceptor_ios: 508241697ff0947f853c061945a8b822463947c1 - PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477 Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index f3a6d800..3df820e6 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -413,8 +413,6 @@ dependencies = ( ); name = HiddifyPacketTunnel; - packageProductDependencies = ( - ); productName = HiddifyPacketTunnel; productReference = 03E392B62ADDA00E000ADF15 /* HiddifyPacketTunnel.appex */; productType = "com.apple.product-type.app-extension"; @@ -450,7 +448,6 @@ FBEFD3291AEA65EDE2F5AEF6 /* [CP] Embed Pods Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 738EC814CF019E6FA6F32C6B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -582,27 +579,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; - 738EC814CF019E6FA6F32C6B /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -648,14 +624,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; diff --git a/lib/core/preferences/preferences_provider.dart b/lib/core/preferences/preferences_provider.dart index 20e7053d..bded75ab 100644 --- a/lib/core/preferences/preferences_provider.dart +++ b/lib/core/preferences/preferences_provider.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:hiddify/core/model/environment.dart'; +import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:loggy/loggy.dart'; import 'package:path/path.dart' as p; @@ -16,6 +18,7 @@ Future sharedPreferences(Ref ref) async { logger.debug("initializing preferences"); try { + if (PlatformUtils.isWindows && Environment.isPortable) SharedPreferences.setPrefix('portable.'); sharedPreferences = await SharedPreferences.getInstance(); } catch (e) { logger.error("error initializing preferences", e); diff --git a/lib/core/router/dialog/dialog_notifier.dart b/lib/core/router/dialog/dialog_notifier.dart index eb19af3d..c785e303 100644 --- a/lib/core/router/dialog/dialog_notifier.dart +++ b/lib/core/router/dialog/dialog_notifier.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hiddify/core/router/dialog/widgets/action_at_closing_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/confirmation_dialog.dart'; @@ -10,7 +9,6 @@ import 'package:hiddify/core/router/dialog/widgets/new_version_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/no_active_profile_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/ok_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/proxy_info_dialog.dart'; -import 'package:hiddify/core/router/dialog/widgets/region_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/save_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_checkbox_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_input_dialog.dart'; @@ -112,10 +110,6 @@ class DialogNotifier extends _$DialogNotifier { false; } - Future showRegion({required Region selected}) async { - return await _show(RegionDialog(selected: selected)); - } - Future showActionAtClosing({required ActionsAtClosing selected}) async { return await _show(ActionsAtClosingDialog(selected: selected)); } @@ -216,13 +210,21 @@ class DialogNotifier extends _$DialogNotifier { Future showSettingPicker({ required String title, + bool showFlag = false, required T selected, required List options, required String Function(T e) getTitle, VoidCallback? onReset, }) async { return await _show( - SettingPickerDialog(title: title, selected: selected, options: options, getTitle: getTitle, onReset: onReset), + SettingPickerDialog( + title: title, + showFlag: showFlag, + selected: selected, + options: options, + getTitle: getTitle, + onReset: onReset, + ), ); } diff --git a/lib/core/router/dialog/widgets/region_dialog.dart b/lib/core/router/dialog/widgets/region_dialog.dart deleted file mode 100644 index 7b57984d..00000000 --- a/lib/core/router/dialog/widgets/region_dialog.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:hiddify/core/localization/translations.dart'; -import 'package:hiddify/core/model/region.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -class RegionDialog extends HookConsumerWidget { - const RegionDialog({super.key, required this.selected}); - - final Region selected; - @override - Widget build(BuildContext context, WidgetRef ref) { - final t = ref.watch(translationsProvider).requireValue; - return SimpleDialog( - title: Text(t.pages.settings.routing.region), - children: Region.values - .map((e) => RadioListTile(title: Text(e.present(t)), value: e, groupValue: selected, onChanged: context.pop)) - .toList(), - ); - } -} diff --git a/lib/core/router/dialog/widgets/setting_picker_dialog.dart b/lib/core/router/dialog/widgets/setting_picker_dialog.dart index 5e315c96..dd9bd056 100644 --- a/lib/core/router/dialog/widgets/setting_picker_dialog.dart +++ b/lib/core/router/dialog/widgets/setting_picker_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; +import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -8,6 +9,7 @@ class SettingPickerDialog extends HookConsumerWidget with PresLogger { const SettingPickerDialog({ super.key, required this.title, + this.showFlag = false, required this.selected, required this.options, required this.getTitle, @@ -15,6 +17,7 @@ class SettingPickerDialog extends HookConsumerWidget with PresLogger { }); final String title; + final bool showFlag; final T selected; final List options; final String Function(T e) getTitle; @@ -29,16 +32,17 @@ class SettingPickerDialog extends HookConsumerWidget with PresLogger { content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, - children: options - .map( - (e) => RadioListTile( - title: Text(getTitle(e)), - value: e, - groupValue: selected, - onChanged: (value) => context.pop(e), - ), - ) - .toList(), + children: options.map((e) { + final title = getTitle(e); + final countryCode = title.substring(title.length - 3, title.length - 1); + return RadioListTile( + title: Text(title), + secondary: showFlag ? IPCountryFlag(countryCode: countryCode, size: 32) : null, + value: e, + groupValue: selected, + onChanged: (value) => context.pop(e), + ); + }).toList(), ), ), actions: [ diff --git a/lib/features/common/general_pref_tiles.dart b/lib/features/common/general_pref_tiles.dart index e58fa369..f130b80c 100644 --- a/lib/features/common/general_pref_tiles.dart +++ b/lib/features/common/general_pref_tiles.dart @@ -7,7 +7,6 @@ import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/theme/app_theme_mode.dart'; import 'package:hiddify/core/theme/theme_preferences.dart'; -import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class LocalePrefTile extends ConsumerWidget { @@ -40,41 +39,6 @@ class LocalePrefTile extends ConsumerWidget { } } -class RegionPrefTile extends ConsumerWidget { - const RegionPrefTile({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final t = ref.watch(translationsProvider).requireValue; - - final region = ref.watch(ConfigOptions.region); - - return ListTile( - title: Text(t.pages.settings.routing.region), - subtitle: Text(region.present(t), style: Theme.of(context).textTheme.bodySmall), - leading: const Icon(Icons.place_rounded), - onTap: () async { - final selectedRegion = await ref.read(dialogNotifierProvider.notifier).showRegion(selected: region); - if (selectedRegion != null) { - // await ref.read(Preferences.region.notifier).update(selectedRegion); - - await ref.watch(ConfigOptions.region.notifier).update(selectedRegion); - - await ref.watch(ConfigOptions.directDnsAddress.notifier).reset(); - - // await ref.read(configOptionNotifierProvider.notifier).build(); - // await ref.watch(ConfigOptions.resolveDestination.notifier).update(!ref.watch(ConfigOptions.resolveDestination.notifier).raw()); - //for reload config - // final tmp = ref.watch(ConfigOptions.resolveDestination.notifier).raw(); - // await ref.watch(ConfigOptions.resolveDestination.notifier).update(!tmp); - // await ref.watch(ConfigOptions.resolveDestination.notifier).update(tmp); - //TODO: fix it - } - }, - ); - } -} - class EnableAnalyticsPrefTile extends ConsumerWidget { const EnableAnalyticsPrefTile({super.key, this.onChanged}); diff --git a/lib/features/intro/widget/intro_page.dart b/lib/features/intro/widget/intro_page.dart index 53dc8254..24917acc 100644 --- a/lib/features/intro/widget/intro_page.dart +++ b/lib/features/intro/widget/intro_page.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -14,10 +15,10 @@ import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/common/general_pref_tiles.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; +import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:timezone_to_country/timezone_to_country.dart'; class IntroPage extends HookConsumerWidget with PresLogger { const IntroPage({super.key}); @@ -94,7 +95,18 @@ class IntroPage extends HookConsumerWidget with PresLogger { ), const Gap(24), const LocalePrefTile(), - const RegionPrefTile(), + ChoicePreferenceWidget( + selected: ref.watch(ConfigOptions.region), + preferences: ref.watch(ConfigOptions.region.notifier), + choices: Region.values, + title: t.pages.settings.routing.region, + showFlag: true, + icon: Icons.place_rounded, + presentChoice: (value) => value.present(t), + onChanged: (val) async { + await ref.read(ConfigOptions.directDnsAddress.notifier).reset(); + }, + ), const EnableAnalyticsPrefTile(), const Gap(24), Focus( @@ -182,7 +194,7 @@ class IntroPage extends HookConsumerWidget with PresLogger { Future autoSelectRegion(WidgetRef ref) async { try { - final countryCode = await TimeZoneToCountry.getLocalCountryCode(); + final countryCode = RegionDetector.detect(); final regionLocale = _getRegionLocale(countryCode); loggy.debug('Timezone Region: ${regionLocale.region} Locale: ${regionLocale.locale}'); await ref.read(ConfigOptions.region.notifier).update(regionLocale.region); @@ -227,9 +239,9 @@ class IntroPage extends HookConsumerWidget with PresLogger { case "AF": return RegionLocale(Region.af, AppLocale.fa); case "BR": - return RegionLocale(Region.other, AppLocale.ptBr); + return RegionLocale(Region.br, AppLocale.ptBr); case "TR": - return RegionLocale(Region.other, AppLocale.tr); + return RegionLocale(Region.tr, AppLocale.tr); default: return RegionLocale(Region.other, AppLocale.en); } @@ -242,3 +254,181 @@ class RegionLocale { RegionLocale(this.region, this.locale); } + +class RegionDetector { + /// Returns: 'IR' | 'AF' | 'CN' | 'TR' | 'RU' | 'BR' | 'US' + static String detect() { + final now = DateTime.now(); + final offset = now.timeZoneOffset.inMinutes; + final tz = now.timeZoneName.toLowerCase().trim(); + + if (offset == 210) return 'IR'; + + if (offset == 270) { + final (_, country) = _parseLocale(); + return country == 'IR' ? 'IR' : 'AF'; + } + + final fromName = _fromTzName(tz, offset); + if (fromName != null) return fromName; + + final candidates = _candidatesForOffset(offset); + if (candidates.isEmpty) return 'US'; + + return _resolveByLocale(candidates); + } + + static String? _fromTzName(String tz, int offset) { + if (tz.contains('/')) { + final city = tz.split('/').last.replaceAll(' ', '_'); + final r = _ianaCities[city]; + if (r != null) return r; + } + + if (tz == 'irst' || tz == 'irdt' || tz.contains('iran')) return 'IR'; + + if (tz == 'aft' || tz.contains('afghanistan')) return 'AF'; + + if (tz == 'trt' || tz.contains('turkey') || tz.contains('istanbul')) { + return 'TR'; + } + + if (tz.contains('china') || tz.contains('beijing')) return 'CN'; + if (tz == 'cst' && offset == 480) return 'CN'; + + if (_matchesRussiaTz(tz)) return 'RU'; + + if (_matchesBrazilTz(tz)) return 'BR'; + + return null; + } + + static bool _matchesRussiaTz(String tz) { + if (tz.contains('russia') || tz.contains('moscow')) return true; + + const abbrs = {'msk', 'yekt', 'omst', 'krat', 'irkt', 'yakt', 'vlat', 'magt', 'pett', 'sakt', 'sret'}; + if (abbrs.contains(tz)) return true; + + const winKeys = [ + 'ekaterinburg', + 'kaliningrad', + 'yakutsk', + 'vladivostok', + 'magadan', + 'sakhalin', + 'kamchatka', + 'astrakhan', + 'saratov', + 'volgograd', + 'altai', + 'tomsk', + 'transbaikal', + 'n. central asia', + 'north asia', + ]; + return winKeys.any(tz.contains); + } + + static bool _matchesBrazilTz(String tz) { + if (tz == 'brt' || tz == 'brst') return true; + if (tz.contains('brazil') || tz.contains('brasilia')) return true; + + const winKeys = ['e. south america', 'central brazilian', 'tocantins', 'bahia']; + return winKeys.any(tz.contains); + } + + static Set _candidatesForOffset(int offset) { + final c = {}; + + if (offset == 180) c.add('TR'); + + if (offset == 480) c.add('CN'); + + if (_ruOffsets.contains(offset)) c.add('RU'); + + if (_brOffsets.contains(offset)) c.add('BR'); + + return c; + } + + static const _ruOffsets = {120, 180, 240, 300, 360, 420, 480, 540, 600, 660, 720}; + + static const _brOffsets = {-120, -180, -240, -300}; + + static String _resolveByLocale(Set candidates) { + final (lang, country) = _parseLocale(); + + if (country != null && candidates.contains(country)) { + return country; + } + + final regionFromLang = _langToRegion[lang]; + if (regionFromLang != null && candidates.contains(regionFromLang)) { + return regionFromLang; + } + + return 'US'; + } + + static (String, String?) _parseLocale() { + try { + final parts = Platform.localeName.split(RegExp(r'[_\-.]')); + final lang = parts.first.toLowerCase(); + + String? country; + for (final p in parts.skip(1)) { + if (p.length == 2) { + country = p.toUpperCase(); + break; + } + } + + return (lang, country); + } catch (_) { + return ('en', null); + } + } + + static const _langToRegion = {'fa': 'IR', 'ps': 'AF', 'tr': 'TR', 'zh': 'CN', 'ru': 'RU', 'pt': 'BR'}; + + static const _ianaCities = { + 'tehran': 'IR', + 'kabul': 'AF', + 'istanbul': 'TR', + 'shanghai': 'CN', + 'chongqing': 'CN', + 'urumqi': 'CN', + 'harbin': 'CN', + 'moscow': 'RU', + 'kaliningrad': 'RU', + 'samara': 'RU', + 'yekaterinburg': 'RU', + 'omsk': 'RU', + 'novosibirsk': 'RU', + 'barnaul': 'RU', + 'tomsk': 'RU', + 'krasnoyarsk': 'RU', + 'irkutsk': 'RU', + 'chita': 'RU', + 'yakutsk': 'RU', + 'vladivostok': 'RU', + 'magadan': 'RU', + 'sakhalin': 'RU', + 'kamchatka': 'RU', + 'anadyr': 'RU', + 'volgograd': 'RU', + 'saratov': 'RU', + 'astrakhan': 'RU', + 'sao_paulo': 'BR', + 'fortaleza': 'BR', + 'recife': 'BR', + 'manaus': 'BR', + 'belem': 'BR', + 'cuiaba': 'BR', + 'bahia': 'BR', + 'rio_branco': 'BR', + 'noronha': 'BR', + 'porto_velho': 'BR', + 'campo_grande': 'BR', + }; +} diff --git a/lib/features/settings/overview/sections/route_options_page.dart b/lib/features/settings/overview/sections/route_options_page.dart index bd25208c..89eb4e90 100644 --- a/lib/features/settings/overview/sections/route_options_page.dart +++ b/lib/features/settings/overview/sections/route_options_page.dart @@ -46,6 +46,7 @@ class RouteOptionsPage extends HookConsumerWidget { preferences: ref.watch(ConfigOptions.region.notifier), choices: Region.values, title: t.pages.settings.routing.region, + showFlag: true, icon: Icons.place_rounded, presentChoice: (value) => value.present(t), onChanged: (val) async { diff --git a/lib/features/settings/widget/preference_tile.dart b/lib/features/settings/widget/preference_tile.dart index 33fa48ae..71ef8d25 100644 --- a/lib/features/settings/widget/preference_tile.dart +++ b/lib/features/settings/widget/preference_tile.dart @@ -71,6 +71,7 @@ class ChoicePreferenceWidget extends HookConsumerWidget { this.enabled = true, required this.choices, required this.title, + this.showFlag = false, this.icon, required this.presentChoice, this.validateInput, @@ -82,6 +83,7 @@ class ChoicePreferenceWidget extends HookConsumerWidget { final bool enabled; final List choices; final String title; + final bool showFlag; final IconData? icon; final String Function(T value) presentChoice; final bool Function(String value)? validateInput; @@ -98,6 +100,7 @@ class ChoicePreferenceWidget extends HookConsumerWidget { .read(dialogNotifierProvider.notifier) .showSettingPicker( title: title, + showFlag: showFlag, selected: selected, options: choices, getTitle: (e) => presentChoice(e), diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index ae51662b..3893a3e7 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,7 +7,6 @@ #include "generated_plugin_registrant.h" #include -#include #include #include #include @@ -20,9 +19,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); - g_autoptr(FlPluginRegistrar) flutter_timezone_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterTimezonePlugin"); - flutter_timezone_plugin_register_with_registrar(flutter_timezone_registrar); g_autoptr(FlPluginRegistrar) gtk_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); gtk_plugin_register_with_registrar(gtk_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 07a9285f..ebddfcdc 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,7 +4,6 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color - flutter_timezone gtk screen_retriever_linux sentry_flutter diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 25cd81ac..001b8d66 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,7 +9,6 @@ import app_links import device_info_plus import dynamic_color import file_picker -import flutter_timezone import in_app_review import mobile_scanner import network_info_plus @@ -29,7 +28,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) - FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index ec648566..25a6c5aa 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -10,8 +10,6 @@ PODS: - FlutterMacOS - file_picker (0.0.1): - FlutterMacOS - - flutter_timezone (0.1.0): - - FlutterMacOS - FlutterMacOS (1.0.0) - in_app_review (2.0.0): - FlutterMacOS @@ -71,7 +69,6 @@ DEPENDENCIES: - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - - flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`) - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`) @@ -104,8 +101,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos file_picker: :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos - flutter_timezone: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos FlutterMacOS: :path: Flutter/ephemeral in_app_review: @@ -143,7 +138,6 @@ SPEC CHECKSUMS: device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af - flutter_timezone: 62400baa441155f2a4144188648f2ff861649747 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93 mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e diff --git a/macos/packaging/dmg/background.png b/macos/packaging/dmg/background.png new file mode 100644 index 00000000..283c0385 Binary files /dev/null and b/macos/packaging/dmg/background.png differ diff --git a/macos/packaging/dmg/make_config.yaml b/macos/packaging/dmg/make_config.yaml index 4c545c51..17b4702d 100644 --- a/macos/packaging/dmg/make_config.yaml +++ b/macos/packaging/dmg/make_config.yaml @@ -1,10 +1,15 @@ title: Hiddify +background: "background.png" +window: + size: + width: 600 + height: 400 contents: - - x: 448 - y: 344 + - x: 440 + y: 230 type: link path: "/Applications" - - x: 192 - y: 344 + - x: 160 + y: 230 type: file path: Hiddify.app diff --git a/pubspec.lock b/pubspec.lock index a21d568d..5686c344 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -721,14 +721,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_timezone: - dependency: transitive - description: - name: flutter_timezone - sha256: bc286cecb0366d88e6c4644e3962ebd1ce1d233abc658eb1e0cd803389f84b64 - url: "https://pub.dev" - source: hosted - version: "4.1.0" flutter_typeahead: dependency: "direct main" description: @@ -1892,22 +1884,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.5" - timezone: - dependency: transitive - description: - name: timezone - sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d - url: "https://pub.dev" - source: hosted - version: "0.10.0" - timezone_to_country: - dependency: "direct main" - description: - name: timezone_to_country - sha256: c96ffcfbd92f56c1727c18166a73bc6973e47d0516a6989148f219414682948e - url: "https://pub.dev" - source: hosted - version: "3.0.0" timing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 70a518a6..1f1d0953 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,7 +85,6 @@ dependencies: cupertino_http: ^2.0.1 dart_mappable: ^4.2.1 fluentui_system_icons: ^1.1.229 - timezone_to_country: ^3.0.0 json_path: ^0.7.1 # permission_handler: ^11.3.0 # is not compatible with windows #flutter_easy_permission: ^1.1.2 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 93cc8663..30d74013 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -23,8 +22,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("AppLinksPluginCApi")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); - FlutterTimezonePluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterTimezonePluginCApi")); ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SentryFlutterPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b6f557c3..fa47a18d 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,7 +5,6 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links dynamic_color - flutter_timezone screen_retriever_windows sentry_flutter share_plus diff --git a/windows/packaging/msix/make_config.yaml b/windows/packaging/msix/make_config.yaml index 0638a835..5ff503c8 100644 --- a/windows/packaging/msix/make_config.yaml +++ b/windows/packaging/msix/make_config.yaml @@ -1,6 +1,6 @@ display_name: Hiddify publisher_display_name: Hiddify -identity_name: Hiddify.App +identity_name: Hiddify.HiddifyNext msix_version: 4.0.5.0 logo_path: windows\runner\resources\app_icon.ico capabilities: internetClient, internetClientServer, privateNetworkClientServer