Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement dismissed events bucket #587

Merged
merged 5 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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 DISMISSED_EVENTS_STORAGE_KEY: &str = "dismissed_events";
pub const LIBRARY_COLLECTION_NAME: &str = "libraryItem";
pub const SEARCH_EXTRA_NAME: &str = "search";
/// `https://{ADDON_UR}/meta/...` resource
Expand All @@ -32,7 +33,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 = 11;
pub const SCHEMA_VERSION: u32 = 12;
pub const IMDB_LINK_CATEGORY: &str = "imdb";
pub const GENRES_LINK_CATEGORY: &str = "Genres";
pub const CINEMETA_TOP_CATALOG_ID: &str = "top";
Expand Down
15 changes: 13 additions & 2 deletions src/models/ctx/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::types::api::{
fetch_api, APIRequest, APIResult, AuthRequest, AuthResponse, CollectionResponse,
DatastoreCommand, DatastoreRequest, LibraryItemsResponse, SuccessResponse,
};
use crate::types::events::Events;
use crate::types::events::{DismissedEventsBucket, Events};
use crate::types::library::LibraryBucket;
use crate::types::notifications::NotificationsBucket;
use crate::types::profile::{Auth, AuthKey, Profile};
Expand Down Expand Up @@ -48,6 +48,8 @@ pub struct Ctx {
#[serde(skip)]
pub search_history: SearchHistoryBucket,
#[serde(skip)]
pub dismissed_events: DismissedEventsBucket,
#[serde(skip)]
#[cfg_attr(test, derivative(Default(value = "CtxStatus::Ready")))]
pub status: CtxStatus,
#[serde(skip)]
Expand All @@ -65,12 +67,14 @@ impl Ctx {
streams: StreamsBucket,
notifications: NotificationsBucket,
search_history: SearchHistoryBucket,
dismissed_events: DismissedEventsBucket,
) -> Self {
Self {
profile,
library,
streams,
search_history,
dismissed_events,
notifications,
trakt_addon: None,
notification_catalogs: vec![],
Expand Down Expand Up @@ -103,6 +107,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
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 events_effects =
update_events::<E>(&mut self.events, &mut self.dismissed_events, msg);
let trakt_addon_effects = update_trakt_addon::<E>(
&mut self.trakt_addon,
&self.profile,
Expand All @@ -125,6 +131,7 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(library_effects)
.join(streams_effects)
.join(search_history_effects)
.join(events_effects)
.join(trakt_addon_effects)
.join(notifications_effects)
}
Expand All @@ -150,6 +157,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
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 events_effects =
update_events::<E>(&mut self.events, &mut self.dismissed_events, msg);
let ctx_effects = match &self.status {
CtxStatus::Loading(loading_auth_request)
if loading_auth_request == auth_request =>
Expand Down Expand Up @@ -177,6 +186,7 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(trakt_addon_effects)
.join(notifications_effects)
.join(search_history_effects)
.join(events_effects)
.join(ctx_effects)
}
_ => {
Expand All @@ -201,7 +211,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
);
let search_history_effects =
update_search_history::<E>(&mut self.search_history, &self.status, msg);
let events_effects = update_events::<E>(&mut self.events, msg);
let events_effects =
update_events::<E>(&mut self.events, &mut self.dismissed_events, msg);
profile_effects
.join(library_effects)
.join(streams_effects)
Expand Down
98 changes: 90 additions & 8 deletions src/models/ctx/update_events.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
use chrono::DateTime;
use enclose::enclose;
use futures::{future, FutureExt, TryFutureExt};

use crate::constants::DISMISSED_EVENTS_STORAGE_KEY;
use crate::models::common::{eq_update, Loadable};
use crate::models::ctx::CtxError;
use crate::runtime::msg::{Action, ActionCtx, Internal, Msg};
use crate::runtime::msg::{Action, ActionCtx, Event, Internal, Msg};
use crate::runtime::{Effect, EffectFuture, Effects, Env, EnvFutureExt};
use crate::types::api::{
fetch_api, APIRequest, APIResult, GetModalResponse, GetNotificationResponse,
};
use crate::types::events::Events;
use crate::types::events::{DismissedEventsBucket, Events};

pub fn update_events<E: Env + 'static>(events: &mut Events, msg: &Msg) -> Effects {
pub fn update_events<E: Env + 'static>(
events: &mut Events,
dismissed_events: &mut DismissedEventsBucket,
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
let next_dismissed_events = DismissedEventsBucket::default();
*dismissed_events = next_dismissed_events;
Effects::msg(Msg::Internal(Internal::DismissedEventsChanged))
}
Msg::Action(Action::Ctx(ActionCtx::GetEvents)) => {
let modal_effects = eq_update(&mut events.modal, Loadable::Loading);
let notification_effects = eq_update(&mut events.notification, Loadable::Loading);
Expand All @@ -21,17 +32,69 @@ pub fn update_events<E: Env + 'static>(events: &mut Events, msg: &Msg) -> Effect
.join(notification_effects)
.join(requests_effects)
}
Msg::Action(Action::Ctx(ActionCtx::DismissEvent(id))) => {
dismissed_events
.items
.insert(id.to_owned(), DateTime::from(E::now()));

let message_effects = Effects::one(Effect::Msg(Box::new(Msg::Internal(
Internal::DismissedEventsChanged,
))));

let events_modal_effects = match events.modal.as_ref() {
Loadable::Ready(result) => match result {
Some(GetModalResponse { id, .. })
if dismissed_events.items.contains_key(id) =>
{
eq_update(&mut events.modal, Loadable::Ready(None))
}
_ => Effects::none().unchanged(),
},
_ => Effects::none().unchanged(),
};

let events_notification_effects = match events.notification.as_ref() {
Loadable::Ready(result) => match result {
Some(GetNotificationResponse { id, .. })
if dismissed_events.items.contains_key(id) =>
{
eq_update(&mut events.notification, Loadable::Ready(None))
}
_ => Effects::none().unchanged(),
},
_ => Effects::none().unchanged(),
};

message_effects
.join(events_modal_effects)
.join(events_notification_effects)
}
Msg::Internal(Internal::GetModalResult(_, result)) => match result {
Ok(response) => eq_update(&mut events.modal, Loadable::Ready(response.to_owned())),
Ok(response) => match response {
Some(GetModalResponse { id, .. }) if !dismissed_events.items.contains_key(id) => {
eq_update(&mut events.modal, Loadable::Ready(response.to_owned()))
}
_ => eq_update(&mut events.modal, Loadable::Ready(None)),
},
Err(error) => eq_update(&mut events.modal, Loadable::Err(error.to_owned())),
},
Msg::Internal(Internal::GetNotificationResult(_, result)) => match result {
Ok(response) => eq_update(
&mut events.notification,
Loadable::Ready(response.to_owned()),
),
Ok(response) => match response {
Some(GetNotificationResponse { id, .. })
if !dismissed_events.items.contains_key(id) =>
{
eq_update(
&mut events.notification,
Loadable::Ready(response.to_owned()),
)
}
_ => eq_update(&mut events.notification, Loadable::Ready(None)),
},
Err(error) => eq_update(&mut events.notification, Loadable::Err(error.to_owned())),
},
Msg::Internal(Internal::DismissedEventsChanged) => {
Effects::one(push_dismissed_events_to_storage::<E>(dismissed_events)).unchanged()
}
_ => Effects::none().unchanged(),
}
}
Expand Down Expand Up @@ -71,3 +134,22 @@ fn get_notification<E: Env + 'static>() -> Effect {
)
.into()
}

