Skip to content

Commit

Permalink
Merge pull request #562 from Stremio/feat/search-history-bucket
Browse files Browse the repository at this point in the history
feat: implement search history bucket
  • Loading branch information
tymmesyde authored Nov 20, 2023
2 parents ef1e665 + b00a841 commit 75cfb62
Show file tree
Hide file tree
Showing 30 changed files with 387 additions and 7 deletions.
3 changes: 2 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const PROFILE_STORAGE_KEY: &str = "profile";
pub const LIBRARY_STORAGE_KEY: &str = "library";
pub const LIBRARY_RECENT_STORAGE_KEY: &str = "library_recent";
pub const STREAMS_STORAGE_KEY: &str = "streams";
pub const SEARCH_HISTORY_STORAGE_KEY: &str = "search_history";
pub const NOTIFICATIONS_STORAGE_KEY: &str = "notifications";
pub const LIBRARY_COLLECTION_NAME: &str = "libraryItem";
pub const SEARCH_EXTRA_NAME: &str = "search";
Expand All @@ -31,7 +32,7 @@ pub const NOTIFICATION_ITEMS_COUNT: usize = 100;
pub const WATCHED_THRESHOLD_COEF: f64 = 0.7;
pub const CREDITS_THRESHOLD_COEF: f64 = 0.9;
/// The latest migration scheme version
pub const SCHEMA_VERSION: u32 = 9;
pub const SCHEMA_VERSION: u32 = 10;
pub const IMDB_LINK_CATEGORY: &str = "imdb";
pub const GENRES_LINK_CATEGORY: &str = "Genres";
pub const CINEMETA_TOP_CATALOG_ID: &str = "top";
Expand Down
17 changes: 16 additions & 1 deletion src/models/catalogs_with_extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,22 @@ impl<E: Env + 'static> UpdateWithCtx<E> for CatalogsWithExtra {
let selected_effects = selected_update(&mut self.selected, selected);
let catalogs_effects =
catalogs_update::<E>(&mut self.catalogs, &self.selected, None, &ctx.profile);
selected_effects.join(catalogs_effects)
let search_effects = match &self.selected {
Some(Selected { extra, .. }) => match extra
.iter()
.find(|ExtraValue { name, .. }| name == "search")
{
Some(ExtraValue { value, .. }) => {
Effects::msg(Msg::Internal(Internal::CatalogsWithExtraSearch {
query: value.to_owned(),
}))
.unchanged()
}
None => Effects::none().unchanged(),
},
None => Effects::none().unchanged(),
};
selected_effects.join(catalogs_effects).join(search_effects)
}
Msg::Action(Action::Unload) => {
let selected_effects = eq_update(&mut self.selected, None);
Expand Down
18 changes: 16 additions & 2 deletions src/models/ctx/ctx.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::constants::LIBRARY_COLLECTION_NAME;
use crate::models::common::{DescriptorLoadable, ResourceLoadable};
use crate::models::ctx::{
update_library, update_notifications, update_profile, update_streams, update_trakt_addon,
CtxError,
update_library, update_notifications, update_profile, update_search_history, update_streams,
update_trakt_addon, CtxError,
};
use crate::runtime::msg::{Action, ActionCtx, Event, Internal, Msg};
use crate::runtime::{Effect, EffectFuture, Effects, Env, EnvFutureExt, Update};
Expand All @@ -14,6 +14,7 @@ use crate::types::library::LibraryBucket;
use crate::types::notifications::NotificationsBucket;
use crate::types::profile::{Auth, AuthKey, Profile};
use crate::types::resource::MetaItem;
use crate::types::search_history::SearchHistoryBucket;
use crate::types::streams::StreamsBucket;

#[cfg(test)]
Expand Down Expand Up @@ -44,6 +45,8 @@ pub struct Ctx {
#[serde(skip)]
pub streams: StreamsBucket,
#[serde(skip)]
pub search_history: SearchHistoryBucket,
#[serde(skip)]
#[cfg_attr(test, derivative(Default(value = "CtxStatus::Ready")))]
pub status: CtxStatus,
#[serde(skip)]
Expand All @@ -59,11 +62,13 @@ impl Ctx {
library: LibraryBucket,
streams: StreamsBucket,
notifications: NotificationsBucket,
search_history: SearchHistoryBucket,
) -> Self {
Self {
profile,
library,
streams,
search_history,
notifications,
trakt_addon: None,
notification_catalogs: vec![],
Expand All @@ -90,6 +95,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
let library_effects =
update_library::<E>(&mut self.library, &self.profile, &self.status, msg);
let streams_effects = update_streams::<E>(&mut self.streams, &self.status, msg);
let search_history_effects =
update_search_history::<E>(&mut self.search_history, &self.status, msg);
let trakt_addon_effects = update_trakt_addon::<E>(
&mut self.trakt_addon,
&self.profile,
Expand All @@ -111,6 +118,7 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(profile_effects)
.join(library_effects)
.join(streams_effects)
.join(search_history_effects)
.join(trakt_addon_effects)
.join(notifications_effects)
}
Expand All @@ -134,6 +142,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
msg,
);
let streams_effects = update_streams::<E>(&mut self.streams, &self.status, msg);
let search_history_effects =
update_search_history::<E>(&mut self.search_history, &self.status, msg);
let ctx_effects = match &self.status {
CtxStatus::Loading(loading_auth_request)
if loading_auth_request == auth_request =>
Expand All @@ -160,6 +170,7 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(streams_effects)
.join(trakt_addon_effects)
.join(notifications_effects)
.join(search_history_effects)
.join(ctx_effects)
}
_ => {
Expand All @@ -182,11 +193,14 @@ impl<E: Env + 'static> Update<E> for Ctx {
&self.status,
msg,
);
let search_history_effects =
update_search_history::<E>(&mut self.search_history, &self.status, msg);
profile_effects
.join(library_effects)
.join(streams_effects)
.join(trakt_addon_effects)
.join(notifications_effects)
.join(search_history_effects)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/models/ctx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use update_profile::*;
mod update_streams;
use update_streams::*;

mod update_search_history;
use update_search_history::*;

mod update_trakt_addon;
use update_trakt_addon::*;

Expand Down
63 changes: 63 additions & 0 deletions src/models/ctx/update_search_history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use enclose::enclose;
use futures::FutureExt;

use crate::constants::SEARCH_HISTORY_STORAGE_KEY;
use crate::models::ctx::{CtxError, CtxStatus};
use crate::runtime::msg::{Action, ActionCtx, Event, Internal};
use crate::runtime::{Effect, EffectFuture, Effects, Env, EnvFutureExt};
use crate::{runtime::msg::Msg, types::search_history::SearchHistoryBucket};

pub fn update_search_history<E: Env + 'static>(
search_history: &mut SearchHistoryBucket,
status: &CtxStatus,
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
let next_search_history = SearchHistoryBucket::default();
*search_history = next_search_history;
Effects::msg(Msg::Internal(Internal::SearchHistoryChanged))
}
Msg::Action(Action::Ctx(ActionCtx::ClearSearchHistory)) => {
search_history.items.clear();
Effects::msg(Msg::Internal(Internal::SearchHistoryChanged))
}
Msg::Internal(Internal::CatalogsWithExtraSearch { query }) => {
search_history.items.insert(query.to_owned(), E::now());
Effects::msg(Msg::Internal(Internal::SearchHistoryChanged))
}
Msg::Internal(Internal::CtxAuthResult(auth_request, result)) => match (status, result) {
(CtxStatus::Loading(loading_auth_request), Ok((auth, ..)))
if loading_auth_request == auth_request =>
{
let next_search_history = SearchHistoryBucket::new(Some(auth.user.id.to_owned()));
*search_history = next_search_history;
Effects::msg(Msg::Internal(Internal::SearchHistoryChanged))
}
_ => Effects::none().unchanged(),
},
Msg::Internal(Internal::SearchHistoryChanged) => {
Effects::one(push_search_history_to_storage::<E>(search_history)).unchanged()
}
_ => Effects::none().unchanged(),
}
}

