Compare commits
1 Commits
v0.8.83-pr
...
v0.8.83-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cb5d40969 |
1
.github/workflows/build.yaml
vendored
1
.github/workflows/build.yaml
vendored
@@ -201,6 +201,7 @@ jobs:
|
||||
env:
|
||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
@@ -391,5 +391,6 @@
|
||||
"messageTest": "Message test",
|
||||
"messageTestTip": "This is a message.",
|
||||
"crashTest": "Crash test",
|
||||
"clearData": "Clear Data"
|
||||
"clearData": "Clear Data",
|
||||
"textScale": "Text Scaling"
|
||||
}
|
||||
@@ -391,5 +391,7 @@
|
||||
"messageTest": "メッセージテスト",
|
||||
"messageTestTip": "これはメッセージです。",
|
||||
"crashTest": "クラッシュテスト",
|
||||
"clearData": "データを消去"
|
||||
"clearData": "データを消去",
|
||||
"zoom": "ズーム",
|
||||
"textScale": "テキストスケーリング"
|
||||
}
|
||||
@@ -391,5 +391,7 @@
|
||||
"messageTest": "Тестирование сообщения",
|
||||
"messageTestTip": "Это сообщение.",
|
||||
"crashTest": "Тест на сбои",
|
||||
"clearData": "Очистить данные"
|
||||
"clearData": "Очистить данные",
|
||||
"zoom": "Масштаб",
|
||||
"textScale": "Масштабирование текста"
|
||||
}
|
||||
@@ -391,5 +391,7 @@
|
||||
"messageTest": "消息测试",
|
||||
"messageTestTip": "这是一条消息。",
|
||||
"crashTest": "崩溃测试",
|
||||
"clearData": "清除数据"
|
||||
"clearData": "清除数据",
|
||||
"zoom": "缩放",
|
||||
"textScale": "文本缩放"
|
||||
}
|
||||
@@ -17,15 +17,12 @@ const packageName = "com.follow.clash";
|
||||
final unixSocketPath = "/tmp/FlClashSocket_${Random().nextInt(10000)}.sock";
|
||||
const helperPort = 47890;
|
||||
const helperTag = "2024125";
|
||||
const baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16.ap,
|
||||
horizontal: 16.ap,
|
||||
);
|
||||
|
||||
double textScaleFactor = min(
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
|
||||
1.2,
|
||||
);
|
||||
final defaultTextScaleFactor = WidgetsBinding.instance.platformDispatcher.textScaleFactor;
|
||||
const httpTimeoutDuration = Duration(milliseconds: 5000);
|
||||
const moreDuration = Duration(milliseconds: 100);
|
||||
const animateDuration = Duration(milliseconds: 100);
|
||||
@@ -44,7 +41,6 @@ const profilesDirectoryName = "profiles";
|
||||
const localhost = "127.0.0.1";
|
||||
const clashConfigKey = "clash_config";
|
||||
const configKey = "config";
|
||||
const listItemPadding = EdgeInsets.symmetric(horizontal: 16);
|
||||
const double dialogCommonWidth = 300;
|
||||
const repository = "chen08209/FlClash";
|
||||
const defaultExternalController = "127.0.0.1:9090";
|
||||
@@ -81,7 +77,7 @@ const viewModeColumnsMap = {
|
||||
const defaultPrimaryColor = 0xFF795548;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
return max(lines * 84 * textScaleFactor + (lines - 1) * 16, 0);
|
||||
return max(lines * 84 + (lines - 1) * 16, 0).ap;
|
||||
}
|
||||
|
||||
final mainIsolate = "FlClashMainIsolate";
|
||||
|
||||
@@ -3,11 +3,13 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Measure {
|
||||
final TextScaler _textScale;
|
||||
final TextScaler _textScaler;
|
||||
final BuildContext context;
|
||||
final Map<String, dynamic> _measureMap;
|
||||
|
||||
Measure.of(this.context)
|
||||
: _textScale = TextScaler.linear(
|
||||
Measure.of(this.context, double textScaleFactor)
|
||||
: _measureMap = {},
|
||||
_textScaler = TextScaler.linear(
|
||||
textScaleFactor,
|
||||
);
|
||||
|
||||
@@ -21,7 +23,7 @@ class Measure {
|
||||
style: text.style,
|
||||
),
|
||||
maxLines: text.maxLines,
|
||||
textScaler: _textScale,
|
||||
textScaler: _textScaler,
|
||||
textDirection: text.textDirection ?? TextDirection.ltr,
|
||||
)..layout(
|
||||
maxWidth: maxWidth,
|
||||
@@ -29,81 +31,75 @@ class Measure {
|
||||
return textPainter.size;
|
||||
}
|
||||
|
||||
double? _bodyMediumHeight;
|
||||
Size? _bodyLargeSize;
|
||||
double? _bodySmallHeight;
|
||||
double? _labelSmallHeight;
|
||||
double? _labelMediumHeight;
|
||||
double? _titleLargeHeight;
|
||||
double? _titleMediumHeight;
|
||||
|
||||
double get bodyMediumHeight {
|
||||
_bodyMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
).height;
|
||||
return _bodyMediumHeight!;
|
||||
}
|
||||
|
||||
Size get bodyLargeSize {
|
||||
_bodyLargeSize ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
return _measureMap.getCacheValue(
|
||||
"bodyMediumHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
return _bodyLargeSize!;
|
||||
}
|
||||
|
||||
double get bodySmallHeight {
|
||||
_bodySmallHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
).height;
|
||||
return _bodySmallHeight!;
|
||||
return _measureMap.getCacheValue(
|
||||
"bodySmallHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get labelSmallHeight {
|
||||
_labelSmallHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelSmall,
|
||||
),
|
||||
).height;
|
||||
return _labelSmallHeight!;
|
||||
return _measureMap.getCacheValue(
|
||||
"labelSmallHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelSmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get labelMediumHeight {
|
||||
_labelMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelMedium,
|
||||
),
|
||||
).height;
|
||||
return _labelMediumHeight!;
|
||||
return _measureMap.getCacheValue(
|
||||
"labelMediumHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.labelMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get titleLargeHeight {
|
||||
_titleLargeHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleLarge,
|
||||
),
|
||||
).height;
|
||||
return _titleLargeHeight!;
|
||||
return _measureMap.getCacheValue(
|
||||
"titleLargeHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleLarge,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get titleMediumHeight {
|
||||
_titleMediumHeight ??= computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
).height;
|
||||
return _titleMediumHeight!;
|
||||
return _measureMap.getCacheValue(
|
||||
"titleMediumHeight",
|
||||
computeTextSize(
|
||||
Text(
|
||||
"X",
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -12,6 +13,10 @@ extension NumExt on num {
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
double get ap {
|
||||
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleExt on double {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -20,10 +19,7 @@ class CommonPrint {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.app,
|
||||
payload: payload,
|
||||
),
|
||||
Log.app(payload),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ import 'package:flutter/material.dart';
|
||||
class CommonTheme {
|
||||
final BuildContext context;
|
||||
final Map<String, Color> _colorMap;
|
||||
final double textScaleFactor;
|
||||
|
||||
CommonTheme.of(this.context) : _colorMap = {};
|
||||
CommonTheme.of(
|
||||
this.context,
|
||||
this.textScaleFactor,
|
||||
) : _colorMap = {};
|
||||
|
||||
Color get darkenSecondaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
|
||||
@@ -334,12 +334,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
_ref.read(logsProvider.notifier).addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.info,
|
||||
payload: e.toString(),
|
||||
),
|
||||
);
|
||||
commonPrint.log(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
updateRequestsThrottler();
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,72 +148,77 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
|
||||
return child!;
|
||||
},
|
||||
child: ValueListenableBuilder<ConnectionsState>(
|
||||
valueListenable: _requestsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
final connections = state.list;
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
);
|
||||
}
|
||||
final items = connections
|
||||
.map<Widget>(
|
||||
(connection) => ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ScrollToEndBox(
|
||||
controller: _scrollController,
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: connections,
|
||||
child: CommonScrollBar(
|
||||
child: TextScaleNotification(
|
||||
child: ValueListenableBuilder<ConnectionsState>(
|
||||
valueListenable: _requestsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
final connections = state.list;
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
);
|
||||
}
|
||||
final items = connections
|
||||
.map<Widget>(
|
||||
(connection) => ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ScrollToEndBox(
|
||||
controller: _scrollController,
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: connections,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
itemExtentBuilder: (index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final measure = globalState.measure;
|
||||
final bodyMediumHeight = measure.bodyMediumHeight;
|
||||
final connection = connections[(index / 2).floor()];
|
||||
final height = _calcCacheHeight(connection);
|
||||
return height + bodyMediumHeight + 32;
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final connection = connections[(index / 2).floor()];
|
||||
return connection.id;
|
||||
},
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemExtentBuilder: (index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final measure = globalState.measure;
|
||||
final bodyMediumHeight = measure.bodyMediumHeight;
|
||||
final connection = connections[(index / 2).floor()];
|
||||
final height = _calcCacheHeight(connection);
|
||||
return height + bodyMediumHeight + 32;
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final connection = connections[(index / 2).floor()];
|
||||
return connection.id;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
),
|
||||
onNotification: (_) {
|
||||
_key.currentState?.clearCache();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -103,8 +103,8 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
|
||||
child: SuperGrid(
|
||||
key: key,
|
||||
crossAxisCount: columns,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16.ap,
|
||||
mainAxisSpacing: 16.ap,
|
||||
children: [
|
||||
...dashboardState.dashboardWidgets
|
||||
.where(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -57,37 +58,43 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
onPressed: () {
|
||||
clashCore.requestGc();
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _memoryInfoStateNotifier,
|
||||
builder: (_, trafficValue, __) {
|
||||
return Padding(
|
||||
padding: baseInfoEdgeInsets.copyWith(
|
||||
bottom: 0,
|
||||
top: 12,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
trafficValue.showValue,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
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: _memoryInfoStateNotifier,
|
||||
builder: (_, trafficValue, __) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
trafficValue.showValue,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style: context.textTheme.bodyMedium?.toLight
|
||||
.adjustSize(1),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -206,7 +206,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
size: 16,
|
||||
size: 16.ap,
|
||||
Icons.info_outline,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
||||
@@ -17,56 +17,66 @@ class OutboundMode extends StatelessWidget {
|
||||
height: height,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final mode =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.mode));
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.outboundMode,
|
||||
iconData: Icons.call_split_sharp,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
for (final item in Mode.values)
|
||||
Flexible(
|
||||
child: ListItem.radio(
|
||||
dense: true,
|
||||
horizontalTitleGap: 4,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 16,
|
||||
),
|
||||
delegate: RadioDelegate(
|
||||
value: item,
|
||||
groupValue: mode,
|
||||
onChanged: (value) async {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.changeMode(value);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
final mode = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.mode,
|
||||
),
|
||||
);
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent
|
||||
),
|
||||
child: CommonCard(
|
||||
onPressed: () {},
|
||||
info: Info(
|
||||
label: appLocalizations.outboundMode,
|
||||
iconData: Icons.call_split_sharp,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (final item in Mode.values)
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: ListItem.radio(
|
||||
dense: true,
|
||||
horizontalTitleGap: 4,
|
||||
padding: EdgeInsets.only(
|
||||
left: 12.ap,
|
||||
right: 16.ap,
|
||||
),
|
||||
delegate: RadioDelegate(
|
||||
value: item,
|
||||
groupValue: mode,
|
||||
onChanged: (value) async {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.changeMode(value);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.toSoftBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -76,12 +86,21 @@ class OutboundMode extends StatelessWidget {
|
||||
class OutboundModeV2 extends StatelessWidget {
|
||||
const OutboundModeV2({super.key});
|
||||
|
||||
Color _getTextColor(BuildContext context, Mode mode) {
|
||||
return switch (mode) {
|
||||
Mode.rule => context.colorScheme.onSecondaryContainer,
|
||||
Mode.global => context.colorScheme.onPrimaryContainer,
|
||||
Mode.direct => context.colorScheme.onTertiaryContainer,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final height = getWidgetHeight(0.72);
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final mode = ref.watch(
|
||||
@@ -102,14 +121,22 @@ class OutboundModeV2 extends StatelessWidget {
|
||||
(item) => MapEntry(
|
||||
item,
|
||||
Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(),
|
||||
height: height - 16,
|
||||
child: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall
|
||||
?.adjustSize(1),
|
||||
?.adjustSize(1)
|
||||
.copyWith(
|
||||
color: _getTextColor(
|
||||
context,
|
||||
mode,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -34,19 +34,6 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
|
||||
logs: globalState.appState.logs.list,
|
||||
);
|
||||
ref.listenManual(
|
||||
logsProvider.select((state) => state.list),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
final isEquality = logListEquality.equals(prev, next);
|
||||
if (!isEquality) {
|
||||
_logs = next;
|
||||
updateLogsThrottler();
|
||||
}
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(
|
||||
isCurrentPageProvider(
|
||||
PageLabel.logs,
|
||||
@@ -60,6 +47,18 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(
|
||||
logsProvider.select((state) => state.list),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
final isEquality = logListEquality.equals(prev, next);
|
||||
if (!isEquality) {
|
||||
_logs = next;
|
||||
updateLogsThrottler();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -123,7 +122,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
final height = globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
log.payload ?? "",
|
||||
log.payload,
|
||||
style: globalState.appController.context.textTheme.bodyLarge,
|
||||
),
|
||||
maxWidth: _currentMaxWidth,
|
||||
@@ -154,8 +153,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
return LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
_handleTryClearCache(constraints.maxWidth - 40);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
return TextScaleNotification(
|
||||
child: ValueListenableBuilder<LogsState>(
|
||||
valueListenable: _logsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
@@ -168,7 +166,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
final items = logs
|
||||
.map<Widget>(
|
||||
(log) => LogItem(
|
||||
key: Key(log.dateTime.toString()),
|
||||
key: Key(log.dateTime),
|
||||
log: log,
|
||||
onClick: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
@@ -181,43 +179,49 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return ScrollToEndBox<Log>(
|
||||
controller: _scrollController,
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: logs,
|
||||
child: CommonScrollBar(
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ScrollToEndBox<Log>(
|
||||
controller: _scrollController,
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
cacheKey: _cacheKey,
|
||||
dataSource: logs,
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemExtentBuilder: (index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return _getItemHeight(log);
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return log.payload ?? "";
|
||||
},
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemExtentBuilder: (index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return 0;
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return _getItemHeight(log);
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return log.payload;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
onNotification: (_) {
|
||||
_key.currentState?.clearCache();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -242,14 +246,14 @@ class LogItem extends StatelessWidget {
|
||||
vertical: 4,
|
||||
),
|
||||
title: SelectableText(
|
||||
log.payload ?? '',
|
||||
log.payload,
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SelectableText(
|
||||
"${log.dateTime}",
|
||||
log.dateTime,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
|
||||
@@ -228,7 +228,7 @@ class _OverrideProfileState extends State<OverrideProfile> {
|
||||
message: TextSpan(
|
||||
text: appLocalizations.saveTip,
|
||||
),
|
||||
confirmText: appLocalizations.tip,
|
||||
confirmText: appLocalizations.save,
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
|
||||
@@ -39,7 +39,19 @@ class ThemeFragment extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(child: ThemeColorsBox());
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_ThemeModeItem(),
|
||||
_PrimaryColorItem(),
|
||||
_PrueBlackItem(),
|
||||
_TextScaleFactorItem(),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,29 +87,6 @@ class ItemCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeColorsBox extends ConsumerStatefulWidget {
|
||||
const ThemeColorsBox({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ThemeColorsBox> createState() => _ThemeColorsBoxState();
|
||||
}
|
||||
|
||||
class _ThemeColorsBoxState extends ConsumerState<ThemeColorsBox> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
_ThemeModeItem(),
|
||||
_PrimaryColorItem(),
|
||||
_PrueBlackItem(),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThemeModeItem extends ConsumerWidget {
|
||||
const _ThemeModeItem();
|
||||
|
||||
@@ -482,6 +471,77 @@ class _PrueBlackItem extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _TextScaleFactorItem extends ConsumerWidget {
|
||||
const _TextScaleFactorItem();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final textScale = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
(state) => state.textScale,
|
||||
),
|
||||
);
|
||||
final String process =
|
||||
"${((textScale.scale * 100) as double).round()}%";
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: ListItem.switchItem(
|
||||
leading: Icon(
|
||||
Icons.text_fields,
|
||||
),
|
||||
horizontalTitleGap: 12,
|
||||
title: Text(
|
||||
appLocalizations.textScale,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
delegate: SwitchDelegate(
|
||||
value: textScale.enable,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.textScale(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
DisabledMask(
|
||||
status: !textScale.enable,
|
||||
child: ActivateBox(
|
||||
active: textScale.enable,
|
||||
child: SliderTheme(
|
||||
data: _SliderDefaultsM3(context),
|
||||
child: Slider(
|
||||
min: 0.8,
|
||||
divisions: 8,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
max: 1.2,
|
||||
label: process,
|
||||
value: textScale.scale,
|
||||
onChanged: (value) {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.textScale(
|
||||
scale: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaletteDialog extends StatefulWidget {
|
||||
const _PaletteDialog();
|
||||
|
||||
@@ -544,3 +604,112 @@ class _PaletteDialogState extends State<_PaletteDialog> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SliderDefaultsM3 extends SliderThemeData {
|
||||
_SliderDefaultsM3(this.context) : super(trackHeight: 16.0);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
Color? get activeTrackColor => _colors.primary;
|
||||
|
||||
@override
|
||||
Color? get inactiveTrackColor => _colors.secondaryContainer;
|
||||
|
||||
@override
|
||||
Color? get secondaryActiveTrackColor => _colors.primary.withOpacity(0.54);
|
||||
|
||||
@override
|
||||
Color? get disabledActiveTrackColor => _colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get disabledInactiveTrackColor => _colors.onSurface.withOpacity(0.12);
|
||||
|
||||
@override
|
||||
Color? get disabledSecondaryActiveTrackColor =>
|
||||
_colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get activeTickMarkColor => _colors.onPrimary.withOpacity(1.0);
|
||||
|
||||
@override
|
||||
Color? get inactiveTickMarkColor =>
|
||||
_colors.onSecondaryContainer.withOpacity(1.0);
|
||||
|
||||
@override
|
||||
Color? get disabledActiveTickMarkColor => _colors.onInverseSurface;
|
||||
|
||||
@override
|
||||
Color? get disabledInactiveTickMarkColor => _colors.onSurface;
|
||||
|
||||
@override
|
||||
Color? get thumbColor => _colors.primary;
|
||||
|
||||
@override
|
||||
Color? get disabledThumbColor => _colors.onSurface.withOpacity(0.38);
|
||||
|
||||
@override
|
||||
Color? get overlayColor =>
|
||||
WidgetStateColor.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.dragged)) {
|
||||
return _colors.primary.withOpacity(0.1);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return _colors.primary.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return _colors.primary.withOpacity(0.1);
|
||||
}
|
||||
|
||||
return Colors.transparent;
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get valueIndicatorTextStyle =>
|
||||
Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||
color: _colors.onInverseSurface,
|
||||
);
|
||||
|
||||
@override
|
||||
Color? get valueIndicatorColor => _colors.inverseSurface;
|
||||
|
||||
@override
|
||||
SliderComponentShape? get valueIndicatorShape =>
|
||||
const RoundedRectSliderValueIndicatorShape();
|
||||
|
||||
@override
|
||||
SliderComponentShape? get thumbShape => const HandleThumbShape();
|
||||
|
||||
@override
|
||||
SliderTrackShape? get trackShape => const GappedSliderTrackShape();
|
||||
|
||||
@override
|
||||
SliderComponentShape? get overlayShape => const RoundSliderOverlayShape();
|
||||
|
||||
@override
|
||||
SliderTickMarkShape? get tickMarkShape =>
|
||||
const RoundSliderTickMarkShape(tickMarkRadius: 4.0 / 2);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Size?>? get thumbSize {
|
||||
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return const Size(4.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return const Size(4.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return const Size(2.0, 44.0);
|
||||
}
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return const Size(2.0, 44.0);
|
||||
}
|
||||
return const Size(4.0, 44.0);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
double? get trackGap => 6.0;
|
||||
}
|
||||
|
||||
@@ -637,6 +637,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Enabling it will allow TCP concurrency",
|
||||
),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("Test url"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("Text Scaling"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -471,6 +471,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("テキストスケーリング"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("テーマ"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("テーマカラー"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage("ダークモードの設定、色の調整"),
|
||||
|
||||
@@ -675,6 +675,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Включение позволит использовать параллелизм TCP",
|
||||
),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("Тест URL"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("Масштабирование текста"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Тема"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("Цвет темы"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -413,6 +413,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
|
||||
"textScale": MessageLookupByLibrary.simpleMessage("文本缩放"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("主题"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
|
||||
|
||||
@@ -3054,6 +3054,11 @@ class AppLocalizations {
|
||||
String get clearData {
|
||||
return Intl.message('Clear Data', name: 'clearData', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Text Scaling`
|
||||
String get textScale {
|
||||
return Intl.message('Text Scaling', name: 'textScale', desc: '', args: []);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -73,7 +73,7 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
void onLog(Log log) {
|
||||
ref.watch(logsProvider.notifier).addLog(log);
|
||||
if (log.logLevel == LogLevel.error) {
|
||||
globalState.showNotifier(log.payload ?? '');
|
||||
globalState.showNotifier(log.payload);
|
||||
}
|
||||
super.onLog(log);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'dart:math';
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/measure.dart';
|
||||
import 'package:fl_clash/common/theme.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ThemeManager extends StatelessWidget {
|
||||
class ThemeManager extends ConsumerWidget {
|
||||
final Widget child;
|
||||
|
||||
const ThemeManager({
|
||||
@@ -13,9 +16,20 @@ class ThemeManager extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
globalState.measure = Measure.of(context);
|
||||
globalState.theme = CommonTheme.of(context);
|
||||
Widget build(BuildContext context, ref) {
|
||||
final textScale = ref.read(
|
||||
themeSettingProvider.select((state) => state.textScale),
|
||||
);
|
||||
final double textScaleFactor = max(
|
||||
min(
|
||||
textScale.enable ? textScale.scale : defaultTextScaleFactor,
|
||||
1.2,
|
||||
),
|
||||
0.8,
|
||||
);
|
||||
|
||||
globalState.measure = Measure.of(context, textScaleFactor);
|
||||
globalState.theme = CommonTheme.of(context, textScaleFactor);
|
||||
final padding = MediaQuery.of(context).padding;
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
return MediaQuery(
|
||||
|
||||
@@ -84,33 +84,33 @@ extension ConnectionExt on Connection {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Log {
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel logLevel;
|
||||
@JsonKey(name: "Payload")
|
||||
String? payload;
|
||||
DateTime _dateTime;
|
||||
String _logDateTime(_) {
|
||||
return DateTime.now().toString();
|
||||
}
|
||||
|
||||
Log({
|
||||
required this.logLevel,
|
||||
this.payload,
|
||||
}) : _dateTime = DateTime.now();
|
||||
// String _logId(_) {
|
||||
// return utils.id;
|
||||
// }
|
||||
|
||||
DateTime get dateTime => _dateTime;
|
||||
@freezed
|
||||
class Log with _$Log {
|
||||
const factory Log({
|
||||
@JsonKey(name: "LogLevel") @Default(LogLevel.app) LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") @Default("") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) required String dateTime,
|
||||
}) = _Log;
|
||||
|
||||
factory Log.fromJson(Map<String, dynamic> json) {
|
||||
return _$LogFromJson(json);
|
||||
factory Log.app(
|
||||
String payload,
|
||||
) {
|
||||
return Log(
|
||||
payload: payload,
|
||||
dateTime: _logDateTime(null),
|
||||
// id: _logId(null),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LogToJson(this);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Log{logLevel: $logLevel, payload: $payload, dateTime: $dateTime}';
|
||||
}
|
||||
factory Log.fromJson(Map<String, Object?> json) => _$LogFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -127,11 +127,10 @@ extension LogsStateExt on LogsState {
|
||||
final lowQuery = query.toLowerCase();
|
||||
return logs.where(
|
||||
(log) {
|
||||
final payload = log.payload?.toLowerCase();
|
||||
final payload = log.payload.toLowerCase();
|
||||
final logLevelName = log.logLevel.name;
|
||||
return {logLevelName}.containsAll(keywords) &&
|
||||
((payload?.contains(lowQuery) ?? false) ||
|
||||
logLevelName.contains(lowQuery));
|
||||
((payload.contains(lowQuery)) || logLevelName.contains(lowQuery));
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@@ -173,6 +173,17 @@ class ProxiesStyle with _$ProxiesStyle {
|
||||
json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TextScale with _$TextScale {
|
||||
const factory TextScale({
|
||||
@Default(false) enable,
|
||||
@Default(1.0) scale,
|
||||
}) = _TextScale;
|
||||
|
||||
factory TextScale.fromJson(Map<String, Object?> json) =>
|
||||
_$TextScaleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ThemeProps with _$ThemeProps {
|
||||
const factory ThemeProps({
|
||||
@@ -181,6 +192,7 @@ class ThemeProps with _$ThemeProps {
|
||||
@Default(ThemeMode.dark) ThemeMode themeMode,
|
||||
@Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant,
|
||||
@Default(false) bool pureBlack,
|
||||
@Default(TextScale()) TextScale textScale,
|
||||
}) = _ThemeProps;
|
||||
|
||||
factory ThemeProps.fromJson(Map<String, Object?> json) =>
|
||||
|
||||
@@ -1092,6 +1092,203 @@ abstract class _Connection implements Connection {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Log _$LogFromJson(Map<String, dynamic> json) {
|
||||
return _Log.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Log {
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel get logLevel => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "Payload")
|
||||
String get payload => throw _privateConstructorUsedError;
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
String get dateTime => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Log to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LogCopyWith<Log> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $LogCopyWith<$Res> {
|
||||
factory $LogCopyWith(Log value, $Res Function(Log) then) =
|
||||
_$LogCopyWithImpl<$Res, Log>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "LogLevel") LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) String dateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LogCopyWithImpl<$Res, $Val extends Log> implements $LogCopyWith<$Res> {
|
||||
_$LogCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? logLevel = null,
|
||||
Object? payload = null,
|
||||
Object? dateTime = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
logLevel: null == logLevel
|
||||
? _value.logLevel
|
||||
: logLevel // ignore: cast_nullable_to_non_nullable
|
||||
as LogLevel,
|
||||
payload: null == payload
|
||||
? _value.payload
|
||||
: payload // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
dateTime: null == dateTime
|
||||
? _value.dateTime
|
||||
: dateTime // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LogImplCopyWith<$Res> implements $LogCopyWith<$Res> {
|
||||
factory _$$LogImplCopyWith(_$LogImpl value, $Res Function(_$LogImpl) then) =
|
||||
__$$LogImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "LogLevel") LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") String payload,
|
||||
@JsonKey(fromJson: _logDateTime) String dateTime});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LogImplCopyWithImpl<$Res> extends _$LogCopyWithImpl<$Res, _$LogImpl>
|
||||
implements _$$LogImplCopyWith<$Res> {
|
||||
__$$LogImplCopyWithImpl(_$LogImpl _value, $Res Function(_$LogImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? logLevel = null,
|
||||
Object? payload = null,
|
||||
Object? dateTime = null,
|
||||
}) {
|
||||
return _then(_$LogImpl(
|
||||
logLevel: null == logLevel
|
||||
? _value.logLevel
|
||||
: logLevel // ignore: cast_nullable_to_non_nullable
|
||||
as LogLevel,
|
||||
payload: null == payload
|
||||
? _value.payload
|
||||
: payload // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
dateTime: null == dateTime
|
||||
? _value.dateTime
|
||||
: dateTime // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$LogImpl implements _Log {
|
||||
const _$LogImpl(
|
||||
{@JsonKey(name: "LogLevel") this.logLevel = LogLevel.app,
|
||||
@JsonKey(name: "Payload") this.payload = "",
|
||||
@JsonKey(fromJson: _logDateTime) required this.dateTime});
|
||||
|
||||
factory _$LogImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$LogImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "LogLevel")
|
||||
final LogLevel logLevel;
|
||||
@override
|
||||
@JsonKey(name: "Payload")
|
||||
final String payload;
|
||||
@override
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
final String dateTime;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Log(logLevel: $logLevel, payload: $payload, dateTime: $dateTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$LogImpl &&
|
||||
(identical(other.logLevel, logLevel) ||
|
||||
other.logLevel == logLevel) &&
|
||||
(identical(other.payload, payload) || other.payload == payload) &&
|
||||
(identical(other.dateTime, dateTime) ||
|
||||
other.dateTime == dateTime));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, logLevel, payload, dateTime);
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
|
||||
__$$LogImplCopyWithImpl<_$LogImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$LogImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Log implements Log {
|
||||
const factory _Log(
|
||||
{@JsonKey(name: "LogLevel") final LogLevel logLevel,
|
||||
@JsonKey(name: "Payload") final String payload,
|
||||
@JsonKey(fromJson: _logDateTime) required final String dateTime}) =
|
||||
_$LogImpl;
|
||||
|
||||
factory _Log.fromJson(Map<String, dynamic> json) = _$LogImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "LogLevel")
|
||||
LogLevel get logLevel;
|
||||
@override
|
||||
@JsonKey(name: "Payload")
|
||||
String get payload;
|
||||
@override
|
||||
@JsonKey(fromJson: _logDateTime)
|
||||
String get dateTime;
|
||||
|
||||
/// Create a copy of Log
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LogsState {
|
||||
List<Log> get logs => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -6,25 +6,6 @@ part of '../common.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Log _$LogFromJson(Map<String, dynamic> json) => Log(
|
||||
logLevel: $enumDecode(_$LogLevelEnumMap, json['LogLevel']),
|
||||
payload: json['Payload'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LogToJson(Log instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
LogLevel.info: 'info',
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PackageImpl(
|
||||
packageName: json['packageName'] as String,
|
||||
@@ -88,6 +69,28 @@ Map<String, dynamic> _$$ConnectionImplToJson(_$ConnectionImpl instance) =>
|
||||
'chains': instance.chains,
|
||||
};
|
||||
|
||||
_$LogImpl _$$LogImplFromJson(Map<String, dynamic> json) => _$LogImpl(
|
||||
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??
|
||||
LogLevel.app,
|
||||
payload: json['Payload'] as String? ?? "",
|
||||
dateTime: _logDateTime(json['dateTime']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$LogImplToJson(_$LogImpl instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
'dateTime': instance.dateTime,
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
LogLevel.info: 'info',
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
_$DAVImpl _$$DAVImplFromJson(Map<String, dynamic> json) => _$DAVImpl(
|
||||
uri: json['uri'] as String,
|
||||
user: json['user'] as String,
|
||||
|
||||
@@ -1740,6 +1740,170 @@ abstract class _ProxiesStyle implements ProxiesStyle {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
TextScale _$TextScaleFromJson(Map<String, dynamic> json) {
|
||||
return _TextScale.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TextScale {
|
||||
dynamic get enable => throw _privateConstructorUsedError;
|
||||
dynamic get scale => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this TextScale to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TextScaleCopyWith<TextScale> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TextScaleCopyWith<$Res> {
|
||||
factory $TextScaleCopyWith(TextScale value, $Res Function(TextScale) then) =
|
||||
_$TextScaleCopyWithImpl<$Res, TextScale>;
|
||||
@useResult
|
||||
$Res call({dynamic enable, dynamic scale});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TextScaleCopyWithImpl<$Res, $Val extends TextScale>
|
||||
implements $TextScaleCopyWith<$Res> {
|
||||
_$TextScaleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = freezed,
|
||||
Object? scale = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
enable: freezed == enable
|
||||
? _value.enable
|
||||
: enable // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
scale: freezed == scale
|
||||
? _value.scale
|
||||
: scale // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TextScaleImplCopyWith<$Res>
|
||||
implements $TextScaleCopyWith<$Res> {
|
||||
factory _$$TextScaleImplCopyWith(
|
||||
_$TextScaleImpl value, $Res Function(_$TextScaleImpl) then) =
|
||||
__$$TextScaleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({dynamic enable, dynamic scale});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TextScaleImplCopyWithImpl<$Res>
|
||||
extends _$TextScaleCopyWithImpl<$Res, _$TextScaleImpl>
|
||||
implements _$$TextScaleImplCopyWith<$Res> {
|
||||
__$$TextScaleImplCopyWithImpl(
|
||||
_$TextScaleImpl _value, $Res Function(_$TextScaleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = freezed,
|
||||
Object? scale = freezed,
|
||||
}) {
|
||||
return _then(_$TextScaleImpl(
|
||||
enable: freezed == enable ? _value.enable! : enable,
|
||||
scale: freezed == scale ? _value.scale! : scale,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$TextScaleImpl implements _TextScale {
|
||||
const _$TextScaleImpl({this.enable = false, this.scale = 1.0});
|
||||
|
||||
factory _$TextScaleImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TextScaleImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final dynamic enable;
|
||||
@override
|
||||
@JsonKey()
|
||||
final dynamic scale;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TextScale(enable: $enable, scale: $scale)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TextScaleImpl &&
|
||||
const DeepCollectionEquality().equals(other.enable, enable) &&
|
||||
const DeepCollectionEquality().equals(other.scale, scale));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(enable),
|
||||
const DeepCollectionEquality().hash(scale));
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
|
||||
__$$TextScaleImplCopyWithImpl<_$TextScaleImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TextScaleImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TextScale implements TextScale {
|
||||
const factory _TextScale({final dynamic enable, final dynamic scale}) =
|
||||
_$TextScaleImpl;
|
||||
|
||||
factory _TextScale.fromJson(Map<String, dynamic> json) =
|
||||
_$TextScaleImpl.fromJson;
|
||||
|
||||
@override
|
||||
dynamic get enable;
|
||||
@override
|
||||
dynamic get scale;
|
||||
|
||||
/// Create a copy of TextScale
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) {
|
||||
return _ThemeProps.fromJson(json);
|
||||
}
|
||||
@@ -1751,6 +1915,7 @@ mixin _$ThemeProps {
|
||||
ThemeMode get themeMode => throw _privateConstructorUsedError;
|
||||
DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError;
|
||||
bool get pureBlack => throw _privateConstructorUsedError;
|
||||
TextScale get textScale => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ThemeProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -1773,7 +1938,10 @@ abstract class $ThemePropsCopyWith<$Res> {
|
||||
List<int> primaryColors,
|
||||
ThemeMode themeMode,
|
||||
DynamicSchemeVariant schemeVariant,
|
||||
bool pureBlack});
|
||||
bool pureBlack,
|
||||
TextScale textScale});
|
||||
|
||||
$TextScaleCopyWith<$Res> get textScale;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1796,6 +1964,7 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
|
||||
Object? themeMode = null,
|
||||
Object? schemeVariant = null,
|
||||
Object? pureBlack = null,
|
||||
Object? textScale = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
primaryColor: freezed == primaryColor
|
||||
@@ -1818,8 +1987,22 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
|
||||
? _value.pureBlack
|
||||
: pureBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
textScale: null == textScale
|
||||
? _value.textScale
|
||||
: textScale // ignore: cast_nullable_to_non_nullable
|
||||
as TextScale,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$TextScaleCopyWith<$Res> get textScale {
|
||||
return $TextScaleCopyWith<$Res>(_value.textScale, (value) {
|
||||
return _then(_value.copyWith(textScale: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1835,7 +2018,11 @@ abstract class _$$ThemePropsImplCopyWith<$Res>
|
||||
List<int> primaryColors,
|
||||
ThemeMode themeMode,
|
||||
DynamicSchemeVariant schemeVariant,
|
||||
bool pureBlack});
|
||||
bool pureBlack,
|
||||
TextScale textScale});
|
||||
|
||||
@override
|
||||
$TextScaleCopyWith<$Res> get textScale;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1856,6 +2043,7 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
Object? themeMode = null,
|
||||
Object? schemeVariant = null,
|
||||
Object? pureBlack = null,
|
||||
Object? textScale = null,
|
||||
}) {
|
||||
return _then(_$ThemePropsImpl(
|
||||
primaryColor: freezed == primaryColor
|
||||
@@ -1878,6 +2066,10 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
? _value.pureBlack
|
||||
: pureBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
textScale: null == textScale
|
||||
? _value.textScale
|
||||
: textScale // ignore: cast_nullable_to_non_nullable
|
||||
as TextScale,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1890,7 +2082,8 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
final List<int> primaryColors = defaultPrimaryColors,
|
||||
this.themeMode = ThemeMode.dark,
|
||||
this.schemeVariant = DynamicSchemeVariant.tonalSpot,
|
||||
this.pureBlack = false})
|
||||
this.pureBlack = false,
|
||||
this.textScale = const TextScale()})
|
||||
: _primaryColors = primaryColors;
|
||||
|
||||
factory _$ThemePropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -1916,10 +2109,13 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool pureBlack;
|
||||
@override
|
||||
@JsonKey()
|
||||
final TextScale textScale;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack)';
|
||||
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack, textScale: $textScale)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1936,7 +2132,9 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
(identical(other.schemeVariant, schemeVariant) ||
|
||||
other.schemeVariant == schemeVariant) &&
|
||||
(identical(other.pureBlack, pureBlack) ||
|
||||
other.pureBlack == pureBlack));
|
||||
other.pureBlack == pureBlack) &&
|
||||
(identical(other.textScale, textScale) ||
|
||||
other.textScale == textScale));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -1947,7 +2145,8 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
const DeepCollectionEquality().hash(_primaryColors),
|
||||
themeMode,
|
||||
schemeVariant,
|
||||
pureBlack);
|
||||
pureBlack,
|
||||
textScale);
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1971,7 +2170,8 @@ abstract class _ThemeProps implements ThemeProps {
|
||||
final List<int> primaryColors,
|
||||
final ThemeMode themeMode,
|
||||
final DynamicSchemeVariant schemeVariant,
|
||||
final bool pureBlack}) = _$ThemePropsImpl;
|
||||
final bool pureBlack,
|
||||
final TextScale textScale}) = _$ThemePropsImpl;
|
||||
|
||||
factory _ThemeProps.fromJson(Map<String, dynamic> json) =
|
||||
_$ThemePropsImpl.fromJson;
|
||||
@@ -1986,6 +2186,8 @@ abstract class _ThemeProps implements ThemeProps {
|
||||
DynamicSchemeVariant get schemeVariant;
|
||||
@override
|
||||
bool get pureBlack;
|
||||
@override
|
||||
TextScale get textScale;
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -222,6 +222,18 @@ const _$ProxyCardTypeEnumMap = {
|
||||
ProxyCardType.min: 'min',
|
||||
};
|
||||
|
||||
_$TextScaleImpl _$$TextScaleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$TextScaleImpl(
|
||||
enable: json['enable'] ?? false,
|
||||
scale: json['scale'] ?? 1.0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TextScaleImplToJson(_$TextScaleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'scale': instance.scale,
|
||||
};
|
||||
|
||||
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ThemePropsImpl(
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
@@ -235,6 +247,9 @@ _$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
|
||||
DynamicSchemeVariant.tonalSpot,
|
||||
pureBlack: json['pureBlack'] as bool? ?? false,
|
||||
textScale: json['textScale'] == null
|
||||
? const TextScale()
|
||||
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
|
||||
@@ -244,6 +259,7 @@ Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
|
||||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||
'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,
|
||||
'pureBlack': instance.pureBlack,
|
||||
'textScale': instance.textScale,
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
|
||||
@@ -23,6 +23,7 @@ typedef UpdateTasks = List<FutureOr Function()>;
|
||||
class GlobalState {
|
||||
static GlobalState? _instance;
|
||||
Map<Key, double> cacheScrollPosition = {};
|
||||
Map<Key, FixedMap<String, double>> cacheHeightMap = {};
|
||||
bool isService = false;
|
||||
Timer? timer;
|
||||
Timer? groupsUpdateTimer;
|
||||
@@ -66,7 +67,8 @@ class GlobalState {
|
||||
_initDynamicColor() async {
|
||||
try {
|
||||
corePalette = await DynamicColorPlugin.getCorePalette();
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ?? Color(defaultPrimaryColor);
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ??
|
||||
Color(defaultPrimaryColor);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ class DonutChartPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
const strokeWidth = 10.0;
|
||||
final strokeWidth = 10.0.ap;
|
||||
final radius = min(size.width / 2, size.height / 2) - strokeWidth / 2;
|
||||
|
||||
final gapAngle = 2 * asin(strokeWidth * 1 / (2 * radius)) * 1.2;
|
||||
|
||||
@@ -62,6 +62,22 @@ class OpenDelegate extends Delegate {
|
||||
});
|
||||
}
|
||||
|
||||
class NextDelegate extends Delegate {
|
||||
final Widget widget;
|
||||
final String title;
|
||||
final double? maxWidth;
|
||||
final Widget? action;
|
||||
final bool blur;
|
||||
|
||||
const NextDelegate({
|
||||
required this.title,
|
||||
required this.widget,
|
||||
this.maxWidth,
|
||||
this.action,
|
||||
this.blur = true,
|
||||
});
|
||||
}
|
||||
|
||||
class OptionsDelegate<T> extends Delegate {
|
||||
final List<T> options;
|
||||
final String title;
|
||||
@@ -138,6 +154,21 @@ class ListItem<T> extends StatelessWidget {
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
const ListItem.next({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 16),
|
||||
this.trailing,
|
||||
required NextDelegate this.delegate,
|
||||
this.horizontalTitleGap,
|
||||
this.dense,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.tileTitleAlignment = ListTileTitleAlignment.center,
|
||||
}) : onTap = null;
|
||||
|
||||
const ListItem.options({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -285,6 +316,34 @@ class ListItem<T> extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
if (delegate is NextDelegate) {
|
||||
final nextDelegate = delegate as NextDelegate;
|
||||
final child = SafeArea(
|
||||
child: nextDelegate.widget,
|
||||
);
|
||||
|
||||
return _buildListTile(
|
||||
onTap: () {
|
||||
showExtend(
|
||||
context,
|
||||
props: ExtendProps(
|
||||
blur: nextDelegate.blur,
|
||||
maxWidth: nextDelegate.maxWidth,
|
||||
),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
actions: [
|
||||
if (nextDelegate.action != null) nextDelegate.action!,
|
||||
],
|
||||
type: type,
|
||||
body: child,
|
||||
title: nextDelegate.title,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (delegate is OptionsDelegate) {
|
||||
final optionsDelegate = delegate as OptionsDelegate<T>;
|
||||
return _buildListTile(
|
||||
@@ -353,14 +412,11 @@ class ListItem<T> extends StatelessWidget {
|
||||
radioDelegate.onChanged!(radioDelegate.value);
|
||||
}
|
||||
},
|
||||
leading: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: Radio<T>(
|
||||
value: radioDelegate.value,
|
||||
groupValue: radioDelegate.groupValue,
|
||||
onChanged: radioDelegate.onChanged,
|
||||
),
|
||||
leading: Radio<T>(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
value: radioDelegate.value,
|
||||
groupValue: radioDelegate.groupValue,
|
||||
onChanged: radioDelegate.onChanged,
|
||||
),
|
||||
trailing: trailing,
|
||||
);
|
||||
|
||||
33
lib/widgets/notification.dart
Normal file
33
lib/widgets/notification.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:fl_clash/models/config.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class TextScaleNotification extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Function(TextScale textScale) onNotification;
|
||||
|
||||
const TextScaleNotification({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.onNotification,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer(
|
||||
builder: (_, ref, __) {
|
||||
ref.listen(
|
||||
themeSettingProvider.select((state) => state.textScale),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
onNotification(next);
|
||||
}
|
||||
},
|
||||
);
|
||||
return child;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -125,25 +125,25 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
}
|
||||
}
|
||||
|
||||
ThemeData _appBarTheme(BuildContext context) {
|
||||
Widget _buildSearchingAppBarTheme(Widget child) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
return theme.copyWith(
|
||||
appBarTheme: AppBarTheme(
|
||||
systemOverlayStyle: colorScheme.brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark,
|
||||
backgroundColor: colorScheme.brightness == Brightness.dark
|
||||
? Colors.grey[900]
|
||||
: Colors.white,
|
||||
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: InputBorder.none,
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
appBarTheme: theme.appBarTheme.copyWith(
|
||||
backgroundColor: colorScheme.brightness == Brightness.dark
|
||||
? Colors.grey[900]
|
||||
: Colors.white,
|
||||
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
|
||||
titleTextStyle: theme.textTheme.titleLarge,
|
||||
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -318,72 +318,66 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
child: appBar,
|
||||
);
|
||||
}
|
||||
return _isSearch
|
||||
? Theme(
|
||||
data: _appBarTheme(context),
|
||||
child: CommonPopScope(
|
||||
onPop: () {
|
||||
if (_isSearch) {
|
||||
_handleExitSearching();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: appBar,
|
||||
),
|
||||
)
|
||||
: appBar;
|
||||
return _isSearch ? _buildSearchingAppBarTheme(appBar) : appBar;
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ValueListenableBuilder<AppBarState>(
|
||||
valueListenable: _appBarState,
|
||||
builder: (_, state, __) {
|
||||
return _buildAppBarWrap(
|
||||
AppBar(
|
||||
centerTitle: widget.centerTitle ?? false,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||
leading: _buildLeading(),
|
||||
title: _buildTitle(state.searchState),
|
||||
actions: _buildActions(
|
||||
state.searchState != null,
|
||||
state.actions.isNotEmpty
|
||||
? state.actions
|
||||
: widget.actions ?? [],
|
||||
),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
appBarTheme: AppBarTheme(
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: widget.appBar ??
|
||||
Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ValueListenableBuilder<AppBarState>(
|
||||
valueListenable: _appBarState,
|
||||
builder: (_, state, __) {
|
||||
return _buildAppBarWrap(
|
||||
AppBar(
|
||||
centerTitle: widget.centerTitle ?? false,
|
||||
automaticallyImplyLeading:
|
||||
widget.automaticallyImplyLeading,
|
||||
leading: _buildLeading(),
|
||||
title: _buildTitle(state.searchState),
|
||||
actions: _buildActions(
|
||||
state.searchState != null,
|
||||
state.actions.isNotEmpty
|
||||
? state.actions
|
||||
: widget.actions ?? [],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -391,49 +385,51 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(widget.appBar != null || widget.title != null);
|
||||
final body = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _keywordsNotifier,
|
||||
builder: (_, keywords, __) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_onKeywordsUpdate != null) {
|
||||
_onKeywordsUpdate!(keywords);
|
||||
final body = SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _keywordsNotifier,
|
||||
builder: (_, keywords, __) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_onKeywordsUpdate != null) {
|
||||
_onKeywordsUpdate!(keywords);
|
||||
}
|
||||
});
|
||||
if (keywords.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
});
|
||||
if (keywords.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final keyword in keywords)
|
||||
CommonChip(
|
||||
label: keyword,
|
||||
type: ChipType.delete,
|
||||
onPressed: () {
|
||||
_deleteKeyword(keyword);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: widget.body,
|
||||
),
|
||||
],
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final keyword in keywords)
|
||||
CommonChip(
|
||||
label: keyword,
|
||||
type: ChipType.delete,
|
||||
onPressed: () {
|
||||
_deleteKeyword(keyword);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: widget.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
final scaffold = Scaffold(
|
||||
appBar: widget.appBar ?? _buildAppBar(),
|
||||
appBar: _buildAppBar(),
|
||||
body: body,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
floatingActionButton: ValueListenableBuilder<Widget?>(
|
||||
|
||||
@@ -1058,18 +1058,18 @@ class _RenderSegmentedControl<T extends Object> extends RenderBox
|
||||
}
|
||||
|
||||
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {
|
||||
const List<BoxShadow> thumbShadow = <BoxShadow>[
|
||||
BoxShadow(color: Color(0x1F000000), offset: Offset(0, 3), blurRadius: 8),
|
||||
BoxShadow(color: Color(0x0A000000), offset: Offset(0, 3), blurRadius: 1),
|
||||
];
|
||||
// const List<BoxShadow> thumbShadow = <BoxShadow>[
|
||||
// BoxShadow(color: Color(0x1F000000), offset: Offset(0, 3), blurRadius: 8),
|
||||
// BoxShadow(color: Color(0x0A000000), offset: Offset(0, 3), blurRadius: 1),
|
||||
// ];
|
||||
|
||||
final RRect thumbRRect =
|
||||
RRect.fromRectAndRadius(thumbRect.shift(offset), _kThumbRadius);
|
||||
|
||||
for (final BoxShadow shadow in thumbShadow) {
|
||||
context.canvas
|
||||
.drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
|
||||
}
|
||||
// for (final BoxShadow shadow in thumbShadow) {
|
||||
// context.canvas
|
||||
// .drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint());
|
||||
// }
|
||||
|
||||
context.canvas.drawRRect(
|
||||
thumbRRect.inflate(0.5), Paint()..color = const Color(0x0A000000));
|
||||
|
||||
@@ -84,6 +84,7 @@ class EmojiText extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RichText(
|
||||
textScaler: MediaQuery.of(context).textScaler,
|
||||
maxLines: maxLines,
|
||||
overflow: overflow ?? TextOverflow.clip,
|
||||
text: TextSpan(
|
||||
|
||||
@@ -32,3 +32,4 @@ export 'effect.dart';
|
||||
export 'palette.dart';
|
||||
export 'tab.dart';
|
||||
export 'container.dart';
|
||||
export 'notification.dart';
|
||||
|
||||
@@ -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.83+202504221
|
||||
version: 0.8.83+202504231
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -93,5 +93,5 @@ ffigen:
|
||||
flutter_intl:
|
||||
enabled: true
|
||||
class_name: AppLocalizations
|
||||
arb_dir: lib/l10n/arb
|
||||
arb_dir: arb
|
||||
output_dir: lib/l10n
|
||||
@@ -4,6 +4,7 @@ import requests
|
||||
|
||||
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
|
||||
TAG = os.getenv("TAG")
|
||||
RUN_ID = os.getenv("RUN_ID")
|
||||
|
||||
IS_STABLE = "-" not in TAG
|
||||
|
||||
@@ -45,7 +46,8 @@ if TAG:
|
||||
|
||||
if IS_STABLE:
|
||||
text += f"\nhttps://github.com/chen08209/FlClash/releases/tag/{TAG}\n"
|
||||
|
||||
else:
|
||||
text += f"\nhttps://github.com/chen08209/FlClash/actions/runs/{RUN_ID}\n"
|
||||
|
||||
if os.path.exists(release):
|
||||
text += "\n"
|
||||
|
||||
83
windows/packaging/exe/inno_setup.iss
Normal file
83
windows/packaging/exe/inno_setup.iss
Normal file
@@ -0,0 +1,83 @@
|
||||
[Setup]
|
||||
AppId={{APP_ID}}
|
||||
AppVersion={{APP_VERSION}}
|
||||
AppName={{DISPLAY_NAME}}
|
||||
AppPublisher={{PUBLISHER_NAME}}
|
||||
AppPublisherURL={{PUBLISHER_URL}}
|
||||
AppSupportURL={{PUBLISHER_URL}}
|
||||
AppUpdatesURL={{PUBLISHER_URL}}
|
||||
DefaultDirName={{INSTALL_DIR_NAME}}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=.
|
||||
OutputBaseFilename={{OUTPUT_BASE_FILENAME}}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
SetupIconFile={{SETUP_ICON_FILE}}
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired={{PRIVILEGES_REQUIRED}}
|
||||
ArchitecturesAllowed={{ARCH}}
|
||||
ArchitecturesInstallIn64BitMode={{ARCH}}
|
||||
|
||||
[Code]
|
||||
procedure KillProcesses;
|
||||
var
|
||||
Processes: TArrayOfString;
|
||||
i: Integer;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Processes := ['FlClash.exe', 'FlClashCore.exe', 'FlClashHelperService.exe'];
|
||||
|
||||
for i := 0 to GetArrayLength(Processes)-1 do
|
||||
begin
|
||||
Exec('taskkill', '/f /im ' + Processes[i], '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
KillProcesses;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
[Languages]
|
||||
{% for locale in LOCALES %}
|
||||
{% if locale.lang == 'en' %}Name: "english"; MessagesFile: "compiler:Default.isl"{% endif %}
|
||||
{% if locale.lang == 'hy' %}Name: "armenian"; MessagesFile: "compiler:Languages\\Armenian.isl"{% endif %}
|
||||
{% if locale.lang == 'bg' %}Name: "bulgarian"; MessagesFile: "compiler:Languages\\Bulgarian.isl"{% endif %}
|
||||
{% if locale.lang == 'ca' %}Name: "catalan"; MessagesFile: "compiler:Languages\\Catalan.isl"{% endif %}
|
||||
{% if locale.lang == 'zh' %}
|
||||
Name: "chineseSimplified"; MessagesFile: {% if locale.file %}{{ locale.file }}{% else %}"compiler:Languages\\ChineseSimplified.isl"{% endif %}
|
||||
{% endif %}
|
||||
{% if locale.lang == 'co' %}Name: "corsican"; MessagesFile: "compiler:Languages\\Corsican.isl"{% endif %}
|
||||
{% if locale.lang == 'cs' %}Name: "czech"; MessagesFile: "compiler:Languages\\Czech.isl"{% endif %}
|
||||
{% if locale.lang == 'da' %}Name: "danish"; MessagesFile: "compiler:Languages\\Danish.isl"{% endif %}
|
||||
{% if locale.lang == 'nl' %}Name: "dutch"; MessagesFile: "compiler:Languages\\Dutch.isl"{% endif %}
|
||||
{% if locale.lang == 'fi' %}Name: "finnish"; MessagesFile: "compiler:Languages\\Finnish.isl"{% endif %}
|
||||
{% if locale.lang == 'fr' %}Name: "french"; MessagesFile: "compiler:Languages\\French.isl"{% endif %}
|
||||
{% if locale.lang == 'de' %}Name: "german"; MessagesFile: "compiler:Languages\\German.isl"{% endif %}
|
||||
{% if locale.lang == 'he' %}Name: "hebrew"; MessagesFile: "compiler:Languages\\Hebrew.isl"{% endif %}
|
||||
{% if locale.lang == 'is' %}Name: "icelandic"; MessagesFile: "compiler:Languages\\Icelandic.isl"{% endif %}
|
||||
{% if locale.lang == 'it' %}Name: "italian"; MessagesFile: "compiler:Languages\\Italian.isl"{% endif %}
|
||||
{% if locale.lang == 'ja' %}Name: "japanese"; MessagesFile: "compiler:Languages\\Japanese.isl"{% endif %}
|
||||
{% if locale.lang == 'no' %}Name: "norwegian"; MessagesFile: "compiler:Languages\\Norwegian.isl"{% endif %}
|
||||
{% if locale.lang == 'pl' %}Name: "polish"; MessagesFile: "compiler:Languages\\Polish.isl"{% endif %}
|
||||
{% if locale.lang == 'pt' %}Name: "portuguese"; MessagesFile: "compiler:Languages\\Portuguese.isl"{% endif %}
|
||||
{% if locale.lang == 'ru' %}Name: "russian"; MessagesFile: "compiler:Languages\\Russian.isl"{% endif %}
|
||||
{% if locale.lang == 'sk' %}Name: "slovak"; MessagesFile: "compiler:Languages\\Slovak.isl"{% endif %}
|
||||
{% if locale.lang == 'sl' %}Name: "slovenian"; MessagesFile: "compiler:Languages\\Slovenian.isl"{% endif %}
|
||||
{% if locale.lang == 'es' %}Name: "spanish"; MessagesFile: "compiler:Languages\\Spanish.isl"{% endif %}
|
||||
{% if locale.lang == 'tr' %}Name: "turkish"; MessagesFile: "compiler:Languages\\Turkish.isl"{% endif %}
|
||||
{% if locale.lang == 'uk' %}Name: "ukrainian"; MessagesFile: "compiler:Languages\\Ukrainian.isl"{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}
|
||||
[Files]
|
||||
Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"
|
||||
Name: "{autodesktop}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: desktopicon
|
||||
[Run]
|
||||
Filename: "{app}\\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent
|
||||
@@ -1,3 +1,4 @@
|
||||
script_template: inno_setup.iss
|
||||
app_id: 728B3532-C74B-4870-9068-BE70FE12A3E6
|
||||
app_name: FlClash
|
||||
publisher: chen08209
|
||||
@@ -9,4 +10,5 @@ setup_icon_file: ..\windows\runner\resources\app_icon.ico
|
||||
locales:
|
||||
- lang: zh
|
||||
file: ..\windows\packaging\exe\ChineseSimplified.isl
|
||||
- lang: en
|
||||
- lang: en
|
||||
privileges_required: admin
|
||||
Reference in New Issue
Block a user