fix: icon permissions and GTK single instance
- Use GTK default flags for single instance - Fix icon path to absolute /usr/share/icons - Add postinstall chmod 644 for icon - Remove Dart-level single instance code
This commit is contained in:
@@ -50,11 +50,11 @@ GoRouter router(RouterRef ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final tabLocations = [
|
final tabLocations = [
|
||||||
const HomeRoute().location, // 0: Главная
|
const HomeRoute().location, // 0: Главная
|
||||||
const ProxiesRoute().location, // 1: Локации
|
const ProxiesRoute().location, // 1: Локации
|
||||||
const PerAppProxyRoute().location, // 2: Исключения
|
const PerAppProxyRoute().location, // 2: Исключения
|
||||||
const SettingsRoute().location, // 3: Настройки
|
const SettingsRoute().location, // 3: Настройки
|
||||||
const AboutRoute().location, // 4: О программе
|
const AboutRoute().location, // 4: О программе
|
||||||
];
|
];
|
||||||
|
|
||||||
int getCurrentIndex(BuildContext context) {
|
int getCurrentIndex(BuildContext context) {
|
||||||
@@ -66,9 +66,9 @@ int getCurrentIndex(BuildContext context) {
|
|||||||
// Проверяем остальные маршруты по порядку
|
// Проверяем остальные маршруты по порядку
|
||||||
// ВАЖНО: более длинные пути проверяем раньше!
|
// ВАЖНО: более длинные пути проверяем раньше!
|
||||||
if (location.startsWith(const PerAppProxyRoute().location)) return 2; // /settings/per-app-proxy
|
if (location.startsWith(const PerAppProxyRoute().location)) return 2; // /settings/per-app-proxy
|
||||||
if (location.startsWith(const ProxiesRoute().location)) return 1; // /proxies
|
if (location.startsWith(const ProxiesRoute().location)) return 1; // /proxies
|
||||||
if (location.startsWith(const SettingsRoute().location)) return 3; // /settings
|
if (location.startsWith(const SettingsRoute().location)) return 3; // /settings
|
||||||
if (location.startsWith(const AboutRoute().location)) return 4; // /about
|
if (location.startsWith(const AboutRoute().location)) return 4; // /about
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,49 +172,49 @@ class _CustomAdaptiveScaffold extends HookConsumerWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
children: [
|
children: [
|
||||||
// О программе
|
// О программе
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final t = ref.watch(translationsProvider);
|
final t = ref.watch(translationsProvider);
|
||||||
return _DrawerMenuItem(
|
return _DrawerMenuItem(
|
||||||
icon: FluentIcons.info_24_regular,
|
icon: FluentIcons.info_24_regular,
|
||||||
selectedIcon: FluentIcons.info_24_filled,
|
selectedIcon: FluentIcons.info_24_filled,
|
||||||
label: t.about.pageTitle,
|
label: t.about.pageTitle,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
RootScaffold.stateKey.currentState?.closeDrawer();
|
RootScaffold.stateKey.currentState?.closeDrawer();
|
||||||
const AboutRoute().push(context);
|
const AboutRoute().push(context);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
// Настройки
|
|
||||||
Builder(
|
|
||||||
builder: (context) {
|
|
||||||
final t = ref.watch(translationsProvider);
|
|
||||||
return _DrawerMenuItem(
|
|
||||||
icon: FluentIcons.settings_24_regular,
|
|
||||||
selectedIcon: FluentIcons.settings_24_filled,
|
|
||||||
label: t.settings.pageTitle,
|
|
||||||
isSelected: false,
|
|
||||||
onTap: () {
|
|
||||||
RootScaffold.stateKey.currentState?.closeDrawer();
|
|
||||||
const SettingsRoute().push(context);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const _DrawerThemeItem(),
|
|
||||||
const _DrawerLanguageItem(),
|
|
||||||
const _DrawerLicensesItem(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
// Настройки
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final t = ref.watch(translationsProvider);
|
||||||
|
return _DrawerMenuItem(
|
||||||
|
icon: FluentIcons.settings_24_regular,
|
||||||
|
selectedIcon: FluentIcons.settings_24_filled,
|
||||||
|
label: t.settings.pageTitle,
|
||||||
|
isSelected: false,
|
||||||
|
onTap: () {
|
||||||
|
RootScaffold.stateKey.currentState?.closeDrawer();
|
||||||
|
const SettingsRoute().push(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(),
|
||||||
|
const _DrawerThemeItem(),
|
||||||
|
const _DrawerLanguageItem(),
|
||||||
|
const _DrawerLicensesItem(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
body: AdaptiveLayout(
|
body: AdaptiveLayout(
|
||||||
primaryNavigation: SlotLayout(
|
primaryNavigation: SlotLayout(
|
||||||
config: <Breakpoint, SlotLayoutConfig>{
|
config: <Breakpoint, SlotLayoutConfig>{
|
||||||
|
|||||||
@@ -68,15 +68,15 @@ class PerAppProxyPage extends HookConsumerWidget with PresLogger {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
bottom: PlatformUtils.isDesktop
|
bottom: PlatformUtils.isDesktop
|
||||||
? null // На Desktop только вкладка "Домены"
|
? null // На Desktop только вкладка "Домены"
|
||||||
: TabBar(
|
: TabBar(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
onTap: (index) => currentTab.value = index,
|
onTap: (index) => currentTab.value = index,
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(text: t.settings.network.excludedDomains.domainsTab),
|
Tab(text: t.settings.network.excludedDomains.domainsTab),
|
||||||
Tab(text: t.settings.network.excludedDomains.appsTab),
|
Tab(text: t.settings.network.excludedDomains.appsTab),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final searchAppBar = SliverAppBar(
|
final searchAppBar = SliverAppBar(
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ class WindowNotifier extends _$WindowNotifier with AppLogger {
|
|||||||
Future<void> build() async {
|
Future<void> build() async {
|
||||||
if (!PlatformUtils.isDesktop) return;
|
if (!PlatformUtils.isDesktop) return;
|
||||||
|
|
||||||
// if (Platform.isWindows) {
|
|
||||||
// loggy.debug("ensuring single instance");
|
|
||||||
// await WindowsSingleInstance.ensureSingleInstance([], "Hiddify");
|
|
||||||
// }
|
|
||||||
|
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
await windowManager.setMinimumSize(minimumWindowSize);
|
await windowManager.setMinimumSize(minimumWindowSize);
|
||||||
await windowManager.setSize(defaultWindowSize);
|
await windowManager.setSize(defaultWindowSize);
|
||||||
|
|||||||
@@ -1,70 +1,9 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:umbrix/bootstrap.dart';
|
import 'package:umbrix/bootstrap.dart';
|
||||||
import 'package:umbrix/core/model/environment.dart';
|
import 'package:umbrix/core/model/environment.dart';
|
||||||
|
|
||||||
final lockFile = File('/tmp/umbrix.lock');
|
|
||||||
|
|
||||||
Future<void> _activateExistingWindow() async {
|
|
||||||
// Try to activate existing window via wmctrl
|
|
||||||
try {
|
|
||||||
final result = await Process.run('wmctrl', ['-a', 'Umbrix']);
|
|
||||||
if (result.exitCode == 0) return;
|
|
||||||
} catch (e) {
|
|
||||||
// wmctrl not available, try xdotool
|
|
||||||
try {
|
|
||||||
await Process.run('xdotool', ['search', '--name', 'Umbrix', 'windowactivate']);
|
|
||||||
} catch (e) {
|
|
||||||
// No window activation possible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _cleanupLockFile() {
|
|
||||||
try {
|
|
||||||
if (lockFile.existsSync()) {
|
|
||||||
lockFile.deleteSync();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore cleanup errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// Single instance check - BEFORE Flutter initialization
|
|
||||||
if (Platform.isLinux || Platform.isWindows) {
|
|
||||||
if (await lockFile.exists()) {
|
|
||||||
// Check if process is still alive
|
|
||||||
try {
|
|
||||||
final pidString = await lockFile.readAsString();
|
|
||||||
final pid = int.tryParse(pidString.trim());
|
|
||||||
if (pid != null) {
|
|
||||||
final result = await Process.run('ps', ['-p', pid.toString()]);
|
|
||||||
if (result.exitCode != 0) {
|
|
||||||
// Process dead, remove stale lock
|
|
||||||
await lockFile.delete();
|
|
||||||
} else {
|
|
||||||
// Process alive - activate window and exit
|
|
||||||
await _activateExistingWindow();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Error reading lock, remove it
|
|
||||||
try { await lockFile.delete(); } catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create lock file
|
|
||||||
try {
|
|
||||||
await lockFile.create();
|
|
||||||
await lockFile.writeAsString(pid.toString());
|
|
||||||
} catch (e) {
|
|
||||||
// If can't create, continue anyway
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct _MyApplication
|
|||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||||
#define ICON_PATH "./umbrix.png"
|
#define ICON_PATH "/usr/share/icons/hicolor/256x256/apps/umbrix.png"
|
||||||
|
|
||||||
// Implements GApplication::activate.
|
// Implements GApplication::activate.
|
||||||
static void my_application_activate(GApplication *application)
|
static void my_application_activate(GApplication *application)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ essential: false
|
|||||||
icon: ./logo/ic_launcher_playstore.png
|
icon: ./logo/ic_launcher_playstore.png
|
||||||
|
|
||||||
postinstall_scripts:
|
postinstall_scripts:
|
||||||
|
- chmod 644 /usr/share/icons/hicolor/256x256/apps/umbrix.png
|
||||||
- echo "Installed Umbrix VPN"
|
- echo "Installed Umbrix VPN"
|
||||||
postuninstall_scripts:
|
postuninstall_scripts:
|
||||||
- echo "Uninstalled Umbrix"
|
- echo "Uninstalled Umbrix"
|
||||||
|
|||||||
Reference in New Issue
Block a user