From 94bad092673a856166898c1f9d4bb7a0d556502e Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sun, 14 Jun 2026 12:04:13 +0300 Subject: [PATCH 1/2] feat(theme): add ThemePaths directory helpers Centralize app support themes/imported paths and optional ~/.querya/themes without creating directories unless ensure* methods are called. --- lib/core/theme/theme_paths.dart | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/core/theme/theme_paths.dart diff --git a/lib/core/theme/theme_paths.dart b/lib/core/theme/theme_paths.dart new file mode 100644 index 00000000..7a0f6811 --- /dev/null +++ b/lib/core/theme/theme_paths.dart @@ -0,0 +1,47 @@ +import 'dart:io'; + +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +/// Centralizes theme file locations under app support and legacy paths. +abstract final class ThemePaths { + static const _themesSegment = 'themes'; + static const _importedSegment = 'imported'; + + /// App support `themes/` directory. Does not create the directory. + static Future userThemesDirectory() async { + final support = await getApplicationSupportDirectory(); + return Directory(p.join(support.path, _themesSegment)); + } + + /// App support `themes/imported/` directory. Does not create the directory. + static Future importedThemesDirectory() async { + final themes = await userThemesDirectory(); + return Directory(p.join(themes.path, _importedSegment)); + } + + /// Creates app support `themes/` if missing. + static Future ensureUserThemesDirectory() async { + final dir = await userThemesDirectory(); + if (!await dir.exists()) { + await dir.create(recursive: true); + } + return dir; + } + + /// Creates app support `themes/imported/` if missing. + static Future ensureImportedThemesDirectory() async { + final dir = await importedThemesDirectory(); + if (!await dir.exists()) { + await dir.create(recursive: true); + } + return dir; + } + + /// Optional legacy `~/.querya/themes`. Does not create the directory. + static Future legacyDotQueryaThemesDirectory() async { + final home = Platform.environment['HOME']; + if (home == null || home.isEmpty) return null; + return Directory(p.join(home, '.querya', _themesSegment)); + } +} From 784656cf00efe6abd86c0e4de717c0776797a9d8 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sun, 14 Jun 2026 12:04:13 +0300 Subject: [PATCH 2/2] test(theme): cover ThemePaths resolution and ensure helpers Closes #106. --- test/core/theme/theme_paths_test.dart | 84 +++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 test/core/theme/theme_paths_test.dart diff --git a/test/core/theme/theme_paths_test.dart b/test/core/theme/theme_paths_test.dart new file mode 100644 index 00000000..8374f3b1 --- /dev/null +++ b/test/core/theme/theme_paths_test.dart @@ -0,0 +1,84 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:querya_desktop/core/theme/theme_paths.dart'; + +class _FakePathProvider extends PathProviderPlatform { + _FakePathProvider(this._root); + final String _root; + + @override + Future getApplicationSupportPath() async => _root; +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + + setUpAll(() async { + tempDir = await Directory.systemTemp.createTemp('querya_theme_paths_test_'); + PathProviderPlatform.instance = _FakePathProvider(tempDir.path); + }); + + tearDownAll(() async { + if (await tempDir.exists()) { + await tempDir.delete(recursive: true); + } + }); + + group('ThemePaths', () { + test('userThemesDirectory resolves under app support', () async { + final dir = await ThemePaths.userThemesDirectory(); + + expect(dir.path, p.join(tempDir.path, 'themes')); + }); + + test('importedThemesDirectory resolves under user themes', () async { + final dir = await ThemePaths.importedThemesDirectory(); + + expect(dir.path, p.join(tempDir.path, 'themes', 'imported')); + }); + + test('path getters do not create directories', () async { + await ThemePaths.userThemesDirectory(); + await ThemePaths.importedThemesDirectory(); + + expect(await Directory(p.join(tempDir.path, 'themes')).exists(), isFalse); + expect( + await Directory(p.join(tempDir.path, 'themes', 'imported')).exists(), + isFalse, + ); + }); + + test('ensureUserThemesDirectory creates themes folder', () async { + final dir = await ThemePaths.ensureUserThemesDirectory(); + + expect(dir.path, p.join(tempDir.path, 'themes')); + expect(await dir.exists(), isTrue); + }); + + test('ensureImportedThemesDirectory creates imported folder', () async { + final dir = await ThemePaths.ensureImportedThemesDirectory(); + + expect(dir.path, p.join(tempDir.path, 'themes', 'imported')); + expect(await dir.exists(), isTrue); + }); + + test('legacyDotQueryaThemesDirectory resolves ~/.querya/themes', () async { + final home = Platform.environment['HOME']; + if (home == null || home.isEmpty) { + expect(await ThemePaths.legacyDotQueryaThemesDirectory(), isNull); + return; + } + + final dir = await ThemePaths.legacyDotQueryaThemesDirectory(); + + expect(dir, isNotNull); + expect(dir!.path, p.join(home, '.querya', 'themes')); + expect(await dir.exists(), isFalse); + }); + }); +}