Introducing Damus Notedeck: a nostr browser

This splits notedeck into:

- notedeck
- notedeck_chrome
- notedeck_columns

The `notedeck` crate is the library that `notedeck_chrome` and
`notedeck_columns`, use. It contains common functionality related to
notedeck apps such as the NoteCache, ImageCache, etc.

The `notedeck_chrome` crate is the binary and ui chrome. It is
responsible for managing themes, user accounts, signing, data paths,
nostrdb, image caches etc. It will eventually have its own ui which has
yet to be determined.  For now it just manages the browser data, which
is passed to apps via a new struct called `AppContext`.

`notedeck_columns` is our columns app, with less responsibility now that
more things are handled by `notedeck_chrome`

There is still much work left to do before this is a proper browser:

- process isolation
- sandboxing
- etc

This is the beginning of a new era! We're just getting started.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-12-11 04:22:05 -08:00
parent aa14fb092d
commit ec755493d9
146 changed files with 2820 additions and 2794 deletions

View File

@@ -1,10 +1,10 @@
use crate::app_style::NotedeckTextStyle;
use crate::key_parsing::AcquireKeyError;
use crate::login_manager::AcquireKeyState;
use crate::ui::{Preview, PreviewConfig, View};
use egui::TextEdit;
use egui::{Align, Button, Color32, Frame, InnerResponse, Margin, RichText, Vec2};
use enostr::Keypair;
use notedeck::NotedeckTextStyle;
pub struct AccountLoginView<'a> {
manager: &'a mut AcquireKeyState,

View File

@@ -1,14 +1,9 @@
use crate::colors::PINK;
use crate::imgcache::ImageCache;
use crate::{
accounts::Accounts,
ui::{Preview, PreviewConfig, View},
Damus,
};
use egui::{
Align, Button, Frame, Image, InnerResponse, Layout, RichText, ScrollArea, Ui, UiBuilder, Vec2,
};
use nostrdb::{Ndb, Transaction};
use notedeck::{Accounts, ImageCache};
use super::profile::preview::SimpleProfilePreview;
@@ -180,7 +175,7 @@ fn scroll_area() -> ScrollArea {
}
fn add_account_button() -> Button<'static> {
let img_data = egui::include_image!("../../assets/icons/add_account_icon_4x.png");
let img_data = egui::include_image!("../../../../assets/icons/add_account_icon_4x.png");
let img = Image::new(img_data).fit_to_exact_size(Vec2::new(48.0, 48.0));
Button::image_and_text(
img,
@@ -195,48 +190,3 @@ fn add_account_button() -> Button<'static> {
fn sign_out_button() -> egui::Button<'static> {
egui::Button::new(RichText::new("Sign out"))
}
// PREVIEWS
mod preview {
use super::*;
use crate::{accounts::process_accounts_view_response, test_data};
pub struct AccountsPreview {
app: Damus,
}
impl AccountsPreview {
fn new() -> Self {
let app = test_data::test_app();
AccountsPreview { app }
}
}
impl View for AccountsPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
ui.add_space(24.0);
// TODO(jb55): maybe just use render_nav here so we can step through routes
if let Some(response) =
AccountsView::new(&self.app.ndb, &self.app.accounts, &mut self.app.img_cache)
.ui(ui)
.inner
{
process_accounts_view_response(
&mut self.app.accounts,
&mut self.app.decks_cache,
0,
response,
);
}
}
}
impl Preview for AccountsView<'_> {
type Prev = AccountsPreview;
fn preview(_cfg: PreviewConfig) -> Self::Prev {
AccountsPreview::new()
}
}
}

View File

