mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-17 16:54:18 +01:00
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
This commit is contained in:
@@ -133,6 +133,7 @@ impl TimelineTab {
|
|||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
|
use_front_insert: bool,
|
||||||
) -> Option<UnknownPks<'a>> {
|
) -> Option<UnknownPks<'a>> {
|
||||||
if payloads.is_empty() {
|
if payloads.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -158,7 +159,11 @@ impl TimelineTab {
|
|||||||
debug!("spliced when inserting {num_refs} new notes, resetting virtual list",);
|
debug!("spliced when inserting {num_refs} new notes, resetting virtual list",);
|
||||||
list.reset();
|
list.reset();
|
||||||
}
|
}
|
||||||
MergeKind::FrontInsert => {
|
MergeKind::FrontInsert => 's: {
|
||||||
|
if !use_front_insert {
|
||||||
|
break 's;
|
||||||
|
}
|
||||||
|
|
||||||
// only run this logic if we're reverse-chronological
|
// only run this logic if we're reverse-chronological
|
||||||
// reversed in this case means chronological, since the
|
// reversed in this case means chronological, since the
|
||||||
// default is reverse-chronological. yeah it's confusing.
|
// default is reverse-chronological. yeah it's confusing.
|
||||||
@@ -210,6 +215,7 @@ pub struct Timeline {
|
|||||||
pub selected_view: usize,
|
pub selected_view: usize,
|
||||||
|
|
||||||
pub subscription: TimelineSub,
|
pub subscription: TimelineSub,
|
||||||
|
pub enable_front_insert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timeline {
|
impl Timeline {
|
||||||
@@ -271,12 +277,16 @@ impl Timeline {
|
|||||||
let subscription = TimelineSub::default();
|
let subscription = TimelineSub::default();
|
||||||
let selected_view = 0;
|
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 {
|
Timeline {
|
||||||
kind,
|
kind,
|
||||||
filter,
|
filter,
|
||||||
views,
|
views,
|
||||||
subscription,
|
subscription,
|
||||||
selected_view,
|
selected_view,
|
||||||
|
enable_front_insert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +412,9 @@ impl Timeline {
|
|||||||
match view.filter {
|
match view.filter {
|
||||||
ViewFilter::NotesAndReplies => {
|
ViewFilter::NotesAndReplies => {
|
||||||
let res: Vec<&NotePayload<'_>> = payloads.iter().collect();
|
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);
|
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);
|
res.process(unknown_ids, ndb, txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,16 +101,25 @@ impl NoteUnits {
|
|||||||
|
|
||||||
let inserted_new = new_order.len();
|
let inserted_new = new_order.len();
|
||||||
|
|
||||||
let front_insertion = inserted_new > 0
|
let front_insertion = if self.order.is_empty() || new_order.is_empty() {
|
||||||
&& if self.order.is_empty() || new_order.is_empty() {
|
!new_order.is_empty()
|
||||||
true
|
} else if self.reversed {
|
||||||
} 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();
|
let first_new = *new_order.first().unwrap(); // most recent unit of the new order
|
||||||
let last_old = *self.order.last().unwrap();
|
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]
|
self.storage[first_new] >= self.storage[last_old]
|
||||||
} else {
|
} else {
|
||||||
let last_new = *new_order.last().unwrap();
|
// reversed is false, sorting should occur most recent to least recent (newest to oldest, as it is in `self.order`)
|
||||||
let first_old = *self.order.first().unwrap();
|
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]
|
self.storage[last_new] <= self.storage[first_old]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,10 +128,10 @@ impl NoteUnits {
|
|||||||
while i < self.order.len() && j < new_order.len() {
|
while i < self.order.len() && j < new_order.len() {
|
||||||
let index_left = self.order[i];
|
let index_left = self.order[i];
|
||||||
let index_right = new_order[j];
|
let index_right = new_order[j];
|
||||||
let left_item = &self.storage[index_left];
|
let left_unit = &self.storage[index_left];
|
||||||
let right_item = &self.storage[index_right];
|
let right_unit = &self.storage[index_right];
|
||||||
if left_item <= right_item {
|
if left_unit <= right_unit {
|
||||||
// left_item is newer than right_item
|
// the left unit is more recent than (or the same recency as) the right unit
|
||||||
merged.push(index_left);
|
merged.push(index_left);
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ pub fn render_timeline_route(
|
|||||||
| TimelineKind::Generic(_) => {
|
| TimelineKind::Generic(_) => {
|
||||||
let note_action =
|
let note_action =
|
||||||
ui::TimelineView::new(kind, timeline_cache, note_context, note_options, jobs, col)
|
ui::TimelineView::new(kind, timeline_cache, note_context, note_options, jobs, col)
|
||||||
.scroll_to_top(scroll_to_top)
|
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
|
||||||
note_action.map(RenderNavAction::NoteAction)
|
note_action.map(RenderNavAction::NoteAction)
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
self.note_context,
|
self.note_context,
|
||||||
txn,
|
txn,
|
||||||
id.bytes(),
|
id.bytes(),
|
||||||
None,
|
nostrdb::NoteKey::new(0),
|
||||||
self.note_options,
|
self.note_options,
|
||||||
self.jobs,
|
self.jobs,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ pub enum ProfileViewAction {
|
|||||||
Follow(Pubkey),
|
Follow(Pubkey),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ProfileScrollResponse {
|
||||||
|
body_end_pos: f32,
|
||||||
|
action: Option<ProfileViewAction>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'd> ProfileView<'a, 'd> {
|
impl<'a, 'd> ProfileView<'a, 'd> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@@ -65,15 +70,13 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
|
||||||
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey);
|
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);
|
let profile_timeline = self
|
||||||
|
.timeline_cache
|
||||||
|
.get_mut(&TimelineKind::Profile(*self.pubkey))?;
|
||||||
|
|
||||||
if let Some(offset) = ui.data(|i| i.get_temp::<f32>(offset_id)) {
|
let output = scroll_area.show(ui, |ui| {
|
||||||
scroll_area = scroll_area.vertical_scroll_offset(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = scroll_area.show(ui, |ui| 's: {
|
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
let txn = Transaction::new(self.note_context.ndb).expect("txn");
|
let txn = Transaction::new(self.note_context.ndb).expect("txn");
|
||||||
let profile = self
|
let profile = self
|
||||||
@@ -82,23 +85,19 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
.get_profile_by_pubkey(&txn, self.pubkey.bytes())
|
.get_profile_by_pubkey(&txn, self.pubkey.bytes())
|
||||||
.ok();
|
.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);
|
action = Some(profile_view_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(profile_timeline) = self
|
let tabs_resp = tabs_ui(
|
||||||
.timeline_cache
|
|
||||||
.get_mut(&TimelineKind::Profile(*self.pubkey))
|
|
||||||
else {
|
|
||||||
break 's action;
|
|
||||||
};
|
|
||||||
|
|
||||||
profile_timeline.selected_view = tabs_ui(
|
|
||||||
ui,
|
ui,
|
||||||
self.note_context.i18n,
|
self.note_context.i18n,
|
||||||
profile_timeline.selected_view,
|
profile_timeline.selected_view,
|
||||||
&profile_timeline.views,
|
&profile_timeline.views,
|
||||||
);
|
);
|
||||||
|
profile_timeline.selected_view = tabs_resp.inner;
|
||||||
|
|
||||||
let reversed = false;
|
let reversed = false;
|
||||||
// poll for new notes and insert them into our existing notes
|
// poll for new notes and insert them into our existing notes
|
||||||
@@ -124,17 +123,23 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
action = Some(ProfileViewAction::Note(note_action));
|
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(
|
fn profile_body(
|
||||||
&mut self,
|
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
note_context: &mut NoteContext,
|
||||||
profile: Option<&ProfileRecord<'_>>,
|
profile: Option<&ProfileRecord<'_>>,
|
||||||
) -> Option<ProfileViewAction> {
|
) -> Option<ProfileViewAction> {
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
@@ -158,16 +163,16 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.put(
|
ui.put(
|
||||||
pfp_rect,
|
pfp_rect,
|
||||||
&mut ProfilePic::new(self.note_context.img_cache, get_profile_url(profile))
|
&mut ProfilePic::new(note_context.img_cache, get_profile_url(profile))
|
||||||
.size(size)
|
.size(size)
|
||||||
.border(ProfilePic::border_stroke(ui)),
|
.border(ProfilePic::border_stroke(ui)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
.add(copy_key_widget(&pfp_rect, self.note_context.i18n))
|
.add(copy_key_widget(&pfp_rect, note_context.i18n))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
let to_copy = if let Some(bech) = self.pubkey.npub() {
|
let to_copy = if let Some(bech) = pubkey.npub() {
|
||||||
bech
|
bech
|
||||||
} else {
|
} else {
|
||||||
error!("Could not convert Pubkey to bech");
|
error!("Could not convert Pubkey to bech");
|
||||||
@@ -179,12 +184,12 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
ui.with_layout(Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
ui.with_layout(Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
|
|
||||||
let target_key = self.pubkey;
|
let target_key = pubkey;
|
||||||
let selected = self.note_context.accounts.get_selected_account();
|
let selected = note_context.accounts.get_selected_account();
|
||||||
|
|
||||||
let profile_type = if selected.key.secret_key.is_none() {
|
let profile_type = if selected.key.secret_key.is_none() {
|
||||||
ProfileType::ReadOnly
|
ProfileType::ReadOnly
|
||||||
} else if &selected.key.pubkey == self.pubkey {
|
} else if &selected.key.pubkey == pubkey {
|
||||||
ProfileType::MyProfile
|
ProfileType::MyProfile
|
||||||
} else {
|
} else {
|
||||||
ProfileType::Followable(selected.is_following(target_key.bytes()))
|
ProfileType::Followable(selected.is_following(target_key.bytes()))
|
||||||
@@ -192,10 +197,7 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
|
|
||||||
match profile_type {
|
match profile_type {
|
||||||
ProfileType::MyProfile => {
|
ProfileType::MyProfile => {
|
||||||
if ui
|
if ui.add(edit_profile_button(note_context.i18n)).clicked() {
|
||||||
.add(edit_profile_button(self.note_context.i18n))
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
action = Some(ProfileViewAction::EditProfile);
|
action = Some(ProfileViewAction::EditProfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +265,6 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
|
|
||||||
action
|
action
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
enum ProfileType {
|
enum ProfileType {
|
||||||
MyProfile,
|
MyProfile,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use egui::containers::scroll_area::ScrollBarVisibility;
|
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 egui_tabs::TabColor;
|
||||||
use enostr::Pubkey;
|
use enostr::Pubkey;
|
||||||
use nostrdb::{Note, ProfileRecord, Transaction};
|
use nostrdb::{Note, ProfileRecord, Transaction};
|
||||||
|
use notedeck::fonts::get_font_size;
|
||||||
use notedeck::name::get_display_name;
|
use notedeck::name::get_display_name;
|
||||||
use notedeck::ui::is_narrow;
|
use notedeck::ui::is_narrow;
|
||||||
use notedeck::{tr_plural, JobsCache, Muted, NotedeckTextStyle};
|
use notedeck::{tr_plural, JobsCache, Muted, NotedeckTextStyle};
|
||||||
@@ -118,7 +119,8 @@ fn timeline_ui(
|
|||||||
note_context.i18n,
|
note_context.i18n,
|
||||||
timeline.selected_view,
|
timeline.selected_view,
|
||||||
&timeline.views,
|
&timeline.views,
|
||||||
);
|
)
|
||||||
|
.inner;
|
||||||
|
|
||||||
// need this for some reason??
|
// need this for some reason??
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
@@ -151,12 +153,6 @@ fn timeline_ui(
|
|||||||
.auto_shrink([false, false])
|
.auto_shrink([false, false])
|
||||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible);
|
.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()) {
|
if goto_top_resp.is_some_and(|r| r.clicked()) {
|
||||||
scroll_area = scroll_area.vertical_scroll_offset(0.0);
|
scroll_area = scroll_area.vertical_scroll_offset(0.0);
|
||||||
}
|
}
|
||||||
@@ -195,8 +191,6 @@ fn timeline_ui(
|
|||||||
.show(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 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));
|
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,
|
i18n: &mut Localization,
|
||||||
selected: usize,
|
selected: usize,
|
||||||
views: &[TimelineTab],
|
views: &[TimelineTab],
|
||||||
) -> usize {
|
) -> egui::InnerResponse<usize> {
|
||||||
ui.spacing_mut().item_spacing.y = 0.0;
|
ui.spacing_mut().item_spacing.y = 0.0;
|
||||||
|
|
||||||
let tab_res = egui_tabs::Tabs::new(views.len() as i32)
|
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 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 underline_width = underline.span();
|
||||||
|
|
||||||
let tab_anim_id = ui.id().with("tab_anim");
|
let tab_anim_id = ui.id().with("tab_anim");
|
||||||
@@ -359,7 +355,7 @@ pub fn tabs_ui(
|
|||||||
|
|
||||||
ui.painter().hline(underline, underline_y, stroke);
|
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 {
|
fn get_label_width(ui: &mut egui::Ui, text: &str) -> f32 {
|
||||||
@@ -734,7 +730,7 @@ fn render_reaction_cluster(
|
|||||||
fn render_composite_entry(
|
fn render_composite_entry(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
note_options: NoteOptions,
|
mut note_options: NoteOptions,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
underlying_note: &nostrdb::Note<'_>,
|
underlying_note: &nostrdb::Note<'_>,
|
||||||
profiles_to_show: Vec<ProfileEntry>,
|
profiles_to_show: Vec<ProfileEntry>,
|
||||||
@@ -760,6 +756,16 @@ fn render_composite_entry(
|
|||||||
ReferencedNoteType::Yours
|
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()
|
egui::Frame::new()
|
||||||
.inner_margin(Margin::symmetric(8, 4))
|
.inner_margin(Margin::symmetric(8, 4))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
@@ -829,7 +835,10 @@ fn render_composite_entry(
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(48.0);
|
ui.add_space(48.0);
|
||||||
ui.horizontal_wrapped(|ui| {
|
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
|
let resp = ui
|
||||||
.horizontal(|ui| {
|
.horizontal(|ui| {
|
||||||
let mut options = note_options;
|
if note_options.contains(NoteOptions::Notification) {
|
||||||
if options.contains(NoteOptions::Notification) {
|
note_options = note_options
|
||||||
options = options
|
|
||||||
.difference(NoteOptions::ActionBar | NoteOptions::OptionsButton)
|
.difference(NoteOptions::ActionBar | NoteOptions::OptionsButton)
|
||||||
.union(NoteOptions::NotificationPreview);
|
.union(NoteOptions::NotificationPreview);
|
||||||
|
|
||||||
ui.add_space(48.0);
|
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;
|
.inner;
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ use crate::{
|
|||||||
secondary_label,
|
secondary_label,
|
||||||
};
|
};
|
||||||
use egui::{Color32, Hyperlink, Label, RichText};
|
use egui::{Color32, Hyperlink, Label, RichText};
|
||||||
use nostrdb::{BlockType, Mention, Note, Transaction};
|
use nostrdb::{BlockType, Mention, Note, NoteKey, Transaction};
|
||||||
use notedeck::Localization;
|
use notedeck::Localization;
|
||||||
use notedeck::{
|
use notedeck::{time_format, update_imeta_blurhashes, NoteCache, NoteContext, NotedeckTextStyle};
|
||||||
time_format, update_imeta_blurhashes, IsFollowing, NoteCache, NoteContext, NotedeckTextStyle,
|
|
||||||
};
|
|
||||||
use notedeck::{JobsCache, RenderableMedia};
|
use notedeck::{JobsCache, RenderableMedia};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
@@ -16,7 +14,6 @@ pub struct NoteContents<'a, 'd> {
|
|||||||
note_context: &'a mut NoteContext<'d>,
|
note_context: &'a mut NoteContext<'d>,
|
||||||
txn: &'a Transaction,
|
txn: &'a Transaction,
|
||||||
note: &'a Note<'a>,
|
note: &'a Note<'a>,
|
||||||
parent: Option<&'a Note<'a>>,
|
|
||||||
options: NoteOptions,
|
options: NoteOptions,
|
||||||
pub action: Option<NoteAction>,
|
pub action: Option<NoteAction>,
|
||||||
jobs: &'a mut JobsCache,
|
jobs: &'a mut JobsCache,
|
||||||
@@ -28,7 +25,6 @@ impl<'a, 'd> NoteContents<'a, 'd> {
|
|||||||
note_context: &'a mut NoteContext<'d>,
|
note_context: &'a mut NoteContext<'d>,
|
||||||
txn: &'a Transaction,
|
txn: &'a Transaction,
|
||||||
note: &'a Note,
|
note: &'a Note,
|
||||||
parent: Option<&'a Note>,
|
|
||||||
options: NoteOptions,
|
options: NoteOptions,
|
||||||
jobs: &'a mut JobsCache,
|
jobs: &'a mut JobsCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -36,7 +32,6 @@ impl<'a, 'd> NoteContents<'a, 'd> {
|
|||||||
note_context,
|
note_context,
|
||||||
txn,
|
txn,
|
||||||
note,
|
note,
|
||||||
parent,
|
|
||||||
options,
|
options,
|
||||||
action: None,
|
action: None,
|
||||||
jobs,
|
jobs,
|
||||||
@@ -51,7 +46,6 @@ impl egui::Widget for &mut NoteContents<'_, '_> {
|
|||||||
self.note_context,
|
self.note_context,
|
||||||
self.txn,
|
self.txn,
|
||||||
self.note,
|
self.note,
|
||||||
self.parent,
|
|
||||||
self.options,
|
self.options,
|
||||||
self.jobs,
|
self.jobs,
|
||||||
);
|
);
|
||||||
@@ -87,7 +81,7 @@ pub fn render_note_preview(
|
|||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
id: &[u8; 32],
|
id: &[u8; 32],
|
||||||
parent: Option<&Note>,
|
parent: NoteKey,
|
||||||
note_options: NoteOptions,
|
note_options: NoteOptions,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
) -> NoteResponse {
|
) -> NoteResponse {
|
||||||
@@ -118,12 +112,10 @@ pub fn render_note_preview(
|
|||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut view = NoteView::new(note_context, ¬e, note_options, jobs).preview_style();
|
NoteView::new(note_context, ¬e, note_options, jobs)
|
||||||
if let Some(parent) = parent {
|
.preview_style()
|
||||||
view = view.parent(parent);
|
.parent(parent)
|
||||||
}
|
.show(ui)
|
||||||
|
|
||||||
view.show(ui)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render note contents and surrounding info (client name, full date timestamp)
|
/// Render note contents and surrounding info (client name, full date timestamp)
|
||||||
@@ -132,12 +124,10 @@ fn render_note_contents(
|
|||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
note: &Note,
|
note: &Note,
|
||||||
parent: Option<&Note>,
|
|
||||||
options: NoteOptions,
|
options: NoteOptions,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
) -> NoteResponse {
|
) -> NoteResponse {
|
||||||
let response =
|
let response = render_undecorated_note_contents(ui, note_context, txn, note, options, jobs);
|
||||||
render_undecorated_note_contents(ui, note_context, txn, note, parent, options, jobs);
|
|
||||||
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
note_bottom_metadata_ui(
|
note_bottom_metadata_ui(
|
||||||
@@ -178,7 +168,6 @@ fn render_undecorated_note_contents<'a>(
|
|||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
note: &'a Note,
|
note: &'a Note,
|
||||||
parent: Option<&'a Note>,
|
|
||||||
options: NoteOptions,
|
options: NoteOptions,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
) -> NoteResponse {
|
) -> NoteResponse {
|
||||||
@@ -366,7 +355,7 @@ fn render_undecorated_note_contents<'a>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let preview_note_action = inline_note.and_then(|(id, _)| {
|
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
|
.action
|
||||||
.map(|a| match a {
|
.map(|a| match a {
|
||||||
NoteAction::Note { note_id, .. } => NoteAction::Note {
|
NoteAction::Note { note_id, .. } => NoteAction::Note {
|
||||||
@@ -383,28 +372,6 @@ fn render_undecorated_note_contents<'a>(
|
|||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
let carousel_id = egui::Id::new(("carousel", note.key().expect("expected tx note")));
|
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(
|
media_action = image_carousel(
|
||||||
ui,
|
ui,
|
||||||
note_context.img_cache,
|
note_context.img_cache,
|
||||||
@@ -412,7 +379,6 @@ fn render_undecorated_note_contents<'a>(
|
|||||||
jobs,
|
jobs,
|
||||||
&supported_medias,
|
&supported_medias,
|
||||||
carousel_id,
|
carousel_id,
|
||||||
trusted_media,
|
|
||||||
note_context.i18n,
|
note_context.i18n,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ pub fn image_carousel(
|
|||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
medias: &[RenderableMedia],
|
medias: &[RenderableMedia],
|
||||||
carousel_id: egui::Id,
|
carousel_id: egui::Id,
|
||||||
trusted_media: bool,
|
|
||||||
i18n: &mut Localization,
|
i18n: &mut Localization,
|
||||||
note_options: NoteOptions,
|
note_options: NoteOptions,
|
||||||
) -> Option<MediaAction> {
|
) -> Option<MediaAction> {
|
||||||
@@ -68,7 +67,7 @@ pub fn image_carousel(
|
|||||||
job_pool,
|
job_pool,
|
||||||
jobs,
|
jobs,
|
||||||
media,
|
media,
|
||||||
trusted_media,
|
note_options.contains(NoteOptions::TrustMedia),
|
||||||
i18n,
|
i18n,
|
||||||
size,
|
size,
|
||||||
if note_options.contains(NoteOptions::NoAnimations) {
|
if note_options.contains(NoteOptions::NoAnimations) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use notedeck::{
|
|||||||
|
|
||||||
pub struct NoteView<'a, 'd> {
|
pub struct NoteView<'a, 'd> {
|
||||||
note_context: &'a mut NoteContext<'d>,
|
note_context: &'a mut NoteContext<'d>,
|
||||||
parent: Option<&'a Note<'a>>,
|
parent: Option<NoteKey>,
|
||||||
note: &'a nostrdb::Note<'a>,
|
note: &'a nostrdb::Note<'a>,
|
||||||
flags: NoteOptions,
|
flags: NoteOptions,
|
||||||
jobs: &'a mut JobsCache,
|
jobs: &'a mut JobsCache,
|
||||||
@@ -85,7 +85,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
flags: NoteOptions,
|
flags: NoteOptions,
|
||||||
jobs: &'a mut JobsCache,
|
jobs: &'a mut JobsCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let parent: Option<&Note> = None;
|
let parent: Option<NoteKey> = None;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
note_context,
|
note_context,
|
||||||
@@ -209,7 +209,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(mut self, parent: &'a Note<'a>) -> Self {
|
pub fn parent(mut self, parent: NoteKey) -> Self {
|
||||||
self.parent = Some(parent);
|
self.parent = Some(parent);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -255,7 +255,6 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
self.note_context,
|
self.note_context,
|
||||||
txn,
|
txn,
|
||||||
self.note,
|
self.note,
|
||||||
self.parent,
|
|
||||||
self.flags,
|
self.flags,
|
||||||
self.jobs,
|
self.jobs,
|
||||||
));
|
));
|
||||||
@@ -303,6 +302,18 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> NoteResponse {
|
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) {
|
if self.options().contains(NoteOptions::Textmode) {
|
||||||
NoteResponse::new(self.textmode_ui(ui))
|
NoteResponse::new(self.textmode_ui(ui))
|
||||||
} else if self.options().contains(NoteOptions::Framed) {
|
} else if self.options().contains(NoteOptions::Framed) {
|
||||||
@@ -426,14 +437,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut contents = NoteContents::new(
|
let mut contents =
|
||||||
self.note_context,
|
NoteContents::new(self.note_context, txn, self.note, self.flags, self.jobs);
|
||||||
txn,
|
|
||||||
self.note,
|
|
||||||
self.parent,
|
|
||||||
self.flags,
|
|
||||||
self.jobs,
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.add(&mut contents);
|
ui.add(&mut contents);
|
||||||
|
|
||||||
@@ -526,14 +531,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut contents = NoteContents::new(
|
let mut contents =
|
||||||
self.note_context,
|
NoteContents::new(self.note_context, txn, self.note, self.flags, self.jobs);
|
||||||
txn,
|
|
||||||
self.note,
|
|
||||||
self.parent,
|
|
||||||
self.flags,
|
|
||||||
self.jobs,
|
|
||||||
);
|
|
||||||
ui.add(&mut contents);
|
ui.add(&mut contents);
|
||||||
|
|
||||||
note_action = contents.action.or(note_action);
|
note_action = contents.action.or(note_action);
|
||||||
@@ -577,12 +576,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
.ndb
|
.ndb
|
||||||
.get_profile_by_pubkey(txn, self.note.pubkey());
|
.get_profile_by_pubkey(txn, self.note.pubkey());
|
||||||
|
|
||||||
let hitbox_id = note_hitbox_id(
|
let hitbox_id = note_hitbox_id(note_key, self.options(), self.parent);
|
||||||
note_key,
|
|
||||||
self.options(),
|
|
||||||
self.parent
|
|
||||||
.map(|n| n.key().expect("todo: support non-db notes")),
|
|
||||||
);
|
|
||||||
let maybe_hitbox = maybe_note_hitbox(ui, hitbox_id);
|
let maybe_hitbox = maybe_note_hitbox(ui, hitbox_id);
|
||||||
|
|
||||||
// wide design
|
// wide design
|
||||||
@@ -749,7 +743,7 @@ fn note_hitbox_id(
|
|||||||
|
|
||||||
fn maybe_note_hitbox(ui: &mut egui::Ui, hitbox_id: egui::Id) -> Option<Response> {
|
fn maybe_note_hitbox(ui: &mut egui::Ui, hitbox_id: egui::Id) -> Option<Response> {
|
||||||
ui.ctx()
|
ui.ctx()
|
||||||
.data_mut(|d| d.get_persisted(hitbox_id))
|
.data_mut(|d| d.get_temp(hitbox_id))
|
||||||
.map(|note_size: Vec2| {
|
.map(|note_size: Vec2| {
|
||||||
// The hitbox should extend the entire width of the
|
// The hitbox should extend the entire width of the
|
||||||
// container. The hitbox height was cached last layout.
|
// container. The hitbox height was cached last layout.
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ bitflags! {
|
|||||||
|
|
||||||
/// The note is a notification
|
/// The note is a notification
|
||||||
const Notification = 1 << 19;
|
const Notification = 1 << 19;
|
||||||
|
|
||||||
|
/// There is enough trust to show media in this note
|
||||||
|
const TrustMedia = 1 << 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user