Compare commits
1 Commits
dev
...
v0.8.92-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4370df6a61 |
@@ -22,7 +22,6 @@
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:name=".Application"
|
||||
|
||||
@@ -85,24 +85,22 @@ class NotificationModule(private val service: Service) : Module() {
|
||||
|
||||
private val notificationBuilder: NotificationCompat.Builder by lazy {
|
||||
val intent = Intent().setComponent(Components.MAIN_ACTIVITY)
|
||||
|
||||
NotificationCompat.Builder(
|
||||
service, GlobalState.NOTIFICATION_CHANNEL
|
||||
).apply {
|
||||
setSmallIcon(R.drawable.ic_service)
|
||||
with(
|
||||
NotificationCompat.Builder(
|
||||
service, GlobalState.NOTIFICATION_CHANNEL
|
||||
)
|
||||
) {
|
||||
setSmallIcon(R.drawable.ic)
|
||||
setContentTitle("FlClash")
|
||||
setContentIntent(intent.toPendingIntent)
|
||||
setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
setOngoing(true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
setOngoing(true)
|
||||
setShowWhen(true)
|
||||
setOnlyAlertOnce(true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
setRequestPromotedOngoing(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -532,9 +532,6 @@ func handleDelFile(path string, result ActionResult) {
|
||||
}
|
||||
|
||||
func handleSetupConfig(bytes []byte) string {
|
||||
if !isInit {
|
||||
return "not initialized"
|
||||
}
|
||||
var params = defaultSetupParams()
|
||||
err := UnmarshalJson(bytes, params)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ export 'file.dart';
|
||||
export 'fixed.dart';
|
||||
export 'function.dart';
|
||||
export 'future.dart';
|
||||
export 'hive.dart';
|
||||
export 'http.dart';
|
||||
export 'icons.dart';
|
||||
export 'indexing.dart';
|
||||
|
||||
@@ -29,7 +29,6 @@ final listHeaderPadding = EdgeInsets.only(
|
||||
top: 24.mAp,
|
||||
bottom: 8.mAp,
|
||||
);
|
||||
const sheetAppBarHeight = 68.0;
|
||||
|
||||
const watchExecution = true;
|
||||
|
||||
@@ -63,7 +62,7 @@ const defaultTestUrl = 'https://www.gstatic.com/generate_204';
|
||||
final commonFilter = ImageFilter.blur(
|
||||
sigmaX: 5,
|
||||
sigmaY: 5,
|
||||
tileMode: TileMode.clamp,
|
||||
tileMode: TileMode.mirror,
|
||||
);
|
||||
|
||||
const listEquality = ListEquality();
|
||||
@@ -78,7 +77,6 @@ const scriptListEquality = ListEquality<Script>();
|
||||
const externalProviderListEquality = ListEquality<ExternalProvider>();
|
||||
const packageListEquality = ListEquality<Package>();
|
||||
const profileListEquality = ListEquality<Profile>();
|
||||
const proxyGroupsEquality = ListEquality<ProxyGroup>();
|
||||
const hotKeyActionListEquality = ListEquality<HotKeyAction>();
|
||||
const stringAndStringMapEquality = MapEquality<String, String>();
|
||||
const stringAndStringMapEntryListEquality =
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/manager/manager.dart';
|
||||
import 'package:fl_clash/models/state.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:fl_clash/widgets/sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension BuildContextExtension on BuildContext {
|
||||
@@ -12,15 +9,6 @@ extension BuildContextExtension on BuildContext {
|
||||
return findAncestorStateOfType<CommonScaffoldState>();
|
||||
}
|
||||
|
||||
double get sheetTopPadding {
|
||||
final sheetType = SheetProvider.of(this)!.type;
|
||||
if (sheetType == SheetType.bottomSheet) {
|
||||
return sheetAppBarHeight;
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
void showNotifier(String text, {MessageActionState? actionState}) {
|
||||
return findAncestorStateOfType<StatusManagerState>()?.message(
|
||||
text,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
|
||||
class Debouncer {
|
||||
final Map<dynamic, Timer?> _operations = {};
|
||||
final Map<FunctionTag, Timer?> _operations = {};
|
||||
|
||||
void call(
|
||||
dynamic tag,
|
||||
FunctionTag tag,
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration? duration,
|
||||
@@ -29,10 +30,10 @@ class Debouncer {
|
||||
}
|
||||
|
||||
class Throttler {
|
||||
final Map<dynamic, Timer?> _operations = {};
|
||||
final Map<FunctionTag, Timer?> _operations = {};
|
||||
|
||||
bool call(
|
||||
dynamic tag,
|
||||
FunctionTag tag,
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
|
||||
0
lib/common/hive.dart
Normal file
0
lib/common/hive.dart
Normal file
@@ -255,10 +255,6 @@ class Indexing {
|
||||
.followedBy(generateNKeysBetween(c, b, n - mid - 1))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<String?> generateNKeys(int n) {
|
||||
return generateNKeysBetween(null, null, n);
|
||||
}
|
||||
}
|
||||
|
||||
final indexing = Indexing();
|
||||
|
||||
@@ -11,25 +11,16 @@ class Measure {
|
||||
: _measureMap = {},
|
||||
_textScaler = TextScaler.linear(textScaleFactor);
|
||||
|
||||
TextPainter computeText(Text text, {TextStyle? style, double? maxWidth}) {
|
||||
return TextPainter(
|
||||
text: TextSpan(text: text.data, style: text.style ?? style),
|
||||
Size computeTextSize(Text text, {double maxWidth = double.infinity}) {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(text: text.data, style: text.style),
|
||||
maxLines: text.maxLines,
|
||||
textScaler: _textScaler,
|
||||
textDirection: text.textDirection ?? TextDirection.ltr,
|
||||
)..layout(maxWidth: maxWidth ?? double.infinity);
|
||||
}
|
||||
|
||||
Size computeTextSize(Text text, {TextStyle? style, double? maxWidth}) {
|
||||
final textPainter = computeText(text, style: style, maxWidth: maxWidth);
|
||||
)..layout(maxWidth: maxWidth);
|
||||
return textPainter.size;
|
||||
}
|
||||
|
||||
bool computeTextIsOverflow(Text text, {TextStyle? style, double? maxWidth}) {
|
||||
final textPainter = computeText(text, style: style, maxWidth: maxWidth);
|
||||
return textPainter.didExceedMaxLines;
|
||||
}
|
||||
|
||||
double get bodyMediumHeight {
|
||||
return _measureMap.updateCacheValue(
|
||||
'bodyMediumHeight',
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
T get value => state;
|
||||
|
||||
@@ -44,7 +41,3 @@ mixin AsyncNotifierMixin<T> on AnyNotifier<AsyncValue<T>, T> {
|
||||
state = AsyncData(value);
|
||||
}
|
||||
}
|
||||
|
||||
mixin UniqueKeyStateMixin<T extends StatefulWidget> on State<T> {
|
||||
final key = utils.id;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -611,16 +610,3 @@ String _getScriptPath(String root, String fileName) {
|
||||
String _getProfilePath(String root, String fileName) {
|
||||
return join(root, 'profiles', '$fileName.yaml');
|
||||
}
|
||||
|
||||
Future<List<T>> mapListTask<T, S>(List<S> results, T Function(S) mapper) async {
|
||||
return await compute<VM2<List<S>, T Function(S)>, List<T>>(
|
||||
_mapListTask,
|
||||
VM2(results, mapper),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<T>> _mapListTask<T, S>(VM2<List<S>, T Function(S)> vm2) async {
|
||||
final results = vm2.a;
|
||||
final mapper = vm2.b;
|
||||
return results.map((item) => mapper(item)).toList();
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ class CommonTheme {
|
||||
final Map<String, Color> _colorMap;
|
||||
final double textScaleFactor;
|
||||
|
||||
CommonTheme.of(this.context, this.textScaleFactor) : _colorMap = {};
|
||||
CommonTheme.of(
|
||||
this.context,
|
||||
this.textScaleFactor,
|
||||
) : _colorMap = {};
|
||||
|
||||
Color get darkenSecondaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darkenSecondaryContainer',
|
||||
() => context.colorScheme.secondaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.1,
|
||||
),
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,20 +31,16 @@ class CommonTheme {
|
||||
Color get darken2SecondaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darken2SecondaryContainer',
|
||||
() => context.colorScheme.secondaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.2,
|
||||
),
|
||||
() => context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.2),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken3PrimaryContainer {
|
||||
return _colorMap.updateCacheValue(
|
||||
'darken3PrimaryContainer',
|
||||
() => context.colorScheme.primaryContainer.blendDarken(
|
||||
context,
|
||||
factor: 0.3,
|
||||
),
|
||||
() => context.colorScheme.primaryContainer
|
||||
.blendDarken(context, factor: 0.3),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,10 +210,6 @@ extension StateControllerExt on AppController {
|
||||
return _ref.read(isMobileViewProvider);
|
||||
}
|
||||
|
||||
Size get viewSize {
|
||||
return _ref.read(viewSizeProvider);
|
||||
}
|
||||
|
||||
bool get isStart {
|
||||
return _ref.read(isStartProvider);
|
||||
}
|
||||
@@ -234,6 +230,10 @@ extension StateControllerExt on AppController {
|
||||
return _ref.read(getSelectedProxyNameProvider(groupName));
|
||||
}
|
||||
|
||||
Future<SetupState> getSetupState(int profileId) async {
|
||||
return await _ref.read(setupStateProvider(profileId).future);
|
||||
}
|
||||
|
||||
String getRealTestUrl(String? url) {
|
||||
return _ref.read(realTestUrlProvider(url));
|
||||
}
|
||||
@@ -669,7 +669,7 @@ extension SetupControllerExt on AppController {
|
||||
|
||||
Future<Map<String, dynamic>> getProfile({
|
||||
required SetupState setupState,
|
||||
required PatchClashConfig patchConfig,
|
||||
required ClashConfig patchConfig,
|
||||
}) async {
|
||||
final profileId = setupState.profileId;
|
||||
if (profileId == null) {
|
||||
@@ -779,6 +779,7 @@ extension CoreControllerExt on AppController {
|
||||
} else {
|
||||
await updateGroups();
|
||||
}
|
||||
_ref.read(coreInitProvider.notifier).value = true;
|
||||
}
|
||||
|
||||
Future<void> _connectCore() async {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
part of 'database.dart';
|
||||
|
||||
class StringMapConverter extends TypeConverter<Map<String, String>, String> {
|
||||
const StringMapConverter();
|
||||
|
||||
@override
|
||||
Map<String, String> fromSql(String fromDb) {
|
||||
return Map<String, String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Map<String, String> value) {
|
||||
return json.encode(value);
|
||||
}
|
||||
}
|
||||
|
||||
class StringListConverter extends TypeConverter<List<String>, String> {
|
||||
const StringListConverter();
|
||||
|
||||
@override
|
||||
List<String> fromSql(String fromDb) {
|
||||
return List<String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(List<String> value) {
|
||||
return json.encode(value.toList());
|
||||
}
|
||||
}
|
||||
|
||||
class StringSetConverter extends TypeConverter<Set<String>, String> {
|
||||
const StringSetConverter();
|
||||
|
||||
@override
|
||||
Set<String> fromSql(String fromDb) {
|
||||
return Set<String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Set<String> value) {
|
||||
return json.encode(value.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -9,23 +8,21 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
part 'converter.dart';
|
||||
part 'generated/database.g.dart';
|
||||
part 'groups.dart';
|
||||
part 'links.dart';
|
||||
part 'profiles.dart';
|
||||
part 'rules.dart';
|
||||
part 'scripts.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [Profiles, Scripts, Rules, ProfileRuleLinks, ProxyGroups],
|
||||
daos: [ProfilesDao, ScriptsDao, RulesDao, ProxyGroupsDao],
|
||||
tables: [Profiles, Scripts, Rules, ProfileRuleLinks],
|
||||
daos: [ProfilesDao, ScriptsDao, RulesDao],
|
||||
)
|
||||
class Database extends _$Database {
|
||||
Database([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 2;
|
||||
int get schemaVersion => 1;
|
||||
|
||||
static LazyDatabase _openConnection() {
|
||||
return LazyDatabase(() async {
|
||||
@@ -34,22 +31,6 @@ class Database extends _$Database {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onUpgrade: (m, from, to) async {
|
||||
if (from < 2) {
|
||||
await m.createTable(proxyGroups);
|
||||
await _resetOrders();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _resetOrders() async {
|
||||
await rulesDao.resetOrders();
|
||||
}
|
||||
|
||||
Future<void> restore(
|
||||
List<Profile> profiles,
|
||||
List<Script> scripts,
|
||||
@@ -73,17 +54,6 @@ class Database extends _$Database {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setProfileCustomData(
|
||||
int profileId,
|
||||
List<ProxyGroup> groups,
|
||||
List<Rule> rules,
|
||||
) async {
|
||||
await batch((b) {
|
||||
proxyGroupsDao.setAllWithBatch(profileId, b, groups);
|
||||
rulesDao.setCustomRulesWithBatch(profileId, b, rules);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
|
||||
@@ -91,21 +61,9 @@ extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
|
||||
Batch batch,
|
||||
Iterable<Insertable<Row>> items, {
|
||||
required Expression<bool> Function(Tbl tbl) deleteFilter,
|
||||
bool preDelete = false,
|
||||
}) async {
|
||||
if (preDelete) {
|
||||
batch.deleteWhere(this, deleteFilter);
|
||||
}
|
||||
batch.insertAllOnConflictUpdate(this, items);
|
||||
if (!preDelete) {
|
||||
batch.deleteWhere(this, deleteFilter);
|
||||
}
|
||||
}
|
||||
|
||||
Selectable<int?> get count {
|
||||
final countExp = countAll();
|
||||
final query = select().addColumns([countExp]);
|
||||
return query.map((row) => row.read(countExp));
|
||||
batch.deleteWhere(this, deleteFilter);
|
||||
}
|
||||
|
||||
Future<int> remove(Expression<bool> Function(Tbl tbl) filter) async {
|
||||
@@ -117,78 +75,4 @@ extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
|
||||
}
|
||||
}
|
||||
|
||||
extension SimpleSelectStatementExt<T extends HasResultSet, D>
|
||||
on SimpleSelectStatement<T, D> {
|
||||
Selectable<int> get count {
|
||||
final countExp = countAll();
|
||||
final query = addColumns([countExp]);
|
||||
return query.map((row) => row.read(countExp)!);
|
||||
}
|
||||
}
|
||||
|
||||
extension JoinedSelectStatementExt<T extends HasResultSet, D>
|
||||
on JoinedSelectStatement<T, D> {
|
||||
Selectable<int> get count {
|
||||
final countExp = countAll();
|
||||
addColumns([countExp]);
|
||||
return map((row) => row.read(countExp)!);
|
||||
}
|
||||
}
|
||||
|
||||
// extension _AsyncMapPerSubscription<S> on Stream<S> {
|
||||
// Stream<T> asyncMapPerSubscription<T>(FutureOr<T> Function(S) mapper) {
|
||||
// return Stream.multi((listener) {
|
||||
// late StreamSubscription<S> subscription;
|
||||
//
|
||||
// void onData(S original) {
|
||||
// subscription.pause();
|
||||
// Future.sync(() => mapper(original))
|
||||
// .then(listener.addSync, onError: listener.addErrorSync)
|
||||
// .whenComplete(subscription.resume);
|
||||
// }
|
||||
//
|
||||
// subscription = listen(
|
||||
// onData,
|
||||
// onError: listener.addErrorSync,
|
||||
// onDone: listener.closeSync,
|
||||
// cancelOnError: false, // Determined by downstream subscription
|
||||
// );
|
||||
//
|
||||
// listener
|
||||
// ..onPause = subscription.pause
|
||||
// ..onResume = subscription.resume
|
||||
// ..onCancel = subscription.cancel;
|
||||
// }, isBroadcast: isBroadcast);
|
||||
// }
|
||||
// }
|
||||
|
||||
// extension SelectableExt<T> on Selectable<T> {
|
||||
// Selectable<N> isolateMap<N>(N Function(T) mapper) {
|
||||
// return _IsolateMappedSelectable<T, N>(this, mapper);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// class _IsolateMappedSelectable<S, T> extends Selectable<T> {
|
||||
// final Selectable<S> _source;
|
||||
// final T Function(S) _mapper;
|
||||
//
|
||||
// _IsolateMappedSelectable(this._source, this._mapper);
|
||||
//
|
||||
// @override
|
||||
// Future<List<T>> get() {
|
||||
// return _source.get().then(_mapResults);
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Stream<List<T>> watch() {
|
||||
// return _AsyncMapPerSubscription(
|
||||
// _source.watch(),
|
||||
// ).asyncMapPerSubscription(_mapResults);
|
||||
// }
|
||||
//
|
||||
// Future<List<T>> _mapResults(List<S> results) async {
|
||||
// return mapListTask(results, _mapper);
|
||||
// }
|
||||
// }
|
||||
|
||||
final database = Database();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,172 +0,0 @@
|
||||
part of 'database.dart';
|
||||
|
||||
@DataClassName('RawProxyGroup')
|
||||
@TableIndex(
|
||||
name: 'idx_profile_name_order',
|
||||
columns: {#profileId, #name, #order},
|
||||
)
|
||||
class ProxyGroups extends Table {
|
||||
@override
|
||||
String get tableName => 'proxy_groups';
|
||||
|
||||
IntColumn get profileId => integer().nullable().references(
|
||||
Profiles,
|
||||
#id,
|
||||
onDelete: KeyAction.cascade,
|
||||
)();
|
||||
|
||||
TextColumn get name => text()();
|
||||
|
||||
TextColumn get type => text()();
|
||||
|
||||
TextColumn get proxies =>
|
||||
text().map(const StringListConverter()).nullable()();
|
||||
|
||||
TextColumn get use => text().map(const StringListConverter()).nullable()();
|
||||
|
||||
TextColumn get url => text().nullable()();
|
||||
|
||||
IntColumn get interval => integer().nullable()();
|
||||
|
||||
IntColumn get timeout => integer().nullable()();
|
||||
|
||||
IntColumn get maxFailedTimes => integer().nullable()();
|
||||
|
||||
BoolColumn get lazy => boolean().nullable()();
|
||||
|
||||
BoolColumn get disableUDP => boolean().nullable()();
|
||||
|
||||
TextColumn get filter => text().nullable()();
|
||||
|
||||
TextColumn get excludeFilter => text().nullable()();
|
||||
|
||||
TextColumn get excludeType => text().nullable()();
|
||||
|
||||
TextColumn get expectedStatus => text().nullable()();
|
||||
|
||||
BoolColumn get includeAll => boolean().nullable()();
|
||||
|
||||
BoolColumn get includeAllProxies => boolean().nullable()();
|
||||
|
||||
BoolColumn get includeAllProviders => boolean().nullable()();
|
||||
|
||||
BoolColumn get hidden => boolean().nullable()();
|
||||
|
||||
TextColumn get icon => text().nullable()();
|
||||
|
||||
TextColumn get order => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {profileId, name};
|
||||
}
|
||||
|
||||
@DriftAccessor(tables: [ProxyGroups])
|
||||
class ProxyGroupsDao extends DatabaseAccessor<Database>
|
||||
with _$ProxyGroupsDaoMixin {
|
||||
ProxyGroupsDao(super.attachedDatabase);
|
||||
|
||||
Selectable<ProxyGroup> all(int profileId) {
|
||||
final stmt = proxyGroups.select();
|
||||
stmt.where((row) => row.profileId.equals(profileId));
|
||||
stmt.orderBy([
|
||||
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
|
||||
]);
|
||||
return stmt.map((item) => item.toProxyGroup());
|
||||
}
|
||||
|
||||
Selectable<int> count(int profileId) {
|
||||
final stmt = proxyGroups.select();
|
||||
stmt.where((row) => row.profileId.equals(profileId));
|
||||
stmt.orderBy([
|
||||
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
|
||||
]);
|
||||
return stmt.count;
|
||||
}
|
||||
|
||||
Selectable<ProxyGroup> get(int profileId, String name) {
|
||||
final stmt = proxyGroups.select();
|
||||
stmt.where(
|
||||
(row) => row.profileId.equals(profileId) & row.name.equals(name),
|
||||
);
|
||||
return stmt.map((item) => item.toProxyGroup());
|
||||
}
|
||||
|
||||
Future<int> order(
|
||||
int profileId, {
|
||||
required ProxyGroup proxyGroup,
|
||||
required String order,
|
||||
}) async {
|
||||
return await proxyGroups.insertOnConflictUpdate(
|
||||
proxyGroup.toCompanion(profileId, order),
|
||||
);
|
||||
}
|
||||
|
||||
void setAllWithBatch(
|
||||
int profileId,
|
||||
Batch batch,
|
||||
Iterable<ProxyGroup> proxyGroups,
|
||||
) async {
|
||||
final keys = indexing.generateNKeys(proxyGroups.length);
|
||||
this.proxyGroups.setAll(
|
||||
batch,
|
||||
proxyGroups.mapIndexed(
|
||||
(index, item) => item.toCompanion(profileId, keys[index]),
|
||||
),
|
||||
deleteFilter: (row) => row.profileId.equals(profileId),
|
||||
preDelete: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension RawProxyGroupExt on RawProxyGroup {
|
||||
ProxyGroup toProxyGroup() {
|
||||
return ProxyGroup(
|
||||
name: name,
|
||||
type: GroupType.parseProfileType(type),
|
||||
proxies: proxies,
|
||||
url: url,
|
||||
interval: interval,
|
||||
timeout: timeout,
|
||||
maxFailedTimes: maxFailedTimes,
|
||||
lazy: lazy,
|
||||
disableUDP: disableUDP,
|
||||
filter: filter,
|
||||
excludeFilter: excludeFilter,
|
||||
excludeType: excludeType,
|
||||
expectedStatus: expectedStatus,
|
||||
includeAll: includeAll,
|
||||
includeAllProxies: includeAllProxies,
|
||||
includeAllProviders: includeAllProviders,
|
||||
hidden: hidden,
|
||||
icon: icon,
|
||||
order: order,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ProxyGroupsCompanionExt on ProxyGroup {
|
||||
ProxyGroupsCompanion toCompanion(int profileId, [String? order]) {
|
||||
return ProxyGroupsCompanion.insert(
|
||||
profileId: Value(profileId),
|
||||
name: name,
|
||||
type: type.value,
|
||||
proxies: Value(proxies),
|
||||
url: Value(url),
|
||||
interval: Value(interval),
|
||||
timeout: Value(timeout),
|
||||
maxFailedTimes: Value(maxFailedTimes),
|
||||
lazy: Value(lazy),
|
||||
disableUDP: Value(disableUDP),
|
||||
filter: Value(filter),
|
||||
excludeFilter: Value(excludeFilter),
|
||||
excludeType: Value(excludeType),
|
||||
expectedStatus: Value(expectedStatus),
|
||||
includeAll: Value(includeAll),
|
||||
includeAllProxies: Value(includeAllProxies),
|
||||
includeAllProviders: Value(includeAllProviders),
|
||||
hidden: Value(hidden),
|
||||
icon: Value(icon),
|
||||
order: Value(order ?? this.order),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -40,13 +40,13 @@ extension RawProfileRuleLinkExt on RawProfileRuleLink {
|
||||
}
|
||||
|
||||
extension ProfileRuleLinksCompanionExt on ProfileRuleLink {
|
||||
ProfileRuleLinksCompanion toCompanion([String? order]) {
|
||||
ProfileRuleLinksCompanion toCompanion() {
|
||||
return ProfileRuleLinksCompanion.insert(
|
||||
id: key,
|
||||
ruleId: ruleId,
|
||||
scene: Value(scene),
|
||||
profileId: Value(profileId),
|
||||
order: Value(order ?? this.order),
|
||||
order: Value(order),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,34 @@ class ProfilesDao extends DatabaseAccessor<Database> with _$ProfilesDaoMixin {
|
||||
}
|
||||
}
|
||||
|
||||
class StringMapConverter extends TypeConverter<Map<String, String>, String> {
|
||||
const StringMapConverter();
|
||||
|
||||
@override
|
||||
Map<String, String> fromSql(String fromDb) {
|
||||
return Map<String, String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Map<String, String> value) {
|
||||
return json.encode(value);
|
||||
}
|
||||
}
|
||||
|
||||
class StringSetConverter extends TypeConverter<Set<String>, String> {
|
||||
const StringSetConverter();
|
||||
|
||||
@override
|
||||
Set<String> fromSql(String fromDb) {
|
||||
return Set<String>.from(json.decode(fromDb));
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(Set<String> value) {
|
||||
return json.encode(value.toList());
|
||||
}
|
||||
}
|
||||
|
||||
extension RawProfilExt on RawProfile {
|
||||
Profile toProfile() {
|
||||
return Profile(
|
||||
|
||||
@@ -29,18 +29,6 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
return _get(profileId: profileId, scene: RuleScene.disabled);
|
||||
}
|
||||
|
||||
Selectable<Rule> allProfileCustomRules(int profileId) {
|
||||
return _get(profileId: profileId, scene: RuleScene.custom);
|
||||
}
|
||||
|
||||
Selectable<int> profileCustomRulesCount(int profileId) {
|
||||
final query = _getSelectStatement(
|
||||
profileId: profileId,
|
||||
scene: RuleScene.custom,
|
||||
);
|
||||
return query.count;
|
||||
}
|
||||
|
||||
Selectable<Rule> allAddedRules(int profileId) {
|
||||
final disabledIdsQuery = selectOnly(profileRuleLinks)
|
||||
..addColumns([profileRuleLinks.ruleId])
|
||||
@@ -77,26 +65,6 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> resetOrders() async {
|
||||
final stmt = profileRuleLinks.select();
|
||||
|
||||
stmt.orderBy([
|
||||
(t) => OrderingTerm.asc(t.scene),
|
||||
//v0.8.92 ordering desc
|
||||
(t) => OrderingTerm.desc(t.order),
|
||||
(t) => OrderingTerm.desc(t.id),
|
||||
]);
|
||||
|
||||
final links = await stmt.map((item) => item.toLink()).get();
|
||||
final keys = indexing.generateNKeys(links.length);
|
||||
await batch((b) {
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
links.mapIndexed((index, item) => item.toCompanion(keys[index])),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void restoreWithBatch(
|
||||
Batch batch,
|
||||
Iterable<Rule> rules,
|
||||
@@ -108,10 +76,9 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
);
|
||||
final ruleIds = rules.map((item) => item.id);
|
||||
batch.deleteWhere(this.rules, (t) => t.id.isNotIn(ruleIds));
|
||||
final keys = indexing.generateNKeys(links.length);
|
||||
batch.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
links.mapIndexed((index, item) => item.toCompanion(keys[index])),
|
||||
links.map((item) => item.toCompanion()),
|
||||
);
|
||||
final linkKeys = links.map((item) => item.key);
|
||||
batch.deleteWhere(profileRuleLinks, (t) => t.id.isNotIn(linkKeys));
|
||||
@@ -129,16 +96,16 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
return _put(rule, profileId: profileId, scene: RuleScene.added);
|
||||
}
|
||||
|
||||
Future<void> putProfileCustomRule(int profileId, Rule rule) {
|
||||
return _put(rule, profileId: profileId, scene: RuleScene.custom);
|
||||
}
|
||||
|
||||
Future<void> putProfileDisabledRule(int profileId, Rule rule) {
|
||||
return _put(rule, profileId: profileId, scene: RuleScene.added);
|
||||
}
|
||||
|
||||
void setCustomRulesWithBatch(int profileId, Batch b, Iterable<Rule> rules) {
|
||||
_setWithBatch(b, rules, profileId: profileId, scene: RuleScene.custom);
|
||||
Future<void> putGlobalRules(Iterable<Rule> rules) {
|
||||
return _putAll(rules);
|
||||
}
|
||||
|
||||
Future<void> setGlobalRules(Iterable<Rule> rules) {
|
||||
return _set(rules);
|
||||
}
|
||||
|
||||
Future<int> putDisabledLink(int profileId, int ruleId) async {
|
||||
@@ -181,23 +148,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> orderProfileCustomRule(
|
||||
int profileId, {
|
||||
required int ruleId,
|
||||
required String order,
|
||||
}) async {
|
||||
return await _order(
|
||||
ruleId: ruleId,
|
||||
order: order,
|
||||
profileId: profileId,
|
||||
scene: RuleScene.custom,
|
||||
);
|
||||
}
|
||||
|
||||
JoinedSelectStatement<HasResultSet, dynamic> _getSelectStatement({
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) {
|
||||
Selectable<Rule> _get({int? profileId, RuleScene? scene}) {
|
||||
final query = select(rules).join([
|
||||
innerJoin(profileRuleLinks, profileRuleLinks.ruleId.equalsExp(rules.id)),
|
||||
]);
|
||||
@@ -209,13 +160,10 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
profileRuleLinks.scene.equalsValue(scene),
|
||||
);
|
||||
|
||||
query.orderBy([OrderingTerm.asc(profileRuleLinks.order)]);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
Selectable<Rule> _get({int? profileId, RuleScene? scene}) {
|
||||
final query = _getSelectStatement(profileId: profileId, scene: scene);
|
||||
query.orderBy([
|
||||
OrderingTerm.desc(profileRuleLinks.order),
|
||||
OrderingTerm.desc(profileRuleLinks.id),
|
||||
]);
|
||||
|
||||
return query.map((row) {
|
||||
return row.readTable(rules).toRule(row.read(profileRuleLinks.order));
|
||||
@@ -250,7 +198,6 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
ruleId: rule.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
order: rule.order,
|
||||
).toCompanion(),
|
||||
);
|
||||
});
|
||||
@@ -260,43 +207,65 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
|
||||
await rules.deleteWhere((t) => t.id.isIn(ruleIds));
|
||||
}
|
||||
|
||||
void _setWithBatch(
|
||||
Batch b,
|
||||
Future<void> _putAll(
|
||||
Iterable<Rule> rules, {
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) async {
|
||||
b.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
await batch((b) {
|
||||
b.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
rules.map(
|
||||
(item) => ProfileRuleLink(
|
||||
ruleId: item.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
b.deleteWhere(
|
||||
profileRuleLinks,
|
||||
(t) =>
|
||||
(profileId == null
|
||||
? t.profileId.isNull()
|
||||
: t.profileId.equals(profileId)) &
|
||||
(scene == null ? const Constant(true) : t.scene.equalsValue(scene)),
|
||||
);
|
||||
Future<void> _set(
|
||||
Iterable<Rule> rules, {
|
||||
int? profileId,
|
||||
RuleScene? scene,
|
||||
}) async {
|
||||
await batch((b) {
|
||||
b.insertAllOnConflictUpdate(
|
||||
this.rules,
|
||||
rules.map((item) => item.toCompanion()),
|
||||
);
|
||||
|
||||
final keys = indexing.generateNKeys(rules.length);
|
||||
b.deleteWhere(
|
||||
profileRuleLinks,
|
||||
(t) =>
|
||||
(profileId == null
|
||||
? t.profileId.isNull()
|
||||
: t.profileId.equals(profileId)) &
|
||||
(scene == null ? const Constant(true) : t.scene.equalsValue(scene)),
|
||||
);
|
||||
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
rules.mapIndexed(
|
||||
(index, item) => ProfileRuleLink(
|
||||
ruleId: item.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(keys[index]),
|
||||
),
|
||||
);
|
||||
b.insertAllOnConflictUpdate(
|
||||
profileRuleLinks,
|
||||
rules.map(
|
||||
(item) => ProfileRuleLink(
|
||||
ruleId: item.id,
|
||||
profileId: profileId,
|
||||
scene: scene,
|
||||
).toCompanion(),
|
||||
),
|
||||
);
|
||||
|
||||
b.deleteWhere(this.rules, (r) {
|
||||
final linkedIds = selectOnly(profileRuleLinks);
|
||||
linkedIds.addColumns([profileRuleLinks.ruleId]);
|
||||
return r.id.isNotInQuery(linkedIds);
|
||||
b.deleteWhere(this.rules, (r) {
|
||||
final linkedIds = selectOnly(profileRuleLinks);
|
||||
linkedIds.addColumns([profileRuleLinks.ruleId]);
|
||||
return r.id.isNotInQuery(linkedIds);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ enum GroupType {
|
||||
Relay;
|
||||
|
||||
static GroupType parseProfileType(String type) {
|
||||
return switch (type.toLowerCase()) {
|
||||
return switch (type) {
|
||||
'url-test' => URLTest,
|
||||
'select' => Selector,
|
||||
'fallback' => Fallback,
|
||||
@@ -72,13 +72,7 @@ extension GroupTypeExtension on GroupType {
|
||||
return GroupType.values[index];
|
||||
}
|
||||
|
||||
String get value => switch (this) {
|
||||
GroupType.URLTest => 'url-test',
|
||||
GroupType.Selector => 'select',
|
||||
GroupType.Fallback => 'fallback',
|
||||
GroupType.LoadBalance => 'load-balance',
|
||||
GroupType.Relay => 'relay',
|
||||
};
|
||||
String get value => GroupTypeExtension.valueList[index];
|
||||
}
|
||||
|
||||
enum UsedProxy { GLOBAL, DIRECT, REJECT }
|
||||
@@ -288,7 +282,6 @@ enum FunctionTag {
|
||||
autoScrollToEnd,
|
||||
loadedProvider,
|
||||
saveSharedFile,
|
||||
removeProxy,
|
||||
}
|
||||
|
||||
enum DashboardWidget {
|
||||
@@ -409,7 +402,7 @@ enum OverwriteType {
|
||||
// none,
|
||||
standard,
|
||||
script,
|
||||
custom,
|
||||
// custom,
|
||||
}
|
||||
|
||||
enum RuleTarget { DIRECT, REJECT, MATCH }
|
||||
@@ -431,43 +424,3 @@ enum LoadingTag { profiles, backup_restore, access, proxies }
|
||||
enum CoreStatus { connecting, connected, disconnected }
|
||||
|
||||
enum RuleScene { added, disabled, custom }
|
||||
|
||||
enum ItemPosition {
|
||||
start,
|
||||
middle,
|
||||
end,
|
||||
startAndEnd;
|
||||
|
||||
static ItemPosition get(int index, int length) {
|
||||
ItemPosition position = ItemPosition.middle;
|
||||
if (length == 1) {
|
||||
position = ItemPosition.startAndEnd;
|
||||
} else if (index == length - 1) {
|
||||
position = ItemPosition.end;
|
||||
} else if (index == 0) {
|
||||
position = ItemPosition.start;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
static ItemPosition calculateVisualPosition<T>(
|
||||
int currentIndex,
|
||||
List<T> items,
|
||||
Set<T> deletedItems,
|
||||
) {
|
||||
final currentItem = items[currentIndex];
|
||||
if (deletedItems.contains(currentItem)) {
|
||||
return ItemPosition.middle;
|
||||
}
|
||||
final int visualLength = items.length - deletedItems.length;
|
||||
if (visualLength <= 0) return ItemPosition.middle;
|
||||
int deletedCountBeforeMe = 0;
|
||||
for (int i = 0; i < currentIndex; i++) {
|
||||
if (deletedItems.contains(items[i])) {
|
||||
deletedCountBeforeMe++;
|
||||
}
|
||||
}
|
||||
final int visualIndex = currentIndex - deletedCountBeforeMe;
|
||||
return ItemPosition.get(visualIndex, visualLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,8 @@ import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/input.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:fl_clash/widgets/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final ruleItemHeight = globalState.measure.bodyMediumHeight * 2 + 14;
|
||||
|
||||
class RuleItem extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final bool isEditing;
|
||||
@@ -31,19 +28,14 @@ class RuleItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SelectedDecorationListItem(
|
||||
return CommonSelectedListItem(
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
onSelected();
|
||||
},
|
||||
title: TooltipText(
|
||||
text: Text(
|
||||
rule.value,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
),
|
||||
title: Text(
|
||||
rule.value,
|
||||
style: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
),
|
||||
onPressed: () {
|
||||
onEdit(rule);
|
||||
@@ -66,19 +58,30 @@ class RuleStatusItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecorationListItem(
|
||||
title: TooltipText(
|
||||
text: Text(
|
||||
rule.value,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
onPressed: () {
|
||||
onChange(!status);
|
||||
},
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
trailing: Switch(value: status, onChanged: onChange),
|
||||
title: Text(rule.value),
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: Switch(value: status, onChanged: onChange),
|
||||
onPressed: () {
|
||||
onChange(!status);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'generated/clash_config.freezed.dart';
|
||||
part 'generated/clash_config.g.dart';
|
||||
|
||||
const defaultClashConfig = PatchClashConfig();
|
||||
const defaultClashConfig = ClashConfig();
|
||||
|
||||
const defaultTun = Tun();
|
||||
const defaultDns = Dns();
|
||||
@@ -108,37 +108,21 @@ abstract class ProxyGroup with _$ProxyGroup {
|
||||
List<String>? use,
|
||||
int? interval,
|
||||
bool? lazy,
|
||||
@JsonKey(name: 'disable-udp') bool? disableUDP,
|
||||
String? url,
|
||||
int? timeout,
|
||||
@JsonKey(name: 'max-failed-times') int? maxFailedTimes,
|
||||
String? filter,
|
||||
@JsonKey(name: 'exclude-filter') String? excludeFilter,
|
||||
@JsonKey(name: 'expected-filter') String? excludeFilter,
|
||||
@JsonKey(name: 'exclude-type') String? excludeType,
|
||||
@JsonKey(name: 'expected-status') String? expectedStatus,
|
||||
@JsonKey(name: 'include-all') bool? includeAll,
|
||||
@JsonKey(name: 'include-all-proxies') bool? includeAllProxies,
|
||||
@JsonKey(name: 'include-all-providers') bool? includeAllProviders,
|
||||
@JsonKey(name: 'expected-status') dynamic expectedStatus,
|
||||
bool? hidden,
|
||||
String? icon,
|
||||
String? order,
|
||||
}) = _ProxyGroup;
|
||||
|
||||
factory ProxyGroup.fromJson(Map<String, Object?> json) =>
|
||||
_$ProxyGroupFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Proxy with _$Proxy {
|
||||
const factory Proxy({
|
||||
required String name,
|
||||
required String type,
|
||||
String? now,
|
||||
}) = _Proxy;
|
||||
|
||||
factory Proxy.fromJson(Map<String, Object?> json) => _$ProxyFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class RuleProvider with _$RuleProvider {
|
||||
const factory RuleProvider({required String name}) = _RuleProvider;
|
||||
@@ -227,7 +211,7 @@ abstract class FallbackFilter with _$FallbackFilter {
|
||||
const factory FallbackFilter({
|
||||
@Default(true) bool geoip,
|
||||
@Default('CN') @JsonKey(name: 'geoip-code') String geoipCode,
|
||||
@Default(['']) List<String> geosite,
|
||||
@Default(['gfw']) List<String> geosite,
|
||||
@Default(['240.0.0.0/4']) List<String> ipcidr,
|
||||
@Default(['+.google.com', '+.facebook.com', '+.youtube.com'])
|
||||
List<String> domain,
|
||||
@@ -406,7 +390,7 @@ extension RulesExt on List<Rule> {
|
||||
var newList = List<Rule>.from(this);
|
||||
final index = newList.indexWhere((item) => item.id == rule.id);
|
||||
if (index != -1) {
|
||||
rule = newList[index] = rule;
|
||||
newList[index] = rule;
|
||||
} else {
|
||||
newList.insert(0, rule);
|
||||
}
|
||||
@@ -429,36 +413,34 @@ List<Rule> _genRule(List<dynamic>? rules) {
|
||||
return rules.map((item) => Rule.value(item)).toList();
|
||||
}
|
||||
|
||||
// List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
|
||||
// return json.entries.map((entry) => RuleProvider(name: entry.key)).toList();
|
||||
// }
|
||||
//
|
||||
// List<SubRule> _genSubRules(Map<String, dynamic> json) {
|
||||
// return json.entries.map((entry) => SubRule(name: entry.key)).toList();
|
||||
// }
|
||||
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
|
||||
return json.entries.map((entry) => RuleProvider(name: entry.key)).toList();
|
||||
}
|
||||
|
||||
List<SubRule> _genSubRules(Map<String, dynamic> json) {
|
||||
return json.entries.map((entry) => SubRule(name: entry.key)).toList();
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class ClashConfigSnippet with _$ClashConfigSnippet {
|
||||
const factory ClashConfigSnippet({
|
||||
@Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,
|
||||
@JsonKey(fromJson: _genRule, name: 'rules') @Default([]) List<Rule> rule,
|
||||
@JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)
|
||||
@Default([])
|
||||
List<RuleProvider> ruleProvider,
|
||||
@JsonKey(name: 'sub-rules', fromJson: _genSubRules)
|
||||
@Default([])
|
||||
List<SubRule> subRules,
|
||||
}) = _ClashConfigSnippet;
|
||||
|
||||
factory ClashConfigSnippet.fromJson(Map<String, Object?> json) =>
|
||||
_$ClashConfigSnippetFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class ClashConfig with _$ClashConfig {
|
||||
const factory ClashConfig({
|
||||
@Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,
|
||||
@JsonKey(fromJson: _genRule) @Default([]) List<Rule> rules,
|
||||
@Default([]) List<Proxy> proxies,
|
||||
// @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)
|
||||
// @Default([])
|
||||
// List<RuleProvider> ruleProvider,
|
||||
// @JsonKey(name: 'sub-rules', fromJson: _genSubRules)
|
||||
// @Default([])
|
||||
// List<SubRule> subRules,
|
||||
@Default({}) Map<String, String> proxyTypeMap,
|
||||
}) = _ClashConfig;
|
||||
|
||||
factory ClashConfig.fromJson(Map<String, Object?> json) =>
|
||||
_$ClashConfigFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class PatchClashConfig with _$PatchClashConfig {
|
||||
const factory PatchClashConfig({
|
||||
@Default(defaultMixedPort) @JsonKey(name: 'mixed-port') int mixedPort,
|
||||
@Default(0) @JsonKey(name: 'socks-port') int socksPort,
|
||||
@Default(0) @JsonKey(name: 'port') int port,
|
||||
@@ -487,22 +469,24 @@ abstract class PatchClashConfig with _$PatchClashConfig {
|
||||
@Default(GeodataLoader.memconservative)
|
||||
@JsonKey(name: 'geodata-loader')
|
||||
GeodataLoader geodataLoader,
|
||||
@Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,
|
||||
@Default([]) List<String> rule,
|
||||
@JsonKey(name: 'global-ua') String? globalUa,
|
||||
@Default(ExternalControllerStatus.close)
|
||||
@JsonKey(name: 'external-controller')
|
||||
ExternalControllerStatus externalController,
|
||||
@Default({}) Map<String, String> hosts,
|
||||
}) = _PatchClashConfig;
|
||||
}) = _ClashConfig;
|
||||
|
||||
factory PatchClashConfig.fromJson(Map<String, Object?> json) =>
|
||||
_$PatchClashConfigFromJson(json);
|
||||
factory ClashConfig.fromJson(Map<String, Object?> json) =>
|
||||
_$ClashConfigFromJson(json);
|
||||
|
||||
factory PatchClashConfig.safeFormJson(Map<String, Object?>? json) {
|
||||
factory ClashConfig.safeFormJson(Map<String, Object?>? json) {
|
||||
if (json == null) {
|
||||
return defaultClashConfig;
|
||||
}
|
||||
try {
|
||||
return PatchClashConfig.fromJson(json);
|
||||
return ClashConfig.fromJson(json);
|
||||
} catch (_) {
|
||||
return defaultClashConfig;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'clash_config.dart';
|
||||
|
||||
part 'generated/common.freezed.dart';
|
||||
part 'generated/common.g.dart';
|
||||
|
||||
@@ -287,6 +285,17 @@ 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<String, Object?> json) => _$ProxyFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Group with _$Group {
|
||||
const factory Group({
|
||||
@@ -587,11 +596,3 @@ abstract class UpdatingMessage with _$UpdatingMessage {
|
||||
required String message,
|
||||
}) = _UpdatingMessage;
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class IconButtonData with _$IconButtonData {
|
||||
const factory IconButtonData({
|
||||
required IconData icon,
|
||||
required VoidCallback onPressed,
|
||||
}) = _IconButtonData;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ abstract class Config with _$Config {
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson) required ThemeProps themeProps,
|
||||
@Default(defaultProxiesStyleProps) ProxiesStyleProps proxiesStyleProps,
|
||||
@Default(defaultWindowProps) WindowProps windowProps,
|
||||
@Default(defaultClashConfig) PatchClashConfig patchClashConfig,
|
||||
@Default(defaultClashConfig) ClashConfig patchClashConfig,
|
||||
}) = _Config;
|
||||
|
||||
factory Config.fromJson(Map<String, Object?> json) => _$ConfigFromJson(json);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,20 +15,15 @@ _ProxyGroup _$ProxyGroupFromJson(Map<String, dynamic> json) => _ProxyGroup(
|
||||
use: (json['use'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
interval: (json['interval'] as num?)?.toInt(),
|
||||
lazy: json['lazy'] as bool?,
|
||||
disableUDP: json['disable-udp'] as bool?,
|
||||
url: json['url'] as String?,
|
||||
timeout: (json['timeout'] as num?)?.toInt(),
|
||||
maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(),
|
||||
filter: json['filter'] as String?,
|
||||
excludeFilter: json['exclude-filter'] as String?,
|
||||
excludeFilter: json['expected-filter'] as String?,
|
||||
excludeType: json['exclude-type'] as String?,
|
||||
expectedStatus: json['expected-status'] as String?,
|
||||
includeAll: json['include-all'] as bool?,
|
||||
includeAllProxies: json['include-all-proxies'] as bool?,
|
||||
includeAllProviders: json['include-all-providers'] as bool?,
|
||||
expectedStatus: json['expected-status'],
|
||||
hidden: json['hidden'] as bool?,
|
||||
icon: json['icon'] as String?,
|
||||
order: json['order'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ProxyGroupToJson(_ProxyGroup instance) =>
|
||||
@@ -39,20 +34,15 @@ Map<String, dynamic> _$ProxyGroupToJson(_ProxyGroup instance) =>
|
||||
'use': instance.use,
|
||||
'interval': instance.interval,
|
||||
'lazy': instance.lazy,
|
||||
'disable-udp': instance.disableUDP,
|
||||
'url': instance.url,
|
||||
'timeout': instance.timeout,
|
||||
'max-failed-times': instance.maxFailedTimes,
|
||||
'filter': instance.filter,
|
||||
'exclude-filter': instance.excludeFilter,
|
||||
'expected-filter': instance.excludeFilter,
|
||||
'exclude-type': instance.excludeType,
|
||||
'expected-status': instance.expectedStatus,
|
||||
'include-all': instance.includeAll,
|
||||
'include-all-proxies': instance.includeAllProxies,
|
||||
'include-all-providers': instance.includeAllProviders,
|
||||
'hidden': instance.hidden,
|
||||
'icon': instance.icon,
|
||||
'order': instance.order,
|
||||
};
|
||||
|
||||
const _$GroupTypeEnumMap = {
|
||||
@@ -63,18 +53,6 @@ const _$GroupTypeEnumMap = {
|
||||
GroupType.Relay: 'Relay',
|
||||
};
|
||||
|
||||
_Proxy _$ProxyFromJson(Map<String, dynamic> json) => _Proxy(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
now: json['now'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ProxyToJson(_Proxy instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'now': instance.now,
|
||||
};
|
||||
|
||||
_RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) =>
|
||||
_RuleProvider(name: json['name'] as String);
|
||||
|
||||
@@ -190,7 +168,7 @@ _FallbackFilter _$FallbackFilterFromJson(
|
||||
geoipCode: json['geoip-code'] as String? ?? 'CN',
|
||||
geosite:
|
||||
(json['geosite'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [''],
|
||||
const ['gfw'],
|
||||
ipcidr:
|
||||
(json['ipcidr'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const ['240.0.0.0/4'],
|
||||
@@ -326,85 +304,89 @@ Map<String, dynamic> _$SubRuleToJson(_SubRule instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
_ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) =>
|
||||
_ClashConfigSnippet(
|
||||
proxyGroups:
|
||||
(json['proxy-groups'] as List<dynamic>?)
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
rule: json['rules'] == null ? const [] : _genRule(json['rules'] as List?),
|
||||
ruleProvider: json['rule-providers'] == null
|
||||
? const []
|
||||
: _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),
|
||||
subRules: json['sub-rules'] == null
|
||||
? const []
|
||||
: _genSubRules(json['sub-rules'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ClashConfigSnippetToJson(_ClashConfigSnippet instance) =>
|
||||
<String, dynamic>{
|
||||
'proxy-groups': instance.proxyGroups,
|
||||
'rules': instance.rule,
|
||||
'rule-providers': instance.ruleProvider,
|
||||
'sub-rules': instance.subRules,
|
||||
};
|
||||
|
||||
_ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => _ClashConfig(
|
||||
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
|
||||
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
|
||||
port: (json['port'] as num?)?.toInt() ?? 0,
|
||||
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
|
||||
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
|
||||
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
|
||||
allowLan: json['allow-lan'] as bool? ?? false,
|
||||
logLevel:
|
||||
$enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
|
||||
LogLevel.error,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
findProcessMode:
|
||||
$enumDecodeNullable(
|
||||
_$FindProcessModeEnumMap,
|
||||
json['find-process-mode'],
|
||||
unknownValue: FindProcessMode.always,
|
||||
) ??
|
||||
FindProcessMode.always,
|
||||
keepAliveInterval:
|
||||
(json['keep-alive-interval'] as num?)?.toInt() ??
|
||||
defaultKeepAliveInterval,
|
||||
unifiedDelay: json['unified-delay'] as bool? ?? true,
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
|
||||
tun: json['tun'] == null
|
||||
? defaultTun
|
||||
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
|
||||
dns: json['dns'] == null
|
||||
? defaultDns
|
||||
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
|
||||
geoXUrl: json['geox-url'] == null
|
||||
? defaultGeoXUrl
|
||||
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
|
||||
geodataLoader:
|
||||
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
|
||||
GeodataLoader.memconservative,
|
||||
proxyGroups:
|
||||
(json['proxy-groups'] as List<dynamic>?)
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
rules: json['rules'] == null ? const [] : _genRule(json['rules'] as List?),
|
||||
proxies:
|
||||
(json['proxies'] as List<dynamic>?)
|
||||
?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
rule:
|
||||
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [],
|
||||
proxyTypeMap:
|
||||
(json['proxyTypeMap'] as Map<String, dynamic>?)?.map(
|
||||
globalUa: json['global-ua'] as String?,
|
||||
externalController:
|
||||
$enumDecodeNullable(
|
||||
_$ExternalControllerStatusEnumMap,
|
||||
json['external-controller'],
|
||||
) ??
|
||||
ExternalControllerStatus.close,
|
||||
hosts:
|
||||
(json['hosts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ClashConfigToJson(_ClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
'proxy-groups': instance.proxyGroups,
|
||||
'rules': instance.rules,
|
||||
'proxies': instance.proxies,
|
||||
'proxyTypeMap': instance.proxyTypeMap,
|
||||
};
|
||||
|
||||
_PatchClashConfig _$PatchClashConfigFromJson(Map<String, dynamic> json) =>
|
||||
_PatchClashConfig(
|
||||
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
|
||||
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
|
||||
port: (json['port'] as num?)?.toInt() ?? 0,
|
||||
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
|
||||
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
|
||||
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
|
||||
allowLan: json['allow-lan'] as bool? ?? false,
|
||||
logLevel:
|
||||
$enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
|
||||
LogLevel.error,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
findProcessMode:
|
||||
$enumDecodeNullable(
|
||||
_$FindProcessModeEnumMap,
|
||||
json['find-process-mode'],
|
||||
unknownValue: FindProcessMode.always,
|
||||
) ??
|
||||
FindProcessMode.always,
|
||||
keepAliveInterval:
|
||||
(json['keep-alive-interval'] as num?)?.toInt() ??
|
||||
defaultKeepAliveInterval,
|
||||
unifiedDelay: json['unified-delay'] as bool? ?? true,
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
|
||||
tun: json['tun'] == null
|
||||
? defaultTun
|
||||
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
|
||||
dns: json['dns'] == null
|
||||
? defaultDns
|
||||
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
|
||||
geoXUrl: json['geox-url'] == null
|
||||
? defaultGeoXUrl
|
||||
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
|
||||
geodataLoader:
|
||||
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
|
||||
GeodataLoader.memconservative,
|
||||
globalUa: json['global-ua'] as String?,
|
||||
externalController:
|
||||
$enumDecodeNullable(
|
||||
_$ExternalControllerStatusEnumMap,
|
||||
json['external-controller'],
|
||||
) ??
|
||||
ExternalControllerStatus.close,
|
||||
hosts:
|
||||
(json['hosts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PatchClashConfigToJson(_PatchClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
'mixed-port': instance.mixedPort,
|
||||
'socks-port': instance.socksPort,
|
||||
@@ -423,6 +405,8 @@ Map<String, dynamic> _$PatchClashConfigToJson(_PatchClashConfig instance) =>
|
||||
'dns': instance.dns,
|
||||
'geox-url': instance.geoXUrl,
|
||||
'geodata-loader': _$GeodataLoaderEnumMap[instance.geodataLoader]!,
|
||||
'proxy-groups': instance.proxyGroups,
|
||||
'rule': instance.rule,
|
||||
'global-ua': instance.globalUa,
|
||||
'external-controller':
|
||||
_$ExternalControllerStatusEnumMap[instance.externalController]!,
|
||||
|
||||
@@ -3356,6 +3356,275 @@ as String,
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Proxy {
|
||||
|
||||
String get name; String get type; String? get now;
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$ProxyCopyWith<Proxy> get copyWith => _$ProxyCopyWithImpl<Proxy>(this as Proxy, _$identity);
|
||||
|
||||
/// Serializes this Proxy to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Proxy&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.now, now) || other.now == now));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,type,now);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Proxy(name: $name, type: $type, now: $now)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ProxyCopyWith<$Res> {
|
||||
factory $ProxyCopyWith(Proxy value, $Res Function(Proxy) _then) = _$ProxyCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String name, String type, String? now
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ProxyCopyWithImpl<$Res>
|
||||
implements $ProxyCopyWith<$Res> {
|
||||
_$ProxyCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Proxy _self;
|
||||
final $Res Function(Proxy) _then;
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? type = null,Object? now = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [Proxy].
|
||||
extension ProxyPatterns on Proxy {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Proxy value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Proxy value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Proxy value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name, String type, String? now)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy() when $default != null:
|
||||
return $default(_that.name,_that.type,_that.now);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name, String type, String? now) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy():
|
||||
return $default(_that.name,_that.type,_that.now);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name, String type, String? now)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Proxy() when $default != null:
|
||||
return $default(_that.name,_that.type,_that.now);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _Proxy implements Proxy {
|
||||
const _Proxy({required this.name, required this.type, this.now});
|
||||
factory _Proxy.fromJson(Map<String, dynamic> json) => _$ProxyFromJson(json);
|
||||
|
||||
@override final String name;
|
||||
@override final String type;
|
||||
@override final String? now;
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ProxyCopyWith<_Proxy> get copyWith => __$ProxyCopyWithImpl<_Proxy>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$ProxyToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Proxy&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.now, now) || other.now == now));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,type,now);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Proxy(name: $name, type: $type, now: $now)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ProxyCopyWith<$Res> implements $ProxyCopyWith<$Res> {
|
||||
factory _$ProxyCopyWith(_Proxy value, $Res Function(_Proxy) _then) = __$ProxyCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String name, String type, String? now
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ProxyCopyWithImpl<$Res>
|
||||
implements _$ProxyCopyWith<$Res> {
|
||||
__$ProxyCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Proxy _self;
|
||||
final $Res Function(_Proxy) _then;
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? type = null,Object? now = freezed,}) {
|
||||
return _then(_Proxy(
|
||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Group {
|
||||
|
||||
@@ -5750,266 +6019,6 @@ as String,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$IconButtonData {
|
||||
|
||||
IconData get icon; VoidCallback get onPressed;
|
||||
/// Create a copy of IconButtonData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$IconButtonDataCopyWith<IconButtonData> get copyWith => _$IconButtonDataCopyWithImpl<IconButtonData>(this as IconButtonData, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is IconButtonData&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.onPressed, onPressed) || other.onPressed == onPressed));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,icon,onPressed);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IconButtonData(icon: $icon, onPressed: $onPressed)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $IconButtonDataCopyWith<$Res> {
|
||||
factory $IconButtonDataCopyWith(IconButtonData value, $Res Function(IconButtonData) _then) = _$IconButtonDataCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
IconData icon, VoidCallback onPressed
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$IconButtonDataCopyWithImpl<$Res>
|
||||
implements $IconButtonDataCopyWith<$Res> {
|
||||
_$IconButtonDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final IconButtonData _self;
|
||||
final $Res Function(IconButtonData) _then;
|
||||
|
||||
/// Create a copy of IconButtonData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? icon = null,Object? onPressed = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as IconData,onPressed: null == onPressed ? _self.onPressed : onPressed // ignore: cast_nullable_to_non_nullable
|
||||
as VoidCallback,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [IconButtonData].
|
||||
extension IconButtonDataPatterns on IconButtonData {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _IconButtonData value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _IconButtonData value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _IconButtonData value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( IconData icon, VoidCallback onPressed)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData() when $default != null:
|
||||
return $default(_that.icon,_that.onPressed);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( IconData icon, VoidCallback onPressed) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData():
|
||||
return $default(_that.icon,_that.onPressed);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( IconData icon, VoidCallback onPressed)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _IconButtonData() when $default != null:
|
||||
return $default(_that.icon,_that.onPressed);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _IconButtonData implements IconButtonData {
|
||||
const _IconButtonData({required this.icon, required this.onPressed});
|
||||
|
||||
|
||||
@override final IconData icon;
|
||||
@override final VoidCallback onPressed;
|
||||
|
||||
/// Create a copy of IconButtonData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$IconButtonDataCopyWith<_IconButtonData> get copyWith => __$IconButtonDataCopyWithImpl<_IconButtonData>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _IconButtonData&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.onPressed, onPressed) || other.onPressed == onPressed));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,icon,onPressed);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IconButtonData(icon: $icon, onPressed: $onPressed)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$IconButtonDataCopyWith<$Res> implements $IconButtonDataCopyWith<$Res> {
|
||||
factory _$IconButtonDataCopyWith(_IconButtonData value, $Res Function(_IconButtonData) _then) = __$IconButtonDataCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
IconData icon, VoidCallback onPressed
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$IconButtonDataCopyWithImpl<$Res>
|
||||
implements _$IconButtonDataCopyWith<$Res> {
|
||||
__$IconButtonDataCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _IconButtonData _self;
|
||||
final $Res Function(_IconButtonData) _then;
|
||||
|
||||
/// Create a copy of IconButtonData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? icon = null,Object? onPressed = null,}) {
|
||||
return _then(_IconButtonData(
|
||||
icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as IconData,onPressed: null == onPressed ? _self.onPressed : onPressed // ignore: cast_nullable_to_non_nullable
|
||||
as VoidCallback,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
||||
@@ -158,6 +158,18 @@ Map<String, dynamic> _$TrafficToJson(_Traffic instance) => <String, dynamic>{
|
||||
'down': instance.down,
|
||||
};
|
||||
|
||||
_Proxy _$ProxyFromJson(Map<String, dynamic> json) => _Proxy(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
now: json['now'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ProxyToJson(_Proxy instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'now': instance.now,
|
||||
};
|
||||
|
||||
_Group _$GroupFromJson(Map<String, dynamic> json) => _Group(
|
||||
type: $enumDecode(_$GroupTypeEnumMap, json['type']),
|
||||
all:
|
||||
|
||||
@@ -2326,7 +2326,7 @@ $TextScaleCopyWith<$Res> get textScale {
|
||||
/// @nodoc
|
||||
mixin _$Config {
|
||||
|
||||
int? get currentProfileId; bool get overrideDns; List<HotKeyAction> get hotKeyActions;@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps get appSettingProps; DAVProps? get davProps; NetworkProps get networkProps; VpnProps get vpnProps;@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps get themeProps; ProxiesStyleProps get proxiesStyleProps; WindowProps get windowProps; PatchClashConfig get patchClashConfig;
|
||||
int? get currentProfileId; bool get overrideDns; List<HotKeyAction> get hotKeyActions;@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps get appSettingProps; DAVProps? get davProps; NetworkProps get networkProps; VpnProps get vpnProps;@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps get themeProps; ProxiesStyleProps get proxiesStyleProps; WindowProps get windowProps; ClashConfig get patchClashConfig;
|
||||
/// Create a copy of Config
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -2359,11 +2359,11 @@ abstract mixin class $ConfigCopyWith<$Res> {
|
||||
factory $ConfigCopyWith(Config value, $Res Function(Config) _then) = _$ConfigCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions,@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, PatchClashConfig patchClashConfig
|
||||
int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions,@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, ClashConfig patchClashConfig
|
||||
});
|
||||
|
||||
|
||||
$AppSettingPropsCopyWith<$Res> get appSettingProps;$DAVPropsCopyWith<$Res>? get davProps;$NetworkPropsCopyWith<$Res> get networkProps;$VpnPropsCopyWith<$Res> get vpnProps;$ThemePropsCopyWith<$Res> get themeProps;$ProxiesStylePropsCopyWith<$Res> get proxiesStyleProps;$WindowPropsCopyWith<$Res> get windowProps;$PatchClashConfigCopyWith<$Res> get patchClashConfig;
|
||||
$AppSettingPropsCopyWith<$Res> get appSettingProps;$DAVPropsCopyWith<$Res>? get davProps;$NetworkPropsCopyWith<$Res> get networkProps;$VpnPropsCopyWith<$Res> get vpnProps;$ThemePropsCopyWith<$Res> get themeProps;$ProxiesStylePropsCopyWith<$Res> get proxiesStyleProps;$WindowPropsCopyWith<$Res> get windowProps;$ClashConfigCopyWith<$Res> get patchClashConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -2389,7 +2389,7 @@ as VpnProps,themeProps: null == themeProps ? _self.themeProps : themeProps // ig
|
||||
as ThemeProps,proxiesStyleProps: null == proxiesStyleProps ? _self.proxiesStyleProps : proxiesStyleProps // ignore: cast_nullable_to_non_nullable
|
||||
as ProxiesStyleProps,windowProps: null == windowProps ? _self.windowProps : windowProps // ignore: cast_nullable_to_non_nullable
|
||||
as WindowProps,patchClashConfig: null == patchClashConfig ? _self.patchClashConfig : patchClashConfig // ignore: cast_nullable_to_non_nullable
|
||||
as PatchClashConfig,
|
||||
as ClashConfig,
|
||||
));
|
||||
}
|
||||
/// Create a copy of Config
|
||||
@@ -2462,9 +2462,9 @@ $WindowPropsCopyWith<$Res> get windowProps {
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PatchClashConfigCopyWith<$Res> get patchClashConfig {
|
||||
$ClashConfigCopyWith<$Res> get patchClashConfig {
|
||||
|
||||
return $PatchClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {
|
||||
return $ClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {
|
||||
return _then(_self.copyWith(patchClashConfig: value));
|
||||
});
|
||||
}
|
||||
@@ -2549,7 +2549,7 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, PatchClashConfig patchClashConfig)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, ClashConfig patchClashConfig)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Config() when $default != null:
|
||||
return $default(_that.currentProfileId,_that.overrideDns,_that.hotKeyActions,_that.appSettingProps,_that.davProps,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyleProps,_that.windowProps,_that.patchClashConfig);case _:
|
||||
@@ -2570,7 +2570,7 @@ return $default(_that.currentProfileId,_that.overrideDns,_that.hotKeyActions,_th
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, PatchClashConfig patchClashConfig) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, ClashConfig patchClashConfig) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Config():
|
||||
return $default(_that.currentProfileId,_that.overrideDns,_that.hotKeyActions,_that.appSettingProps,_that.davProps,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyleProps,_that.windowProps,_that.patchClashConfig);case _:
|
||||
@@ -2590,7 +2590,7 @@ return $default(_that.currentProfileId,_that.overrideDns,_that.hotKeyActions,_th
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, PatchClashConfig patchClashConfig)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions, @JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, ClashConfig patchClashConfig)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Config() when $default != null:
|
||||
return $default(_that.currentProfileId,_that.overrideDns,_that.hotKeyActions,_that.appSettingProps,_that.davProps,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyleProps,_that.windowProps,_that.patchClashConfig);case _:
|
||||
@@ -2624,7 +2624,7 @@ class _Config implements Config {
|
||||
@override@JsonKey(fromJson: ThemeProps.safeFromJson) final ThemeProps themeProps;
|
||||
@override@JsonKey() final ProxiesStyleProps proxiesStyleProps;
|
||||
@override@JsonKey() final WindowProps windowProps;
|
||||
@override@JsonKey() final PatchClashConfig patchClashConfig;
|
||||
@override@JsonKey() final ClashConfig patchClashConfig;
|
||||
|
||||
/// Create a copy of Config
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -2659,11 +2659,11 @@ abstract mixin class _$ConfigCopyWith<$Res> implements $ConfigCopyWith<$Res> {
|
||||
factory _$ConfigCopyWith(_Config value, $Res Function(_Config) _then) = __$ConfigCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions,@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, PatchClashConfig patchClashConfig
|
||||
int? currentProfileId, bool overrideDns, List<HotKeyAction> hotKeyActions,@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSettingProps, DAVProps? davProps, NetworkProps networkProps, VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyleProps proxiesStyleProps, WindowProps windowProps, ClashConfig patchClashConfig
|
||||
});
|
||||
|
||||
|
||||
@override $AppSettingPropsCopyWith<$Res> get appSettingProps;@override $DAVPropsCopyWith<$Res>? get davProps;@override $NetworkPropsCopyWith<$Res> get networkProps;@override $VpnPropsCopyWith<$Res> get vpnProps;@override $ThemePropsCopyWith<$Res> get themeProps;@override $ProxiesStylePropsCopyWith<$Res> get proxiesStyleProps;@override $WindowPropsCopyWith<$Res> get windowProps;@override $PatchClashConfigCopyWith<$Res> get patchClashConfig;
|
||||
@override $AppSettingPropsCopyWith<$Res> get appSettingProps;@override $DAVPropsCopyWith<$Res>? get davProps;@override $NetworkPropsCopyWith<$Res> get networkProps;@override $VpnPropsCopyWith<$Res> get vpnProps;@override $ThemePropsCopyWith<$Res> get themeProps;@override $ProxiesStylePropsCopyWith<$Res> get proxiesStyleProps;@override $WindowPropsCopyWith<$Res> get windowProps;@override $ClashConfigCopyWith<$Res> get patchClashConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -2689,7 +2689,7 @@ as VpnProps,themeProps: null == themeProps ? _self.themeProps : themeProps // ig
|
||||
as ThemeProps,proxiesStyleProps: null == proxiesStyleProps ? _self.proxiesStyleProps : proxiesStyleProps // ignore: cast_nullable_to_non_nullable
|
||||
as ProxiesStyleProps,windowProps: null == windowProps ? _self.windowProps : windowProps // ignore: cast_nullable_to_non_nullable
|
||||
as WindowProps,patchClashConfig: null == patchClashConfig ? _self.patchClashConfig : patchClashConfig // ignore: cast_nullable_to_non_nullable
|
||||
as PatchClashConfig,
|
||||
as ClashConfig,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2763,9 +2763,9 @@ $WindowPropsCopyWith<$Res> get windowProps {
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PatchClashConfigCopyWith<$Res> get patchClashConfig {
|
||||
$ClashConfigCopyWith<$Res> get patchClashConfig {
|
||||
|
||||
return $PatchClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {
|
||||
return $ClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {
|
||||
return _then(_self.copyWith(patchClashConfig: value));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -339,9 +339,7 @@ _Config _$ConfigFromJson(Map<String, dynamic> json) => _Config(
|
||||
: WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),
|
||||
patchClashConfig: json['patchClashConfig'] == null
|
||||
? defaultClashConfig
|
||||
: PatchClashConfig.fromJson(
|
||||
json['patchClashConfig'] as Map<String, dynamic>,
|
||||
),
|
||||
: ClashConfig.fromJson(json['patchClashConfig'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ConfigToJson(_Config instance) => <String, dynamic>{
|
||||
|
||||
@@ -73,7 +73,6 @@ Map<String, dynamic> _$ProfileToJson(_Profile instance) => <String, dynamic>{
|
||||
const _$OverwriteTypeEnumMap = {
|
||||
OverwriteType.standard: 'standard',
|
||||
OverwriteType.script: 'script',
|
||||
OverwriteType.custom: 'custom',
|
||||
};
|
||||
|
||||
_StandardOverwrite _$StandardOverwriteFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -7364,6 +7364,287 @@ as int,
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ClashConfigState {
|
||||
|
||||
bool get overrideDns; ClashConfig get clashConfig; RouteMode get routeMode;
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$ClashConfigStateCopyWith<ClashConfigState> get copyWith => _$ClashConfigStateCopyWithImpl<ClashConfigState>(this as ClashConfigState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ClashConfigState&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.clashConfig, clashConfig) || other.clashConfig == clashConfig)&&(identical(other.routeMode, routeMode) || other.routeMode == routeMode));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,overrideDns,clashConfig,routeMode);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, routeMode: $routeMode)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ClashConfigStateCopyWith<$Res> {
|
||||
factory $ClashConfigStateCopyWith(ClashConfigState value, $Res Function(ClashConfigState) _then) = _$ClashConfigStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool overrideDns, ClashConfig clashConfig, RouteMode routeMode
|
||||
});
|
||||
|
||||
|
||||
$ClashConfigCopyWith<$Res> get clashConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ClashConfigStateCopyWithImpl<$Res>
|
||||
implements $ClashConfigStateCopyWith<$Res> {
|
||||
_$ClashConfigStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final ClashConfigState _self;
|
||||
final $Res Function(ClashConfigState) _then;
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? overrideDns = null,Object? clashConfig = null,Object? routeMode = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,clashConfig: null == clashConfig ? _self.clashConfig : clashConfig // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,routeMode: null == routeMode ? _self.routeMode : routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
));
|
||||
}
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ClashConfigCopyWith<$Res> get clashConfig {
|
||||
|
||||
return $ClashConfigCopyWith<$Res>(_self.clashConfig, (value) {
|
||||
return _then(_self.copyWith(clashConfig: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [ClashConfigState].
|
||||
extension ClashConfigStatePatterns on ClashConfigState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ClashConfigState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ClashConfigState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ClashConfigState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool overrideDns, ClashConfig clashConfig, RouteMode routeMode)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState() when $default != null:
|
||||
return $default(_that.overrideDns,_that.clashConfig,_that.routeMode);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool overrideDns, ClashConfig clashConfig, RouteMode routeMode) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState():
|
||||
return $default(_that.overrideDns,_that.clashConfig,_that.routeMode);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool overrideDns, ClashConfig clashConfig, RouteMode routeMode)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ClashConfigState() when $default != null:
|
||||
return $default(_that.overrideDns,_that.clashConfig,_that.routeMode);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _ClashConfigState implements ClashConfigState {
|
||||
const _ClashConfigState({required this.overrideDns, required this.clashConfig, required this.routeMode});
|
||||
|
||||
|
||||
@override final bool overrideDns;
|
||||
@override final ClashConfig clashConfig;
|
||||
@override final RouteMode routeMode;
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ClashConfigStateCopyWith<_ClashConfigState> get copyWith => __$ClashConfigStateCopyWithImpl<_ClashConfigState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ClashConfigState&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.clashConfig, clashConfig) || other.clashConfig == clashConfig)&&(identical(other.routeMode, routeMode) || other.routeMode == routeMode));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,overrideDns,clashConfig,routeMode);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, routeMode: $routeMode)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ClashConfigStateCopyWith<$Res> implements $ClashConfigStateCopyWith<$Res> {
|
||||
factory _$ClashConfigStateCopyWith(_ClashConfigState value, $Res Function(_ClashConfigState) _then) = __$ClashConfigStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool overrideDns, ClashConfig clashConfig, RouteMode routeMode
|
||||
});
|
||||
|
||||
|
||||
@override $ClashConfigCopyWith<$Res> get clashConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ClashConfigStateCopyWithImpl<$Res>
|
||||
implements _$ClashConfigStateCopyWith<$Res> {
|
||||
__$ClashConfigStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _ClashConfigState _self;
|
||||
final $Res Function(_ClashConfigState) _then;
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? overrideDns = null,Object? clashConfig = null,Object? routeMode = null,}) {
|
||||
return _then(_ClashConfigState(
|
||||
overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,clashConfig: null == clashConfig ? _self.clashConfig : clashConfig // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,routeMode: null == routeMode ? _self.routeMode : routeMode // ignore: cast_nullable_to_non_nullable
|
||||
as RouteMode,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of ClashConfigState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ClashConfigCopyWith<$Res> get clashConfig {
|
||||
|
||||
return $ClashConfigCopyWith<$Res>(_self.clashConfig, (value) {
|
||||
return _then(_self.copyWith(clashConfig: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$DashboardState {
|
||||
|
||||
@@ -8805,7 +9086,7 @@ $ProxiesDataCopyWith<$Res> get proxiesData {
|
||||
/// @nodoc
|
||||
mixin _$MakeRealProfileState {
|
||||
|
||||
String get profilesPath; int get profileId; Map<String, dynamic> get rawConfig; PatchClashConfig get realPatchConfig; bool get overrideDns; bool get appendSystemDns; List<Rule> get addedRules; String get defaultUA;
|
||||
String get profilesPath; int get profileId; Map<String, dynamic> get rawConfig; ClashConfig get realPatchConfig; bool get overrideDns; bool get appendSystemDns; List<Rule> get addedRules; String get defaultUA;
|
||||
/// Create a copy of MakeRealProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -8836,11 +9117,11 @@ abstract mixin class $MakeRealProfileStateCopyWith<$Res> {
|
||||
factory $MakeRealProfileStateCopyWith(MakeRealProfileState value, $Res Function(MakeRealProfileState) _then) = _$MakeRealProfileStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String profilesPath, int profileId, Map<String, dynamic> rawConfig, PatchClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA
|
||||
String profilesPath, int profileId, Map<String, dynamic> rawConfig, ClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA
|
||||
});
|
||||
|
||||
|
||||
$PatchClashConfigCopyWith<$Res> get realPatchConfig;
|
||||
$ClashConfigCopyWith<$Res> get realPatchConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -8859,7 +9140,7 @@ profilesPath: null == profilesPath ? _self.profilesPath : profilesPath // ignore
|
||||
as String,profileId: null == profileId ? _self.profileId : profileId // ignore: cast_nullable_to_non_nullable
|
||||
as int,rawConfig: null == rawConfig ? _self.rawConfig : rawConfig // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,realPatchConfig: null == realPatchConfig ? _self.realPatchConfig : realPatchConfig // ignore: cast_nullable_to_non_nullable
|
||||
as PatchClashConfig,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,appendSystemDns: null == appendSystemDns ? _self.appendSystemDns : appendSystemDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,addedRules: null == addedRules ? _self.addedRules : addedRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,defaultUA: null == defaultUA ? _self.defaultUA : defaultUA // ignore: cast_nullable_to_non_nullable
|
||||
@@ -8870,9 +9151,9 @@ as String,
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PatchClashConfigCopyWith<$Res> get realPatchConfig {
|
||||
$ClashConfigCopyWith<$Res> get realPatchConfig {
|
||||
|
||||
return $PatchClashConfigCopyWith<$Res>(_self.realPatchConfig, (value) {
|
||||
return $ClashConfigCopyWith<$Res>(_self.realPatchConfig, (value) {
|
||||
return _then(_self.copyWith(realPatchConfig: value));
|
||||
});
|
||||
}
|
||||
@@ -8957,7 +9238,7 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, PatchClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, ClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MakeRealProfileState() when $default != null:
|
||||
return $default(_that.profilesPath,_that.profileId,_that.rawConfig,_that.realPatchConfig,_that.overrideDns,_that.appendSystemDns,_that.addedRules,_that.defaultUA);case _:
|
||||
@@ -8978,7 +9259,7 @@ return $default(_that.profilesPath,_that.profileId,_that.rawConfig,_that.realPat
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, PatchClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, ClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MakeRealProfileState():
|
||||
return $default(_that.profilesPath,_that.profileId,_that.rawConfig,_that.realPatchConfig,_that.overrideDns,_that.appendSystemDns,_that.addedRules,_that.defaultUA);case _:
|
||||
@@ -8998,7 +9279,7 @@ return $default(_that.profilesPath,_that.profileId,_that.rawConfig,_that.realPat
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, PatchClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String profilesPath, int profileId, Map<String, dynamic> rawConfig, ClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MakeRealProfileState() when $default != null:
|
||||
return $default(_that.profilesPath,_that.profileId,_that.rawConfig,_that.realPatchConfig,_that.overrideDns,_that.appendSystemDns,_that.addedRules,_that.defaultUA);case _:
|
||||
@@ -9025,7 +9306,7 @@ class _MakeRealProfileState implements MakeRealProfileState {
|
||||
return EqualUnmodifiableMapView(_rawConfig);
|
||||
}
|
||||
|
||||
@override final PatchClashConfig realPatchConfig;
|
||||
@override final ClashConfig realPatchConfig;
|
||||
@override final bool overrideDns;
|
||||
@override final bool appendSystemDns;
|
||||
final List<Rule> _addedRules;
|
||||
@@ -9067,11 +9348,11 @@ abstract mixin class _$MakeRealProfileStateCopyWith<$Res> implements $MakeRealPr
|
||||
factory _$MakeRealProfileStateCopyWith(_MakeRealProfileState value, $Res Function(_MakeRealProfileState) _then) = __$MakeRealProfileStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String profilesPath, int profileId, Map<String, dynamic> rawConfig, PatchClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA
|
||||
String profilesPath, int profileId, Map<String, dynamic> rawConfig, ClashConfig realPatchConfig, bool overrideDns, bool appendSystemDns, List<Rule> addedRules, String defaultUA
|
||||
});
|
||||
|
||||
|
||||
@override $PatchClashConfigCopyWith<$Res> get realPatchConfig;
|
||||
@override $ClashConfigCopyWith<$Res> get realPatchConfig;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -9090,7 +9371,7 @@ profilesPath: null == profilesPath ? _self.profilesPath : profilesPath // ignore
|
||||
as String,profileId: null == profileId ? _self.profileId : profileId // ignore: cast_nullable_to_non_nullable
|
||||
as int,rawConfig: null == rawConfig ? _self._rawConfig : rawConfig // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,realPatchConfig: null == realPatchConfig ? _self.realPatchConfig : realPatchConfig // ignore: cast_nullable_to_non_nullable
|
||||
as PatchClashConfig,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,appendSystemDns: null == appendSystemDns ? _self.appendSystemDns : appendSystemDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,addedRules: null == addedRules ? _self._addedRules : addedRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,defaultUA: null == defaultUA ? _self.defaultUA : defaultUA // ignore: cast_nullable_to_non_nullable
|
||||
@@ -9102,9 +9383,9 @@ as String,
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PatchClashConfigCopyWith<$Res> get realPatchConfig {
|
||||
$ClashConfigCopyWith<$Res> get realPatchConfig {
|
||||
|
||||
return $PatchClashConfigCopyWith<$Res>(_self.realPatchConfig, (value) {
|
||||
return $ClashConfigCopyWith<$Res>(_self.realPatchConfig, (value) {
|
||||
return _then(_self.copyWith(realPatchConfig: value));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -271,6 +271,15 @@ abstract class ProxyState with _$ProxyState {
|
||||
}) = _ProxyState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class ClashConfigState with _$ClashConfigState {
|
||||
const factory ClashConfigState({
|
||||
required bool overrideDns,
|
||||
required ClashConfig clashConfig,
|
||||
required RouteMode routeMode,
|
||||
}) = _ClashConfigState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class DashboardState with _$DashboardState {
|
||||
const factory DashboardState({
|
||||
@@ -334,7 +343,7 @@ abstract class MakeRealProfileState with _$MakeRealProfileState {
|
||||
required String profilesPath,
|
||||
required int profileId,
|
||||
required Map<String, dynamic> rawConfig,
|
||||
required PatchClashConfig realPatchConfig,
|
||||
required ClashConfig realPatchConfig,
|
||||
required bool overrideDns,
|
||||
required bool appendSystemDns,
|
||||
required List<Rule> addedRules,
|
||||
|
||||
@@ -20,7 +20,7 @@ typedef TextEditingValueChangeBuilder = Widget Function(TextEditingValue value);
|
||||
|
||||
class EditorPage extends ConsumerStatefulWidget {
|
||||
final String title;
|
||||
final String? content;
|
||||
final String content;
|
||||
final List<Language> languages;
|
||||
final bool supportRemoteDownload;
|
||||
final bool titleEditable;
|
||||
@@ -60,7 +60,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
super.initState();
|
||||
readOnly = widget.onSave == null;
|
||||
_toolbarController = ContextMenuControllerImpl(readOnly);
|
||||
_focusNode = FocusNode();
|
||||
_focusNode = FocusNode(canRequestFocus: !readOnly);
|
||||
_controller = CodeLineEditingController.fromText(widget.content);
|
||||
_findController = CodeFindController(_controller);
|
||||
_titleController = TextEditingController(text: widget.title);
|
||||
@@ -91,18 +91,6 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final content = widget.content;
|
||||
if (content != null && oldWidget.content != content) {
|
||||
_controller.text = content;
|
||||
_controller.clearHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_toolbarController.hide(context);
|
||||
@@ -192,7 +180,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
enabled: widget.titleEditable,
|
||||
controller: _titleController,
|
||||
decoration: InputDecoration(
|
||||
border: NoInputBorder(),
|
||||
border: _NoInputBorder(),
|
||||
counter: SizedBox(),
|
||||
hintText: appLocalizations.unnamed,
|
||||
),
|
||||
@@ -268,78 +256,58 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
),
|
||||
]),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
CodeEditor(
|
||||
readOnly: readOnly,
|
||||
autofocus: false,
|
||||
showCursorWhenReadOnly: false,
|
||||
findController: _findController,
|
||||
findBuilder: (context, controller, readOnly) => FindPanel(
|
||||
controller: controller,
|
||||
readOnly: readOnly,
|
||||
isMobileView: isMobileView,
|
||||
),
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
autocompleteSymbols: true,
|
||||
focusNode: _focusNode,
|
||||
scrollbarBuilder: (context, child, details) {
|
||||
return CommonScrollBar(
|
||||
controller: details.controller,
|
||||
child: child,
|
||||
body: CodeEditor(
|
||||
readOnly: readOnly,
|
||||
autofocus: false,
|
||||
findController: _findController,
|
||||
findBuilder: (context, controller, readOnly) => FindPanel(
|
||||
controller: controller,
|
||||
readOnly: readOnly,
|
||||
isMobileView: isMobileView,
|
||||
),
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
autocompleteSymbols: true,
|
||||
focusNode: _focusNode,
|
||||
scrollbarBuilder: (context, child, details) {
|
||||
return CommonScrollBar(
|
||||
controller: details.controller,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
toolbarController: _toolbarController,
|
||||
indicatorBuilder:
|
||||
(context, editingController, chunkController, notifier) {
|
||||
return Row(
|
||||
children: [
|
||||
DefaultCodeLineNumber(
|
||||
controller: editingController,
|
||||
notifier: notifier,
|
||||
),
|
||||
DefaultCodeChunkIndicator(
|
||||
width: 20,
|
||||
controller: chunkController,
|
||||
notifier: notifier,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
toolbarController: _toolbarController,
|
||||
indicatorBuilder:
|
||||
(context, editingController, chunkController, notifier) {
|
||||
return Row(
|
||||
children: [
|
||||
DefaultCodeLineNumber(
|
||||
controller: editingController,
|
||||
notifier: notifier,
|
||||
),
|
||||
DefaultCodeChunkIndicator(
|
||||
width: 20,
|
||||
controller: chunkController,
|
||||
notifier: notifier,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
shortcutsActivatorsBuilder:
|
||||
DefaultCodeShortcutsActivatorsBuilder(),
|
||||
controller: _controller,
|
||||
style: CodeEditorStyle(
|
||||
fontSize: context.textTheme.bodyLarge?.fontSize?.ap,
|
||||
fontFamily: FontFamily.jetBrainsMono.value,
|
||||
codeTheme: CodeHighlightTheme(
|
||||
languages: {
|
||||
if (widget.languages.contains(Language.yaml))
|
||||
'yaml': CodeHighlightThemeMode(mode: langYaml),
|
||||
if (widget.languages.contains(Language.javaScript))
|
||||
'javascript': CodeHighlightThemeMode(
|
||||
mode: langJavascript,
|
||||
),
|
||||
if (widget.languages.contains(Language.json))
|
||||
'json': CodeHighlightThemeMode(mode: langJson),
|
||||
},
|
||||
theme: atomOneLightTheme,
|
||||
),
|
||||
),
|
||||
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
|
||||
controller: _controller,
|
||||
style: CodeEditorStyle(
|
||||
fontSize: context.textTheme.bodyLarge?.fontSize?.ap,
|
||||
fontFamily: FontFamily.jetBrainsMono.value,
|
||||
codeTheme: CodeHighlightTheme(
|
||||
languages: {
|
||||
if (widget.languages.contains(Language.yaml))
|
||||
'yaml': CodeHighlightThemeMode(mode: langYaml),
|
||||
if (widget.languages.contains(Language.javaScript))
|
||||
'javascript': CodeHighlightThemeMode(mode: langJavascript),
|
||||
if (widget.languages.contains(Language.json))
|
||||
'json': CodeHighlightThemeMode(mode: langJson),
|
||||
},
|
||||
theme: atomOneLightTheme,
|
||||
),
|
||||
FadeBox(
|
||||
child: widget.content == null
|
||||
? Container(
|
||||
color: context.colorScheme.surface,
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox.square(
|
||||
dimension: 200,
|
||||
child: CommonCircleLoading(),
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -440,7 +408,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [bar, SizedBox(height: 12), _buildFindInput(context, value)],
|
||||
children: [bar, SizedBox(height: 4), _buildFindInput(context, value)],
|
||||
);
|
||||
}
|
||||
return bar;
|
||||
@@ -639,6 +607,55 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
|
||||
}
|
||||
}
|
||||
|
||||
class _NoInputBorder extends InputBorder {
|
||||
const _NoInputBorder() : super(borderSide: BorderSide.none);
|
||||
|
||||
@override
|
||||
_NoInputBorder copyWith({BorderSide? borderSide}) => const _NoInputBorder();
|
||||
|
||||
@override
|
||||
bool get isOutline => false;
|
||||
|
||||
@override
|
||||
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
|
||||
|
||||
@override
|
||||
_NoInputBorder scale(double t) => const _NoInputBorder();
|
||||
|
||||
@override
|
||||
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
|
||||
return Path()..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
|
||||
return Path()..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(
|
||||
Canvas canvas,
|
||||
Rect rect,
|
||||
Paint paint, {
|
||||
TextDirection? textDirection,
|
||||
}) {
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(
|
||||
Canvas canvas,
|
||||
Rect rect, {
|
||||
double? gapStart,
|
||||
double gapExtent = 0.0,
|
||||
double gapPercentage = 0.0,
|
||||
TextDirection? textDirection,
|
||||
}) {}
|
||||
}
|
||||
|
||||
class _ImportOptionsDialog extends StatefulWidget {
|
||||
const _ImportOptionsDialog();
|
||||
|
||||
|
||||
@@ -159,6 +159,14 @@ class Init extends _$Init with AutoDisposeNotifierMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class CoreInit extends _$CoreInit with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
bool build() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class CurrentPageLabel extends _$CurrentPageLabel
|
||||
with AutoDisposeNotifierMixin {
|
||||
@@ -295,7 +303,7 @@ class Loading extends _$Loading with AutoDisposeNotifierMixin {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Items extends _$Items with AutoDisposeNotifierMixin {
|
||||
class SelectedItems extends _$SelectedItems with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
Set<dynamic> build(String key) {
|
||||
return {};
|
||||
@@ -303,7 +311,7 @@ class Items extends _$Items with AutoDisposeNotifierMixin {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Item extends _$Item with AutoDisposeNotifierMixin {
|
||||
class SelectedItem extends _$SelectedItem with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
dynamic build(String key) {
|
||||
return null;
|
||||
|
||||
@@ -88,12 +88,12 @@ class ProxiesStyleSetting extends _$ProxiesStyleSetting
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(name: 'patchClashConfigProvider')
|
||||
class _PatchClashConfig extends _$PatchClashConfig
|
||||
@riverpod
|
||||
class PatchClashConfig extends _$PatchClashConfig
|
||||
with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
PatchClashConfig build() {
|
||||
return PatchClashConfig();
|
||||
ClashConfig build() {
|
||||
return ClashConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/database/database.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -15,25 +12,10 @@ Stream<List<Profile>> profilesStream(Ref ref) {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Stream<List<Rule>> addedRulesStream(Ref ref, int profileId) {
|
||||
Stream<List<Rule>> addedRuleStream(Ref ref, int profileId) {
|
||||
return database.rulesDao.allAddedRules(profileId).watch();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<Rule>> addedRules(Ref ref, int profileId) {
|
||||
return database.rulesDao.allAddedRules(profileId).get();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Stream<int> customRulesCount(Ref ref, int profileId) {
|
||||
return database.rulesDao.profileCustomRulesCount(profileId).watchSingle();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Stream<int> proxyGroupsCount(Ref ref, int profileId) {
|
||||
return database.proxyGroupsDao.count(profileId).watchSingle();
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class Profiles extends _$Profiles {
|
||||
@override
|
||||
@@ -161,16 +143,8 @@ class GlobalRules extends _$GlobalRules with AsyncNotifierMixin {
|
||||
}
|
||||
|
||||
void put(Rule rule) {
|
||||
final Rule newRule;
|
||||
if (rule.order?.isNotEmpty != true) {
|
||||
newRule = rule.copyWith(
|
||||
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
|
||||
);
|
||||
} else {
|
||||
newRule = rule;
|
||||
}
|
||||
value = value.copyAndPut(newRule);
|
||||
database.rulesDao.putGlobalRule(newRule);
|
||||
value = value.copyAndPut(rule);
|
||||
database.rulesDao.putGlobalRule(rule);
|
||||
}
|
||||
|
||||
void order(int oldIndex, int newIndex) {
|
||||
@@ -184,7 +158,7 @@ class GlobalRules extends _$GlobalRules with AsyncNotifierMixin {
|
||||
value = nextItems;
|
||||
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
|
||||
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
|
||||
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
|
||||
final newOrder = indexing.generateKeyBetween(nextOrder, preOrder)!;
|
||||
database.rulesDao.orderGlobalRule(ruleId: item.id, order: newOrder);
|
||||
}
|
||||
}
|
||||
@@ -208,16 +182,8 @@ class ProfileAddedRules extends _$ProfileAddedRules with AsyncNotifierMixin {
|
||||
}
|
||||
|
||||
void put(Rule rule) {
|
||||
final Rule newRule;
|
||||
if (rule.order?.isNotEmpty != true) {
|
||||
newRule = rule.copyWith(
|
||||
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
|
||||
);
|
||||
} else {
|
||||
newRule = rule;
|
||||
}
|
||||
value = value.copyAndPut(newRule);
|
||||
database.rulesDao.putProfileAddedRule(profileId, newRule);
|
||||
value = value.copyAndPut(rule);
|
||||
database.rulesDao.putProfileAddedRule(profileId, rule);
|
||||
}
|
||||
|
||||
void delAll(Iterable<int> ruleIds) {
|
||||
@@ -236,7 +202,7 @@ class ProfileAddedRules extends _$ProfileAddedRules with AsyncNotifierMixin {
|
||||
value = nextItems;
|
||||
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
|
||||
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
|
||||
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
|
||||
final newOrder = indexing.generateKeyBetween(nextOrder, preOrder)!;
|
||||
database.rulesDao.orderProfileAddedRule(
|
||||
profileId,
|
||||
ruleId: item.id,
|
||||
@@ -245,104 +211,6 @@ class ProfileAddedRules extends _$ProfileAddedRules with AsyncNotifierMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ProfileCustomRules extends _$ProfileCustomRules with AsyncNotifierMixin {
|
||||
@override
|
||||
Stream<List<Rule>> build(int profileId) {
|
||||
return database.rulesDao.allProfileCustomRules(profileId).watch();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Rule> get value => state.value ?? [];
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(
|
||||
AsyncValue<List<Rule>> previous,
|
||||
AsyncValue<List<Rule>> next,
|
||||
) {
|
||||
return !ruleListEquality.equals(previous.value, next.value);
|
||||
}
|
||||
|
||||
void put(Rule rule) {
|
||||
final Rule newRule;
|
||||
if (rule.order?.isNotEmpty != true) {
|
||||
newRule = rule.copyWith(
|
||||
order: indexing.generateKeyBetween(null, value.firstOrNull?.order),
|
||||
);
|
||||
} else {
|
||||
newRule = rule;
|
||||
}
|
||||
value = value.copyAndPut(newRule);
|
||||
database.rulesDao.putProfileCustomRule(profileId, newRule);
|
||||
}
|
||||
|
||||
void delAll(Iterable<int> ruleIds) {
|
||||
value = List<Rule>.from(value.where((item) => !ruleIds.contains(item.id)));
|
||||
database.rulesDao.delRules(ruleIds);
|
||||
}
|
||||
|
||||
void order(int oldIndex, int newIndex) {
|
||||
int insertIndex = newIndex;
|
||||
if (oldIndex < newIndex) {
|
||||
insertIndex -= 1;
|
||||
}
|
||||
final nextItems = List<Rule>.from(value);
|
||||
final item = nextItems.removeAt(oldIndex);
|
||||
nextItems.insert(insertIndex, item);
|
||||
value = nextItems;
|
||||
final preOrder = nextItems.safeGet(insertIndex - 1)?.order;
|
||||
final nextOrder = nextItems.safeGet(insertIndex + 1)?.order;
|
||||
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
|
||||
database.rulesDao.orderProfileCustomRule(
|
||||
profileId,
|
||||
ruleId: item.id,
|
||||
order: newOrder,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ProxyGroups extends _$ProxyGroups with AsyncNotifierMixin {
|
||||
@override
|
||||
Stream<List<ProxyGroup>> build(int profileId) {
|
||||
return database.proxyGroupsDao.all(profileId).watch();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(
|
||||
AsyncValue<List<ProxyGroup>> previous,
|
||||
AsyncValue<List<ProxyGroup>> next,
|
||||
) {
|
||||
return !proxyGroupsEquality.equals(previous.value, next.value);
|
||||
}
|
||||
|
||||
void del(String name) {
|
||||
database.proxyGroups.remove(
|
||||
(t) => t.profileId.equals(profileId) & t.name.equals(name),
|
||||
);
|
||||
List<ProxyGroup> newList = List.from(value);
|
||||
newList = newList.where((item) => item.name != name).toList();
|
||||
value = newList;
|
||||
}
|
||||
|
||||
void order(int oldIndex, int newIndex) {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final nextItems = List<ProxyGroup>.from(value);
|
||||
final item = nextItems.removeAt(oldIndex);
|
||||
nextItems.insert(newIndex, item);
|
||||
value = nextItems;
|
||||
final preOrder = nextItems.safeGet(newIndex - 1)?.order;
|
||||
final nextOrder = nextItems.safeGet(newIndex + 1)?.order;
|
||||
final newOrder = indexing.generateKeyBetween(preOrder, nextOrder)!;
|
||||
database.proxyGroupsDao.order(profileId, proxyGroup: item, order: newOrder);
|
||||
}
|
||||
|
||||
@override
|
||||
List<ProxyGroup> get value => state.value ?? [];
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ProfileDisabledRuleIds extends _$ProfileDisabledRuleIds
|
||||
with AsyncNotifierMixin {
|
||||
@@ -357,14 +225,6 @@ class ProfileDisabledRuleIds extends _$ProfileDisabledRuleIds
|
||||
.watch();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(
|
||||
AsyncValue<List<int>> previous,
|
||||
AsyncValue<List<int>> next,
|
||||
) {
|
||||
return !intListEquality.equals(previous.value, next.value);
|
||||
}
|
||||
|
||||
void _put(int ruleId) {
|
||||
var newList = List<int>.from(value);
|
||||
final index = newList.indexWhere((item) => item == ruleId);
|
||||
|
||||
@@ -857,6 +857,58 @@ abstract class _$Init extends $Notifier<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(CoreInit)
|
||||
const coreInitProvider = CoreInitProvider._();
|
||||
|
||||
final class CoreInitProvider extends $NotifierProvider<CoreInit, bool> {
|
||||
const CoreInitProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'coreInitProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$coreInitHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
CoreInit create() => CoreInit();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$coreInitHash() => r'db77b825e2b198311ff7753a078ce060aaef9c53';
|
||||
|
||||
abstract class _$CoreInit extends $Notifier<bool> {
|
||||
bool build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(CurrentPageLabel)
|
||||
const currentPageLabelProvider = CurrentPageLabelProvider._();
|
||||
|
||||
@@ -1511,34 +1563,35 @@ abstract class _$Loading extends $Notifier<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(Items)
|
||||
const itemsProvider = ItemsFamily._();
|
||||
@ProviderFor(SelectedItems)
|
||||
const selectedItemsProvider = SelectedItemsFamily._();
|
||||
|
||||
final class ItemsProvider extends $NotifierProvider<Items, Set<dynamic>> {
|
||||
const ItemsProvider._({
|
||||
required ItemsFamily super.from,
|
||||
final class SelectedItemsProvider
|
||||
extends $NotifierProvider<SelectedItems, Set<dynamic>> {
|
||||
const SelectedItemsProvider._({
|
||||
required SelectedItemsFamily super.from,
|
||||
required String super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'itemsProvider',
|
||||
name: r'selectedItemsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$itemsHash();
|
||||
String debugGetCreateSourceHash() => _$selectedItemsHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'itemsProvider'
|
||||
return r'selectedItemsProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
Items create() => Items();
|
||||
SelectedItems create() => SelectedItems();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Set<dynamic> value) {
|
||||
@@ -1550,7 +1603,7 @@ final class ItemsProvider extends $NotifierProvider<Items, Set<dynamic>> {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ItemsProvider && other.argument == argument;
|
||||
return other is SelectedItemsProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1559,33 +1612,34 @@ final class ItemsProvider extends $NotifierProvider<Items, Set<dynamic>> {
|
||||
}
|
||||
}
|
||||
|
||||
String _$itemsHash() => r'e4d68c86d62dfa3ba7153954208891e4df4c4355';
|
||||
String _$selectedItemsHash() => r'05ef5c5584cbac90d416e5c4fe53ec9e29604020';
|
||||
|
||||
final class ItemsFamily extends $Family
|
||||
final class SelectedItemsFamily extends $Family
|
||||
with
|
||||
$ClassFamilyOverride<
|
||||
Items,
|
||||
SelectedItems,
|
||||
Set<dynamic>,
|
||||
Set<dynamic>,
|
||||
Set<dynamic>,
|
||||
String
|
||||
> {
|
||||
const ItemsFamily._()
|
||||
const SelectedItemsFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'itemsProvider',
|
||||
name: r'selectedItemsProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ItemsProvider call(String key) => ItemsProvider._(argument: key, from: this);
|
||||
SelectedItemsProvider call(String key) =>
|
||||
SelectedItemsProvider._(argument: key, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'itemsProvider';
|
||||
String toString() => r'selectedItemsProvider';
|
||||
}
|
||||
|
||||
abstract class _$Items extends $Notifier<Set<dynamic>> {
|
||||
abstract class _$SelectedItems extends $Notifier<Set<dynamic>> {
|
||||
late final _$args = ref.$arg as String;
|
||||
String get key => _$args;
|
||||
|
||||
@@ -1607,34 +1661,35 @@ abstract class _$Items extends $Notifier<Set<dynamic>> {
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(Item)
|
||||
const itemProvider = ItemFamily._();
|
||||
@ProviderFor(SelectedItem)
|
||||
const selectedItemProvider = SelectedItemFamily._();
|
||||
|
||||
final class ItemProvider extends $NotifierProvider<Item, dynamic> {
|
||||
const ItemProvider._({
|
||||
required ItemFamily super.from,
|
||||
final class SelectedItemProvider
|
||||
extends $NotifierProvider<SelectedItem, dynamic> {
|
||||
const SelectedItemProvider._({
|
||||
required SelectedItemFamily super.from,
|
||||
required String super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'itemProvider',
|
||||
name: r'selectedItemProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$itemHash();
|
||||
String debugGetCreateSourceHash() => _$selectedItemHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'itemProvider'
|
||||
return r'selectedItemProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
Item create() => Item();
|
||||
SelectedItem create() => SelectedItem();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(dynamic value) {
|
||||
@@ -1646,7 +1701,7 @@ final class ItemProvider extends $NotifierProvider<Item, dynamic> {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ItemProvider && other.argument == argument;
|
||||
return other is SelectedItemProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1655,26 +1710,27 @@ final class ItemProvider extends $NotifierProvider<Item, dynamic> {
|
||||
}
|
||||
}
|
||||
|
||||
String _$itemHash() => r'bd46bf2e285d7171173ed7c46455ff4c39e80a46';
|
||||
String _$selectedItemHash() => r'b50be0386d53ee8441c37d1a2a4c25640ce10766';
|
||||
|
||||
final class ItemFamily extends $Family
|
||||
with $ClassFamilyOverride<Item, dynamic, dynamic, dynamic, String> {
|
||||
const ItemFamily._()
|
||||
final class SelectedItemFamily extends $Family
|
||||
with $ClassFamilyOverride<SelectedItem, dynamic, dynamic, dynamic, String> {
|
||||
const SelectedItemFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'itemProvider',
|
||||
name: r'selectedItemProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ItemProvider call(String key) => ItemProvider._(argument: key, from: this);
|
||||
SelectedItemProvider call(String key) =>
|
||||
SelectedItemProvider._(argument: key, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'itemProvider';
|
||||
String toString() => r'selectedItemProvider';
|
||||
}
|
||||
|
||||
abstract class _$Item extends $Notifier<dynamic> {
|
||||
abstract class _$SelectedItem extends $Notifier<dynamic> {
|
||||
late final _$args = ref.$arg as String;
|
||||
String get key => _$args;
|
||||
|
||||
|
||||
@@ -538,12 +538,12 @@ abstract class _$ProxiesStyleSetting extends $Notifier<ProxiesStyleProps> {
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(_PatchClashConfig)
|
||||
const patchClashConfigProvider = _PatchClashConfigProvider._();
|
||||
@ProviderFor(PatchClashConfig)
|
||||
const patchClashConfigProvider = PatchClashConfigProvider._();
|
||||
|
||||
final class _PatchClashConfigProvider
|
||||
extends $NotifierProvider<_PatchClashConfig, PatchClashConfig> {
|
||||
const _PatchClashConfigProvider._()
|
||||
final class PatchClashConfigProvider
|
||||
extends $NotifierProvider<PatchClashConfig, ClashConfig> {
|
||||
const PatchClashConfigProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
@@ -555,35 +555,35 @@ final class _PatchClashConfigProvider
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$_patchClashConfigHash();
|
||||
String debugGetCreateSourceHash() => _$patchClashConfigHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
_PatchClashConfig create() => _PatchClashConfig();
|
||||
PatchClashConfig create() => PatchClashConfig();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(PatchClashConfig value) {
|
||||
Override overrideWithValue(ClashConfig value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<PatchClashConfig>(value),
|
||||
providerOverride: $SyncValueProvider<ClashConfig>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$_patchClashConfigHash() => r'855a2162368773b90de48fd0139018ce73d209cc';
|
||||
String _$patchClashConfigHash() => r'ff92f991ccb3a3d13a938affc006d7e2cb85fecd';
|
||||
|
||||
abstract class _$PatchClashConfig extends $Notifier<PatchClashConfig> {
|
||||
PatchClashConfig build();
|
||||
abstract class _$PatchClashConfig extends $Notifier<ClashConfig> {
|
||||
ClashConfig build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<PatchClashConfig, PatchClashConfig>;
|
||||
final ref = this.ref as $Ref<ClashConfig, ClashConfig>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<PatchClashConfig, PatchClashConfig>,
|
||||
PatchClashConfig,
|
||||
AnyNotifier<ClashConfig, ClashConfig>,
|
||||
ClashConfig,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
|
||||
@@ -48,10 +48,10 @@ final class ProfilesStreamProvider
|
||||
|
||||
String _$profilesStreamHash() => r'483907aa6c324209b5202369300a4a53230f83db';
|
||||
|
||||
@ProviderFor(addedRulesStream)
|
||||
const addedRulesStreamProvider = AddedRulesStreamFamily._();
|
||||
@ProviderFor(addedRuleStream)
|
||||
const addedRuleStreamProvider = AddedRuleStreamFamily._();
|
||||
|
||||
final class AddedRulesStreamProvider
|
||||
final class AddedRuleStreamProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<Rule>>,
|
||||
@@ -59,23 +59,23 @@ final class AddedRulesStreamProvider
|
||||
Stream<List<Rule>>
|
||||
>
|
||||
with $FutureModifier<List<Rule>>, $StreamProvider<List<Rule>> {
|
||||
const AddedRulesStreamProvider._({
|
||||
required AddedRulesStreamFamily super.from,
|
||||
const AddedRuleStreamProvider._({
|
||||
required AddedRuleStreamFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'addedRulesStreamProvider',
|
||||
name: r'addedRuleStreamProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$addedRulesStreamHash();
|
||||
String debugGetCreateSourceHash() => _$addedRuleStreamHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'addedRulesStreamProvider'
|
||||
return r'addedRuleStreamProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
@@ -88,12 +88,12 @@ final class AddedRulesStreamProvider
|
||||
@override
|
||||
Stream<List<Rule>> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return addedRulesStream(ref, argument);
|
||||
return addedRuleStream(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is AddedRulesStreamProvider && other.argument == argument;
|
||||
return other is AddedRuleStreamProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -102,236 +102,24 @@ final class AddedRulesStreamProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$addedRulesStreamHash() => r'3147271d6149b9c3861e99671fe7ac1f8a8fa23b';
|
||||
String _$addedRuleStreamHash() => r'491968ce795e56d4516a95676fcf46d575b3495f';
|
||||
|
||||
final class AddedRulesStreamFamily extends $Family
|
||||
final class AddedRuleStreamFamily extends $Family
|
||||
with $FunctionalFamilyOverride<Stream<List<Rule>>, int> {
|
||||
const AddedRulesStreamFamily._()
|
||||
const AddedRuleStreamFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'addedRulesStreamProvider',
|
||||
name: r'addedRuleStreamProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
AddedRulesStreamProvider call(int profileId) =>
|
||||
AddedRulesStreamProvider._(argument: profileId, from: this);
|
||||
AddedRuleStreamProvider call(int profileId) =>
|
||||
AddedRuleStreamProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'addedRulesStreamProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(addedRules)
|
||||
const addedRulesProvider = AddedRulesFamily._();
|
||||
|
||||
final class AddedRulesProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<Rule>>,
|
||||
List<Rule>,
|
||||
FutureOr<List<Rule>>
|
||||
>
|
||||
with $FutureModifier<List<Rule>>, $FutureProvider<List<Rule>> {
|
||||
const AddedRulesProvider._({
|
||||
required AddedRulesFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'addedRulesProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$addedRulesHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'addedRulesProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<Rule>> $createElement($ProviderPointer pointer) =>
|
||||
$FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<Rule>> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return addedRules(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is AddedRulesProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$addedRulesHash() => r'fa2569f7781c93e00bd2017c956ff377e436667a';
|
||||
|
||||
final class AddedRulesFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<List<Rule>>, int> {
|
||||
const AddedRulesFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'addedRulesProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
AddedRulesProvider call(int profileId) =>
|
||||
AddedRulesProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'addedRulesProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(customRulesCount)
|
||||
const customRulesCountProvider = CustomRulesCountFamily._();
|
||||
|
||||
final class CustomRulesCountProvider
|
||||
extends $FunctionalProvider<AsyncValue<int>, int, Stream<int>>
|
||||
with $FutureModifier<int>, $StreamProvider<int> {
|
||||
const CustomRulesCountProvider._({
|
||||
required CustomRulesCountFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'customRulesCountProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$customRulesCountHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'customRulesCountProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<int> $createElement($ProviderPointer pointer) =>
|
||||
$StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<int> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return customRulesCount(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is CustomRulesCountProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$customRulesCountHash() => r'a3ff7941bcbb2696ba48c82b9310d81d7238536f';
|
||||
|
||||
final class CustomRulesCountFamily extends $Family
|
||||
with $FunctionalFamilyOverride<Stream<int>, int> {
|
||||
const CustomRulesCountFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'customRulesCountProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
CustomRulesCountProvider call(int profileId) =>
|
||||
CustomRulesCountProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'customRulesCountProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(proxyGroupsCount)
|
||||
const proxyGroupsCountProvider = ProxyGroupsCountFamily._();
|
||||
|
||||
final class ProxyGroupsCountProvider
|
||||
extends $FunctionalProvider<AsyncValue<int>, int, Stream<int>>
|
||||
with $FutureModifier<int>, $StreamProvider<int> {
|
||||
const ProxyGroupsCountProvider._({
|
||||
required ProxyGroupsCountFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'proxyGroupsCountProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$proxyGroupsCountHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'proxyGroupsCountProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<int> $createElement($ProviderPointer pointer) =>
|
||||
$StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<int> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return proxyGroupsCount(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ProxyGroupsCountProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxyGroupsCountHash() => r'9bf90fc25a9ae3b9ab7aa0784d4e47786f4c4d52';
|
||||
|
||||
final class ProxyGroupsCountFamily extends $Family
|
||||
with $FunctionalFamilyOverride<Stream<int>, int> {
|
||||
const ProxyGroupsCountFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'proxyGroupsCountProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ProxyGroupsCountProvider call(int profileId) =>
|
||||
ProxyGroupsCountProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'proxyGroupsCountProvider';
|
||||
String toString() => r'addedRuleStreamProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(Profiles)
|
||||
@@ -456,7 +244,7 @@ final class GlobalRulesProvider
|
||||
GlobalRules create() => GlobalRules();
|
||||
}
|
||||
|
||||
String _$globalRulesHash() => r'39d27f04f14d4498dc9dd89cea8e9cc2cc9da548';
|
||||
String _$globalRulesHash() => r'3ed947f389649a86d5c6d78d8c02ba5b8d0f7119';
|
||||
|
||||
abstract class _$GlobalRules extends $StreamNotifier<List<Rule>> {
|
||||
Stream<List<Rule>> build();
|
||||
@@ -518,7 +306,7 @@ final class ProfileAddedRulesProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$profileAddedRulesHash() => r'6909191ccf493d8b9dd657265f3da1ae27485d73';
|
||||
String _$profileAddedRulesHash() => r'4155448335cf14a8928db6adf68e59572aa4ce47';
|
||||
|
||||
final class ProfileAddedRulesFamily extends $Family
|
||||
with
|
||||
@@ -567,188 +355,6 @@ abstract class _$ProfileAddedRules extends $StreamNotifier<List<Rule>> {
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(ProfileCustomRules)
|
||||
const profileCustomRulesProvider = ProfileCustomRulesFamily._();
|
||||
|
||||
final class ProfileCustomRulesProvider
|
||||
extends $StreamNotifierProvider<ProfileCustomRules, List<Rule>> {
|
||||
const ProfileCustomRulesProvider._({
|
||||
required ProfileCustomRulesFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'profileCustomRulesProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$profileCustomRulesHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'profileCustomRulesProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ProfileCustomRules create() => ProfileCustomRules();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ProfileCustomRulesProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$profileCustomRulesHash() =>
|
||||
r'b267939b552c7967a85caff5a249c0534686753b';
|
||||
|
||||
final class ProfileCustomRulesFamily extends $Family
|
||||
with
|
||||
$ClassFamilyOverride<
|
||||
ProfileCustomRules,
|
||||
AsyncValue<List<Rule>>,
|
||||
List<Rule>,
|
||||
Stream<List<Rule>>,
|
||||
int
|
||||
> {
|
||||
const ProfileCustomRulesFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'profileCustomRulesProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ProfileCustomRulesProvider call(int profileId) =>
|
||||
ProfileCustomRulesProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'profileCustomRulesProvider';
|
||||
}
|
||||
|
||||
abstract class _$ProfileCustomRules extends $StreamNotifier<List<Rule>> {
|
||||
late final _$args = ref.$arg as int;
|
||||
int get profileId => _$args;
|
||||
|
||||
Stream<List<Rule>> build(int profileId);
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build(_$args);
|
||||
final ref = this.ref as $Ref<AsyncValue<List<Rule>>, List<Rule>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<List<Rule>>, List<Rule>>,
|
||||
AsyncValue<List<Rule>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(ProxyGroups)
|
||||
const proxyGroupsProvider = ProxyGroupsFamily._();
|
||||
|
||||
final class ProxyGroupsProvider
|
||||
extends $StreamNotifierProvider<ProxyGroups, List<ProxyGroup>> {
|
||||
const ProxyGroupsProvider._({
|
||||
required ProxyGroupsFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'proxyGroupsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$proxyGroupsHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'proxyGroupsProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ProxyGroups create() => ProxyGroups();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ProxyGroupsProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxyGroupsHash() => r'b747de5d114e8e6d764befca26e9a8dc81d9d127';
|
||||
|
||||
final class ProxyGroupsFamily extends $Family
|
||||
with
|
||||
$ClassFamilyOverride<
|
||||
ProxyGroups,
|
||||
AsyncValue<List<ProxyGroup>>,
|
||||
List<ProxyGroup>,
|
||||
Stream<List<ProxyGroup>>,
|
||||
int
|
||||
> {
|
||||
const ProxyGroupsFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'proxyGroupsProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ProxyGroupsProvider call(int profileId) =>
|
||||
ProxyGroupsProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'proxyGroupsProvider';
|
||||
}
|
||||
|
||||
abstract class _$ProxyGroups extends $StreamNotifier<List<ProxyGroup>> {
|
||||
late final _$args = ref.$arg as int;
|
||||
int get profileId => _$args;
|
||||
|
||||
Stream<List<ProxyGroup>> build(int profileId);
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build(_$args);
|
||||
final ref =
|
||||
this.ref as $Ref<AsyncValue<List<ProxyGroup>>, List<ProxyGroup>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<List<ProxyGroup>>, List<ProxyGroup>>,
|
||||
AsyncValue<List<ProxyGroup>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(ProfileDisabledRuleIds)
|
||||
const profileDisabledRuleIdsProvider = ProfileDisabledRuleIdsFamily._();
|
||||
|
||||
@@ -792,7 +398,7 @@ final class ProfileDisabledRuleIdsProvider
|
||||
}
|
||||
|
||||
String _$profileDisabledRuleIdsHash() =>
|
||||
r'5093cc1d77ec69a2c1db6efa86a3f5916475d4f0';
|
||||
r'22d6e68bcee55b42fbb909e7f66e5c7095935224';
|
||||
|
||||
final class ProfileDisabledRuleIdsFamily extends $Family
|
||||
with
|
||||
|
||||
@@ -2443,81 +2443,6 @@ final class ScriptFamily extends $Family
|
||||
String toString() => r'scriptProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(clashConfig)
|
||||
const clashConfigProvider = ClashConfigFamily._();
|
||||
|
||||
final class ClashConfigProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<ClashConfig>,
|
||||
ClashConfig,
|
||||
FutureOr<ClashConfig>
|
||||
>
|
||||
with $FutureModifier<ClashConfig>, $FutureProvider<ClashConfig> {
|
||||
const ClashConfigProvider._({
|
||||
required ClashConfigFamily super.from,
|
||||
required int super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'clashConfigProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$clashConfigHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'clashConfigProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<ClashConfig> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<ClashConfig> create(Ref ref) {
|
||||
final argument = this.argument as int;
|
||||
return clashConfig(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ClashConfigProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$clashConfigHash() => r'f7da0ec3a29379f6192c1206d2cd7535b45fab5e';
|
||||
|
||||
final class ClashConfigFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<ClashConfig>, int> {
|
||||
const ClashConfigFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'clashConfigProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
ClashConfigProvider call(int profileId) =>
|
||||
ClashConfigProvider._(argument: profileId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'clashConfigProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(setupState)
|
||||
const setupStateProvider = SetupStateFamily._();
|
||||
|
||||
@@ -2572,7 +2497,7 @@ final class SetupStateProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$setupStateHash() => r'3ded29d70c607f869b625dc808936887cbd6ecc1';
|
||||
String _$setupStateHash() => r'8e0c849fa1a51ee15f8b40be94e3094182325b58';
|
||||
|
||||
final class SetupStateFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<SetupState>, int?> {
|
||||
@@ -2645,57 +2570,3 @@ abstract class _$AccessControlState extends $Notifier<AccessControlProps> {
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(ProxyGroupProvider)
|
||||
const proxyGroupProvider = ProxyGroupProviderProvider._();
|
||||
|
||||
final class ProxyGroupProviderProvider
|
||||
extends $NotifierProvider<ProxyGroupProvider, ProxyGroup> {
|
||||
const ProxyGroupProviderProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'proxyGroupProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$proxyGroupProviderHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ProxyGroupProvider create() => ProxyGroupProvider();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ProxyGroup value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ProxyGroup>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxyGroupProviderHash() =>
|
||||
r'3d14cffb0b0316646fa78b85083e74d573e55fe9';
|
||||
|
||||
abstract class _$ProxyGroupProvider extends $Notifier<ProxyGroup> {
|
||||
ProxyGroup build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<ProxyGroup, ProxyGroup>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<ProxyGroup, ProxyGroup>,
|
||||
ProxyGroup,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -678,20 +677,6 @@ Future<Script?> script(Ref ref, int? scriptId) async {
|
||||
return script;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<ClashConfig> clashConfig(Ref ref, int profileId) async {
|
||||
final configMap = await coreController.getConfig(profileId);
|
||||
final clashConfig = ClashConfig.fromJson(configMap);
|
||||
final Map<String, String> proxyTypeMap = {};
|
||||
for (final proxy in clashConfig.proxies) {
|
||||
proxyTypeMap[proxy.name] = proxy.type;
|
||||
}
|
||||
for (final proxyGroup in clashConfig.proxyGroups) {
|
||||
proxyTypeMap[proxyGroup.name] = proxyGroup.type.value;
|
||||
}
|
||||
return clashConfig.copyWith(proxyTypeMap: proxyTypeMap);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<SetupState> setupState(Ref ref, int? profileId) async {
|
||||
final profile = ref.watch(profileProvider(profileId));
|
||||
@@ -701,15 +686,9 @@ Future<SetupState> setupState(Ref ref, int? profileId) async {
|
||||
final dns = ref.watch(patchClashConfigProvider.select((state) => state.dns));
|
||||
final script = await ref.watch(scriptProvider(scriptId).future);
|
||||
final overrideDns = ref.watch(overrideDnsProvider);
|
||||
List<Rule> addedRules = [];
|
||||
if (profileId != null) {
|
||||
final currentProfileId = ref.read(currentProfileIdProvider);
|
||||
if (currentProfileId == profileId) {
|
||||
addedRules = await ref.watch(addedRulesStreamProvider(profileId).future);
|
||||
} else {
|
||||
addedRules = await ref.read(addedRulesProvider(profileId).future);
|
||||
}
|
||||
}
|
||||
final List<Rule> addedRules = profileId != null
|
||||
? await ref.watch(addedRuleStreamProvider(profileId).future)
|
||||
: [];
|
||||
return SetupState(
|
||||
profileId: profileId,
|
||||
profileLastUpdateDate: profileLastUpdateDate,
|
||||
@@ -727,12 +706,3 @@ class AccessControlState extends _$AccessControlState
|
||||
@override
|
||||
AccessControlProps build() => AccessControlProps();
|
||||
}
|
||||
|
||||
@Riverpod(name: 'proxyGroupProvider')
|
||||
class ProxyGroupProvider extends _$ProxyGroupProvider
|
||||
with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
ProxyGroup build() {
|
||||
return throw 'Initialization proxyGroupProvider error';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,9 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
await showSheet<int>(
|
||||
context: context,
|
||||
props: SheetProps(isScrollControlled: true),
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: AccessControlPanel(),
|
||||
title: appLocalizations.accessControlSettings,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/features/features.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/providers/database.dart';
|
||||
@@ -30,7 +29,7 @@ class _AddedRulesViewState extends ConsumerState<AddedRulesView> {
|
||||
}
|
||||
|
||||
void _handleSelected(int ruleId) {
|
||||
ref.read(itemsProvider(_key).notifier).update((selectedRules) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selectedRules) {
|
||||
final newSelectedRules = Set<int>.from(selectedRules)
|
||||
..addOrRemove(ruleId);
|
||||
return newSelectedRules;
|
||||
@@ -41,7 +40,7 @@ class _AddedRulesViewState extends ConsumerState<AddedRulesView> {
|
||||
final ids =
|
||||
ref.read(globalRulesProvider).value?.map((item) => item.id).toSet() ??
|
||||
{};
|
||||
ref.read(itemsProvider(_key).notifier).update((selected) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
@@ -56,19 +55,19 @@ class _AddedRulesViewState extends ConsumerState<AddedRulesView> {
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
final selectedRules = ref.read(itemsProvider(_key));
|
||||
final selectedRules = ref.read(selectedItemsProvider(_key));
|
||||
ref.read(globalRulesProvider.notifier).delAll(selectedRules.cast<int>());
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final rules = ref.watch(globalRulesProvider).value ?? [];
|
||||
final selectedRules = ref.watch(itemsProvider(_key));
|
||||
final selectedRules = ref.watch(selectedItemsProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedRules.isNotEmpty) {
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
@@ -107,30 +106,25 @@ class _AddedRulesViewState extends ConsumerState<AddedRulesView> {
|
||||
illustration: RuleEmptyIllustration(),
|
||||
)
|
||||
: ReorderableList(
|
||||
padding: EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
final position = ItemPosition.get(index, rules.length);
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ObjectKey(rule),
|
||||
index: index,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: RuleItem(
|
||||
isEditing: selectedRules.isNotEmpty,
|
||||
rule: rule,
|
||||
isSelected: selectedRules.contains(rule.id),
|
||||
onSelected: () {
|
||||
_handleSelected(rule.id);
|
||||
},
|
||||
onEdit: (Rule rule) {
|
||||
_handleAddOrUpdate(rule);
|
||||
},
|
||||
),
|
||||
child: RuleItem(
|
||||
isEditing: selectedRules.isNotEmpty,
|
||||
rule: rule,
|
||||
isSelected: selectedRules.contains(rule.id),
|
||||
onSelected: () {
|
||||
_handleSelected(rule.id);
|
||||
},
|
||||
onEdit: (Rule rule) {
|
||||
_handleAddOrUpdate(rule);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
itemExtent: ruleItemHeight,
|
||||
itemCount: rules.length,
|
||||
onReorder: ref.read(globalRulesProvider.notifier).order,
|
||||
),
|
||||
|
||||
@@ -37,7 +37,7 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
|
||||
return;
|
||||
}
|
||||
ref.read(scriptsProvider.notifier).del(id);
|
||||
ref.read(itemProvider(_key).notifier).value = null;
|
||||
ref.read(selectedItemProvider(_key).notifier).value = null;
|
||||
_clearEffect(id);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
|
||||
}
|
||||
|
||||
void _handleSelected(int id) {
|
||||
ref.read(itemProvider(_key).notifier).update((value) {
|
||||
ref.read(selectedItemProvider(_key).notifier).update((value) {
|
||||
if (value == id) {
|
||||
return null;
|
||||
}
|
||||
@@ -187,11 +187,11 @@ class _ScriptsViewState extends ConsumerState<ScriptsView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scripts = ref.watch(scriptsProvider).value ?? [];
|
||||
final selectedScriptId = ref.watch(itemProvider(_key));
|
||||
final selectedScriptId = ref.watch(selectedItemProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedScriptId != null) {
|
||||
ref.read(itemProvider(_key).notifier).value = null;
|
||||
ref.read(selectedItemProvider(_key).notifier).value = null;
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -144,8 +144,9 @@ class TrackerInfoItem extends ConsumerWidget {
|
||||
onTap: () {
|
||||
showExtend(
|
||||
context,
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: TrackerInfoDetailView(trackerInfo: trackerInfo),
|
||||
title: detailTitle,
|
||||
);
|
||||
|
||||
@@ -178,11 +178,12 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
|
||||
void _showAddWidgetsModal() {
|
||||
showSheet(
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _addedWidgetsNotifier,
|
||||
builder: (_, value, _) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: _AddDashboardWidgetModal(
|
||||
items: value,
|
||||
onAdd: (gridItem) {
|
||||
@@ -210,7 +211,7 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
if (currentState == null) {
|
||||
return;
|
||||
}
|
||||
if (mounted && currentState.children.isNotEmpty) {
|
||||
if (mounted) {
|
||||
await currentState.isTransformCompleter;
|
||||
final dashboardWidgets = currentState.children
|
||||
.map((item) => DashboardWidget.getDashboardWidget(item))
|
||||
@@ -262,7 +263,15 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
children: children,
|
||||
children: [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
(item) => item.platforms.contains(
|
||||
SupportPlatform.currentPlatform,
|
||||
),
|
||||
)
|
||||
.map((item) => item.widget),
|
||||
],
|
||||
onUpdate: () {
|
||||
_handleSave();
|
||||
},
|
||||
|
||||
@@ -49,49 +49,44 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(1),
|
||||
child: RepaintBoundary(
|
||||
child: CommonCard(
|
||||
info: Info(
|
||||
iconData: Icons.memory,
|
||||
label: appLocalizations.memoryInfo,
|
||||
),
|
||||
onPressed: () {
|
||||
coreController.requestGc();
|
||||
},
|
||||
child: Container(
|
||||
padding: baseInfoEdgeInsets.copyWith(top: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: globalState.measure.bodyMediumHeight + 2,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _memoryStateNotifier,
|
||||
builder: (_, memory, _) {
|
||||
final traffic = memory.traffic;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
traffic.value,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
traffic.unit,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
child: CommonCard(
|
||||
info: Info(iconData: Icons.memory, label: appLocalizations.memoryInfo),
|
||||
onPressed: () {
|
||||
coreController.requestGc();
|
||||
},
|
||||
child: Container(
|
||||
padding: baseInfoEdgeInsets.copyWith(top: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: globalState.measure.bodyMediumHeight + 2,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _memoryStateNotifier,
|
||||
builder: (_, memory, _) {
|
||||
final traffic = memory.traffic;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
traffic.value,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
traffic.unit,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -110,6 +110,7 @@ class OutboundModeV2 extends StatelessWidget {
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final mode = ref.watch(
|
||||
|
||||
@@ -16,22 +16,19 @@ class TUNButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return AdaptiveSheetScaffold(
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
if (system.isMacOS) const AutoSetSystemDnsItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: appLocalizations.tun,
|
||||
);
|
||||
},
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
if (system.isMacOS) const AutoSetSystemDnsItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: appLocalizations.tun,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -95,8 +92,9 @@ class SystemProxyButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [SystemProxyItem(), BypassDomainItem()],
|
||||
@@ -167,8 +165,9 @@ class VpnButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
|
||||
@@ -71,67 +71,66 @@ class _StartButtonState extends ConsumerState<StartButton>
|
||||
final hasProfile = ref.watch(
|
||||
profilesProvider.select((state) => state.isNotEmpty),
|
||||
);
|
||||
if (!hasProfile) {
|
||||
final isInit = ref.watch(coreInitProvider);
|
||||
if (!hasProfile || !isInit) {
|
||||
return Container();
|
||||
}
|
||||
return RepaintBoundary(
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller!.view,
|
||||
builder: (_, child) {
|
||||
final textWidth =
|
||||
globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
utils.getTimeDifference(DateTime.now()),
|
||||
style: context.textTheme.titleMedium?.toSoftBold,
|
||||
),
|
||||
)
|
||||
.width +
|
||||
16;
|
||||
return FloatingActionButton(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||
heroTag: null,
|
||||
onPressed: () {
|
||||
handleSwitchStart();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: _animation,
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
sizeConstraints: BoxConstraints(minWidth: 56, maxWidth: 200),
|
||||
),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller!.view,
|
||||
builder: (_, child) {
|
||||
final textWidth =
|
||||
globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
utils.getTimeDifference(DateTime.now()),
|
||||
style: context.textTheme.titleMedium?.toSoftBold,
|
||||
),
|
||||
)
|
||||
.width +
|
||||
16;
|
||||
return FloatingActionButton(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||
heroTag: null,
|
||||
onPressed: () {
|
||||
handleSwitchStart();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: _animation,
|
||||
),
|
||||
SizedBox(width: textWidth * _animation.value, child: child!),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: textWidth * _animation.value, child: child!),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final runTime = ref.watch(runTimeProvider);
|
||||
final text = utils.getTimeText(runTime);
|
||||
return Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.visible,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold
|
||||
.copyWith(color: context.colorScheme.onPrimaryContainer),
|
||||
);
|
||||
},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final runTime = ref.watch(runTimeProvider);
|
||||
final text = utils.getTimeText(runTime);
|
||||
return Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.visible,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold
|
||||
.copyWith(color: context.colorScheme.onPrimaryContainer),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -52,154 +52,147 @@ class TrafficUsage extends StatelessWidget {
|
||||
final secondaryColor = globalState.theme.darken2SecondaryContainer;
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
child: RepaintBoundary(
|
||||
child: CommonCard(
|
||||
info: Info(
|
||||
label: appLocalizations.trafficUsage,
|
||||
iconData: Icons.data_saver_off,
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final totalTraffic = ref.watch(totalTrafficProvider);
|
||||
final upTotalTrafficValue = totalTraffic.up;
|
||||
final downTotalTrafficValue = totalTraffic.down;
|
||||
return Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(top: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: DonutChart(
|
||||
data: [
|
||||
DonutChartData(
|
||||
value: upTotalTrafficValue.toDouble(),
|
||||
color: primaryColor,
|
||||
),
|
||||
DonutChartData(
|
||||
value: downTotalTrafficValue.toDouble(),
|
||||
color: secondaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CommonCard(
|
||||
info: Info(
|
||||
label: appLocalizations.trafficUsage,
|
||||
iconData: Icons.data_saver_off,
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final totalTraffic = ref.watch(totalTrafficProvider);
|
||||
final upTotalTrafficValue = totalTraffic.up;
|
||||
final downTotalTrafficValue = totalTraffic.down;
|
||||
return Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(top: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: DonutChart(
|
||||
data: [
|
||||
DonutChartData(
|
||||
value: upTotalTrafficValue.toDouble(),
|
||||
color: primaryColor,
|
||||
),
|
||||
DonutChartData(
|
||||
value: downTotalTrafficValue.toDouble(),
|
||||
color: secondaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
final uploadText = Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.upload,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
);
|
||||
final downloadText = Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.download,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
);
|
||||
final uploadTextSize = globalState.measure
|
||||
.computeTextSize(uploadText);
|
||||
final downloadTextSize = globalState.measure
|
||||
.computeTextSize(downloadText);
|
||||
final maxTextWidth = max(
|
||||
uploadTextSize.width,
|
||||
downloadTextSize.width,
|
||||
);
|
||||
if (maxTextWidth + 24 > container.maxWidth) {
|
||||
return Container();
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
color: primaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
final uploadText = Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.upload,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
);
|
||||
final downloadText = Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.download,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
);
|
||||
final uploadTextSize = globalState.measure
|
||||
.computeTextSize(uploadText);
|
||||
final downloadTextSize = globalState.measure
|
||||
.computeTextSize(downloadText);
|
||||
final maxTextWidth = max(
|
||||
uploadTextSize.width,
|
||||
downloadTextSize.width,
|
||||
);
|
||||
if (maxTextWidth + 24 > container.maxWidth) {
|
||||
return Container();
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
color: primaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.upload,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
color: secondaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.upload,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 8,
|
||||
decoration: ShapeDecoration(
|
||||
color: secondaryColor,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.download,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
maxLines: 1,
|
||||
appLocalizations.download,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildTrafficDataItem(
|
||||
context,
|
||||
Icon(Icons.arrow_upward, color: primaryColor, size: 14),
|
||||
upTotalTrafficValue,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildTrafficDataItem(
|
||||
context,
|
||||
Icon(
|
||||
Icons.arrow_downward,
|
||||
color: secondaryColor,
|
||||
size: 14,
|
||||
),
|
||||
downTotalTrafficValue,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
_buildTrafficDataItem(
|
||||
context,
|
||||
Icon(Icons.arrow_upward, color: primaryColor, size: 14),
|
||||
upTotalTrafficValue,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildTrafficDataItem(
|
||||
context,
|
||||
Icon(Icons.arrow_downward, color: secondaryColor, size: 14),
|
||||
downTotalTrafficValue,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
524
lib/views/profiles/overwrite.dart
Normal file
524
lib/views/profiles/overwrite.dart
Normal file
@@ -0,0 +1,524 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/features/overwrite/rule.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/pages/editor.dart';
|
||||
import 'package:fl_clash/providers/database.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/views/config/scripts.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class OverwriteView extends ConsumerStatefulWidget {
|
||||
final int profileId;
|
||||
|
||||
const OverwriteView({super.key, required this.profileId});
|
||||
|
||||
@override
|
||||
ConsumerState<OverwriteView> createState() => _OverwriteViewState();
|
||||
}
|
||||
|
||||
class _OverwriteViewState extends ConsumerState<OverwriteView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> _handlePreview() async {
|
||||
final profile = ref.read(profileProvider(widget.profileId));
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
final configMap = await appController.getProfileWithId(profile.id);
|
||||
final content = await encodeYamlTask(configMap);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final previewPage = EditorPage(title: profile.realLabel, content: content);
|
||||
BaseNavigator.push<String>(context, previewPage);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonScaffold(
|
||||
title: appLocalizations.override,
|
||||
actions: [
|
||||
CommonMinFilledButtonTheme(
|
||||
child: FilledButton(
|
||||
onPressed: _handlePreview,
|
||||
child: Text(appLocalizations.preview),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
body: CustomScrollView(
|
||||
slivers: [_Title(widget.profileId), _Content(widget.profileId)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
appController.autoApplyProfile();
|
||||
}
|
||||
}
|
||||
|
||||
class _Title extends ConsumerWidget {
|
||||
final int profileId;
|
||||
|
||||
const _Title(this.profileId);
|
||||
|
||||
String _getTitle(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => appLocalizations.standard,
|
||||
OverwriteType.script => appLocalizations.script,
|
||||
// OverwriteType.custom => appLocalizations.overwriteTypeCustom,
|
||||
};
|
||||
}
|
||||
|
||||
IconData _getIcon(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => Icons.stars,
|
||||
OverwriteType.script => Icons.rocket,
|
||||
// OverwriteType.custom => Icons.dashboard_customize,
|
||||
};
|
||||
}
|
||||
|
||||
String _getDesc(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => appLocalizations.standardModeDesc,
|
||||
OverwriteType.script => appLocalizations.scriptModeDesc,
|
||||
// OverwriteType.custom => appLocalizations.overwriteTypeCustomDesc,
|
||||
};
|
||||
}
|
||||
|
||||
void _handleChangeType(WidgetRef ref, OverwriteType type) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||
return state.copyWith(overwriteType: type);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context, ref) {
|
||||
final overwriteType = ref.watch(overwriteTypeProvider(profileId));
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InfoHeader(info: Info(label: appLocalizations.overrideMode)),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
children: [
|
||||
for (final type in OverwriteType.values)
|
||||
CommonCard(
|
||||
isSelected: overwriteType == type,
|
||||
onPressed: () {
|
||||
_handleChangeType(ref, type);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(_getIcon(type)),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(child: Text(_getTitle(type))),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
_getDesc(overwriteType),
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant.opacity80,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Content extends ConsumerWidget {
|
||||
final int profileId;
|
||||
|
||||
const _Content(this.profileId);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final overwriteType = ref.watch(overwriteTypeProvider(profileId));
|
||||
return switch (overwriteType) {
|
||||
OverwriteType.standard => _StandardContent(profileId),
|
||||
OverwriteType.script => _ScriptContent(profileId),
|
||||
// OverwriteType.custom => SliverToBoxAdapter(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class _StandardContent extends ConsumerStatefulWidget {
|
||||
final int profileId;
|
||||
|
||||
const _StandardContent(this.profileId);
|
||||
|
||||
@override
|
||||
ConsumerState createState() => __StandardContentState();
|
||||
}
|
||||
|
||||
class __StandardContentState extends ConsumerState<_StandardContent> {
|
||||
final _key = utils.id;
|
||||
|
||||
Future<void> _handleAddOrUpdate([Rule? rule]) async {
|
||||
final res = await globalState.showCommonDialog<Rule>(
|
||||
child: AddOrEditRuleDialog(rule: rule),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileAddedRulesProvider(widget.profileId).notifier).put(res);
|
||||
}
|
||||
|
||||
void _handleSelected(int ruleId) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selectedRules) {
|
||||
final newSelectedRules = Set<int>.from(selectedRules)
|
||||
..addOrRemove(ruleId);
|
||||
return newSelectedRules;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSelectAll() {
|
||||
final ids =
|
||||
ref
|
||||
.read(profileAddedRulesProvider(widget.profileId))
|
||||
.value
|
||||
?.map((item) => item.id)
|
||||
.toSet() ??
|
||||
{};
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handleDelete() async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.tip,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.deleteMultipTip(appLocalizations.rule),
|
||||
),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
final selectedRules = ref.read(selectedItemsProvider(_key));
|
||||
ref
|
||||
.read(profileAddedRulesProvider(widget.profileId).notifier)
|
||||
.delAll(selectedRules.cast<int>());
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final addedRules =
|
||||
ref.watch(profileAddedRulesProvider(widget.profileId)).value ?? [];
|
||||
final selectedRules = ref.watch(selectedItemsProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedRules.isNotEmpty) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
return false;
|
||||
},
|
||||
child: SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
InfoHeader(
|
||||
info: Info(label: appLocalizations.addedRules),
|
||||
actions: [
|
||||
if (selectedRules.isNotEmpty) ...[
|
||||
CommonMinIconButtonTheme(
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: () {
|
||||
_handleDelete();
|
||||
},
|
||||
icon: Icon(Icons.delete),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
CommonMinFilledButtonTheme(
|
||||
child: selectedRules.isNotEmpty
|
||||
? FilledButton(
|
||||
onPressed: () {
|
||||
_handleSelectAll();
|
||||
},
|
||||
child: Text(appLocalizations.selectAll),
|
||||
)
|
||||
: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
_handleAddOrUpdate();
|
||||
},
|
||||
child: Text(appLocalizations.add),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 8)),
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
return SliverReorderableList(
|
||||
itemCount: addedRules.length,
|
||||
itemBuilder: (_, index) {
|
||||
final rule = addedRules[index];
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ObjectKey(rule),
|
||||
index: index,
|
||||
child: RuleItem(
|
||||
isEditing: selectedRules.isNotEmpty,
|
||||
isSelected: selectedRules.contains(rule.id),
|
||||
rule: rule,
|
||||
onSelected: () {
|
||||
_handleSelected(rule.id);
|
||||
},
|
||||
onEdit: (rule) {
|
||||
_handleAddOrUpdate(rule);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
onReorder: ref
|
||||
.read(profileAddedRulesProvider(widget.profileId).notifier)
|
||||
.order,
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
radius: 18,
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
appLocalizations.controlGlobalAddedRules,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Icon(Icons.arrow_forward, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
BaseNavigator.push(
|
||||
context,
|
||||
_EditGlobalAddedRules(profileId: widget.profileId),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ScriptContent extends ConsumerWidget {
|
||||
final int profileId;
|
||||
|
||||
const _ScriptContent(this.profileId);
|
||||
|
||||
void _handleChange(WidgetRef ref, int scriptId) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||
return state.copyWith(
|
||||
scriptId: state.scriptId == scriptId ? null : scriptId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final scriptId = ref.watch(
|
||||
profileProvider(profileId).select((state) => state?.scriptId),
|
||||
);
|
||||
final scripts = ref.watch(scriptsProvider).value ?? [];
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
InfoHeader(info: Info(label: appLocalizations.overrideScript)),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 8)),
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
return SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverList.builder(
|
||||
itemCount: scripts.length,
|
||||
itemBuilder: (_, index) {
|
||||
final script = scripts[index];
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
type: CommonCardType.filled,
|
||||
radius: 18,
|
||||
child: ListTile(
|
||||
minLeadingWidth: 0,
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 16,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: Radio(
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
toggleable: true,
|
||||
value: script.id,
|
||||
groupValue: scriptId,
|
||||
onChanged: (_) {
|
||||
_handleChange(ref, script.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Flexible(child: Text(script.label)),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
_handleChange(ref, script.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
radius: 18,
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
appLocalizations.goToConfigureScript,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Icon(Icons.arrow_forward, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
BaseNavigator.push(context, const ScriptsView());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EditGlobalAddedRules extends ConsumerWidget {
|
||||
final int profileId;
|
||||
|
||||
const _EditGlobalAddedRules({required this.profileId});
|
||||
|
||||
void _handleChange(WidgetRef ref, bool status, int ruleId) {
|
||||
if (status) {
|
||||
ref.read(profileDisabledRuleIdsProvider(profileId).notifier).put(ruleId);
|
||||
} else {
|
||||
ref.read(profileDisabledRuleIdsProvider(profileId).notifier).del(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final disabledRuleIds =
|
||||
ref.watch(profileDisabledRuleIdsProvider(profileId)).value ?? [];
|
||||
final rules = ref.watch(globalRulesProvider).value ?? [];
|
||||
return BaseScaffold(
|
||||
title: appLocalizations.editGlobalRules,
|
||||
body: rules.isEmpty
|
||||
? NullStatus(
|
||||
label: appLocalizations.nullTip(appLocalizations.rule),
|
||||
illustration: RuleEmptyIllustration(),
|
||||
)
|
||||
: ListView.builder(
|
||||
padding: EdgeInsets.all(16),
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
return RuleStatusItem(
|
||||
status: !disabledRuleIds.contains(rule.id),
|
||||
rule: rule,
|
||||
onChange: (status) {
|
||||
_handleChange(ref, !status, rule.id);
|
||||
},
|
||||
);
|
||||
},
|
||||
itemCount: rules.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
part of 'overwrite.dart';
|
||||
|
||||
class _CustomContent extends ConsumerWidget {
|
||||
const _CustomContent();
|
||||
|
||||
void _handleUseDefault(WidgetRef ref, int profileId) async {
|
||||
final clashConfig = await ref.read(clashConfigProvider(profileId).future);
|
||||
await database.setProfileCustomData(
|
||||
profileId,
|
||||
clashConfig.proxyGroups,
|
||||
clashConfig.rules,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleToProxyGroupsView(BuildContext context, int profileId) {
|
||||
BaseNavigator.push(context, _CustomProxyGroupsView(profileId));
|
||||
}
|
||||
|
||||
void _handleToRulesView(BuildContext context, int profileId) {
|
||||
BaseNavigator.push(context, _CustomRulesView(profileId));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
final proxyGroupNum =
|
||||
ref.watch(proxyGroupsCountProvider(profileId)).value ?? -1;
|
||||
final ruleNum = ref.watch(customRulesCountProvider(profileId)).value ?? -1;
|
||||
final hasDefault = ref.watch(
|
||||
clashConfigProvider(profileId).select((state) {
|
||||
final clashConfig = state.value;
|
||||
return ((clashConfig?.proxyGroups.length ?? 0) +
|
||||
(clashConfig?.rules.length ?? 0)) >
|
||||
0;
|
||||
}),
|
||||
);
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [InfoHeader(info: Info(label: '自定义'))],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 8)),
|
||||
SliverToBoxAdapter(
|
||||
child: _MoreActionButton(
|
||||
label: '策略组',
|
||||
onPressed: () {
|
||||
_handleToProxyGroupsView(context, profileId);
|
||||
},
|
||||
trailing: Card.filled(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minWidth: 44),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
'$proxyGroupNum',
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 4)),
|
||||
SliverToBoxAdapter(
|
||||
child: _MoreActionButton(
|
||||
label: '规则',
|
||||
onPressed: () {
|
||||
_handleToRulesView(context, profileId);
|
||||
},
|
||||
trailing: Card.filled(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minWidth: 44),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: Text(
|
||||
'$ruleNum',
|
||||
style: context.textTheme.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 32)),
|
||||
if (proxyGroupNum == 0 && ruleNum == 0 && hasDefault)
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: MaterialBanner(
|
||||
elevation: 0,
|
||||
dividerColor: Colors.transparent,
|
||||
content: Text('检测到没有数据'),
|
||||
actions: [
|
||||
CommonMinFilledButtonTheme(
|
||||
child: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
_handleUseDefault(ref, profileId);
|
||||
},
|
||||
child: Text('一键填入'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
||||
part of 'overwrite.dart';
|
||||
|
||||
class _CustomRulesView extends ConsumerStatefulWidget {
|
||||
final int profileId;
|
||||
|
||||
const _CustomRulesView(this.profileId);
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _CustomRulesViewState();
|
||||
}
|
||||
|
||||
class _CustomRulesViewState extends ConsumerState<_CustomRulesView>
|
||||
with UniqueKeyStateMixin {
|
||||
int get _profileId => widget.profileId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _handleReorder(int oldIndex, int newIndex) {
|
||||
ref
|
||||
.read(profileCustomRulesProvider(_profileId).notifier)
|
||||
.order(oldIndex, newIndex);
|
||||
}
|
||||
|
||||
void _handleSelected(int ruleId) {
|
||||
ref.read(itemsProvider(key).notifier).update((selectedRules) {
|
||||
final newSelectedRules = Set<int>.from(selectedRules)
|
||||
..addOrRemove(ruleId);
|
||||
return newSelectedRules;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSelectAll() {
|
||||
final ids =
|
||||
ref
|
||||
.read(profileCustomRulesProvider(_profileId))
|
||||
.value
|
||||
?.map((item) => item.id)
|
||||
.toSet() ??
|
||||
{};
|
||||
ref.read(itemsProvider(key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handleDelete() async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.tip,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.deleteMultipTip(appLocalizations.rule),
|
||||
),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
final selectedRules = ref.read(itemsProvider(key));
|
||||
ref
|
||||
.read(profileCustomRulesProvider(_profileId).notifier)
|
||||
.delAll(selectedRules.cast<int>());
|
||||
ref.read(itemsProvider(key).notifier).value = {};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
final rules = ref.watch(profileCustomRulesProvider(_profileId)).value ?? [];
|
||||
final selectedRules = ref.watch(itemsProvider(key));
|
||||
return CommonScaffold(
|
||||
title: appLocalizations.rule,
|
||||
actions: [
|
||||
if (selectedRules.isNotEmpty) ...[
|
||||
CommonMinIconButtonTheme(
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: _handleDelete,
|
||||
icon: Icon(Icons.delete),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
],
|
||||
CommonMinFilledButtonTheme(
|
||||
child: selectedRules.isNotEmpty
|
||||
? FilledButton(
|
||||
onPressed: _handleSelectAll,
|
||||
child: Text(appLocalizations.selectAll),
|
||||
)
|
||||
: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
// _handleAddOrUpdate();
|
||||
},
|
||||
child: Text(appLocalizations.add),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
body: ReorderableListView.builder(
|
||||
buildDefaultDragHandles: false,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
itemBuilder: (_, index) {
|
||||
final rule = rules[index];
|
||||
final position = ItemPosition.get(index, rules.length);
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey(rule),
|
||||
index: index,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: RuleItem(
|
||||
isEditing: selectedRules.isNotEmpty,
|
||||
isSelected: selectedRules.contains(rule.id),
|
||||
rule: rule,
|
||||
onSelected: () {
|
||||
_handleSelected(rule.id);
|
||||
},
|
||||
onEdit: (rule) {
|
||||
// _handleAddOrUpdate(rule);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemExtent: ruleItemHeight,
|
||||
itemCount: rules.length,
|
||||
onReorder: _handleReorder,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/database/database.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/features/overwrite/rule.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/providers/database.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/views/config/scripts.dart';
|
||||
import 'package:fl_clash/views/profiles/preview.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:smooth_sheets/smooth_sheets.dart';
|
||||
|
||||
part 'custom.dart';
|
||||
part 'custom_groups.dart';
|
||||
part 'custom_rules.dart';
|
||||
part 'script.dart';
|
||||
part 'standard.dart';
|
||||
part 'widgets.dart';
|
||||
|
||||
class OverwriteView extends ConsumerStatefulWidget {
|
||||
final int profileId;
|
||||
|
||||
const OverwriteView({super.key, required this.profileId});
|
||||
|
||||
@override
|
||||
ConsumerState<OverwriteView> createState() => _OverwriteViewState();
|
||||
}
|
||||
|
||||
class _OverwriteViewState extends ConsumerState<OverwriteView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> _handlePreview() async {
|
||||
final profile = ref.read(profileProvider(widget.profileId));
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
BaseNavigator.push<String>(context, PreviewProfileView(profile: profile));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProfileIdProvider(
|
||||
profileId: widget.profileId,
|
||||
child: CommonScaffold(
|
||||
title: appLocalizations.override,
|
||||
actions: [
|
||||
CommonMinFilledButtonTheme(
|
||||
child: FilledButton(
|
||||
onPressed: _handlePreview,
|
||||
child: Text(appLocalizations.preview),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
body: CustomScrollView(slivers: [_Title(), _Content()]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
appController.autoApplyProfile();
|
||||
}
|
||||
}
|
||||
|
||||
class _Title extends ConsumerWidget {
|
||||
const _Title();
|
||||
|
||||
String _getTitle(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => appLocalizations.standard,
|
||||
OverwriteType.script => appLocalizations.script,
|
||||
OverwriteType.custom => appLocalizations.overwriteTypeCustom,
|
||||
};
|
||||
}
|
||||
|
||||
IconData _getIcon(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => Icons.stars,
|
||||
OverwriteType.script => Icons.rocket,
|
||||
OverwriteType.custom => Icons.dashboard_customize,
|
||||
};
|
||||
}
|
||||
|
||||
String _getDesc(OverwriteType type) {
|
||||
return switch (type) {
|
||||
OverwriteType.standard => appLocalizations.standardModeDesc,
|
||||
OverwriteType.script => appLocalizations.scriptModeDesc,
|
||||
OverwriteType.custom => appLocalizations.overwriteTypeCustomDesc,
|
||||
};
|
||||
}
|
||||
|
||||
void _handleChangeType(WidgetRef ref, int profileId, OverwriteType type) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||
return state.copyWith(overwriteType: type);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context, ref) {
|
||||
final profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
final overwriteType = ref.watch(overwriteTypeProvider(profileId));
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InfoHeader(info: Info(label: appLocalizations.overrideMode)),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
children: [
|
||||
for (final type in OverwriteType.values)
|
||||
CommonCard(
|
||||
isSelected: overwriteType == type,
|
||||
onPressed: () {
|
||||
_handleChangeType(ref, profileId, type);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(_getIcon(type)),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(child: Text(_getTitle(type))),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
_getDesc(overwriteType),
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant.opacity80,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Content extends ConsumerWidget {
|
||||
const _Content();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
final overwriteType = ref.watch(overwriteTypeProvider(profileId));
|
||||
ref.listen(clashConfigProvider(profileId), (_, _) {});
|
||||
return switch (overwriteType) {
|
||||
OverwriteType.standard => _StandardContent(),
|
||||
OverwriteType.script => _ScriptContent(),
|
||||
OverwriteType.custom => _CustomContent(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
part of 'overwrite.dart';
|
||||
|
||||
class _ScriptContent extends ConsumerWidget {
|
||||
const _ScriptContent();
|
||||
|
||||
void _handleChange(WidgetRef ref, int profileId, int scriptId) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(profileId, (state) {
|
||||
return state.copyWith(
|
||||
scriptId: state.scriptId == scriptId ? null : scriptId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
final scriptId = ref.watch(
|
||||
profileProvider(profileId).select((state) => state?.scriptId),
|
||||
);
|
||||
final scripts = ref.watch(scriptsProvider).value ?? [];
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
InfoHeader(info: Info(label: appLocalizations.overrideScript)),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 8)),
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
return SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverList.builder(
|
||||
itemCount: scripts.length,
|
||||
itemBuilder: (_, index) {
|
||||
final script = scripts[index];
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
radius: 18,
|
||||
child: ListTile(
|
||||
minLeadingWidth: 0,
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 16,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: Radio(
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
toggleable: true,
|
||||
value: script.id,
|
||||
groupValue: scriptId,
|
||||
onChanged: (_) {
|
||||
_handleChange(ref, profileId, script.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Flexible(child: Text(script.label)),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
_handleChange(ref, profileId, script.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: CommonCard(
|
||||
radius: 18,
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
appLocalizations.goToConfigureScript,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
Icon(Icons.arrow_forward_ios, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
BaseNavigator.push(context, const ScriptsView());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
part of 'overwrite.dart';
|
||||
|
||||
class _StandardContent extends ConsumerStatefulWidget {
|
||||
const _StandardContent();
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _StandardContentState();
|
||||
}
|
||||
|
||||
class _StandardContentState extends ConsumerState<_StandardContent> {
|
||||
final _key = utils.id;
|
||||
late int _profileId;
|
||||
|
||||
Future<void> _handleAddOrUpdate([Rule? rule]) async {
|
||||
final res = await globalState.showCommonDialog<Rule>(
|
||||
child: AddOrEditRuleDialog(rule: rule),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileAddedRulesProvider(_profileId).notifier).put(res);
|
||||
}
|
||||
|
||||
void _handleSelected(int ruleId) {
|
||||
ref.read(itemsProvider(_key).notifier).update((selectedRules) {
|
||||
final newSelectedRules = Set<int>.from(selectedRules)
|
||||
..addOrRemove(ruleId);
|
||||
return newSelectedRules;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSelectAll() {
|
||||
final ids =
|
||||
ref
|
||||
.read(profileAddedRulesProvider(_profileId))
|
||||
.value
|
||||
?.map((item) => item.id)
|
||||
.toSet() ??
|
||||
{};
|
||||
ref.read(itemsProvider(_key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handleDelete() async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.tip,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.deleteMultipTip(appLocalizations.rule),
|
||||
),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
final selectedRules = ref.read(itemsProvider(_key));
|
||||
ref
|
||||
.read(profileAddedRulesProvider(_profileId).notifier)
|
||||
.delAll(selectedRules.cast<int>());
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
}
|
||||
|
||||
void _handleToEditGlobalAddedRules() {
|
||||
BaseNavigator.push(context, _EditGlobalAddedRules(_profileId));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_profileId = ProfileIdProvider.of(context)!.profileId;
|
||||
final addedRules =
|
||||
ref.watch(profileAddedRulesProvider(_profileId)).value ?? [];
|
||||
final selectedRules = ref.watch(itemsProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedRules.isNotEmpty) {
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
return false;
|
||||
},
|
||||
child: SliverMainAxisGroup(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
InfoHeader(
|
||||
info: Info(label: appLocalizations.addedRules),
|
||||
actions: [
|
||||
if (selectedRules.isNotEmpty) ...[
|
||||
CommonMinIconButtonTheme(
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: () {
|
||||
_handleDelete();
|
||||
},
|
||||
icon: Icon(Icons.delete),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
CommonMinFilledButtonTheme(
|
||||
child: selectedRules.isNotEmpty
|
||||
? FilledButton(
|
||||
onPressed: () {
|
||||
_handleSelectAll();
|
||||
},
|
||||
child: Text(appLocalizations.selectAll),
|
||||
)
|
||||
: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
_handleAddOrUpdate();
|
||||
},
|
||||
child: Text(appLocalizations.add),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 8)),
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
return SliverReorderableList(
|
||||
itemCount: addedRules.length,
|
||||
itemBuilder: (_, index) {
|
||||
final rule = addedRules[index];
|
||||
final position = ItemPosition.get(index, addedRules.length);
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ObjectKey(rule),
|
||||
index: index,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: RuleItem(
|
||||
isEditing: selectedRules.isNotEmpty,
|
||||
isSelected: selectedRules.contains(rule.id),
|
||||
rule: rule,
|
||||
onSelected: () {
|
||||
_handleSelected(rule.id);
|
||||
},
|
||||
onEdit: (rule) {
|
||||
_handleAddOrUpdate(rule);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemExtent: ruleItemHeight,
|
||||
onReorder: ref
|
||||
.read(profileAddedRulesProvider(_profileId).notifier)
|
||||
.order,
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 16)),
|
||||
SliverToBoxAdapter(
|
||||
child: _MoreActionButton(
|
||||
label: appLocalizations.controlGlobalAddedRules,
|
||||
onPressed: _handleToEditGlobalAddedRules,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EditGlobalAddedRules extends ConsumerWidget {
|
||||
final int profileId;
|
||||
|
||||
const _EditGlobalAddedRules(this.profileId);
|
||||
|
||||
void _handleChange(WidgetRef ref, int profileId, bool status, int ruleId) {
|
||||
if (status) {
|
||||
ref.read(profileDisabledRuleIdsProvider(profileId).notifier).put(ruleId);
|
||||
} else {
|
||||
ref.read(profileDisabledRuleIdsProvider(profileId).notifier).del(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final disabledRuleIds =
|
||||
ref.watch(profileDisabledRuleIdsProvider(profileId)).value ?? [];
|
||||
final rules = ref.watch(globalRulesProvider).value ?? [];
|
||||
return BaseScaffold(
|
||||
title: appLocalizations.editGlobalRules,
|
||||
body: rules.isEmpty
|
||||
? NullStatus(
|
||||
label: appLocalizations.nullTip(appLocalizations.rule),
|
||||
illustration: RuleEmptyIllustration(),
|
||||
)
|
||||
: ListView.builder(
|
||||
padding: EdgeInsets.all(16),
|
||||
itemExtent: ruleItemHeight,
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
final position = ItemPosition.get(index, rules.length);
|
||||
return ItemPositionProvider(
|
||||
position: position,
|
||||
child: RuleStatusItem(
|
||||
status: !disabledRuleIds.contains(rule.id),
|
||||
rule: rule,
|
||||
onChange: (status) {
|
||||
_handleChange(ref, profileId, !status, rule.id);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: rules.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
part of 'overwrite.dart';
|
||||
|
||||
class _MoreActionButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final String label;
|
||||
final Widget? trailing;
|
||||
|
||||
const _MoreActionButton({this.onPressed, required this.label, this.trailing});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: CommonCard(
|
||||
radius: 18,
|
||||
onPressed: onPressed,
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
title: Text(label, style: context.textTheme.bodyLarge),
|
||||
trailing: trailing ?? Icon(Icons.arrow_forward_ios, size: 18),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileIdProvider extends InheritedWidget {
|
||||
final int profileId;
|
||||
|
||||
const ProfileIdProvider({
|
||||
super.key,
|
||||
required this.profileId,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static ProfileIdProvider? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ProfileIdProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ProfileIdProvider oldWidget) =>
|
||||
profileId != oldWidget.profileId;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import 'package:fl_clash/common/task.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/models/profile.dart';
|
||||
import 'package:fl_clash/pages/editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PreviewProfileView extends StatefulWidget {
|
||||
final Profile profile;
|
||||
|
||||
const PreviewProfileView({super.key, required this.profile});
|
||||
|
||||
@override
|
||||
State<PreviewProfileView> createState() => _PreviewProfileViewState();
|
||||
}
|
||||
|
||||
class _PreviewProfileViewState extends State<PreviewProfileView> {
|
||||
final contentNotifier = ValueNotifier<String?>(null);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final configMap = await appController.getProfileWithId(widget.profile.id);
|
||||
final content = await encodeYamlTask(configMap);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
contentNotifier.value = content;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
contentNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: contentNotifier,
|
||||
builder: (_, content, _) {
|
||||
final title = widget.profile.realLabel;
|
||||
|
||||
return EditorPage(key: Key('content'), title: title, content: content);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/pages/editor.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/views/profiles/overwrite/overwrite.dart';
|
||||
import 'package:fl_clash/views/profiles/overwrite.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -12,7 +13,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'add.dart';
|
||||
import 'edit.dart';
|
||||
import 'preview.dart';
|
||||
|
||||
class ProfilesView extends StatefulWidget {
|
||||
const ProfilesView({super.key});
|
||||
@@ -25,29 +25,12 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
Function? applyConfigDebounce;
|
||||
bool _isUpdating = false;
|
||||
|
||||
// final GlobalKey _targetKey = GlobalKey();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// final context = _targetKey.currentContext;
|
||||
// if (context == null) {
|
||||
// return;
|
||||
// }
|
||||
// Scrollable.ensureVisible(
|
||||
// context,
|
||||
// duration: commonDuration,
|
||||
// alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd,
|
||||
// );
|
||||
// });
|
||||
}
|
||||
|
||||
void _handleShowAddExtendPage() {
|
||||
showExtend(
|
||||
globalState.navigatorKey.currentState!.context,
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: AddProfileView(
|
||||
context: globalState.navigatorKey.currentState!.context,
|
||||
),
|
||||
@@ -93,8 +76,11 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return ReorderableProfilesSheet(profiles: profiles);
|
||||
builder: (_, type) {
|
||||
return ReorderableProfilesSheet(
|
||||
type: type,
|
||||
profiles: profiles,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -148,6 +134,7 @@ class _ProfilesViewState extends State<ProfilesView> {
|
||||
for (int i = 0; i < state.profiles.length; i++)
|
||||
GridItem(
|
||||
child: ProfileItem(
|
||||
key: Key(state.profiles[i].id.toString()),
|
||||
profile: state.profiles[i],
|
||||
groupValue: state.currentProfileId,
|
||||
onChanged: (profileId) {
|
||||
@@ -194,7 +181,14 @@ class ProfileItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
Future<void> _handlePreview(BuildContext context) async {
|
||||
BaseNavigator.push<String>(context, PreviewProfileView(profile: profile));
|
||||
final configMap = await appController.getProfileWithId(profile.id);
|
||||
final content = await encodeYamlTask(configMap);
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final previewPage = EditorPage(title: profile.realLabel, content: content);
|
||||
BaseNavigator.push<String>(context, previewPage);
|
||||
}
|
||||
|
||||
Future updateProfile() async {
|
||||
@@ -208,8 +202,9 @@ class ProfileItem extends StatelessWidget {
|
||||
void _handleShowEditExtendPage(BuildContext context) {
|
||||
showExtend(
|
||||
context,
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: EditProfileView(profile: profile, context: context),
|
||||
title: '${appLocalizations.edit}${appLocalizations.profile}',
|
||||
);
|
||||
@@ -421,8 +416,13 @@ class ProfileItem extends StatelessWidget {
|
||||
|
||||
class ReorderableProfilesSheet extends StatefulWidget {
|
||||
final List<Profile> profiles;
|
||||
final SheetType type;
|
||||
|
||||
const ReorderableProfilesSheet({super.key, required this.profiles});
|
||||
const ReorderableProfilesSheet({
|
||||
super.key,
|
||||
required this.profiles,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ReorderableProfilesSheet> createState() =>
|
||||
@@ -439,19 +439,19 @@ class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
|
||||
}
|
||||
|
||||
Widget _buildItem(int index, [bool isDecorator = false]) {
|
||||
final position = ItemPosition.get(index, profiles.length);
|
||||
final isLast = index == profiles.length - 1;
|
||||
final isFirst = index == 0;
|
||||
final profile = profiles[index];
|
||||
return ItemPositionProvider(
|
||||
return CommonInputListItem(
|
||||
key: Key(profile.id.toString()),
|
||||
position: position,
|
||||
child: DecorationListItem(
|
||||
trailing: ReorderableDelayedDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
title: Text(profile.realLabel),
|
||||
isDecorator: isDecorator,
|
||||
trailing: ReorderableDelayedDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
title: Text(profile.realLabel),
|
||||
isFirst: isFirst,
|
||||
isLast: isLast,
|
||||
isDecorator: isDecorator,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -463,15 +463,30 @@ class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AdaptiveSheetScaffold(
|
||||
sheetTransparentToolBar: true,
|
||||
actions: [IconButtonData(icon: Icons.check, onPressed: _handleSave)],
|
||||
type: widget.type,
|
||||
actions: [
|
||||
if (widget.type == SheetType.bottomSheet)
|
||||
IconButton.filledTonal(
|
||||
onPressed: _handleSave,
|
||||
style: IconButton.styleFrom(
|
||||
visualDensity: VisualDensity.comfortable,
|
||||
tapTargetSize: MaterialTapTargetSize.padded,
|
||||
padding: EdgeInsets.all(8),
|
||||
iconSize: 20,
|
||||
),
|
||||
icon: Icon(Icons.check),
|
||||
)
|
||||
else
|
||||
IconButton.filledTonal(
|
||||
icon: Icon(Icons.check),
|
||||
onPressed: _handleSave,
|
||||
),
|
||||
],
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(bottom: 32),
|
||||
padding: EdgeInsets.only(bottom: 32, top: 12),
|
||||
child: ReorderableListView.builder(
|
||||
buildDefaultDragHandles: false,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
).copyWith(top: context.sheetTopPadding),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
proxyDecorator: (child, index, animation) {
|
||||
return commonProxyDecorator(
|
||||
_buildItem(index, true),
|
||||
|
||||
@@ -15,7 +15,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
typedef UpdatingMap = Map<String, bool>;
|
||||
|
||||
class ProvidersView extends ConsumerStatefulWidget {
|
||||
const ProvidersView({super.key});
|
||||
final SheetType type;
|
||||
|
||||
const ProvidersView({super.key, required this.type});
|
||||
|
||||
@override
|
||||
ConsumerState<ProvidersView> createState() => _ProvidersViewState();
|
||||
@@ -56,7 +58,15 @@ class _ProvidersViewState extends ConsumerState<ProvidersView> {
|
||||
items: ruleProviders,
|
||||
);
|
||||
return AdaptiveSheetScaffold(
|
||||
actions: [IconButtonData(icon: Icons.sync, onPressed: _updateProviders)],
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_updateProviders();
|
||||
},
|
||||
icon: const Icon(Icons.sync),
|
||||
),
|
||||
],
|
||||
type: widget.type,
|
||||
body: generateListView([...proxySection, ...ruleSection]),
|
||||
title: appLocalizations.providers,
|
||||
);
|
||||
|
||||
@@ -53,8 +53,9 @@ class _ProxiesViewState extends ConsumerState<ProxiesView> {
|
||||
showSheet(
|
||||
context: context,
|
||||
props: SheetProps(isScrollControlled: true),
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: const ProxiesSetting(),
|
||||
title: appLocalizations.settings,
|
||||
);
|
||||
@@ -69,8 +70,8 @@ class _ProxiesViewState extends ConsumerState<ProxiesView> {
|
||||
onPressed: () {
|
||||
showExtend(
|
||||
context,
|
||||
builder: (_) {
|
||||
return ProvidersView();
|
||||
builder: (_, type) {
|
||||
return ProvidersView(type: type);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:math';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -83,8 +82,9 @@ class ProxiesTabViewState extends ConsumerState<ProxiesTabView>
|
||||
showSheet(
|
||||
context: context,
|
||||
props: SheetProps(isScrollControlled: false),
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Consumer(
|
||||
|
||||
@@ -44,7 +44,7 @@ class FloatingActionButtonExtendedBuilder extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isExtended =
|
||||
CommonScaffoldFabExtendedProvider.of(context)?.isExtended ?? true;
|
||||
CommonScaffoldFabExtendedProvider.of(context)?.isExtended ?? false;
|
||||
return builder(isExtended);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class CommonFloatingActionButton extends StatelessWidget {
|
||||
curve: Curves.easeOutBack,
|
||||
child: AnimatedOpacity(
|
||||
duration: midDuration,
|
||||
opacity: isExtended ? 1.0 : 0.4,
|
||||
opacity: isExtended ? 1.0 : 0.0,
|
||||
curve: Curves.linear,
|
||||
child: isExtended
|
||||
? Padding(
|
||||
|
||||
@@ -186,7 +186,7 @@ class CommonCard extends StatelessWidget {
|
||||
onLongPress: onLongPress,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(padding ?? EdgeInsets.zero),
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(radius ?? 14),
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ExternalDismissibleEffect { normal, resize }
|
||||
|
||||
class ExternalDismissible extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onDismissed;
|
||||
final bool dismiss;
|
||||
final ExternalDismissibleEffect effect;
|
||||
|
||||
const ExternalDismissible({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.dismiss,
|
||||
this.onDismissed,
|
||||
this.effect = ExternalDismissibleEffect.normal,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ExternalDismissible> createState() => _ExternalDismissibleState();
|
||||
}
|
||||
|
||||
class _ExternalDismissibleState extends State<ExternalDismissible>
|
||||
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
late final AnimationController _controller;
|
||||
Animation<Offset>? _slideAnimation;
|
||||
Animation<double>? _fadeAnimation;
|
||||
late Animation<double> _resizeAnimation;
|
||||
|
||||
bool _isDismissing = false;
|
||||
|
||||
bool get _isNormal => widget.effect == ExternalDismissibleEffect.normal;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 400),
|
||||
);
|
||||
_initAnimations();
|
||||
if (widget.dismiss) {
|
||||
_dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
void _initAnimations() {
|
||||
const curve = Curves.fastOutSlowIn;
|
||||
|
||||
if (_isNormal) {
|
||||
_slideAnimation =
|
||||
Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(1.0, 0.0),
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(0.0, 1.0, curve: curve),
|
||||
),
|
||||
);
|
||||
|
||||
_resizeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(0.3, 1.0, curve: curve),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_fadeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(0.0, 0.6, curve: curve),
|
||||
),
|
||||
);
|
||||
|
||||
_resizeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(0.2, 1.0, curve: curve),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => _isDismissing;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ExternalDismissible oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (!oldWidget.dismiss && widget.dismiss) {
|
||||
_dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _dismiss() async {
|
||||
if (_isDismissing) return;
|
||||
if (!mounted) return;
|
||||
_isDismissing = true;
|
||||
updateKeepAlive();
|
||||
await _controller.forward();
|
||||
if (mounted && widget.onDismissed != null) {
|
||||
widget.onDismissed!();
|
||||
}
|
||||
_isDismissing = false;
|
||||
updateKeepAlive();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
||||
Widget content = widget.child;
|
||||
|
||||
if (_slideAnimation != null) {
|
||||
content = SlideTransition(position: _slideAnimation!, child: content);
|
||||
}
|
||||
|
||||
if (_fadeAnimation != null) {
|
||||
content = FadeTransition(opacity: _fadeAnimation!, child: content);
|
||||
}
|
||||
|
||||
return SizeTransition(
|
||||
axisAlignment: 0.5,
|
||||
sizeFactor: _resizeAnimation,
|
||||
axis: Axis.vertical,
|
||||
child: content,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/widgets/sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommonScaffoldBackActionProvider extends InheritedWidget {
|
||||
@@ -41,55 +39,3 @@ class CommonScaffoldFabExtendedProvider extends InheritedWidget {
|
||||
bool updateShouldNotify(CommonScaffoldFabExtendedProvider oldWidget) =>
|
||||
isExtended != oldWidget.isExtended;
|
||||
}
|
||||
|
||||
class ItemPositionProvider extends InheritedWidget {
|
||||
final ItemPosition position;
|
||||
|
||||
const ItemPositionProvider({
|
||||
super.key,
|
||||
required this.position,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static ItemPositionProvider? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ItemPositionProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ItemPositionProvider oldWidget) =>
|
||||
position != oldWidget.position;
|
||||
}
|
||||
|
||||
class SheetProvider extends InheritedWidget {
|
||||
final SheetType type;
|
||||
final VoidCallback? nestedNavigatorPopCallback;
|
||||
|
||||
const SheetProvider({
|
||||
super.key,
|
||||
required super.child,
|
||||
required this.type,
|
||||
this.nestedNavigatorPopCallback,
|
||||
});
|
||||
|
||||
SheetProvider copyWith({
|
||||
SheetType? type,
|
||||
VoidCallback? nestedNavigatorPopCallback,
|
||||
required Widget child,
|
||||
}) {
|
||||
return SheetProvider(
|
||||
type: type ?? this.type,
|
||||
nestedNavigatorPopCallback:
|
||||
nestedNavigatorPopCallback ?? this.nestedNavigatorPopCallback,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
static SheetProvider? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<SheetProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(SheetProvider oldWidget) =>
|
||||
type != oldWidget.type &&
|
||||
nestedNavigatorPopCallback != oldWidget.nestedNavigatorPopCallback;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:fl_clash/widgets/null_status.dart';
|
||||
import 'package:fl_clash/widgets/pop_scope.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
@@ -247,7 +245,7 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
}
|
||||
|
||||
void _handleSelected(String value) {
|
||||
ref.read(itemsProvider(_key).notifier).update((state) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((state) {
|
||||
final newState = Set<String>.from(state)..addOrRemove(value);
|
||||
return newState;
|
||||
});
|
||||
@@ -255,7 +253,7 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
|
||||
void _handleSelectAll() {
|
||||
final ids = _items.toSet();
|
||||
ref.read(itemsProvider(_key).notifier).update((selected) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
@@ -298,12 +296,12 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
}
|
||||
|
||||
void _handleDelete() {
|
||||
final selectedItems = ref.read(itemsProvider(_key));
|
||||
final selectedItems = ref.read(selectedItemsProvider(_key));
|
||||
final newItems = _items
|
||||
.where((item) => !selectedItems.contains(item))
|
||||
.toList();
|
||||
_items = newItems;
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -321,46 +319,46 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
Widget _buildItem({
|
||||
required String value,
|
||||
required int index,
|
||||
required int length,
|
||||
required int totalLength,
|
||||
required bool isSelected,
|
||||
required bool isEditing,
|
||||
isDecorator = false,
|
||||
}) {
|
||||
final position = ItemPosition.get(index, length);
|
||||
final isFirst = index == 0;
|
||||
final isLast = index == totalLength - 1;
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey(value),
|
||||
index: index,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: SelectedDecorationListItem(
|
||||
isDecorator: isDecorator,
|
||||
title: widget.titleBuilder(value),
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
),
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
isLast: isLast,
|
||||
isFirst: isFirst,
|
||||
title: widget.titleBuilder(value),
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedItems = ref.watch(itemsProvider(_key));
|
||||
final selectedItems = ref.watch(selectedItemsProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedItems.isNotEmpty) {
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop(_items);
|
||||
@@ -417,7 +415,7 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
return _buildItem(
|
||||
value: value,
|
||||
index: index,
|
||||
length: _items.length,
|
||||
totalLength: _items.length,
|
||||
isSelected: selectedItems.contains(value),
|
||||
isEditing: selectedItems.isNotEmpty,
|
||||
);
|
||||
@@ -428,7 +426,7 @@ class _ListInputPageState extends ConsumerState<ListInputPage> {
|
||||
_buildItem(
|
||||
value: value,
|
||||
index: index,
|
||||
length: _items.length,
|
||||
totalLength: _items.length,
|
||||
isDecorator: true,
|
||||
isSelected: selectedItems.contains(value),
|
||||
isEditing: selectedItems.isNotEmpty,
|
||||
@@ -492,7 +490,7 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
}
|
||||
|
||||
void _handleSelected(MapEntry<String, String> value) {
|
||||
ref.read(itemsProvider(_key).notifier).update((state) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((state) {
|
||||
final newState = Set<String>.from(state)..addOrRemove(value.key);
|
||||
return newState;
|
||||
});
|
||||
@@ -500,7 +498,7 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
|
||||
void _handleSelectAll() {
|
||||
final ids = _items.map((item) => item.key).toSet();
|
||||
ref.read(itemsProvider(_key).notifier).update((selected) {
|
||||
ref.read(selectedItemsProvider(_key).notifier).update((selected) {
|
||||
return selected.containsAll(ids) ? {} : ids;
|
||||
});
|
||||
}
|
||||
@@ -551,12 +549,12 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
}
|
||||
|
||||
void _handleDelete() {
|
||||
final selectedItems = ref.read(itemsProvider(_key));
|
||||
final selectedItems = ref.read(selectedItemsProvider(_key));
|
||||
final newItems = _items
|
||||
.where((item) => !selectedItems.contains(item.key))
|
||||
.toList();
|
||||
_items = newItems;
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -574,46 +572,46 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
Widget _buildItem({
|
||||
required MapEntry<String, String> value,
|
||||
required int index,
|
||||
required int length,
|
||||
required int totalLength,
|
||||
required bool isSelected,
|
||||
required bool isEditing,
|
||||
isDecorator = false,
|
||||
}) {
|
||||
final position = ItemPosition.get(index, length);
|
||||
final isFirst = index == 0;
|
||||
final isLast = index == totalLength - 1;
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey(value),
|
||||
index: index,
|
||||
child: ItemPositionProvider(
|
||||
position: position,
|
||||
child: SelectedDecorationListItem(
|
||||
isDecorator: isDecorator,
|
||||
title: widget.titleBuilder(value),
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
),
|
||||
child: CommonSelectedInputListItem(
|
||||
isDecorator: isDecorator,
|
||||
isLast: isLast,
|
||||
isFirst: isFirst,
|
||||
title: widget.titleBuilder(value),
|
||||
leading: widget.leadingBuilder != null
|
||||
? widget.leadingBuilder!(value)
|
||||
: null,
|
||||
subtitle: widget.subtitleBuilder != null
|
||||
? widget.subtitleBuilder!(value)
|
||||
: null,
|
||||
isSelected: isSelected,
|
||||
isEditing: isEditing,
|
||||
onSelected: () {
|
||||
_handleSelected(value);
|
||||
},
|
||||
onPressed: () {
|
||||
_handleAddOrEdit(value);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedItems = ref.watch(itemsProvider(_key));
|
||||
final selectedItems = ref.watch(selectedItemsProvider(_key));
|
||||
return CommonPopScope(
|
||||
onPop: (_) {
|
||||
if (selectedItems.isNotEmpty) {
|
||||
ref.read(itemsProvider(_key).notifier).value = {};
|
||||
ref.read(selectedItemsProvider(_key).notifier).value = {};
|
||||
return false;
|
||||
}
|
||||
Navigator.of(context).pop(Map<String, String>.fromEntries(_items));
|
||||
@@ -673,7 +671,7 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
return _buildItem(
|
||||
value: value,
|
||||
index: index,
|
||||
length: _items.length,
|
||||
totalLength: _items.length,
|
||||
isSelected: selectedItems.contains(value.key),
|
||||
isEditing: selectedItems.isNotEmpty,
|
||||
);
|
||||
@@ -684,7 +682,7 @@ class _MapInputPageState extends ConsumerState<MapInputPage> {
|
||||
_buildItem(
|
||||
value: value,
|
||||
index: index,
|
||||
length: _items.length,
|
||||
totalLength: _items.length,
|
||||
isDecorator: true,
|
||||
isSelected: selectedItems.contains(value.key),
|
||||
isEditing: selectedItems.isNotEmpty,
|
||||
@@ -820,52 +818,3 @@ class _AddDialogState extends State<AddDialog> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NoInputBorder extends InputBorder {
|
||||
const NoInputBorder() : super(borderSide: BorderSide.none);
|
||||
|
||||
@override
|
||||
NoInputBorder copyWith({BorderSide? borderSide}) => const NoInputBorder();
|
||||
|
||||
@override
|
||||
bool get isOutline => false;
|
||||
|
||||
@override
|
||||
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
|
||||
|
||||
@override
|
||||
NoInputBorder scale(double t) => const NoInputBorder();
|
||||
|
||||
@override
|
||||
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
|
||||
return Path()..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
|
||||
return Path()..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(
|
||||
Canvas canvas,
|
||||
Rect rect,
|
||||
Paint paint, {
|
||||
TextDirection? textDirection,
|
||||
}) {
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(
|
||||
Canvas canvas,
|
||||
Rect rect, {
|
||||
double? gapStart,
|
||||
double gapExtent = 0.0,
|
||||
double gapPercentage = 0.0,
|
||||
TextDirection? textDirection,
|
||||
}) {}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -314,7 +312,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
maxWidth: openDelegate.maxWidth,
|
||||
forceFull: openDelegate.forceFull,
|
||||
),
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return child;
|
||||
},
|
||||
);
|
||||
@@ -346,7 +344,7 @@ class ListItem<T> extends StatelessWidget {
|
||||
blur: nextDelegate.blur,
|
||||
maxWidth: nextDelegate.maxWidth,
|
||||
),
|
||||
builder: (_) {
|
||||
builder: (_, type) {
|
||||
return child;
|
||||
},
|
||||
);
|
||||
@@ -542,27 +540,6 @@ Widget generateSectionV2({
|
||||
);
|
||||
}
|
||||
|
||||
Widget generateSectionV3({
|
||||
String? title,
|
||||
required Iterable<Widget> items,
|
||||
List<Widget>? actions,
|
||||
}) {
|
||||
final genItems = items.mapIndexed<Widget>((index, item) {
|
||||
final position = ItemPosition.get(index, items.length);
|
||||
if (position != ItemPosition.middle) {
|
||||
return ItemPositionProvider(position: position, child: item);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return Column(
|
||||
children: [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(title: title, actions: actions),
|
||||
Column(children: [...genItems]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> generateInfoSection({
|
||||
required Info info,
|
||||
required Iterable<Widget> items,
|
||||
@@ -610,6 +587,7 @@ class CommonSelectedListItem extends StatelessWidget {
|
||||
margin: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
color: Colors.transparent,
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
isSelected: isSelected,
|
||||
@@ -643,105 +621,92 @@ class CommonSelectedListItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DecorationListItem extends StatelessWidget {
|
||||
class CommonInputListItem extends StatelessWidget {
|
||||
final bool isDecorator;
|
||||
final Widget title;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final Widget? title;
|
||||
final Widget? subtitle;
|
||||
final Widget? leading;
|
||||
final Widget? trailing;
|
||||
final bool? isSelected;
|
||||
final VoidCallback? onPressed;
|
||||
final double minVerticalPadding;
|
||||
|
||||
const DecorationListItem({
|
||||
const CommonInputListItem({
|
||||
super.key,
|
||||
this.isDecorator = false,
|
||||
required this.title,
|
||||
this.isFirst = false,
|
||||
this.isLast = false,
|
||||
this.title,
|
||||
this.leading,
|
||||
this.trailing,
|
||||
this.subtitle,
|
||||
this.isSelected,
|
||||
this.onPressed,
|
||||
this.minVerticalPadding = 6,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final position = ItemPositionProvider.of(context)?.position;
|
||||
final isStart = [
|
||||
ItemPosition.start,
|
||||
ItemPosition.startAndEnd,
|
||||
].contains(position);
|
||||
final isEnd = [
|
||||
ItemPosition.end,
|
||||
ItemPosition.startAndEnd,
|
||||
].contains(position);
|
||||
return Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: ShapeDecoration(
|
||||
shape: isDecorator == true
|
||||
? LinearBorder.none
|
||||
: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: isStart ? Radius.circular(24) : Radius.zero,
|
||||
bottom: isEnd ? Radius.circular(24) : Radius.zero,
|
||||
top: isFirst ? Radius.circular(24) : Radius.zero,
|
||||
bottom: isLast ? Radius.circular(24) : Radius.zero,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: CommonCard(
|
||||
radius: 0,
|
||||
isSelected: isSelected,
|
||||
padding: EdgeInsets.zero,
|
||||
type: CommonCardType.filled,
|
||||
onPressed: onPressed,
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
final isInfinite = constraints.maxHeight >= double.infinity;
|
||||
final tile = ListTile(
|
||||
leading: leading,
|
||||
contentPadding: const EdgeInsets.only(right: 16, left: 16),
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minTileHeight: 54,
|
||||
trailing: trailing,
|
||||
);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
fit: isInfinite ? FlexFit.loose : FlexFit.tight,
|
||||
child: tile,
|
||||
),
|
||||
if (isDecorator != true && !isEnd)
|
||||
Divider(height: 0, indent: 14, endIndent: 14),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
leading: leading,
|
||||
contentPadding: const EdgeInsets.only(right: 16, left: 16),
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
minVerticalPadding: 14,
|
||||
trailing: trailing,
|
||||
),
|
||||
),
|
||||
if (isDecorator != true && !isLast)
|
||||
Divider(height: 0, indent: 14, endIndent: 14),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SelectedDecorationListItem extends StatelessWidget {
|
||||
class CommonSelectedInputListItem extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final bool isEditing;
|
||||
final Widget title;
|
||||
final Widget? subtitle;
|
||||
final VoidCallback onSelected;
|
||||
final VoidCallback onPressed;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final bool isDecorator;
|
||||
final Widget? leading;
|
||||
|
||||
const SelectedDecorationListItem({
|
||||
const CommonSelectedInputListItem({
|
||||
super.key,
|
||||
required this.isSelected,
|
||||
required this.onSelected,
|
||||
this.isEditing = false,
|
||||
required this.title,
|
||||
required this.onPressed,
|
||||
this.isFirst = false,
|
||||
this.isLast = false,
|
||||
this.isDecorator = false,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
@@ -749,10 +714,12 @@ class SelectedDecorationListItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecorationListItem(
|
||||
return CommonInputListItem(
|
||||
title: title,
|
||||
isDecorator: isDecorator,
|
||||
isSelected: isSelected,
|
||||
isFirst: isFirst,
|
||||
isLast: isLast,
|
||||
leading: leading,
|
||||
onPressed: isDecorator
|
||||
? null
|
||||
|
||||
@@ -50,7 +50,7 @@ class CommonScaffold extends StatefulWidget {
|
||||
class CommonScaffoldState extends State<CommonScaffold> {
|
||||
late final ValueNotifier<AppBarState> _appBarState;
|
||||
final ValueNotifier<bool> _loadingNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _isFabExtendedNotifier = ValueNotifier(true);
|
||||
final ValueNotifier<bool> _isFabExtendedNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);
|
||||
final _textController = TextEditingController();
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/controller.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/widgets/inherited.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'scaffold.dart';
|
||||
@@ -16,13 +11,11 @@ class SheetProps {
|
||||
final double? maxHeight;
|
||||
final bool isScrollControlled;
|
||||
final bool useSafeArea;
|
||||
final Color? backgroundColor;
|
||||
final bool blur;
|
||||
|
||||
const SheetProps({
|
||||
this.maxWidth,
|
||||
this.maxHeight,
|
||||
this.backgroundColor,
|
||||
this.useSafeArea = true,
|
||||
this.isScrollControlled = false,
|
||||
this.blur = true,
|
||||
@@ -46,9 +39,11 @@ class ExtendProps {
|
||||
|
||||
enum SheetType { page, bottomSheet, sideSheet }
|
||||
|
||||
typedef SheetBuilder = Widget Function(BuildContext context, SheetType type);
|
||||
|
||||
Future<T?> showSheet<T>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
required SheetBuilder builder,
|
||||
SheetProps props = const SheetProps(),
|
||||
}) {
|
||||
final isMobile = appController.isMobile;
|
||||
@@ -57,12 +52,8 @@ Future<T?> showSheet<T>({
|
||||
context: context,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
builder: (_) {
|
||||
return SheetProvider(
|
||||
type: SheetType.bottomSheet,
|
||||
child: builder(context),
|
||||
);
|
||||
return builder(context, SheetType.bottomSheet);
|
||||
},
|
||||
backgroundColor: props.backgroundColor,
|
||||
showDragHandle: false,
|
||||
useSafeArea: props.useSafeArea,
|
||||
),
|
||||
@@ -70,14 +61,10 @@ Future<T?> showSheet<T>({
|
||||
useSafeArea: props.useSafeArea,
|
||||
isScrollControlled: props.isScrollControlled,
|
||||
context: context,
|
||||
backgroundColor: props.backgroundColor,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (_) {
|
||||
return SheetProvider(
|
||||
type: SheetType.sideSheet,
|
||||
child: builder(context),
|
||||
);
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
@@ -85,41 +72,37 @@ Future<T?> showSheet<T>({
|
||||
|
||||
Future<T?> showExtend<T>(
|
||||
BuildContext context, {
|
||||
required WidgetBuilder builder,
|
||||
required SheetBuilder builder,
|
||||
ExtendProps props = const ExtendProps(),
|
||||
}) {
|
||||
final isMobile = appController.isMobile;
|
||||
return switch (isMobile || props.forceFull) {
|
||||
true => BaseNavigator.push(
|
||||
context,
|
||||
SheetProvider(type: SheetType.page, child: builder(context)),
|
||||
),
|
||||
true => BaseNavigator.push(context, builder(context, SheetType.page)),
|
||||
false => showModalSideSheet<T>(
|
||||
useSafeArea: props.useSafeArea,
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),
|
||||
filter: props.blur ? commonFilter : null,
|
||||
builder: (context) {
|
||||
return SheetProvider(
|
||||
type: SheetType.sideSheet,
|
||||
child: builder(context),
|
||||
);
|
||||
return builder(context, SheetType.sideSheet);
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
class AdaptiveSheetScaffold extends StatefulWidget {
|
||||
final SheetType type;
|
||||
final Widget body;
|
||||
final String title;
|
||||
final bool sheetTransparentToolBar;
|
||||
final List<IconButtonData> actions;
|
||||
final bool? centerTitle;
|
||||
final List<Widget> actions;
|
||||
|
||||
const AdaptiveSheetScaffold({
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.body,
|
||||
required this.title,
|
||||
this.sheetTransparentToolBar = false,
|
||||
this.centerTitle,
|
||||
this.actions = const [],
|
||||
});
|
||||
|
||||
@@ -128,113 +111,34 @@ class AdaptiveSheetScaffold extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
final _isScrolledController = ValueNotifier<bool>(false);
|
||||
|
||||
IconData get backIconData {
|
||||
if (kIsWeb) {
|
||||
return Icons.arrow_back;
|
||||
}
|
||||
switch (Theme.of(context).platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
return Icons.arrow_back;
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
return Icons.arrow_back_ios_new_rounded;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isScrolledController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sheetProvider = SheetProvider.of(context);
|
||||
final nestedNavigatorPopCallback =
|
||||
sheetProvider?.nestedNavigatorPopCallback;
|
||||
final ModalRoute<dynamic>? route = ModalRoute.of(context);
|
||||
final type = sheetProvider?.type ?? SheetType.page;
|
||||
final backgroundColor = type == SheetType.bottomSheet
|
||||
? context.colorScheme.surfaceContainerLow
|
||||
: context.colorScheme.surface;
|
||||
final useCloseIcon =
|
||||
type != SheetType.page &&
|
||||
(nestedNavigatorPopCallback != null &&
|
||||
route?.impliesAppBarDismissal == false ||
|
||||
nestedNavigatorPopCallback == null);
|
||||
Widget buildIconButton(IconButtonData data) {
|
||||
if (type == SheetType.bottomSheet) {
|
||||
return IconButton.filledTonal(
|
||||
onPressed: data.onPressed,
|
||||
style: IconButton.styleFrom(
|
||||
visualDensity: VisualDensity.standard,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
icon: Icon(data.icon),
|
||||
);
|
||||
}
|
||||
return IconButton(
|
||||
onPressed: data.onPressed,
|
||||
style: IconButton.styleFrom(
|
||||
visualDensity: VisualDensity.standard,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
icon: Icon(data.icon),
|
||||
);
|
||||
}
|
||||
|
||||
final actions = widget.actions.map(buildIconButton).toList();
|
||||
|
||||
final popButton = type != SheetType.page
|
||||
? (useCloseIcon
|
||||
? buildIconButton(
|
||||
IconButtonData(
|
||||
icon: Icons.close,
|
||||
onPressed: () {
|
||||
if (nestedNavigatorPopCallback != null) {
|
||||
nestedNavigatorPopCallback();
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
: buildIconButton(
|
||||
IconButtonData(
|
||||
icon: backIconData,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
))
|
||||
: null;
|
||||
|
||||
final suffixPop = type != SheetType.page && actions.isEmpty && useCloseIcon;
|
||||
final backgroundColor = context.colorScheme.surface;
|
||||
final bottomSheet = widget.type == SheetType.bottomSheet;
|
||||
final sideSheet = widget.type == SheetType.sideSheet;
|
||||
final appBar = AppBar(
|
||||
forceMaterialTransparency: bottomSheet ? true : false,
|
||||
automaticallyImplyLeading: bottomSheet
|
||||
? false
|
||||
: widget.actions.isEmpty && sideSheet
|
||||
? false
|
||||
: true,
|
||||
centerTitle:
|
||||
widget.centerTitle ?? (bottomSheet && widget.actions.isEmpty),
|
||||
backgroundColor: backgroundColor,
|
||||
forceMaterialTransparency: type == SheetType.bottomSheet ? true : false,
|
||||
leading: suffixPop ? null : popButton,
|
||||
automaticallyImplyLeading: type == SheetType.page ? true : false,
|
||||
centerTitle: true,
|
||||
toolbarHeight: type == SheetType.bottomSheet ? 48 : null,
|
||||
title: Text(widget.title),
|
||||
titleTextStyle: type == SheetType.bottomSheet
|
||||
? context.textTheme.titleLarge?.adjustSize(-4)
|
||||
: null,
|
||||
actions: !suffixPop ? genActions(actions) : genActions([?popButton]),
|
||||
actions: genActions([
|
||||
if (widget.actions.isEmpty && sideSheet) CloseButton(),
|
||||
...widget.actions,
|
||||
]),
|
||||
);
|
||||
if (type == SheetType.bottomSheet) {
|
||||
final handleSize = Size(28, 4);
|
||||
final sheetAppBar = Column(
|
||||
if (bottomSheet) {
|
||||
final handleSize = Size(32, 4);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 6),
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: handleSize.height,
|
||||
@@ -247,71 +151,11 @@ class _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: appBar),
|
||||
SizedBox(height: 6),
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: appBar),
|
||||
Flexible(flex: 1, child: widget.body),
|
||||
SizedBox(height: MediaQuery.of(context).viewPadding.bottom),
|
||||
],
|
||||
);
|
||||
return ScrollConfiguration(
|
||||
behavior: HiddenBarScrollBehavior(),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!widget.sheetTransparentToolBar) ...[
|
||||
sheetAppBar,
|
||||
Flexible(child: widget.body),
|
||||
] else ...[
|
||||
Flexible(
|
||||
child: Stack(
|
||||
children: [
|
||||
NotificationListener<ScrollNotification>(
|
||||
child: widget.body,
|
||||
onNotification: (notification) {
|
||||
if (notification is ScrollUpdateNotification) {
|
||||
final pixels = notification.metrics.pixels;
|
||||
_isScrolledController.value = pixels > 6;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _isScrolledController,
|
||||
builder: (_, isScrolled, child) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(28),
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: 12.0,
|
||||
sigmaY: 12.0,
|
||||
),
|
||||
child: ColoredBox(
|
||||
color: isScrolled
|
||||
? backgroundColor.opacity60
|
||||
: backgroundColor,
|
||||
child: child!,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: sheetAppBar,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: MediaQuery.of(context).viewPadding.bottom),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return CommonScaffold(
|
||||
appBar: appBar,
|
||||
|
||||
@@ -113,7 +113,6 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
children = widget.children;
|
||||
_childrenNotifier.addListener(() {
|
||||
children = _childrenNotifier.value;
|
||||
if (widget.onUpdate != null) {
|
||||
@@ -231,7 +230,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1,
|
||||
).chain(CurveTween(curve: Curves.fastOutSlowIn)),
|
||||
).chain(CurveTween(curve: Easing.emphasizedAccelerate)),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -505,7 +504,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
|
||||
Widget _builderItem(int index) {
|
||||
final girdItem = _childrenNotifier.value[index];
|
||||
final child = girdItem.child;
|
||||
final child = RepaintBoundary(child: girdItem.child);
|
||||
return GridItem(
|
||||
mainAxisCellCount: girdItem.mainAxisCellCount,
|
||||
crossAxisCellCount: girdItem.crossAxisCellCount,
|
||||
|
||||
@@ -7,18 +7,20 @@ import '../state.dart';
|
||||
class TooltipText extends StatelessWidget {
|
||||
final Text text;
|
||||
|
||||
const TooltipText({super.key, required this.text});
|
||||
const TooltipText({
|
||||
super.key,
|
||||
required this.text,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final isOverflow = globalState.measure.computeTextIsOverflow(
|
||||
builder: (context, container) {
|
||||
final maxWidth = container.maxWidth;
|
||||
final size = globalState.measure.computeTextSize(
|
||||
text,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
if (isOverflow) {
|
||||
if (maxWidth < size.width) {
|
||||
return Tooltip(
|
||||
triggerMode: TooltipTriggerMode.longPress,
|
||||
preferBelow: false,
|
||||
@@ -55,21 +57,26 @@ class EmojiText extends StatelessWidget {
|
||||
if (match.start > lastMatchEnd) {
|
||||
spans.add(
|
||||
TextSpan(
|
||||
text: text.substring(lastMatchEnd, match.start),
|
||||
style: style,
|
||||
),
|
||||
text: text.substring(lastMatchEnd, match.start), style: style),
|
||||
);
|
||||
}
|
||||
spans.add(
|
||||
TextSpan(
|
||||
text: match.group(0),
|
||||
style: style?.copyWith(fontFamily: FontFamily.twEmoji.value),
|
||||
style: style?.copyWith(
|
||||
fontFamily: FontFamily.twEmoji.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
lastMatchEnd = match.end;
|
||||
}
|
||||
if (lastMatchEnd < text.length) {
|
||||
spans.add(TextSpan(text: text.substring(lastMatchEnd), style: style));
|
||||
spans.add(
|
||||
TextSpan(
|
||||
text: text.substring(lastMatchEnd),
|
||||
style: style,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return spans;
|
||||
@@ -81,7 +88,9 @@ class EmojiText extends StatelessWidget {
|
||||
textScaler: MediaQuery.of(context).textScaler,
|
||||
maxLines: maxLines,
|
||||
overflow: overflow ?? TextOverflow.clip,
|
||||
text: TextSpan(children: _buildTextSpans(text)),
|
||||
text: TextSpan(
|
||||
children: _buildTextSpans(text),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ export 'color_scheme_box.dart';
|
||||
export 'container.dart';
|
||||
export 'dialog.dart';
|
||||
export 'disabled_mask.dart';
|
||||
export 'dismissible.dart';
|
||||
export 'donut_chart.dart';
|
||||
export 'effect.dart';
|
||||
export 'fade_box.dart';
|
||||
|
||||
26
pubspec.lock
26
pubspec.lock
@@ -918,14 +918,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
navigator_resizable:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: navigator_resizable
|
||||
sha256: "6df8fcf5ef4267dd8b1cda35b220427a4f4864c659519125a40365c8d3027697"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
nm:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1117,14 +1109,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
reorderable_grid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: reorderable_grid
|
||||
sha256: "15873d8afa6c0a106f0e165f4fbb2bb92dbfe18837e0c9f5d62ac4e26448863d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.13"
|
||||
riverpod:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1314,14 +1298,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
smooth_sheets:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: smooth_sheets
|
||||
sha256: fe1c9e4f1cd20c841cc2155cb125da74c004baf7bcfdf0bef4f87a7183d0907b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.0"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1779,4 +1755,4 @@ packages:
|
||||
version: "2.1.0"
|
||||
sdks:
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.35.1"
|
||||
flutter: ">=3.35.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.92+2026020201
|
||||
version: 0.8.92+2026012603
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
@@ -69,8 +69,6 @@ dependencies:
|
||||
drift: ^2.29.0
|
||||
drift_flutter: ^0.2.7
|
||||
fractional_indexing: ^1.0.0
|
||||
reorderable_grid: ^1.0.13
|
||||
smooth_sheets: ^0.17.0
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Reference in New Issue
Block a user