use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use chrono::{Duration, Utc};
use leptos::prelude::*;
use orbital_paging::Page;
use crate::engine::{apply_filter, apply_sort};
use crate::types::{
HistoryActor, HistoryAttachment, HistoryChange, HistoryCitation, HistoryEntry,
HistoryFetchParams, HistoryFieldDiff, HistoryLocale, HistoryMention, HistoryPageFetcher,
};
pub fn sample_entries() -> Vec<HistoryEntry> {
let now = Utc::now();
vec![
HistoryEntry {
id: "1".into(),
kind: "field_diff".into(),
changed_at: now - Duration::minutes(15),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: Some("/users/u1".into()),
},
change: HistoryChange::FieldDiff {
field: "name".into(),
old_value: "Acme".into(),
new_value: "Acme Corp".into(),
},
},
HistoryEntry {
id: "2".into(),
kind: "created".into(),
changed_at: now - Duration::hours(3),
actor: HistoryActor::System,
change: HistoryChange::Created,
},
HistoryEntry {
id: "3".into(),
kind: "deleted".into(),
changed_at: now - Duration::days(1),
actor: HistoryActor::User {
id: "u2".into(),
display_name: "Sam Rivera".into(),
href: None,
},
change: HistoryChange::Deleted {
label: "Draft note".into(),
},
},
]
}
pub fn empty_entries() -> Vec<HistoryEntry> {
vec![]
}
pub fn long_value_entries() -> Vec<HistoryEntry> {
let now = Utc::now();
let long = "x".repeat(120);
vec![HistoryEntry {
id: "long-1".into(),
kind: "field_diff".into(),
changed_at: now,
actor: HistoryActor::System,
change: HistoryChange::FieldDiff {
field: "description".into(),
old_value: long.clone(),
new_value: format!("{long}-new"),
},
}]
}
pub fn multi_kind_entries() -> Vec<HistoryEntry> {
let mut entries = sample_entries();
entries.insert(
0,
HistoryEntry {
id: "comment-1".into(),
kind: "comment".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Custom {
summary: "Left a comment".into(),
},
},
);
entries
}
pub fn multi_diff_entries() -> Vec<HistoryEntry> {
vec![HistoryEntry {
id: "md-1".into(),
kind: "field_diffs".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::FieldDiffs {
fields: vec![
HistoryFieldDiff {
field: "status".into(),
old_value: "open".into(),
new_value: "closed".into(),
},
HistoryFieldDiff {
field: "owner".into(),
old_value: "A".into(),
new_value: "B".into(),
},
],
},
}]
}
pub fn bucket_span_entries() -> Vec<HistoryEntry> {
let now = Utc::now();
vec![
entry("b1", now, HistoryChange::Created),
entry("b2", now - Duration::days(1), HistoryChange::Created),
entry("b3", now - Duration::days(4), HistoryChange::Created),
entry("b4", now - Duration::days(12), HistoryChange::Created),
entry("b5", now - Duration::days(40), HistoryChange::Created),
]
}
fn entry(id: &str, at: chrono::DateTime<Utc>, change: HistoryChange) -> HistoryEntry {
HistoryEntry {
id: id.into(),
kind: "created".into(),
changed_at: at,
actor: HistoryActor::System,
change,
}
}
fn all_fixture_pages() -> Vec<HistoryEntry> {
let mut entries = sample_entries();
entries.extend(bucket_span_entries());
for i in 0..30 {
entries.push(HistoryEntry {
id: format!("page-{i}"),
kind: "field_diff".into(),
changed_at: Utc::now() - Duration::minutes(i as i64 * 10),
actor: HistoryActor::System,
change: HistoryChange::FieldDiff {
field: "counter".into(),
old_value: format!("{i}"),
new_value: format!("{}", i + 1),
},
});
}
entries
}
async fn mock_paged_history(
params: HistoryFetchParams,
) -> Result<Page<HistoryEntry>, ServerFnError> {
let mut all = apply_sort(&all_fixture_pages(), params.sort);
let locale = HistoryLocale::english();
all = apply_filter(&all, ¶ms.filter, &locale);
let offset = params.page.offset as usize;
let limit = params.page.limit as usize;
let slice: Vec<_> = all.iter().skip(offset).take(limit).cloned().collect();
let next = offset + slice.len();
let has_more = next < all.len();
Ok(Page {
items: slice,
has_more,
total_count: Some(all.len() as u64),
next_request_offset: has_more.then_some(next as u32),
})
}
pub fn mock_page_fetcher() -> HistoryPageFetcher {
Arc::new(|params: HistoryFetchParams| {
Box::pin(mock_paged_history(params))
as Pin<Box<dyn Future<Output = Result<Page<HistoryEntry>, ServerFnError>> + Send>>
})
}
pub fn large_client_entries() -> Vec<HistoryEntry> {
let now = Utc::now();
(0..80)
.map(|i| HistoryEntry {
id: format!("large-{i}"),
kind: "field_diff".into(),
changed_at: now - Duration::minutes(i),
actor: HistoryActor::System,
change: HistoryChange::FieldDiff {
field: "n".into(),
old_value: format!("{i}"),
new_value: format!("{}", i + 1),
},
})
.collect()
}
pub fn markdown_citation_entry() -> HistoryEntry {
HistoryEntry {
id: "md-cite".into(),
kind: "comment".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Markdown {
body: "See [^audit-1] for the audit trail.".into(),
citations: vec![HistoryCitation {
id: "audit-1".into(),
display_index: 1,
}],
mentions: vec![],
attachments: vec![],
},
}
}
pub fn markdown_mention_entry() -> HistoryEntry {
HistoryEntry {
id: "md-mention".into(),
kind: "comment".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Markdown {
body: "Assigned to @[Jordan Lee](u1) for review.".into(),
citations: vec![],
mentions: vec![HistoryMention {
id: "u1".into(),
display_name: "Jordan Lee".into(),
avatar_src: Some("https://i.pravatar.cc/150?img=12".into()),
subtitle: Some("Engineer".into()),
}],
attachments: vec![],
},
}
}
pub fn markdown_image_entry() -> HistoryEntry {
HistoryEntry {
id: "md-image".into(),
kind: "comment".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Markdown {
body: "Uploaded ".into(),
citations: vec![],
mentions: vec![],
attachments: vec![HistoryAttachment {
url: "https://picsum.photos/seed/orbital-history-screenshot/640/360".into(),
name: Some("screenshot.jpg".into()),
mime: Some("image/jpeg".into()),
}],
},
}
}
pub fn grouped_actor_entries() -> Vec<HistoryEntry> {
let now = Utc::now();
(0..4)
.map(|i| HistoryEntry {
id: format!("group-a-{i}"),
kind: if i % 2 == 0 {
"comment".into()
} else {
"updated".into()
},
changed_at: now - Duration::minutes(i),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Created,
})
.collect()
}
pub fn markdown_entry() -> HistoryEntry {
HistoryEntry {
id: "md-body".into(),
kind: "comment".into(),
changed_at: Utc::now(),
actor: HistoryActor::User {
id: "u1".into(),
display_name: "Jordan Lee".into(),
href: None,
},
change: HistoryChange::Markdown {
body: "**Updated** the [design doc](https://example.com)".into(),
citations: vec![],
mentions: vec![],
attachments: vec![],
},
}
}