Skip to content

Commit e31dc29

Browse files
authored
Merge pull request #1382 from TheLastFlame/main
Remembering window closing action
2 parents 0ff4a04 + e3cc918 commit e31dc29

File tree

9 files changed

+222
-64
lines changed

9 files changed

+222
-64
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ app.*.map.json
5555
/android/app/debug
5656
/android/app/profile
5757
/android/app/release
58+
59+
60+
/data

assets/translations/strings_en.i18n.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@
199199
"ignoreBatteryOptimizationsMsg": "Remove Restrictions For Optimal VPN Performance",
200200
"dynamicNotification": "Display Speed in Notification",
201201
"hapticFeedback": "Haptic Feedback",
202-
"autoIpCheck": "Automatically Check Connection IP"
202+
"autoIpCheck": "Automatically Check Connection IP",
203+
"actionAtClosing": "Action at closing",
204+
"actionsAtClosing": {
205+
"askEachTime": "Ask each time",
206+
"hide": "Hide",
207+
"exit": "Exit"
208+
}
203209
},
204210
"advanced": {
205211
"sectionTitle": "Advanced",
@@ -425,6 +431,7 @@
425431
"window": {
426432
"hide": "Hide",
427433
"close": "Exit",
428-
"alertMessage": "Hide or Exit the application?"
434+
"alertMessage": "Hide or Exit the application?",
435+
"remember": "Remember my choice"
429436
}
430-
}
437+
}

assets/translations/strings_ru.i18n.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,13 @@
199199
"ignoreBatteryOptimizationsMsg": "Отключение ограничений для оптимальной производительности VPN",
200200
"dynamicNotification": "Отображение скорости в уведомлении",
201201
"hapticFeedback": "Тактильная обратная связь",
202-
"autoIpCheck": "Автоматически проверять IP-адрес соединения"
202+
"autoIpCheck": "Автоматически проверять IP-адрес соединения",
203+
"actionAtClosing": "Действие при закрытии",
204+
"actionsAtClosing": {
205+
"askEachTime": "Каждый раз спрашивать",
206+
"hide": "Скрыть",
207+
"exit": "Выйти"
208+
}
203209
},
204210
"advanced": {
205211
"sectionTitle": "Расширенные",
@@ -425,6 +431,7 @@
425431
"window": {
426432
"hide": "Скрыть",
427433
"close": "Закрыть",
428-
"alertMessage": "Скрыть или выйти из приложения?"
434+
"alertMessage": "Скрыть приложение или выйти?",
435+
"remember": "Запомнить выбор"
429436
}
430-
}
437+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'package:hiddify/gen/translations.g.dart';
2+
3+
enum ActionsAtClosing {
4+
ask,
5+
hide,
6+
exit;
7+
8+
String present(TranslationsEn t) => switch (this) {
9+
ask => t.settings.general.actionsAtClosing.askEachTime,
10+
hide => t.settings.general.actionsAtClosing.hide,
11+
exit => t.settings.general.actionsAtClosing.exit,
12+
};
13+
}

lib/core/preferences/general_preferences.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/foundation.dart';
22
import 'package:hiddify/core/app_info/app_info_provider.dart';
33
import 'package:hiddify/core/model/environment.dart';
4+
import 'package:hiddify/core/preferences/actions_at_closing.dart';
45
// import 'package:hiddify/core/model/region.dart';
56
import 'package:hiddify/core/preferences/preferences_provider.dart';
67
import 'package:hiddify/core/utils/preferences_utils.dart';
@@ -61,6 +62,13 @@ abstract class Preferences {
6162
"store_reviewed_by_user",
6263
false,
6364
);
65+
66+
static final actionAtClose = PreferencesNotifier.create<ActionsAtClosing, String>(
67+
"action_at_close",
68+
ActionsAtClosing.ask,
69+
mapFrom: ActionsAtClosing.values.byName,
70+
mapTo: (value) => value.name,
71+
);
6472
}
6573

6674
@Riverpod(keepAlive: true)

lib/features/common/general_pref_tiles.dart

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import 'package:hiddify/core/localization/locale_extensions.dart';
55
import 'package:hiddify/core/localization/locale_preferences.dart';
66
import 'package:hiddify/core/localization/translations.dart';
77
import 'package:hiddify/core/model/region.dart';
8+
import 'package:hiddify/core/preferences/actions_at_closing.dart';
89
import 'package:hiddify/core/preferences/general_preferences.dart';
10+
import 'package:hiddify/core/theme/app_theme_mode.dart';
11+
import 'package:hiddify/core/theme/theme_preferences.dart';
912
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
10-
import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart';
1113
import 'package:hooks_riverpod/hooks_riverpod.dart';
1214

