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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|