commit a6d91c43e46a22db155ae0f7d5eff346676dc1fa
parent 19fe3703d9568ca69ccdf578290bd6b3d795ed77
Author: William Casarin <jb55@jb55.com>
Date: Tue, 16 Sep 2025 11:28:20 -0700
Merge a bunch of fixes from kernel
PRs
* 1141
* 1137
* 1136
kernelkind (10):
Revert "feat: transitively trust images from parent note"
feat: enable transitive trust for repost
fix `NoteUnits` front insertion logic
fix: don't reset scroll position when switching toolbar
fix: no longer make the scroll position jump oddly
fix: repost desc text size on newline
make `tabs_ui` return `InnerResponse`
refactor: impl transitive trust via `NoteOptions::TrustMedia`
refactor: move `profile_body` to fn
refactor: remove unnecessary code
Diffstat:
10 files changed, 242 insertions(+), 245 deletions(-)
diff --git a/crates/notedeck_columns/src/timeline/mod.rs b/crates/notedeck_columns/src/timeline/mod.rs
@@ -133,6 +133,7 @@ impl TimelineTab {
ndb: &Ndb,
txn: &Transaction,
reversed: bool,
+ use_front_insert: bool,
) -> Option<UnknownPks<'a>> {
if payloads.is_empty() {
return None;
@@ -158,7 +159,11 @@ impl TimelineTab {
debug!("spliced when inserting {num_refs} new notes, resetting virtual list",);
list.reset();
}
- MergeKind::FrontInsert => {
+ MergeKind::FrontInsert => 's: {
+ if !use_front_insert {
+ break 's;
+ }
+
// 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.
@@ -210,6 +215,7 @@ pub struct Timeline {
pub selected_view: usize,
pub subscription: TimelineSub,
+ pub enable_front_insert: bool,
}
impl Timeline {
@@ -271,12 +277,16 @@ impl Timeline {
let subscription = TimelineSub::default();
let selected_view = 0;
+ // by default, disabled for profiles since they contain widgets above the list items
+ let enable_front_insert = !matches!(kind, TimelineKind::Profile(_));
+
Timeline {
kind,
filter,
views,
subscription,
selected_view,
+ enable_front_insert,
}
}
@@ -402,7 +412,9 @@ impl Timeline {
match view.filter {
ViewFilter::NotesAndReplies => {
let res: Vec<&NotePayload<'_>> = payloads.iter().collect();
- if let Some(res) = view.insert(res, ndb, txn, reversed) {
+ if let Some(res) =
+ view.insert(res, ndb, txn, reversed, self.enable_front_insert)
+ {
res.process(unknown_ids, ndb, txn);
}
}
@@ -418,7 +430,13 @@ impl Timeline {
}
}
- if let Some(res) = view.insert(filtered_payloads, ndb, txn, reversed) {
+ if let Some(res) = view.insert(
+ filtered_payloads,
+ ndb,
+ txn,
+ reversed,
+ self.enable_front_insert,
+ ) {
res.process(unknown_ids, ndb, txn);
}
}
diff --git a/crates/notedeck_columns/src/timeline/note_units.rs b/crates/notedeck_columns/src/timeline/note_units.rs
@@ -101,28 +101,37 @@ impl NoteUnits {
let inserted_new = new_order.len();
- let front_insertion = inserted_new > 0
- && if self.order.is_empty() || new_order.is_empty() {
- true
- } else if !self.reversed {
- let first_new = *new_order.first().unwrap();
- let last_old = *self.order.last().unwrap();
- self.storage[first_new] >= self.storage[last_old]
- } else {
- let last_new = *new_order.last().unwrap();
- let first_old = *self.order.first().unwrap();
- self.storage[last_new] <= self.storage[first_old]
- };
+ let front_insertion = if self.order.is_empty() || new_order.is_empty() {
+ !new_order.is_empty()
+ } else if self.reversed {
+ // reversed is true, sorting should occur less recent to most recent (oldest to newest, opposite of `self.order`)
+ let first_new = *new_order.first().unwrap(); // most recent unit of the new order
+ let last_old = *self.order.last().unwrap(); // least recent unit of the current order
+
+ // if the most recent unit of the new order is less recent than the least recent unit of the current order,
+ // all current order units are less recent than the new order units.
+ // In other words, they are all being inserted in the front
+ self.storage[first_new] >= self.storage[last_old]
+ } else {
+ // reversed is false, sorting should occur most recent to least recent (newest to oldest, as it is in `self.order`)
+ let last_new = *new_order.last().unwrap(); // least recent unit of the new order
+ let first_old = *self.order.first().unwrap(); // most recent unit of the current order
+
+ // if the least recent unit of the new order is more recent than the most recent unit of the current order,
+ // all new units are more recent than the current units.
+ // In other words, they are all being inserted in the front
+ self.storage[last_new] <= self.storage[first_old]
+ };
let mut merged = Vec::with_capacity(self.order.len() + new_order.len());
let (mut i, mut j) = (0, 0);
while i < self.order.len() && j < new_order.len() {
let index_left = self.order[i];
let index_right = new_order[j];
- let left_item = &self.storage[index_left];
- let right_item = &self.storage[index_right];
- if left_item <= right_item {
- // left_item is newer than right_item
+ let left_unit = &self.storage[index_left];
+ let right_unit = &self.storage[index_right];
+ if left_unit <= right_unit {
+ // the left unit is more recent than (or the same recency as) the right unit
merged.push(index_left);
i += 1;
} else {
diff --git a/crates/notedeck_columns/src/timeline/route.rs b/crates/notedeck_columns/src/timeline/route.rs
@@ -31,7 +31,6 @@ pub fn render_timeline_route(
| TimelineKind::Generic(_) => {
let note_action =
ui::TimelineView::new(kind, timeline_cache, note_context, note_options, jobs, col)
- .scroll_to_top(scroll_to_top)
.ui(ui);
note_action.map(RenderNavAction::NoteAction)
diff --git a/crates/notedeck_columns/src/ui/note/post.rs b/crates/notedeck_columns/src/ui/note/post.rs
@@ -410,7 +410,7 @@ impl<'a, 'd> PostView<'a, 'd> {
self.note_context,
txn,
id.bytes(),
- None,
+ nostrdb::NoteKey::new(0),
self.note_options,
self.jobs,
)
diff --git a/crates/notedeck_columns/src/ui/profile/mod.rs b/crates/notedeck_columns/src/ui/profile/mod.rs
@@ -39,6 +39,11 @@ pub enum ProfileViewAction {
Follow(Pubkey),
}
+struct ProfileScrollResponse {
+ body_end_pos: f32,
+ action: Option<ProfileViewAction>,
+}
+
impl<'a, 'd> ProfileView<'a, 'd> {
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -65,15 +70,13 @@ impl<'a, 'd> ProfileView<'a, 'd> {
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey);
- let offset_id = scroll_id.with("scroll_offset");
+ let scroll_area = ScrollArea::vertical().id_salt(scroll_id).animated(false);
- let mut scroll_area = ScrollArea::vertical().id_salt(scroll_id);
-
- if let Some(offset) = ui.data(|i| i.get_temp::<f32>(offset_id)) {
- scroll_area = scroll_area.vertical_scroll_offset(offset);
- }
+ let profile_timeline = self
+ .timeline_cache
+ .get_mut(&TimelineKind::Profile(*self.pubkey))?;
- let output = scroll_area.show(ui, |ui| 's: {
+ let output = scroll_area.show(ui, |ui| {
let mut action = None;
let txn = Transaction::new(self.note_context.ndb).expect("txn");
let profile = self
@@ -82,23 +85,19 @@ impl<'a, 'd> ProfileView<'a, 'd> {
.get_profile_by_pubkey(&txn, self.pubkey.bytes())
.ok();
- if let Some(profile_view_action) = self.profile_body(ui, profile.as_ref()) {
+ if let Some(profile_view_action) =
+ profile_body(ui, self.pubkey, self.note_context, profile.as_ref())
+ {
action = Some(profile_view_action);
}
- let Some(profile_timeline) = self
- .timeline_cache
- .get_mut(&TimelineKind::Profile(*self.pubkey))
- else {
- break 's action;
- };
-
- profile_timeline.selected_view = tabs_ui(
+ let tabs_resp = tabs_ui(
ui,
self.note_context.i18n,
profile_timeline.selected_view,
&profile_timeline.views,
);
+ profile_timeline.selected_view = tabs_resp.inner;
let reversed = false;
// poll for new notes and insert them into our existing notes
@@ -124,145 +123,147 @@ impl<'a, 'd> ProfileView<'a, 'd> {
action = Some(ProfileViewAction::Note(note_action));
}
- action
+ ProfileScrollResponse {
+ body_end_pos: tabs_resp.response.rect.bottom(),
+ action,
+ }
});
- ui.data_mut(|d| d.insert_temp(offset_id, output.state.offset.y));
+ // only allow front insert when the profile body is fully obstructed
+ profile_timeline.enable_front_insert = output.inner.body_end_pos < ui.clip_rect().top();
- output.inner
+ output.inner.action
}
+}
- fn profile_body(
- &mut self,
- ui: &mut egui::Ui,
- profile: Option<&ProfileRecord<'_>>,
- ) -> Option<ProfileViewAction> {
- let mut action = None;
- ui.vertical(|ui| {
- banner(
- ui,
- profile
- .map(|p| p.record().profile())
- .and_then(|p| p.and_then(|p| p.banner())),
- 120.0,
- );
-
- let padding = 12.0;
- notedeck_ui::padding(padding, ui, |ui| {
- let mut pfp_rect = ui.available_rect_before_wrap();
- let size = 80.0;
- pfp_rect.set_width(size);
- pfp_rect.set_height(size);
- let pfp_rect = pfp_rect.translate(egui::vec2(0.0, -(padding + 2.0 + (size / 2.0))));
-
- ui.horizontal(|ui| {
- ui.put(
- pfp_rect,
- &mut ProfilePic::new(self.note_context.img_cache, get_profile_url(profile))
- .size(size)
- .border(ProfilePic::border_stroke(ui)),
- );
-
- if ui
- .add(copy_key_widget(&pfp_rect, self.note_context.i18n))
- .clicked()
- {
- let to_copy = if let Some(bech) = self.pubkey.npub() {
- bech
- } else {
- error!("Could not convert Pubkey to bech");
- String::new()
- };
- ui.ctx().copy_text(to_copy)
- }
+fn profile_body(
+ ui: &mut egui::Ui,
+ pubkey: &Pubkey,
+ note_context: &mut NoteContext,
+ profile: Option<&ProfileRecord<'_>>,
+) -> Option<ProfileViewAction> {
+ let mut action = None;
+ ui.vertical(|ui| {
+ banner(
+ ui,
+ profile
+ .map(|p| p.record().profile())
+ .and_then(|p| p.and_then(|p| p.banner())),
+ 120.0,
+ );
- ui.with_layout(Layout::right_to_left(egui::Align::RIGHT), |ui| {
- ui.add_space(24.0);
-
- let target_key = self.pubkey;
- let selected = self.note_context.accounts.get_selected_account();
-
- let profile_type = if selected.key.secret_key.is_none() {
- ProfileType::ReadOnly
- } else if &selected.key.pubkey == self.pubkey {
- ProfileType::MyProfile
- } else {
- ProfileType::Followable(selected.is_following(target_key.bytes()))
- };
-
- match profile_type {
- ProfileType::MyProfile => {
- if ui
- .add(edit_profile_button(self.note_context.i18n))
- .clicked()
- {
- action = Some(ProfileViewAction::EditProfile);
- }
+ let padding = 12.0;
+ notedeck_ui::padding(padding, ui, |ui| {
+ let mut pfp_rect = ui.available_rect_before_wrap();
+ let size = 80.0;
+ pfp_rect.set_width(size);
+ pfp_rect.set_height(size);
+ let pfp_rect = pfp_rect.translate(egui::vec2(0.0, -(padding + 2.0 + (size / 2.0))));
+
+ ui.horizontal(|ui| {
+ ui.put(
+ pfp_rect,
+ &mut ProfilePic::new(note_context.img_cache, get_profile_url(profile))
+ .size(size)
+ .border(ProfilePic::border_stroke(ui)),
+ );
+
+ if ui
+ .add(copy_key_widget(&pfp_rect, note_context.i18n))
+ .clicked()
+ {
+ let to_copy = if let Some(bech) = pubkey.npub() {
+ bech
+ } else {
+ error!("Could not convert Pubkey to bech");
+ String::new()
+ };
+ ui.ctx().copy_text(to_copy)
+ }
+
+ ui.with_layout(Layout::right_to_left(egui::Align::RIGHT), |ui| {
+ ui.add_space(24.0);
+
+ let target_key = pubkey;
+ let selected = note_context.accounts.get_selected_account();
+
+ let profile_type = if selected.key.secret_key.is_none() {
+ ProfileType::ReadOnly
+ } else if &selected.key.pubkey == pubkey {
+ ProfileType::MyProfile
+ } else {
+ ProfileType::Followable(selected.is_following(target_key.bytes()))
+ };
+
+ match profile_type {
+ ProfileType::MyProfile => {
+ if ui.add(edit_profile_button(note_context.i18n)).clicked() {
+ action = Some(ProfileViewAction::EditProfile);
}
- ProfileType::Followable(is_following) => {
- let follow_button = ui.add(follow_button(is_following));
-
- if follow_button.clicked() {
- action = match is_following {
- IsFollowing::Unknown => {
- // don't do anything, we don't have contact list
- None
- }
-
- IsFollowing::Yes => {
- Some(ProfileViewAction::Unfollow(target_key.to_owned()))
- }
-
- IsFollowing::No => {
- Some(ProfileViewAction::Follow(target_key.to_owned()))
- }
- };
- }
+ }
+ ProfileType::Followable(is_following) => {
+ let follow_button = ui.add(follow_button(is_following));
+
+ if follow_button.clicked() {
+ action = match is_following {
+ IsFollowing::Unknown => {
+ // don't do anything, we don't have contact list
+ None
+ }
+
+ IsFollowing::Yes => {
+ Some(ProfileViewAction::Unfollow(target_key.to_owned()))
+ }
+
+ IsFollowing::No => {
+ Some(ProfileViewAction::Follow(target_key.to_owned()))
+ }
+ };
}
- ProfileType::ReadOnly => {}
}
- });
+ ProfileType::ReadOnly => {}
+ }
});
+ });
- ui.add_space(18.0);
+ ui.add_space(18.0);
- ui.add(display_name_widget(&get_display_name(profile), false));
+ ui.add(display_name_widget(&get_display_name(profile), false));
- ui.add_space(8.0);
+ ui.add_space(8.0);
- ui.add(about_section_widget(profile));
+ ui.add(about_section_widget(profile));
- ui.horizontal_wrapped(|ui| {
- let website_url = profile
- .as_ref()
- .map(|p| p.record().profile())
- .and_then(|p| p.and_then(|p| p.website()).filter(|s| !s.is_empty()));
+ ui.horizontal_wrapped(|ui| {
+ let website_url = profile
+ .as_ref()
+ .map(|p| p.record().profile())
+ .and_then(|p| p.and_then(|p| p.website()).filter(|s| !s.is_empty()));
- let lud16 = profile
- .as_ref()
- .map(|p| p.record().profile())
- .and_then(|p| p.and_then(|p| p.lud16()).filter(|s| !s.is_empty()));
+ let lud16 = profile
+ .as_ref()
+ .map(|p| p.record().profile())
+ .and_then(|p| p.and_then(|p| p.lud16()).filter(|s| !s.is_empty()));
- if let Some(website_url) = website_url {
- ui.horizontal(|ui| {
- handle_link(ui, website_url);
- });
- }
+ if let Some(website_url) = website_url {
+ ui.horizontal(|ui| {
+ handle_link(ui, website_url);
+ });
+ }
- if let Some(lud16) = lud16 {
- if website_url.is_some() {
- ui.end_row();
- }
- ui.horizontal(|ui| {
- handle_lud16(ui, lud16);
- });
+ if let Some(lud16) = lud16 {
+ if website_url.is_some() {
+ ui.end_row();
}
- });
+ ui.horizontal(|ui| {
+ handle_lud16(ui, lud16);
+ });
+ }
});
});
+ });
- action
- }
+ action
}
enum ProfileType {
diff --git a/crates/notedeck_columns/src/ui/timeline.rs b/crates/notedeck_columns/src/ui/timeline.rs
@@ -1,8 +1,9 @@
use egui::containers::scroll_area::ScrollBarVisibility;
-use egui::{vec2, Color32, Direction, Layout, Margin, Pos2, ScrollArea, Sense, Stroke};
+use egui::{vec2, Color32, Direction, Layout, Margin, Pos2, RichText, ScrollArea, Sense, Stroke};
use egui_tabs::TabColor;
use enostr::Pubkey;
use nostrdb::{Note, ProfileRecord, Transaction};
+use notedeck::fonts::get_font_size;
use notedeck::name::get_display_name;
use notedeck::ui::is_narrow;
use notedeck::{tr_plural, JobsCache, Muted, NotedeckTextStyle};
@@ -118,7 +119,8 @@ fn timeline_ui(
note_context.i18n,
timeline.selected_view,
&timeline.views,
- );
+ )
+ .inner;
// need this for some reason??
ui.add_space(3.0);
@@ -151,12 +153,6 @@ fn timeline_ui(
.auto_shrink([false, false])
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible);
- let offset_id = scroll_id.with("timeline_scroll_offset");
-
- if let Some(offset) = ui.data(|i| i.get_temp::<f32>(offset_id)) {
- scroll_area = scroll_area.vertical_scroll_offset(offset);
- }
-
if goto_top_resp.is_some_and(|r| r.clicked()) {
scroll_area = scroll_area.vertical_scroll_offset(0.0);
}
@@ -195,8 +191,6 @@ fn timeline_ui(
.show(ui)
});
- ui.data_mut(|d| d.insert_temp(offset_id, scroll_output.state.offset.y));
-
let at_top_after_scroll = scroll_output.state.offset.y == 0.0;
let cur_show_top_button = ui.ctx().data(|d| d.get_temp::<bool>(show_top_button_id));
@@ -284,7 +278,7 @@ pub fn tabs_ui(
i18n: &mut Localization,
selected: usize,
views: &[TimelineTab],
-) -> usize {
+) -> egui::InnerResponse<usize> {
ui.spacing_mut().item_spacing.y = 0.0;
let tab_res = egui_tabs::Tabs::new(views.len() as i32)
@@ -332,7 +326,9 @@ pub fn tabs_ui(
let sel = tab_res.selected().unwrap_or_default();
- let (underline, underline_y) = tab_res.inner()[sel as usize].inner;
+ let res_inner = &tab_res.inner()[sel as usize];
+
+ let (underline, underline_y) = res_inner.inner;
let underline_width = underline.span();
let tab_anim_id = ui.id().with("tab_anim");
@@ -359,7 +355,7 @@ pub fn tabs_ui(
ui.painter().hline(underline, underline_y, stroke);
- sel as usize
+ egui::InnerResponse::new(sel as usize, res_inner.response.clone())
}
fn get_label_width(ui: &mut egui::Ui, text: &str) -> f32 {
@@ -734,7 +730,7 @@ fn render_reaction_cluster(
fn render_composite_entry(
ui: &mut egui::Ui,
note_context: &mut NoteContext,
- note_options: NoteOptions,
+ mut note_options: NoteOptions,
jobs: &mut JobsCache,
underlying_note: &nostrdb::Note<'_>,
profiles_to_show: Vec<ProfileEntry>,
@@ -760,6 +756,16 @@ fn render_composite_entry(
ReferencedNoteType::Yours
};
+ if !note_options.contains(NoteOptions::TrustMedia) {
+ let acc = note_context.accounts.get_selected_account();
+ for entry in &profiles_to_show {
+ if matches!(acc.is_following(entry.pk), notedeck::IsFollowing::Yes) {
+ note_options = note_options.union(NoteOptions::TrustMedia);
+ break;
+ }
+ }
+ }
+
egui::Frame::new()
.inner_margin(Margin::symmetric(8, 4))
.show(ui, |ui| {
@@ -829,7 +835,10 @@ fn render_composite_entry(
ui.horizontal(|ui| {
ui.add_space(48.0);
ui.horizontal_wrapped(|ui| {
- ui.label(desc);
+ ui.add(egui::Label::new(
+ RichText::new(desc)
+ .size(get_font_size(ui.ctx(), &NotedeckTextStyle::Small)),
+ ));
});
});
}
@@ -838,15 +847,14 @@ fn render_composite_entry(
let resp = ui
.horizontal(|ui| {
- let mut options = note_options;
- if options.contains(NoteOptions::Notification) {
- options = options
+ if note_options.contains(NoteOptions::Notification) {
+ note_options = note_options
.difference(NoteOptions::ActionBar | NoteOptions::OptionsButton)
.union(NoteOptions::NotificationPreview);
ui.add_space(48.0);
};
- NoteView::new(note_context, underlying_note, options, jobs).show(ui)
+ NoteView::new(note_context, underlying_note, note_options, jobs).show(ui)
})
.inner;
diff --git a/crates/notedeck_ui/src/note/contents.rs b/crates/notedeck_ui/src/note/contents.rs
@@ -4,11 +4,9 @@ use crate::{
secondary_label,
};
use egui::{Color32, Hyperlink, Label, RichText};
-use nostrdb::{BlockType, Mention, Note, Transaction};
+use nostrdb::{BlockType, Mention, Note, NoteKey, Transaction};
use notedeck::Localization;
-use notedeck::{
- time_format, update_imeta_blurhashes, IsFollowing, NoteCache, NoteContext, NotedeckTextStyle,
-};
+use notedeck::{time_format, update_imeta_blurhashes, NoteCache, NoteContext, NotedeckTextStyle};
use notedeck::{JobsCache, RenderableMedia};
use tracing::warn;
@@ -16,7 +14,6 @@ pub struct NoteContents<'a, 'd> {
note_context: &'a mut NoteContext<'d>,
txn: &'a Transaction,
note: &'a Note<'a>,
- parent: Option<&'a Note<'a>>,
options: NoteOptions,
pub action: Option<NoteAction>,
jobs: &'a mut JobsCache,
@@ -28,7 +25,6 @@ impl<'a, 'd> NoteContents<'a, 'd> {
note_context: &'a mut NoteContext<'d>,
txn: &'a Transaction,
note: &'a Note,
- parent: Option<&'a Note>,
options: NoteOptions,
jobs: &'a mut JobsCache,
) -> Self {
@@ -36,7 +32,6 @@ impl<'a, 'd> NoteContents<'a, 'd> {
note_context,
txn,
note,
- parent,
options,
action: None,
jobs,
@@ -51,7 +46,6 @@ impl egui::Widget for &mut NoteContents<'_, '_> {
self.note_context,
self.txn,
self.note,
- self.parent,
self.options,
self.jobs,
);
@@ -87,7 +81,7 @@ pub fn render_note_preview(
note_context: &mut NoteContext,
txn: &Transaction,
id: &[u8; 32],
- parent: Option<&Note>,
+ parent: NoteKey,
note_options: NoteOptions,
jobs: &mut JobsCache,
) -> NoteResponse {
@@ -118,12 +112,10 @@ pub fn render_note_preview(
*/
};
- let mut view = NoteView::new(note_context, ¬e, note_options, jobs).preview_style();
- if let Some(parent) = parent {
- view = view.parent(parent);
- }
-
- view.show(ui)
+ NoteView::new(note_context, ¬e, note_options, jobs)
+ .preview_style()
+ .parent(parent)
+ .show(ui)
}
/// Render note contents and surrounding info (client name, full date timestamp)
@@ -132,12 +124,10 @@ fn render_note_contents(
note_context: &mut NoteContext,
txn: &Transaction,
note: &Note,
- parent: Option<&Note>,
options: NoteOptions,
jobs: &mut JobsCache,
) -> NoteResponse {
- let response =
- render_undecorated_note_contents(ui, note_context, txn, note, parent, options, jobs);
+ let response = render_undecorated_note_contents(ui, note_context, txn, note, options, jobs);
ui.horizontal_wrapped(|ui| {
note_bottom_metadata_ui(
@@ -178,7 +168,6 @@ fn render_undecorated_note_contents<'a>(
note_context: &mut NoteContext,
txn: &Transaction,
note: &'a Note,
- parent: Option<&'a Note>,
options: NoteOptions,
jobs: &mut JobsCache,
) -> NoteResponse {
@@ -366,7 +355,7 @@ fn render_undecorated_note_contents<'a>(
});
let preview_note_action = inline_note.and_then(|(id, _)| {
- render_note_preview(ui, note_context, txn, id, Some(note), options, jobs)
+ render_note_preview(ui, note_context, txn, id, note_key, options, jobs)
.action
.map(|a| match a {
NoteAction::Note { note_id, .. } => NoteAction::Note {
@@ -383,28 +372,6 @@ fn render_undecorated_note_contents<'a>(
ui.add_space(2.0);
let carousel_id = egui::Id::new(("carousel", note.key().expect("expected tx note")));
- let is_self = note.pubkey()
- == note_context
- .accounts
- .get_selected_account()
- .key
- .pubkey
- .bytes();
-
- let trusted_media = {
- let is_followed = |pk| {
- matches!(
- note_context
- .accounts
- .get_selected_account()
- .is_following(pk),
- IsFollowing::Yes
- )
- };
-
- is_self || is_followed(note.pubkey()) || parent.is_some_and(|p| is_followed(p.pubkey()))
- };
-
media_action = image_carousel(
ui,
note_context.img_cache,
@@ -412,7 +379,6 @@ fn render_undecorated_note_contents<'a>(
jobs,
&supported_medias,
carousel_id,
- trusted_media,
note_context.i18n,
options,
);
diff --git a/crates/notedeck_ui/src/note/media.rs b/crates/notedeck_ui/src/note/media.rs
@@ -33,7 +33,6 @@ pub fn image_carousel(
jobs: &mut JobsCache,
medias: &[RenderableMedia],
carousel_id: egui::Id,
- trusted_media: bool,
i18n: &mut Localization,
note_options: NoteOptions,
) -> Option<MediaAction> {
@@ -68,7 +67,7 @@ pub fn image_carousel(
job_pool,
jobs,
media,
- trusted_media,
+ note_options.contains(NoteOptions::TrustMedia),
i18n,
size,
if note_options.contains(NoteOptions::NoAnimations) {
diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs
@@ -32,7 +32,7 @@ use notedeck::{
pub struct NoteView<'a, 'd> {
note_context: &'a mut NoteContext<'d>,
- parent: Option<&'a Note<'a>>,
+ parent: Option<NoteKey>,
note: &'a nostrdb::Note<'a>,
flags: NoteOptions,
jobs: &'a mut JobsCache,
@@ -85,7 +85,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
flags: NoteOptions,
jobs: &'a mut JobsCache,
) -> Self {
- let parent: Option<&Note> = None;
+ let parent: Option<NoteKey> = None;
Self {
note_context,
@@ -209,7 +209,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
}
#[inline]
- pub fn parent(mut self, parent: &'a Note<'a>) -> Self {
+ pub fn parent(mut self, parent: NoteKey) -> Self {
self.parent = Some(parent);
self
}
@@ -255,7 +255,6 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.note_context,
txn,
self.note,
- self.parent,
self.flags,
self.jobs,
));
@@ -303,6 +302,18 @@ impl<'a, 'd> NoteView<'a, 'd> {
}
pub fn show(&mut self, ui: &mut egui::Ui) -> NoteResponse {
+ if !self.flags.contains(NoteOptions::TrustMedia) {
+ let acc = self.note_context.accounts.get_selected_account();
+ if self.note.pubkey() == acc.key.pubkey.bytes()
+ || matches!(
+ acc.is_following(self.note.pubkey()),
+ notedeck::IsFollowing::Yes
+ )
+ {
+ self.flags = self.flags.union(NoteOptions::TrustMedia);
+ }
+ }
+
if self.options().contains(NoteOptions::Textmode) {
NoteResponse::new(self.textmode_ui(ui))
} else if self.options().contains(NoteOptions::Framed) {
@@ -426,14 +437,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
});
}
- let mut contents = NoteContents::new(
- self.note_context,
- txn,
- self.note,
- self.parent,
- self.flags,
- self.jobs,
- );
+ let mut contents =
+ NoteContents::new(self.note_context, txn, self.note, self.flags, self.jobs);
ui.add(&mut contents);
@@ -526,14 +531,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
});
}
- let mut contents = NoteContents::new(
- self.note_context,
- txn,
- self.note,
- self.parent,
- self.flags,
- self.jobs,
- );
+ let mut contents =
+ NoteContents::new(self.note_context, txn, self.note, self.flags, self.jobs);
ui.add(&mut contents);
note_action = contents.action.or(note_action);
@@ -577,12 +576,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
.ndb
.get_profile_by_pubkey(txn, self.note.pubkey());
- let hitbox_id = note_hitbox_id(
- note_key,
- self.options(),
- self.parent
- .map(|n| n.key().expect("todo: support non-db notes")),
- );
+ let hitbox_id = note_hitbox_id(note_key, self.options(), self.parent);
let maybe_hitbox = maybe_note_hitbox(ui, hitbox_id);
// wide design
@@ -749,7 +743,7 @@ fn note_hitbox_id(
fn maybe_note_hitbox(ui: &mut egui::Ui, hitbox_id: egui::Id) -> Option<Response> {
ui.ctx()
- .data_mut(|d| d.get_persisted(hitbox_id))
+ .data_mut(|d| d.get_temp(hitbox_id))
.map(|note_size: Vec2| {
// The hitbox should extend the entire width of the
// container. The hitbox height was cached last layout.
diff --git a/crates/notedeck_ui/src/note/options.rs b/crates/notedeck_ui/src/note/options.rs
@@ -44,6 +44,9 @@ bitflags! {
/// The note is a notification
const Notification = 1 << 19;
+
+ /// There is enough trust to show media in this note
+ const TrustMedia = 1 << 20;
}
}