// ignore_for_file: invalid_annotation_target import 'dart:math'; 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 class NavigationItem with _$NavigationItem { const factory NavigationItem({ required Icon icon, required PageLabel label, final String? description, required Widget fragment, @Default(true) bool keep, String? path, @Default([NavigationItemMode.mobile, NavigationItemMode.desktop]) List modes, }) = _NavigationItem; } @freezed 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); } @freezed class Metadata with _$Metadata { const factory Metadata({ required int uid, required String network, required String sourceIP, required String sourcePort, required String destinationIP, required String destinationPort, required String host, required String process, required String remoteDestination, }) = _Metadata; factory Metadata.fromJson(Map json) => _$MetadataFromJson(json); } @freezed class Connection with _$Connection { const factory Connection({ required String id, num? upload, num? download, required DateTime start, required Metadata metadata, required List chains, }) = _Connection; factory Connection.fromJson(Map json) => _$ConnectionFromJson(json); } extension ConnectionExt on Connection { 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 _logDateTime(_) { return DateTime.now().toString(); } // String _logId(_) { // return utils.id; // } @freezed class Log with _$Log { const factory Log({ @JsonKey(name: "LogLevel") @Default(LogLevel.app) 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 class LogsState with _$LogsState { const factory LogsState({ @Default([]) List logs, @Default([]) List keywords, @Default("") String query, @Default(false) bool loading, }) = _LogsState; } extension LogsStateExt on LogsState { List get list { final lowQuery = query.toLowerCase(); return logs.where( (log) { final payload = log.payload.toLowerCase(); final logLevelName = log.logLevel.name; return {logLevelName}.containsAll(keywords) && ((payload.contains(lowQuery)) || logLevelName.contains(lowQuery)); }, ).toList(); } } @freezed class ConnectionsState with _$ConnectionsState { const factory ConnectionsState({ @Default([]) List connections, @Default([]) List keywords, @Default("") String query, @Default(false) bool loading, }) = _ConnectionsState; } extension ConnectionsStateExt on ConnectionsState { List get list { final lowerQuery = query.toLowerCase().trim(); final lowQuery = query.toLowerCase(); return connections.where((connection) { final chains = connection.chains; final process = connection.metadata.process; final networkText = connection.metadata.network.toLowerCase(); final hostText = connection.metadata.host.toLowerCase(); final destinationIPText = connection.metadata.destinationIP.toLowerCase(); final processText = connection.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 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 class FileInfo with _$FileInfo { const factory FileInfo({ required int size, required DateTime lastModified, }) = _FileInfo; } extension FileInfoExt on FileInfo { String get desc => "${TrafficValue(value: size).show} · ${lastModified.lastUpdateTimeDesc}"; } @freezed class VersionInfo with _$VersionInfo { const factory VersionInfo({ @Default("") String clashName, @Default("") String version, }) = _VersionInfo; factory VersionInfo.fromJson(Map json) => _$VersionInfoFromJson(json); } class Traffic { int id; TrafficValue up; TrafficValue down; Traffic({int? up, int? down}) : id = DateTime.now().millisecondsSinceEpoch, up = TrafficValue(value: up), down = TrafficValue(value: down); num get speed => up.value + down.value; factory Traffic.fromMap(Map map) { return Traffic( up: map['up'], down: map['down'], ); } @override String toString() { return '$up↑ $down↓'; } @override bool operator ==(Object other) => identical(this, other) || other is Traffic && runtimeType == other.runtimeType && id == other.id && up == other.up && down == other.down; @override int get hashCode => id.hashCode ^ up.hashCode ^ down.hashCode; } @immutable class TrafficValueShow { final double value; final TrafficUnit unit; const TrafficValueShow({ required this.value, required this.unit, }); } @freezed class Proxy with _$Proxy { const factory Proxy({ required String name, required String type, String? now, }) = _Proxy; factory Proxy.fromJson(Map json) => _$ProxyFromJson(json); } @freezed 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; } } @immutable class TrafficValue { final int _value; const TrafficValue({int? value}) : _value = value ?? 0; int get value => _value; String get show => "$showValue $showUnit"; String get shortShow => "${trafficValueShow.value.fixed(decimals: 1)} $showUnit"; String get showValue => trafficValueShow.value.fixed(); String get showUnit => trafficValueShow.unit.name; TrafficValueShow get trafficValueShow { if (_value > pow(1024, 4)) { return TrafficValueShow( value: _value / pow(1024, 4), unit: TrafficUnit.TB, ); } if (_value > pow(1024, 3)) { return TrafficValueShow( value: _value / pow(1024, 3), unit: TrafficUnit.GB, ); } if (_value > pow(1024, 2)) { return TrafficValueShow( value: _value / pow(1024, 2), unit: TrafficUnit.MB); } if (_value > pow(1024, 1)) { return TrafficValueShow( value: _value / pow(1024, 1), unit: TrafficUnit.KB, ); } return TrafficValueShow( value: _value.toDouble(), unit: TrafficUnit.B, ); } @override String toString() { return "$showValue$showUnit"; } @override bool operator ==(Object other) => identical(this, other) || other is TrafficValue && runtimeType == other.runtimeType && _value == other._value; @override int get hashCode => _value.hashCode; } @freezed 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, ); } } class IpInfo { final String ip; final String countryCode; const IpInfo({ required this.ip, required this.countryCode, }); 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"), }; } @override String toString() { return 'IpInfo{ip: $ip, countryCode: $countryCode}'; } } @freezed 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 class Field with _$Field { const factory Field({ required String label, required String value, Validator? validator, }) = _Field; } enum PopupMenuItemType { primary, danger, } class PopupMenuItemData { const PopupMenuItemData({ this.icon, required this.label, required this.onPressed, this.type, this.iconSize, }); final double? iconSize; final String label; final VoidCallback? onPressed; final IconData? icon; final PopupMenuItemType? type; } @freezed class TextPainterParams with _$TextPainterParams { const factory TextPainterParams({ required String? text, required double? fontSize, required double textScaleFactor, @Default(double.infinity) double maxWidth, int? maxLines, }) = _TextPainterParams; factory TextPainterParams.fromJson(Map json) => _$TextPainterParamsFromJson(json); }