Skip to content

Commit ca5ecb3

Browse files
committed
Merge multiple hashtags in a column
Fernando López Guevara (1): hashtag-column: allow multiple hashtags William Casarin (2): hashtag: improve sanitization function
2 parents 5c31bf1 + b67a2dd commit ca5ecb3

File tree

5 files changed

+52
-27
lines changed

5 files changed

+52
-27
lines changed

crates/notedeck_columns/src/actionbar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fn execute_note_action(
8888
});
8989
}
9090
NoteAction::Hashtag(htag) => {
91-
let kind = TimelineKind::Hashtag(htag.clone());
91+
let kind = TimelineKind::Hashtag(vec![htag.clone()]);
9292
router_action = Some(RouterAction::route_to(Route::Timeline(kind.clone())));
9393
timeline_res = timeline_cache
9494
.open(ndb, note_cache, txn, pool, &kind)

crates/notedeck_columns/src/route.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ impl fmt::Display for Route {
461461
TimelineKind::Universe => write!(f, "Universe"),
462462
TimelineKind::Generic(_) => write!(f, "Custom"),
463463
TimelineKind::Search(_) => write!(f, "Search"),
464-
TimelineKind::Hashtag(ht) => write!(f, "Hashtag ({})", ht),
464+
TimelineKind::Hashtag(ht) => write!(f, "Hashtags ({})", ht.join(" ")),
465465
TimelineKind::Profile(_id) => write!(f, "Profile"),
466466
},
467467
Route::Thread(_) => write!(f, "Thread"),

crates/notedeck_columns/src/timeline/kind.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ pub enum TimelineKind {
213213
/// Generic filter, references a hash of a filter
214214
Generic(u64),
215215

216-
Hashtag(String),
216+
Hashtag(Vec<String>),
217217
}
218218

219219
const NOTIFS_TOKEN_DEPRECATED: &str = "notifs";
@@ -263,7 +263,7 @@ impl Display for TimelineKind {
263263
TimelineKind::Notifications(_) => f.write_str("Notifications"),
264264
TimelineKind::Profile(_) => f.write_str("Profile"),
265265
TimelineKind::Universe => f.write_str("Universe"),
266-
TimelineKind::Hashtag(_) => f.write_str("Hashtag"),
266+
TimelineKind::Hashtag(_) => f.write_str("Hashtags"),
267267
TimelineKind::Search(_) => f.write_str("Search"),
268268
}
269269
}
@@ -325,7 +325,7 @@ impl TimelineKind {
325325
}
326326
TimelineKind::Hashtag(ht) => {
327327
writer.write_token("hashtag");
328-
writer.write_token(ht);
328+
writer.write_token(&ht.join(" "));
329329
}
330330
}
331331
}
@@ -379,7 +379,13 @@ impl TimelineKind {
379379
},
380380
|p| {
381381
p.parse_token("hashtag")?;
382-
Ok(TimelineKind::Hashtag(p.pull_token()?.to_string()))
382+
Ok(TimelineKind::Hashtag(
383+
p.pull_token()?
384+
.split_whitespace()
385+
.filter(|s| !s.is_empty())
386+
.map(|s| s.to_lowercase().to_string())
387+
.collect(),
388+
))
383389
},
384390
|p| {
385391
p.parse_token("search")?;
@@ -437,12 +443,19 @@ impl TimelineKind {
437443
.build()]),
438444

439445
TimelineKind::Hashtag(hashtag) => {
440-
let url: &str = &hashtag.to_lowercase();
441-
FilterState::ready(vec![Filter::new()
442-
.kinds([1])
443-
.limit(filter::default_limit())
444-
.tags([url], 't')
445-
.build()])
446+
let filters = hashtag
447+
.iter()
448+
.filter(|tag| !tag.is_empty())
449+
.map(|tag| {
450+
Filter::new()
451+
.kinds([1])
452+
.limit(filter::default_limit())
453+
.tags([tag.to_lowercase().as_str()], 't')
454+
.build()
455+
})
456+
.collect::<Vec<_>>();
457+
458+
FilterState::ready(filters)
446459
}
447460

