diff --git a/README.md b/README.md index 8f34853..d83568d 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,6 @@ Potential improvements: - [Application] Allow user-provided TLS certificate. - [Distribution] Flatpack release. - [Distribution] compiled binary in GitHub release. -- [UI] Sort bookmarks. Things to consider: @@ -77,6 +76,7 @@ Things to consider: - [Application] Consider leveraging linkding's `/check` endpoint when adding bookmarks. - [Application] Do not block on when executing local database queries. - [UI] Loading indicator when performing long HTTP calls. +- [UI] Dynamically generate tags that affect filter. ## Thanks diff --git a/i18n/en/cosmicding.ftl b/i18n/en/cosmicding.ftl index 486bf04..c72ed5f 100644 --- a/i18n/en/cosmicding.ftl +++ b/i18n/en/cosmicding.ftl @@ -9,6 +9,10 @@ added-bookmark-to-account = Added bookmark {$bkmrk} to {$acc} api-key = API Key appearance = Appearance archived = Archived +bookmark-date-newest = Newest First +bookmark-date-oldest = Oldest First +bookmark-alphabetical-ascending = A-Z (Bookmark Title) +bookmark-alphabetical-descending = Z-A (Bookmark Title) bookmarks = Bookmarks bookmarks-with-count = Bookmarks ({$count}) cancel = Cancel @@ -59,6 +63,7 @@ settings = Settings shared = Shared shared-disabled = Shared (Disabled) snapshot = Snapshot +sort = Sort successful = successful tags = Tags tags-subtext = Enter any number of tags separated by space. diff --git a/res/screenshots/bookmarks.png b/res/screenshots/bookmarks.png index 8d3b1f4..d6158a4 100644 Binary files a/res/screenshots/bookmarks.png and b/res/screenshots/bookmarks.png differ diff --git a/src/app.rs b/src/app.rs index 91f9d11..01e2c64 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,4 @@ -use crate::config::{AppTheme, Config, CONFIG_VERSION}; +use crate::config::{AppTheme, Config, SortOption, CONFIG_VERSION}; use crate::db::{self, SqliteDatabase}; use crate::fl; use crate::http::{self}; @@ -102,6 +102,7 @@ pub enum Message { SetBookmarkTitle(String), SetBookmarkURL(String), SetBookmarkUnread(bool), + SortOption(SortOption), StartRefreshAccountProfile(Account), StartRefreshBookmarksForAccount(Account), StartRefreshBookmarksForAllAccounts, @@ -197,6 +198,7 @@ impl Application for Cosmicding { &self.key_binds, !self.accounts_view.accounts.is_empty(), !self.bookmarks_view.bookmarks.is_empty(), + self.config.sort_option, )] } @@ -366,6 +368,12 @@ impl Application for Cosmicding { config_set!(app_theme, app_theme); return self.update_config(); } + Message::SortOption(sort_option) => { + config_set!(sort_option, sort_option); + if !self.bookmarks_view.bookmarks.is_empty() { + return self.update(Message::LoadBookmarks); + } + } Message::SystemThemeModeChange => { return self.update_config(); } @@ -894,6 +902,40 @@ impl Application for Cosmicding { Message::LoadBookmarks => { self.bookmarks_view.bookmarks = block_on(async { db::SqliteDatabase::fetch_bookmarks(&mut self.db).await }); + match self.config.sort_option { + SortOption::BookmarksDateNewest => { + self.bookmarks_view.bookmarks.sort_by(|a, b| { + b.clone() + .date_added + .unwrap() + .cmp(&a.clone().date_added.unwrap()) + }); + } + SortOption::BookmarksDateOldest => { + self.bookmarks_view.bookmarks.sort_by(|a, b| { + a.clone() + .date_added + .unwrap() + .cmp(&b.clone().date_added.unwrap()) + }); + } + SortOption::BookmarkAlphabeticalAscending => { + self.bookmarks_view.bookmarks.sort_by(|a, b| { + a.clone() + .title + .to_lowercase() + .cmp(&b.clone().title.to_lowercase()) + }); + } + SortOption::BookmarkAlphabeticalDescending => { + self.bookmarks_view.bookmarks.sort_by(|a, b| { + b.clone() + .title + .to_lowercase() + .cmp(&a.clone().title.to_lowercase()) + }); + } + } } Message::StartupCompleted => { for account in self.accounts_view.accounts.clone() { @@ -901,6 +943,7 @@ impl Application for Cosmicding { } commands.push(Task::perform( async { + // Initial delay for refresh tokio::time::sleep(Duration::from_secs(1)).await; crate::app::Message::StartRefreshBookmarksForAllAccounts }, @@ -1030,6 +1073,7 @@ pub enum MenuAction { Empty, RefreshBookmarks, Settings, + SetSortBookmarks(SortOption), } impl _MenuAction for MenuAction { @@ -1043,6 +1087,7 @@ impl _MenuAction for MenuAction { MenuAction::Settings => Message::ToggleContextPage(ContextPage::Settings), MenuAction::AddBookmark => Message::AddBookmarkForm, MenuAction::RefreshBookmarks => Message::StartRefreshBookmarksForAllAccounts, + MenuAction::SetSortBookmarks(option) => Message::SortOption(*option), } } } diff --git a/src/config.rs b/src/config.rs index 932b2da..5796855 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,16 +31,26 @@ impl AppTheme { } } +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub enum SortOption { + BookmarksDateNewest, + BookmarksDateOldest, + BookmarkAlphabeticalAscending, + BookmarkAlphabeticalDescending, +} + #[derive(Debug, Clone, CosmicConfigEntry, Eq, PartialEq)] #[version = 1] pub struct Config { pub app_theme: AppTheme, + pub sort_option: SortOption, } impl Default for Config { fn default() -> Self { Self { app_theme: AppTheme::System, + sort_option: SortOption::BookmarksDateNewest, } } } diff --git a/src/menu.rs b/src/menu.rs index 2ca4cd5..a1a75f4 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use crate::config::SortOption; use cosmic::widget::menu::key_bind::KeyBind; use cosmic::{ widget::menu::{items, root, Item, ItemHeight, ItemWidth, MenuBar, Tree}, @@ -16,6 +17,7 @@ pub fn menu_bar<'a>( key_binds: &HashMap<KeyBind, MenuAction>, accounts_present: bool, bookmarks_present: bool, + sort_option: SortOption, ) -> Element<'a, Message> { MenuBar::new(vec![ Tree::with_children( @@ -48,6 +50,55 @@ pub fn menu_bar<'a>( ], ), ), + // TODO: (vkhitrin) dynamically generate enabled/disabled entries + // instead of writing manual code + Tree::with_children( + root(fl!("sort")), + items( + key_binds, + if bookmarks_present { + vec![ + Item::CheckBox( + fl!("bookmark-date-newest"), + matches!(sort_option, SortOption::BookmarksDateNewest), + MenuAction::SetSortBookmarks(SortOption::BookmarksDateNewest), + ), + Item::CheckBox( + fl!("bookmark-date-oldest"), + matches!(sort_option, SortOption::BookmarksDateOldest), + MenuAction::SetSortBookmarks(SortOption::BookmarksDateOldest), + ), + Item::Divider, + Item::CheckBox( + fl!("bookmark-alphabetical-ascending"), + matches!(sort_option, SortOption::BookmarkAlphabeticalAscending), + MenuAction::SetSortBookmarks(SortOption::BookmarkAlphabeticalAscending), + ), + Item::CheckBox( + fl!("bookmark-alphabetical-descending"), + matches!(sort_option, SortOption::BookmarkAlphabeticalDescending), + MenuAction::SetSortBookmarks( + SortOption::BookmarkAlphabeticalDescending, + ), + ), + ] + } else { + vec![ + Item::ButtonDisabled(fl!("bookmark-date-newest"), MenuAction::Empty), + Item::ButtonDisabled(fl!("bookmark-date-oldest"), MenuAction::Empty), + Item::Divider, + Item::ButtonDisabled( + fl!("bookmark-alphabetical-ascending"), + MenuAction::Empty, + ), + Item::ButtonDisabled( + fl!("bookmark-alphabetical-descending"), + MenuAction::Empty, + ), + ] + }, + ), + ), ]) .item_height(ItemHeight::Dynamic(40)) .item_width(ItemWidth::Uniform(240))