web: show tag list in all modalities. WIP
This commit is contained in:
parent
605af13a37
commit
d5fa89b38c
@ -3,8 +3,8 @@ use seed_hooks::{state_access::CloneState, topo, use_state};
|
||||
|
||||
use crate::{
|
||||
api::urls,
|
||||
state::{Context, Model, Msg, Tag},
|
||||
view::{self, view_header, view_search_results},
|
||||
state::{Context, Model, Msg},
|
||||
view::{self, view_header, view_search_results, view_tags},
|
||||
};
|
||||
|
||||
#[topo::nested]
|
||||
@ -33,100 +33,9 @@ pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||
show_icon_text,
|
||||
),
|
||||
};
|
||||
fn view_tag_li(display_name: &str, indent: usize, t: &Tag, search_unread: bool) -> Node<Msg> {
|
||||
let href = if search_unread {
|
||||
urls::search(&format!("is:unread tag:{}", t.name), 0)
|
||||
} else {
|
||||
urls::search(&format!("tag:{}", t.name), 0)
|
||||
};
|
||||
li![a![
|
||||
attrs! {
|
||||
At::Href => href
|
||||
},
|
||||
(0..indent).map(|_| span![C!["tag-indent"], ""]),
|
||||
i![
|
||||
C!["tag-tag", "fa-solid", "fa-tag"],
|
||||
style! {
|
||||
//"--fa-primary-color" => t.fg_color,
|
||||
St::Color => t.bg_color,
|
||||
},
|
||||
],
|
||||
display_name,
|
||||
IF!(t.unread>0 => format!(" ({})", t.unread))
|
||||
]]
|
||||
}
|
||||
fn matches(a: &[&str], b: &[&str]) -> usize {
|
||||
std::iter::zip(a.iter(), b.iter())
|
||||
.take_while(|(a, b)| a == b)
|
||||
.count()
|
||||
}
|
||||
fn view_tag_list<'a>(
|
||||
tags: impl Iterator<Item = &'a Tag>,
|
||||
search_unread: bool,
|
||||
) -> Vec<Node<Msg>> {
|
||||
let mut lis = Vec::new();
|
||||
let mut last = Vec::new();
|
||||
for t in tags {
|
||||
let parts: Vec<_> = t.name.split('/').collect();
|
||||
let mut n = matches(&last, &parts);
|
||||
if n <= parts.len() - 2 && parts.len() > 1 {
|
||||
// Synthesize fake tags for proper indenting.
|
||||
for i in n..parts.len() - 1 {
|
||||
let display_name = parts[n];
|
||||
lis.push(view_tag_li(
|
||||
&display_name,
|
||||
n,
|
||||
&Tag {
|
||||
name: parts[..i + 1].join("/"),
|
||||
bg_color: "#fff".to_string(),
|
||||
fg_color: "#000".to_string(),
|
||||
unread: 0,
|
||||
},
|
||||
search_unread,
|
||||
));
|
||||
}
|
||||
n = parts.len() - 1;
|
||||
}
|
||||
let display_name = parts[n];
|
||||
lis.push(view_tag_li(&display_name, n, t, search_unread));
|
||||
last = parts;
|
||||
}
|
||||
lis
|
||||
}
|
||||
let unread = model
|
||||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| tags.iter().filter(|t| t.unread > 0).collect())
|
||||
.unwrap_or(Vec::new());
|
||||
let tags_open = use_state(|| false);
|
||||
let force_tags_open = unread.is_empty();
|
||||
div![
|
||||
C!["main-content"],
|
||||
aside![
|
||||
C!["tags-menu", "menu"],
|
||||
IF!(!unread.is_empty() => p![C!["menu-label"], "Unread"]),
|
||||
IF!(!unread.is_empty() => ul![C!["menu-list"], view_tag_list(unread.into_iter(),true)]),
|
||||
p![
|
||||
C!["menu-label"],
|
||||
IF!(!force_tags_open =>
|
||||
i![C![
|
||||
"fa-solid",
|
||||
if tags_open.get() {
|
||||
"fa-angle-up"
|
||||
} else {
|
||||
"fa-angle-down"
|
||||
}
|
||||
]]),
|
||||
" Tags",
|
||||
ev(Ev::Click, move |_| {
|
||||
tags_open.set(!tags_open.get());
|
||||
})
|
||||
],
|
||||
ul![
|
||||
C!["menu-list"],
|
||||
IF!(force_tags_open||tags_open.get() => model.tags.as_ref().map(|tags| view_tag_list(tags.iter(),false))),
|
||||
]
|
||||
],
|
||||
view_tags(model),
|
||||
div![
|
||||
view_header(&model.query, &model.refreshing_state),
|
||||
content,
|
||||
|
||||
@ -6,7 +6,10 @@ use crate::{
|
||||
api::urls,
|
||||
graphql::front_page_query::*,
|
||||
state::{Context, Model, Msg},
|
||||
view::{self, human_age, pretty_authors, search_toolbar, set_title, tags_chiclet, view_header},
|
||||
view::{
|
||||
self, human_age, pretty_authors, search_toolbar, set_title, tags_chiclet, view_header,
|
||||
view_tags,
|
||||
},
|
||||
};
|
||||
|
||||
pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||
@ -37,6 +40,7 @@ pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||
view_header(&model.query, &model.refreshing_state),
|
||||
content,
|
||||
view_header(&model.query, &model.refreshing_state),
|
||||
view_tags(model),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ use seed_hooks::{state_access::CloneState, topo, use_state};
|
||||
use crate::{
|
||||
api::urls,
|
||||
graphql::{front_page_query::*, show_thread_query::*},
|
||||
state::{unread_query, Model, Msg, RefreshingState},
|
||||
state::{unread_query, Model, Msg, RefreshingState, Tag},
|
||||
};
|
||||
|
||||
mod desktop;
|
||||
@ -1000,3 +1000,97 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
_ => div![C!["desktop"], desktop::view(model)],
|
||||
},]
|
||||
}
|
||||
pub fn view_tags(model: &Model) -> Node<Msg> {
|
||||
fn view_tag_li(display_name: &str, indent: usize, t: &Tag, search_unread: bool) -> Node<Msg> {
|
||||
let href = if search_unread {
|
||||
urls::search(&format!("is:unread tag:{}", t.name), 0)
|
||||
} else {
|
||||
urls::search(&format!("tag:{}", t.name), 0)
|
||||
};
|
||||
li![a![
|
||||
attrs! {
|
||||
At::Href => href
|
||||
},
|
||||
(0..indent).map(|_| span![C!["tag-indent"], ""]),
|
||||
i![
|
||||
C!["tag-tag", "fa-solid", "fa-tag"],
|
||||
style! {
|
||||
//"--fa-primary-color" => t.fg_color,
|
||||
St::Color => t.bg_color,
|
||||
},
|
||||
],
|
||||
display_name,
|
||||
IF!(t.unread>0 => format!(" ({})", t.unread))
|
||||
]]
|
||||
}
|
||||
fn matches(a: &[&str], b: &[&str]) -> usize {
|
||||
std::iter::zip(a.iter(), b.iter())
|
||||
.take_while(|(a, b)| a == b)
|
||||
.count()
|
||||
}
|
||||
fn view_tag_list<'a>(
|
||||
tags: impl Iterator<Item = &'a Tag>,
|
||||
search_unread: bool,
|
||||
) -> Vec<Node<Msg>> {
|
||||
let mut lis = Vec::new();
|
||||
let mut last = Vec::new();
|
||||
for t in tags {
|
||||
let parts: Vec<_> = t.name.split('/').collect();
|
||||
let mut n = matches(&last, &parts);
|
||||
if n <= parts.len() - 2 && parts.len() > 1 {
|
||||
// Synthesize fake tags for proper indenting.
|
||||
for i in n..parts.len() - 1 {
|
||||
let display_name = parts[n];
|
||||
lis.push(view_tag_li(
|
||||
&display_name,
|
||||
n,
|
||||
&Tag {
|
||||
name: parts[..i + 1].join("/"),
|
||||
bg_color: "#fff".to_string(),
|
||||
fg_color: "#000".to_string(),
|
||||
unread: 0,
|
||||
},
|
||||
search_unread,
|
||||
));
|
||||
}
|
||||
n = parts.len() - 1;
|
||||
}
|
||||
let display_name = parts[n];
|
||||
lis.push(view_tag_li(&display_name, n, t, search_unread));
|
||||
last = parts;
|
||||
}
|
||||
lis
|
||||
}
|
||||
let unread = model
|
||||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| tags.iter().filter(|t| t.unread > 0).collect())
|
||||
.unwrap_or(Vec::new());
|
||||
let tags_open = use_state(|| false);
|
||||
let force_tags_open = unread.is_empty();
|
||||
aside![
|
||||
C!["tags-menu", "menu"],
|
||||
IF!(!unread.is_empty() => p![C!["menu-label"], "Unread"]),
|
||||
IF!(!unread.is_empty() => ul![C!["menu-list"], view_tag_list(unread.into_iter(),true)]),
|
||||
p![
|
||||
C!["menu-label"],
|
||||
IF!(!force_tags_open =>
|
||||
i![C![
|
||||
"fa-solid",
|
||||
if tags_open.get() {
|
||||
"fa-angle-up"
|
||||
} else {
|
||||
"fa-angle-down"
|
||||
}
|
||||
]]),
|
||||
" Tags",
|
||||
ev(Ev::Click, move |_| {
|
||||
tags_open.set(!tags_open.get());
|
||||
})
|
||||
],
|
||||
ul![
|
||||
C!["menu-list"],
|
||||
IF!(force_tags_open||tags_open.get() => model.tags.as_ref().map(|tags| view_tag_list(tags.iter(),false))),
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use seed::{prelude::*, *};
|
||||
|
||||
use crate::{
|
||||
state::{Context, Model, Msg},
|
||||
view::{self, view_header, view_search_results},
|
||||
view::{self, view_header, view_search_results, view_tags},
|
||||
};
|
||||
|
||||
pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||
@ -36,6 +36,7 @@ pub(super) fn view(model: &Model) -> Node<Msg> {
|
||||
view_header(&model.query, &model.refreshing_state),
|
||||
content,
|
||||
view_header(&model.query, &model.refreshing_state),
|
||||
view_tags(model),
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user