mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-19 09:34:19 +01:00
prop drag id through responses instead of manual wiring
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -7,6 +7,7 @@ use notedeck_ui::nip51_set::Nip51SetUiCache;
|
|||||||
pub use crate::accounts::route::AccountsResponse;
|
pub use crate::accounts::route::AccountsResponse;
|
||||||
use crate::app::get_active_columns_mut;
|
use crate::app::get_active_columns_mut;
|
||||||
use crate::decks::DecksCache;
|
use crate::decks::DecksCache;
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::onboarding::Onboarding;
|
use crate::onboarding::Onboarding;
|
||||||
use crate::profile::send_new_contact_list;
|
use crate::profile::send_new_contact_list;
|
||||||
use crate::subscriptions::Subscriptions;
|
use crate::subscriptions::Subscriptions;
|
||||||
@@ -81,7 +82,7 @@ pub fn render_accounts_route(
|
|||||||
onboarding: &mut Onboarding,
|
onboarding: &mut Onboarding,
|
||||||
follow_packs_ui: &mut Nip51SetUiCache,
|
follow_packs_ui: &mut Nip51SetUiCache,
|
||||||
route: AccountsRoute,
|
route: AccountsRoute,
|
||||||
) -> Option<AccountsResponse> {
|
) -> BodyResponse<AccountsResponse> {
|
||||||
match route {
|
match route {
|
||||||
AccountsRoute::Accounts => AccountsView::new(
|
AccountsRoute::Accounts => AccountsView::new(
|
||||||
app_ctx.ndb,
|
app_ctx.ndb,
|
||||||
@@ -90,15 +91,15 @@ pub fn render_accounts_route(
|
|||||||
app_ctx.i18n,
|
app_ctx.i18n,
|
||||||
)
|
)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.inner
|
.map_output(AccountsRouteResponse::Accounts)
|
||||||
.map(AccountsRouteResponse::Accounts)
|
.map_output(AccountsResponse::Account),
|
||||||
.map(AccountsResponse::Account),
|
|
||||||
AccountsRoute::AddAccount => {
|
AccountsRoute::AddAccount => {
|
||||||
AccountLoginView::new(login_state, app_ctx.clipboard, app_ctx.i18n)
|
let action = AccountLoginView::new(login_state, app_ctx.clipboard, app_ctx.i18n)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.inner
|
.inner
|
||||||
.map(AccountsRouteResponse::AddAccount)
|
.map(AccountsRouteResponse::AddAccount)
|
||||||
.map(AccountsResponse::Account)
|
.map(AccountsResponse::Account);
|
||||||
|
BodyResponse::output(action)
|
||||||
}
|
}
|
||||||
AccountsRoute::Onboarding => FollowPackOnboardingView::new(
|
AccountsRoute::Onboarding => FollowPackOnboardingView::new(
|
||||||
onboarding,
|
onboarding,
|
||||||
@@ -110,7 +111,7 @@ pub fn render_accounts_route(
|
|||||||
jobs,
|
jobs,
|
||||||
)
|
)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.map(|r| match r {
|
.map_output(|r| match r {
|
||||||
OnboardingResponse::FollowPacks(follow_packs_response) => {
|
OnboardingResponse::FollowPacks(follow_packs_response) => {
|
||||||
AccountsResponse::Account(AccountsRouteResponse::AddAccount(
|
AccountsResponse::Account(AccountsRouteResponse::AddAccount(
|
||||||
AccountLoginResponse::Onboarding(follow_packs_response),
|
AccountLoginResponse::Onboarding(follow_packs_response),
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use crate::{
|
|||||||
Damus,
|
Damus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use egui::scroll_area::ScrollAreaOutput;
|
||||||
use egui_nav::{Nav, NavAction, NavResponse, NavUiType, Percent, PopupResponse, PopupSheet};
|
use egui_nav::{Nav, NavAction, NavResponse, NavUiType, Percent, PopupResponse, PopupSheet};
|
||||||
use enostr::ProfileState;
|
use enostr::ProfileState;
|
||||||
use nostrdb::{Filter, Ndb, Transaction};
|
use nostrdb::{Filter, Ndb, Transaction};
|
||||||
@@ -532,7 +533,7 @@ fn render_nav_body(
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
col: usize,
|
col: usize,
|
||||||
inner_rect: egui::Rect,
|
inner_rect: egui::Rect,
|
||||||
) -> Option<RenderNavAction> {
|
) -> BodyResponse<RenderNavAction> {
|
||||||
let mut note_context = NoteContext {
|
let mut note_context = NoteContext {
|
||||||
ndb: ctx.ndb,
|
ndb: ctx.ndb,
|
||||||
accounts: ctx.accounts,
|
accounts: ctx.accounts,
|
||||||
@@ -555,7 +556,7 @@ fn render_nav_body(
|
|||||||
.is_some_and(|ind| ind == col)
|
.is_some_and(|ind| ind == col)
|
||||||
&& app.options.contains(AppOptions::ScrollToTop);
|
&& app.options.contains(AppOptions::ScrollToTop);
|
||||||
|
|
||||||
let nav_action = render_timeline_route(
|
let resp = render_timeline_route(
|
||||||
&mut app.timeline_cache,
|
&mut app.timeline_cache,
|
||||||
kind,
|
kind,
|
||||||
col,
|
col,
|
||||||
@@ -574,7 +575,7 @@ fn render_nav_body(
|
|||||||
app.options.remove(AppOptions::ScrollToTop);
|
app.options.remove(AppOptions::ScrollToTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav_action
|
resp
|
||||||
}
|
}
|
||||||
Route::Thread(selection) => render_thread_route(
|
Route::Thread(selection) => render_thread_route(
|
||||||
&mut app.threads,
|
&mut app.threads,
|
||||||
@@ -585,8 +586,8 @@ fn render_nav_body(
|
|||||||
&mut note_context,
|
&mut note_context,
|
||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
),
|
),
|
||||||
Route::Accounts(amr) => 's: {
|
Route::Accounts(amr) => {
|
||||||
let Some(action) = render_accounts_route(
|
let resp = render_accounts_route(
|
||||||
ui,
|
ui,
|
||||||
ctx,
|
ctx,
|
||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
@@ -594,11 +595,9 @@ fn render_nav_body(
|
|||||||
&mut app.onboarding,
|
&mut app.onboarding,
|
||||||
&mut app.view_state.follow_packs,
|
&mut app.view_state.follow_packs,
|
||||||
*amr,
|
*amr,
|
||||||
) else {
|
);
|
||||||
break 's None;
|
|
||||||
};
|
|
||||||
|
|
||||||
match action {
|
resp.map_output_maybe(|action| match action {
|
||||||
AccountsResponse::ViewProfile(pubkey) => {
|
AccountsResponse::ViewProfile(pubkey) => {
|
||||||
Some(RenderNavAction::NoteAction(NoteAction::Profile(pubkey)))
|
Some(RenderNavAction::NoteAction(NoteAction::Profile(pubkey)))
|
||||||
}
|
}
|
||||||
@@ -611,11 +610,11 @@ fn render_nav_body(
|
|||||||
.accounts_action
|
.accounts_action
|
||||||
.map(|f| RenderNavAction::SwitchingAction(SwitchingAction::Accounts(f)))
|
.map(|f| RenderNavAction::SwitchingAction(SwitchingAction::Accounts(f)))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
Route::Relays => RelayView::new(ctx.pool, &mut app.view_state.id_string_map, ctx.i18n)
|
Route::Relays => RelayView::new(ctx.pool, &mut app.view_state.id_string_map, ctx.i18n)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.map(RenderNavAction::RelayAction),
|
.map_output(RenderNavAction::RelayAction),
|
||||||
|
|
||||||
Route::Settings => SettingsView::new(
|
Route::Settings => SettingsView::new(
|
||||||
ctx.settings.get_settings_mut(),
|
ctx.settings.get_settings_mut(),
|
||||||
@@ -624,7 +623,7 @@ fn render_nav_body(
|
|||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
)
|
)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.map(RenderNavAction::SettingsAction),
|
.map_output(RenderNavAction::SettingsAction),
|
||||||
|
|
||||||
Route::Reply(id) => {
|
Route::Reply(id) => {
|
||||||
let txn = if let Ok(txn) = Transaction::new(ctx.ndb) {
|
let txn = if let Ok(txn) = Transaction::new(ctx.ndb) {
|
||||||
@@ -635,7 +634,7 @@ fn render_nav_body(
|
|||||||
"Reply to unknown note",
|
"Reply to unknown note",
|
||||||
"Error message when reply note cannot be found"
|
"Error message when reply note cannot be found"
|
||||||
));
|
));
|
||||||
return None;
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
let note = if let Ok(note) = ctx.ndb.get_note_by_id(&txn, id.bytes()) {
|
let note = if let Ok(note) = ctx.ndb.get_note_by_id(&txn, id.bytes()) {
|
||||||
@@ -646,18 +645,20 @@ fn render_nav_body(
|
|||||||
"Reply to unknown note",
|
"Reply to unknown note",
|
||||||
"Error message when reply note cannot be found"
|
"Error message when reply note cannot be found"
|
||||||
));
|
));
|
||||||
return None;
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
let poster = ctx.accounts.selected_filled()?;
|
let Some(poster) = ctx.accounts.selected_filled() else {
|
||||||
|
return BodyResponse::none();
|
||||||
|
};
|
||||||
|
|
||||||
let action = {
|
let resp = {
|
||||||
let draft = app.drafts.reply_mut(note.id());
|
let draft = app.drafts.reply_mut(note.id());
|
||||||
|
|
||||||
let mut options = app.note_options;
|
let mut options = app.note_options;
|
||||||
options.set(NoteOptions::Wide, false);
|
options.set(NoteOptions::Wide, false);
|
||||||
|
|
||||||
let response = ui::PostReplyView::new(
|
ui::PostReplyView::new(
|
||||||
&mut note_context,
|
&mut note_context,
|
||||||
poster,
|
poster,
|
||||||
draft,
|
draft,
|
||||||
@@ -667,12 +668,10 @@ fn render_nav_body(
|
|||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
col,
|
col,
|
||||||
)
|
)
|
||||||
.show(ui);
|
.show(ui)
|
||||||
|
|
||||||
response.action
|
|
||||||
};
|
};
|
||||||
|
|
||||||
action.map(Into::into)
|
resp.map_output_maybe(|o| Some(o.action?.into()))
|
||||||
}
|
}
|
||||||
Route::Quote(id) => {
|
Route::Quote(id) => {
|
||||||
let txn = Transaction::new(ctx.ndb).expect("txn");
|
let txn = Transaction::new(ctx.ndb).expect("txn");
|
||||||
@@ -685,10 +684,13 @@ fn render_nav_body(
|
|||||||
"Quote of unknown note",
|
"Quote of unknown note",
|
||||||
"Error message when quote note cannot be found"
|
"Error message when quote note cannot be found"
|
||||||
));
|
));
|
||||||
return None;
|
return BodyResponse::none();
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(poster) = ctx.accounts.selected_filled() else {
|
||||||
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
let poster = ctx.accounts.selected_filled()?;
|
|
||||||
let draft = app.drafts.quote_mut(note.id());
|
let draft = app.drafts.quote_mut(note.id());
|
||||||
|
|
||||||
let response = crate::ui::note::QuoteRepostView::new(
|
let response = crate::ui::note::QuoteRepostView::new(
|
||||||
@@ -703,10 +705,12 @@ fn render_nav_body(
|
|||||||
)
|
)
|
||||||
.show(ui);
|
.show(ui);
|
||||||
|
|
||||||
response.action.map(Into::into)
|
response.map_output_maybe(|o| Some(o.action?.into()))
|
||||||
}
|
}
|
||||||
Route::ComposeNote => {
|
Route::ComposeNote => {
|
||||||
let kp = ctx.accounts.get_selected_account().key.to_full()?;
|
let Some(kp) = ctx.accounts.get_selected_account().key.to_full() else {
|
||||||
|
return BodyResponse::none();
|
||||||
|
};
|
||||||
let draft = app.drafts.compose_mut();
|
let draft = app.drafts.compose_mut();
|
||||||
|
|
||||||
let txn = Transaction::new(ctx.ndb).expect("txn");
|
let txn = Transaction::new(ctx.ndb).expect("txn");
|
||||||
@@ -721,16 +725,16 @@ fn render_nav_body(
|
|||||||
)
|
)
|
||||||
.ui(&txn, ui);
|
.ui(&txn, ui);
|
||||||
|
|
||||||
post_response.action.map(Into::into)
|
post_response.map_output_maybe(|o| Some(o.action?.into()))
|
||||||
}
|
}
|
||||||
Route::AddColumn(route) => {
|
Route::AddColumn(route) => {
|
||||||
render_add_column_routes(ui, app, ctx, col, route);
|
render_add_column_routes(ui, app, ctx, col, route);
|
||||||
|
|
||||||
None
|
BodyResponse::none()
|
||||||
}
|
}
|
||||||
Route::Support => {
|
Route::Support => {
|
||||||
SupportView::new(&mut app.support, ctx.i18n).show(ui);
|
SupportView::new(&mut app.support, ctx.i18n).show(ui);
|
||||||
None
|
BodyResponse::none()
|
||||||
}
|
}
|
||||||
Route::Search => {
|
Route::Search => {
|
||||||
let id = ui.id().with(("search", depth, col));
|
let id = ui.id().with(("search", depth, col));
|
||||||
@@ -759,7 +763,7 @@ fn render_nav_body(
|
|||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
)
|
)
|
||||||
.show(ui)
|
.show(ui)
|
||||||
.map(RenderNavAction::NoteAction)
|
.map_output(RenderNavAction::NoteAction)
|
||||||
}
|
}
|
||||||
Route::NewDeck => {
|
Route::NewDeck => {
|
||||||
let id = ui.id().with("new-deck");
|
let id = ui.id().with("new-deck");
|
||||||
@@ -784,7 +788,8 @@ fn render_nav_body(
|
|||||||
.get_selected_router()
|
.get_selected_router()
|
||||||
.go_back();
|
.go_back();
|
||||||
}
|
}
|
||||||
resp
|
|
||||||
|
BodyResponse::output(resp)
|
||||||
}
|
}
|
||||||
Route::EditDeck(index) => {
|
Route::EditDeck(index) => {
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
@@ -816,31 +821,37 @@ fn render_nav_body(
|
|||||||
.go_back();
|
.go_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
action
|
BodyResponse::output(action)
|
||||||
}
|
}
|
||||||
Route::EditProfile(pubkey) => {
|
Route::EditProfile(pubkey) => {
|
||||||
let mut action = None;
|
|
||||||
let Some(kp) = ctx.accounts.get_full(pubkey) else {
|
let Some(kp) = ctx.accounts.get_full(pubkey) else {
|
||||||
error!("Pubkey in EditProfile route did not have an nsec attached in Accounts");
|
error!("Pubkey in EditProfile route did not have an nsec attached in Accounts");
|
||||||
return None;
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(state) = app.view_state.pubkey_to_profile_state.get_mut(kp.pubkey) else {
|
let Some(state) = app.view_state.pubkey_to_profile_state.get_mut(kp.pubkey) else {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"No profile state when navigating to EditProfile... was handle_navigating_edit_profile not called?"
|
"No profile state when navigating to EditProfile... was handle_navigating_edit_profile not called?"
|
||||||
);
|
);
|
||||||
return action;
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
if EditProfileView::new(ctx.i18n, state, ctx.img_cache, ctx.clipboard).ui(ui) {
|
EditProfileView::new(ctx.i18n, state, ctx.img_cache, ctx.clipboard)
|
||||||
if let Some(state) = app.view_state.pubkey_to_profile_state.get(kp.pubkey) {
|
.ui(ui)
|
||||||
action = Some(RenderNavAction::ProfileAction(ProfileAction::SaveChanges(
|
.map_output_maybe(|save| {
|
||||||
|
if save {
|
||||||
|
app.view_state
|
||||||
|
.pubkey_to_profile_state
|
||||||
|
.get(kp.pubkey)
|
||||||
|
.map(|state| {
|
||||||
|
RenderNavAction::ProfileAction(ProfileAction::SaveChanges(
|
||||||
SaveProfileChanges::new(kp.to_full(), state.clone()),
|
SaveProfileChanges::new(kp.to_full(), state.clone()),
|
||||||
)))
|
))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
action
|
|
||||||
}
|
}
|
||||||
Route::Wallet(wallet_type) => {
|
Route::Wallet(wallet_type) => {
|
||||||
let state = match wallet_type {
|
let state = match wallet_type {
|
||||||
@@ -887,13 +898,13 @@ fn render_nav_body(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletView::new(state, ctx.i18n, ctx.clipboard)
|
BodyResponse::output(WalletView::new(state, ctx.i18n, ctx.clipboard).ui(ui))
|
||||||
.ui(ui)
|
.map_output(RenderNavAction::WalletAction)
|
||||||
.map(RenderNavAction::WalletAction)
|
|
||||||
}
|
}
|
||||||
Route::CustomizeZapAmount(target) => {
|
Route::CustomizeZapAmount(target) => {
|
||||||
let txn = Transaction::new(ctx.ndb).expect("txn");
|
let txn = Transaction::new(ctx.ndb).expect("txn");
|
||||||
let default_msats = get_current_default_msats(ctx.accounts, ctx.global_wallet);
|
let default_msats = get_current_default_msats(ctx.accounts, ctx.global_wallet);
|
||||||
|
BodyResponse::output(
|
||||||
CustomZapView::new(
|
CustomZapView::new(
|
||||||
ctx.i18n,
|
ctx.i18n,
|
||||||
ctx.img_cache,
|
ctx.img_cache,
|
||||||
@@ -902,8 +913,9 @@ fn render_nav_body(
|
|||||||
&target.zap_recipient,
|
&target.zap_recipient,
|
||||||
default_msats,
|
default_msats,
|
||||||
)
|
)
|
||||||
.ui(ui)
|
.ui(ui),
|
||||||
.map(|msats| {
|
)
|
||||||
|
.map_output(|msats| {
|
||||||
get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache)
|
get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache)
|
||||||
.column_mut(col)
|
.column_mut(col)
|
||||||
.router_mut()
|
.router_mut()
|
||||||
@@ -919,6 +931,87 @@ fn render_nav_body(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BodyResponse<R> {
|
||||||
|
pub drag_id: Option<egui::Id>, // the id which was used for dragging.
|
||||||
|
pub output: Option<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> BodyResponse<R> {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
drag_id: None,
|
||||||
|
output: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scroll(output: ScrollAreaOutput<Option<R>>) -> Self {
|
||||||
|
Self {
|
||||||
|
drag_id: Some(Self::scroll_output_to_drag_id(output.id)),
|
||||||
|
output: output.inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scroll_id(&mut self, output: &ScrollAreaOutput<Option<R>>) {
|
||||||
|
self.drag_id = Some(Self::scroll_output_to_drag_id(output.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output(output: Option<R>) -> Self {
|
||||||
|
Self {
|
||||||
|
drag_id: None,
|
||||||
|
output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_output(&mut self, output: R) {
|
||||||
|
self.output = Some(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The id of an `egui::ScrollAreaOutput`
|
||||||
|
/// Should use `Self::scroll` when possible
|
||||||
|
pub fn scroll_raw(mut self, id: egui::Id) -> Self {
|
||||||
|
self.drag_id = Some(Self::scroll_output_to_drag_id(id));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The id which is directly used for dragging
|
||||||
|
pub fn set_drag_id_raw(&mut self, id: egui::Id) {
|
||||||
|
self.drag_id = Some(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_output_to_drag_id(id: egui::Id) -> egui::Id {
|
||||||
|
id.with("area")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_output<S>(self, f: impl FnOnce(R) -> S) -> BodyResponse<S> {
|
||||||
|
BodyResponse {
|
||||||
|
drag_id: self.drag_id,
|
||||||
|
output: self.output.map(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_output_maybe<S>(self, f: impl FnOnce(R) -> Option<S>) -> BodyResponse<S> {
|
||||||
|
BodyResponse {
|
||||||
|
drag_id: self.drag_id,
|
||||||
|
output: self.output.and_then(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_map_output<S>(self, f: impl FnOnce(Option<R>) -> S) -> BodyResponse<S> {
|
||||||
|
BodyResponse {
|
||||||
|
drag_id: self.drag_id,
|
||||||
|
output: Some(f(self.output)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// insert the contents of the new BodyResponse if they are empty in Self
|
||||||
|
pub fn insert(&mut self, body: BodyResponse<R>) {
|
||||||
|
self.drag_id = self.drag_id.or(body.drag_id);
|
||||||
|
if self.output.is_none() {
|
||||||
|
self.output = body.output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use = "RenderNavResponse must be handled by calling .process_render_nav_response(..)"]
|
#[must_use = "RenderNavResponse must be handled by calling .process_render_nav_response(..)"]
|
||||||
pub fn render_nav(
|
pub fn render_nav(
|
||||||
col: usize,
|
col: usize,
|
||||||
@@ -967,7 +1060,9 @@ pub fn render_nav(
|
|||||||
.show_move_button(!narrow)
|
.show_move_button(!narrow)
|
||||||
.show_delete_button(!narrow)
|
.show_delete_button(!narrow)
|
||||||
.show(ui),
|
.show(ui),
|
||||||
NavUiType::Body => render_nav_body(ui, app, ctx, route, 1, col, inner_rect),
|
NavUiType::Body => {
|
||||||
|
render_nav_body(ui, app, ctx, route, 1, col, inner_rect).output
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return RenderNavResponse::new(col, NotedeckNavResponse::Popup(Box::new(resp)));
|
return RenderNavResponse::new(col, NotedeckNavResponse::Popup(Box::new(resp)));
|
||||||
@@ -1047,8 +1142,9 @@ pub fn render_nav(
|
|||||||
if let Some(top) = nav.routes().last() {
|
if let Some(top) = nav.routes().last() {
|
||||||
render_nav_body(ui, app, ctx, top, nav.routes().len(), col, inner_rect)
|
render_nav_body(ui, app, ctx, top, nav.routes().len(), col, inner_rect)
|
||||||
} else {
|
} else {
|
||||||
None
|
BodyResponse::none()
|
||||||
}
|
}
|
||||||
|
.output
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
nav::RenderNavAction,
|
nav::{BodyResponse, RenderNavAction},
|
||||||
profile::ProfileAction,
|
profile::ProfileAction,
|
||||||
timeline::{thread::Threads, ThreadSelection, TimelineCache, TimelineKind},
|
timeline::{thread::Threads, ThreadSelection, TimelineCache, TimelineKind},
|
||||||
ui::{self, ProfileView},
|
ui::{self, ProfileView},
|
||||||
@@ -20,7 +20,7 @@ pub fn render_timeline_route(
|
|||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
scroll_to_top: bool,
|
scroll_to_top: bool,
|
||||||
) -> Option<RenderNavAction> {
|
) -> BodyResponse<RenderNavAction> {
|
||||||
match kind {
|
match kind {
|
||||||
TimelineKind::List(_)
|
TimelineKind::List(_)
|
||||||
| TimelineKind::Search(_)
|
| TimelineKind::Search(_)
|
||||||
@@ -29,11 +29,11 @@ pub fn render_timeline_route(
|
|||||||
| TimelineKind::Universe
|
| TimelineKind::Universe
|
||||||
| TimelineKind::Hashtag(_)
|
| TimelineKind::Hashtag(_)
|
||||||
| TimelineKind::Generic(_) => {
|
| TimelineKind::Generic(_) => {
|
||||||
let note_action =
|
let resp =
|
||||||
ui::TimelineView::new(kind, timeline_cache, note_context, note_options, jobs, col)
|
ui::TimelineView::new(kind, timeline_cache, note_context, note_options, jobs, col)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
|
||||||
note_action.map(RenderNavAction::NoteAction)
|
resp.map_output(RenderNavAction::NoteAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineKind::Profile(pubkey) => {
|
TimelineKind::Profile(pubkey) => {
|
||||||
@@ -49,7 +49,7 @@ pub fn render_timeline_route(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// we render profiles like timelines if they are at the root
|
// we render profiles like timelines if they are at the root
|
||||||
let note_action = ui::TimelineView::new(
|
let resp = ui::TimelineView::new(
|
||||||
kind,
|
kind,
|
||||||
timeline_cache,
|
timeline_cache,
|
||||||
note_context,
|
note_context,
|
||||||
@@ -60,7 +60,7 @@ pub fn render_timeline_route(
|
|||||||
.scroll_to_top(scroll_to_top)
|
.scroll_to_top(scroll_to_top)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
|
||||||
note_action.map(RenderNavAction::NoteAction)
|
resp.map_output(RenderNavAction::NoteAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ pub fn render_thread_route(
|
|||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
) -> Option<RenderNavAction> {
|
) -> BodyResponse<RenderNavAction> {
|
||||||
// don't truncate thread notes for now, since they are
|
// don't truncate thread notes for now, since they are
|
||||||
// default truncated everywher eelse
|
// default truncated everywher eelse
|
||||||
note_options.set(NoteOptions::Truncate, false);
|
note_options.set(NoteOptions::Truncate, false);
|
||||||
@@ -92,7 +92,7 @@ pub fn render_thread_route(
|
|||||||
col,
|
col,
|
||||||
)
|
)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.map(Into::into)
|
.map_output(RenderNavAction::NoteAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -104,7 +104,7 @@ pub fn render_profile_route(
|
|||||||
note_options: NoteOptions,
|
note_options: NoteOptions,
|
||||||
note_context: &mut NoteContext,
|
note_context: &mut NoteContext,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
) -> Option<RenderNavAction> {
|
) -> BodyResponse<RenderNavAction> {
|
||||||
let profile_view = ProfileView::new(
|
let profile_view = ProfileView::new(
|
||||||
pubkey,
|
pubkey,
|
||||||
col,
|
col,
|
||||||
@@ -115,8 +115,7 @@ pub fn render_profile_route(
|
|||||||
)
|
)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
|
||||||
if let Some(action) = profile_view {
|
profile_view.map_output_maybe(|action| match action {
|
||||||
match action {
|
|
||||||
ui::profile::ProfileViewAction::EditProfile => note_context
|
ui::profile::ProfileViewAction::EditProfile => note_context
|
||||||
.accounts
|
.accounts
|
||||||
.get_full(pubkey)
|
.get_full(pubkey)
|
||||||
@@ -124,14 +123,11 @@ pub fn render_profile_route(
|
|||||||
ui::profile::ProfileViewAction::Note(note_action) => {
|
ui::profile::ProfileViewAction::Note(note_action) => {
|
||||||
Some(RenderNavAction::NoteAction(note_action))
|
Some(RenderNavAction::NoteAction(note_action))
|
||||||
}
|
}
|
||||||
ui::profile::ProfileViewAction::Follow(target_key) => Some(
|
ui::profile::ProfileViewAction::Follow(target_key) => Some(RenderNavAction::ProfileAction(
|
||||||
RenderNavAction::ProfileAction(ProfileAction::Follow(target_key)),
|
ProfileAction::Follow(target_key),
|
||||||
),
|
)),
|
||||||
ui::profile::ProfileViewAction::Unfollow(target_key) => Some(
|
ui::profile::ProfileViewAction::Unfollow(target_key) => Some(
|
||||||
RenderNavAction::ProfileAction(ProfileAction::Unfollow(target_key)),
|
RenderNavAction::ProfileAction(ProfileAction::Unfollow(target_key)),
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use notedeck_ui::profile::preview::SimpleProfilePreview;
|
|||||||
|
|
||||||
use notedeck_ui::app_images;
|
use notedeck_ui::app_images;
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
|
|
||||||
pub struct AccountsView<'a> {
|
pub struct AccountsView<'a> {
|
||||||
ndb: &'a Ndb,
|
ndb: &'a Ndb,
|
||||||
accounts: &'a Accounts,
|
accounts: &'a Accounts,
|
||||||
@@ -44,20 +46,26 @@ impl<'a> AccountsView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut Ui) -> InnerResponse<Option<AccountsViewResponse>> {
|
pub fn ui(&mut self, ui: &mut Ui) -> BodyResponse<AccountsViewResponse> {
|
||||||
|
let mut out = BodyResponse::none();
|
||||||
Frame::new().outer_margin(12.0).show(ui, |ui| {
|
Frame::new().outer_margin(12.0).show(ui, |ui| {
|
||||||
if let Some(resp) = Self::top_section_buttons_widget(ui, self.i18n).inner {
|
if let Some(resp) = Self::top_section_buttons_widget(ui, self.i18n).inner {
|
||||||
return Some(resp);
|
out.set_output(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
scroll_area()
|
let scroll_out = scroll_area()
|
||||||
.id_salt(AccountsView::scroll_id())
|
.id_salt(AccountsView::scroll_id())
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
Self::show_accounts(ui, self.accounts, self.ndb, self.img_cache, self.i18n)
|
Self::show_accounts(ui, self.accounts, self.ndb, self.img_cache, self.i18n)
|
||||||
})
|
});
|
||||||
.inner
|
|
||||||
})
|
out.set_scroll_id(&scroll_out);
|
||||||
|
if let Some(scroll_output) = scroll_out.inner {
|
||||||
|
out.set_output(scroll_output);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_id() -> egui::Id {
|
pub fn scroll_id() -> egui::Id {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use notedeck_ui::{
|
|||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
|
|
||||||
/// Displays user profiles for the user to pick from.
|
/// Displays user profiles for the user to pick from.
|
||||||
/// Useful for manually typing a username and selecting the profile desired
|
/// Useful for manually typing a username and selecting the profile desired
|
||||||
pub struct MentionPickerView<'a> {
|
pub struct MentionPickerView<'a> {
|
||||||
@@ -64,7 +66,11 @@ impl<'a> MentionPickerView<'a> {
|
|||||||
MentionPickerResponse::SelectResult(selection)
|
MentionPickerResponse::SelectResult(selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_in_rect(&mut self, rect: egui::Rect, ui: &mut egui::Ui) -> MentionPickerResponse {
|
pub fn show_in_rect(
|
||||||
|
&mut self,
|
||||||
|
rect: egui::Rect,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
) -> BodyResponse<MentionPickerResponse> {
|
||||||
let widget_id = ui.id().with("mention_results");
|
let widget_id = ui.id().with("mention_results");
|
||||||
let area_resp = egui::Area::new(widget_id)
|
let area_resp = egui::Area::new(widget_id)
|
||||||
.order(egui::Order::Foreground)
|
.order(egui::Order::Foreground)
|
||||||
@@ -102,15 +108,17 @@ impl<'a> MentionPickerView<'a> {
|
|||||||
let scroll_resp = ScrollArea::vertical()
|
let scroll_resp = ScrollArea::vertical()
|
||||||
.max_width(rect.width())
|
.max_width(rect.width())
|
||||||
.auto_shrink(Vec2b::FALSE)
|
.auto_shrink(Vec2b::FALSE)
|
||||||
.show(ui, |ui| self.show(ui, width));
|
.show(ui, |ui| Some(self.show(ui, width)));
|
||||||
ui.advance_cursor_after_rect(rect);
|
ui.advance_cursor_after_rect(rect);
|
||||||
|
|
||||||
|
BodyResponse::scroll(scroll_resp).map_output(|o| {
|
||||||
if close_button_resp {
|
if close_button_resp {
|
||||||
MentionPickerResponse::DeleteMention
|
MentionPickerResponse::DeleteMention
|
||||||
} else {
|
} else {
|
||||||
scroll_resp.inner
|
o
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
.inner
|
.inner
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::draft::{Draft, Drafts, MentionHint};
|
use crate::draft::{Draft, Drafts, MentionHint};
|
||||||
use crate::media_upload::nostrbuild_nip96_upload;
|
use crate::media_upload::nostrbuild_nip96_upload;
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::post::{downcast_post_buffer, MentionType, NewPost};
|
use crate::post::{downcast_post_buffer, MentionType, NewPost};
|
||||||
use crate::ui::mentions_picker::MentionPickerView;
|
use crate::ui::mentions_picker::MentionPickerView;
|
||||||
use crate::ui::{self, Preview, PreviewConfig};
|
use crate::ui::{self, Preview, PreviewConfig};
|
||||||
@@ -143,7 +144,7 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> egui::Response {
|
fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> EditBoxResponse {
|
||||||
ui.spacing_mut().item_spacing.x = 12.0;
|
ui.spacing_mut().item_spacing.x = 12.0;
|
||||||
|
|
||||||
let pfp_size = 24.0;
|
let pfp_size = 24.0;
|
||||||
@@ -221,37 +222,42 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
self.draft.buffer.selected_mention = false;
|
self.draft.buffer.selected_mention = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mention_hints_drag_id =
|
||||||
if let Some(cursor_index) = get_cursor_index(&out.state.cursor.char_range()) {
|
if let Some(cursor_index) = get_cursor_index(&out.state.cursor.char_range()) {
|
||||||
self.show_mention_hints(txn, ui, cursor_index, &out);
|
self.show_mention_hints(txn, ui, cursor_index, &out)
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let focused = out.response.has_focus();
|
let focused = out.response.has_focus();
|
||||||
|
|
||||||
ui.ctx()
|
ui.ctx()
|
||||||
.data_mut(|d| d.insert_temp(PostView::id(), focused));
|
.data_mut(|d| d.insert_temp(PostView::id(), focused));
|
||||||
|
|
||||||
out.response
|
EditBoxResponse {
|
||||||
|
resp: out.response,
|
||||||
|
mention_hints_drag_id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Displays the mention picker and handles when one is selected.
|
// Displays the mention picker and handles when one is selected.
|
||||||
|
// returns the drag id of the mention hint widget
|
||||||
fn show_mention_hints(
|
fn show_mention_hints(
|
||||||
&mut self,
|
&mut self,
|
||||||
txn: &nostrdb::Transaction,
|
txn: &nostrdb::Transaction,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
cursor_index: usize,
|
cursor_index: usize,
|
||||||
textedit_output: &TextEditOutput,
|
textedit_output: &TextEditOutput,
|
||||||
) {
|
) -> Option<egui::Id> {
|
||||||
let Some(mention) = self.draft.buffer.get_mention(cursor_index) else {
|
let mention = self.draft.buffer.get_mention(cursor_index)?;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if mention.info.mention_type != MentionType::Pending {
|
if mention.info.mention_type != MentionType::Pending {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.ctx().input(|r| r.key_pressed(egui::Key::Escape)) {
|
if ui.ctx().input(|r| r.key_pressed(egui::Key::Escape)) {
|
||||||
self.draft.buffer.delete_mention(mention.index);
|
self.draft.buffer.delete_mention(mention.index);
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mention_str = self.draft.buffer.get_mention_string(&mention);
|
let mention_str = self.draft.buffer.get_mention_string(&mention);
|
||||||
@@ -274,10 +280,8 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hint_rect = {
|
let hint_rect = {
|
||||||
let hint = if let Some(hint) = &self.draft.cur_mention_hint {
|
let Some(hint) = &self.draft.cur_mention_hint else {
|
||||||
hint
|
return None;
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hint_rect = self.inner_rect;
|
let mut hint_rect = self.inner_rect;
|
||||||
@@ -285,9 +289,11 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
hint_rect
|
hint_rect
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(res) = self.note_context.ndb.search_profile(txn, mention_str, 10) else {
|
let res = self
|
||||||
return;
|
.note_context
|
||||||
};
|
.ndb
|
||||||
|
.search_profile(txn, mention_str, 10)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let resp = MentionPickerView::new(
|
let resp = MentionPickerView::new(
|
||||||
self.note_context.img_cache,
|
self.note_context.img_cache,
|
||||||
@@ -298,7 +304,12 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
.show_in_rect(hint_rect, ui);
|
.show_in_rect(hint_rect, ui);
|
||||||
|
|
||||||
let mut selection_made = None;
|
let mut selection_made = None;
|
||||||
match resp {
|
|
||||||
|
let Some(out) = resp.output else {
|
||||||
|
return resp.drag_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
match out {
|
||||||
ui::mentions_picker::MentionPickerResponse::SelectResult(selection) => {
|
ui::mentions_picker::MentionPickerResponse::SelectResult(selection) => {
|
||||||
if let Some(hint_index) = selection {
|
if let Some(hint_index) = selection {
|
||||||
if let Some(pk) = res.get(hint_index) {
|
if let Some(pk) = res.get(hint_index) {
|
||||||
@@ -326,6 +337,8 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
if let Some(selection) = selection_made {
|
if let Some(selection) = selection_made {
|
||||||
selection.process(ui.ctx(), textedit_output);
|
selection.process(ui.ctx(), textedit_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp.drag_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focused(&self, ui: &egui::Ui) -> bool {
|
fn focused(&self, ui: &egui::Ui) -> bool {
|
||||||
@@ -341,14 +354,25 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
12
|
12
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> PostResponse {
|
pub fn ui(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
ScrollArea::vertical()
|
let scroll_out = ScrollArea::vertical()
|
||||||
.id_salt(PostView::scroll_id())
|
.id_salt(PostView::scroll_id())
|
||||||
.show(ui, |ui| self.ui_no_scroll(txn, ui))
|
.show(ui, |ui| Some(self.ui_no_scroll(txn, ui)));
|
||||||
.inner
|
|
||||||
|
let scroll_id = scroll_out.id;
|
||||||
|
if let Some(inner) = scroll_out.inner {
|
||||||
|
inner // should override the PostView scroll for the mention scroll
|
||||||
|
} else {
|
||||||
|
BodyResponse::none()
|
||||||
|
}
|
||||||
|
.scroll_raw(scroll_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui_no_scroll(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> PostResponse {
|
pub fn ui_no_scroll(
|
||||||
|
&mut self,
|
||||||
|
txn: &Transaction,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
) -> BodyResponse<PostResponse> {
|
||||||
while let Some(selected_file) = get_next_selected_file() {
|
while let Some(selected_file) = get_next_selected_file() {
|
||||||
match selected_file {
|
match selected_file {
|
||||||
Ok(selected_media) => {
|
Ok(selected_media) => {
|
||||||
@@ -393,7 +417,7 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
.inner
|
.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_ui(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> PostResponse {
|
fn input_ui(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
let edit_response = ui.horizontal(|ui| self.editbox(txn, ui)).inner;
|
let edit_response = ui.horizontal(|ui| self.editbox(txn, ui)).inner;
|
||||||
|
|
||||||
let note_response = if let PostType::Quote(id) = self.post_type {
|
let note_response = if let PostType::Quote(id) = self.post_type {
|
||||||
@@ -445,10 +469,14 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
.and_then(|nr| nr.action.map(PostAction::QuotedNoteAction))
|
.and_then(|nr| nr.action.map(PostAction::QuotedNoteAction))
|
||||||
.or(post_action.map(PostAction::NewPostAction));
|
.or(post_action.map(PostAction::NewPostAction));
|
||||||
|
|
||||||
PostResponse {
|
let mut resp = BodyResponse::output(action);
|
||||||
action,
|
if let Some(drag_id) = edit_response.mention_hints_drag_id {
|
||||||
edit_response,
|
resp.set_drag_id_raw(drag_id);
|
||||||
}
|
}
|
||||||
|
resp.maybe_map_output(|action| PostResponse {
|
||||||
|
action,
|
||||||
|
edit_response: edit_response.resp,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_buttons(&mut self, ui: &mut egui::Ui) -> Option<NewPostAction> {
|
fn input_buttons(&mut self, ui: &mut egui::Ui) -> Option<NewPostAction> {
|
||||||
@@ -596,6 +624,11 @@ impl<'a, 'd> PostView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EditBoxResponse {
|
||||||
|
resp: egui::Response,
|
||||||
|
mention_hints_drag_id: Option<egui::Id>,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn render_post_view_media(
|
fn render_post_view_media(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::{PostResponse, PostType};
|
use super::{PostResponse, PostType};
|
||||||
use crate::{
|
use crate::{
|
||||||
draft::Draft,
|
draft::Draft,
|
||||||
|
nav::BodyResponse,
|
||||||
ui::{self},
|
ui::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,14 +53,22 @@ impl<'a, 'd> QuoteRepostView<'a, 'd> {
|
|||||||
QuoteRepostView::id(col, note_id).with("scroll")
|
QuoteRepostView::id(col, note_id).with("scroll")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
pub fn show(&mut self, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
ScrollArea::vertical()
|
let scroll_out = ScrollArea::vertical()
|
||||||
.id_salt(self.scroll_id)
|
.id_salt(self.scroll_id)
|
||||||
.show(ui, |ui| self.show_internal(ui))
|
.show(ui, |ui| Some(self.show_internal(ui)));
|
||||||
.inner
|
|
||||||
|
let scroll_id = scroll_out.id;
|
||||||
|
|
||||||
|
if let Some(inner) = scroll_out.inner {
|
||||||
|
inner
|
||||||
|
} else {
|
||||||
|
BodyResponse::none()
|
||||||
|
}
|
||||||
|
.scroll_raw(scroll_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_internal(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
fn show_internal(&mut self, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
let quoting_note_id = self.quoting_note.id();
|
let quoting_note_id = self.quoting_note.id();
|
||||||
|
|
||||||
let post_resp = ui::PostView::new(
|
let post_resp = ui::PostView::new(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::draft::Draft;
|
use crate::draft::Draft;
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
self,
|
self,
|
||||||
note::{PostAction, PostResponse, PostType},
|
note::{PostAction, PostResponse, PostType},
|
||||||
@@ -52,16 +53,23 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
|
|||||||
PostReplyView::id(col, note_id).with("scroll")
|
PostReplyView::id(col, note_id).with("scroll")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
pub fn show(&mut self, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
ScrollArea::vertical()
|
let scroll_out = ScrollArea::vertical()
|
||||||
.id_salt(self.scroll_id)
|
.id_salt(self.scroll_id)
|
||||||
.stick_to_bottom(true)
|
.stick_to_bottom(true)
|
||||||
.show(ui, |ui| self.show_internal(ui))
|
.show(ui, |ui| Some(self.show_internal(ui)));
|
||||||
.inner
|
|
||||||
|
let scroll_id = scroll_out.id;
|
||||||
|
if let Some(inner) = scroll_out.inner {
|
||||||
|
inner
|
||||||
|
} else {
|
||||||
|
BodyResponse::none()
|
||||||
|
}
|
||||||
|
.scroll_raw(scroll_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// no scroll
|
// no scroll
|
||||||
fn show_internal(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
fn show_internal(&mut self, ui: &mut egui::Ui) -> BodyResponse<PostResponse> {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
let avail_rect = ui.available_rect_before_wrap();
|
let avail_rect = ui.available_rect_before_wrap();
|
||||||
|
|
||||||
@@ -103,17 +111,22 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
|
|||||||
.ui_no_scroll(self.note.txn().unwrap(), ui)
|
.ui_no_scroll(self.note.txn().unwrap(), ui)
|
||||||
};
|
};
|
||||||
|
|
||||||
post_response.action = post_response
|
post_response = post_response.map_output(|mut o| {
|
||||||
|
o.action = o
|
||||||
.action
|
.action
|
||||||
.or(quoted_note.action.map(PostAction::QuotedNoteAction));
|
.or(quoted_note.action.map(PostAction::QuotedNoteAction));
|
||||||
|
o
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(p) = &post_response.output {
|
||||||
reply_line_ui(
|
reply_line_ui(
|
||||||
&rect_before_post,
|
&rect_before_post,
|
||||||
&post_response.edit_response,
|
&p.edit_response,
|
||||||
pfp_offset as f32,
|
pfp_offset as f32,
|
||||||
&avail_rect,
|
&avail_rect,
|
||||||
ui,
|
ui,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// NOTE(jb55): We add some space so that you can scroll to
|
// NOTE(jb55): We add some space so that you can scroll to
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use notedeck_ui::{
|
|||||||
nip51_set::{Nip51SetUiCache, Nip51SetWidget, Nip51SetWidgetAction, Nip51SetWidgetFlags},
|
nip51_set::{Nip51SetUiCache, Nip51SetWidget, Nip51SetWidgetAction, Nip51SetWidgetFlags},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{onboarding::Onboarding, ui::widgets::styled_button};
|
use crate::{nav::BodyResponse, onboarding::Onboarding, ui::widgets::styled_button};
|
||||||
|
|
||||||
/// Display Follow Packs for the user to choose from authors trusted by the Damus team
|
/// Display Follow Packs for the user to choose from authors trusted by the Damus team
|
||||||
pub struct FollowPackOnboardingView<'a> {
|
pub struct FollowPackOnboardingView<'a> {
|
||||||
@@ -56,17 +56,17 @@ impl<'a> FollowPackOnboardingView<'a> {
|
|||||||
egui::Id::new("follow_pack_onboarding")
|
egui::Id::new("follow_pack_onboarding")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<OnboardingResponse> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<OnboardingResponse> {
|
||||||
let Some(follow_pack_state) = self.onboarding.get_follow_packs() else {
|
let Some(follow_pack_state) = self.onboarding.get_follow_packs() else {
|
||||||
return Some(OnboardingResponse::FollowPacks(
|
return BodyResponse::output(Some(OnboardingResponse::FollowPacks(
|
||||||
FollowPacksResponse::NoFollowPacks,
|
FollowPacksResponse::NoFollowPacks,
|
||||||
));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_height = ui.available_height() - 48.0;
|
let max_height = ui.available_height() - 48.0;
|
||||||
|
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
ScrollArea::vertical()
|
let scroll_out = ScrollArea::vertical()
|
||||||
.id_salt(Self::scroll_id())
|
.id_salt(Self::scroll_id())
|
||||||
.max_height(max_height)
|
.max_height(max_height)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
@@ -114,6 +114,6 @@ impl<'a> FollowPackOnboardingView<'a> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
action
|
BodyResponse::output(action).scroll_raw(scroll_out.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use notedeck::{profile::unwrap_profile_url, tr, Images, Localization, NotedeckTe
|
|||||||
use notedeck_ui::context_menu::{input_context, PasteBehavior};
|
use notedeck_ui::context_menu::{input_context, PasteBehavior};
|
||||||
use notedeck_ui::{profile::banner, ProfilePic};
|
use notedeck_ui::{profile::banner, ProfilePic};
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
|
|
||||||
pub struct EditProfileView<'a> {
|
pub struct EditProfileView<'a> {
|
||||||
state: &'a mut ProfileState,
|
state: &'a mut ProfileState,
|
||||||
clipboard: &'a mut Clipboard,
|
clipboard: &'a mut Clipboard,
|
||||||
@@ -34,8 +36,8 @@ impl<'a> EditProfileView<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return true to save
|
// return true to save
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> bool {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<bool> {
|
||||||
ScrollArea::vertical()
|
let scroll_out = ScrollArea::vertical()
|
||||||
.id_salt(EditProfileView::scroll_id())
|
.id_salt(EditProfileView::scroll_id())
|
||||||
.stick_to_bottom(true)
|
.stick_to_bottom(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
@@ -71,9 +73,9 @@ impl<'a> EditProfileView<'a> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
save
|
Some(save)
|
||||||
})
|
});
|
||||||
.inner
|
BodyResponse::scroll(scroll_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner(&mut self, ui: &mut egui::Ui, padding: f32) {
|
fn inner(&mut self, ui: &mut egui::Ui, padding: f32) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use robius_open::Uri;
|
|||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
nav::BodyResponse,
|
||||||
timeline::{TimelineCache, TimelineKind},
|
timeline::{TimelineCache, TimelineKind},
|
||||||
ui::timeline::{tabs_ui, TimelineTabView},
|
ui::timeline::{tabs_ui, TimelineTabView},
|
||||||
};
|
};
|
||||||
@@ -68,13 +69,16 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
egui::Id::new(("profile_scroll", col_id, profile_pubkey))
|
egui::Id::new(("profile_scroll", col_id, profile_pubkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<ProfileViewAction> {
|
||||||
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey);
|
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey);
|
||||||
let scroll_area = ScrollArea::vertical().id_salt(scroll_id).animated(false);
|
let scroll_area = ScrollArea::vertical().id_salt(scroll_id).animated(false);
|
||||||
|
|
||||||
let profile_timeline = self
|
let Some(profile_timeline) = self
|
||||||
.timeline_cache
|
.timeline_cache
|
||||||
.get_mut(&TimelineKind::Profile(*self.pubkey))?;
|
.get_mut(&TimelineKind::Profile(*self.pubkey))
|
||||||
|
else {
|
||||||
|
return BodyResponse::none();
|
||||||
|
};
|
||||||
|
|
||||||
let output = scroll_area.show(ui, |ui| {
|
let output = scroll_area.show(ui, |ui| {
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
@@ -132,7 +136,7 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
|||||||
// only allow front insert when the profile body is fully obstructed
|
// 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();
|
profile_timeline.enable_front_insert = output.inner.body_end_pos < ui.clip_rect().top();
|
||||||
|
|
||||||
output.inner.action
|
BodyResponse::output(output.inner.action).scroll_raw(output.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::ui::{Preview, PreviewConfig};
|
use crate::ui::{Preview, PreviewConfig};
|
||||||
use egui::{Align, Button, CornerRadius, Frame, Id, Layout, Margin, Rgba, RichText, Ui, Vec2};
|
use egui::{Align, Button, CornerRadius, Frame, Id, Layout, Margin, Rgba, RichText, Ui, Vec2};
|
||||||
use enostr::{RelayPool, RelayStatus};
|
use enostr::{RelayPool, RelayStatus};
|
||||||
@@ -17,9 +18,8 @@ pub struct RelayView<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RelayView<'_> {
|
impl RelayView<'_> {
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<RelayAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<RelayAction> {
|
||||||
let mut action = None;
|
let scroll_out = Frame::new()
|
||||||
Frame::new()
|
|
||||||
.inner_margin(Margin::symmetric(10, 0))
|
.inner_margin(Margin::symmetric(10, 0))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
@@ -40,6 +40,7 @@ impl RelayView<'_> {
|
|||||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
|
let mut action = None;
|
||||||
if let Some(relay_to_remove) = self.show_relays(ui) {
|
if let Some(relay_to_remove) = self.show_relays(ui) {
|
||||||
action = Some(RelayAction::Remove(relay_to_remove));
|
action = Some(RelayAction::Remove(relay_to_remove));
|
||||||
}
|
}
|
||||||
@@ -47,10 +48,12 @@ impl RelayView<'_> {
|
|||||||
if let Some(relay_to_add) = self.show_add_relay_ui(ui) {
|
if let Some(relay_to_add) = self.show_add_relay_ui(ui) {
|
||||||
action = Some(RelayAction::Add(relay_to_add));
|
action = Some(RelayAction::Add(relay_to_add));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
action
|
action
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.inner;
|
||||||
|
|
||||||
|
BodyResponse::scroll(scroll_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_id() -> egui::Id {
|
pub fn scroll_id() -> egui::Id {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use enostr::{NoteId, Pubkey};
|
|||||||
use state::TypingType;
|
use state::TypingType;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
nav::BodyResponse,
|
||||||
timeline::{TimelineTab, TimelineUnits},
|
timeline::{TimelineTab, TimelineUnits},
|
||||||
ui::timeline::TimelineTabView,
|
ui::timeline::TimelineTabView,
|
||||||
};
|
};
|
||||||
@@ -49,11 +50,11 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
pub fn show(&mut self, ui: &mut egui::Ui) -> BodyResponse<NoteAction> {
|
||||||
padding(8.0, ui, |ui| self.show_impl(ui)).inner
|
padding(8.0, ui, |ui| self.show_impl(ui)).inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_impl(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
pub fn show_impl(&mut self, ui: &mut egui::Ui) -> BodyResponse<NoteAction> {
|
||||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 12.0);
|
ui.spacing_mut().item_spacing = egui::vec2(0.0, 12.0);
|
||||||
|
|
||||||
let search_resp = search_box(
|
let search_resp = search_box(
|
||||||
@@ -67,7 +68,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
search_resp.process(self.query);
|
search_resp.process(self.query);
|
||||||
|
|
||||||
let mut search_action = None;
|
let mut search_action = None;
|
||||||
let mut note_action = None;
|
let mut body_resp = BodyResponse::none();
|
||||||
match &self.query.state {
|
match &self.query.state {
|
||||||
SearchState::New | SearchState::Navigating => {}
|
SearchState::New | SearchState::Navigating => {}
|
||||||
SearchState::Typing(TypingType::Mention(mention_name)) => 's: {
|
SearchState::Typing(TypingType::Mention(mention_name)) => 's: {
|
||||||
@@ -87,7 +88,11 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
)
|
)
|
||||||
.show_in_rect(ui.available_rect_before_wrap(), ui);
|
.show_in_rect(ui.available_rect_before_wrap(), ui);
|
||||||
|
|
||||||
search_action = match search_res {
|
let Some(res) = search_res.output else {
|
||||||
|
break 's;
|
||||||
|
};
|
||||||
|
|
||||||
|
search_action = match res {
|
||||||
MentionPickerResponse::SelectResult(Some(index)) => {
|
MentionPickerResponse::SelectResult(Some(index)) => {
|
||||||
let Some(pk_bytes) = results.get(index) else {
|
let Some(pk_bytes) = results.get(index) else {
|
||||||
break 's;
|
break 's;
|
||||||
@@ -120,7 +125,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
&mut self.query.notes,
|
&mut self.query.notes,
|
||||||
);
|
);
|
||||||
search_action = Some(SearchAction::Searched);
|
search_action = Some(SearchAction::Searched);
|
||||||
note_action = self.show_search_results(ui);
|
body_resp.insert(self.show_search_results(ui));
|
||||||
}
|
}
|
||||||
SearchState::Searched => {
|
SearchState::Searched => {
|
||||||
ui.label(tr_plural!(
|
ui.label(tr_plural!(
|
||||||
@@ -131,7 +136,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
self.query.notes.units.len(), // count
|
self.query.notes.units.len(), // count
|
||||||
query = &self.query.string
|
query = &self.query.string
|
||||||
));
|
));
|
||||||
note_action = self.show_search_results(ui);
|
body_resp.insert(self.show_search_results(ui));
|
||||||
}
|
}
|
||||||
SearchState::Typing(TypingType::AutoSearch) => {
|
SearchState::Typing(TypingType::AutoSearch) => {
|
||||||
ui.label(tr!(
|
ui.label(tr!(
|
||||||
@@ -141,7 +146,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
query = &self.query.string
|
query = &self.query.string
|
||||||
));
|
));
|
||||||
|
|
||||||
note_action = self.show_search_results(ui);
|
body_resp.insert(self.show_search_results(ui));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,11 +154,11 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
resp.process(self.query);
|
resp.process(self.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
note_action
|
body_resp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_search_results(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
fn show_search_results(&mut self, ui: &mut egui::Ui) -> BodyResponse<NoteAction> {
|
||||||
egui::ScrollArea::vertical()
|
let scroll_out = egui::ScrollArea::vertical()
|
||||||
.id_salt(SearchView::scroll_id())
|
.id_salt(SearchView::scroll_id())
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
TimelineTabView::new(
|
TimelineTabView::new(
|
||||||
@@ -164,8 +169,9 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
|||||||
self.jobs,
|
self.jobs,
|
||||||
)
|
)
|
||||||
.show(ui)
|
.show(ui)
|
||||||
})
|
});
|
||||||
.inner
|
|
||||||
|
BodyResponse::scroll(scroll_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_id() -> egui::Id {
|
pub fn scroll_id() -> egui::Id {
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ use notedeck_ui::{
|
|||||||
AnimationHelper, NoteOptions, NoteView,
|
AnimationHelper, NoteOptions, NoteView,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{nav::RouterAction, ui::account_login_view::eye_button, Damus, Route};
|
use crate::{
|
||||||
|
nav::{BodyResponse, RouterAction},
|
||||||
|
ui::account_login_view::eye_button,
|
||||||
|
Damus, Route,
|
||||||
|
};
|
||||||
|
|
||||||
const PREVIEW_NOTE_ID: &str = "note1edjc8ggj07hwv77g2405uh6j2jkk5aud22gktxrvc2wnre4vdwgqzlv2gw";
|
const PREVIEW_NOTE_ID: &str = "note1edjc8ggj07hwv77g2405uh6j2jkk5aud22gktxrvc2wnre4vdwgqzlv2gw";
|
||||||
|
|
||||||
@@ -638,13 +642,12 @@ impl<'a> SettingsView<'a> {
|
|||||||
action
|
action
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<SettingsAction> {
|
||||||
let mut action: Option<SettingsAction> = None;
|
let scroll_out = Frame::default()
|
||||||
|
|
||||||
Frame::default()
|
|
||||||
.inner_margin(Margin::symmetric(10, 10))
|
.inner_margin(Margin::symmetric(10, 10))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ScrollArea::vertical().show(ui, |ui| {
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
let mut action = None;
|
||||||
if let Some(new_action) = self.appearance_section(ui) {
|
if let Some(new_action) = self.appearance_section(ui) {
|
||||||
action = Some(new_action);
|
action = Some(new_action);
|
||||||
}
|
}
|
||||||
@@ -670,10 +673,12 @@ impl<'a> SettingsView<'a> {
|
|||||||
if let Some(new_action) = self.manage_relays_section(ui) {
|
if let Some(new_action) = self.manage_relays_section(ui) {
|
||||||
action = Some(new_action);
|
action = Some(new_action);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
action
|
action
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.inner;
|
||||||
|
|
||||||
|
BodyResponse::scroll(scroll_out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use notedeck::{NoteAction, NoteContext};
|
|||||||
use notedeck_ui::note::NoteResponse;
|
use notedeck_ui::note::NoteResponse;
|
||||||
use notedeck_ui::{NoteOptions, NoteView};
|
use notedeck_ui::{NoteOptions, NoteView};
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::timeline::thread::{NoteSeenFlags, ParentState, Threads};
|
use crate::timeline::thread::{NoteSeenFlags, ParentState, Threads};
|
||||||
|
|
||||||
pub struct ThreadView<'a, 'd> {
|
pub struct ThreadView<'a, 'd> {
|
||||||
@@ -42,7 +43,7 @@ impl<'a, 'd> ThreadView<'a, 'd> {
|
|||||||
egui::Id::new(("threadscroll", selected_note_id, col))
|
egui::Id::new(("threadscroll", selected_note_id, col))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<NoteAction> {
|
||||||
let txn = Transaction::new(self.note_context.ndb).expect("txn");
|
let txn = Transaction::new(self.note_context.ndb).expect("txn");
|
||||||
|
|
||||||
let scroll_id = ThreadView::scroll_id(self.selected_note_id, self.col);
|
let scroll_id = ThreadView::scroll_id(self.selected_note_id, self.col);
|
||||||
@@ -60,6 +61,7 @@ impl<'a, 'd> ThreadView<'a, 'd> {
|
|||||||
|
|
||||||
let output = scroll_area.show(ui, |ui| self.notes(ui, &txn));
|
let output = scroll_area.show(ui, |ui| self.notes(ui, &txn));
|
||||||
|
|
||||||
|
let out_id = output.id;
|
||||||
let mut resp = output.inner;
|
let mut resp = output.inner;
|
||||||
|
|
||||||
if let Some(NoteAction::Note {
|
if let Some(NoteAction::Note {
|
||||||
@@ -71,7 +73,7 @@ impl<'a, 'd> ThreadView<'a, 'd> {
|
|||||||
*scroll_offset = output.state.offset.y;
|
*scroll_offset = output.state.offset.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
resp
|
BodyResponse::output(resp).scroll_raw(out_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notes(&mut self, ui: &mut egui::Ui, txn: &Transaction) -> Option<NoteAction> {
|
fn notes(&mut self, ui: &mut egui::Ui, txn: &Transaction) -> Option<NoteAction> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use notedeck_ui::{ProfilePic, ProfilePreview};
|
|||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
|
use crate::nav::BodyResponse;
|
||||||
use crate::timeline::{
|
use crate::timeline::{
|
||||||
CompositeType, CompositeUnit, NoteUnit, ReactionUnit, RepostUnit, TimelineCache, TimelineKind,
|
CompositeType, CompositeUnit, NoteUnit, ReactionUnit, RepostUnit, TimelineCache, TimelineKind,
|
||||||
TimelineTab, ViewFilter,
|
TimelineTab, ViewFilter,
|
||||||
@@ -56,7 +57,7 @@ impl<'a, 'd> TimelineView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> BodyResponse<NoteAction> {
|
||||||
timeline_ui(
|
timeline_ui(
|
||||||
ui,
|
ui,
|
||||||
self.timeline_id,
|
self.timeline_id,
|
||||||
@@ -94,7 +95,7 @@ fn timeline_ui(
|
|||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
col: usize,
|
col: usize,
|
||||||
scroll_to_top: bool,
|
scroll_to_top: bool,
|
||||||
) -> Option<NoteAction> {
|
) -> BodyResponse<NoteAction> {
|
||||||
//padding(4.0, ui, |ui| ui.heading("Notifications"));
|
//padding(4.0, ui, |ui| ui.heading("Notifications"));
|
||||||
/*
|
/*
|
||||||
let font_id = egui::TextStyle::Body.resolve(ui.style());
|
let font_id = egui::TextStyle::Body.resolve(ui.style());
|
||||||
@@ -102,7 +103,9 @@ fn timeline_ui(
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let scroll_id = TimelineView::scroll_id(timeline_cache, timeline_id, col)?;
|
let Some(scroll_id) = TimelineView::scroll_id(timeline_cache, timeline_id, col) else {
|
||||||
|
return BodyResponse::none();
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let timeline = if let Some(timeline) = timeline_cache.get_mut(timeline_id) {
|
let timeline = if let Some(timeline) = timeline_cache.get_mut(timeline_id) {
|
||||||
@@ -111,7 +114,7 @@ fn timeline_ui(
|
|||||||
error!("tried to render timeline in column, but timeline was missing");
|
error!("tried to render timeline in column, but timeline was missing");
|
||||||
// TODO (jb55): render error when timeline is missing?
|
// TODO (jb55): render error when timeline is missing?
|
||||||
// this shouldn't happen...
|
// this shouldn't happen...
|
||||||
return None;
|
return BodyResponse::none();
|
||||||
};
|
};
|
||||||
|
|
||||||
timeline.selected_view = tabs_ui(
|
timeline.selected_view = tabs_ui(
|
||||||
@@ -204,7 +207,9 @@ fn timeline_ui(
|
|||||||
.data_mut(|d| d.insert_temp(show_top_button_id, true));
|
.data_mut(|d| d.insert_temp(show_top_button_id, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll_output.inner.or_else(|| {
|
let scroll_id = scroll_output.id;
|
||||||
|
|
||||||
|
let action = scroll_output.inner.or_else(|| {
|
||||||
// if we're scrolling, return that as a response. We need this
|
// if we're scrolling, return that as a response. We need this
|
||||||
// for auto-closing the side menu
|
// for auto-closing the side menu
|
||||||
|
|
||||||
@@ -215,7 +220,9 @@ fn timeline_ui(
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
BodyResponse::output(action).scroll_raw(scroll_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn goto_top_button(center: Pos2) -> impl egui::Widget {
|
fn goto_top_button(center: Pos2) -> impl egui::Widget {
|
||||||
|
|||||||
Reference in New Issue
Block a user