1use std::{fmt::Display, sync::Arc};
2use synd_auth::device_flow::DeviceAuthorizationResponse;
3use synd_feed::types::{Category, FeedUrl};
4
5use crate::{
6 application::{Direction, Populate, RequestSequence},
7 auth::{AuthenticationProvider, Credential, Verified},
8 client::{
9 github::{FetchNotificationsParams, GithubError},
10 synd_api::{
11 mutation::subscribe_feed::SubscribeFeedInput, payload,
12 query::subscription::SubscriptionOutput, SyndApiError,
13 },
14 },
15 types::{
16 github::{
17 IssueContext, IssueOrPullRequest, Notification, NotificationId, PullRequestContext,
18 PullRequestState, Reason,
19 },
20 Feed,
21 },
22 ui::components::{filter::FilterLane, gh_notifications::GhNotificationFilterUpdater},
23};
24
25#[derive(Debug, Clone)]
26pub(crate) enum ApiResponse {
27 DeviceFlowAuthorization {
28 provider: AuthenticationProvider,
29 device_authorization: DeviceAuthorizationResponse,
30 },
31 DeviceFlowCredential {
32 credential: Verified<Credential>,
33 },
34 SubscribeFeed {
35 feed: Box<Feed>,
36 },
37 UnsubscribeFeed {
38 url: FeedUrl,
39 },
40 FetchSubscription {
41 populate: Populate,
42 subscription: SubscriptionOutput,
43 },
44 FetchEntries {
45 populate: Populate,
46 payload: payload::FetchEntriesPayload,
47 },
48 FetchGithubNotifications {
49 populate: Populate,
50 notifications: Vec<Notification>,
51 },
52 FetchGithubIssue {
53 notification_id: NotificationId,
54 issue: IssueContext,
55 },
56 FetchGithubPullRequest {
57 notification_id: NotificationId,
58 pull_request: PullRequestContext,
59 },
60 MarkGithubNotificationAsDone {
61 notification_id: NotificationId,
62 },
63 UnsubscribeGithubThread {},
64}
65
66impl Display for ApiResponse {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 ApiResponse::DeviceFlowCredential { .. } => f.write_str("DeviceFlowCredential"),
70 ApiResponse::FetchSubscription { .. } => f.write_str("FetchSubscription"),
71 ApiResponse::FetchEntries { .. } => f.write_str("FetchEntries"),
72 ApiResponse::FetchGithubNotifications { .. } => f.write_str("FetchGithubNotifications"),
73 ApiResponse::FetchGithubIssue { .. } => f.write_str("FetchGithubIssue"),
74 ApiResponse::FetchGithubPullRequest { .. } => f.write_str("FetchGithubPullRequest"),
75 cmd => write!(f, "{cmd:?}"),
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
81pub(crate) enum Command {
82 Nop,
83 Quit,
84 ResizeTerminal {
85 _columns: u16,
86 _rows: u16,
87 },
88 RenderThrobber,
89 Idle,
90
91 Authenticate,
92 MoveAuthenticationProvider(Direction),
93
94 HandleApiResponse {
95 request_seq: RequestSequence,
96 response: ApiResponse,
97 },
98
99 RefreshCredential {
100 credential: Verified<Credential>,
101 },
102
103 MoveTabSelection(Direction),
104
105 MoveSubscribedFeed(Direction),
107 MoveSubscribedFeedFirst,
108 MoveSubscribedFeedLast,
109 PromptFeedSubscription,
110 PromptFeedEdition,
111 PromptFeedUnsubscription,
112 MoveFeedUnsubscriptionPopupSelection(Direction),
113 SelectFeedUnsubscriptionPopup,
114 CancelFeedUnsubscriptionPopup,
115 SubscribeFeed {
116 input: SubscribeFeedInput,
117 },
118 FetchSubscription {
119 after: Option<String>,
120 first: i64,
121 },
122 ReloadSubscription,
123 OpenFeed,
124
125 FetchEntries {
127 after: Option<String>,
128 first: i64,
129 },
130 ReloadEntries,
131 MoveEntry(Direction),
132 MoveEntryFirst,
133 MoveEntryLast,
134 OpenEntry,
135 BrowseEntry,
136
137 MoveFilterRequirement(Direction),
139 ActivateCategoryFilterling,
140 ActivateSearchFiltering,
141 PromptChanged,
142 DeactivateFiltering,
143 ToggleFilterCategory {
144 lane: FilterLane,
145 category: Category<'static>,
146 },
147 ActivateAllFilterCategories {
148 lane: FilterLane,
149 },
150 DeactivateAllFilterCategories {
151 lane: FilterLane,
152 },
153
154 RotateTheme,
156
157 InformLatestRelease(update_informer::Version),
159
160 FetchGhNotifications {
162 populate: Populate,
163 params: FetchNotificationsParams,
164 },
165 FetchGhNotificationDetails {
166 contexts: Vec<IssueOrPullRequest>,
167 },
168 MoveGhNotification(Direction),
169 MoveGhNotificationFirst,
170 MoveGhNotificationLast,
171 OpenGhNotification {
172 with_mark_as_done: bool,
173 },
174 ReloadGhNotifications,
175 MarkGhNotificationAsDone {
176 all: bool,
177 },
178 UnsubscribeGhThread,
179 OpenGhNotificationFilterPopup,
180 CloseGhNotificationFilterPopup,
181 UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater),
182
183 HandleError {
185 message: String,
186 },
187 HandleApiError {
188 error: Arc<SyndApiError>,
190 request_seq: RequestSequence,
191 },
192 HandleOauthApiError {
193 error: Arc<anyhow::Error>,
195 request_seq: RequestSequence,
196 },
197 HandleGithubApiError {
198 error: Arc<GithubError>,
200 request_seq: RequestSequence,
201 },
202}
203
204impl Display for Command {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 match self {
207 Command::HandleApiResponse { response, .. } => response.fmt(f),
208 Command::FetchGhNotificationDetails { .. } => f
209 .debug_struct("FetchGhNotificationDetails")
210 .finish_non_exhaustive(),
211 _ => write!(f, "{self:?}"),
212 }
213 }
214}
215
216impl Command {
217 pub(crate) fn api_error(error: SyndApiError, request_seq: RequestSequence) -> Self {
218 Command::HandleApiError {
219 error: Arc::new(error),
220 request_seq,
221 }
222 }
223 pub(crate) fn oauth_api_error(error: anyhow::Error, request_seq: RequestSequence) -> Self {
224 Command::HandleOauthApiError {
225 error: Arc::new(error),
226 request_seq,
227 }
228 }
229}
230
231impl Command {
232 pub fn quit() -> Self {
233 Command::Quit
234 }
235 pub fn authenticate() -> Self {
236 Command::Authenticate
237 }
238 pub fn move_right_tab_selection() -> Self {
239 Command::MoveTabSelection(Direction::Right)
240 }
241 pub fn move_left_tab_selection() -> Self {
242 Command::MoveTabSelection(Direction::Left)
243 }
244 pub fn move_up_authentication_provider() -> Self {
245 Command::MoveAuthenticationProvider(Direction::Up)
246 }
247 pub fn move_down_authentication_provider() -> Self {
248 Command::MoveAuthenticationProvider(Direction::Down)
249 }
250 pub fn move_up_entry() -> Self {
251 Command::MoveEntry(Direction::Up)
252 }
253 pub fn move_down_entry() -> Self {
254 Command::MoveEntry(Direction::Down)
255 }
256 pub fn reload_entries() -> Self {
257 Command::ReloadEntries
258 }
259 pub fn open_entry() -> Self {
260 Command::OpenEntry
261 }
262 pub fn browse_entry() -> Self {
263 Command::BrowseEntry
264 }
265 pub fn move_entry_first() -> Self {
266 Command::MoveEntryFirst
267 }
268 pub fn move_entry_last() -> Self {
269 Command::MoveEntryLast
270 }
271 pub fn prompt_feed_subscription() -> Self {
272 Command::PromptFeedSubscription
273 }
274 pub fn prompt_feed_edition() -> Self {
275 Command::PromptFeedEdition
276 }
277 pub fn prompt_feed_unsubscription() -> Self {
278 Command::PromptFeedUnsubscription
279 }
280 pub fn move_feed_unsubscription_popup_selection_left() -> Self {
281 Command::MoveFeedUnsubscriptionPopupSelection(Direction::Left)
282 }
283 pub fn move_feed_unsubscription_popup_selection_right() -> Self {
284 Command::MoveFeedUnsubscriptionPopupSelection(Direction::Right)
285 }
286 pub fn select_feed_unsubscription_popup() -> Self {
287 Command::SelectFeedUnsubscriptionPopup
288 }
289 pub fn cancel_feed_unsubscription_popup() -> Self {
290 Command::CancelFeedUnsubscriptionPopup
291 }
292 pub fn move_up_subscribed_feed() -> Self {
293 Command::MoveSubscribedFeed(Direction::Up)
294 }
295 pub fn move_down_subscribed_feed() -> Self {
296 Command::MoveSubscribedFeed(Direction::Down)
297 }
298 pub fn reload_subscription() -> Self {
299 Command::ReloadSubscription
300 }
301 pub fn open_feed() -> Self {
302 Command::OpenFeed
303 }
304 pub fn move_subscribed_feed_first() -> Self {
305 Command::MoveSubscribedFeedFirst
306 }
307 pub fn move_subscribed_feed_last() -> Self {
308 Command::MoveSubscribedFeedLast
309 }
310 pub fn move_filter_requirement_left() -> Self {
311 Command::MoveFilterRequirement(Direction::Left)
312 }
313 pub fn move_filter_requirement_right() -> Self {
314 Command::MoveFilterRequirement(Direction::Right)
315 }
316 pub fn activate_category_filtering() -> Self {
317 Command::ActivateCategoryFilterling
318 }
319 pub fn activate_search_filtering() -> Self {
320 Command::ActivateSearchFiltering
321 }
322 pub fn deactivate_filtering() -> Self {
323 Command::DeactivateFiltering
324 }
325 pub fn rotate_theme() -> Self {
326 Command::RotateTheme
327 }
328 pub fn move_up_gh_notification() -> Self {
329 Command::MoveGhNotification(Direction::Up)
330 }
331 pub fn move_down_gh_notification() -> Self {
332 Command::MoveGhNotification(Direction::Down)
333 }
334 pub fn move_gh_notification_first() -> Self {
335 Command::MoveGhNotificationFirst
336 }
337 pub fn move_gh_notification_last() -> Self {
338 Command::MoveGhNotificationLast
339 }
340 pub fn open_gh_notification() -> Self {
341 Command::OpenGhNotification {
342 with_mark_as_done: false,
343 }
344 }
345 pub fn open_gh_notification_with_done() -> Self {
346 Command::OpenGhNotification {
347 with_mark_as_done: true,
348 }
349 }
350 pub fn reload_gh_notifications() -> Self {
351 Command::ReloadGhNotifications
352 }
353 pub fn mark_gh_notification_as_done() -> Self {
354 Command::MarkGhNotificationAsDone { all: false }
355 }
356 pub fn mark_gh_notification_as_done_all() -> Self {
357 Command::MarkGhNotificationAsDone { all: true }
358 }
359 pub fn unsubscribe_gh_thread() -> Self {
360 Command::UnsubscribeGhThread
361 }
362 pub fn open_gh_notification_filter_popup() -> Self {
363 Command::OpenGhNotificationFilterPopup
364 }
365 pub fn close_gh_notification_filter_popup() -> Self {
366 Command::CloseGhNotificationFilterPopup
367 }
368 pub fn toggle_gh_notification_filter_popup_include_unread() -> Self {
369 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
370 toggle_include: true,
371 ..Default::default()
372 })
373 }
374 pub fn toggle_gh_notification_filter_popup_participating() -> Self {
375 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
376 toggle_participating: true,
377 ..Default::default()
378 })
379 }
380 pub fn toggle_gh_notification_filter_popup_visibility_public() -> Self {
381 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
382 toggle_visilibty_public: true,
383 ..Default::default()
384 })
385 }
386 pub fn toggle_gh_notification_filter_popup_visibility_private() -> Self {
387 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
388 toggle_visilibty_private: true,
389 ..Default::default()
390 })
391 }
392 pub fn toggle_gh_notification_filter_popup_pr_open() -> Self {
393 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
394 toggle_pull_request_condition: Some(PullRequestState::Open),
395 ..Default::default()
396 })
397 }
398 pub fn toggle_gh_notification_filter_popup_pr_closed() -> Self {
399 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
400 toggle_pull_request_condition: Some(PullRequestState::Closed),
401 ..Default::default()
402 })
403 }
404 pub fn toggle_gh_notification_filter_popup_pr_merged() -> Self {
405 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
406 toggle_pull_request_condition: Some(PullRequestState::Merged),
407 ..Default::default()
408 })
409 }
410 pub fn toggle_gh_notification_filter_popup_reason_mentioned() -> Self {
411 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
412 toggle_reason: Some(Reason::Mention),
413 ..Default::default()
414 })
415 }
416 pub fn toggle_gh_notification_filter_popup_reason_review() -> Self {
417 Command::UpdateGhnotificationFilterPopupOptions(GhNotificationFilterUpdater {
418 toggle_reason: Some(Reason::ReviewRequested),
419 ..Default::default()
420 })
421 }
422}