notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

kind.rs (6436B)


      1 use crate::error::{Error, FilterError};
      2 use crate::filter;
      3 use crate::filter::FilterState;
      4 use crate::timeline::Timeline;
      5 use crate::ui::profile::preview::get_profile_displayname_string;
      6 use enostr::{Filter, Pubkey};
      7 use nostrdb::{Ndb, Transaction};
      8 use serde::{Deserialize, Serialize};
      9 use std::fmt::Display;
     10 use tracing::{error, warn};
     11 
     12 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
     13 pub enum PubkeySource {
     14     Explicit(Pubkey),
     15     DeckAuthor,
     16 }
     17 
     18 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     19 pub enum ListKind {
     20     Contact(PubkeySource),
     21 }
     22 
     23 ///
     24 /// What kind of timeline is it?
     25 ///   - Follow List
     26 ///   - Notifications
     27 ///   - DM
     28 ///   - filter
     29 ///   - ... etc
     30 ///
     31 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     32 pub enum TimelineKind {
     33     List(ListKind),
     34 
     35     Notifications(PubkeySource),
     36 
     37     Profile(PubkeySource),
     38 
     39     Universe,
     40 
     41     /// Generic filter
     42     Generic,
     43 }
     44 
     45 impl Display for TimelineKind {
     46     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     47         match self {
     48             TimelineKind::List(ListKind::Contact(_src)) => f.write_str("Contacts"),
     49             TimelineKind::Generic => f.write_str("Timeline"),
     50             TimelineKind::Notifications(_) => f.write_str("Notifications"),
     51             TimelineKind::Profile(_) => f.write_str("Profile"),
     52             TimelineKind::Universe => f.write_str("Universe"),
     53         }
     54     }
     55 }
     56 
     57 impl TimelineKind {
     58     pub fn contact_list(pk: PubkeySource) -> Self {
     59         TimelineKind::List(ListKind::Contact(pk))
     60     }
     61 
     62     pub fn is_contacts(&self) -> bool {
     63         matches!(self, TimelineKind::List(ListKind::Contact(_)))
     64     }
     65 
     66     pub fn profile(pk: PubkeySource) -> Self {
     67         TimelineKind::Profile(pk)
     68     }
     69 
     70     pub fn is_notifications(&self) -> bool {
     71         matches!(self, TimelineKind::Notifications(_))
     72     }
     73 
     74     pub fn notifications(pk: PubkeySource) -> Self {
     75         TimelineKind::Notifications(pk)
     76     }
     77 
     78     pub fn into_timeline(self, ndb: &Ndb, default_user: Option<&[u8; 32]>) -> Option<Timeline> {
     79         match self {
     80             TimelineKind::Universe => Some(Timeline::new(
     81                 TimelineKind::Universe,
     82                 FilterState::ready(vec![Filter::new()
     83                     .kinds([1])
     84                     .limit(filter::default_limit())
     85                     .build()]),
     86             )),
     87 
     88             TimelineKind::Generic => {
     89                 warn!("you can't convert a TimelineKind::Generic to a Timeline");
     90                 None
     91             }
     92 
     93             TimelineKind::Profile(pk_src) => {
     94                 let pk = match &pk_src {
     95                     PubkeySource::DeckAuthor => default_user?,
     96                     PubkeySource::Explicit(pk) => pk.bytes(),
     97                 };
     98 
     99                 let filter = Filter::new()
    100                     .authors([pk])
    101                     .kinds([1])
    102                     .limit(filter::default_limit())
    103                     .build();
    104 
    105                 Some(Timeline::new(
    106                     TimelineKind::profile(pk_src),
    107                     FilterState::ready(vec![filter]),
    108                 ))
    109             }
    110 
    111             TimelineKind::Notifications(pk_src) => {
    112                 let pk = match &pk_src {
    113                     PubkeySource::DeckAuthor => default_user?,
    114                     PubkeySource::Explicit(pk) => pk.bytes(),
    115                 };
    116 
    117                 let notifications_filter = Filter::new()
    118                     .pubkeys([pk])
    119                     .kinds([1])
    120                     .limit(crate::filter::default_limit())
    121                     .build();
    122 
    123                 Some(Timeline::new(
    124                     TimelineKind::notifications(pk_src),
    125                     FilterState::ready(vec![notifications_filter]),
    126                 ))
    127             }
    128 
    129             TimelineKind::List(ListKind::Contact(pk_src)) => {
    130                 let pk = match &pk_src {
    131                     PubkeySource::DeckAuthor => default_user?,
    132                     PubkeySource::Explicit(pk) => pk.bytes(),
    133                 };
    134 
    135                 let contact_filter = Filter::new().authors([pk]).kinds([3]).limit(1).build();
    136 
    137                 let txn = Transaction::new(ndb).expect("txn");
    138                 let results = ndb
    139                     .query(&txn, &[contact_filter.clone()], 1)
    140                     .expect("contact query failed?");
    141 
    142                 if results.is_empty() {
    143                     return Some(Timeline::new(
    144                         TimelineKind::contact_list(pk_src),
    145                         FilterState::needs_remote(vec![contact_filter.clone()]),
    146                     ));
    147                 }
    148 
    149                 match Timeline::contact_list(&results[0].note, pk_src.clone()) {
    150                     Err(Error::Filter(FilterError::EmptyContactList)) => Some(Timeline::new(
    151                         TimelineKind::contact_list(pk_src),
    152                         FilterState::needs_remote(vec![contact_filter]),
    153                     )),
    154                     Err(e) => {
    155                         error!("Unexpected error: {e}");
    156                         None
    157                     }
    158                     Ok(tl) => Some(tl),
    159                 }
    160             }
    161         }
    162     }
    163 
    164     pub fn to_title(&self, ndb: &Ndb) -> String {
    165         match self {
    166             TimelineKind::List(list_kind) => match list_kind {
    167                 ListKind::Contact(pubkey_source) => match pubkey_source {
    168                     PubkeySource::Explicit(pubkey) => {
    169                         format!("{}'s Contacts", get_profile_displayname_string(ndb, pubkey))
    170                     }
    171                     PubkeySource::DeckAuthor => "Contacts".to_owned(),
    172                 },
    173             },
    174             TimelineKind::Notifications(pubkey_source) => match pubkey_source {
    175                 PubkeySource::DeckAuthor => "Notifications".to_owned(),
    176                 PubkeySource::Explicit(pk) => format!(
    177                     "{}'s Notifications",
    178                     get_profile_displayname_string(ndb, pk)
    179                 ),
    180             },
    181             TimelineKind::Profile(pubkey_source) => match pubkey_source {
    182                 PubkeySource::DeckAuthor => "Profile".to_owned(),
    183                 PubkeySource::Explicit(pk) => {
    184                     format!("{}'s Profile", get_profile_displayname_string(ndb, pk))
    185                 }
    186             },
    187             TimelineKind::Universe => "Universe".to_owned(),
    188             TimelineKind::Generic => "Custom Filter".to_owned(),
    189         }
    190     }
    191 }