Files
marcer-gamedvd-launcher/src/MarcerGameDvdLauncher/FavoritesService.cs
T
stefankoelle 62968dbcd1 refactor: decompose LauncherApp, centralize magic strings/constants, configurable colors
- Extract InputController from AppHost (key handling + ReloadGameEntries)
- Centralize hardcoded values as code constants (non-configurable):
  FavoritesRootName, DefaultFileName, DefaultTitle, scroll fractions,
  AvailableLines helper, removed redundant ArgsTemplate fallback
- Make color scheme configurable via 'Colors' section in launcher.config.json
  with Enum.TryParse + default fallback; MenuRenderer uses constructor injection
- Clean up MenuRenderer: remove dead maxRow logic, unify cache sizing via EnsureCache
- Document intentional error swallowing in FavoritesService.Save/UIErrorService
- Update README + AGENTS.md with configurable color docs
- Remove redundant ArgsTemplate fallback (config is single source of truth)
2026-08-11 01:33:33 +02:00

107 lines
3.7 KiB
C#

// Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)
// Licensed under the MIT License. See LICENSE file in project root for details.
namespace MarcerGameDvdLauncher
{
// Manages loading, saving and querying favorite ZIP paths.
public class FavoritesService
{
// Virtual folder name displayed at the root when favorites exist.
// Not configurable — fixed UI element.
public const string FavoritesRootName = "Favorites";
// Filename used for persisting favorites to disk.
// Not configurable — fixed persistence file.
public const string DefaultFileName = "favorites.txt";
private readonly string _filePath;
// Use a SortedSet so favorites are kept in sorted order in memory.
private SortedSet<string> _favorites = new(StringComparer.OrdinalIgnoreCase);
public FavoritesService(string filePath)
{
_filePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
}
// Load favorites from disk (no-op if file missing)
public void Load()
{
_favorites = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
if (!File.Exists(_filePath)) return;
var lines = File.ReadAllLines(_filePath, System.Text.Encoding.UTF8);
foreach (var l in lines)
{
var t = l?.Trim();
if (string.IsNullOrEmpty(t)) continue;
try
{
var full = Path.GetFullPath(t);
_favorites.Add(full);
}
catch
{
// ignore invalid paths in the file
}
}
}
public bool IsFavorite(string path)
{
if (string.IsNullOrWhiteSpace(path)) return false;
try
{
var full = Path.GetFullPath(path);
return _favorites.Contains(full);
}
catch
{
return false;
}
}
// Returns true if any favorites are present
public bool HasFavorites() => _favorites.Count > 0;
// Returns all favorites as absolute paths in sorted order
public IReadOnlyList<string> GetAll() => _favorites.ToList();
// Toggle favorite state. Returns true if added, false if removed.
public bool Toggle(string path)
{
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
var full = Path.GetFullPath(path);
bool added;
if (_favorites.Contains(full))
{
_favorites.Remove(full);
added = false;
}
else
{
_favorites.Add(full);
added = true;
}
Save();
return added;
}
private void Save()
{
try
{
var dir = Path.GetDirectoryName(_filePath) ?? AppContext.BaseDirectory;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllLines(_filePath, _favorites.OrderBy(x => x), System.Text.Encoding.UTF8);
}
catch
{
// Swallowed intentionally ("bewusst still"): a persistence failure
// (e.g. disk full, read-only directory) must not crash or interrupt
// the UI. The in-memory state is still updated so the user sees
// immediate feedback; only the on-disk write is lost. On next
// application start the favorites reflect the last successful save.
}
}
}
}