use std::collections::HashSet;
use leptos::prelude::*;
use crate::{
ancestor_chain, resolve_focus_for_reply, DiscussionAppearance, DiscussionEvents,
DiscussionFeatures, DiscussionFocus, DiscussionLocale, DiscussionRenderers, DiscussionReply,
DiscussionReplyGraph, DiscussionViewMode,
};
#[cfg(feature = "hydrate")]
use crate::schedule_scroll_reply_into_view;
use super::composer::ComposerContext;
#[derive(Clone, Copy)]
pub struct DiscussionContext {
pub replies: Signal<Vec<DiscussionReply>>,
pub graph: Memo<DiscussionReplyGraph>,
pub locale: ReadSignal<DiscussionLocale>,
pub focus: Signal<DiscussionFocus>,
pub current_user_id: Signal<Option<String>>,
pub appearance: Signal<DiscussionAppearance>,
pub view_mode: Signal<DiscussionViewMode>,
pub collapsed: Signal<HashSet<String>>,
pub set_focus: WriteSignal<DiscussionFocus>,
pub set_view_mode: WriteSignal<DiscussionViewMode>,
pub set_collapsed: WriteSignal<HashSet<String>>,
pub features: DiscussionFeatures,
pub max_visible_depth: u32,
pub renderers: StoredValue<DiscussionRenderers>,
pub events: StoredValue<DiscussionEvents>,
}
#[component]
pub fn DiscussionProvider(
replies: Signal<Vec<DiscussionReply>>,
graph: Memo<DiscussionReplyGraph>,
locale: ReadSignal<DiscussionLocale>,
focus: Signal<DiscussionFocus>,
set_focus: WriteSignal<DiscussionFocus>,
view_mode: Signal<DiscussionViewMode>,
set_view_mode: WriteSignal<DiscussionViewMode>,
collapsed: Signal<HashSet<String>>,
set_collapsed: WriteSignal<HashSet<String>>,
features: DiscussionFeatures,
max_visible_depth: u32,
renderers: StoredValue<DiscussionRenderers>,
events: StoredValue<DiscussionEvents>,
#[prop(optional)]
current_user_id: Signal<Option<String>>,
#[prop(optional)]
appearance: Signal<DiscussionAppearance>,
children: Children,
) -> impl IntoView {
provide_context(DiscussionContext {
replies,
graph,
locale,
focus,
current_user_id,
appearance,
view_mode,
collapsed,
set_focus,
set_view_mode,
set_collapsed,
features,
max_visible_depth,
renderers,
events,
});
provide_context(ComposerContext::new());
children()
}
pub fn use_discussion() -> DiscussionContext {
expect_context::<DiscussionContext>()
}
pub fn use_discussion_locale() -> ReadSignal<DiscussionLocale> {
use_discussion().locale
}
pub fn navigate_focus_to(ctx: DiscussionContext, reply_id: &str, graph: &DiscussionReplyGraph) {
let next = crate::push_focus(&ctx.focus.get_untracked(), reply_id, graph);
ctx.set_focus.set(next.clone());
ctx.events
.with_value(|events| events.notify_focus_change(next));
}
pub fn navigate_focus_back(ctx: DiscussionContext) {
let next = crate::pop_focus(&ctx.focus.get_untracked());
ctx.set_focus.set(next.clone());
ctx.events
.with_value(|events| events.notify_focus_change(next));
}
pub fn navigate_to_reply(ctx: DiscussionContext, reply_id: &str) {
let graph = ctx.graph.get_untracked();
if graph.get(reply_id).is_none() {
return;
}
let chain = ancestor_chain(&graph, reply_id);
ctx.set_collapsed.update(|collapsed| {
for id in &chain {
collapsed.remove(id);
}
});
if ctx.view_mode.get_untracked() == DiscussionViewMode::Tree {
if let Some(focus) = resolve_focus_for_reply(&graph, reply_id, ctx.max_visible_depth) {
ctx.set_focus.set(focus.clone());
ctx.events
.with_value(|events| events.notify_focus_change(focus));
}
}
#[cfg(feature = "hydrate")]
schedule_scroll_reply_into_view(reply_id.to_string());
}
pub fn toggle_collapse(ctx: DiscussionContext, reply_id: String) {
let mut next = ctx.collapsed.get_untracked();
let collapsed = if next.contains(&reply_id) {
next.remove(&reply_id);
false
} else {
next.insert(reply_id.clone());
true
};
ctx.set_collapsed.set(next);
ctx.events
.with_value(|events| events.notify_collapse_change(reply_id, collapsed));
}