feat(settings): allow sorting thread replies newest first

This commit is contained in:
Fernando López Guevara
2025-07-29 21:30:35 -03:00
parent 40764d7368
commit f2153f53dc
7 changed files with 154 additions and 97 deletions

View File

@@ -1,5 +1,7 @@
use egui::{vec2, Button, Color32, ComboBox, Frame, Margin, RichText, ThemePreference};
use notedeck::{tr, Images, LanguageIdentifier, Localization, NotedeckTextStyle, SettingsHandler};
use egui::{vec2, Button, Color32, ComboBox, Frame, Margin, RichText, ScrollArea, ThemePreference};
use notedeck::{
tr, Images, LanguageIdentifier, Localization, NotedeckTextStyle, Settings, SettingsHandler,
};
use notedeck_ui::NoteOptions;
use strum::Display;
@@ -97,6 +99,7 @@ pub enum SettingsAction {
SetTheme(ThemePreference),
SetShowSourceClient(ShowSourceClientOption),
SetLocale(LanguageIdentifier),
SetRepliestNewestFirst(bool),
OpenRelays,
OpenCacheFolder,
ClearCacheFolder,
@@ -135,6 +138,11 @@ impl SettingsAction {
settings_handler.set_locale(language.to_string());
}
}
Self::SetRepliestNewestFirst(value) => {
app.note_options.set(NoteOptions::RepliesNewestFirst, value);
settings_handler.set_show_replies_newest_first(value);
settings_handler.save();
}
Self::OpenCacheFolder => {
use opener;
let _ = opener::open(img_cache.base_path.clone());
@@ -149,9 +157,7 @@ impl SettingsAction {
}
pub struct SettingsView<'a> {
theme: &'a mut String,
selected_language: &'a mut String,
show_note_client: &'a mut ShowSourceClientOption,
settings: &'a mut Settings,
i18n: &'a mut Localization,
img_cache: &'a mut Images,
}
@@ -181,30 +187,30 @@ where
impl<'a> SettingsView<'a> {
pub fn new(
img_cache: &'a mut Images,
selected_language: &'a mut String,
theme: &'a mut String,
show_note_client: &'a mut ShowSourceClientOption,
i18n: &'a mut Localization,
img_cache: &'a mut Images,
settings: &'a mut Settings,
// theme: &'a mut String,
// show_note_client: &'a mut ShowSourceClientOption,
// show_wide: &'a mut bool,
// show_replies_newest_first: &'a mut bool,
) -> Self {
Self {
show_note_client,
theme,
settings,
img_cache,
selected_language,
i18n,
}
}
/// Get the localized name for a language identifier
fn get_selected_language_name(&mut self) -> String {
if let Ok(lang_id) = self.selected_language.parse::<LanguageIdentifier>() {
if let Ok(lang_id) = self.settings.locale.parse::<LanguageIdentifier>() {
self.i18n
.get_locale_native_name(&lang_id)
.map(|s| s.to_owned())
.unwrap_or_else(|| lang_id.to_string())
} else {
self.selected_language.clone()
self.settings.locale.clone()
}
}
@@ -289,7 +295,7 @@ impl<'a> SettingsView<'a> {
.map(|s| s.to_owned())
.unwrap_or_else(|| lang.to_string());
if ui
.selectable_value(self.selected_language, lang.to_string(), name)
.selectable_value(&mut self.settings.locale, lang.to_string(), name)
.clicked()
{
action = Some(SettingsAction::SetLocale(lang.to_owned()))
@@ -304,10 +310,11 @@ impl<'a> SettingsView<'a> {
"Theme:",
"Label for theme, Appearance settings section",
));
if ui
.selectable_value(
self.theme,
THEME_LIGHT.into(),
&mut self.settings.theme,
ThemePreference::Light,
small_richtext(
self.i18n,
THEME_LIGHT.into(),
@@ -318,10 +325,11 @@ impl<'a> SettingsView<'a> {
{
action = Some(SettingsAction::SetTheme(ThemePreference::Light));
}
if ui
.selectable_value(
self.theme,
THEME_DARK.into(),
&mut self.settings.theme,
ThemePreference::Dark,
small_richtext(
self.i18n,
THEME_DARK.into(),
@@ -435,11 +443,32 @@ impl<'a> SettingsView<'a> {
let title = tr!(self.i18n, "Others", "Label for others settings section");
settings_group(ui, title, |ui| {
ui.horizontal(|ui| {
ui.label(small_richtext(
self.i18n,
"Sort replies newest first",
"Label for Sort replies newest first, others settings section",
));
if ui
.toggle_value(
&mut self.settings.show_replies_newest_first,
RichText::new(tr!(self.i18n, "ON", "ON"))
.text_style(NotedeckTextStyle::Small.text_style()),
)
.changed()
{
action = Some(SettingsAction::SetRepliestNewestFirst(
self.settings.show_replies_newest_first,
));
}
});
ui.horizontal_wrapped(|ui| {
ui.label(small_richtext(
self.i18n,
"Show source client",
"Label for Show source client, others settings section",
"Source client",
"Label for Source client, others settings section",
));
for option in [
@@ -447,9 +476,12 @@ impl<'a> SettingsView<'a> {
ShowSourceClientOption::Top,
ShowSourceClientOption::Bottom,
] {
let mut current: ShowSourceClientOption =
self.settings.show_source_client.clone().into();
if ui
.selectable_value(
self.show_note_client,
&mut current,
option,
RichText::new(option.label(self.i18n))
.text_style(NotedeckTextStyle::Small.text_style()),
@@ -491,27 +523,29 @@ impl<'a> SettingsView<'a> {
Frame::default()
.inner_margin(Margin::symmetric(10, 10))
.show(ui, |ui| {
if let Some(new_action) = self.appearance_section(ui) {
action = Some(new_action);
}
ScrollArea::vertical().show(ui, |ui| {
if let Some(new_action) = self.appearance_section(ui) {
action = Some(new_action);
}
ui.add_space(5.0);
ui.add_space(5.0);
if let Some(new_action) = self.storage_section(ui) {
action = Some(new_action);
}
if let Some(new_action) = self.storage_section(ui) {
action = Some(new_action);
}
ui.add_space(5.0);
ui.add_space(5.0);
if let Some(new_action) = self.other_options_section(ui) {
action = Some(new_action);
}
if let Some(new_action) = self.other_options_section(ui) {
action = Some(new_action);
}
ui.add_space(10.0);
ui.add_space(10.0);
if let Some(new_action) = self.manage_relays_section(ui) {
action = Some(new_action);
}
if let Some(new_action) = self.manage_relays_section(ui) {
action = Some(new_action);
}
});
});
action

View File

@@ -115,7 +115,10 @@ impl<'a, 'd> ThreadView<'a, 'd> {
.unwrap()
.list;
let notes = note_builder.into_notes(&mut self.threads.seen_flags);
let notes = note_builder.into_notes(
self.note_options.contains(NoteOptions::RepliesNewestFirst),
&mut self.threads.seen_flags,
);
if !full_chain {
// TODO(kernelkind): insert UI denoting we don't have the full chain yet
@@ -223,7 +226,11 @@ impl<'a> ThreadNoteBuilder<'a> {
self.replies.push(note);
}
pub fn into_notes(mut self, seen_flags: &mut NoteSeenFlags) -> ThreadNotes<'a> {
pub fn into_notes(
mut self,
replies_newer_first: bool,
seen_flags: &mut NoteSeenFlags,
) -> ThreadNotes<'a> {
let mut notes = Vec::new();
let selected_is_root = self.chain.is_empty();
@@ -246,6 +253,11 @@ impl<'a> ThreadNoteBuilder<'a> {
unread_and_have_replies: false,
});
if replies_newer_first {
self.replies
.sort_by(|a, b| b.created_at().cmp(&a.created_at()));
}
for reply in self.replies {
notes.push(ThreadNote {
unread_and_have_replies: *seen_flags.get(reply.id()).unwrap_or(&false),