448461
TimelineKind::Algo(algo_timeline) => match algo_timeline {
@@ -579,7 +592,7 @@ impl TimelineKind {
579592
TimelineKind::Profile(_pubkey_source) => ColumnTitle::needs_db(self),
580593
TimelineKind::Universe => ColumnTitle::simple("Universe"),
581594
TimelineKind::Generic(_) => ColumnTitle::simple("Custom"),
582-
TimelineKind::Hashtag(hashtag) => ColumnTitle::formatted(hashtag.to_string()),
595+
TimelineKind::Hashtag(hashtag) => ColumnTitle::formatted(hashtag.join(" ").to_string()),
583596
}
584597
}
585598
}

crates/notedeck_columns/src/timeline/mod.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,22 @@ impl Timeline {
226226
))
227227
}
228228

229-
pub fn hashtag(hashtag: String) -> Self {
230-
let hashtag = hashtag.to_lowercase();
231-
let htag: &str = &hashtag;
232-
let filter = Filter::new()
233-
.kinds([1])
234-
.limit(filter::default_limit())
235-
.tags([htag], 't')
236-
.build();
229+
pub fn hashtag(hashtag: Vec<String>) -> Self {
230+
let filters = hashtag
231+
.iter()
232+
.filter(|tag| !tag.is_empty())
233+
.map(|tag| {
234+
Filter::new()
235+
.kinds([1])
236+
.limit(filter::default_limit())
237+
.tags([tag.as_str()], 't')
238+
.build()
239+
})
240+
.collect::<Vec<_>>();
237241

238242
Timeline::new(
239243
TimelineKind::Hashtag(hashtag),
240-
FilterState::ready(vec![filter]),
244+
FilterState::ready(filters),
241245
TimelineTab::only_notes_and_replies(),
242246
)
243247
}

crates/notedeck_columns/src/ui/add_column.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ impl<'a> AddColumnView<'a> {
474474
option: AddColumnOption::UndecidedNotification,
475475
});
476476
vec.push(ColumnOptionData {
477-
title: "Hashtag",
477+
title: "Hashtags",
478478
description: "Stay up to date with a certain hashtag",
479479
icon: egui::include_image!("../../../../assets/icons/hashtag_icon_4x.png"),
480480
option: AddColumnOption::UndecidedHashtag,
@@ -754,7 +754,7 @@ pub fn hashtag_ui(
754754

755755
let text_edit = egui::TextEdit::singleline(text_buffer)
756756
.hint_text(
757-
RichText::new("Enter the desired hashtag here")
757+
RichText::new("Enter the desired hashtags here (for multiple space-separated)")
758758
.text_style(NotedeckTextStyle::Body.text_style()),
759759
)
760760
.vertical_align(Align::Center)
@@ -775,8 +775,13 @@ pub fn hashtag_ui(
775775
}
776776

777777
if handle_user_input && !text_buffer.is_empty() {
778-
let resp =
779-
AddColumnResponse::Timeline(TimelineKind::Hashtag(sanitize_hashtag(text_buffer)));
778+
let resp = AddColumnResponse::Timeline(TimelineKind::Hashtag(
779+
text_buffer
780+
.split_whitespace()
781+
.filter(|s| !s.is_empty())
782+
.map(|s| sanitize_hashtag(s).to_lowercase().to_string())
783+
.collect::<Vec<_>>(),
784+
));
780785
id_string_map.remove(&id);
781786
Some(resp)
782787
} else {
@@ -787,7 +792,10 @@ pub fn hashtag_ui(
787792
}
788793

789794
fn sanitize_hashtag(raw_hashtag: &str) -> String {
790-
raw_hashtag.replace("#", "")
795+
raw_hashtag
796+
.chars()
797+
.filter(|c| c.is_alphanumeric()) // keep letters and numbers only
798+
.collect()
791799
}
792800

793801
#[cfg(test)]

0 commit comments

Comments
 (0)