fn push_search_history_to_storage<E: Env + 'static>(
search_history: &SearchHistoryBucket,
) -> Effect {
EffectFuture::Sequential(
E::set_storage(SEARCH_HISTORY_STORAGE_KEY, Some(&search_history))
.map(
enclose!((search_history.uid => uid) move |result| match result {
Ok(_) => Msg::Event(Event::SearchHistoryPushedToStorage { uid }),
Err(error) => Msg::Event(Event::Error {
error: CtxError::from(error),
source: Box::new(Event::SearchHistoryPushedToStorage { uid }),
})
}),
)
.boxed_env(),
)
.into()
}
44 changes: 41 additions & 3 deletions src/runtime/env.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::addon_transport::{AddonHTTPTransport, AddonTransport, UnsupportedTransport};
use crate::constants::{
LIBRARY_RECENT_STORAGE_KEY, LIBRARY_STORAGE_KEY, PROFILE_STORAGE_KEY, SCHEMA_VERSION,
SCHEMA_VERSION_STORAGE_KEY, STREAMS_STORAGE_KEY,
SCHEMA_VERSION_STORAGE_KEY, SEARCH_HISTORY_STORAGE_KEY, STREAMS_STORAGE_KEY,
};
use crate::models::ctx::Ctx;
use crate::models::streaming_server::StreamingServer;
Expand Down Expand Up @@ -232,6 +232,12 @@ pub trait Env {
.await?;
schema_version = 9;
}
if schema_version == 9 {
migrate_storage_schema_to_v10::<Self>()
.map_err(|error| EnvError::StorageSchemaVersionUpgrade(Box::new(error)))
.await?;
schema_version = 10;
}
if schema_version != SCHEMA_VERSION {
panic!(
"Storage schema version must be upgraded from {} to {}",
Expand Down Expand Up @@ -491,6 +497,12 @@ fn migrate_storage_schema_to_v9<E: Env>() -> TryEnvFuture<()> {
.boxed_env()
}

fn migrate_storage_schema_to_v10<E: Env>() -> TryEnvFuture<()> {
E::set_storage::<()>(SEARCH_HISTORY_STORAGE_KEY, None)
.and_then(|_| E::set_storage(SCHEMA_VERSION_STORAGE_KEY, Some(&10)))
.boxed_env()
}

#[cfg(test)]
mod test {
use serde_json::{json, Value};
Expand All @@ -501,8 +513,9 @@ mod test {
},
runtime::{
env::{
migrate_storage_schema_to_v6, migrate_storage_schema_to_v7,
migrate_storage_schema_to_v8, migrate_storage_schema_to_v9,
migrate_storage_schema_to_v10, migrate_storage_schema_to_v6,
migrate_storage_schema_to_v7, migrate_storage_schema_to_v8,
migrate_storage_schema_to_v9,
},
Env,
},
Expand Down Expand Up @@ -542,6 +555,18 @@ mod test {
);
}

fn assert_storage_shema_version(schema_v: u32) {
let storage = STORAGE.read().expect("Should lock");

assert_eq!(
&schema_v.to_string(),
storage
.get(SCHEMA_VERSION_STORAGE_KEY)
.expect("Should have the schema set"),
"Scheme version should be {schema_v}"
);
}

#[tokio::test]
async fn test_migration_to_latest_version() {
{
Expand Down Expand Up @@ -865,4 +890,17 @@ mod test {
);
}
}

#[tokio::test]
async fn test_migration_from_9_to_10() {
let _test_env_guard = TestEnv::reset().expect("Should lock TestEnv");

migrate_storage_schema_to_v10::<TestEnv>()
.await
.expect("Should migrate");

{
assert_storage_shema_version(10);
}
}
}
1 change: 1 addition & 0 deletions src/runtime/msg/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub enum ActionCtx {
ToggleLibraryItemNotifications(LibraryItemId, bool),
/// Dismiss all Notification for a given [`MetaItemId`].
DismissNotificationItem(MetaItemId),
ClearSearchHistory,
PushUserToAPI,
PullUserFromAPI,
PushAddonsToAPI,
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/msg/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub enum Event {
StreamsPushedToStorage {
uid: UID,
},
SearchHistoryPushedToStorage {
uid: UID,
},
NotificationsPushedToStorage {
ids: Vec<String>,
},
Expand Down
6 changes: 6 additions & 0 deletions src/runtime/msg/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ pub enum Internal {
stream_request: Option<ResourceRequest>,
meta_request: Option<ResourceRequest>,
},
/// Dispatched when requesting search on catalogs.
CatalogsWithExtraSearch {
query: String,
},
/// Dispatched when library item needs to be updated in the memory, storage and API.
UpdateLibraryItem(LibraryItem),
/// Dispatched when some of auth, addons or settings changed.
Expand All @@ -66,6 +70,8 @@ pub enum Internal {
LibraryChanged(bool),
/// Dispatched when streams bucket changes with a flag if its already persisted.
StreamsChanged(bool),
/// Search history haschanged.
SearchHistoryChanged,
/// User notifications have changed
NotificationsChanged,
/// Dismiss all Notifications for a given [`MetaItemId`].
Expand Down
1 change: 1 addition & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod library;
pub mod notifications;
pub mod profile;
pub mod resource;
pub mod search_history;
pub mod streaming_server;
pub mod streams;

Expand Down
2 changes: 2 additions & 0 deletions src/types/search_history/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod search_history_bucket;
pub use search_history_bucket::*;
21 changes: 21 additions & 0 deletions src/types/search_history/search_history_bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::collections::HashMap;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::types::profile::UID;

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchHistoryBucket {
pub uid: UID,
pub items: HashMap<String, DateTime<Utc>>,
}

impl SearchHistoryBucket {
pub fn new(uid: UID) -> Self {
Self {
uid,
items: HashMap::new(),
}
}
}
3 changes: 3 additions & 0 deletions src/unit_tests/catalog_with_filters/load_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::types::library::LibraryBucket;
use crate::types::notifications::NotificationsBucket;
use crate::types::profile::Profile;
use crate::types::resource::MetaItemPreview;
use crate::types::search_history::SearchHistoryBucket;
use crate::types::streams::StreamsBucket;
use crate::unit_tests::{
default_fetch_handler, Request, TestEnv, EVENTS, FETCH_HANDLER, REQUESTS, STATES,
Expand Down Expand Up @@ -49,6 +50,7 @@ fn default_catalog() {
LibraryBucket::default(),
StreamsBucket::default(),
NotificationsBucket::new::<TestEnv>(None, vec![]),
SearchHistoryBucket::default(),
);
let (discover, effects) = CatalogWithFilters::<MetaItemPreview>::new(&ctx.profile);
let (runtime, rx) = Runtime::<TestEnv, _>::new(
Expand Down Expand Up @@ -148,6 +150,7 @@ fn search_catalog() {
LibraryBucket::default(),
StreamsBucket::default(),
NotificationsBucket::new::<TestEnv>(None, vec![]),
SearchHistoryBucket::default(),
);
let (discover, effects) = CatalogWithFilters::<MetaItemPreview>::new(&ctx.profile);
let (runtime, rx) = Runtime::<TestEnv, _>::new(
Expand Down
Loading

0 comments on commit 75cfb62

Please sign in to comment.