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
//! How the screen is shaped: cursors, filters, what is open over the content.
use std::collections::HashSet;
use std::time::Instant;
use super::{
CommentTarget, Input, InputMode, IssueList, IssueLists, NewIssueForm, PerSource, Popup,
ViewKind,
};
use crate::config::Config;
use crate::core::entity::Issue;
use crate::core::entity::{FavoriteId, IssueId};
use crate::interface::tui::grouping::GroupBy;
#[derive(Debug)]
pub struct ViewState {
pub input_mode: InputMode,
pub popup: Popup,
/// The highlighted row of the open popup, counted in what its query
/// leaves — see `App::popup_rows`.
pub popup_index: usize,
/// What has been typed into the open popup to narrow it.
pub popup_query: Input,
/// Whether the open popup was reached from the command palette, so Esc
/// steps back there instead of closing.
pub popup_from_palette: bool,
/// How each of the five issue lists is shaped.
pub lists: IssueLists,
pub group_by: GroupBy,
/// Group keys the user has folded away.
pub collapsed_groups: HashSet<String>,
/// Which tab the Views pages show.
pub view_kind: ViewKind,
pub selected_view_index: usize,
pub selected_view_project_index: usize,
pub selected_project_index: usize,
pub selected_cycle_index: usize,
pub sidebar: Sidebar,
pub palette: Palette,
pub detail_scroll: u16,
pub comment: Input,
/// What the comment field posts: a comment, a reply, or an edit.
pub comment_target: CommentTarget,
/// The title being typed, while renaming.
pub title: Input,
/// The description being typed, where there is no `$EDITOR`.
pub description: Input,
/// The issue being renamed or described, held by id: a page landing
/// can move the cursor while the field is open.
pub editing: Option<IssueId>,
/// The note being typed, and the issue it is about.
pub note: Input,
pub note_about: Option<crate::core::entity::Subject>,
/// Draft issue, present only while the create form is open.
pub new_issue: Option<NewIssueForm>,
/// First key of a pending multi-key chord (Linear's `g …` sequences).
pub pending_chord: Option<char>,
pub show_help: bool,
pub help_scroll: u16,
pub status_message: Option<String>,
pub error_popup: Option<String>,
pub spinner_frame: usize,
}
#[derive(Debug, Default)]
pub struct Palette {
pub query: Input,
/// The highlighted entry, as an index into what the query matches.
pub selected: usize,
/// The issue under the cursor when the palette opened. A command runs on
/// that issue or not at all — a page landing under the palette can move
/// the cursor to another one.
pub issue: Option<IssueId>,
/// Commands run from the palette, most recent first, by title.
pub recent: Vec<&'static str>,
/// Issues Linear's search returned for the query.
pub results: Vec<Issue>,
/// True while a search for the query is in flight.
pub searching: bool,
/// When the query has rested long enough to be searched.
pub search_due: Option<Instant>,
/// Which search `results` must answer; older answers are dropped.
pub seq: u64,
}
#[derive(Debug)]
pub struct Sidebar {
pub visible: bool,
pub width: u16,
/// True while the cursor is in the sidebar rather than the content pane.
pub focus: bool,
pub index: usize,
/// Favorites folders the user has folded, by favorite id.
pub collapsed_folders: HashSet<FavoriteId>,
}
impl ViewState {
pub fn new(config: &Config) -> Self {
Self {
input_mode: InputMode::Normal,
popup: Popup::None,
popup_index: 0,
popup_query: Input::default(),
popup_from_palette: false,
lists: PerSource::from_fn(IssueList::new),
group_by: GroupBy::from_config(config.ui.group_by),
collapsed_groups: HashSet::new(),
view_kind: ViewKind::default(),
selected_view_index: 0,
selected_view_project_index: 0,
selected_project_index: 0,
selected_cycle_index: 0,
sidebar: Sidebar {
visible: config.ui.sidebar,
width: config.ui.sidebar_width,
focus: false,
index: 0,
collapsed_folders: HashSet::new(),
},
palette: Palette::default(),
detail_scroll: 0,
comment: Input::default(),
comment_target: CommentTarget::New,
title: Input::default(),
description: Input::default(),
editing: None,
note: Input::default(),
note_about: None,
new_issue: None,
pending_chord: None,
show_help: false,
help_scroll: 0,
status_message: None,
error_popup: None,
spinner_frame: 0,
}
}
}