Hide cw pictures behind a summary/details (#483)
* Hide cw pictures behind a summary/details * refactor md_to_html a bit and add cw support * use random id for cw checkbox
This commit is contained in:
+105
-41
@@ -56,53 +56,117 @@ fn to_inline(tag: Tag) -> Tag {
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_text<'a>(state: &mut Option<String>, evt: Event<'a>) -> Option<Vec<Event<'a>>> {
|
||||
let (s, res) = match evt {
|
||||
Event::Text(txt) => match state.take() {
|
||||
Some(mut prev_txt) => {
|
||||
prev_txt.push_str(&txt);
|
||||
(Some(prev_txt), vec![])
|
||||
}
|
||||
None => (Some(txt.into_owned()), vec![]),
|
||||
},
|
||||
e => match state.take() {
|
||||
Some(prev) => (None, vec![Event::Text(Cow::Owned(prev)), e]),
|
||||
None => (None, vec![e]),
|
||||
},
|
||||
};
|
||||
*state = s;
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn inline_tags<'a>(
|
||||
(state, inline): &mut (Vec<Tag<'a>>, bool),
|
||||
evt: Event<'a>,
|
||||
) -> Option<Event<'a>> {
|
||||
if *inline {
|
||||
let new_evt = match evt {
|
||||
Event::Start(t) => {
|
||||
let tag = to_inline(t);
|
||||
state.push(tag.clone());
|
||||
Event::Start(tag)
|
||||
}
|
||||
Event::End(t) => match state.pop() {
|
||||
Some(other) => Event::End(other),
|
||||
None => Event::End(t),
|
||||
},
|
||||
e => e,
|
||||
};
|
||||
Some(new_evt)
|
||||
} else {
|
||||
Some(evt)
|
||||
}
|
||||
}
|
||||
|
||||
pub type MediaProcessor<'a> = Box<'a + Fn(i32) -> Option<(String, Option<String>)>>;
|
||||
|
||||
fn process_image<'a, 'b>(
|
||||
evt: Event<'a>,
|
||||
inline: bool,
|
||||
processor: &Option<MediaProcessor<'b>>,
|
||||
) -> Event<'a> {
|
||||
if let Some(ref processor) = *processor {
|
||||
match evt {
|
||||
Event::Start(Tag::Image(id, title)) => {
|
||||
if let Some((url, cw)) = id.parse::<i32>().ok().and_then(processor.as_ref()) {
|
||||
if inline || cw.is_none() {
|
||||
Event::Start(Tag::Image(Cow::Owned(url), title))
|
||||
} else {
|
||||
// there is a cw, and where are not inline
|
||||
Event::Html(Cow::Owned(format!(
|
||||
r#"<label for="postcontent-cw-{id}">
|
||||
<input type="checkbox" id="postcontent-cw-{id}" checked="checked" class="cw-checkbox">
|
||||
<span class="cw-container">
|
||||
<span class="cw-text">
|
||||
{cw}
|
||||
</span>
|
||||
<img src="{url}" alt=""#,
|
||||
id = random_hex(),
|
||||
cw = cw.unwrap(),
|
||||
url = url
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Event::Start(Tag::Image(id, title))
|
||||
}
|
||||
}
|
||||
Event::End(Tag::Image(id, title)) => {
|
||||
if let Some((url, cw)) = id.parse::<i32>().ok().and_then(processor.as_ref()) {
|
||||
if inline || cw.is_none() {
|
||||
Event::End(Tag::Image(Cow::Owned(url), title))
|
||||
} else {
|
||||
Event::Html(Cow::Borrowed(
|
||||
r#""/>
|
||||
</span>
|
||||
</label>"#,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Event::End(Tag::Image(id, title))
|
||||
}
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
} else {
|
||||
evt
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns (HTML, mentions, hashtags)
|
||||
pub fn md_to_html(
|
||||
pub fn md_to_html<'a>(
|
||||
md: &str,
|
||||
base_url: &str,
|
||||
inline: bool,
|
||||
media_processor: Option<MediaProcessor<'a>>,
|
||||
) -> (String, HashSet<String>, HashSet<String>) {
|
||||
let parser = Parser::new_ext(md, Options::all());
|
||||
|
||||
let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser
|
||||
.scan(None, |state: &mut Option<String>, evt| {
|
||||
let (s, res) = match evt {
|
||||
Event::Text(txt) => match state.take() {
|
||||
Some(mut prev_txt) => {
|
||||
prev_txt.push_str(&txt);
|
||||
(Some(prev_txt), vec![])
|
||||
}
|
||||
None => (Some(txt.into_owned()), vec![]),
|
||||
},
|
||||
e => match state.take() {
|
||||
Some(prev) => (None, vec![Event::Text(Cow::Owned(prev)), e]),
|
||||
None => (None, vec![e]),
|
||||
},
|
||||
};
|
||||
*state = s;
|
||||
Some(res)
|
||||
})
|
||||
// Flatten text because pulldown_cmark break #hashtag in two individual text elements
|
||||
.scan(None, flatten_text)
|
||||
.flat_map(IntoIterator::into_iter)
|
||||
.map(|evt| process_image(evt, inline, &media_processor))
|
||||
// Ignore headings, images, and tables if inline = true
|
||||
.scan(vec![], |state: &mut Vec<Tag>, evt| {
|
||||
if inline {
|
||||
let new_evt = match evt {
|
||||
Event::Start(t) => {
|
||||
let tag = to_inline(t);
|
||||
state.push(tag.clone());
|
||||
Event::Start(tag)
|
||||
}
|
||||
Event::End(t) => match state.pop() {
|
||||
Some(other) => Event::End(other),
|
||||
None => Event::End(t),
|
||||
},
|
||||
e => e,
|
||||
};
|
||||
Some(new_evt)
|
||||
} else {
|
||||
Some(evt)
|
||||
}
|
||||
})
|
||||
.scan((vec![], inline), inline_tags)
|
||||
.map(|evt| match evt {
|
||||
Event::Text(txt) => {
|
||||
let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold(
|
||||
@@ -273,7 +337,7 @@ mod tests {
|
||||
|
||||
for (md, mentions) in tests {
|
||||
assert_eq!(
|
||||
md_to_html(md, "", false).1,
|
||||
md_to_html(md, "", false, None).1,
|
||||
mentions
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
@@ -298,7 +362,7 @@ mod tests {
|
||||
|
||||
for (md, mentions) in tests {
|
||||
assert_eq!(
|
||||
md_to_html(md, "", false).2,
|
||||
md_to_html(md, "", false, None).2,
|
||||
mentions
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
@@ -310,11 +374,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_inline() {
|
||||
assert_eq!(
|
||||
md_to_html("# Hello", "", false).0,
|
||||
md_to_html("# Hello", "", false, None).0,
|
||||
String::from("<h1>Hello</h1>\n")
|
||||
);
|
||||
assert_eq!(
|
||||
md_to_html("# Hello", "", true).0,
|
||||
md_to_html("# Hello", "", true, None).0,
|
||||
String::from("<p>Hello</p>\n")
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user