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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user