13-
class LocalePrefTile extends HookConsumerWidget {
15+
class LocalePrefTile extends ConsumerWidget {
1416
const LocalePrefTile({super.key});
1517

1618
@override
@@ -50,7 +52,7 @@ class LocalePrefTile extends HookConsumerWidget {
5052
}
5153
}
5254

53-
class RegionPrefTile extends HookConsumerWidget {
55+
class RegionPrefTile extends ConsumerWidget {
5456
const RegionPrefTile({super.key});
5557

5658
@override
@@ -102,7 +104,7 @@ class RegionPrefTile extends HookConsumerWidget {
102104
}
103105
}
104106

105-
class EnableAnalyticsPrefTile extends HookConsumerWidget {
107+
class EnableAnalyticsPrefTile extends ConsumerWidget {
106108
const EnableAnalyticsPrefTile({
107109
super.key,
108110
this.onChanged,
@@ -137,3 +139,83 @@ class EnableAnalyticsPrefTile extends HookConsumerWidget {
137139
);
138140
}
139141
}
142+
143+
class ThemeModePrefTile extends ConsumerWidget {
144+
const ThemeModePrefTile({super.key});
145+
146+
@override
147+
Widget build(BuildContext context, WidgetRef ref) {
148+
final t = ref.watch(translationsProvider);
149+
150+
final themeMode = ref.watch(themePreferencesProvider);
151+
152+
return ListTile(
153+
title: Text(t.settings.general.themeMode),
154+
subtitle: Text(themeMode.present(t)),
155+
leading: const Icon(FluentIcons.weather_moon_20_regular),
156+
onTap: () async {
157+
final selectedThemeMode = await showDialog<AppThemeMode>(
158+
context: context,
159+
builder: (context) {
160+
return SimpleDialog(
161+
title: Text(t.settings.general.themeMode),
162+
children: AppThemeMode.values
163+
.map(
164+
(e) => RadioListTile(
165+
title: Text(e.present(t)),
166+
value: e,
167+
groupValue: themeMode,
168+
onChanged: Navigator.of(context).maybePop,
169+
),
170+
)
171+
.toList(),
172+
);
173+
},
174+
);
175+
if (selectedThemeMode != null) {
176+
await ref.read(themePreferencesProvider.notifier).changeThemeMode(selectedThemeMode);
177+
}
178+
},
179+
);
180+
}
181+
}
182+
183+
class ClosingPrefTile extends ConsumerWidget {
184+
const ClosingPrefTile({super.key});
185+
186+
@override
187+
Widget build(BuildContext context, WidgetRef ref) {
188+
final t = ref.watch(translationsProvider);
189+
190+
final action = ref.watch(Preferences.actionAtClose);
191+
192+
return ListTile(
193+
title: Text(t.settings.general.actionAtClosing),
194+
subtitle: Text(action.present(t)),
195+
leading: const Icon(FluentIcons.arrow_exit_20_regular),
196+
onTap: () async {
197+
final selectedAction = await showDialog<ActionsAtClosing>(
198+
context: context,
199+
builder: (context) {
200+
return SimpleDialog(
201+
title: Text(t.settings.general.actionAtClosing),
202+
children: ActionsAtClosing.values
203+
.map(
204+
(e) => RadioListTile(
205+
title: Text(e.present(t)),
206+
value: e,
207+
groupValue: action,
208+
onChanged: Navigator.of(context).maybePop,
209+
),
210+
)
211+
.toList(),
212+
);
213+
},
214+
);
215+
if (selectedAction != null) {
216+
await ref.read(Preferences.actionAtClose.notifier).update(selectedAction);
217+
}
218+
},
219+
);
220+
}
221+
}

lib/features/settings/widgets/general_setting_tiles.dart

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import 'package:flutter/material.dart';
55
import 'package:hiddify/core/haptic/haptic_service.dart';
66
import 'package:hiddify/core/localization/translations.dart';
77
import 'package:hiddify/core/preferences/general_preferences.dart';
8-
import 'package:hiddify/core/theme/app_theme_mode.dart';
9-
import 'package:hiddify/core/theme/theme_preferences.dart';
108
import 'package:hiddify/features/auto_start/notifier/auto_start_notifier.dart';
119
import 'package:hiddify/features/common/general_pref_tiles.dart';
1210
import 'package:hiddify/utils/utils.dart';
@@ -19,39 +17,10 @@ class GeneralSettingTiles extends HookConsumerWidget {
1917
Widget build(BuildContext context, WidgetRef ref) {
2018
final t = ref.watch(translationsProvider);
2119

22-
final themeMode = ref.watch(themePreferencesProvider);
23-
2420
return Column(
2521
children: [
2622
const LocalePrefTile(),
27-
ListTile(
28-
title: Text(t.settings.general.themeMode),
29-
subtitle: Text(themeMode.present(t)),
30-
leading: const Icon(FluentIcons.weather_moon_20_regular),
31-
onTap: () async {
32-
final selectedThemeMode = await showDialog<AppThemeMode>(
33-
context: context,
34-
builder: (context) {
35-
return SimpleDialog(
36-
title: Text(t.settings.general.themeMode),
37-
children: AppThemeMode.values
38-
.map(
39-
(e) => RadioListTile(
40-
title: Text(e.present(t)),
41-
value: e,
42-
groupValue: themeMode,
43-
onChanged: Navigator.of(context).maybePop,
44-
),
45-
)
46-
.toList(),
47-
);
48-
},
49-
);
50-
if (selectedThemeMode != null) {
51-
await ref.read(themePreferencesProvider.notifier).changeThemeMode(selectedThemeMode);
52-
}
53-
},
54-
),
23+
const ThemeModePrefTile(),
5524
const EnableAnalyticsPrefTile(),
5625
SwitchListTile(
5726
title: Text(t.settings.general.autoIpCheck),
@@ -76,6 +45,7 @@ class GeneralSettingTiles extends HookConsumerWidget {
7645
),
7746
],
7847
if (PlatformUtils.isDesktop) ...[
48+
const ClosingPrefTile(),
7949
SwitchListTile(
8050
title: Text(t.settings.general.autoStart),
8151
value: ref.watch(autoStartNotifierProvider).asData!.value,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:hiddify/core/localization/translations.dart';
3+
import 'package:hiddify/core/preferences/actions_at_closing.dart';
4+
import 'package:hiddify/core/preferences/general_preferences.dart';
5+
import 'package:hiddify/features/window/notifier/window_notifier.dart';
6+
import 'package:hooks_riverpod/hooks_riverpod.dart';
7+
8+
class WindowClosingDialog extends ConsumerStatefulWidget {
9+
const WindowClosingDialog({super.key});
10+
11+
@override
12+
ConsumerState<WindowClosingDialog> createState() => _WindowClosingDialogState();
13+
}
14+
15+
class _WindowClosingDialogState extends ConsumerState<WindowClosingDialog> {
16+
bool remember = false;
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
final t = ref.watch(translationsProvider);
21+
22+
return AlertDialog(
23+
title: Text(t.window.alertMessage),
24+
content: GestureDetector(
25+
onTap: () => setState(() {
26+
remember = !remember;
27+
}),
28+
behavior: HitTestBehavior.translucent,
29+
child: Row(
30+
children: [
31+
Checkbox(
32+
value: remember,
33+
onChanged: (v) {
34+
remember = v ?? remember;
35+
setState(() {});
36+
},
37+
),
38+
const SizedBox(width: 16),
39+
Text(
40+
t.window.remember,
41+
style: const TextStyle(fontSize: 16),
42+
),
43+
],
44+
),
45+
),
46+
actions: [
47+
TextButton(
48+
onPressed: () {
49+
if (remember) {
50+
ref.read(Preferences.actionAtClose.notifier).update(ActionsAtClosing.exit);
51+
}
52+
ref.read(windowNotifierProvider.notifier).quit();
53+
},
54+
child: Text(t.window.close),
55+
),
56+
FilledButton(
57+
onPressed: () async {
58+
if (remember) {
59+
ref.read(Preferences.actionAtClose.notifier).update(ActionsAtClosing.hide);
60+
}
61+
Navigator.of(context).maybePop(false);
62+
await ref.read(windowNotifierProvider.notifier).close();
63+
},
64+
child: Text(t.window.hide),
65+
),
66+
],
67+
);
68+
}
69+
}

0 commit comments

Comments
 (0)