Add about page
This commit is contained in:
@@ -104,6 +104,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"pageTitle": "about",
|
||||
"version": "version",
|
||||
"whatsNew": "what's new",
|
||||
"sourceCode": "source code",
|
||||
"telegramChannel": "telegram channel",
|
||||
"checkForUpdate": "check for update"
|
||||
},
|
||||
"tray": {
|
||||
"dashboard": "dashboard",
|
||||
"quit": "quit",
|
||||
|
||||
@@ -104,6 +104,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"pageTitle": "درباره",
|
||||
"version": "ورژن",
|
||||
"whatsNew": "تغییرات",
|
||||
"sourceCode": "سورس کد",
|
||||
"telegramChannel": "کانال تلگرام",
|
||||
"checkForUpdate": "بررسی آپدیت جدید"
|
||||
},
|
||||
"tray": {
|
||||
"dashboard": "داشبورد",
|
||||
"quit": "خروج",
|
||||
|
||||
@@ -39,6 +39,7 @@ int getCurrentIndex(BuildContext context) {
|
||||
if (location.startsWith(ProxiesRoute.path)) return 1;
|
||||
if (location.startsWith(LogsRoute.path)) return 2;
|
||||
if (location.startsWith(SettingsRoute.path)) return 3;
|
||||
if (location.startsWith(AboutRoute.path)) return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -52,5 +53,7 @@ void switchTab(int index, BuildContext context) {
|
||||
const LogsRoute().go(context);
|
||||
case 3:
|
||||
const SettingsRoute().go(context);
|
||||
case 4:
|
||||
const AboutRoute().go(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hiddify/core/router/routes/shared_routes.dart';
|
||||
import 'package:hiddify/features/about/view/view.dart';
|
||||
import 'package:hiddify/features/logs/view/view.dart';
|
||||
import 'package:hiddify/features/settings/view/view.dart';
|
||||
import 'package:hiddify/features/wrapper/wrapper.dart';
|
||||
@@ -20,6 +21,7 @@ part 'desktop_routes.g.dart';
|
||||
TypedGoRoute<ProxiesRoute>(path: ProxiesRoute.path),
|
||||
TypedGoRoute<LogsRoute>(path: LogsRoute.path),
|
||||
TypedGoRoute<SettingsRoute>(path: SettingsRoute.path),
|
||||
TypedGoRoute<AboutRoute>(path: AboutRoute.path),
|
||||
],
|
||||
)
|
||||
class DesktopWrapperRoute extends ShellRouteData {
|
||||
@@ -50,3 +52,13 @@ class SettingsRoute extends GoRouteData {
|
||||
return const NoTransitionPage(child: SettingsPage());
|
||||
}
|
||||
}
|
||||
|
||||
class AboutRoute extends GoRouteData {
|
||||
const AboutRoute();
|
||||
static const path = '/about';
|
||||
|
||||
@override
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
return const NoTransitionPage(child: AboutPage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hiddify/core/router/routes/shared_routes.dart';
|
||||
import 'package:hiddify/features/about/view/view.dart';
|
||||
import 'package:hiddify/features/logs/view/view.dart';
|
||||
import 'package:hiddify/features/settings/view/view.dart';
|
||||
import 'package:hiddify/features/wrapper/wrapper.dart';
|
||||
@@ -60,3 +61,19 @@ class SettingsRoute extends GoRouteData {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@TypedGoRoute<AboutRoute>(path: AboutRoute.path)
|
||||
class AboutRoute extends GoRouteData {
|
||||
const AboutRoute();
|
||||
static const path = '/about';
|
||||
|
||||
static final GlobalKey<NavigatorState> $parentNavigatorKey = rootNavigatorKey;
|
||||
|
||||
@override
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
return const MaterialPage(
|
||||
fullscreenDialog: true,
|
||||
child: AboutPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,6 @@ abstract class Constants {
|
||||
static const delayTestUrl = "https://www.google.com";
|
||||
static const configFileName = "config";
|
||||
static const countryMMDBFileName = "Country";
|
||||
static const githubUrl = "https://github.com/hiddify/hiddify-next";
|
||||
static const telegramChannelUrl = "https://t.me/hiddify";
|
||||
}
|
||||
|
||||
91
lib/features/about/view/about_page.dart
Normal file
91
lib/features/about/view/about_page.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hiddify/core/core_providers.dart';
|
||||
import 'package:hiddify/domain/constants.dart';
|
||||
import 'package:hiddify/features/common/runtime_details.dart';
|
||||
import 'package:hiddify/gen/assets.gen.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class AboutPage extends HookConsumerWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final t = ref.watch(translationsProvider);
|
||||
final details = ref.watch(runtimeDetailsNotifierProvider);
|
||||
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
title: Text(t.about.pageTitle.titleCase),
|
||||
),
|
||||
...switch (details) {
|
||||
AsyncData(:final value) => [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Assets.images.logo.svg(width: 64, height: 64),
|
||||
const Gap(16),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
t.general.appTitle.titleCase,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
"${t.about.version} ${value.version} ${value.buildNumber}",
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
ListTile(
|
||||
title: Text(t.about.whatsNew.sentenceCase),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.about.sourceCode.sentenceCase),
|
||||
trailing: const Icon(Icons.open_in_new),
|
||||
onTap: () async {
|
||||
await launchUrl(
|
||||
Uri.parse(Constants.githubUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.about.telegramChannel.sentenceCase),
|
||||
trailing: const Icon(Icons.open_in_new),
|
||||
onTap: () async {
|
||||
await launchUrl(
|
||||
Uri.parse(Constants.telegramChannelUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.about.checkForUpdate.sentenceCase),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
_ => [],
|
||||
}
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1
lib/features/about/view/view.dart
Normal file
1
lib/features/about/view/view.dart
Normal file
@@ -0,0 +1 @@
|
||||
export 'about_page.dart';
|
||||
37
lib/features/common/runtime_details.dart
Normal file
37
lib/features/common/runtime_details.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hiddify/utils/utils.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'runtime_details.freezed.dart';
|
||||
part 'runtime_details.g.dart';
|
||||
|
||||
// TODO implement clash version
|
||||
@Riverpod(keepAlive: true)
|
||||
class RuntimeDetailsNotifier extends _$RuntimeDetailsNotifier with AppLogger {
|
||||
@override
|
||||
Future<RuntimeDetails> build() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
return RuntimeDetails(
|
||||
version: packageInfo.version,
|
||||
buildNumber: packageInfo.buildNumber,
|
||||
installerStore: packageInfo.installerStore,
|
||||
clashVersion: "",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RuntimeDetails with _$RuntimeDetails {
|
||||
const RuntimeDetails._();
|
||||
|
||||
const factory RuntimeDetails({
|
||||
required String version,
|
||||
required String buildNumber,
|
||||
String? installerStore,
|
||||
required String clashVersion,
|
||||
}) = _RuntimeDetails;
|
||||
|
||||
factory RuntimeDetails.fromJson(Map<String, dynamic> json) =>
|
||||
_$RuntimeDetailsFromJson(json);
|
||||
}
|
||||
@@ -33,6 +33,10 @@ class DesktopWrapper extends HookConsumerWidget {
|
||||
icon: const Icon(Icons.settings),
|
||||
label: Text(t.settings.pageTitle.titleCase),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Icons.info),
|
||||
label: Text(t.about.pageTitle.titleCase),
|
||||
),
|
||||
];
|
||||
|
||||
return Scaffold(
|
||||
|
||||
@@ -42,6 +42,12 @@ class MobileWrapper extends HookConsumerWidget {
|
||||
selected: location == LogsRoute.path,
|
||||
onSelect: () => const LogsRoute().push(context),
|
||||
),
|
||||
DrawerTile(
|
||||
label: t.about.pageTitle.titleCase,
|
||||
icon: Icons.info,
|
||||
selected: location == AboutRoute.path,
|
||||
onSelect: () => const AboutRoute().push(context),
|
||||
),
|
||||
const Spacer(),
|
||||
Align(
|
||||
child: Column(
|
||||
|
||||
@@ -7,6 +7,7 @@ import Foundation
|
||||
|
||||
import flutter_local_notifications
|
||||
import mobile_scanner
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import protocol_handler
|
||||
import proxy_manager
|
||||
@@ -15,11 +16,13 @@ import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sqlite3_flutter_libs
|
||||
import tray_manager
|
||||
import url_launcher_macos
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ProtocolHandlerPlugin.register(with: registry.registrar(forPlugin: "ProtocolHandlerPlugin"))
|
||||
ProxyManagerPlugin.register(with: registry.registrar(forPlugin: "ProxyManagerPlugin"))
|
||||
@@ -28,5 +31,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
||||
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
}
|
||||
|
||||
56
pubspec.lock
56
pubspec.lock
@@ -645,6 +645,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.3"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -805,6 +813,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1330,6 +1354,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.12"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.37"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1338,6 +1386,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -54,6 +54,7 @@ dependencies:
|
||||
share_plus: ^7.0.2
|
||||
window_manager: ^0.3.5
|
||||
tray_manager: ^0.2.0
|
||||
package_info_plus: ^4.0.2
|
||||
|
||||
# utils
|
||||
combine: ^0.5.3
|
||||
@@ -76,6 +77,7 @@ dependencies:
|
||||
sliver_tools: ^0.2.12
|
||||
flutter_adaptive_scaffold: ^0.1.6
|
||||
fl_chart: ^0.63.0
|
||||
url_launcher: ^6.1.12
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user