144 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			144 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C#
		
	
	
	
| 
								 | 
							
								using Contime.data;
							 | 
						|||
| 
								 | 
							
								using System.Diagnostics;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace Contime.model {
							 | 
						|||
| 
								 | 
							
								    public class TimeTrackerModel {
							 | 
						|||
| 
								 | 
							
								        // --- Public State & Data ---
							 | 
						|||
| 
								 | 
							
								        public enum AppState { Idle, WorkdayActive, TaskActive, OutOfContext }
							 | 
						|||
| 
								 | 
							
								        public AppState CurrentState { get; private set; } = AppState.Idle;
							 | 
						|||
| 
								 | 
							
								        public List<TaskItem> Tasks { get; private set; } = new List<TaskItem>();
							 | 
						|||
| 
								 | 
							
								        public TaskItem SelectedTask { get; private set; }
							 | 
						|||
| 
								 | 
							
								        public string OutOfContextReason { get; set; } = "";
							 | 
						|||
| 
								 | 
							
								        public DataAccess DataAccess => _dataAccess;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // --- Public Read-only Timer Properties ---
							 | 
						|||
| 
								 | 
							
								        public TimeSpan WorkdayTime => _workdayStopwatch.Elapsed;
							 | 
						|||
| 
								 | 
							
								        public TimeSpan ActiveTaskTime => _activeTaskStopwatch.Elapsed;
							 | 
						|||
| 
								 | 
							
								        public TimeSpan IdleTime => _idleStopwatch.Elapsed;
							 | 
						|||
| 
								 | 
							
								        public TimeSpan SelectedTaskTotalTime => SelectedTask?.ElapsedTime ?? TimeSpan.Zero;
							 | 
						|||
| 
								 | 
							
								        public TimeSpan DayTotal { get; private set; } = TimeSpan.Zero;
							 | 
						|||
| 
								 | 
							
								        public TimeSpan WeekTotal { get; private set; } = TimeSpan.Zero;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // --- Events ---
							 | 
						|||
| 
								 | 
							
								        public event Action StateChanged; // For high-frequency updates (timers)
							 | 
						|||
| 
								 | 
							
								        public event Action MajorStateChanged; // For low-frequency updates (task list)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // --- Private Fields ---
							 | 
						|||
| 
								 | 
							
								        private readonly DataAccess _dataAccess;
							 | 
						|||
| 
								 | 
							
								        private readonly Stopwatch _workdayStopwatch = new Stopwatch();
							 | 
						|||
| 
								 | 
							
								        private readonly Stopwatch _activeTaskStopwatch = new Stopwatch();
							 | 
						|||
| 
								 | 
							
								        private readonly Stopwatch _idleStopwatch = new Stopwatch();
							 | 
						|||
| 
								 | 
							
								        private long _currentWorkdayId;
							 | 
						|||
| 
								 | 
							
								        private long _currentTaskTimeId;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public TimeTrackerModel(DataAccess dataAccess) {
							 | 
						|||
| 
								 | 
							
								            _dataAccess = dataAccess;
							 | 
						|||
| 
								 | 
							
								            ReloadAllData(); // Initial load
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void ReloadAllData() {
							 | 
						|||
| 
								 | 
							
								            long? selectedTaskId = SelectedTask?.Id;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            Tasks = _dataAccess.GetAllTasks();
							 | 
						|||
| 
								 | 
							
								            DayTotal = _dataAccess.GetTotalTimeForDate(DateTime.Today);
							 | 
						|||
| 
								 | 
							
								            WeekTotal = _dataAccess.GetTotalTimeForCurrentWeek();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (selectedTaskId.HasValue) {
							 | 
						|||
| 
								 | 
							
								                SelectedTask = Tasks.FirstOrDefault(t => t.Id == selectedTaskId.Value);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            MajorStateChanged?.Invoke();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // --- Public Methods (State Changers) ---
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void StartStopWorkday() {
							 | 
						|||
| 
								 | 
							
								            if (CurrentState == AppState.Idle) {
							 | 
						|||
| 
								 | 
							
								                _currentWorkdayId = _dataAccess.GetOrCreateWorkday(DateTime.Today);
							 | 
						|||
| 
								 | 
							
								                CurrentState = AppState.WorkdayActive;
							 | 
						|||
| 
								 | 
							
								                _workdayStopwatch.Restart();
							 | 
						|||
| 
								 | 
							
								                _idleStopwatch.Restart();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else {
							 | 
						|||
| 
								 | 
							
								                if (CurrentState == AppState.TaskActive) StopTask();
							 | 
						|||
| 
								 | 
							
								                CurrentState = AppState.Idle;
							 | 
						|||
| 
								 | 
							
								                _workdayStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                _idleStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                SelectedTask = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ReloadAllData();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void SelectTask(long taskId) {
							 | 
						|||
| 
								 | 
							
								            if (SelectedTask?.Id == taskId) return; // Prevent re-selection loop
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            SelectedTask = taskId == -1 ? null : Tasks.FirstOrDefault(t => t.Id == taskId);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            // A selection change is a major state change that affects buttons
							 | 
						|||
| 
								 | 
							
								            MajorStateChanged?.Invoke();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void StartStopTask() {
							 | 
						|||
| 
								 | 
							
								            if (CurrentState == AppState.TaskActive) // Stopping
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                StopTask();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (CurrentState == AppState.WorkdayActive && SelectedTask != null && SelectedTask.Status == "open") // Starting
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                _currentTaskTimeId = _dataAccess.StartTaskTime(SelectedTask.Id, _currentWorkdayId);
							 | 
						|||
| 
								 | 
							
								                CurrentState = AppState.TaskActive;
							 | 
						|||
| 
								 | 
							
								                _activeTaskStopwatch.Restart();
							 | 
						|||
| 
								 | 
							
								                _idleStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                _idleStopwatch.Reset();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ReloadAllData();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void SwitchContext() {
							 | 
						|||
| 
								 | 
							
								            if (CurrentState == AppState.OutOfContext) // Returning
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                CurrentState = _currentTaskTimeId > 0 ? AppState.TaskActive : AppState.WorkdayActive;
							 | 
						|||
| 
								 | 
							
								                _workdayStopwatch.Start();
							 | 
						|||
| 
								 | 
							
								                if (CurrentState == AppState.TaskActive) _activeTaskStopwatch.Start();
							 | 
						|||
| 
								 | 
							
								                else _idleStopwatch.Start();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else if (CurrentState != AppState.Idle) // Going
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                _workdayStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                if (CurrentState == AppState.TaskActive) _activeTaskStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                if (CurrentState == AppState.WorkdayActive) _idleStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								                CurrentState = AppState.OutOfContext;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            StateChanged?.Invoke();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void AddTask(string name) {
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrWhiteSpace(name)) return;
							 | 
						|||
| 
								 | 
							
								            _dataAccess.AddTask(name);
							 | 
						|||
| 
								 | 
							
								            ReloadAllData();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void ToggleSelectedTaskStatus() {
							 | 
						|||
| 
								 | 
							
								            if (SelectedTask == null || CurrentState == AppState.TaskActive) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            string newStatus = SelectedTask.Status == "open" ? "closed" : "open";
							 | 
						|||
| 
								 | 
							
								            _dataAccess.UpdateTaskStatus(SelectedTask.Id, newStatus, newStatus == "closed");
							 | 
						|||
| 
								 | 
							
								            ReloadAllData();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private void StopTask() {
							 | 
						|||
| 
								 | 
							
								            if (!_activeTaskStopwatch.IsRunning) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            _dataAccess.EndTaskTime(_currentTaskTimeId);
							 | 
						|||
| 
								 | 
							
								            _currentTaskTimeId = 0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            CurrentState = AppState.WorkdayActive;
							 | 
						|||
| 
								 | 
							
								            _activeTaskStopwatch.Stop();
							 | 
						|||
| 
								 | 
							
								            _activeTaskStopwatch.Reset();
							 | 
						|||
| 
								 | 
							
								            _idleStopwatch.Restart();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 |