Add about page
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user