settings: use timed serializer, handle zoom properly, use custom text style for note body font size, added font size slider, added preview note

This commit is contained in:
Fernando López Guevara
2025-07-29 21:41:03 -03:00
parent b9e2fe5dd1
commit 9ff5753bca
21 changed files with 379 additions and 283 deletions

View File

@@ -1,13 +1,13 @@
use crate::account::FALLBACK_PUBKEY; use crate::account::FALLBACK_PUBKEY;
use crate::i18n::Localization; use crate::i18n::Localization;
use crate::persist::{AppSizeHandler, ZoomHandler}; use crate::persist::{AppSizeHandler, SettingsHandler};
use crate::wallet::GlobalWallet; use crate::wallet::GlobalWallet;
use crate::zaps::Zaps; use crate::zaps::Zaps;
use crate::JobPool;
use crate::{ use crate::{
frame_history::FrameHistory, AccountStorage, Accounts, AppContext, Args, DataPath, frame_history::FrameHistory, AccountStorage, Accounts, AppContext, Args, DataPath,
DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, UnknownIds, DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, UnknownIds,
}; };
use crate::{JobPool, SettingsHandler};
use egui::Margin; use egui::Margin;
use egui::ThemePreference; use egui::ThemePreference;
use egui_winit::clipboard::Clipboard; use egui_winit::clipboard::Clipboard;
@@ -40,9 +40,8 @@ pub struct Notedeck {
global_wallet: GlobalWallet, global_wallet: GlobalWallet,
path: DataPath, path: DataPath,
args: Args, args: Args,
settings_handler: SettingsHandler, settings: SettingsHandler,
app: Option<Rc<RefCell<dyn App>>>, app: Option<Rc<RefCell<dyn App>>>,
zoom: ZoomHandler,
app_size: AppSizeHandler, app_size: AppSizeHandler,
unrecognized_args: BTreeSet<String>, unrecognized_args: BTreeSet<String>,
clipboard: Clipboard, clipboard: Clipboard,
@@ -99,7 +98,15 @@ impl eframe::App for Notedeck {
render_notedeck(self, ctx); render_notedeck(self, ctx);
self.zoom.try_save_zoom_factor(ctx); self.settings.update_batch(|settings| {
settings.zoom_factor = ctx.zoom_factor();
settings.locale = self.i18n.get_current_locale().to_string();
settings.theme = if ctx.style().visuals.dark_mode {
ThemePreference::Dark
} else {
ThemePreference::Light
};
});
self.app_size.try_save_app_size(ctx); self.app_size.try_save_app_size(ctx);
if self.args.relay_debug { if self.args.relay_debug {
@@ -159,7 +166,7 @@ impl Notedeck {
1024usize * 1024usize * 1024usize * 1024usize 1024usize * 1024usize * 1024usize * 1024usize
}; };
let settings_handler = SettingsHandler::new(&path).load(); let settings = SettingsHandler::new(&path).load();
let config = Config::new().set_ingester_threads(2).set_mapsize(map_size); let config = Config::new().set_ingester_threads(2).set_mapsize(map_size);
@@ -214,12 +221,8 @@ impl Notedeck {
let img_cache = Images::new(img_cache_dir); let img_cache = Images::new(img_cache_dir);
let note_cache = NoteCache::default(); let note_cache = NoteCache::default();
let zoom = ZoomHandler::new(&path);
let app_size = AppSizeHandler::new(&path);
if let Some(z) = zoom.get_zoom_factor() { let app_size = AppSizeHandler::new(&path);
ctx.set_zoom_factor(z);
}
// migrate // migrate
if let Err(e) = img_cache.migrate_v0() { if let Err(e) = img_cache.migrate_v0() {
@@ -234,7 +237,7 @@ impl Notedeck {
let mut i18n = Localization::new(); let mut i18n = Localization::new();
let setting_locale: Result<LanguageIdentifier, LanguageIdentifierError> = let setting_locale: Result<LanguageIdentifier, LanguageIdentifierError> =
settings_handler.locale().parse(); settings.locale().parse();
if setting_locale.is_ok() { if setting_locale.is_ok() {
if let Err(err) = i18n.set_locale(setting_locale.unwrap()) { if let Err(err) = i18n.set_locale(setting_locale.unwrap()) {
@@ -261,9 +264,8 @@ impl Notedeck {
global_wallet, global_wallet,
path: path.clone(), path: path.clone(),
args: parsed_args, args: parsed_args,
settings_handler, settings,
app: None, app: None,
zoom,
app_size, app_size,
unrecognized_args, unrecognized_args,
frame_history: FrameHistory::default(), frame_history: FrameHistory::default(),
@@ -290,7 +292,7 @@ impl Notedeck {
global_wallet: &mut self.global_wallet, global_wallet: &mut self.global_wallet,
path: &self.path, path: &self.path,
args: &self.args, args: &self.args,
settings_handler: &mut self.settings_handler, settings: &mut self.settings,
clipboard: &mut self.clipboard, clipboard: &mut self.clipboard,
zaps: &mut self.zaps, zaps: &mut self.zaps,
frame_history: &mut self.frame_history, frame_history: &mut self.frame_history,
@@ -308,7 +310,15 @@ impl Notedeck {
} }
pub fn theme(&self) -> ThemePreference { pub fn theme(&self) -> ThemePreference {
self.settings_handler.theme() self.settings.theme()
}
pub fn note_body_font_size(&self) -> f32 {
self.settings.note_body_font_size()
}
pub fn zoom_factor(&self) -> f32 {
self.settings.zoom_factor()
} }
pub fn unrecognized_args(&self) -> &BTreeSet<String> { pub fn unrecognized_args(&self) -> &BTreeSet<String> {

View File

@@ -20,7 +20,7 @@ pub struct AppContext<'a> {
pub global_wallet: &'a mut GlobalWallet, pub global_wallet: &'a mut GlobalWallet,
pub path: &'a DataPath, pub path: &'a DataPath,
pub args: &'a Args, pub args: &'a Args,
pub settings_handler: &'a mut SettingsHandler, pub settings: &'a mut SettingsHandler,
pub clipboard: &'a mut Clipboard, pub clipboard: &'a mut Clipboard,
pub zaps: &'a mut Zaps, pub zaps: &'a mut Zaps,
pub frame_history: &'a mut FrameHistory, pub frame_history: &'a mut FrameHistory,

View File

@@ -31,6 +31,7 @@ pub fn desktop_font_size(text_style: &NotedeckTextStyle) -> f32 {
NotedeckTextStyle::Button => 13.0, NotedeckTextStyle::Button => 13.0,
NotedeckTextStyle::Small => 12.0, NotedeckTextStyle::Small => 12.0,
NotedeckTextStyle::Tiny => 10.0, NotedeckTextStyle::Tiny => 10.0,
NotedeckTextStyle::NoteBody => 16.0,
} }
} }
@@ -46,6 +47,7 @@ pub fn mobile_font_size(text_style: &NotedeckTextStyle) -> f32 {
NotedeckTextStyle::Button => 13.0, NotedeckTextStyle::Button => 13.0,
NotedeckTextStyle::Small => 12.0, NotedeckTextStyle::Small => 12.0,
NotedeckTextStyle::Tiny => 10.0, NotedeckTextStyle::Tiny => 10.0,
NotedeckTextStyle::NoteBody => 13.0,
} }
} }

View File

@@ -1,12 +1,9 @@
mod app_size; mod app_size;
mod settings_handler; mod settings_handler;
mod theme_handler;
mod token_handler; mod token_handler;
mod zoom;
pub use app_size::AppSizeHandler; pub use app_size::AppSizeHandler;
pub use settings_handler::Settings; pub use settings_handler::Settings;
pub use settings_handler::SettingsHandler; pub use settings_handler::SettingsHandler;
pub use theme_handler::ThemeHandler; pub use settings_handler::DEFAULT_NOTE_BODY_FONT_SIZE;
pub use token_handler::TokenHandler; pub use token_handler::TokenHandler;
pub use zoom::ZoomHandler;

View File

@@ -1,18 +1,23 @@
use crate::{ use crate::{
storage::{self, delete_file}, storage::delete_file, timed_serializer::TimedSerializer, DataPath, DataPathType, Directory,
DataPath, DataPathType, Directory,
}; };
use egui::ThemePreference; use egui::ThemePreference;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::{error, info}; use tracing::{error, info};
const THEME_FILE: &str = "theme.txt"; const THEME_FILE: &str = "theme.txt";
const ZOOM_FACTOR_FILE: &str = "zoom_level.json";
const SETTINGS_FILE: &str = "settings.json"; const SETTINGS_FILE: &str = "settings.json";
const DEFAULT_THEME: ThemePreference = ThemePreference::Dark; const DEFAULT_THEME: ThemePreference = ThemePreference::Dark;
const DEFAULT_LOCALE: &str = "es-US"; const DEFAULT_LOCALE: &str = "es-US";
const DEFAULT_ZOOM_FACTOR: f32 = 1.0; const DEFAULT_ZOOM_FACTOR: f32 = 1.0;
const DEFAULT_SHOW_SOURCE_CLIENT: &str = "hide"; const DEFAULT_SHOW_SOURCE_CLIENT: &str = "hide";
const DEFAULT_SHOW_REPLIES_NEWEST_FIRST: bool = false;
#[cfg(any(target_os = "android", target_os = "ios"))]
pub const DEFAULT_NOTE_BODY_FONT_SIZE: f32 = 13.0;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub const DEFAULT_NOTE_BODY_FONT_SIZE: f32 = 16.0;
fn deserialize_theme(serialized_theme: &str) -> Option<ThemePreference> { fn deserialize_theme(serialized_theme: &str) -> Option<ThemePreference> {
match serialized_theme { match serialized_theme {
@@ -23,72 +28,96 @@ fn deserialize_theme(serialized_theme: &str) -> Option<ThemePreference> {
} }
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, PartialEq, Clone)]
pub struct Settings { pub struct Settings {
pub theme: ThemePreference, pub theme: ThemePreference,
pub locale: String, pub locale: String,
pub zoom_factor: f32, pub zoom_factor: f32,
pub show_source_client: String, pub show_source_client: String,
pub show_replies_newest_first: bool, pub show_replies_newest_first: bool,
pub note_body_font_size: f32,
} }
impl Default for Settings { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
// Use the same fallback theme as before
Self { Self {
theme: DEFAULT_THEME, theme: DEFAULT_THEME,
locale: DEFAULT_LOCALE.to_string(), locale: DEFAULT_LOCALE.to_string(),
zoom_factor: DEFAULT_ZOOM_FACTOR, zoom_factor: DEFAULT_ZOOM_FACTOR,
show_source_client: DEFAULT_SHOW_SOURCE_CLIENT.to_string(), show_source_client: DEFAULT_SHOW_SOURCE_CLIENT.to_string(),
show_replies_newest_first: false, show_replies_newest_first: DEFAULT_SHOW_REPLIES_NEWEST_FIRST,
note_body_font_size: DEFAULT_NOTE_BODY_FONT_SIZE,
} }
} }
} }
pub struct SettingsHandler { pub struct SettingsHandler {
directory: Directory, directory: Directory,
serializer: TimedSerializer<Settings>,
current_settings: Option<Settings>, current_settings: Option<Settings>,
} }
impl SettingsHandler { impl SettingsHandler {
fn read_legacy_theme(&self) -> Option<ThemePreference> { fn read_from_theme_file(&self) -> Option<ThemePreference> {
match self.directory.get_file(THEME_FILE.to_string()) { match self.directory.get_file(THEME_FILE.to_string()) {
Ok(contents) => deserialize_theme(contents.trim()), Ok(contents) => deserialize_theme(contents.trim()),
Err(_) => None, Err(_) => None,
} }
} }
fn migrate_to_settings_file(&mut self) -> Result<(), ()> { fn read_from_zomfactor_file(&self) -> Option<f32> {
match self.directory.get_file(ZOOM_FACTOR_FILE.to_string()) {
Ok(contents) => serde_json::from_str::<f32>(&contents).ok(),
Err(_) => None,
}
}
fn migrate_to_settings_file(&mut self) -> bool {
let mut settings = Settings::default();
let mut migrated = false;
// if theme.txt exists migrate // if theme.txt exists migrate
if let Some(theme_from_file) = self.read_legacy_theme() { if let Some(theme_from_file) = self.read_from_theme_file() {
info!("migrating theme preference from theme.txt file"); info!("migrating theme preference from theme.txt file");
_ = delete_file(&self.directory.file_path, THEME_FILE.to_string()); _ = delete_file(&self.directory.file_path, THEME_FILE.to_string());
self.current_settings = Some(Settings { settings.theme = theme_from_file;
theme: theme_from_file, migrated = true;
..Settings::default()
});
self.save();
Ok(())
} else { } else {
Err(()) info!("theme.txt file not found, using default theme");
};
// if zoom_factor.txt exists migrate
if let Some(zom_factor) = self.read_from_zomfactor_file() {
info!("migrating theme preference from zom_factor file");
_ = delete_file(&self.directory.file_path, ZOOM_FACTOR_FILE.to_string());
settings.zoom_factor = zom_factor;
migrated = true;
} else {
info!("zoom_factor.txt exists migrate file not found, using default zoom factor");
};
if migrated {
self.current_settings = Some(settings);
self.try_save_settings();
} }
migrated
} }
pub fn new(path: &DataPath) -> Self { pub fn new(path: &DataPath) -> Self {
let directory = Directory::new(path.path(DataPathType::Setting)); let directory = Directory::new(path.path(DataPathType::Setting));
let current_settings: Option<Settings> = None; let serializer =
TimedSerializer::new(path, DataPathType::Setting, "settings.json".to_owned());
Self { Self {
directory, directory,
current_settings, serializer,
current_settings: None,
} }
} }
pub fn load(mut self) -> Self { pub fn load(mut self) -> Self {
if self.migrate_to_settings_file().is_ok() { if self.migrate_to_settings_file() {
return self; return self;
} }
@@ -114,22 +143,9 @@ impl SettingsHandler {
self self
} }
pub fn save(&self) { pub(crate) fn try_save_settings(&mut self) {
let settings = self.current_settings.as_ref().unwrap(); let settings = self.get_settings_mut().clone();
match serde_json::to_string(settings) { self.serializer.try_save(settings);
Ok(serialized) => {
if let Err(e) = storage::write_file(
&self.directory.file_path,
SETTINGS_FILE.to_string(),
&serialized,
) {
error!("Could not save settings: {}", e);
} else {
info!("Settings saved successfully");
}
}
Err(e) => error!("Failed to serialize settings: {}", e),
};
} }
pub fn get_settings_mut(&mut self) -> &mut Settings { pub fn get_settings_mut(&mut self) -> &mut Settings {
@@ -141,7 +157,7 @@ impl SettingsHandler {
pub fn set_theme(&mut self, theme: ThemePreference) { pub fn set_theme(&mut self, theme: ThemePreference) {
self.get_settings_mut().theme = theme; self.get_settings_mut().theme = theme;
self.save(); self.try_save_settings();
} }
pub fn set_locale<S>(&mut self, locale: S) pub fn set_locale<S>(&mut self, locale: S)
@@ -149,12 +165,12 @@ impl SettingsHandler {
S: Into<String>, S: Into<String>,
{ {
self.get_settings_mut().locale = locale.into(); self.get_settings_mut().locale = locale.into();
self.save(); self.try_save_settings();
} }
pub fn set_zoom_factor(&mut self, zoom_factor: f32) { pub fn set_zoom_factor(&mut self, zoom_factor: f32) {
self.get_settings_mut().zoom_factor = zoom_factor; self.get_settings_mut().zoom_factor = zoom_factor;
self.save(); self.try_save_settings();
} }
pub fn set_show_source_client<S>(&mut self, option: S) pub fn set_show_source_client<S>(&mut self, option: S)
@@ -162,12 +178,17 @@ impl SettingsHandler {
S: Into<String>, S: Into<String>,
{ {
self.get_settings_mut().show_source_client = option.into(); self.get_settings_mut().show_source_client = option.into();
self.save(); self.try_save_settings();
} }
pub fn set_show_replies_newest_first(&mut self, value: bool) { pub fn set_show_replies_newest_first(&mut self, value: bool) {
self.get_settings_mut().show_replies_newest_first = value; self.get_settings_mut().show_replies_newest_first = value;
self.save(); self.try_save_settings();
}
pub fn set_note_body_font_size(&mut self, value: f32) {
self.get_settings_mut().note_body_font_size = value;
self.try_save_settings();
} }
pub fn update_batch<F>(&mut self, update_fn: F) pub fn update_batch<F>(&mut self, update_fn: F)
@@ -176,12 +197,12 @@ impl SettingsHandler {
{ {
let settings = self.get_settings_mut(); let settings = self.get_settings_mut();
update_fn(settings); update_fn(settings);
self.save(); self.try_save_settings();
} }
pub fn update_settings(&mut self, new_settings: Settings) { pub fn update_settings(&mut self, new_settings: Settings) {
self.current_settings = Some(new_settings); self.current_settings = Some(new_settings);
self.save(); self.try_save_settings();
} }
pub fn theme(&self) -> ThemePreference { pub fn theme(&self) -> ThemePreference {
@@ -216,10 +237,17 @@ impl SettingsHandler {
self.current_settings self.current_settings
.as_ref() .as_ref()
.map(|s| s.show_replies_newest_first) .map(|s| s.show_replies_newest_first)
.unwrap_or(false) .unwrap_or(DEFAULT_SHOW_REPLIES_NEWEST_FIRST)
} }
pub fn is_loaded(&self) -> bool { pub fn is_loaded(&self) -> bool {
self.current_settings.is_some() self.current_settings.is_some()
} }
pub fn note_body_font_size(&self) -> f32 {
self.current_settings
.as_ref()
.map(|s| s.note_body_font_size)
.unwrap_or(DEFAULT_NOTE_BODY_FONT_SIZE)
}
} }

View File

@@ -1,76 +0,0 @@
use egui::ThemePreference;
use tracing::{error, info};
use crate::{storage, DataPath, DataPathType, Directory};
pub struct ThemeHandler {
directory: Directory,
fallback_theme: ThemePreference,
}
const THEME_FILE: &str = "theme.txt";
impl ThemeHandler {
pub fn new(path: &DataPath) -> Self {
let directory = Directory::new(path.path(DataPathType::Setting));
let fallback_theme = ThemePreference::Dark;
Self {
directory,
fallback_theme,
}
}
pub fn load(&self) -> ThemePreference {
match self.directory.get_file(THEME_FILE.to_owned()) {
Ok(contents) => match deserialize_theme(contents) {
Some(theme) => theme,
None => {
error!(
"Could not deserialize theme. Using fallback {:?} instead",
self.fallback_theme
);
self.fallback_theme
}
},
Err(e) => {
error!(
"Could not read {} file: {:?}\nUsing fallback {:?} instead",
THEME_FILE, e, self.fallback_theme
);
self.fallback_theme
}
}
}
pub fn save(&self, theme: ThemePreference) {
match storage::write_file(
&self.directory.file_path,
THEME_FILE.to_owned(),
&theme_to_serialized(&theme),
) {
Ok(_) => info!(
"Successfully saved {:?} theme change to {}",
theme, THEME_FILE
),
Err(_) => error!("Could not save {:?} theme change to {}", theme, THEME_FILE),
}
}
}
fn theme_to_serialized(theme: &ThemePreference) -> String {
match theme {
ThemePreference::Dark => "dark",
ThemePreference::Light => "light",
ThemePreference::System => "system",
}
.to_owned()
}
fn deserialize_theme(serialized_theme: String) -> Option<ThemePreference> {
match serialized_theme.as_str() {
"dark" => Some(ThemePreference::Dark),
"light" => Some(ThemePreference::Light),
"system" => Some(ThemePreference::System),
_ => None,
}
}

View File

@@ -1,26 +0,0 @@
use crate::{DataPath, DataPathType};
use egui::Context;
use crate::timed_serializer::TimedSerializer;
pub struct ZoomHandler {
serializer: TimedSerializer<f32>,
}
impl ZoomHandler {
pub fn new(path: &DataPath) -> Self {
let serializer =
TimedSerializer::new(path, DataPathType::Setting, "zoom_level.json".to_owned());
Self { serializer }
}
pub fn try_save_zoom_factor(&mut self, ctx: &Context) {
let cur_zoom_level = ctx.zoom_factor();
self.serializer.try_save(cur_zoom_level);
}
pub fn get_zoom_factor(&self) -> Option<f32> {
self.serializer.get_item()
}
}

View File

@@ -15,6 +15,7 @@ pub enum NotedeckTextStyle {
Button, Button,
Small, Small,
Tiny, Tiny,
NoteBody,
} }
impl NotedeckTextStyle { impl NotedeckTextStyle {
@@ -29,6 +30,7 @@ impl NotedeckTextStyle {
Self::Button => TextStyle::Button, Self::Button => TextStyle::Button,
Self::Small => TextStyle::Small, Self::Small => TextStyle::Small,
Self::Tiny => TextStyle::Name("Tiny".into()), Self::Tiny => TextStyle::Name("Tiny".into()),
Self::NoteBody => TextStyle::Name("NoteBody".into()),
} }
} }
@@ -43,6 +45,7 @@ impl NotedeckTextStyle {
Self::Button => FontFamily::Proportional, Self::Button => FontFamily::Proportional,
Self::Small => FontFamily::Proportional, Self::Small => FontFamily::Proportional,
Self::Tiny => FontFamily::Proportional, Self::Tiny => FontFamily::Proportional,
Self::NoteBody => FontFamily::Proportional,
} }
} }

View File

@@ -2,16 +2,16 @@ use crate::debouncer::Debouncer;
use crate::{storage, DataPath, DataPathType, Directory}; use crate::{storage, DataPath, DataPathType, Directory};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
use tracing::info; // Adjust this import path as needed use tracing::info;
pub struct TimedSerializer<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> { pub struct TimedSerializer<T: PartialEq + Clone + Serialize + for<'de> Deserialize<'de>> {
directory: Directory, directory: Directory,
file_name: String, file_name: String,
debouncer: Debouncer, debouncer: Debouncer,
saved_item: Option<T>, saved_item: Option<T>,
} }
impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerializer<T> { impl<T: PartialEq + Clone + Serialize + for<'de> Deserialize<'de>> TimedSerializer<T> {
pub fn new(path: &DataPath, path_type: DataPathType, file_name: String) -> Self { pub fn new(path: &DataPath, path_type: DataPathType, file_name: String) -> Self {
let directory = Directory::new(path.path(path_type)); let directory = Directory::new(path.path(path_type));
let delay = Duration::from_millis(1000); let delay = Duration::from_millis(1000);
@@ -30,11 +30,11 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
self self
} }
// returns whether successful /// Returns whether it actually wrote the new value
pub fn try_save(&mut self, cur_item: T) -> bool { pub fn try_save(&mut self, cur_item: T) -> bool {
if self.debouncer.should_act() { if self.debouncer.should_act() {
if let Some(saved_item) = self.saved_item { if let Some(ref saved_item) = self.saved_item {
if saved_item != cur_item { if *saved_item != cur_item {
return self.save(cur_item); return self.save(cur_item);
} }
} else { } else {
@@ -45,8 +45,8 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
} }
pub fn get_item(&self) -> Option<T> { pub fn get_item(&self) -> Option<T> {
if self.saved_item.is_some() { if let Some(ref item) = self.saved_item {
return self.saved_item; return Some(item.clone());
} }
if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) { if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) {
if let Ok(item) = serde_json::from_str::<T>(&file_contents) { if let Ok(item) = serde_json::from_str::<T>(&file_contents) {

View File

@@ -1,8 +1,19 @@
use crate::NotedeckTextStyle;
pub const NARROW_SCREEN_WIDTH: f32 = 550.0;
/// Determine if the screen is narrow. This is useful for detecting mobile /// 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. /// contexts, but with the nuance that we may also have a wide android tablet.
pub fn richtext_small<S>(text: S) -> egui::RichText
where
S: Into<String>,
{
egui::RichText::new(text).text_style(NotedeckTextStyle::Small.text_style())
}
pub fn is_narrow(ctx: &egui::Context) -> bool { pub fn is_narrow(ctx: &egui::Context) -> bool {
let screen_size = ctx.input(|c| c.screen_rect().size()); let screen_size = ctx.input(|c| c.screen_rect().size());
screen_size.x < 550.0 screen_size.x < NARROW_SCREEN_WIDTH
} }
pub fn is_oled() -> bool { pub fn is_oled() -> bool {

View File

@@ -69,7 +69,13 @@ pub async fn android_main(app: AndroidApp) {
Box::new(move |cc| { Box::new(move |cc| {
let ctx = &cc.egui_ctx; let ctx = &cc.egui_ctx;
let mut notedeck = Notedeck::new(ctx, path, &app_args); let mut notedeck = Notedeck::new(ctx, path, &app_args);
setup_chrome(ctx, &notedeck.args(), notedeck.theme()); setup_chrome(
ctx,
&notedeck.args(),
notedeck.theme(),
notedeck.note_body_font_size(),
notedeck.zoom_factor(),
);
let context = &mut notedeck.app_context(); let context = &mut notedeck.app_context();
let dave = Dave::new(cc.wgpu_render_state.as_ref()); let dave = Dave::new(cc.wgpu_render_state.as_ref());

View File

@@ -113,8 +113,7 @@ impl ChromePanelAction {
match self { match self {
Self::SaveTheme(theme) => { Self::SaveTheme(theme) => {
ui.ctx().set_theme(*theme); ui.ctx().set_theme(*theme);
ctx.settings_handler.set_theme(*theme); ctx.settings.set_theme(*theme);
ctx.settings_handler.save();
} }
Self::Toolbar(toolbar_action) => match toolbar_action { Self::Toolbar(toolbar_action) => match toolbar_action {

View File

@@ -98,7 +98,13 @@ async fn main() {
let columns = Damus::new(&mut notedeck.app_context(), &args); let columns = Damus::new(&mut notedeck.app_context(), &args);
let dave = Dave::new(cc.wgpu_render_state.as_ref()); let dave = Dave::new(cc.wgpu_render_state.as_ref());
setup_chrome(ctx, notedeck.args(), notedeck.theme()); setup_chrome(
ctx,
notedeck.args(),
notedeck.theme(),
notedeck.note_body_font_size(),
notedeck.zoom_factor(),
);
// ensure we recognized all the arguments // ensure we recognized all the arguments
let completely_unrecognized: Vec<String> = notedeck let completely_unrecognized: Vec<String> = notedeck

View File

@@ -38,7 +38,13 @@ impl PreviewRunner {
"unrecognized args: {:?}", "unrecognized args: {:?}",
notedeck.unrecognized_args() notedeck.unrecognized_args()
); );
setup_chrome(ctx, notedeck.args(), notedeck.theme()); setup_chrome(
ctx,
notedeck.args(),
notedeck.theme(),
notedeck.note_body_font_size(),
notedeck.zoom_factor(),
);
notedeck.set_app(PreviewApp::new(preview)); notedeck.set_app(PreviewApp::new(preview));

View File

@@ -1,12 +1,18 @@
use crate::{fonts, theme}; use crate::{fonts, theme};
use eframe::NativeOptions; use eframe::NativeOptions;
use egui::ThemePreference; use egui::{FontId, ThemePreference};
use notedeck::{AppSizeHandler, DataPath}; use notedeck::{AppSizeHandler, DataPath, NotedeckTextStyle};
use notedeck_ui::app_images; use notedeck_ui::app_images;
use tracing::info; use tracing::info;
pub fn setup_chrome(ctx: &egui::Context, args: &notedeck::Args, theme: ThemePreference) { pub fn setup_chrome(
ctx: &egui::Context,
args: &notedeck::Args,
theme: ThemePreference,
note_body_font_size: f32,
zoom_factor: f32,
) {
let is_mobile = args let is_mobile = args
.is_mobile .is_mobile
.unwrap_or(notedeck::ui::is_compiled_as_mobile()); .unwrap_or(notedeck::ui::is_compiled_as_mobile());
@@ -31,6 +37,15 @@ pub fn setup_chrome(ctx: &egui::Context, args: &notedeck::Args, theme: ThemePref
ctx.set_visuals_of(egui::Theme::Light, theme::light_mode()); ctx.set_visuals_of(egui::Theme::Light, theme::light_mode());
setup_cc(ctx, is_mobile); setup_cc(ctx, is_mobile);
ctx.set_zoom_factor(zoom_factor);
let mut style = (*ctx.style()).clone();
style.text_styles.insert(
NotedeckTextStyle::NoteBody.text_style(),
FontId::proportional(note_body_font_size),
);
ctx.set_style(style);
} }
pub fn setup_cc(ctx: &egui::Context, is_mobile: bool) { pub fn setup_cc(ctx: &egui::Context, is_mobile: bool) {
@@ -39,7 +54,6 @@ pub fn setup_cc(ctx: &egui::Context, is_mobile: bool) {
if notedeck::ui::is_compiled_as_mobile() { if notedeck::ui::is_compiled_as_mobile() {
ctx.set_pixels_per_point(ctx.pixels_per_point() + 0.2); ctx.set_pixels_per_point(ctx.pixels_per_point() + 0.2);
} }
//ctx.set_pixels_per_point(1.0); //ctx.set_pixels_per_point(1.0);
// //
// //

View File

@@ -18,9 +18,13 @@ use egui_extras::{Size, StripBuilder};
use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPool}; use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPool};
use nostrdb::Transaction; use nostrdb::Transaction;
use notedeck::{ use notedeck::{
tr, ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState, Images, JobsCache, Localization, SettingsHandler, UnknownIds tr, ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState,
Images, JobsCache, Localization, SettingsHandler, UnknownIds,
};
use notedeck_ui::{
media::{MediaViewer, MediaViewerFlags, MediaViewerState},
NoteOptions,
}; };
use notedeck_ui::{media::{MediaViewer, MediaViewerFlags, MediaViewerState}, NoteOptions};
use std::collections::{BTreeSet, HashMap}; use std::collections::{BTreeSet, HashMap};
use std::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
@@ -487,11 +491,11 @@ impl Damus {
// cache.add_deck_default(*pk); // cache.add_deck_default(*pk);
//} //}
}; };
let settings_handler = &app_context.settings_handler; let settings = &app_context.settings;
let support = Support::new(app_context.path); let support = Support::new(app_context.path);
let note_options = get_note_options(parsed_args, settings_handler); let note_options = get_note_options(parsed_args, settings);
let jobs = JobsCache::default(); let jobs = JobsCache::default();

View File

@@ -485,13 +485,9 @@ fn process_render_nav_action(
.process_relay_action(ui.ctx(), ctx.pool, action); .process_relay_action(ui.ctx(), ctx.pool, action);
None None
} }
RenderNavAction::SettingsAction(action) => action.process_settings_action( RenderNavAction::SettingsAction(action) => {
app, action.process_settings_action(app, ctx.settings, ctx.i18n, ctx.img_cache, ui.ctx())
ctx.settings_handler, }
ctx.i18n,
ctx.img_cache,
ui.ctx(),
),
}; };
if let Some(action) = router_action { if let Some(action) = router_action {
@@ -585,13 +581,14 @@ fn render_nav_body(
.ui(ui) .ui(ui)
.map(RenderNavAction::RelayAction), .map(RenderNavAction::RelayAction),
Route::Settings => { Route::Settings => SettingsView::new(
let mut settings = ctx.settings_handler.get_settings_mut(); &mut ctx.settings.get_settings_mut(),
&mut note_context,
SettingsView::new(ctx.i18n, ctx.img_cache, &mut settings) &mut app.note_options,
&mut app.jobs,
)
.ui(ui) .ui(ui)
.map(RenderNavAction::SettingsAction) .map(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) {
txn txn

View File

@@ -1,12 +1,21 @@
use egui::{vec2, Button, Color32, ComboBox, Frame, Margin, RichText, ScrollArea, ThemePreference}; use egui::{
use notedeck::{ vec2, Button, Color32, ComboBox, FontId, Frame, Margin, RichText, ScrollArea, ThemePreference,
tr, Images, LanguageIdentifier, Localization, NotedeckTextStyle, Settings, SettingsHandler,
}; };
use notedeck_ui::NoteOptions; use enostr::NoteId;
use nostrdb::Transaction;
use notedeck::{
tr,
ui::{is_narrow, richtext_small},
Images, JobsCache, LanguageIdentifier, Localization, NoteContext, NotedeckTextStyle, Settings,
SettingsHandler, DEFAULT_NOTE_BODY_FONT_SIZE,
};
use notedeck_ui::{NoteOptions, NoteView};
use strum::Display; use strum::Display;
use crate::{nav::RouterAction, Damus, Route}; use crate::{nav::RouterAction, Damus, Route};
const PREVIEW_NOTE_ID: &str = "note1edjc8ggj07hwv77g2405uh6j2jkk5aud22gktxrvc2wnre4vdwgqzlv2gw";
const THEME_LIGHT: &str = "Light"; const THEME_LIGHT: &str = "Light";
const THEME_DARK: &str = "Dark"; const THEME_DARK: &str = "Dark";
@@ -88,6 +97,7 @@ pub enum SettingsAction {
SetShowSourceClient(ShowSourceClientOption), SetShowSourceClient(ShowSourceClientOption),
SetLocale(LanguageIdentifier), SetLocale(LanguageIdentifier),
SetRepliestNewestFirst(bool), SetRepliestNewestFirst(bool),
SetNoteBodyFontSize(f32),
OpenRelays, OpenRelays,
OpenCacheFolder, OpenCacheFolder,
ClearCacheFolder, ClearCacheFolder,
@@ -97,7 +107,7 @@ impl SettingsAction {
pub fn process_settings_action<'a>( pub fn process_settings_action<'a>(
self, self,
app: &mut Damus, app: &mut Damus,
settings_handler: &'a mut SettingsHandler, settings: &'a mut SettingsHandler,
i18n: &'a mut Localization, i18n: &'a mut Localization,
img_cache: &mut Images, img_cache: &mut Images,
ctx: &egui::Context, ctx: &egui::Context,
@@ -110,26 +120,25 @@ impl SettingsAction {
} }
Self::SetZoomFactor(zoom_factor) => { Self::SetZoomFactor(zoom_factor) => {
ctx.set_zoom_factor(zoom_factor); ctx.set_zoom_factor(zoom_factor);
settings_handler.set_zoom_factor(zoom_factor); settings.set_zoom_factor(zoom_factor);
} }
Self::SetShowSourceClient(option) => { Self::SetShowSourceClient(option) => {
option.set_note_options(&mut app.note_options); option.set_note_options(&mut app.note_options);
settings_handler.set_show_source_client(option); settings.set_show_source_client(option);
} }
Self::SetTheme(theme) => { Self::SetTheme(theme) => {
ctx.set_theme(theme); ctx.set_theme(theme);
settings_handler.set_theme(theme); settings.set_theme(theme);
} }
Self::SetLocale(language) => { Self::SetLocale(language) => {
if i18n.set_locale(language.clone()).is_ok() { if i18n.set_locale(language.clone()).is_ok() {
settings_handler.set_locale(language.to_string()); settings.set_locale(language.to_string());
} }
} }
Self::SetRepliestNewestFirst(value) => { Self::SetRepliestNewestFirst(value) => {
app.note_options.set(NoteOptions::RepliesNewestFirst, value); app.note_options.set(NoteOptions::RepliesNewestFirst, value);
settings_handler.set_show_replies_newest_first(value); settings.set_show_replies_newest_first(value);
settings_handler.save();
} }
Self::OpenCacheFolder => { Self::OpenCacheFolder => {
use opener; use opener;
@@ -138,20 +147,26 @@ impl SettingsAction {
Self::ClearCacheFolder => { Self::ClearCacheFolder => {
let _ = img_cache.clear_folder_contents(); let _ = img_cache.clear_folder_contents();
} }
Self::SetNoteBodyFontSize(size) => {
let mut style = (*ctx.style()).clone();
style.text_styles.insert(
NotedeckTextStyle::NoteBody.text_style(),
FontId::proportional(size),
);
ctx.set_style(style);
settings.set_note_body_font_size(size);
}
} }
settings_handler.save();
route_action route_action
} }
} }
pub struct SettingsView<'a> { pub struct SettingsView<'a> {
settings: &'a mut Settings, settings: &'a mut Settings,
i18n: &'a mut Localization, note_context: &'a mut NoteContext<'a>,
img_cache: &'a mut Images, note_options: &'a mut NoteOptions,
} jobs: &'a mut JobsCache,
fn small_richtext(i18n: &'_ mut Localization, text: &str, comment: &str) -> RichText {
RichText::new(tr!(i18n, text, comment)).text_style(NotedeckTextStyle::Small.text_style())
} }
fn settings_group<S>(ui: &mut egui::Ui, title: S, contents: impl FnOnce(&mut egui::Ui)) fn settings_group<S>(ui: &mut egui::Ui, title: S, contents: impl FnOnce(&mut egui::Ui))
@@ -175,21 +190,24 @@ where
impl<'a> SettingsView<'a> { impl<'a> SettingsView<'a> {
pub fn new( pub fn new(
i18n: &'a mut Localization,
img_cache: &'a mut Images,
settings: &'a mut Settings, settings: &'a mut Settings,
note_context: &'a mut NoteContext<'a>,
note_options: &'a mut NoteOptions,
jobs: &'a mut JobsCache,
) -> Self { ) -> Self {
Self { Self {
settings, settings,
img_cache, note_context,
i18n, note_options,
jobs,
} }
} }
/// Get the localized name for a language identifier /// Get the localized name for a language identifier
fn get_selected_language_name(&mut self) -> String { fn get_selected_language_name(&mut self) -> String {
if let Ok(lang_id) = self.settings.locale.parse::<LanguageIdentifier>() { if let Ok(lang_id) = self.settings.locale.parse::<LanguageIdentifier>() {
self.i18n self.note_context
.i18n
.get_locale_native_name(&lang_id) .get_locale_native_name(&lang_id)
.map(|s| s.to_owned()) .map(|s| s.to_owned())
.unwrap_or_else(|| lang_id.to_string()) .unwrap_or_else(|| lang_id.to_string())
@@ -201,19 +219,76 @@ impl<'a> SettingsView<'a> {
pub fn appearance_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> { pub fn appearance_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> {
let mut action = None; let mut action = None;
let title = tr!( let title = tr!(
self.i18n, self.note_context.i18n,
"Appearance", "Appearance",
"Label for appearance settings section", "Label for appearance settings section",
); );
settings_group(ui, title, |ui| { settings_group(ui, title, |ui| {
ui.horizontal(|ui| {
ui.label(richtext_small(tr!(
self.note_context.i18n,
"Font size:",
"Label for font size, Appearance settings section",
)));
if ui
.add(
egui::Slider::new(&mut self.settings.note_body_font_size, 8.0..=32.0)
.text(""),
)
.changed()
{
action = Some(SettingsAction::SetNoteBodyFontSize(
self.settings.note_body_font_size,
));
};
if ui
.button(richtext_small(tr!(
self.note_context.i18n,
"Reset",
"Label for reset note body font size, Appearance settings section",
)))
.clicked()
{
action = Some(SettingsAction::SetNoteBodyFontSize(
DEFAULT_NOTE_BODY_FONT_SIZE,
));
}
});
let txn = Transaction::new(self.note_context.ndb).unwrap();
if let Some(note_id) = NoteId::from_bech(PREVIEW_NOTE_ID) {
if let Ok(preview_note) =
self.note_context.ndb.get_note_by_id(&txn, &note_id.bytes())
{
notedeck_ui::padding(8.0, ui, |ui| {
if is_narrow(ui.ctx()) {
ui.set_max_width(ui.available_width());
}
NoteView::new(
self.note_context,
&preview_note,
self.note_options.clone(),
self.jobs,
)
.actionbar(false)
.options_button(false)
.show(ui);
});
ui.separator();
}
}
let current_zoom = ui.ctx().zoom_factor(); let current_zoom = ui.ctx().zoom_factor();
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(small_richtext( ui.label(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Zoom Level:", "Zoom Level:",
"Label for zoom level, Appearance settings section", "Label for zoom level, Appearance settings section",
)); )));
let min_reached = current_zoom <= MIN_ZOOM; let min_reached = current_zoom <= MIN_ZOOM;
let max_reached = current_zoom >= MAX_ZOOM; let max_reached = current_zoom >= MAX_ZOOM;
@@ -250,11 +325,11 @@ impl<'a> SettingsView<'a> {
}; };
if ui if ui
.button(small_richtext( .button(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Reset", "Reset",
"Label for reset zoom level, Appearance settings section", "Label for reset zoom level, Appearance settings section",
)) )))
.clicked() .clicked()
{ {
action = Some(SettingsAction::SetZoomFactor(RESET_ZOOM)); action = Some(SettingsAction::SetZoomFactor(RESET_ZOOM));
@@ -262,18 +337,19 @@ impl<'a> SettingsView<'a> {
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(small_richtext( ui.label(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Language:", "Language:",
"Label for language, Appearance settings section", "Label for language, Appearance settings section",
)); )));
// //
ComboBox::from_label("") ComboBox::from_label("")
.selected_text(self.get_selected_language_name()) .selected_text(self.get_selected_language_name())
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
for lang in self.i18n.get_available_locales() { for lang in self.note_context.i18n.get_available_locales() {
let name = self let name = self
.note_context
.i18n .i18n
.get_locale_native_name(lang) .get_locale_native_name(lang)
.map(|s| s.to_owned()) .map(|s| s.to_owned())
@@ -289,21 +365,21 @@ impl<'a> SettingsView<'a> {
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(small_richtext( ui.label(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Theme:", "Theme:",
"Label for theme, Appearance settings section", "Label for theme, Appearance settings section",
)); )));
if ui if ui
.selectable_value( .selectable_value(
&mut self.settings.theme, &mut self.settings.theme,
ThemePreference::Light, ThemePreference::Light,
small_richtext( richtext_small(tr!(
self.i18n, self.note_context.i18n,
THEME_LIGHT.into(), THEME_LIGHT,
"Label for Theme Light, Appearance settings section", "Label for Theme Light, Appearance settings section",
), )),
) )
.clicked() .clicked()
{ {
@@ -314,11 +390,11 @@ impl<'a> SettingsView<'a> {
.selectable_value( .selectable_value(
&mut self.settings.theme, &mut self.settings.theme,
ThemePreference::Dark, ThemePreference::Dark,
small_richtext( richtext_small(tr!(
self.i18n, self.note_context.i18n,
THEME_DARK.into(), THEME_DARK,
"Label for Theme Dark, Appearance settings section", "Label for Theme Dark, Appearance settings section",
), )),
) )
.clicked() .clicked()
{ {
@@ -333,18 +409,28 @@ impl<'a> SettingsView<'a> {
pub fn storage_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> { pub fn storage_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> {
let id = ui.id(); let id = ui.id();
let mut action: Option<SettingsAction> = None; let mut action: Option<SettingsAction> = None;
let title = tr!(self.i18n, "Storage", "Label for storage settings section"); let title = tr!(
self.note_context.i18n,
"Storage",
"Label for storage settings section"
);
settings_group(ui, title, |ui| { settings_group(ui, title, |ui| {
ui.horizontal_wrapped(|ui| { ui.horizontal_wrapped(|ui| {
let static_imgs_size = self.img_cache.static_imgs.cache_size.lock().unwrap(); let static_imgs_size = self
.note_context
.img_cache
.static_imgs
.cache_size
.lock()
.unwrap();
let gifs_size = self.img_cache.gifs.cache_size.lock().unwrap(); let gifs_size = self.note_context.img_cache.gifs.cache_size.lock().unwrap();
ui.label( ui.label(
RichText::new(format!( RichText::new(format!(
"{} {}", "{} {}",
tr!( tr!(
self.i18n, self.note_context.i18n,
"Image cache size:", "Image cache size:",
"Label for Image cache size, Storage settings section" "Label for Image cache size, Storage settings section"
), ),
@@ -361,22 +447,22 @@ impl<'a> SettingsView<'a> {
if !notedeck::ui::is_compiled_as_mobile() if !notedeck::ui::is_compiled_as_mobile()
&& ui && ui
.button(small_richtext( .button(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"View folder", "View folder",
"Label for view folder button, Storage settings section", "Label for view folder button, Storage settings section",
)) )))
.clicked() .clicked()
{ {
action = Some(SettingsAction::OpenCacheFolder); action = Some(SettingsAction::OpenCacheFolder);
} }
let clearcache_resp = ui.button( let clearcache_resp = ui.button(
small_richtext( richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Clear cache", "Clear cache",
"Label for clear cache button, Storage settings section", "Label for clear cache button, Storage settings section",
) ))
.color(Color32::LIGHT_RED), .color(Color32::LIGHT_RED),
); );
@@ -389,7 +475,7 @@ impl<'a> SettingsView<'a> {
let mut confirm_pressed = false; let mut confirm_pressed = false;
clearcache_resp.show_tooltip_ui(|ui| { clearcache_resp.show_tooltip_ui(|ui| {
let confirm_resp = ui.button(tr!( let confirm_resp = ui.button(tr!(
self.i18n, self.note_context.i18n,
"Confirm", "Confirm",
"Label for confirm clear cache, Storage settings section" "Label for confirm clear cache, Storage settings section"
)); ));
@@ -400,7 +486,7 @@ impl<'a> SettingsView<'a> {
if confirm_resp.clicked() if confirm_resp.clicked()
|| ui || ui
.button(tr!( .button(tr!(
self.i18n, self.note_context.i18n,
"Cancel", "Cancel",
"Label for cancel clear cache, Storage settings section" "Label for cancel clear cache, Storage settings section"
)) ))
@@ -425,19 +511,23 @@ impl<'a> SettingsView<'a> {
fn other_options_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> { fn other_options_section(&mut self, ui: &mut egui::Ui) -> Option<SettingsAction> {
let mut action = None; let mut action = None;
let title = tr!(self.i18n, "Others", "Label for others settings section"); let title = tr!(
self.note_context.i18n,
"Others",
"Label for others settings section"
);
settings_group(ui, title, |ui| { settings_group(ui, title, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(small_richtext( ui.label(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Sort replies newest first", "Sort replies newest first",
"Label for Sort replies newest first, others settings section", "Label for Sort replies newest first, others settings section",
)); )));
if ui if ui
.toggle_value( .toggle_value(
&mut self.settings.show_replies_newest_first, &mut self.settings.show_replies_newest_first,
RichText::new(tr!(self.i18n, "ON", "ON")) RichText::new(tr!(self.note_context.i18n, "ON", "ON"))
.text_style(NotedeckTextStyle::Small.text_style()), .text_style(NotedeckTextStyle::Small.text_style()),
) )
.changed() .changed()
@@ -449,11 +539,11 @@ impl<'a> SettingsView<'a> {
}); });
ui.horizontal_wrapped(|ui| { ui.horizontal_wrapped(|ui| {
ui.label(small_richtext( ui.label(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Source client", "Source client",
"Label for Source client, others settings section", "Label for Source client, others settings section",
)); )));
for option in [ for option in [
ShowSourceClientOption::Hide, ShowSourceClientOption::Hide,
@@ -467,7 +557,7 @@ impl<'a> SettingsView<'a> {
.selectable_value( .selectable_value(
&mut current, &mut current,
option, option,
RichText::new(option.label(self.i18n)) RichText::new(option.label(self.note_context.i18n))
.text_style(NotedeckTextStyle::Small.text_style()), .text_style(NotedeckTextStyle::Small.text_style()),
) )
.changed() .changed()
@@ -487,11 +577,11 @@ impl<'a> SettingsView<'a> {
if ui if ui
.add_sized( .add_sized(
[ui.available_width(), 30.0], [ui.available_width(), 30.0],
Button::new(small_richtext( Button::new(richtext_small(tr!(
self.i18n, self.note_context.i18n,
"Configure relays", "Configure relays",
"Label for configure relays, settings section", "Label for configure relays, settings section",
)), ))),
) )
.clicked() .clicked()
{ {

View File

@@ -2,7 +2,7 @@ use crate::ProfilePreview;
use egui::Sense; use egui::Sense;
use enostr::Pubkey; use enostr::Pubkey;
use nostrdb::{Ndb, Transaction}; use nostrdb::{Ndb, Transaction};
use notedeck::{name::get_display_name, Images, NoteAction}; use notedeck::{name::get_display_name, Images, NoteAction, NotedeckTextStyle};
pub struct Mention<'a> { pub struct Mention<'a> {
ndb: &'a Ndb, ndb: &'a Ndb,
@@ -75,7 +75,9 @@ fn mention_ui(
get_display_name(profile.as_ref()).username_or_displayname() get_display_name(profile.as_ref()).username_or_displayname()
); );
let mut text = egui::RichText::new(name).color(link_color); let mut text = egui::RichText::new(name)
.color(link_color)
.text_style(NotedeckTextStyle::NoteBody.text_style());
if let Some(size) = size { if let Some(size) = size {
text = text.size(size); text = text.size(size);
} }

View File

@@ -4,7 +4,7 @@ use crate::{
}; };
use notedeck::{JobsCache, RenderableMedia}; use notedeck::{JobsCache, RenderableMedia};
use egui::{vec2, Color32, Hyperlink, RichText}; use egui::{vec2, Color32, Hyperlink, Label, RichText};
use nostrdb::{BlockType, Mention, Note, NoteKey, Transaction}; use nostrdb::{BlockType, Mention, Note, NoteKey, Transaction};
use tracing::warn; use tracing::warn;
@@ -42,6 +42,8 @@ impl<'a, 'd> NoteContents<'a, 'd> {
impl egui::Widget for &mut NoteContents<'_, '_> { impl egui::Widget for &mut NoteContents<'_, '_> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response { fn ui(self, ui: &mut egui::Ui) -> egui::Response {
ui.spacing_mut().item_spacing = vec2(0.0, 0.0);
if self.options.contains(NoteOptions::ShowNoteClientTop) { if self.options.contains(NoteOptions::ShowNoteClientTop) {
render_client(ui, self.note_context.note_cache, self.note); render_client(ui, self.note_context.note_cache, self.note);
} }
@@ -200,13 +202,24 @@ pub fn render_note_contents<'a>(
} }
_ => { _ => {
ui.colored_label(link_color, format!("@{}", &block.as_str()[..16])); ui.colored_label(
link_color,
RichText::new(format!("@{}", &block.as_str()[..16]))
.text_style(NotedeckTextStyle::NoteBody.text_style()),
);
} }
}, },
BlockType::Hashtag => { BlockType::Hashtag => {
if block.as_str().trim().len() == 0 {
continue 'block_loop;
}
let resp = ui let resp = ui
.colored_label(link_color, format!("#{}", block.as_str())) .colored_label(
link_color,
RichText::new(format!("#{}", block.as_str()))
.text_style(NotedeckTextStyle::NoteBody.text_style()),
)
.on_hover_cursor(egui::CursorIcon::PointingHand); .on_hover_cursor(egui::CursorIcon::PointingHand);
if resp.clicked() { if resp.clicked() {
@@ -231,8 +244,13 @@ pub fn render_note_contents<'a>(
}; };
if hide_media || !found_supported() { if hide_media || !found_supported() {
if block.as_str().trim().len() == 0 {
continue 'block_loop;
}
ui.add(Hyperlink::from_label_and_url( ui.add(Hyperlink::from_label_and_url(
RichText::new(block.as_str()).color(link_color), RichText::new(block.as_str())
.color(link_color)
.text_style(NotedeckTextStyle::NoteBody.text_style()),
block.as_str(), block.as_str(),
)); ));
} }
@@ -263,18 +281,18 @@ pub fn render_note_contents<'a>(
} }
if options.contains(NoteOptions::ScrambleText) { if options.contains(NoteOptions::ScrambleText) {
ui.add( ui.add(
egui::Label::new( Label::new(
RichText::new(rot13(block_str)) RichText::new(rot13(block_str))
.text_style(NotedeckTextStyle::Body.text_style()), .text_style(NotedeckTextStyle::NoteBody.text_style()),
) )
.wrap() .wrap()
.selectable(selectable), .selectable(selectable),
); );
} else { } else {
ui.add( ui.add(
egui::Label::new( Label::new(
RichText::new(block_str) RichText::new(block_str)
.text_style(NotedeckTextStyle::Body.text_style()), .text_style(NotedeckTextStyle::NoteBody.text_style()),
) )
.wrap() .wrap()
.selectable(selectable), .selectable(selectable),

View File

@@ -344,7 +344,12 @@ impl<'a, 'd> NoteView<'a, 'd> {
1.0, 1.0,
ui.visuals().noninteractive().bg_stroke.color, ui.visuals().noninteractive().bg_stroke.color,
)) ))
.show(ui, |ui| self.show_impl(ui)) .show(ui, |ui| {
if is_narrow(ui.ctx()) {
ui.set_width(ui.available_width());
}
self.show_impl(ui)
})
.inner .inner
} else { } else {
self.show_impl(ui) self.show_impl(ui)