@@ -9,14 +9,14 @@ use nostrdb::Ndb;
use tracing::error;
use crate::{
app_style::{get_font_size, NotedeckTextStyle},
login_manager::AcquireKeyState,
timeline::{PubkeySource, Timeline, TimelineKind},
ui::anim::ICON_EXPANSION_MULTIPLE,
user_account::UserAccount,
Damus,
};
use notedeck::{AppContext, NotedeckTextStyle, UserAccount};
use super::{anim::AnimationHelper, padding};
pub enum AddColumnResponse {
@@ -180,8 +180,8 @@ impl<'a> AddColumnView<'a> {
let max_width = ui.available_width();
let title_style = NotedeckTextStyle::Body;
let desc_style = NotedeckTextStyle::Button;
let title_min_font_size = get_font_size(ui.ctx(), &title_style);
let desc_min_font_size = get_font_size(ui.ctx(), &desc_style);
let title_min_font_size = notedeck::fonts::get_font_size(ui.ctx(), &title_style);
let desc_min_font_size = notedeck::fonts::get_font_size(ui.ctx(), &desc_style);
let max_height = {
let max_wrap_width =
@@ -279,7 +279,7 @@ impl<'a> AddColumnView<'a> {
vec.push(ColumnOptionData {
title: "Universe",
description: "See the whole nostr universe",
icon: egui::include_image!("../../assets/icons/universe_icon_dark_4x.png"),
icon: egui::include_image!("../../../../assets/icons/universe_icon_dark_4x.png"),
option: AddColumnOption::Universe,
});
@@ -293,20 +293,20 @@ impl<'a> AddColumnView<'a> {
vec.push(ColumnOptionData {
title: "Home timeline",
description: "See recommended notes first",
icon: egui::include_image!("../../assets/icons/home_icon_dark_4x.png"),
icon: egui::include_image!("../../../../assets/icons/home_icon_dark_4x.png"),
option: AddColumnOption::Home(source.clone()),
});
}
vec.push(ColumnOptionData {
title: "Notifications",
description: "Stay up to date with notifications and mentions",
icon: egui::include_image!("../../assets/icons/notifications_icon_dark_4x.png"),
icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
option: AddColumnOption::UndecidedNotification,
});
vec.push(ColumnOptionData {
title: "Hashtag",
description: "Stay up to date with a certain hashtag",
icon: egui::include_image!("../../assets/icons/notifications_icon_dark_4x.png"),
icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
option: AddColumnOption::UndecidedHashtag,
});
@@ -326,7 +326,9 @@ impl<'a> AddColumnView<'a> {
vec.push(ColumnOptionData {
title: "Your Notifications",
description: "Stay up to date with your notifications and mentions",
icon: egui::include_image!("../../assets/icons/notifications_icon_dark_4x.png"),
icon: egui::include_image!(
"../../../../assets/icons/notifications_icon_dark_4x.png"
),
option: AddColumnOption::Notification(source),
});
}
@@ -334,7 +336,7 @@ impl<'a> AddColumnView<'a> {
vec.push(ColumnOptionData {
title: "Someone else's Notifications",
description: "Stay up to date with someone else's notifications and mentions",
icon: egui::include_image!("../../assets/icons/notifications_icon_dark_4x.png"),
icon: egui::include_image!("../../../../assets/icons/notifications_icon_dark_4x.png"),
option: AddColumnOption::ExternalNotification,
});
@@ -352,19 +354,20 @@ struct ColumnOptionData {
pub fn render_add_column_routes(
ui: &mut egui::Ui,
app: &mut Damus,
ctx: &mut AppContext<'_>,
col: usize,
route: &AddColumnRoute,
) {
let mut add_column_view = AddColumnView::new(
&mut app.view_state.id_state_map,
&app.ndb,
app.accounts.get_selected_account(),
ctx.ndb,
ctx.accounts.get_selected_account(),
);
let resp = match route {
AddColumnRoute::Base => add_column_view.ui(ui),
AddColumnRoute::UndecidedNotification => add_column_view.notifications_ui(ui),
AddColumnRoute::ExternalNotification => add_column_view.external_notification_ui(ui),
AddColumnRoute::Hashtag => hashtag_ui(ui, &app.ndb, &mut app.view_state.id_string_map),
AddColumnRoute::Hashtag => hashtag_ui(ui, ctx.ndb, &mut app.view_state.id_string_map),
};
if let Some(resp) = resp {
@@ -372,27 +375,34 @@ pub fn render_add_column_routes(
AddColumnResponse::Timeline(mut timeline) => {
crate::timeline::setup_new_timeline(
&mut timeline,
&app.ndb,
ctx.ndb,
&mut app.subscriptions,
&mut app.pool,
&mut app.note_cache,
ctx.pool,
ctx.note_cache,
app.since_optimize,
&app.accounts.mutefun(),
&ctx.accounts.mutefun(),
);
app.columns_mut().add_timeline_to_column(col, timeline);
app.columns_mut(ctx.accounts)
.add_timeline_to_column(col, timeline);
}
AddColumnResponse::UndecidedNotification => {
app.columns_mut().column_mut(col).router_mut().route_to(
crate::route::Route::AddColumn(AddColumnRoute::UndecidedNotification),
);
app.columns_mut(ctx.accounts)
.column_mut(col)
.router_mut()
.route_to(crate::route::Route::AddColumn(
AddColumnRoute::UndecidedNotification,
));
}
AddColumnResponse::ExternalNotification => {
app.columns_mut().column_mut(col).router_mut().route_to(
crate::route::Route::AddColumn(AddColumnRoute::ExternalNotification),
);
app.columns_mut(ctx.accounts)
.column_mut(col)
.router_mut()
.route_to(crate::route::Route::AddColumn(
AddColumnRoute::ExternalNotification,
));
}
AddColumnResponse::Hashtag => {
app.columns_mut()
app.columns_mut(ctx.accounts)
.column_mut(col)
.router_mut()
.route_to(crate::route::Route::AddColumn(AddColumnRoute::Hashtag));
@@ -438,44 +448,3 @@ pub fn hashtag_ui(
})
.inner
}
mod preview {
use crate::{
test_data,
ui::{Preview, PreviewConfig, View},
Damus,
};
use super::AddColumnView;
pub struct AddColumnPreview {
app: Damus,
}
impl AddColumnPreview {
fn new() -> Self {
let app = test_data::test_app();
AddColumnPreview { app }
}
}
impl View for AddColumnPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
AddColumnView::new(
&mut self.app.view_state.id_state_map,
&self.app.ndb,
self.app.accounts.get_selected_account(),
)
.ui(ui);
}
}
impl Preview for AddColumnView<'_> {
type Prev = AddColumnPreview;
fn preview(_cfg: PreviewConfig) -> Self::Prev {
AddColumnPreview::new()
}
}
}

View File

@@ -1,7 +1,5 @@
use crate::{
app_style::NotedeckTextStyle,
column::Columns,
imgcache::ImageCache,
nav::RenderNavAction,
route::Route,
timeline::{TimelineId, TimelineRoute},
@@ -14,6 +12,7 @@ use crate::{
use egui::{RichText, Stroke, UiBuilder};
use enostr::Pubkey;
use nostrdb::{Ndb, Transaction};
use notedeck::{ImageCache, NotedeckTextStyle};
pub struct NavTitle<'a> {
ndb: &'a Ndb,
@@ -124,9 +123,9 @@ impl<'a> NavTitle<'a> {
let max_size = icon_width * ICON_EXPANSION_MULTIPLE;
let img_data = if ui.visuals().dark_mode {
egui::include_image!("../../../assets/icons/column_delete_icon_4x.png")
egui::include_image!("../../../../../assets/icons/column_delete_icon_4x.png")
} else {
egui::include_image!("../../../assets/icons/column_delete_icon_light_4x.png")
egui::include_image!("../../../../../assets/icons/column_delete_icon_light_4x.png")
};
let img = egui::Image::new(img_data).max_width(img_size);

View File

@@ -1,11 +1,6 @@
use crate::{app_style::deck_icon_font_sized, colors::PINK, deck_state::DeckState};
use egui::{vec2, Button, Color32, Label, RichText, Stroke, Ui, Widget};
use crate::{
app_style::{deck_icon_font_sized, get_font_size, NotedeckTextStyle},
colors::PINK,
deck_state::DeckState,
fonts::NamedFontFamily,
};
use notedeck::{NamedFontFamily, NotedeckTextStyle};
use super::{
anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE},
@@ -39,7 +34,7 @@ impl<'a> ConfigureDeckView<'a> {
pub fn ui(&mut self, ui: &mut Ui) -> Option<ConfigureDeckResponse> {
let title_font = egui::FontId::new(
get_font_size(ui.ctx(), &NotedeckTextStyle::Heading4),
notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Heading4),
egui::FontFamily::Name(NamedFontFamily::Bold.as_str().into()),
);
padding(16.0, ui, |ui| {
@@ -52,7 +47,10 @@ impl<'a> ConfigureDeckView<'a> {
ui.add(Label::new(
RichText::new("We recommend short names")
.color(ui.visuals().noninteractive().fg_stroke.color)
.size(get_font_size(ui.ctx(), &NotedeckTextStyle::Small)),
.size(notedeck::fonts::get_font_size(
ui.ctx(),
&NotedeckTextStyle::Small,
)),
));
ui.add_space(32.0);

View File

@@ -1,5 +1,6 @@
use crate::{colors, imgcache::ImageCache, ui};
use crate::ui;
use nostrdb::{Ndb, Transaction};
use notedeck::ImageCache;
pub struct Mention<'a> {
ndb: &'a Ndb,
@@ -66,6 +67,8 @@ fn mention_ui(
#[cfg(feature = "profiling")]
puffin::profile_function!();
let link_color = ui.visuals().hyperlink_color;
ui.horizontal(|ui| {
let profile = ndb.get_profile_by_pubkey(txn, pk).ok();
@@ -77,7 +80,7 @@ fn mention_ui(
};
let resp = ui.add(
egui::Label::new(egui::RichText::new(name).color(colors::PURPLE).size(size))
egui::Label::new(egui::RichText::new(name).color(link_color).size(size))
.selectable(selectable),
);

View File

@@ -59,28 +59,3 @@ pub fn hline(ui: &egui::Ui) {
let stroke = ui.style().visuals.widgets.noninteractive.bg_stroke;
ui.painter().hline(rect.x_range(), resize_y, stroke);
}
#[inline]
#[allow(unreachable_code)]
pub fn is_compiled_as_mobile() -> bool {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
true
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
false
}
}
/// Determine if the screen is narrow. This is useful for detecting mobile
/// contexts, but with the nuance that we may also have a wide android tablet.
pub fn is_narrow(ctx: &egui::Context) -> bool {
let screen_size = ctx.input(|c| c.screen_rect().size());
screen_size.x < 550.0
}
pub fn is_oled() -> bool {
is_compiled_as_mobile()
}

View File

@@ -1,14 +1,14 @@
use crate::actionbar::NoteAction;
use crate::images::ImageType;
use crate::imgcache::ImageCache;
use crate::notecache::NoteCache;
use crate::ui;
use crate::ui::note::{NoteOptions, NoteResponse};
use crate::ui::ProfilePic;
use crate::{colors, ui};
use egui::{Color32, Hyperlink, Image, RichText};
use nostrdb::{BlockType, Mention, Ndb, Note, NoteKey, Transaction};
use tracing::warn;
use notedeck::{ImageCache, NoteCache};
pub struct NoteContents<'a> {
ndb: &'a Ndb,
img_cache: &'a mut ImageCache,
@@ -94,8 +94,8 @@ pub fn render_note_preview(
return ui
.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.colored_label(colors::PURPLE, "@");
ui.colored_label(colors::PURPLE, &id_str[4..16]);
ui.colored_label(link_color, "@");
ui.colored_label(link_color, &id_str[4..16]);
})
.response;
*/
@@ -145,6 +145,7 @@ fn render_note_contents(
let mut images: Vec<String> = vec![];
let mut inline_note: Option<(&[u8; 32], &str)> = None;
let hide_media = options.has_hide_media();
let link_color = ui.visuals().hyperlink_color;
let response = ui.horizontal_wrapped(|ui| {
let blocks = if let Ok(blocks) = ndb.get_blocks_by_key(txn, note_key) {
@@ -177,14 +178,14 @@ fn render_note_contents(
}
_ => {
ui.colored_label(colors::PURPLE, format!("@{}", &block.as_str()[4..16]));
ui.colored_label(link_color, format!("@{}", &block.as_str()[4..16]));
}
},
BlockType::Hashtag => {
#[cfg(feature = "profiling")]
puffin::profile_scope!("hashtag contents");
ui.colored_label(colors::PURPLE, format!("#{}", block.as_str()));
ui.colored_label(link_color, format!("#{}", block.as_str()));
}
BlockType::Url => {
@@ -195,7 +196,7 @@ fn render_note_contents(
#[cfg(feature = "profiling")]
puffin::profile_scope!("url contents");
ui.add(Hyperlink::from_label_and_url(
RichText::new(block.as_str()).color(colors::PURPLE),
RichText::new(block.as_str()).color(link_color),
block.as_str(),
));
}
@@ -208,7 +209,7 @@ fn render_note_contents(
}
_ => {
ui.colored_label(colors::PURPLE, block.as_str());
ui.colored_label(link_color, block.as_str());
}
}
}

View File

@@ -1,4 +1,3 @@
use crate::colors;
use egui::{Rect, Vec2};
use enostr::{NoteId, Pubkey};
use nostrdb::{Note, NoteKey};
@@ -136,7 +135,7 @@ impl NoteContextButton {
let translated_radius = (cur_radius - 1.0) / 2.0;
// This works in both themes
let color = colors::GRAY_SECONDARY;
let color = ui.style().visuals.noninteractive().fg_stroke.color;
// Draw circles
let painter = ui.painter_at(put_at);

View File

@@ -14,16 +14,14 @@ pub use reply::PostReplyView;
use crate::{
actionbar::NoteAction,
app_style::NotedeckTextStyle,
colors,
imgcache::ImageCache,
notecache::{CachedNote, NoteCache},
ui::{self, View},
};
use egui::emath::{pos2, Vec2};
use egui::{Id, Label, Pos2, Rect, Response, RichText, Sense};
use enostr::{NoteId, Pubkey};
use nostrdb::{Ndb, Note, NoteKey, NoteReply, Transaction};
use notedeck::{CachedNote, ImageCache, NoteCache, NotedeckTextStyle};
use super::profile::preview::{get_display_name, one_line_display_name_widget};
@@ -80,15 +78,9 @@ fn reply_desc(
let size = 10.0;
let selectable = false;
let color = ui.style().visuals.noninteractive().fg_stroke.color;
ui.add(
Label::new(
RichText::new("replying to")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
);
ui.add(Label::new(RichText::new("replying to").size(size).color(color)).selectable(selectable));
let reply = if let Some(reply) = note_reply.reply() {
reply
@@ -99,14 +91,7 @@ fn reply_desc(
let reply_note = if let Ok(reply_note) = ndb.get_note_by_id(txn, reply.id) {
reply_note
} else {
ui.add(
Label::new(
RichText::new("a note")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
);
ui.add(Label::new(RichText::new("a note").size(size).color(color)).selectable(selectable));
return;
};
@@ -117,14 +102,7 @@ fn reply_desc(
.size(size)
.selectable(selectable),
);
ui.add(
Label::new(
RichText::new("'s note")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
);
ui.add(Label::new(RichText::new("'s note").size(size).color(color)).selectable(selectable));
} else if let Some(root) = note_reply.root() {
// replying to another post in a thread, not the root
@@ -137,12 +115,8 @@ fn reply_desc(
.selectable(selectable),
);
ui.add(
Label::new(
RichText::new("'s note")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
Label::new(RichText::new("'s note").size(size).color(color))
.selectable(selectable),
);
} else {
// replying to bob in alice's thread
@@ -153,8 +127,7 @@ fn reply_desc(
.selectable(selectable),
);
ui.add(
Label::new(RichText::new("in").size(size).color(colors::GRAY_SECONDARY))
.selectable(selectable),
Label::new(RichText::new("in").size(size).color(color)).selectable(selectable),
);
ui.add(
ui::Mention::new(ndb, img_cache, txn, root_note.pubkey())
@@ -162,12 +135,8 @@ fn reply_desc(
.selectable(selectable),
);
ui.add(
Label::new(
RichText::new("'s thread")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
Label::new(RichText::new("'s thread").size(size).color(color))
.selectable(selectable),
);
}
} else {
@@ -177,12 +146,8 @@ fn reply_desc(
.selectable(selectable),
);
ui.add(
Label::new(
RichText::new("in someone's thread")
.size(size)
.color(colors::GRAY_SECONDARY),
)
.selectable(selectable),
Label::new(RichText::new("in someone's thread").size(size).color(color))
.selectable(selectable),
);
}
}
@@ -382,6 +347,7 @@ impl<'a> NoteView<'a> {
});
ui.add_space(6.0);
let resp = ui.add(one_line_display_name_widget(
ui.visuals(),
get_display_name(profile.as_ref().ok()),
style,
));
@@ -391,10 +357,11 @@ impl<'a> NoteView<'a> {
ui.add(ui::ProfilePreview::new(rec, self.img_cache));
});
}
let color = ui.style().visuals.noninteractive().fg_stroke.color;
ui.add_space(4.0);
ui.label(
RichText::new("Reposted")
.color(colors::GRAY_SECONDARY)
.color(color)
.text_style(style.text_style()),
);
});
@@ -690,9 +657,8 @@ fn render_note_actionbar(
}
fn secondary_label(ui: &mut egui::Ui, s: impl Into<String>) {
ui.add(Label::new(
RichText::new(s).size(10.0).color(colors::GRAY_SECONDARY),
));
let color = ui.style().visuals.noninteractive().fg_stroke.color;
ui.add(Label::new(RichText::new(s).size(10.0).color(color)));
}
fn render_reltime(
@@ -718,9 +684,9 @@ fn render_reltime(
fn reply_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
let img_data = if ui.style().visuals.dark_mode {
egui::include_image!("../../../assets/icons/reply.png")
egui::include_image!("../../../../../assets/icons/reply.png")
} else {
egui::include_image!("../../../assets/icons/reply-dark.png")
egui::include_image!("../../../../../assets/icons/reply-dark.png")
};
let (rect, size, resp) =
@@ -737,9 +703,9 @@ fn reply_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
fn repost_icon(dark_mode: bool) -> egui::Image<'static> {
let img_data = if dark_mode {
egui::include_image!("../../../assets/icons/repost_icon_4x.png")
egui::include_image!("../../../../../assets/icons/repost_icon_4x.png")
} else {
egui::include_image!("../../../assets/icons/repost_light_4x.png")
egui::include_image!("../../../../../assets/icons/repost_light_4x.png")
};
egui::Image::new(img_data)
}

View File

@@ -1,6 +1,4 @@
use crate::draft::{Draft, Drafts};
use crate::imgcache::ImageCache;
use crate::notecache::NoteCache;
use crate::post::NewPost;
use crate::ui::{self, Preview, PreviewConfig, View};
use crate::Result;
@@ -10,6 +8,8 @@ use enostr::{FilledKeypair, FullKeypair, NoteId, RelayPool};
use nostrdb::{Config, Ndb, Transaction};
use tracing::info;
use notedeck::{ImageCache, NoteCache};
use super::contents::render_note_preview;
pub struct PostView<'a> {

View File

@@ -1,7 +1,8 @@
use enostr::{FilledKeypair, NoteId};
use nostrdb::Ndb;
use notedeck::{ImageCache, NoteCache};
use crate::{draft::Draft, imgcache::ImageCache, notecache::NoteCache, ui};
use crate::{draft::Draft, ui};
use super::{PostResponse, PostType};

View File

@@ -1,11 +1,11 @@
use crate::draft::Draft;
use crate::imgcache::ImageCache;
use crate::notecache::NoteCache;
use crate::ui;
use crate::ui::note::{PostResponse, PostType};
use enostr::{FilledKeypair, NoteId};
use nostrdb::Ndb;
use notedeck::{ImageCache, NoteCache};
pub struct PostReplyView<'a> {
ndb: &'a Ndb,
poster: FilledKeypair<'a>,

View File

@@ -8,12 +8,10 @@ use nostrdb::{Ndb, Transaction};
pub use picture::ProfilePic;
pub use preview::ProfilePreview;
use crate::{
actionbar::NoteAction, imgcache::ImageCache, muted::MuteFun, notecache::NoteCache,
notes_holder::NotesHolderStorage, profile::Profile,
};
use crate::{actionbar::NoteAction, notes_holder::NotesHolderStorage, profile::Profile};
use super::timeline::{tabs_ui, TimelineTabView};
use notedeck::{ImageCache, MuteFun, NoteCache};
pub struct ProfileView<'a> {
pubkey: &'a Pubkey,

View File

@@ -1,9 +1,10 @@
use crate::images::ImageType;
use crate::imgcache::ImageCache;
use crate::ui::{Preview, PreviewConfig, View};
use egui::{vec2, Sense, TextureHandle};
use nostrdb::{Ndb, Transaction};
use notedeck::ImageCache;
pub struct ProfilePic<'cache, 'url> {
cache: &'cache mut ImageCache,
url: &'url str,

View File

@@ -1,8 +1,4 @@
use crate::app_style::{get_font_size, NotedeckTextStyle};
use crate::imgcache::ImageCache;
use crate::storage::{DataPath, DataPathType};
use crate::ui::ProfilePic;
use crate::user_account::UserAccount;
use crate::{colors, images, DisplayName};
use egui::load::TexturePoll;
use egui::{Frame, Label, RichText, Sense, Widget};
@@ -10,6 +6,8 @@ use egui_extras::Size;
use enostr::{NoteId, Pubkey};
use nostrdb::{Ndb, ProfileRecord, Transaction};
use notedeck::{DataPath, DataPathType, ImageCache, NotedeckTextStyle, UserAccount};
pub struct ProfilePreview<'a, 'cache> {
profile: &'a ProfileRecord<'a>,
cache: &'cache mut ImageCache,
@@ -121,7 +119,10 @@ impl egui::Widget for SimpleProfilePreview<'_, '_> {
ui.add(
Label::new(
RichText::new("Read only")
.size(get_font_size(ui.ctx(), &NotedeckTextStyle::Tiny))
.size(notedeck::fonts::get_font_size(
ui.ctx(),
&NotedeckTextStyle::Tiny,
))
.color(ui.visuals().warn_fg_color),
)
.selectable(false),
@@ -256,17 +257,16 @@ fn display_name_widget(
}
}
pub fn one_line_display_name_widget(
display_name: DisplayName<'_>,
pub fn one_line_display_name_widget<'a>(
visuals: &egui::Visuals,
display_name: DisplayName<'a>,
style: NotedeckTextStyle,
) -> impl egui::Widget + '_ {
) -> impl egui::Widget + 'a {
let text_style = style.text_style();
let color = visuals.noninteractive().fg_stroke.color;
move |ui: &mut egui::Ui| match display_name {
DisplayName::One(n) => ui.label(
RichText::new(n)
.text_style(text_style)
.color(colors::GRAY_SECONDARY),
),
DisplayName::One(n) => ui.label(RichText::new(n).text_style(text_style).color(color)),
DisplayName::Both {
display_name,
@@ -274,7 +274,7 @@ pub fn one_line_display_name_widget(
} => ui.label(
RichText::new(display_name)
.text_style(text_style)
.color(colors::GRAY_SECONDARY),
.color(color),
),
}
}

View File

@@ -2,8 +2,8 @@ use crate::relay_pool_manager::{RelayPoolManager, RelayStatus};
use crate::ui::{Preview, PreviewConfig, View};
use egui::{Align, Button, Frame, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2};
use crate::app_style::NotedeckTextStyle;
use enostr::RelayPool;
use notedeck::NotedeckTextStyle;
pub struct RelayView<'a> {
manager: RelayPoolManager<'a>,
@@ -126,7 +126,7 @@ fn delete_button(_dark_mode: bool) -> egui::Button<'static> {
egui::include_image!("../../assets/icons/delete_icon_4x.png")
};
*/
let img_data = egui::include_image!("../../assets/icons/delete_icon_4x.png");
let img_data = egui::include_image!("../../../../assets/icons/delete_icon_4x.png");
egui::Button::image(egui::Image::new(img_data).max_width(10.0)).frame(false)
}
@@ -165,12 +165,14 @@ fn show_connection_status(ui: &mut Ui, status: &RelayStatus) {
fn get_connection_icon(status: &RelayStatus) -> egui::Image<'static> {
let img_data = match status {
RelayStatus::Connected => egui::include_image!("../../assets/icons/connected_icon_4x.png"),
RelayStatus::Connected => {
egui::include_image!("../../../../assets/icons/connected_icon_4x.png")
}
RelayStatus::Connecting => {
egui::include_image!("../../assets/icons/connecting_icon_4x.png")
egui::include_image!("../../../../assets/icons/connecting_icon_4x.png")
}
RelayStatus::Disconnected => {
egui::include_image!("../../assets/icons/disconnected_icon_4x.png")
egui::include_image!("../../../../assets/icons/disconnected_icon_4x.png")
}
};

View File

@@ -5,21 +5,18 @@ use egui::{
use tracing::{error, info};
use crate::{
accounts::{Accounts, AccountsRoute},
accounts::AccountsRoute,
app::{get_active_columns_mut, get_decks_mut},
app_style::DECK_ICON_SIZE,
colors,
column::Column,
decks::{DecksAction, DecksCache},
imgcache::ImageCache,
nav::SwitchingAction,
route::Route,
support::Support,
theme_handler::ThemeHandler,
user_account::UserAccount,
Damus,
};
use notedeck::{Accounts, ImageCache, NotedeckTextStyle, ThemeHandler, UserAccount};
use super::{
anim::{AnimationHelper, ICON_EXPANSION_MULTIPLE},
configure_deck::deck_icon,
@@ -386,9 +383,9 @@ fn settings_button(dark_mode: bool) -> impl Widget {
let img_size = 24.0;
let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
let img_data = if dark_mode {
egui::include_image!("../../assets/icons/settings_dark_4x.png")
egui::include_image!("../../../../assets/icons/settings_dark_4x.png")
} else {
egui::include_image!("../../assets/icons/settings_light_4x.png")
egui::include_image!("../../../../assets/icons/settings_light_4x.png")
};
let img = egui::Image::new(img_data).max_width(img_size);
@@ -412,9 +409,9 @@ fn add_column_button(dark_mode: bool) -> impl Widget {
let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
let img_data = if dark_mode {
egui::include_image!("../../assets/icons/add_column_dark_4x.png")
egui::include_image!("../../../../assets/icons/add_column_dark_4x.png")
} else {
egui::include_image!("../../assets/icons/add_column_light_4x.png")
egui::include_image!("../../../../assets/icons/add_column_light_4x.png")
};
let img = egui::Image::new(img_data).max_width(img_size);
@@ -531,7 +528,7 @@ fn search_button() -> impl Widget {
fn expand_side_panel_button() -> impl Widget {
|ui: &mut egui::Ui| -> egui::Response {
let img_size = 40.0;
let img_data = egui::include_image!("../../assets/damus_rounded_80.png");
let img_data = egui::include_image!("../../../../assets/damus_rounded_80.png");
let img = egui::Image::new(img_data).max_width(img_size);
ui.add(img)
@@ -544,9 +541,9 @@ fn support_button() -> impl Widget {
let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
let img_data = if ui.visuals().dark_mode {
egui::include_image!("../../assets/icons/help_icon_dark_4x.png")
egui::include_image!("../../../../assets/icons/help_icon_dark_4x.png")
} else {
egui::include_image!("../../assets/icons/help_icon_inverted_4x.png")
egui::include_image!("../../../../assets/icons/help_icon_inverted_4x.png")
};
let img = egui::Image::new(img_data).max_width(img_size);
@@ -569,7 +566,7 @@ fn add_deck_button() -> impl Widget {
let img_size = 40.0;
let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
let img_data = egui::include_image!("../../assets/icons/new_deck_icon_4x_dark.png");
let img_data = egui::include_image!("../../../../assets/icons/new_deck_icon_4x_dark.png");
let img = egui::Image::new(img_data).max_width(img_size);
let helper = AnimationHelper::new(ui, "new-deck-icon", vec2(max_size, max_size));
@@ -626,80 +623,18 @@ fn milestone_name() -> impl Widget {
|ui: &mut egui::Ui| -> egui::Response {
ui.vertical_centered(|ui| {
let font = egui::FontId::new(
crate::app_style::get_font_size(
notedeck::fonts::get_font_size(
ui.ctx(),
&crate::app_style::NotedeckTextStyle::Tiny,
&NotedeckTextStyle::Tiny,
),
egui::FontFamily::Name(crate::fonts::NamedFontFamily::Bold.as_str().into()),
egui::FontFamily::Name(notedeck::fonts::NamedFontFamily::Bold.as_str().into()),
);
ui.add(Label::new(
RichText::new("ALPHA")
.color(crate::colors::GRAY_SECONDARY)
.color( ui.style().visuals.noninteractive().fg_stroke.color)
.font(font),
).selectable(false)).on_hover_text("Notedeck is an alpha product. Expect bugs and contact us when you run into issues.").on_hover_cursor(egui::CursorIcon::Help)
})
.inner
}
}
mod preview {
use egui_extras::{Size, StripBuilder};
use crate::{
app::get_active_columns_mut,
test_data,
ui::{Preview, PreviewConfig},
};
use super::*;
pub struct DesktopSidePanelPreview {
app: Damus,
}
impl DesktopSidePanelPreview {
fn new() -> Self {
let mut app = test_data::test_app();
get_active_columns_mut(&app.accounts, &mut app.decks_cache)
.add_column(Column::new(vec![Route::accounts()]));
DesktopSidePanelPreview { app }
}
}
impl View for DesktopSidePanelPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
StripBuilder::new(ui)
.size(Size::exact(SIDE_PANEL_WIDTH))
.sizes(Size::remainder(), 0)
.clip(true)
.horizontal(|mut strip| {
strip.cell(|ui| {
let mut panel = DesktopSidePanel::new(
&self.app.ndb,
&mut self.app.img_cache,
self.app.accounts.get_selected_account(),
&self.app.decks_cache,
);
let response = panel.show(ui);
DesktopSidePanel::perform_action(
&mut self.app.decks_cache,
&self.app.accounts,
&mut self.app.support,
&mut self.app.theme,
response.action,
);
});
});
}
}
impl Preview for DesktopSidePanel<'_> {
type Prev = DesktopSidePanelPreview;
fn preview(_cfg: PreviewConfig) -> Self::Prev {
DesktopSidePanelPreview::new()
}
}
}

View File

@@ -1,14 +1,10 @@
use egui::{vec2, Button, Label, Layout, RichText};
use tracing::error;
use crate::{
app_style::{get_font_size, NotedeckTextStyle},
colors::PINK,
fonts::NamedFontFamily,
support::Support,
};
use crate::{colors::PINK, support::Support};
use super::padding;
use notedeck::{NamedFontFamily, NotedeckTextStyle};
pub struct SupportView<'a> {
support: &'a mut Support,
@@ -23,7 +19,7 @@ impl<'a> SupportView<'a> {
padding(8.0, ui, |ui| {
ui.spacing_mut().item_spacing = egui::vec2(0.0, 8.0);
let font = egui::FontId::new(
get_font_size(ui.ctx(), &NotedeckTextStyle::Body),
notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Body),
egui::FontFamily::Name(NamedFontFamily::Bold.as_str().into()),
);
ui.add(Label::new(RichText::new("Running into a bug?").font(font)));
@@ -32,7 +28,8 @@ impl<'a> SupportView<'a> {
ui.label("Open your default email client to get help from the Damus team");
let size = vec2(120.0, 40.0);
ui.allocate_ui_with_layout(size, Layout::top_down(egui::Align::Center), |ui| {
let font_size = get_font_size(ui.ctx(), &NotedeckTextStyle::Body);
let font_size =
notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Body);
let button_resp = ui.add(open_email_button(font_size, size));
if button_resp.clicked() {
if let Err(e) = open::that(self.support.get_mailto_url()) {
@@ -54,9 +51,9 @@ impl<'a> SupportView<'a> {
RichText::new("Step 2").text_style(NotedeckTextStyle::Heading3.text_style()),
);
let size = vec2(80.0, 40.0);
let copy_button = Button::new(
RichText::new("Copy").size(get_font_size(ui.ctx(), &NotedeckTextStyle::Body)),
)
let copy_button = Button::new(RichText::new("Copy").size(
notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Body),
))
.fill(PINK)
.min_size(size);
padding(8.0, ui, |ui| {

View File

@@ -1,14 +1,12 @@
use crate::{
actionbar::NoteAction,
imgcache::ImageCache,
muted::MuteFun,
notecache::NoteCache,
notes_holder::{NotesHolder, NotesHolderStorage},
thread::Thread,
ui::note::NoteOptions,
unknowns::UnknownIds,
};
use nostrdb::{Ndb, NoteKey, Transaction};
use notedeck::{ImageCache, MuteFun, NoteCache, UnknownIds};
use tracing::error;
use super::timeline::TimelineTabView;

View File

@@ -1,13 +1,11 @@
use crate::actionbar::NoteAction;
use crate::timeline::TimelineTab;
use crate::{
column::Columns, imgcache::ImageCache, notecache::NoteCache, timeline::TimelineId, ui,
ui::note::NoteOptions,
};
use crate::{column::Columns, timeline::TimelineId, ui, ui::note::NoteOptions};
use egui::containers::scroll_area::ScrollBarVisibility;
use egui::{Direction, Layout};
use egui_tabs::TabColor;
use nostrdb::{Ndb, Transaction};
use notedeck::{ImageCache, NoteCache};
use tracing::{error, warn};
pub struct TimelineView<'a> {

View File

@@ -1,6 +1,6 @@
use crate::fonts::NamedFontFamily;
use egui::{Color32, RichText, Widget};
use nostrdb::ProfileRecord;
use notedeck::fonts::NamedFontFamily;
pub struct Username<'a> {
profile: Option<&'a ProfileRecord<'a>>,