From 4daa0c4ebacc3866dd9350fdb8ff1674a74db176 Mon Sep 17 00:00:00 2001 From: ShaoHua <345265198@qqcom> Date: Sun, 5 Apr 2026 00:57:04 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A41.0.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TodoList/App.xaml | 9 - TodoList/App.xaml.cs | 364 ------------------ TodoList/AssemblyInfo.cs | 10 - TodoList/BuildSetup.ps1 | 55 --- .../Converters/EnumDescriptionConverter.cs | 31 -- TodoList/Models/TodoItem.cs | 67 ---- TodoList/Services/FileDataService.cs | 105 ----- TodoList/Services/GlobalShortcutService.cs | 127 ------ TodoList/Services/IDataService.cs | 15 - TodoList/Services/SettingsService.cs | 56 --- TodoList/Services/SqliteDataService.cs | 76 ---- TodoList/TodoList.csproj | 46 --- TodoList/ViewModels/MainViewModel.cs | 244 ------------ TodoList/ViewModels/QuickEntryViewModel.cs | 58 --- TodoList/Views/MainWindow.xaml | 340 ---------------- TodoList/Views/MainWindow.xaml.cs | 96 ----- TodoList/Views/QuickEntryWindow.xaml | 84 ---- TodoList/Views/QuickEntryWindow.xaml.cs | 23 -- TodoList/icon.ico | Bin 197166 -> 0 bytes TodoList/setup.iss | 56 --- 20 files changed, 1862 deletions(-) delete mode 100644 TodoList/App.xaml delete mode 100644 TodoList/App.xaml.cs delete mode 100644 TodoList/AssemblyInfo.cs delete mode 100644 TodoList/BuildSetup.ps1 delete mode 100644 TodoList/Converters/EnumDescriptionConverter.cs delete mode 100644 TodoList/Models/TodoItem.cs delete mode 100644 TodoList/Services/FileDataService.cs delete mode 100644 TodoList/Services/GlobalShortcutService.cs delete mode 100644 TodoList/Services/IDataService.cs delete mode 100644 TodoList/Services/SettingsService.cs delete mode 100644 TodoList/Services/SqliteDataService.cs delete mode 100644 TodoList/TodoList.csproj delete mode 100644 TodoList/ViewModels/MainViewModel.cs delete mode 100644 TodoList/ViewModels/QuickEntryViewModel.cs delete mode 100644 TodoList/Views/MainWindow.xaml delete mode 100644 TodoList/Views/MainWindow.xaml.cs delete mode 100644 TodoList/Views/QuickEntryWindow.xaml delete mode 100644 TodoList/Views/QuickEntryWindow.xaml.cs delete mode 100644 TodoList/icon.ico delete mode 100644 TodoList/setup.iss diff --git a/TodoList/App.xaml b/TodoList/App.xaml deleted file mode 100644 index 3f556f9..0000000 --- a/TodoList/App.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/TodoList/App.xaml.cs b/TodoList/App.xaml.cs deleted file mode 100644 index 033b4e6..0000000 --- a/TodoList/App.xaml.cs +++ /dev/null @@ -1,364 +0,0 @@ -using System; -using System.Threading; -using System.Windows; -using System.Windows.Interop; -using Microsoft.Win32; -using TodoList.Services; -using TodoList.ViewModels; -using TodoList.Views; -using System.Linq; -using System.Reflection; - -namespace TodoList -{ - public partial class App : System.Windows.Application - { - private const string MainWindowTitle = "待办事项"; - private IDataService _dataService; - private GlobalShortcutService _shortcutService; - private MainWindow _mainWindow; - private QuickEntryWindow? _quickEntryWindow; - private SettingsService _settingsService; - private NotifyIcon _notifyIcon; - private Mutex _mutex; - private EventWaitHandle _eventWaitHandle; - private const string UniqueEventName = "Global\\TodoListApp_Event_v1"; - - [System.Runtime.InteropServices.DllImport("user32.dll")] - private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - - [System.Runtime.InteropServices.DllImport("user32.dll")] - private static extern bool SetForegroundWindow(IntPtr hWnd); - - [System.Runtime.InteropServices.DllImport("user32.dll")] - private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - - public App() - { - // Disable hardware acceleration to prevent black screen issues - // Must be set before any UI is created - System.Windows.Media.RenderOptions.ProcessRenderMode = System.Windows.Interop.RenderMode.SoftwareOnly; - } - - protected override void OnStartup(StartupEventArgs e) - { - // Ensure app doesn't shutdown when main window closes (we hide it) - this.ShutdownMode = ShutdownMode.OnExplicitShutdown; - - const string appName = "Global\\TodoListApp_Unique_Mutex_v1"; - bool createdNew; - _mutex = new Mutex(true, appName, out createdNew); - - if (!createdNew) - { - // Signal the existing instance - try - { - using (var evt = EventWaitHandle.OpenExisting(UniqueEventName)) - { - evt.Set(); - } - } - catch - { - // Fallback to old method if event open fails - var hWnd = FindWindow(null, MainWindowTitle); - if (hWnd != IntPtr.Zero) - { - ShowWindow(hWnd, 9); // SW_RESTORE - SetForegroundWindow(hWnd); - } - } - - // Force exit to prevent second instance from running - Environment.Exit(0); - return; - } - - // Create the event handle for this instance - _eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName); - - // Start a thread to listen for signals - Thread thread = new Thread(() => - { - while (true) - { - _eventWaitHandle.WaitOne(); - this.Dispatcher.Invoke(() => ShowMainWindow()); - } - }); - thread.IsBackground = true; - thread.Start(); - - base.OnStartup(e); - - // Configure Auto Start - ConfigureAutoStart(); - - // Create Desktop/StartMenu Shortcut if needed (optional feature) - // CreateShortcut(); - - this.DispatcherUnhandledException += App_DispatcherUnhandledException; - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - - try - { - _settingsService = new SettingsService(); - _dataService = new SqliteDataService(); - _shortcutService = new GlobalShortcutService(); - - var mainViewModel = new MainViewModel(_dataService, _settingsService); - - _mainWindow = new MainWindow(mainViewModel); - _mainWindow.Title = MainWindowTitle; - _mainWindow.Loaded += MainWindow_Loaded; - - // Initialize Tray Icon - InitializeTrayIcon(); - - // Check for silent mode - bool silent = e.Args.Contains("--silent") || e.Args.Contains("-s"); - - if (!silent) - { - _mainWindow.Show(); - } - } - catch (Exception ex) - { - Log("Startup Error: " + ex.ToString()); - System.Windows.MessageBox.Show("Startup Error: " + ex.Message); - } - } - - private void ConfigureAutoStart() - { - try - { - var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName; - string cmd = $"\"{exePath}\" --silent"; - - // If running as dotnet tool, try to find the shim or stable entry point - // Usually %USERPROFILE%\.dotnet\tools\todo.exe - var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - var toolShim = System.IO.Path.Combine(userProfile, ".dotnet", "tools", "todo.exe"); - - if (System.IO.File.Exists(toolShim)) - { - // If the shim exists and we are likely running it (or just installed it), prefer the shim - // This handles updates better as the shim path stays constant. - // But we should verify if the current process IS related to it? - // Actually, if installed as tool, we definitely want to use the shim. - cmd = $"\"{toolShim}\" --silent"; - } - - var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); - if (key != null) - { - var existing = key.GetValue("TodoListApp"); - if (existing == null || existing.ToString() != cmd) - { - key.SetValue("TodoListApp", cmd); - } - key.Close(); - } - } - catch (Exception ex) - { - Log("AutoStart Error: " + ex.Message); - } - } - - private void InitializeTrayIcon() - { - _notifyIcon = new NotifyIcon(); - - // Try load icon from resource or file - try - { - // Load from embedded resource - var resourceUri = new Uri("pack://application:,,,/icon.ico"); - var streamInfo = System.Windows.Application.GetResourceStream(resourceUri); - - if (streamInfo != null) - { - using (var stream = streamInfo.Stream) - { - _notifyIcon.Icon = new System.Drawing.Icon(stream); - } - } - else - { - _notifyIcon.Icon = System.Drawing.SystemIcons.Application; - } - } - catch - { - _notifyIcon.Icon = System.Drawing.SystemIcons.Application; - } - - _notifyIcon.Visible = true; - _notifyIcon.Text = GetNotifyIconText(); - _notifyIcon.DoubleClick += (s, e) => ShowMainWindow(); - - var contextMenu = new ContextMenuStrip(); - contextMenu.Items.Add("打开主界面", null, (s, e) => ShowMainWindow()); - contextMenu.Items.Add("退出", null, (s, e) => ExitApplication()); - _notifyIcon.ContextMenuStrip = contextMenu; - } - - private static string GetNotifyIconText() - { - var version = GetDisplayVersion(); - var text = string.IsNullOrWhiteSpace(version) ? MainWindowTitle : $"{MainWindowTitle} v{version}"; - - return text.Length > 63 ? text[..63] : text; - } - - private static string? GetDisplayVersion() - { - var asm = Assembly.GetExecutingAssembly(); - - var info = asm.GetCustomAttribute()?.InformationalVersion?.Trim(); - if (!string.IsNullOrWhiteSpace(info)) - { - var plus = info.IndexOf('+'); - return plus >= 0 ? info[..plus] : info; - } - - var file = asm.GetCustomAttribute()?.Version?.Trim(); - if (!string.IsNullOrWhiteSpace(file)) - { - return file; - } - - return asm.GetName().Version?.ToString(); - } - - private void ShowMainWindow() - { - Log("ShowMainWindow called"); - if (_mainWindow != null) - { - if (_mainWindow.WindowState == WindowState.Minimized) - { - _mainWindow.WindowState = WindowState.Normal; - } - _mainWindow.Show(); - _mainWindow.Activate(); - - // Force foreground - var helper = new WindowInteropHelper(_mainWindow); - var handle = helper.Handle; - if (handle != IntPtr.Zero) - { - SetForegroundWindow(handle); - } - } - } - - private void ExitApplication() - { - _notifyIcon?.Dispose(); - _notifyIcon = null; - Shutdown(); - } - - private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) - { - Log("Dispatcher Error: " + e.Exception.ToString()); - System.Windows.MessageBox.Show("Dispatcher Error: " + e.Exception.Message); - e.Handled = true; - } - - private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - Log("Domain Error: " + e.ExceptionObject.ToString()); - System.Windows.MessageBox.Show("Critical Error: " + e.ExceptionObject.ToString()); - } - - private void Log(string message) - { - try - { - System.IO.File.AppendAllText("error.log", DateTime.Now + ": " + message + Environment.NewLine); - } - catch { } - } - - private bool _isHotkeyRegistered; - - private void RegisterHotkey() - { - if (_isHotkeyRegistered || _shortcutService == null || _settingsService == null) return; - - var helper = new WindowInteropHelper(_mainWindow); - var handle = helper.Handle; - - if (handle != IntPtr.Zero) - { - var settings = _settingsService.Settings; - var mods = GlobalShortcutService.GetModifier(settings.ShortcutModifiers); - var key = GlobalShortcutService.GetKey(settings.ShortcutKey); - - _shortcutService.Register(handle, OnHotKeyPressed, mods, key); - _isHotkeyRegistered = true; - - // Subscribe to settings changes to update hotkey - _settingsService.Settings.PropertyChanged += (s, args) => - { - if (args.PropertyName == nameof(AppSettings.ShortcutModifiers) || - args.PropertyName == nameof(AppSettings.ShortcutKey)) - { - var newMods = GlobalShortcutService.GetModifier(_settingsService.Settings.ShortcutModifiers); - var newKey = GlobalShortcutService.GetKey(_settingsService.Settings.ShortcutKey); - _shortcutService.UpdateShortcut(newMods, newKey); - } - }; - } - } - - private void OnHotKeyPressed() - { - Log("Hotkey pressed."); - ShowQuickEntryWindow(); - } - - private void ShowQuickEntryWindow() - { - if (_quickEntryWindow == null) - { - _quickEntryWindow = new QuickEntryWindow(_dataService); - _quickEntryWindow.Title = "新建待办"; - } - - if (_quickEntryWindow.WindowState == WindowState.Minimized) - { - _quickEntryWindow.WindowState = WindowState.Normal; - } - - _quickEntryWindow.Show(); - _quickEntryWindow.Activate(); - - var helper = new WindowInteropHelper(_quickEntryWindow); - var handle = helper.Handle; - if (handle != IntPtr.Zero) - { - SetForegroundWindow(handle); - } - } - - protected override void OnExit(ExitEventArgs e) - { - _notifyIcon?.Dispose(); - _shortcutService?.Dispose(); - base.OnExit(e); - } - - // We can hook into MainWindow's Loaded event to register hotkey - private void MainWindow_Loaded(object sender, RoutedEventArgs e) - { - RegisterHotkey(); - } - } -} diff --git a/TodoList/AssemblyInfo.cs b/TodoList/AssemblyInfo.cs deleted file mode 100644 index cc29e7f..0000000 --- a/TodoList/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Windows; - -[assembly:ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] diff --git a/TodoList/BuildSetup.ps1 b/TodoList/BuildSetup.ps1 deleted file mode 100644 index 2bb2da4..0000000 --- a/TodoList/BuildSetup.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -$ErrorActionPreference = "Stop" - -# Basic configuration -$ScriptPath = $PSScriptRoot -$ProjectFile = (Get-ChildItem -Path $ScriptPath -Filter "*.csproj" -File)[0].FullName -$SetupScript = Join-Path $ScriptPath "setup.iss" - -# Read version from project file -$currentVersion = "1.0.0" -[xml]$csproj = Get-Content $ProjectFile -if ($csproj.Project.PropertyGroup.Version) { - $currentVersion = $csproj.Project.PropertyGroup.Version -} - -# Increment version -$versionParts = $currentVersion.Split(".") -$patch = [int]$versionParts[2] + 1 -$newVersion = $versionParts[0] + "." + $versionParts[1] + "." + $patch - -# Update project version -$content = Get-Content $ProjectFile -Raw -$content = $content -replace ".*", "$newVersion" -Set-Content $ProjectFile -Value $content - -# Update setup script version -if (Test-Path $SetupScript) { - $issContent = Get-Content $SetupScript - for ($i = 0; $i -lt $issContent.Count; $i++) { - if ($issContent[$i] -like '#define MyAppVersion *') { - $issContent[$i] = '#define MyAppVersion "' + $newVersion + '"' - break - } - } - Set-Content $SetupScript -Value $issContent -} - -# Build project -dotnet publish $ProjectFile -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true -if ($LASTEXITCODE -ne 0) { - Write-Error "Build failed" - exit 1 -} - -# Package -$ISCC = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe" -if (Test-Path $ISCC) { - & $ISCC $SetupScript - if ($LASTEXITCODE -eq 0) { - Write-Host "Setup package created successfully!" -ForegroundColor Green - } else { - Write-Error "Packaging failed" - } -} else { - Write-Error "Inno Setup compiler not found" -} diff --git a/TodoList/Converters/EnumDescriptionConverter.cs b/TodoList/Converters/EnumDescriptionConverter.cs deleted file mode 100644 index 9d7d0f8..0000000 --- a/TodoList/Converters/EnumDescriptionConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.ComponentModel; -using System.Globalization; -using System.Reflection; -using System.Windows.Data; - -namespace TodoList.Converters -{ - public class EnumDescriptionConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value == null) return string.Empty; - - var type = value.GetType(); - var name = Enum.GetName(type, value); - if (name == null) return value.ToString(); - - var field = type.GetField(name); - if (field == null) return value.ToString(); - - var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; - return attr?.Description ?? value.ToString(); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value; - } - } -} diff --git a/TodoList/Models/TodoItem.cs b/TodoList/Models/TodoItem.cs deleted file mode 100644 index 49f0607..0000000 --- a/TodoList/Models/TodoItem.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text.Json.Serialization; -using CommunityToolkit.Mvvm.ComponentModel; - -namespace TodoList.Models -{ - public enum TodoPriority - { - [Description("低")] - Low, - [Description("中")] - Medium, - [Description("高")] - High - } - - public enum SyncStatus - { - Synced, - Pending, - Failed - } - - public enum SortBy - { - [Description("创建时间")] - CreatedAt, - [Description("完成时间")] - CompletedAt, - [Description("优先级")] - Priority - } - - public enum SortOrder - { - [Description("升序")] - Ascending, - [Description("降序")] - Descending - } - - public partial class TodoItem : ObservableObject - { - [ObservableProperty] - [property: SQLite.PrimaryKey] - private string id = Guid.NewGuid().ToString(); - - [ObservableProperty] - private string content = string.Empty; - - [ObservableProperty] - private bool isCompleted; - - [ObservableProperty] - private TodoPriority priority = TodoPriority.Medium; - - [ObservableProperty] - private DateTime createdAt = DateTime.Now; - - [ObservableProperty] - private DateTime? completedAt; - - [ObservableProperty] - private SyncStatus syncStatus = SyncStatus.Pending; - } -} diff --git a/TodoList/Services/FileDataService.cs b/TodoList/Services/FileDataService.cs deleted file mode 100644 index 929da82..0000000 --- a/TodoList/Services/FileDataService.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.Json; -using System.Threading.Tasks; -using TodoList.Models; - -namespace TodoList.Services -{ - public class FileDataService : IDataService - { - private readonly string _filePath; - - public FileDataService() - { - var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var folder = Path.Combine(appData, "TodoListApp"); - Directory.CreateDirectory(folder); - _filePath = Path.Combine(folder, "tasks.json"); - } - - public async Task> LoadTasksAsync(bool? completed = null) - { - if (!File.Exists(_filePath)) - { - return new List(); - } - - try - { - using var stream = File.OpenRead(_filePath); - var items = await JsonSerializer.DeserializeAsync>(stream); - var tasks = items ?? new List(); - - if (completed.HasValue) - { - return tasks.Where(t => t.IsCompleted == completed.Value).ToList(); - } - return tasks; - } - catch - { - return new List(); - } - } - - public async Task SaveTaskAsync(TodoItem task) - { - var tasks = await LoadTasksAsync(); - var existing = tasks.Find(t => t.Id == task.Id); - if (existing != null) - { - tasks.Remove(existing); - } - tasks.Add(task); - await SaveAllAsync(tasks); - return task; - } - - public async Task UpdateTaskAsync(TodoItem task) - { - var tasks = await LoadTasksAsync(); - var existing = tasks.Find(t => t.Id == task.Id); - if (existing != null) - { - tasks.Remove(existing); - tasks.Add(task); - await SaveAllAsync(tasks); - } - return task; - } - - public async Task ToggleCompleteAsync(string id) - { - var tasks = await LoadTasksAsync(); - var task = tasks.Find(t => t.Id == id); - if (task != null) - { - task.IsCompleted = !task.IsCompleted; - task.CompletedAt = task.IsCompleted ? DateTime.Now : null; - task.SyncStatus = SyncStatus.Pending; - await SaveAllAsync(tasks); - } - return task; - } - - public async Task SaveAllAsync(List tasks) - { - using var stream = File.Create(_filePath); - await JsonSerializer.SerializeAsync(stream, tasks, new JsonSerializerOptions { WriteIndented = true }); - } - - public async Task DeleteTaskAsync(string id) - { - var tasks = await LoadTasksAsync(); - var existing = tasks.Find(t => t.Id == id); - if (existing != null) - { - tasks.Remove(existing); - await SaveAllAsync(tasks); - } - } - } -} diff --git a/TodoList/Services/GlobalShortcutService.cs b/TodoList/Services/GlobalShortcutService.cs deleted file mode 100644 index b8a5c46..0000000 --- a/TodoList/Services/GlobalShortcutService.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Windows.Input; -using System.Windows.Interop; - -namespace TodoList.Services -{ - public class GlobalShortcutService : IDisposable - { - private const int HOTKEY_ID = 9000; - - public const uint MOD_ALT = 0x0001; - public const uint MOD_CONTROL = 0x0002; - public const uint MOD_SHIFT = 0x0004; - public const uint MOD_WIN = 0x0008; - - [DllImport("user32.dll")] - private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); - - [DllImport("user32.dll")] - private static extern bool UnregisterHotKey(IntPtr hWnd, int id); - - private IntPtr _windowHandle; - private HwndSource _source; - private Action _onHotKeyPressed; - private bool _isRegistered; - - public void Register(IntPtr windowHandle, Action onHotKeyPressed, uint modifiers, uint key) - { - // If already registered, unregister first (to support updating) - if (_isRegistered) - { - UnregisterHotKey(_windowHandle, HOTKEY_ID); - _source?.RemoveHook(HwndHook); - _isRegistered = false; - } - - _windowHandle = windowHandle; - _onHotKeyPressed = onHotKeyPressed; - - _source = HwndSource.FromHwnd(_windowHandle); - if (_source == null) return; // Should not happen if handle is valid - - _source.AddHook(HwndHook); - - if (RegisterHotKey(_windowHandle, HOTKEY_ID, modifiers, key)) - { - _isRegistered = true; - } - else - { - System.Diagnostics.Debug.WriteLine("Failed to register hotkey."); - } - } - - public void UpdateShortcut(uint modifiers, uint key) - { - if (_windowHandle != IntPtr.Zero && _onHotKeyPressed != null) - { - // Re-register with new keys - Register(_windowHandle, _onHotKeyPressed, modifiers, key); - } - } - - private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - const int WM_HOTKEY = 0x0312; - if (msg == WM_HOTKEY) - { - if (wParam.ToInt32() == HOTKEY_ID) - { - _onHotKeyPressed?.Invoke(); - handled = true; - } - } - return IntPtr.Zero; - } - - public void Unregister() - { - if (_isRegistered) - { - _source?.RemoveHook(HwndHook); - UnregisterHotKey(_windowHandle, HOTKEY_ID); - _isRegistered = false; - } - } - - public void Dispose() - { - Unregister(); - } - - public static uint GetModifier(string modifiers) - { - uint mod = 0; - if (string.IsNullOrEmpty(modifiers)) return mod; - - var parts = modifiers.Split(','); - foreach (var part in parts) - { - var p = part.Trim(); - if (p.Equals("Control", StringComparison.OrdinalIgnoreCase)) mod |= MOD_CONTROL; - if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase)) mod |= MOD_ALT; - if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase)) mod |= MOD_SHIFT; - if (p.Equals("Windows", StringComparison.OrdinalIgnoreCase)) mod |= MOD_WIN; - } - return mod; - } - - public static uint GetKey(string key) - { - if (Enum.TryParse(key, out var k)) - { - return (uint)KeyInterop.VirtualKeyFromKey(k); - } - // Fallback for simple letters if Key enum doesn't match directly (though it should for A-Z) - if (key.Length == 1) - { - char c = char.ToUpper(key[0]); - if (c >= 'A' && c <= 'Z') return (uint)c; - if (c >= '0' && c <= '9') return (uint)c; - } - return 0x41; // Default 'A' - } - } -} diff --git a/TodoList/Services/IDataService.cs b/TodoList/Services/IDataService.cs deleted file mode 100644 index f5e690a..0000000 --- a/TodoList/Services/IDataService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using TodoList.Models; - -namespace TodoList.Services -{ - public interface IDataService - { - Task> LoadTasksAsync(bool? completed = null); - Task SaveTaskAsync(TodoItem task); - Task UpdateTaskAsync(TodoItem task); - Task ToggleCompleteAsync(string id); - Task DeleteTaskAsync(string id); - } -} diff --git a/TodoList/Services/SettingsService.cs b/TodoList/Services/SettingsService.cs deleted file mode 100644 index cc2763b..0000000 --- a/TodoList/Services/SettingsService.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.IO; -using System.Text.Json; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.ComponentModel; - -namespace TodoList.Services -{ - public partial class AppSettings : ObservableObject - { - [ObservableProperty] - private string shortcutModifiers = "Control,Alt"; // Comma separated - - [ObservableProperty] - private string shortcutKey = "A"; - } - - public class SettingsService - { - private readonly string _filePath; - public AppSettings Settings { get; private set; } - - public SettingsService() - { - var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var folder = Path.Combine(appData, "TodoListApp"); - Directory.CreateDirectory(folder); - _filePath = Path.Combine(folder, "settings.json"); - Settings = LoadSettings(); - } - - private AppSettings LoadSettings() - { - if (!File.Exists(_filePath)) return new AppSettings(); - try - { - var json = File.ReadAllText(_filePath); - return JsonSerializer.Deserialize(json) ?? new AppSettings(); - } - catch - { - return new AppSettings(); - } - } - - public void SaveSettings() - { - try - { - var json = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true }); - File.WriteAllText(_filePath, json); - } - catch { } - } - } -} diff --git a/TodoList/Services/SqliteDataService.cs b/TodoList/Services/SqliteDataService.cs deleted file mode 100644 index a541bd4..0000000 --- a/TodoList/Services/SqliteDataService.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using SQLite; -using TodoList.Models; - -namespace TodoList.Services -{ - public class SqliteDataService : IDataService - { - private readonly SQLiteAsyncConnection _database; - - public SqliteDataService() - { - var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var folder = Path.Combine(appData, "TodoListApp"); - Directory.CreateDirectory(folder); - var databasePath = Path.Combine(folder, "TodoList.sqlite"); - - _database = new SQLiteAsyncConnection(databasePath); - _database.CreateTableAsync().Wait(); - } - - public async Task> LoadTasksAsync(bool? completed = null) - { - var query = _database.Table(); - if (completed.HasValue) - { - query = query.Where(t => t.IsCompleted == completed.Value); - } - return await query.ToListAsync(); - } - - public async Task SaveTaskAsync(TodoItem task) - { - await _database.InsertOrReplaceAsync(task); - return task; - } - - public async Task UpdateTaskAsync(TodoItem task) - { - await _database.UpdateAsync(task); - return task; - } - - public async Task ToggleCompleteAsync(string id) - { - var task = await _database.FindAsync(id); - if (task != null) - { - task.IsCompleted = !task.IsCompleted; - task.CompletedAt = task.IsCompleted ? DateTime.Now : null; - task.SyncStatus = SyncStatus.Pending; - await _database.UpdateAsync(task); - } - return task; - } - - public async Task SaveAllAsync(List tasks) - { - await _database.RunInTransactionAsync(tran => - { - foreach (var task in tasks) - { - tran.InsertOrReplace(task); - } - }); - } - - public async Task DeleteTaskAsync(string id) - { - await _database.DeleteAsync(id); - } - } -} diff --git a/TodoList/TodoList.csproj b/TodoList/TodoList.csproj deleted file mode 100644 index f8328a4..0000000 --- a/TodoList/TodoList.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - WinExe - net8.0-windows - enable - enable - 65001 - true - true - icon.ico - 1.0.21 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TodoList/ViewModels/MainViewModel.cs b/TodoList/ViewModels/MainViewModel.cs deleted file mode 100644 index 83c7454..0000000 --- a/TodoList/ViewModels/MainViewModel.cs +++ /dev/null @@ -1,244 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using TodoList.Models; -using TodoList.Services; - -namespace TodoList.ViewModels -{ - public partial class MainViewModel : ObservableObject, IRecipient - { - private readonly IDataService _dataService; - private readonly SettingsService _settingsService; - - [ObservableProperty] - private ObservableCollection tasks = new(); - - [ObservableProperty] - private bool showCompleted = false; - - [ObservableProperty] - private string newContent; - - [ObservableProperty] - private TodoPriority newPriority = TodoPriority.Medium; - - [ObservableProperty] - private bool isEditDialogOpen; - - [ObservableProperty] - private TodoItem editingTask; - - [ObservableProperty] - private string editContent; - - [ObservableProperty] - private TodoPriority editPriority = TodoPriority.Medium; - - [ObservableProperty] - private bool isSettingsOpen; - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(FullShortcut))] - private string shortcutKey; - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(FullShortcut))] - private string shortcutModifiers; - - [ObservableProperty] - private SortBy sortBy = SortBy.Priority; - - [ObservableProperty] - private Models.SortOrder sortOrder = Models.SortOrder.Descending; - - public string FullShortcut - { - get - { - var mods = ShortcutModifiers?.Replace(",", " + "); - return string.IsNullOrEmpty(mods) ? ShortcutKey : $"{mods} + {ShortcutKey}"; - } - } - - public MainViewModel(IDataService dataService, SettingsService settingsService) - { - _dataService = dataService; - _settingsService = settingsService; - ShortcutKey = _settingsService.Settings.ShortcutKey; - ShortcutModifiers = _settingsService.Settings.ShortcutModifiers; - - WeakReferenceMessenger.Default.Register(this); - LoadTasksCommand.Execute(null); - } - - [RelayCommand] - private void OpenSettings() - { - IsSettingsOpen = true; - ShortcutKey = _settingsService.Settings.ShortcutKey; - ShortcutModifiers = _settingsService.Settings.ShortcutModifiers; - } - - [RelayCommand] - private void CloseSettings() - { - IsSettingsOpen = false; - } - - [RelayCommand] - private void SaveSettings() - { - if (!string.IsNullOrWhiteSpace(ShortcutKey)) - { - _settingsService.Settings.ShortcutKey = ShortcutKey.ToUpper(); - _settingsService.Settings.ShortcutModifiers = ShortcutModifiers; - _settingsService.SaveSettings(); - } - IsSettingsOpen = false; - } - - async partial void OnShowCompletedChanged(bool value) - { - await LoadTasksAsync(); - } - - async partial void OnSortByChanged(SortBy value) - { - await LoadTasksAsync(); - } - - async partial void OnSortOrderChanged(Models.SortOrder value) - { - await LoadTasksAsync(); - } - - [RelayCommand] - private async Task AddTaskAsync() - { - if (string.IsNullOrWhiteSpace(NewContent)) return; - - var newTask = new TodoItem - { - Content = NewContent, - Priority = NewPriority, - IsCompleted = false, - SyncStatus = SyncStatus.Pending - }; - - await _dataService.SaveTaskAsync(newTask); - NewContent = string.Empty; - NewPriority = TodoPriority.Medium; - await LoadTasksAsync(); - } - - [RelayCommand] - private async Task LoadTasksAsync() - { - var allTasks = await _dataService.LoadTasksAsync(); - - var filtered = ShowCompleted - ? allTasks - : allTasks.Where(t => !t.IsCompleted).ToList(); - - IOrderedEnumerable sorted; - - if (SortBy == SortBy.Priority) - { - sorted = SortOrder == Models.SortOrder.Ascending - ? filtered.OrderBy(t => t.Priority).ThenBy(t => t.CreatedAt) - : filtered.OrderByDescending(t => t.Priority).ThenBy(t => t.CreatedAt); - } - else if (SortBy == SortBy.CreatedAt) - { - sorted = SortOrder == Models.SortOrder.Ascending - ? filtered.OrderBy(t => t.CreatedAt) - : filtered.OrderByDescending(t => t.CreatedAt); - } - else - { - sorted = SortOrder == Models.SortOrder.Ascending - ? filtered.OrderBy(t => t.CompletedAt).ThenBy(t => t.CreatedAt) - : filtered.OrderByDescending(t => t.CompletedAt).ThenBy(t => t.CreatedAt); - } - - Tasks.Clear(); - foreach (var t in sorted) - { - Tasks.Add(t); - } - } - - [RelayCommand] - private async Task ToggleCompleteAsync(TodoItem item) - { - if (item == null) return; - - if (item.IsCompleted) - { - item.CompletedAt = DateTime.Now; - } - else - { - item.CompletedAt = null; - } - - item.SyncStatus = SyncStatus.Pending; - await _dataService.SaveTaskAsync(item); - await LoadTasksAsync(); - } - - [RelayCommand] - private async Task DeleteAsync(TodoItem item) - { - if (item == null) return; - await _dataService.DeleteTaskAsync(item.Id); - Tasks.Remove(item); - } - - [RelayCommand] - private void OpenEditDialog(TodoItem item) - { - if (item == null) return; - EditingTask = item; - EditContent = item.Content; - EditPriority = item.Priority; - IsEditDialogOpen = true; - } - - [RelayCommand] - private void CloseEditDialog() - { - IsEditDialogOpen = false; - EditingTask = null; - EditContent = string.Empty; - EditPriority = TodoPriority.Medium; - } - - [RelayCommand] - private async Task SaveEditAsync() - { - if (EditingTask == null || string.IsNullOrWhiteSpace(EditContent)) return; - - EditingTask.Content = EditContent; - EditingTask.Priority = EditPriority; - EditingTask.SyncStatus = SyncStatus.Pending; - - await _dataService.SaveTaskAsync(EditingTask); - await LoadTasksAsync(); - CloseEditDialog(); - } - - public async void Receive(TaskAddedMessage message) - { - await LoadTasksAsync(); - } - } - - public class TaskAddedMessage - { - } -} diff --git a/TodoList/ViewModels/QuickEntryViewModel.cs b/TodoList/ViewModels/QuickEntryViewModel.cs deleted file mode 100644 index 065af8e..0000000 --- a/TodoList/ViewModels/QuickEntryViewModel.cs +++ /dev/null @@ -1,58 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using System; -using System.Threading.Tasks; -using TodoList.Models; -using TodoList.Services; - -namespace TodoList.ViewModels -{ - public partial class QuickEntryViewModel : ObservableObject - { - private readonly IDataService _dataService; - private Action _closeAction; - - [ObservableProperty] - private string content; - - [ObservableProperty] - private TodoPriority priority = TodoPriority.Medium; - - public QuickEntryViewModel(IDataService dataService, Action closeAction) - { - _dataService = dataService; - _closeAction = closeAction; - } - - [RelayCommand] - private async Task SaveAsync() - { - if (string.IsNullOrWhiteSpace(Content)) return; - - var newTask = new TodoItem - { - Content = Content, - Priority = Priority, - IsCompleted = false, - SyncStatus = SyncStatus.Pending - }; - - await _dataService.SaveTaskAsync(newTask); - - // Notify MainViewModel - WeakReferenceMessenger.Default.Send(new TaskAddedMessage()); - - // Reset and close - Content = string.Empty; - Priority = TodoPriority.Medium; - _closeAction?.Invoke(); - } - - [RelayCommand] - private void Cancel() - { - _closeAction?.Invoke(); - } - } -} diff --git a/TodoList/Views/MainWindow.xaml b/TodoList/Views/MainWindow.xaml deleted file mode 100644 index 1fd19d8..0000000 --- a/TodoList/Views/MainWindow.xaml +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -