feat(sync): include history in snapshots
This commit is contained in:
@@ -32,6 +32,7 @@ mod splits;
|
||||
mod sync;
|
||||
mod sync_apply;
|
||||
mod sync_context;
|
||||
mod sync_history;
|
||||
mod sync_notes;
|
||||
mod sync_profiles;
|
||||
mod sync_reading_list;
|
||||
|
||||
@@ -31,6 +31,9 @@ impl BrowserCore {
|
||||
for record in body.site_permissions {
|
||||
self.apply_site_permission_sync_record(record, &mut summary, &context)?;
|
||||
}
|
||||
for record in body.history {
|
||||
self.apply_history_sync_record(record, &mut summary, &context)?;
|
||||
}
|
||||
Ok(summary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::{
|
||||
num::NonZeroU32,
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use ely_domain::{HistoryEntry, SyncObjectKind, SyncObjectPolicy, TabId, UrlText};
|
||||
use ely_sync_client::SyncClientError;
|
||||
|
||||
use super::{BrowserCore, sync::snapshot_schema_error, sync_context::SyncSnapshotApplyContext};
|
||||
use crate::{sync_engine::SyncSnapshotApplySummary, sync_records::HistorySyncRecord};
|
||||
|
||||
impl BrowserCore {
|
||||
pub(crate) fn visible_history_for_sync(&self) -> Vec<&HistoryEntry> {
|
||||
if self.sync_object_policy(SyncObjectKind::History) == SyncObjectPolicy::Paused {
|
||||
return Vec::new();
|
||||
}
|
||||
self.history_entries
|
||||
.iter()
|
||||
.filter(|entry| self.profile_allows_cloud_sync(entry.profile_id()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn apply_history_sync_record(
|
||||
&mut self,
|
||||
record: HistorySyncRecord,
|
||||
summary: &mut SyncSnapshotApplySummary,
|
||||
context: &SyncSnapshotApplyContext,
|
||||
) -> Result<(), SyncClientError> {
|
||||
let profile_id = self.sync_profile_id(&record.profile_id, context)?;
|
||||
let space_id = self.sync_space_id(&record.space_id, record.space_name.as_deref())?;
|
||||
let url = UrlText::parse(&record.url).map_err(snapshot_schema_error)?;
|
||||
let source_tab_id = self.resolve_history_source_tab_id(
|
||||
&record.source_tab_id,
|
||||
&profile_id,
|
||||
&space_id,
|
||||
&url,
|
||||
)?;
|
||||
let visited_at = UNIX_EPOCH + Duration::from_secs(record.visited_at_secs);
|
||||
let visit_count = NonZeroU32::new(record.visit_count).ok_or_else(|| {
|
||||
snapshot_schema_error("history visit_count must be greater than zero")
|
||||
})?;
|
||||
let existing_index = self.history_entries.iter().position(|entry| {
|
||||
entry.profile_id() == &profile_id
|
||||
&& entry.space_id() == &space_id
|
||||
&& entry.url() == &url
|
||||
});
|
||||
let entry = HistoryEntry::new(
|
||||
profile_id,
|
||||
space_id,
|
||||
source_tab_id,
|
||||
record.title,
|
||||
url,
|
||||
record.favicon_key,
|
||||
visited_at,
|
||||
);
|
||||
let entry = HistoryEntry::restore(entry, visit_count);
|
||||
|
||||
match existing_index {
|
||||
Some(index) if self.history_entries[index] == entry => summary.record_skipped(),
|
||||
Some(index) => {
|
||||
self.history_entries[index] = entry;
|
||||
summary.record_updated();
|
||||
}
|
||||
None => {
|
||||
self.history_entries.push(entry);
|
||||
summary.record_imported();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_history_source_tab_id(
|
||||
&self,
|
||||
raw: &str,
|
||||
profile_id: &ely_domain::ProfileId,
|
||||
space_id: &ely_domain::SpaceId,
|
||||
url: &UrlText,
|
||||
) -> Result<TabId, SyncClientError> {
|
||||
let remote_tab_id = TabId::parse(raw).map_err(snapshot_schema_error)?;
|
||||
if self.tabs.iter().any(|tab| tab.id() == &remote_tab_id) {
|
||||
return Ok(remote_tab_id);
|
||||
}
|
||||
if let Some(tab) = self.tabs.iter().find(|tab| {
|
||||
tab.profile_id() == profile_id && tab.space_id() == space_id && tab.url() == url
|
||||
}) {
|
||||
return Ok(tab.id().clone());
|
||||
}
|
||||
Ok(remote_tab_id)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use ely_domain::{
|
||||
ArchivePolicy, BookmarkEntry, BrowserTab, NoteEntry, NoteTarget, Profile, ProfileKind,
|
||||
ProfileSyncPolicy, ReadingListEntry, ReadingProgress, SitePermissionEntry, Space, TabFlags,
|
||||
ArchivePolicy, BookmarkEntry, BrowserTab, HistoryEntry, NoteEntry, NoteTarget, Profile,
|
||||
ProfileKind, ProfileSyncPolicy, ReadingListEntry, ReadingProgress, SitePermissionEntry, Space,
|
||||
TabFlags,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -26,6 +27,8 @@ pub(crate) struct SyncSnapshotBody {
|
||||
pub(crate) reading_list: Vec<ReadingListSyncRecord>,
|
||||
#[serde(default)]
|
||||
pub(crate) site_permissions: Vec<SitePermissionSyncRecord>,
|
||||
#[serde(default)]
|
||||
pub(crate) history: Vec<HistorySyncRecord>,
|
||||
}
|
||||
|
||||
impl SyncSnapshotBody {
|
||||
@@ -81,6 +84,13 @@ impl SyncSnapshotBody {
|
||||
.into_iter()
|
||||
.map(SitePermissionSyncRecord::from_entry)
|
||||
.collect(),
|
||||
history: core
|
||||
.visible_history_for_sync()
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
HistorySyncRecord::from_entry(entry, core.sync_space_name_for(entry.space_id()))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,6 +409,37 @@ impl SitePermissionSyncRecord {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct HistorySyncRecord {
|
||||
pub(crate) profile_id: String,
|
||||
pub(crate) space_id: String,
|
||||
#[serde(default)]
|
||||
pub(crate) space_name: Option<String>,
|
||||
pub(crate) source_tab_id: String,
|
||||
pub(crate) title: String,
|
||||
pub(crate) url: String,
|
||||
#[serde(default)]
|
||||
pub(crate) favicon_key: Option<String>,
|
||||
pub(crate) visited_at_secs: u64,
|
||||
pub(crate) visit_count: u32,
|
||||
}
|
||||
|
||||
impl HistorySyncRecord {
|
||||
fn from_entry(entry: &HistoryEntry, space_name: Option<String>) -> Self {
|
||||
Self {
|
||||
profile_id: entry.profile_id().as_str().to_string(),
|
||||
space_id: entry.space_id().as_str().to_string(),
|
||||
space_name,
|
||||
source_tab_id: entry.source_tab_id().as_str().to_string(),
|
||||
title: entry.title().to_string(),
|
||||
url: entry.url().as_str().to_string(),
|
||||
favicon_key: entry.favicon_key().map(str::to_string),
|
||||
visited_at_secs: system_time_secs(entry.visited_at()),
|
||||
visit_count: entry.visit_count(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_sync_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ fn sync_snapshot_imports_remote_bookmarks_into_active_scope() -> Result<(), Box<
|
||||
source.set_bookmark_collection_name(&bookmark_id, "Research")?;
|
||||
source.set_bookmark_tags(&bookmark_id, vec!["rust".to_string(), "gpui".to_string()])?;
|
||||
source.set_bookmark_note(&bookmark_id, "Read later")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -129,6 +130,7 @@ fn sync_snapshot_imports_remote_tabs_into_active_scope() -> Result<(), Box<dyn E
|
||||
source.toggle_active_tab_pinned()?;
|
||||
source.toggle_active_tab_favorite()?;
|
||||
source.set_active_tab_zoom_percent(125)?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -166,6 +168,7 @@ fn sync_snapshot_imports_remote_spaces_before_tabs() -> Result<(), Box<dyn Error
|
||||
let research_home_tab_id = source.snapshot()?.active_tab_id;
|
||||
source.set_tab_sync_enabled(&research_home_tab_id, false)?;
|
||||
source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -203,6 +206,7 @@ fn sync_snapshot_updates_existing_tab_metadata() -> Result<(), Box<dyn Error>> {
|
||||
let source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_title(&source_tab_id, "Research Brief")?;
|
||||
source.set_tab_favicon_key(&source_tab_id, "https://example.com/favicon.ico")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -226,6 +230,7 @@ fn sync_snapshot_omits_paused_tabs() -> Result<(), Box<dyn Error>> {
|
||||
let mut source = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_sync_object_policy(SyncObjectKind::Tabs, SyncObjectPolicy::Paused);
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -250,6 +255,7 @@ fn sync_snapshot_updates_existing_bookmark_metadata() -> Result<(), Box<dyn Erro
|
||||
source.set_bookmark_collection_name(&source_bookmark_id, "Research")?;
|
||||
source.set_bookmark_tags(&source_bookmark_id, vec!["servo".to_string()])?;
|
||||
source.set_bookmark_note(&source_bookmark_id, "Canonical")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -279,6 +285,7 @@ fn sync_snapshot_omits_paused_bookmarks() -> Result<(), Box<dyn Error>> {
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.bookmark_active_tab()?;
|
||||
source.set_sync_object_policy(SyncObjectKind::Bookmarks, SyncObjectPolicy::Paused);
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -301,6 +308,7 @@ fn sync_snapshot_imports_remote_url_notes_into_active_scope() -> Result<(), Box<
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.set_tab_title(&source_tab_id, "Research Brief")?;
|
||||
source.save_active_url_note(" # Finding\r\n- one ")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -332,6 +340,7 @@ fn sync_snapshot_imports_remote_tab_notes_after_tabs() -> Result<(), Box<dyn Err
|
||||
let source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_title(&source_tab_id, "Research Brief")?;
|
||||
source.save_active_tab_note("pinned tab context")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -365,6 +374,7 @@ fn sync_snapshot_updates_existing_note_body() -> Result<(), Box<dyn Error>> {
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.set_tab_title(&source_tab_id, "Research Brief")?;
|
||||
source.save_active_url_note("canonical note")?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -395,6 +405,7 @@ fn sync_snapshot_omits_paused_notes() -> Result<(), Box<dyn Error>> {
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.save_active_url_note("local only")?;
|
||||
source.set_sync_object_policy(SyncObjectKind::Notes, SyncObjectPolicy::Paused);
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -420,3 +431,7 @@ fn sync_snapshot_rejects_unknown_schema_rev() -> Result<(), Box<dyn Error>> {
|
||||
assert!(error.to_string().contains("unsupported schema_rev 999"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause_history_sync(core: &mut BrowserCore) {
|
||||
core.set_sync_object_policy(SyncObjectKind::History, SyncObjectPolicy::Paused);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
use std::error::Error;
|
||||
|
||||
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
||||
use ely_domain::{SyncObjectKind, SyncObjectPolicy, UrlText};
|
||||
|
||||
#[test]
|
||||
fn sync_snapshot_imports_remote_history_into_active_scope() -> Result<(), Box<dyn Error>> {
|
||||
let mut source = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let source_home_tab_id = source.snapshot()?.active_tab_id;
|
||||
source.set_tab_sync_enabled(&source_home_tab_id, false)?;
|
||||
let source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.set_tab_title(&source_tab_id, "Research Brief")?;
|
||||
source.set_tab_favicon_key(&source_tab_id, "favicons/example.ico")?;
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let target_profile_id = target.snapshot()?.active_profile_id;
|
||||
let target_space_id = target.snapshot()?.active_space_id;
|
||||
let summary = target.apply_sync_snapshot_bytes(&bytes)?;
|
||||
let snapshot = target.snapshot()?;
|
||||
let [entry] = snapshot.history_entries.as_slice() else {
|
||||
return Err(
|
||||
format!("expected 1 history entry, got {}", snapshot.history_entries.len()).into()
|
||||
);
|
||||
};
|
||||
|
||||
assert_eq!(summary.imported(), 1);
|
||||
assert_eq!(summary.updated(), 0);
|
||||
assert_eq!(summary.skipped(), 0);
|
||||
assert_eq!(entry.profile_id(), &target_profile_id);
|
||||
assert_eq!(entry.space_id(), &target_space_id);
|
||||
assert_eq!(entry.source_tab_id(), &source_tab_id);
|
||||
assert_eq!(entry.title(), "Research Brief");
|
||||
assert_eq!(entry.url().as_str(), "https://example.com/research");
|
||||
assert_eq!(entry.favicon_key(), Some("favicons/example.ico"));
|
||||
assert_eq!(entry.visit_count(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_snapshot_updates_existing_history_entry() -> Result<(), Box<dyn Error>> {
|
||||
let mut source = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let source_home_tab_id = source.snapshot()?.active_tab_id;
|
||||
source.set_tab_sync_enabled(&source_home_tab_id, false)?;
|
||||
let first_source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_sync_enabled(&first_source_tab_id, false)?;
|
||||
let latest_source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_sync_enabled(&latest_source_tab_id, false)?;
|
||||
source.set_tab_title(&latest_source_tab_id, "Canonical Research")?;
|
||||
source.set_tab_favicon_key(&latest_source_tab_id, "favicons/canonical.ico")?;
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let target_tab_id = target.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
target.set_tab_title(&target_tab_id, "Old Research")?;
|
||||
target.set_tab_favicon_key(&target_tab_id, "favicons/old.ico")?;
|
||||
let summary = target.apply_sync_snapshot_bytes(&bytes)?;
|
||||
let snapshot = target.snapshot()?;
|
||||
let [entry] = snapshot.history_entries.as_slice() else {
|
||||
return Err(
|
||||
format!("expected 1 history entry, got {}", snapshot.history_entries.len()).into()
|
||||
);
|
||||
};
|
||||
|
||||
assert_eq!(summary.imported(), 0);
|
||||
assert_eq!(summary.updated(), 1);
|
||||
assert_eq!(summary.skipped(), 0);
|
||||
assert_eq!(entry.source_tab_id(), &target_tab_id);
|
||||
assert_eq!(entry.title(), "Canonical Research");
|
||||
assert_eq!(entry.favicon_key(), Some("favicons/canonical.ico"));
|
||||
assert_eq!(entry.visit_count(), 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_snapshot_omits_paused_history() -> Result<(), Box<dyn Error>> {
|
||||
let mut source = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let source_home_tab_id = source.snapshot()?.active_tab_id;
|
||||
source.set_tab_sync_enabled(&source_home_tab_id, false)?;
|
||||
let source_tab_id = source.open_tab(UrlText::parse("https://example.com/research")?);
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.set_sync_object_policy(SyncObjectKind::History, SyncObjectPolicy::Paused);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
let summary = target.apply_sync_snapshot_bytes(&bytes)?;
|
||||
let snapshot = target.snapshot()?;
|
||||
|
||||
assert_eq!(summary.imported(), 0);
|
||||
assert_eq!(summary.updated(), 0);
|
||||
assert_eq!(summary.skipped(), 0);
|
||||
assert!(snapshot.history_entries.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
@@ -94,6 +94,13 @@ fn paused_profile_data_is_omitted_from_sync_snapshots() -> Result<(), Box<dyn Er
|
||||
assert_eq!(summary.updated(), 0);
|
||||
assert_eq!(summary.skipped(), 0);
|
||||
assert!(snapshot.profiles.iter().any(|profile| profile.name() == "Research"));
|
||||
let imported_profile_id = snapshot
|
||||
.profiles
|
||||
.iter()
|
||||
.find(|profile| profile.name() == "Research")
|
||||
.ok_or("missing imported profile")?
|
||||
.id()
|
||||
.clone();
|
||||
assert!(
|
||||
snapshot.tabs.iter().all(|tab| tab.url().as_str() != "https://example.com/paused-profile")
|
||||
);
|
||||
@@ -101,5 +108,7 @@ fn paused_profile_data_is_omitted_from_sync_snapshots() -> Result<(), Box<dyn Er
|
||||
assert!(snapshot.notes.is_empty());
|
||||
assert!(snapshot.reading_list.is_empty());
|
||||
assert!(snapshot.site_permissions.is_empty());
|
||||
target.select_profile(&imported_profile_id)?;
|
||||
assert!(target.snapshot()?.history_entries.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ fn sync_snapshot_imports_remote_reading_list_into_active_scope() -> Result<(), B
|
||||
&entry_id,
|
||||
ReadingProgress::InProgress(ReadingProgressPercent::new(42)?),
|
||||
)?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -52,6 +53,7 @@ fn sync_snapshot_updates_existing_reading_list_entry() -> Result<(), Box<dyn Err
|
||||
source.set_tab_title(&source_tab_id, "Canonical Long Read")?;
|
||||
let source_entry_id = source.save_active_tab_to_reading_list()?;
|
||||
source.set_reading_list_progress(&source_entry_id, ReadingProgress::Finished)?;
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -84,6 +86,7 @@ fn sync_snapshot_omits_paused_reading_list() -> Result<(), Box<dyn Error>> {
|
||||
source.set_tab_sync_enabled(&source_tab_id, false)?;
|
||||
source.save_active_tab_to_reading_list()?;
|
||||
source.set_sync_object_policy(SyncObjectKind::ReadingList, SyncObjectPolicy::Paused);
|
||||
pause_history_sync(&mut source);
|
||||
let bytes = source.build_sync_snapshot_bytes()?;
|
||||
|
||||
let mut target = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
@@ -96,3 +99,7 @@ fn sync_snapshot_omits_paused_reading_list() -> Result<(), Box<dyn Error>> {
|
||||
assert!(snapshot.reading_list.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause_history_sync(core: &mut BrowserCore) {
|
||||
core.set_sync_object_policy(SyncObjectKind::History, SyncObjectPolicy::Paused);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::time::SystemTime;
|
||||
use std::{num::NonZeroU32, time::SystemTime};
|
||||
|
||||
use crate::{ProfileId, SpaceId, TabId, UrlText};
|
||||
|
||||
@@ -53,6 +53,12 @@ impl HistoryEntry {
|
||||
self.visit_count = self.visit_count.saturating_add(1);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn restore(mut entry: Self, visit_count: NonZeroU32) -> Self {
|
||||
entry.visit_count = visit_count.get();
|
||||
entry
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn profile_id(&self) -> &ProfileId {
|
||||
&self.profile_id
|
||||
|
||||
Reference in New Issue
Block a user