use TimelineUnits instead of Vec<NoteRef>
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
actionbar::TimelineOpenResult,
|
actionbar::TimelineOpenResult,
|
||||||
error::Error,
|
error::Error,
|
||||||
timeline::{Timeline, TimelineKind},
|
timeline::{Timeline, TimelineKind, UnknownPksOwned},
|
||||||
};
|
};
|
||||||
|
|
||||||
use notedeck::{filter, FilterState, NoteCache, NoteRef};
|
use notedeck::{filter, FilterState, NoteCache, NoteRef};
|
||||||
@@ -90,17 +90,19 @@ impl TimelineCache {
|
|||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
notes: &[NoteRef],
|
notes: &[NoteRef],
|
||||||
note_cache: &mut NoteCache,
|
note_cache: &mut NoteCache,
|
||||||
) {
|
) -> Option<UnknownPksOwned> {
|
||||||
let mut timeline = if let Some(timeline) = id.clone().into_timeline(txn, ndb) {
|
let mut timeline = if let Some(timeline) = id.clone().into_timeline(txn, ndb) {
|
||||||
timeline
|
timeline
|
||||||
} else {
|
} else {
|
||||||
error!("Error creating timeline from {:?}", &id);
|
error!("Error creating timeline from {:?}", &id);
|
||||||
return;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert initial notes into timeline
|
// insert initial notes into timeline
|
||||||
timeline.insert_new(txn, ndb, note_cache, notes);
|
let res = timeline.insert_new(txn, ndb, note_cache, notes);
|
||||||
self.timelines.insert(id, timeline);
|
self.timelines.insert(id, timeline);
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, id: TimelineKind, timeline: Timeline) {
|
pub fn insert(&mut self, id: TimelineKind, timeline: Timeline) {
|
||||||
@@ -119,13 +121,16 @@ impl TimelineCache {
|
|||||||
note_cache: &mut NoteCache,
|
note_cache: &mut NoteCache,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
id: &TimelineKind,
|
id: &TimelineKind,
|
||||||
) -> Vitality<'a, Timeline> {
|
) -> GetNotesResponse<'a> {
|
||||||
// we can't use the naive hashmap entry API here because lookups
|
// we can't use the naive hashmap entry API here because lookups
|
||||||
// require a copy, wait until we have a raw entry api. We could
|
// require a copy, wait until we have a raw entry api. We could
|
||||||
// also use hashbrown?
|
// also use hashbrown?
|
||||||
|
|
||||||
if self.timelines.contains_key(id) {
|
if self.timelines.contains_key(id) {
|
||||||
return Vitality::Stale(self.get_expected_mut(id));
|
return GetNotesResponse {
|
||||||
|
vitality: Vitality::Stale(self.get_expected_mut(id)),
|
||||||
|
unknown_pks: None,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let notes = if let FilterState::Ready(filters) = id.filters(txn, ndb) {
|
let notes = if let FilterState::Ready(filters) = id.filters(txn, ndb) {
|
||||||
@@ -149,9 +154,12 @@ impl TimelineCache {
|
|||||||
info!("found NotesHolder with {} notes", notes.len());
|
info!("found NotesHolder with {} notes", notes.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.insert_new(id.to_owned(), txn, ndb, ¬es, note_cache);
|
let unknown_pks = self.insert_new(id.to_owned(), txn, ndb, ¬es, note_cache);
|
||||||
|
|
||||||
Vitality::Fresh(self.get_expected_mut(id))
|
GetNotesResponse {
|
||||||
|
vitality: Vitality::Fresh(self.get_expected_mut(id)),
|
||||||
|
unknown_pks,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a timeline, this is another way of saying insert a timeline
|
/// Open a timeline, this is another way of saying insert a timeline
|
||||||
@@ -166,11 +174,12 @@ impl TimelineCache {
|
|||||||
pool: &mut RelayPool,
|
pool: &mut RelayPool,
|
||||||
id: &TimelineKind,
|
id: &TimelineKind,
|
||||||
) -> Option<TimelineOpenResult> {
|
) -> Option<TimelineOpenResult> {
|
||||||
let (open_result, timeline) = match self.notes(ndb, note_cache, txn, id) {
|
let notes_resp = self.notes(ndb, note_cache, txn, id);
|
||||||
|
let (mut open_result, timeline) = match notes_resp.vitality {
|
||||||
Vitality::Stale(timeline) => {
|
Vitality::Stale(timeline) => {
|
||||||
// The timeline cache is stale, let's update it
|
// The timeline cache is stale, let's update it
|
||||||
let notes = find_new_notes(
|
let notes = find_new_notes(
|
||||||
timeline.all_or_any_notes(),
|
timeline.all_or_any_entries().latest(),
|
||||||
timeline.subscription.get_filter()?.local(),
|
timeline.subscription.get_filter()?.local(),
|
||||||
txn,
|
txn,
|
||||||
ndb,
|
ndb,
|
||||||
@@ -207,6 +216,13 @@ impl TimelineCache {
|
|||||||
|
|
||||||
timeline.subscription.increment();
|
timeline.subscription.increment();
|
||||||
|
|
||||||
|
if let Some(unknowns) = notes_resp.unknown_pks {
|
||||||
|
match &mut open_result {
|
||||||
|
Some(o) => o.insert_pks(unknowns.pks),
|
||||||
|
None => open_result = Some(TimelineOpenResult::new_pks(unknowns.pks)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open_result
|
open_result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,18 +247,22 @@ impl TimelineCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GetNotesResponse<'a> {
|
||||||
|
vitality: Vitality<'a, Timeline>,
|
||||||
|
unknown_pks: Option<UnknownPksOwned>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Look for new thread notes since our last fetch
|
/// Look for new thread notes since our last fetch
|
||||||
fn find_new_notes(
|
fn find_new_notes(
|
||||||
notes: &[NoteRef],
|
latest: Option<&NoteRef>,
|
||||||
filters: &[Filter],
|
filters: &[Filter],
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
) -> Vec<NoteRef> {
|
) -> Vec<NoteRef> {
|
||||||
if notes.is_empty() {
|
let Some(last_note) = latest else {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
};
|
||||||
|
|
||||||
let last_note = notes[0];
|
|
||||||
let filters = filter::make_filters_since(filters, last_note.created_at + 1);
|
let filters = filter::make_filters_since(filters, last_note.created_at + 1);
|
||||||
|
|
||||||
if let Ok(results) = ndb.query(txn, &filters, 1000) {
|
if let Ok(results) = ndb.query(txn, &filters, 1000) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
error::Error,
|
error::Error,
|
||||||
multi_subscriber::TimelineSub,
|
multi_subscriber::TimelineSub,
|
||||||
subscriptions::{self, SubKind, Subscriptions},
|
subscriptions::{self, SubKind, Subscriptions},
|
||||||
timeline::kind::ListKind,
|
timeline::{kind::ListKind, note_units::InsertManyResponse, timeline_units::NotePayload},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ use enostr::{PoolRelay, Pubkey, RelayPool};
|
|||||||
use nostrdb::{Filter, Ndb, Note, NoteKey, Transaction};
|
use nostrdb::{Filter, Ndb, Note, NoteKey, Transaction};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
collections::HashSet,
|
||||||
time::{Duration, UNIX_EPOCH},
|
time::{Duration, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
use std::{rc::Rc, time::SystemTime};
|
use std::{rc::Rc, time::SystemTime};
|
||||||
@@ -36,6 +37,7 @@ mod unit;
|
|||||||
pub use cache::TimelineCache;
|
pub use cache::TimelineCache;
|
||||||
pub use kind::{ColumnTitle, PubkeySource, ThreadSelection, TimelineKind};
|
pub use kind::{ColumnTitle, PubkeySource, ThreadSelection, TimelineKind};
|
||||||
pub use note_units::{InsertionResponse, NoteUnits};
|
pub use note_units::{InsertionResponse, NoteUnits};
|
||||||
|
pub use timeline_units::{TimelineUnits, UnknownPks};
|
||||||
pub use unit::{CompositeUnit, NoteUnit, ReactionUnit};
|
pub use unit::{CompositeUnit, NoteUnit, ReactionUnit};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||||
@@ -82,7 +84,7 @@ impl ViewFilter {
|
|||||||
/// be captured by a Filter itself.
|
/// be captured by a Filter itself.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct TimelineTab {
|
pub struct TimelineTab {
|
||||||
pub notes: Vec<NoteRef>,
|
pub units: TimelineUnits,
|
||||||
pub selection: i32,
|
pub selection: i32,
|
||||||
pub filter: ViewFilter,
|
pub filter: ViewFilter,
|
||||||
pub list: Rc<RefCell<VirtualList>>,
|
pub list: Rc<RefCell<VirtualList>>,
|
||||||
@@ -115,10 +117,9 @@ impl TimelineTab {
|
|||||||
list.hide_on_resize(None);
|
list.hide_on_resize(None);
|
||||||
list.over_scan(50.0);
|
list.over_scan(50.0);
|
||||||
let list = Rc::new(RefCell::new(list));
|
let list = Rc::new(RefCell::new(list));
|
||||||
let notes: Vec<NoteRef> = Vec::with_capacity(cap);
|
|
||||||
|
|
||||||
TimelineTab {
|
TimelineTab {
|
||||||
notes,
|
units: TimelineUnits::with_capacity(cap),
|
||||||
selection,
|
selection,
|
||||||
filter,
|
filter,
|
||||||
list,
|
list,
|
||||||
@@ -126,45 +127,54 @@ impl TimelineTab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, new_refs: &[NoteRef], reversed: bool) {
|
fn insert<'a>(
|
||||||
if new_refs.is_empty() {
|
&mut self,
|
||||||
return;
|
payloads: Vec<&'a NotePayload>,
|
||||||
|
ndb: &Ndb,
|
||||||
|
txn: &Transaction,
|
||||||
|
reversed: bool,
|
||||||
|
) -> Option<UnknownPks<'a>> {
|
||||||
|
if payloads.is_empty() {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
let num_prev_items = self.notes.len();
|
|
||||||
let (notes, merge_kind) = crate::timeline::merge_sorted_vecs(&self.notes, new_refs);
|
|
||||||
|
|
||||||
self.notes = notes;
|
let num_refs = payloads.len();
|
||||||
let new_items = self.notes.len() - num_prev_items;
|
|
||||||
|
|
||||||
// TODO: technically items could have been added inbetween
|
let resp = self.units.merge_new_notes(payloads, ndb, txn);
|
||||||
if new_items > 0 {
|
|
||||||
let mut list = self.list.borrow_mut();
|
|
||||||
|
|
||||||
match merge_kind {
|
let InsertManyResponse::Some {
|
||||||
// TODO: update egui_virtual_list to support spliced inserts
|
entries_merged,
|
||||||
MergeKind::Spliced => {
|
merge_kind,
|
||||||
debug!(
|
} = resp.insertion_response
|
||||||
"spliced when inserting {} new notes, resetting virtual list",
|
else {
|
||||||
new_refs.len()
|
return resp.tl_response;
|
||||||
);
|
};
|
||||||
list.reset();
|
|
||||||
}
|
let mut list = self.list.borrow_mut();
|
||||||
MergeKind::FrontInsert => {
|
|
||||||
// only run this logic if we're reverse-chronological
|
match merge_kind {
|
||||||
// reversed in this case means chronological, since the
|
// TODO: update egui_virtual_list to support spliced inserts
|
||||||
// default is reverse-chronological. yeah it's confusing.
|
MergeKind::Spliced => {
|
||||||
if !reversed {
|
debug!("spliced when inserting {num_refs} new notes, resetting virtual list",);
|
||||||
debug!("inserting {} new notes at start", new_refs.len());
|
list.reset();
|
||||||
list.items_inserted_at_start(new_items);
|
}
|
||||||
}
|
MergeKind::FrontInsert => {
|
||||||
|
// only run this logic if we're reverse-chronological
|
||||||
|
// reversed in this case means chronological, since the
|
||||||
|
// default is reverse-chronological. yeah it's confusing.
|
||||||
|
if !reversed {
|
||||||
|
debug!("inserting {num_refs} new notes at start");
|
||||||
|
list.items_inserted_at_start(entries_merged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
resp.tl_response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_down(&mut self) {
|
pub fn select_down(&mut self) {
|
||||||
debug!("select_down {}", self.selection + 1);
|
debug!("select_down {}", self.selection + 1);
|
||||||
if self.selection + 1 > self.notes.len() as i32 {
|
if self.selection + 1 > self.units.len() as i32 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +191,14 @@ impl TimelineTab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> UnknownPks<'a> {
|
||||||
|
pub fn process(&self, unknown_ids: &mut UnknownIds, ndb: &Ndb, txn: &Transaction) {
|
||||||
|
for pk in &self.unknown_pks {
|
||||||
|
unknown_ids.add_pubkey_if_missing(ndb, txn, pk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A column in a deck. Holds navigation state, loaded notes, column kind, etc.
|
/// A column in a deck. Holds navigation state, loaded notes, column kind, etc.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Timeline {
|
pub struct Timeline {
|
||||||
@@ -272,15 +290,20 @@ impl Timeline {
|
|||||||
|
|
||||||
/// Get the note refs for NotesAndReplies. If we only have Notes, then
|
/// Get the note refs for NotesAndReplies. If we only have Notes, then
|
||||||
/// just return that instead
|
/// just return that instead
|
||||||
pub fn all_or_any_notes(&self) -> &[NoteRef] {
|
pub fn all_or_any_entries(&self) -> &TimelineUnits {
|
||||||
self.notes(ViewFilter::NotesAndReplies).unwrap_or_else(|| {
|
self.entries(ViewFilter::NotesAndReplies)
|
||||||
self.notes(ViewFilter::Notes)
|
.unwrap_or_else(|| {
|
||||||
.expect("should have at least notes")
|
self.entries(ViewFilter::Notes)
|
||||||
})
|
.expect("should have at least notes")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notes(&self, view: ViewFilter) -> Option<&[NoteRef]> {
|
pub fn entries(&self, view: ViewFilter) -> Option<&TimelineUnits> {
|
||||||
self.view(view).map(|v| &*v.notes)
|
self.view(view).map(|v| &v.units)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latest_note(&self, view: ViewFilter) -> Option<&NoteRef> {
|
||||||
|
self.view(view).and_then(|v| v.units.latest())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self, view: ViewFilter) -> Option<&TimelineTab> {
|
pub fn view(&self, view: ViewFilter) -> Option<&TimelineTab> {
|
||||||
@@ -299,7 +322,7 @@ impl Timeline {
|
|||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
note_cache: &mut NoteCache,
|
note_cache: &mut NoteCache,
|
||||||
notes: &[NoteRef],
|
notes: &[NoteRef],
|
||||||
) {
|
) -> Option<UnknownPksOwned> {
|
||||||
let filters = {
|
let filters = {
|
||||||
let views = &self.views;
|
let views = &self.views;
|
||||||
let filters: Vec<fn(&CachedNote, &Note) -> bool> =
|
let filters: Vec<fn(&CachedNote, &Note) -> bool> =
|
||||||
@@ -307,6 +330,7 @@ impl Timeline {
|
|||||||
filters
|
filters
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut unknown_pks = HashSet::new();
|
||||||
for note_ref in notes {
|
for note_ref in notes {
|
||||||
for (view, filter) in filters.iter().enumerate() {
|
for (view, filter) in filters.iter().enumerate() {
|
||||||
if let Ok(note) = ndb.get_note_by_key(txn, note_ref.key) {
|
if let Ok(note) = ndb.get_note_by_key(txn, note_ref.key) {
|
||||||
@@ -314,11 +338,32 @@ impl Timeline {
|
|||||||
note_cache.cached_note_or_insert_mut(note_ref.key, ¬e),
|
note_cache.cached_note_or_insert_mut(note_ref.key, ¬e),
|
||||||
¬e,
|
¬e,
|
||||||
) {
|
) {
|
||||||
self.views[view].notes.push(*note_ref)
|
if let Some(resp) = self.views[view]
|
||||||
|
.units
|
||||||
|
.merge_new_notes(
|
||||||
|
vec![&NotePayload {
|
||||||
|
note,
|
||||||
|
key: note_ref.key,
|
||||||
|
}],
|
||||||
|
ndb,
|
||||||
|
txn,
|
||||||
|
)
|
||||||
|
.tl_response
|
||||||
|
{
|
||||||
|
let pks: HashSet<Pubkey> = resp
|
||||||
|
.unknown_pks
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| Pubkey::new(*r))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
unknown_pks.extend(pks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(UnknownPksOwned { pks: unknown_pks })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main function used for inserting notes into timelines. Handles
|
/// The main function used for inserting notes into timelines. Handles
|
||||||
@@ -333,7 +378,7 @@ impl Timeline {
|
|||||||
note_cache: &mut NoteCache,
|
note_cache: &mut NoteCache,
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut new_refs: Vec<(Note, NoteRef)> = Vec::with_capacity(new_note_ids.len());
|
let mut payloads: Vec<NotePayload> = Vec::with_capacity(new_note_ids.len());
|
||||||
|
|
||||||
for key in new_note_ids {
|
for key in new_note_ids {
|
||||||
let note = if let Ok(note) = ndb.get_note_by_key(txn, *key) {
|
let note = if let Ok(note) = ndb.get_note_by_key(txn, *key) {
|
||||||
@@ -350,35 +395,32 @@ impl Timeline {
|
|||||||
// into the timeline
|
// into the timeline
|
||||||
UnknownIds::update_from_note(txn, ndb, unknown_ids, note_cache, ¬e);
|
UnknownIds::update_from_note(txn, ndb, unknown_ids, note_cache, ¬e);
|
||||||
|
|
||||||
let created_at = note.created_at();
|
payloads.push(NotePayload { note, key: *key });
|
||||||
new_refs.push((
|
|
||||||
note,
|
|
||||||
NoteRef {
|
|
||||||
key: *key,
|
|
||||||
created_at,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for view in &mut self.views {
|
for view in &mut self.views {
|
||||||
match view.filter {
|
match view.filter {
|
||||||
ViewFilter::NotesAndReplies => {
|
ViewFilter::NotesAndReplies => {
|
||||||
let refs: Vec<NoteRef> = new_refs.iter().map(|(_note, nr)| *nr).collect();
|
let res: Vec<&NotePayload<'_>> = payloads.iter().collect();
|
||||||
|
if let Some(res) = view.insert(res, ndb, txn, reversed) {
|
||||||
view.insert(&refs, reversed);
|
res.process(unknown_ids, ndb, txn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewFilter::Notes => {
|
ViewFilter::Notes => {
|
||||||
let mut filtered_refs = Vec::with_capacity(new_refs.len());
|
let mut filtered_payloads = Vec::with_capacity(payloads.len());
|
||||||
for (note, nr) in &new_refs {
|
for payload in &payloads {
|
||||||
let cached_note = note_cache.cached_note_or_insert(nr.key, note);
|
let cached_note =
|
||||||
|
note_cache.cached_note_or_insert(payload.key, &payload.note);
|
||||||
|
|
||||||
if ViewFilter::filter_notes(cached_note, note) {
|
if ViewFilter::filter_notes(cached_note, &payload.note) {
|
||||||
filtered_refs.push(*nr);
|
filtered_payloads.push(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.insert(&filtered_refs, reversed);
|
if let Some(res) = view.insert(filtered_payloads, ndb, txn, reversed) {
|
||||||
|
res.process(unknown_ids, ndb, txn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,6 +457,18 @@ impl Timeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UnknownPksOwned {
|
||||||
|
pub pks: HashSet<Pubkey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnknownPksOwned {
|
||||||
|
pub fn process(&self, ndb: &Ndb, txn: &Transaction, unknown_ids: &mut UnknownIds) {
|
||||||
|
self.pks
|
||||||
|
.iter()
|
||||||
|
.for_each(|p| unknown_ids.add_pubkey_if_missing(ndb, txn, p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum MergeKind {
|
pub enum MergeKind {
|
||||||
FrontInsert,
|
FrontInsert,
|
||||||
Spliced,
|
Spliced,
|
||||||
@@ -544,7 +598,7 @@ pub fn send_initial_timeline_filter(
|
|||||||
filter = filter.limit_mut(lim);
|
filter = filter.limit_mut(lim);
|
||||||
}
|
}
|
||||||
|
|
||||||
let notes = timeline.all_or_any_notes();
|
let entries = timeline.all_or_any_entries();
|
||||||
|
|
||||||
// Should we since optimize? Not always. For example
|
// Should we since optimize? Not always. For example
|
||||||
// if we only have a few notes locally. One way to
|
// if we only have a few notes locally. One way to
|
||||||
@@ -552,8 +606,8 @@ pub fn send_initial_timeline_filter(
|
|||||||
// and seeing what its limit is. If we have less
|
// and seeing what its limit is. If we have less
|
||||||
// notes than the limit, we might want to backfill
|
// notes than the limit, we might want to backfill
|
||||||
// older notes
|
// older notes
|
||||||
if can_since_optimize && filter::should_since_optimize(lim, notes.len()) {
|
if can_since_optimize && filter::should_since_optimize(lim, entries.len()) {
|
||||||
filter = filter::since_optimize_filter(filter, Some(¬es[0]));
|
filter = filter::since_optimize_filter(filter, entries.latest());
|
||||||
} else {
|
} else {
|
||||||
warn!("Skipping since optimization for {:?}: number of local notes is less than limit, attempting to backfill.", &timeline.kind);
|
warn!("Skipping since optimization for {:?}: number of local notes is less than limit, attempting to backfill.", &timeline.kind);
|
||||||
}
|
}
|
||||||
@@ -635,7 +689,9 @@ fn setup_initial_timeline(
|
|||||||
.map(NoteRef::from_query_result)
|
.map(NoteRef::from_query_result)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
timeline.insert_new(txn, ndb, note_cache, ¬es);
|
if let Some(pks) = timeline.insert_new(txn, ndb, note_cache, ¬es) {
|
||||||
|
pks.process(ndb, txn, unknown_ids);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use egui::{vec2, Align, Color32, CornerRadius, RichText, Stroke, TextEdit};
|
|||||||
use enostr::{NoteId, Pubkey};
|
use enostr::{NoteId, Pubkey};
|
||||||
use state::TypingType;
|
use state::TypingType;
|
||||||
|
|
||||||
use crate::{timeline::TimelineTab, ui::timeline::TimelineTabView};
|
use crate::{
|
||||||
|
timeline::{TimelineTab, TimelineUnits},
|
||||||
|
ui::timeline::TimelineTabView,
|
||||||
|
};
|
||||||
use egui_winit::clipboard::Clipboard;
|
use egui_winit::clipboard::Clipboard;
|
||||||
use nostrdb::{Filter, Ndb, Transaction};
|
use nostrdb::{Filter, Ndb, Transaction};
|
||||||
use notedeck::{tr, tr_plural, JobsCache, Localization, NoteAction, NoteContext, NoteRef};
|
use notedeck::{tr, tr_plural, JobsCache, Localization, NoteAction, NoteContext, NoteRef};
|
||||||
@@ -125,7 +128,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
"Got {count} result for '{query}'", // one
|
"Got {count} result for '{query}'", // one
|
||||||
"Got {count} results for '{query}'", // other
|
"Got {count} results for '{query}'", // other
|
||||||
"Search results count", // comment
|
"Search results count", // comment
|
||||||
self.query.notes.notes.len(), // count
|
self.query.notes.units.len(), // count
|
||||||
query = &self.query.string
|
query = &self.query.string
|
||||||
));
|
));
|
||||||
note_action = self.show_search_results(ui);
|
note_action = self.show_search_results(ui);
|
||||||
@@ -190,7 +193,7 @@ fn execute_search(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
tab.notes = note_refs;
|
tab.units = TimelineUnits::from_refs_single(note_refs);
|
||||||
tab.list.borrow_mut().reset();
|
tab.list.borrow_mut().reset();
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -415,57 +415,30 @@ impl<'a, 'd> TimelineTabView<'a, 'd> {
|
|||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
pub fn show(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
||||||
let mut action: Option<NoteAction> = None;
|
let mut action: Option<NoteAction> = None;
|
||||||
let len = self.tab.notes.len();
|
let len = self.tab.units.len();
|
||||||
|
|
||||||
let is_muted = self.note_context.accounts.mutefun();
|
let mute = self.note_context.accounts.mute();
|
||||||
|
|
||||||
self.tab
|
self.tab
|
||||||
.list
|
.list
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.ui_custom_layout(ui, len, |ui, start_index| {
|
.ui_custom_layout(ui, len, |ui, index| {
|
||||||
|
// tracing::info!("rendering index: {index}");
|
||||||
ui.spacing_mut().item_spacing.y = 0.0;
|
ui.spacing_mut().item_spacing.y = 0.0;
|
||||||
ui.spacing_mut().item_spacing.x = 4.0;
|
ui.spacing_mut().item_spacing.x = 4.0;
|
||||||
|
|
||||||
let ind = if self.reversed {
|
let Some(entry) = self.tab.units.get(index) else {
|
||||||
len - start_index - 1
|
return 0;
|
||||||
} else {
|
|
||||||
start_index
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let note_key = self.tab.notes[ind].key;
|
match self.render_entry(ui, entry, &mute) {
|
||||||
|
RenderEntryResponse::Unsuccessful => return 0,
|
||||||
|
|
||||||
let note =
|
RenderEntryResponse::Success(note_action) => {
|
||||||
if let Ok(note) = self.note_context.ndb.get_note_by_key(self.txn, note_key) {
|
if let Some(cur_action) = note_action {
|
||||||
note
|
action = Some(cur_action);
|
||||||
} else {
|
|
||||||
warn!("failed to query note {:?}", note_key);
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// should we mute the thread? we might not have it!
|
|
||||||
let muted = if let Ok(root_id) = root_note_id_from_selected_id(
|
|
||||||
self.note_context.ndb,
|
|
||||||
self.note_context.note_cache,
|
|
||||||
self.txn,
|
|
||||||
note.id(),
|
|
||||||
) {
|
|
||||||
is_muted(¬e, root_id.bytes())
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !muted {
|
|
||||||
notedeck_ui::padding(8.0, ui, |ui| {
|
|
||||||
let resp =
|
|
||||||
NoteView::new(self.note_context, ¬e, self.note_options, self.jobs)
|
|
||||||
.show(ui);
|
|
||||||
|
|
||||||
if let Some(note_action) = resp.action {
|
|
||||||
action = Some(note_action)
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
notedeck_ui::hline(ui);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1
|
1
|
||||||
|
|||||||
Reference in New Issue
Block a user