commit 69a6bf3664761864ff0fd00131283f84771ea0a7
parent 5b4c7a63715569dcc79dafbf271fc6f6c1ce01ce
Author: kernelkind <kernelkind@gmail.com>
Date: Mon, 16 Dec 2024 18:01:36 -0500
column: add individual column
A column for following a single user
Closes: https://github.com/damus-io/notedeck/pull/583
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
4 files changed, 133 insertions(+), 10 deletions(-)
diff --git a/crates/notedeck_columns/src/route.rs b/crates/notedeck_columns/src/route.rs
@@ -94,6 +94,12 @@ impl Route {
Cow::Borrowed("Add External Notifications Column")
}
AddColumnRoute::Hashtag => Cow::Borrowed("Add Hashtag Column"),
+ AddColumnRoute::UndecidedIndividual => {
+ Cow::Borrowed("Subscribe to someone's notes")
+ }
+ AddColumnRoute::ExternalIndividual => {
+ Cow::Borrowed("Subscribe to someone else's notes")
+ }
},
Route::Support => Cow::Borrowed("Damus Support"),
Route::NewDeck => Cow::Borrowed("Add Deck"),
diff --git a/crates/notedeck_columns/src/storage/decks.rs b/crates/notedeck_columns/src/storage/decks.rs
@@ -304,12 +304,7 @@ fn deserialize_columns(ndb: &Ndb, deck_user: &[u8; 32], serialized: Vec<Vec<Stri
match &ir {
IntermediaryRoute::Route(Route::Timeline(TimelineRoute::Thread(_)))
| IntermediaryRoute::Route(Route::Timeline(TimelineRoute::Profile(_))) => {
- // Do nothing. Threads & Profiles not yet supported for deserialization
- }
- IntermediaryRoute::Timeline(tl)
- if matches!(tl.kind, TimelineKind::Profile(_)) =>
- {
- // Do nothing. Profiles aren't yet supported for deserialization
+ // Do nothing. TimelineRoute Threads & Profiles not yet supported for deserialization
}
_ => cur_routes.push(ir),
}
@@ -361,6 +356,8 @@ enum Keyword {
Support,
Deck,
Edit,
+ IndividualSelection,
+ ExternalIndividualSelection,
}
impl Keyword {
@@ -526,6 +523,12 @@ fn serialize_route(route: &Route, columns: &Columns) -> Option<String> {
AddColumnRoute::Hashtag => {
selections.push(Selection::Keyword(Keyword::HashtagSelection))
}
+ AddColumnRoute::UndecidedIndividual => {
+ selections.push(Selection::Keyword(Keyword::IndividualSelection))
+ }
+ AddColumnRoute::ExternalIndividual => {
+ selections.push(Selection::Keyword(Keyword::ExternalIndividualSelection))
+ }
}
}
Route::Support => selections.push(Selection::Keyword(Keyword::Support)),
@@ -717,6 +720,16 @@ fn selections_to_route(selections: Vec<Selection>) -> Option<CleanIntermediaryRo
Selection::Keyword(Keyword::HashtagSelection) => Some(CleanIntermediaryRoute::ToRoute(
Route::AddColumn(AddColumnRoute::Hashtag),
)),
+ Selection::Keyword(Keyword::IndividualSelection) => {
+ Some(CleanIntermediaryRoute::ToRoute(Route::AddColumn(
+ AddColumnRoute::UndecidedIndividual,
+ )))
+ }
+ Selection::Keyword(Keyword::ExternalIndividualSelection) => {
+ Some(CleanIntermediaryRoute::ToRoute(Route::AddColumn(
+ AddColumnRoute::ExternalIndividual,
+ )))
+ }
_ => None,
},
Selection::Keyword(Keyword::Support) => {
@@ -746,6 +759,8 @@ fn selections_to_route(selections: Vec<Selection>) -> Option<CleanIntermediaryRo
| Selection::Keyword(Keyword::NotificationSelection)
| Selection::Keyword(Keyword::ExternalNotifSelection)
| Selection::Keyword(Keyword::HashtagSelection)
+ | Selection::Keyword(Keyword::IndividualSelection)
+ | Selection::Keyword(Keyword::ExternalIndividualSelection)
| Selection::Keyword(Keyword::Edit) => None,
}
}
diff --git a/crates/notedeck_columns/src/timeline/kind.rs b/crates/notedeck_columns/src/timeline/kind.rs
@@ -200,7 +200,7 @@ impl TimelineKind {
ListKind::Contact(_pubkey_source) => Cow::Borrowed("Contacts"),
},
TimelineKind::Notifications(_pubkey_source) => Cow::Borrowed("Notifications"),
- TimelineKind::Profile(_pubkey_source) => Cow::Borrowed("Profile"),
+ TimelineKind::Profile(_pubkey_source) => Cow::Borrowed("Notes"),
TimelineKind::Universe => Cow::Borrowed("Universe"),
TimelineKind::Generic => Cow::Borrowed("Custom"),
TimelineKind::Hashtag(hashtag) => Cow::Owned(format!("#{}", hashtag)),
diff --git a/crates/notedeck_columns/src/ui/add_column.rs b/crates/notedeck_columns/src/ui/add_column.rs
@@ -5,6 +5,7 @@ use egui::{
pos2, vec2, Align, Button, Color32, FontId, Id, ImageSource, Margin, Pos2, Rect, RichText,
Separator, Ui, Vec2,
};
+use enostr::Pubkey;
use nostrdb::Ndb;
use tracing::error;
@@ -24,6 +25,8 @@ pub enum AddColumnResponse {
UndecidedNotification,
ExternalNotification,
Hashtag,
+ UndecidedIndividual,
+ ExternalIndividual,
}
pub enum NotificationColumnType {
@@ -40,6 +43,9 @@ enum AddColumnOption {
Home(PubkeySource),
UndecidedHashtag,
Hashtag(String),
+ UndecidedIndividual,
+ ExternalIndividual,
+ Individual(PubkeySource),
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
@@ -48,6 +54,8 @@ pub enum AddColumnRoute {
UndecidedNotification,
ExternalNotification,
Hashtag,
+ UndecidedIndividual,
+ ExternalIndividual,
}
impl AddColumnOption {
@@ -76,6 +84,13 @@ impl AddColumnOption {
AddColumnOption::Hashtag(hashtag) => TimelineKind::Hashtag(hashtag)
.into_timeline(ndb, None)
.map(AddColumnResponse::Timeline),
+ AddColumnOption::UndecidedIndividual => Some(AddColumnResponse::UndecidedIndividual),
+ AddColumnOption::ExternalIndividual => Some(AddColumnResponse::ExternalIndividual),
+ AddColumnOption::Individual(pubkey_source) => {
+ let tlk = TimelineKind::profile(pubkey_source);
+ tlk.into_timeline(ndb, cur_account.map(|a| a.pubkey.bytes()))
+ .map(AddColumnResponse::Timeline)
+ }
}
}
}
@@ -128,8 +143,42 @@ impl<'a> AddColumnView<'a> {
}
fn external_notification_ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
+ let id = ui.id().with("external_notif");
+
+ self.external_ui(ui, id, |pubkey| {
+ AddColumnOption::Notification(PubkeySource::Explicit(pubkey))
+ })
+ }
+
+ fn individual_ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
+ let mut selected_option: Option<AddColumnResponse> = None;
+ for column_option_data in self.get_individual_options() {
+ let option = column_option_data.option.clone();
+ if self.column_option_ui(ui, column_option_data).clicked() {
+ selected_option = option.take_as_response(self.ndb, self.cur_account);
+ }
+
+ ui.add(Separator::default().spacing(0.0));
+ }
+
+ selected_option
+ }
+
+ fn external_individual_ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
+ let id = ui.id().with("external_individual");
+
+ self.external_ui(ui, id, |pubkey| {
+ AddColumnOption::Individual(PubkeySource::Explicit(pubkey))
+ })
+ }
+
+ fn external_ui(
+ &mut self,
+ ui: &mut Ui,
+ id: egui::Id,
+ to_tl: fn(Pubkey) -> AddColumnOption,
+ ) -> Option<AddColumnResponse> {
padding(16.0, ui, |ui| {
- let id = ui.id().with("external_notif");
let key_state = self.key_state_map.entry(id).or_default();
let text_edit = key_state.get_acquire_textedit(|text| {
@@ -164,8 +213,7 @@ impl<'a> AddColumnView<'a> {
if let Some(keypair) = key_state.check_for_successful_login() {
key_state.should_create_new();
- AddColumnOption::Notification(PubkeySource::Explicit(keypair.pubkey))
- .take_as_response(self.ndb, self.cur_account)
+ to_tl(keypair.pubkey).take_as_response(self.ndb, self.cur_account)
} else {
None
}
@@ -309,6 +357,12 @@ impl<'a> AddColumnView<'a> {
icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
option: AddColumnOption::UndecidedHashtag,
});
+ vec.push(ColumnOptionData {
+ title: "Individual",
+ description: "Stay up to date with someone's notes & replies",
+ icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
+ option: AddColumnOption::UndecidedIndividual,
+ });
vec
}
@@ -342,6 +396,36 @@ impl<'a> AddColumnView<'a> {
vec
}
+
+ fn get_individual_options(&self) -> Vec<ColumnOptionData> {
+ let mut vec = Vec::new();
+
+ if let Some(acc) = self.cur_account {
+ let source = if acc.secret_key.is_some() {
+ PubkeySource::DeckAuthor
+ } else {
+ PubkeySource::Explicit(acc.pubkey)
+ };
+
+ vec.push(ColumnOptionData {
+ title: "Your Notes",
+ description: "Keep track of your notes & replies",
+ icon: egui::include_image!(
+ "../../../../assets/icons/notifications_icon_dark_4x.png"
+ ),
+ option: AddColumnOption::Individual(source),
+ });
+ }
+
+ vec.push(ColumnOptionData {
+ title: "Someone else's Notes",
+ description: "Stay up to date with someone else's notes & replies",
+ icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
+ option: AddColumnOption::ExternalIndividual,
+ });
+
+ vec
+ }
}
struct ColumnOptionData {
@@ -368,6 +452,8 @@ pub fn render_add_column_routes(
AddColumnRoute::UndecidedNotification => add_column_view.notifications_ui(ui),
AddColumnRoute::ExternalNotification => add_column_view.external_notification_ui(ui),
AddColumnRoute::Hashtag => hashtag_ui(ui, ctx.ndb, &mut app.view_state.id_string_map),
+ AddColumnRoute::UndecidedIndividual => add_column_view.individual_ui(ui),
+ AddColumnRoute::ExternalIndividual => add_column_view.external_individual_ui(ui),
};
if let Some(resp) = resp {
@@ -407,6 +493,22 @@ pub fn render_add_column_routes(
.router_mut()
.route_to(crate::route::Route::AddColumn(AddColumnRoute::Hashtag));
}
+ AddColumnResponse::UndecidedIndividual => {
+ app.columns_mut(ctx.accounts)
+ .column_mut(col)
+ .router_mut()
+ .route_to(crate::route::Route::AddColumn(
+ AddColumnRoute::UndecidedIndividual,
+ ));
+ }
+ AddColumnResponse::ExternalIndividual => {
+ app.columns_mut(ctx.accounts)
+ .column_mut(col)
+ .router_mut()
+ .route_to(crate::route::Route::AddColumn(
+ AddColumnRoute::ExternalIndividual,
+ ));
+ }
};
}
}