use super::{Open, Refusal};
use crate::core::entity::{CustomViewId, Page, Project, TeamId};
use crate::core::store::{List, ListOf, Store};
#[derive(Debug, Clone, PartialEq)]
pub enum Request {
TeamProjects {
team_id: TeamId,
after: Option<String>,
},
ViewProjects {
view_id: CustomViewId,
after: Option<String>,
},
OpenInBrowser(String),
}
impl Request {
pub fn cursor(&self) -> Option<&str> {
match self {
Self::TeamProjects { after, .. } | Self::ViewProjects { after, .. } => after.as_deref(),
Self::OpenInBrowser(_) => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Projects {
Team,
View,
}
impl Projects {
fn list(self) -> List {
match self {
Self::Team => List::Projects,
Self::View => List::ViewProjects,
}
}
}
pub fn open_team_projects(store: &mut Store, team_id: TeamId) -> Open {
store
.open_list(List::Projects, ListOf::TeamProjects(team_id))
.into()
}
pub fn open_view_projects(store: &mut Store, view_id: CustomViewId) -> Open {
store
.open_list(List::ViewProjects, ListOf::ViewProjects(view_id))
.into()
}
pub fn next_page(store: &mut Store, projects: Projects) -> Option<super::Request> {
store.next_page(projects.list()).map(super::Request::page)
}
pub fn reload(store: &mut Store, projects: Projects) -> Option<super::Request> {
store.reload_list(projects.list()).map(super::Request::page)
}
pub fn open_url(url: Option<String>) -> Result<Request, Refusal> {
url.map(Request::OpenInBrowser)
.ok_or(Refusal::NothingToOpen)
}
pub fn take_page(store: &mut Store, projects: Projects, of: &ListOf, page: Page<Project>) -> bool {
match projects {
Projects::Team => store.projects.accept_for(of, page),
Projects::View => store.view_projects.accept_for(of, page),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn project(id: &str) -> Project {
serde_json::from_str(&format!(r#"{{"id":"{id}","name":"{id}"}}"#)).unwrap()
}
#[test]
fn a_project_opens_by_its_url() {
assert_eq!(
open_url(Some("https://linear.app/p".into())),
Ok(Request::OpenInBrowser("https://linear.app/p".into()))
);
assert_eq!(open_url(None), Err(Refusal::NothingToOpen));
}
#[test]
fn a_teams_projects_are_fetched_once() {
let mut store = Store::default();
assert_eq!(
open_team_projects(&mut store, TeamId::from("t")).request(),
Some(crate::core::usecase::Request::Project(
Request::TeamProjects {
team_id: "t".into(),
after: None
}
))
);
let of = ListOf::TeamProjects(TeamId::from("t"));
take_page(
&mut store,
Projects::Team,
&of,
Page::new(vec![project("p")], Default::default(), false),
);
assert_eq!(
open_team_projects(&mut store, TeamId::from("t")),
Open::Cached
);
}
#[test]
fn projects_for_a_team_left_behind_are_dropped() {
let mut store = Store::default();
open_team_projects(&mut store, TeamId::from("u"));
let of = ListOf::TeamProjects(TeamId::from("t"));
let page = Page::new(vec![project("p")], Default::default(), false);
assert!(!take_page(&mut store, Projects::Team, &of, page));
}
#[test]
fn a_project_view_lists_its_own_projects() {
let mut store = Store::default();
assert_eq!(
open_view_projects(&mut store, CustomViewId::from("v")).request(),
Some(crate::core::usecase::Request::Project(
Request::ViewProjects {
view_id: "v".into(),
after: None
}
))
);
assert!(store.projects.of.is_none());
assert_eq!(
reload(&mut store, Projects::View),
Some(crate::core::usecase::Request::Project(
Request::ViewProjects {
view_id: "v".into(),
after: None
}
))
);
assert_eq!(next_page(&mut store, Projects::View), None);
}
#[test]
fn a_teams_projects_reload_from_the_first_page() {
let mut store = Store::default();
open_team_projects(&mut store, TeamId::from("t"));
assert_eq!(
reload(&mut store, Projects::Team),
Some(crate::core::usecase::Request::Project(
Request::TeamProjects {
team_id: "t".into(),
after: None
}
))
);
assert_eq!(next_page(&mut store, Projects::Team), None);
}
}