chromium/ash/system/focus_mode/focus_mode_tasks_model.h

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SYSTEM_FOCUS_MODE_FOCUS_MODE_TASKS_MODEL_H_
#define ASH_SYSTEM_FOCUS_MODE_FOCUS_MODE_TASKS_MODEL_H_

#include <optional>
#include <vector>

#include "ash/ash_export.h"
#include "ash/system/focus_mode/focus_mode_tasks_provider.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"

namespace ash {

class ASH_EXPORT FocusModeTasksModel final {
 public:
  // Options struct used to add or update a task.
  struct TaskUpdate {
    TaskUpdate();
    TaskUpdate(const TaskUpdate&);
    ~TaskUpdate();

    static TaskUpdate CompletedUpdate(const TaskId& task_id);
    static TaskUpdate TitleUpdate(const TaskId& task_id,
                                  std::string_view title);
    static TaskUpdate NewTask(std::string_view title);

    std::optional<TaskId> task_id;
    std::optional<std::string> title;
    std::optional<bool> completed;
  };

  class Observer : public base::CheckedObserver {
   public:
    virtual void OnSelectedTaskChanged(
        const std::optional<FocusModeTask>& selected_task) = 0;
    virtual void OnTasksUpdated(const std::vector<FocusModeTask>& tasks) = 0;
    virtual void OnTaskCompleted(const FocusModeTask& completed_task) = 0;
  };

  // Interface to enable the model to propagate updates/requests to the server.
  class Delegate {
   public:
    // Fetch a single task by id. Primarily used for tasks loaded from
    // preferences.
    using FetchTaskCallback =
        base::OnceCallback<void(const std::optional<FocusModeTask>& task)>;
    virtual void FetchTask(const TaskId& task_id,
                           FetchTaskCallback callback) = 0;

    // Initiate a request for tasks. Implementer is expected to call
    // `SetTaskList()` if this is successful.
    virtual void FetchTasks() = 0;

    // Initiate a request to add the task specified by `update`. Set a callback
    // to update the id.
    virtual void AddTask(const TaskUpdate& update,
                         FetchTaskCallback callback) = 0;

    // Update an existing task described by `update`.
    virtual void UpdateTask(const TaskUpdate& update) = 0;
  };

  FocusModeTasksModel();

  FocusModeTasksModel(const FocusModeTasksModel&) = delete;
  FocusModeTasksModel& operator=(const FocusModeTasksModel&) = delete;

  ~FocusModeTasksModel();

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  void SetDelegate(base::WeakPtr<Delegate> delegate);

  // Initiate a request for updated task data from the server.
  void RequestUpdate();

  // Mark the task with `task_id` as the selected task. If it is different than
  // the currently selected task, it will cause an `OnSelectedTaskChanged()`
  // event.
  bool SetSelectedTask(const TaskId& task_id);

  // Select `task` as the selected task. Will attempt to find the task in the
  // task list by matching the id. If it cannot be found, the task will be added
  // to the list and submitted as a new task to the server.
  void SetSelectedTask(const FocusModeTask& task);

  // Set the selected task from preferences which may be partially constructed.
  // This may trigger multiple events if the downloaded task data updates
  // `task`.
  void SetSelectedTaskFromPrefs(const TaskId& task);

  // Clears the selected task and triggers `Observer::OnSelectedTaskChanged()`
  // if appropriate.
  void ClearSelectedTask();

  // Clears all the cached tasks data immediately. Will also notify observers of
  // this change.
  void Reset();

  // Set the current task list as `tasks`. This will trigger an
  // `OnTasksUpdated()` event. If this causes a change to the selected task, may
  // cause an `OnSelectedTaskChanged()` event.
  void SetTaskList(std::vector<FocusModeTask>&& tasks);

  // Updates the task with the matching `task_id` with the information from
  // `task_update`. If the task is the selected task, this will cause an
  // `OnSelectedTaskChanged()` event. This does NOT trigger `OnTasksUpdated()`.
  void UpdateTask(const TaskUpdate& task_update);

  const std::vector<FocusModeTask>& tasks() const;
  const FocusModeTask* selected_task() const;

  const TaskId& PrefTaskIdForTesting() const;

 private:
  void OnTaskAdded(const std::optional<FocusModeTask>& fetched_task);

  void OnPrefTaskFetched(const std::optional<FocusModeTask>& fetched_task);

  void OnSelectedTaskFetched(const std::optional<FocusModeTask>& fetched_task);

  // Inserts `task` at the front of `tasks_` then resets `selected_task_` and
  // `pending_task_` pointers to the matching tasks in `tasks_`.
  FocusModeTask* InsertTaskIntoTaskList(FocusModeTask&& task);

  base::WeakPtr<Delegate> delegate_;

  // A partially populated task set from preferences while we wait to finish
  // fetching tasks.
  std::optional<TaskId> pref_task_id_;

  std::vector<FocusModeTask> tasks_;
  raw_ptr<FocusModeTask> selected_task_;

  // A task that was created by the user but has not been synced to the server
  // so it does not have TaskId.
  raw_ptr<FocusModeTask> pending_task_;

  base::ObserverList<Observer> observers_;

  base::WeakPtrFactory<FocusModeTasksModel> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_SYSTEM_FOCUS_MODE_FOCUS_MODE_TASKS_MODEL_H_