1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187

ash / glanceables / tasks / glanceables_task_view.h [blame]

// Copyright 2023 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_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_
#define ASH_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_

#include <string>

#include "ash/api/tasks/tasks_client.h"
#include "ash/api/tasks/tasks_types.h"
#include "ash/ash_export.h"
#include "ash/glanceables/tasks/glanceables_tasks_error_type.h"
#include "ash/style/error_message_toast.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "google_apis/common/api_error_codes.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/view_observer.h"

namespace views {
class ImageButton;
class ImageView;
class LabelButton;
}  // namespace views

namespace ash {

class SystemTextfield;

// `GlanceablesTaskView` uses `views::FlexLayout` to show tasks metadata within
// the `GlanceablesTasksView`.
// +---------------------------------------------------------------+
// |`GlanceablesTaskView`                                        |
// |                                                               |
// | +-----------------+ +---------------------------------------+ |
// | |'check_button_'  | |'contents_view_'                       | |
// | |                 | | +-----------------------------------+ | |
// | |                 | | |'tasks_title_view_'                | | |
// | |                 | | +-----------------------------------+ | |
// | |                 | | +-----------------------------------+ | |
// | |                 | | |'tasks_details_view_'              | | |
// | |                 | | +-----------------------------------+ | |
// | +-----------------+ +---------------------------------------+ |
// +---------------------------------------------------------------+
class ASH_EXPORT GlanceablesTaskView : public views::FlexLayoutView,
                                       public views::ViewObserver {
  METADATA_HEADER(GlanceablesTaskView, views::FlexLayoutView)

 public:
  using MarkAsCompletedCallback =
      base::RepeatingCallback<void(const std::string& task_id, bool completed)>;
  using SaveCallback = base::RepeatingCallback<void(
      base::WeakPtr<GlanceablesTaskView> view,
      const std::string& task_id,
      const std::string& title,
      api::TasksClient::OnTaskSavedCallback callback)>;
  using ShowErrorMessageCallback =
      base::RepeatingCallback<void(GlanceablesTasksErrorType,
                                   ErrorMessageToast::ButtonActionType)>;
  using StateChangeObserverCallback =
      base::RepeatingCallback<void(bool view_expanding)>;
  // Modes of `tasks_title_view_` (simple label or text field).
  enum class TaskTitleViewState { kNotInitialized, kView, kEdit };

  GlanceablesTaskView(const api::Task* task,
                      MarkAsCompletedCallback mark_as_completed_callback,
                      SaveCallback save_callback,
                      base::RepeatingClosure edit_in_browser_callback,
                      ShowErrorMessageCallback show_error_message_callback);
  GlanceablesTaskView(const GlanceablesTaskView&) = delete;
  GlanceablesTaskView& operator=(const GlanceablesTaskView&) = delete;
  ~GlanceablesTaskView() override;

  void set_state_change_observer(const StateChangeObserverCallback& observer) {
    state_change_observer_ = observer;
  }

  // views::ViewObserver:
  void OnViewBlurred(views::View* observed_view) override;
  void OnViewIsDeleting(views::View* observed_view) override;

  const views::ImageButton* GetCheckButtonForTest() const;
  bool GetCompletedForTest() const;
  void SetCheckedForTest(bool checked);

  // Updates `tasks_title_view_` according to `state`.
  void UpdateTaskTitleViewForState(TaskTitleViewState state);

  // Sets the network to be connected. This should only be used in tests.
  static void SetIsNetworkConnectedForTest(bool connected);

 private:
  class CheckButton;
  class TaskTitleButton;

  // Adds additional content (assigned task privacy notice if available + edit
  // in browser button) when the view is in edit mode.
  void AddExtraContentForEditState();

  // Updates the margins of views in `contents_view_`.
  void UpdateContentsMargins(TaskTitleViewState state);

  // Updates the `extra_content_for_edit_state_` visibility animating its
  // opacity. When hiding the container, the container is hidden immediately,
  // but copy of its layer is created, and animated in its place.
  void AnimateExtraContentForEditStateVisibility(bool visible);
  void OnExtraContentForEditStateVisibilityAnimationCompleted();

  // Handles press events on `check_button_`.
  void CheckButtonPressed();

  // Handles press events on `task_title_button_`.
  void TaskTitleButtonPressed();

  // Handles finished editing event from the text field, updates `task_title_`
  // and propagates new `title` to the server.
  void OnFinishedEditing(const std::u16string& title);

  // Handles completion of running `save_callback_` callback.
  // `task` - newly created or updated task.
  void OnSaved(google_apis::ApiErrorCode http_error, const api::Task* task);

  // Owned by views hierarchy.
  raw_ptr<CheckButton> check_button_ = nullptr;
  raw_ptr<views::FlexLayoutView> contents_view_ = nullptr;
  raw_ptr<views::FlexLayoutView> tasks_title_view_ = nullptr;
  raw_ptr<TaskTitleButton> task_title_button_ = nullptr;
  raw_ptr<SystemTextfield> task_title_textfield_ = nullptr;
  raw_ptr<views::FlexLayoutView> tasks_details_view_ = nullptr;
  raw_ptr<views::ImageView> origin_surface_type_icon_ = nullptr;
  raw_ptr<views::View> extra_content_for_edit_state_ = nullptr;
  raw_ptr<views::LabelButton> edit_in_browser_button_ = nullptr;

  // ID for the task represented by this view.
  std::string task_id_;

  // Title of the task.
  std::u16string task_title_;

  // The type of surface a task originates from.
  api::Task::OriginSurfaceType origin_surface_type_;

  // Cached to reset the value of `task_title_` when the new title failed to
  // commit after editing.
  std::u16string task_title_before_edit_ = u"";

  bool saving_task_changes_ = false;

  // Marks the task as completed.
  const MarkAsCompletedCallback mark_as_completed_callback_;

  // Saves the task (either creates or updates the existing one).
  const SaveCallback save_callback_;

  // `edit_in_browser_button_` callback that opens the Tasks in browser.
  const base::RepeatingClosure edit_in_browser_callback_;

  // Shows an error message in the parent `GlanceablesTasksView`.
  const ShowErrorMessageCallback show_error_message_callback_;

  // Callback which, if set, gets called when the task view state changes
  // between editing and viewing state.
  StateChangeObserverCallback state_change_observer_;

  // Copy of `extra_content_for_edit_state_` (assigned task privacy notice if
  // available + edit in browser button) layer used for the visibility animation
  // when hiding the container. The container gets hidden immediately, and the
  // layer is faded out in its place. Deleted when the fade out animation
  // completes.
  std::unique_ptr<ui::Layer> animating_extra_content_for_edit_state_layer_;

  base::ScopedMultiSourceObservation<views::View, GlanceablesTaskView>
      edit_exit_observer_{this};

  base::WeakPtrFactory<GlanceablesTaskView> state_change_weak_ptr_factory_{
      this};

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

}  // namespace ash

#endif  // ASH_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_