import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'generated/common.freezed.dart'; part 'generated/common.g.dart'; @freezed abstract class NavigationItem with _$NavigationItem { const factory NavigationItem({ required Icon icon, required PageLabel label, final String? description, required WidgetBuilder builder, @Default(true) bool keep, String? path, @Default([NavigationItemMode.mobile, NavigationItemMode.desktop]) List modes, }) = _NavigationItem; } @freezed abstract class Package with _$Package { const factory Package({ required String packageName, required String label, required bool system, required bool internet, required int lastUpdateTime, }) = _Package; factory Package.fromJson(Map json) => _$PackageFromJson(json); } extension PackagesExt on List { List getViewList({ required List pinedList, required AccessSortType sortType, required bool isFilterSystemApp, required bool isFilterNonInternetApp, }) { return where( (item) => (isFilterSystemApp ? item.system == false : true) && (isFilterNonInternetApp ? item.internet == true : true), ).sorted((a, b) { final isSelectA = pinedList.contains(a.packageName); final isSelectB = pinedList.contains(b.packageName); if (isSelectA != isSelectB) { return isSelectA ? -1 : 1; } return switch (sortType) { AccessSortType.none => 0, AccessSortType.name => a.label.compareTo(b.label), AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime), }; }); } } @freezed abstract class Metadata with _$Metadata { const factory Metadata({ @Default(0) int uid, @Default('') String network, @Default('') String sourceIP, @Default('') String sourcePort, @Default('') String destinationIP, @Default('') String destinationPort, @Default('') String host, DnsMode? dnsMode, @Default('') String process, @Default('') String processPath, @Default('') String remoteDestination, @Default([]) List sourceGeoIP, @Default([]) List destinationGeoIP, @Default('') String destinationIPASN, @Default('') String sourceIPASN, @Default('') String specialRules, @Default('') String specialProxy, }) = _Metadata; factory Metadata.fromJson(Map json) => _$MetadataFromJson(json); } @freezed abstract class TrackerInfo with _$TrackerInfo { const factory TrackerInfo({ required String id, @Default(0) int upload, @Default(0) int download, required DateTime start, required Metadata metadata, required List chains, required String rule, required String rulePayload, int? downloadSpeed, int? uploadSpeed, }) = _TrackerInfo; factory TrackerInfo.fromJson(Map json) => _$TrackerInfoFromJson(json); } extension TrackerInfoExt on TrackerInfo { String get desc { var text = '${metadata.network}://'; final ips = [ metadata.host, metadata.destinationIP, ].where((ip) => ip.isNotEmpty); text += ips.join('/'); text += ':${metadata.destinationPort}'; return text; } String get progressText { final process = metadata.process; final uid = metadata.uid; if (uid != 0) { return '$process($uid)'.trim(); } return process.trim(); } } String _logDateTime(dynamic _) { return DateTime.now().showFull; } // String _logId(_) { // return utils.id; // } @freezed abstract class Log with _$Log { const factory Log({ // @JsonKey(fromJson: _logId) required String id, @JsonKey(name: 'LogLevel') @Default(LogLevel.info) LogLevel logLevel, @JsonKey(name: 'Payload') @Default('') String payload, @JsonKey(fromJson: _logDateTime) required String dateTime, }) = _Log; factory Log.app(String payload) { return Log( payload: payload, dateTime: _logDateTime(null), // id: _logId(null), ); } factory Log.fromJson(Map json) => _$LogFromJson(json); } @freezed abstract class LogsState with _$LogsState { const factory LogsState({ @Default([]) List logs, @Default([]) List keywords, @Default('') String query, @Default(true) bool autoScrollToEnd, }) = _LogsState; } extension LogsStateExt on LogsState { List get list { final lowQuery = query.toLowerCase(); return logs.where((log) { final logLevelName = log.logLevel.name; return {logLevelName}.containsAll(keywords) && ((log.payload.toLowerCase().contains(lowQuery)) || logLevelName.contains(lowQuery)); }).toList(); } } @freezed abstract class TrackerInfosState with _$TrackerInfosState { const factory TrackerInfosState({ @Default([]) List trackerInfos, @Default([]) List keywords, @Default('') String query, @Default(true) bool autoScrollToEnd, }) = _TrackerInfosState; } extension TrackerInfosStateExt on TrackerInfosState { List get list { final lowerQuery = query.toLowerCase().trim(); final lowQuery = query.toLowerCase(); return trackerInfos.where((trackerInfo) { final chains = trackerInfo.chains; final process = trackerInfo.metadata.process; final networkText = trackerInfo.metadata.network.toLowerCase(); final hostText = trackerInfo.metadata.host.toLowerCase(); final destinationIPText = trackerInfo.metadata.destinationIP .toLowerCase(); final processText = trackerInfo.metadata.process.toLowerCase(); final chainsText = chains.join('').toLowerCase(); return {...chains, process}.containsAll(keywords) && (networkText.contains(lowerQuery) || hostText.contains(lowerQuery) || destinationIPText.contains(lowQuery) || processText.contains(lowerQuery) || chainsText.contains(lowerQuery)); }).toList(); } } const defaultDavFileName = 'backup.zip'; @freezed abstract class DAV with _$DAV { const factory DAV({ required String uri, required String user, required String password, @Default(defaultDavFileName) String fileName, }) = _DAV; factory DAV.fromJson(Map json) => _$DAVFromJson(json); } @freezed abstract class FileInfo with _$FileInfo { const factory FileInfo({required int size, required DateTime lastModified}) = _FileInfo; } extension FileInfoExt on FileInfo { String get desc => '${size.traffic.show} · ${lastModified.lastUpdateTimeDesc}'; } @freezed abstract class VersionInfo with _$VersionInfo { const factory VersionInfo({ @Default('') String clashName, @Default('') String version, }) = _VersionInfo; factory VersionInfo.fromJson(Map json) => _$VersionInfoFromJson(json); } @freezed abstract class Traffic with _$Traffic { const factory Traffic({@Default(0) num up, @Default(0) num down}) = _Traffic; factory Traffic.fromJson(Map json) => _$TrafficFromJson(json); } extension TrafficExt on Traffic { String get speedText { return '↑ ${up.traffic.show}/s ↓ ${down.traffic.show}/s'; } String get desc { return '${up.traffic.show} ↑ ${down.traffic.show} ↓'; } String get trayTitle { return '${up.shortTraffic.show}/s \n ${down.shortTraffic.show}/s'; } num get speed => up + down; } @freezed abstract class TrafficShow with _$TrafficShow { const factory TrafficShow({required String value, required String unit}) = _TrafficShow; } extension TrafficShowExt on TrafficShow { String get show => '$value$unit'; } @freezed abstract class Proxy with _$Proxy { const factory Proxy({ required String name, required String type, String? now, }) = _Proxy; factory Proxy.fromJson(Map json) => _$ProxyFromJson(json); } @freezed abstract class Group with _$Group { const factory Group({ required GroupType type, @Default([]) List all, String? now, bool? hidden, String? testUrl, @Default('') String icon, required String name, }) = _Group; factory Group.fromJson(Map json) => _$GroupFromJson(json); } extension GroupsExt on List { Group? getGroup(String groupName) { final index = indexWhere((element) => element.name == groupName); return index != -1 ? this[index] : null; } } extension GroupExt on Group { String get realNow => now ?? ''; String getCurrentSelectedName(String proxyName) { if (type.isComputedSelected) { return realNow.isNotEmpty ? realNow : proxyName; } return proxyName.isNotEmpty ? proxyName : realNow; } } @freezed abstract class ColorSchemes with _$ColorSchemes { const factory ColorSchemes({ ColorScheme? lightColorScheme, ColorScheme? darkColorScheme, }) = _ColorSchemes; } extension ColorSchemesExt on ColorSchemes { ColorScheme getColorSchemeForBrightness( Brightness brightness, DynamicSchemeVariant schemeVariant, ) { if (brightness == Brightness.dark) { return darkColorScheme != null ? ColorScheme.fromSeed( seedColor: darkColorScheme!.primary, brightness: Brightness.dark, dynamicSchemeVariant: schemeVariant, ) : ColorScheme.fromSeed( seedColor: Color(defaultPrimaryColor), brightness: Brightness.dark, dynamicSchemeVariant: schemeVariant, ); } return lightColorScheme != null ? ColorScheme.fromSeed( seedColor: lightColorScheme!.primary, dynamicSchemeVariant: schemeVariant, ) : ColorScheme.fromSeed( seedColor: Color(defaultPrimaryColor), dynamicSchemeVariant: schemeVariant, ); } } @freezed abstract class IpInfo with _$IpInfo { const factory IpInfo({required String ip, required String countryCode}) = _IpInfo; static IpInfo fromIpInfoIoJson(Map json) { return switch (json) { {'ip': final String ip, 'country': final String country} => IpInfo( ip: ip, countryCode: country, ), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpApiCoJson(Map json) { return switch (json) { {'ip': final String ip, 'country_code': final String countryCode} => IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpSbJson(Map json) { return switch (json) { {'ip': final String ip, 'country_code': final String countryCode} => IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpWhoIsJson(Map json) { return switch (json) { {'ip': final String ip, 'country_code': final String countryCode} => IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } static IpInfo fromMyIpJson(Map json) { return switch (json) { {'ip': final String ip, 'cc': final String countryCode} => IpInfo( ip: ip, countryCode: countryCode, ), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpAPIJson(Map json) { return switch (json) { {'query': final String ip, 'countryCode': final String countryCode} => IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIdentMeJson(Map json) { return switch (json) { {'ip': final String ip, 'cc': final String countryCode} => IpInfo( ip: ip, countryCode: countryCode, ), _ => throw const FormatException('invalid json'), }; } } @freezed abstract class HotKeyAction with _$HotKeyAction { const factory HotKeyAction({ required HotAction action, int? key, @Default({}) Set modifiers, }) = _HotKeyAction; factory HotKeyAction.fromJson(Map json) => _$HotKeyActionFromJson(json); } typedef Validator = String? Function(String? value); @freezed abstract class Field with _$Field { const factory Field({ required String label, required String value, Validator? validator, }) = _Field; } class PopupMenuItemData { const PopupMenuItemData({ this.icon, required this.label, this.onPressed, this.danger = false, this.subItems = const [], }); final String label; final VoidCallback? onPressed; final IconData? icon; final bool danger; final List subItems; } @freezed abstract class AndroidState with _$AndroidState { const factory AndroidState({ required String currentProfileName, required String stopText, required bool onlyStatisticsProxy, required bool crashlytics, }) = _AndroidState; factory AndroidState.fromJson(Map json) => _$AndroidStateFromJson(json); } class CloseWindowIntent extends Intent { const CloseWindowIntent(); } @freezed abstract class Result with _$Result { const factory Result({ required T? data, required ResultType type, required String message, }) = _Result; factory Result.success(T data) => Result(data: data, type: ResultType.success, message: ''); factory Result.error(String message) => Result(data: null, type: ResultType.error, message: message); } extension ResultExt on Result { bool get isError => type == ResultType.error; bool get isSuccess => type == ResultType.success; } @freezed abstract class Script with _$Script { const factory Script({ required String id, required String label, required String content, }) = _Script; factory Script.create({required String label, required String content}) { return Script(id: utils.uuidV4, label: label, content: content); } factory Script.fromJson(Map json) => _$ScriptFromJson(json); } extension ScriptsExt on List