fn push_dismissed_events_to_storage<E: Env + 'static>(
dismissed_events: &DismissedEventsBucket,
) -> Effect {
EffectFuture::Sequential(
E::set_storage(DISMISSED_EVENTS_STORAGE_KEY, Some(&dismissed_events))
.map(
enclose!((dismissed_events.uid => uid) move |result| match result {
Ok(_) => Msg::Event(Event::DismissedEventsPushedToStorage { uid }),
Err(error) => Msg::Event(Event::Error {
error: CtxError::from(error),
source: Box::new(Event::DismissedEventsPushedToStorage { uid }),
})
}),
)
.boxed_env(),
)
.into()
}
39 changes: 33 additions & 6 deletions src/runtime/env.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
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, SEARCH_HISTORY_STORAGE_KEY, STREAMS_STORAGE_KEY,
DISMISSED_EVENTS_STORAGE_KEY, LIBRARY_RECENT_STORAGE_KEY, LIBRARY_STORAGE_KEY,
PROFILE_STORAGE_KEY, SCHEMA_VERSION, 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 @@ -244,6 +245,12 @@ pub trait Env {
.await?;
schema_version = 11;
}
if schema_version == 11 {
migrate_storage_schema_to_v12::<Self>()
.map_err(|error| EnvError::StorageSchemaVersionUpgrade(Box::new(error)))
.await?;
schema_version = 12;
}
if schema_version != SCHEMA_VERSION {
panic!(
"Storage schema version must be upgraded from {} to {}",
Expand Down Expand Up @@ -529,6 +536,12 @@ fn migrate_storage_schema_to_v11<E: Env>() -> TryEnvFuture<()> {
.boxed_env()
}

fn migrate_storage_schema_to_v12<E: Env>() -> TryEnvFuture<()> {
E::set_storage::<()>(DISMISSED_EVENTS_STORAGE_KEY, None)
.and_then(|_| E::set_storage(SCHEMA_VERSION_STORAGE_KEY, Some(&12)))
.boxed_env()
}

#[cfg(test)]
mod test {
use serde_json::{json, Value};
Expand All @@ -540,8 +553,9 @@ mod test {
runtime::{
env::{
migrate_storage_schema_to_v10, migrate_storage_schema_to_v11,
migrate_storage_schema_to_v6, migrate_storage_schema_to_v7,
migrate_storage_schema_to_v8, migrate_storage_schema_to_v9,
migrate_storage_schema_to_v12, 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 @@ -581,7 +595,7 @@ mod test {
);
}

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

assert_eq!(
Expand Down Expand Up @@ -926,7 +940,7 @@ mod test {
.expect("Should migrate");

{
assert_storage_shema_version(10);
assert_storage_schema_version(10);
}
}

Expand Down Expand Up @@ -970,4 +984,17 @@ mod test {
);
}
}

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

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

{
assert_storage_schema_version(12);
}
}
}
2 changes: 2 additions & 0 deletions src/runtime/msg/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub enum ActionCtx {
PullNotifications,
/// Make request to api to get events modal and notification
GetEvents,
/// Dismiss an event by id, either a Modal or Notification
DismissEvent(String),
}

#[derive(Clone, Deserialize, Debug)]
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 @@ -51,6 +51,9 @@ pub enum Event {
NotificationsPushedToStorage {
ids: Vec<String>,
},
DismissedEventsPushedToStorage {
uid: UID,
},
UserPulledFromAPI {
uid: UID,
},
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/msg/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,6 @@ pub enum Internal {
APIRequest,
Result<Option<GetNotificationResponse>, CtxError>,
),
/// When dismissed events changed
DismissedEventsChanged,
}
23 changes: 23 additions & 0 deletions src/types/events/dimissed_events_bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::collections::HashMap;

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

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

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DismissedEventsBucket {
pub uid: UID,
/// the key is the event (modal or notification) that has been dismissed
/// while the value is the time at which it was dismissed.
pub items: HashMap<String, DateTime<Local>>,
tymmesyde marked this conversation as resolved.
Show resolved Hide resolved
}

impl DismissedEventsBucket {
pub fn new(uid: UID) -> Self {
Self {
uid,
items: HashMap::new(),
}
}
}
3 changes: 3 additions & 0 deletions src/types/events/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
mod dimissed_events_bucket;
pub use dimissed_events_bucket::*;

mod events;
pub use events::*;
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 @@ -4,6 +4,7 @@ use crate::models::ctx::Ctx;
use crate::runtime::msg::{Action, ActionLoad};
use crate::runtime::{EnvFutureExt, Runtime, RuntimeAction, RuntimeEvent, TryEnvFuture};
use crate::types::addon::{ExtraValue, ResourcePath, ResourceRequest, ResourceResponse};
use crate::types::events::DismissedEventsBucket;
use crate::types::library::LibraryBucket;
use crate::types::notifications::NotificationsBucket;
use crate::types::profile::Profile;
Expand Down Expand Up @@ -51,6 +52,7 @@ fn default_catalog() {
StreamsBucket::default(),
NotificationsBucket::new::<TestEnv>(None, vec![]),
SearchHistoryBucket::default(),
DismissedEventsBucket::default(),
);
let (discover, effects) = CatalogWithFilters::<MetaItemPreview>::new(&ctx.profile);
let (runtime, rx) = Runtime::<TestEnv, _>::new(
Expand Down Expand Up @@ -151,6 +153,7 @@ fn search_catalog() {
StreamsBucket::default(),
NotificationsBucket::new::<TestEnv>(None, vec![]),
SearchHistoryBucket::default(),
DismissedEventsBucket::default(),
);
let (discover, effects) = CatalogWithFilters::<MetaItemPreview>::new(&ctx.profile);
let (runtime, rx) = Runtime::<TestEnv, _>::new(
Expand Down
Loading