Jelajahi Sumber

Add ability to change entry sort order in the UI

James Loh 4 tahun lalu
induk
melakukan
78f6bbe93d

+ 2 - 0
client/model.go

@@ -26,6 +26,7 @@ type User struct {
 	Language          string     `json:"language"`
 	Timezone          string     `json:"timezone"`
 	EntryDirection    string     `json:"entry_sorting_direction"`
+	EntryOrder        string     `json:"entry_sorting_order"`
 	Stylesheet        string     `json:"stylesheet"`
 	GoogleID          string     `json:"google_id"`
 	OpenIDConnectID   string     `json:"openid_connect_id"`
@@ -59,6 +60,7 @@ type UserModificationRequest struct {
 	Language          *string `json:"language"`
 	Timezone          *string `json:"timezone"`
 	EntryDirection    *string `json:"entry_sorting_direction"`
+	EntryOrder        *string `json:"entry_sorting_order"`
 	Stylesheet        *string `json:"stylesheet"`
 	GoogleID          *string `json:"google_id"`
 	OpenIDConnectID   *string `json:"openid_connect_id"`

+ 20 - 12
database/migrations.go

@@ -17,7 +17,7 @@ var migrations = []func(tx *sql.Tx) error{
 			CREATE TABLE schema_version (
 				version text not null
 			);
-			
+
 			CREATE TABLE users (
 				id serial not null,
 				username text not null unique,
@@ -29,7 +29,7 @@ var migrations = []func(tx *sql.Tx) error{
 				last_login_at timestamp with time zone,
 				primary key (id)
 			);
-			
+
 			CREATE TABLE sessions (
 				id serial not null,
 				user_id int not null,
@@ -41,7 +41,7 @@ var migrations = []func(tx *sql.Tx) error{
 				unique (user_id, token),
 				foreign key (user_id) references users(id) on delete cascade
 			);
-			
+
 			CREATE TABLE categories (
 				id serial not null,
 				user_id int not null,
@@ -50,7 +50,7 @@ var migrations = []func(tx *sql.Tx) error{
 				unique (user_id, title),
 				foreign key (user_id) references users(id) on delete cascade
 			);
-			
+
 			CREATE TABLE feeds (
 				id bigserial not null,
 				user_id int not null,
@@ -68,9 +68,9 @@ var migrations = []func(tx *sql.Tx) error{
 				foreign key (user_id) references users(id) on delete cascade,
 				foreign key (category_id) references categories(id) on delete cascade
 			);
-			
+
 			CREATE TYPE entry_status as enum('unread', 'read', 'removed');
-			
+
 			CREATE TABLE entries (
 				id bigserial not null,
 				user_id int not null,
@@ -87,9 +87,9 @@ var migrations = []func(tx *sql.Tx) error{
 				foreign key (user_id) references users(id) on delete cascade,
 				foreign key (feed_id) references feeds(id) on delete cascade
 			);
-			
+
 			CREATE INDEX entries_feed_idx on entries using btree(feed_id);
-			
+
 			CREATE TABLE enclosures (
 				id bigserial not null,
 				user_id int not null,
@@ -101,7 +101,7 @@ var migrations = []func(tx *sql.Tx) error{
 				foreign key (user_id) references users(id) on delete cascade,
 				foreign key (entry_id) references entries(id) on delete cascade
 			);
-			
+
 			CREATE TABLE icons (
 				id bigserial not null,
 				hash text not null unique,
@@ -109,14 +109,14 @@ var migrations = []func(tx *sql.Tx) error{
 				content bytea not null,
 				primary key (id)
 			);
-			
+
 			CREATE TABLE feed_icons (
 				feed_id bigint not null,
 				icon_id bigint not null,
 				primary key(feed_id, icon_id),
 				foreign key (feed_id) references feeds(id) on delete cascade,
 				foreign key (icon_id) references icons(id) on delete cascade
-			);		
+			);
 		`
 		_, err = tx.Exec(sql)
 		return err
@@ -410,7 +410,7 @@ var migrations = []func(tx *sql.Tx) error{
 	},
 	func(tx *sql.Tx) (err error) {
 		sql := `
-			ALTER TABLE feeds 
+			ALTER TABLE feeds
 				ADD COLUMN blocklist_rules text not null default '',
 				ADD COLUMN keeplist_rules text not null default ''
 		`
@@ -555,4 +555,12 @@ var migrations = []func(tx *sql.Tx) error{
 		_, err = tx.Exec(sql)
 		return err
 	},
+	func(tx *sql.Tx) (err error) {
+		sql := `
+			CREATE TYPE entry_sorting_order AS enum('published_at', 'created_at');
+			ALTER TABLE users ADD COLUMN entry_order entry_sorting_order default 'published_at';
+		`
+		_, err = tx.Exec(sql)
+		return err
+	},
 }

+ 3 - 0
locale/translations/de_DE.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Eigenständige",
     "form.prefs.select.minimal_ui": "Minimal",
     "form.prefs.select.browser": "Browser",
+    "form.prefs.select.publish_time": "Eintrag veröffentlichte Zeit",
+    "form.prefs.select.created_time": "Eintrag erstellt Zeit",
     "form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
     "form.prefs.label.entry_swipe": "Wischgeste für Einträge auf dem Handy aktivieren",
     "form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen",
     "form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
+    "form.prefs.label.entry_order": "Eintrag Sortierspalte",
     "form.import.label.file": "OPML Datei",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Fever API aktivieren",

+ 3 - 0
locale/translations/el_EL.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Μεμονωμένο",
     "form.prefs.select.minimal_ui": "Ελάχιστη",
     "form.prefs.select.browser": "Περιηγητής",
+    "form.prefs.select.publish_time": "Δημοσιευμένος χρόνος εισόδου",
+    "form.prefs.select.created_time": "Χρόνος δημιουργίας καταχώρησης",
     "form.prefs.label.keyboard_shortcuts": "Ενεργοποίηση συντομεύσεων πληκτρολογίου",
     "form.prefs.label.entry_swipe": "Ενεργοποιήστε τη χειρονομία σάρωσης στις καταχωρήσεις στο κινητό",
     "form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα",
     "form.prefs.label.custom_css": "Προσαρμοσμένο CSS",
+    "form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου",
     "form.import.label.file": "Αρχείο OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Ενεργοποιήστε το Fever API",

+ 3 - 0
locale/translations/en_US.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Standalone",
     "form.prefs.select.minimal_ui": "Minimal",
     "form.prefs.select.browser": "Browser",
+    "form.prefs.select.publish_time": "Entry published time",
+    "form.prefs.select.created_time": "Entry created time",
     "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
     "form.prefs.label.entry_swipe": "Enable swipe gesture on entries on mobile",
     "form.prefs.label.show_reading_time": "Show estimated reading time for articles",
     "form.prefs.label.custom_css": "Custom CSS",
+    "form.prefs.label.entry_order": "Entry Sorting Column",
     "form.import.label.file": "OPML file",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Activate Fever API",

+ 3 - 0
locale/translations/es_ES.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Ser único",
     "form.prefs.select.minimal_ui": "Mínimo",
     "form.prefs.select.browser": "Navegador",
+    "form.prefs.select.publish_time": "Hora de publicación de la entrada",
+    "form.prefs.select.created_time": "Hora de creación de la entrada",
     "form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
     "form.prefs.label.entry_swipe": "Habilitar el gesto de deslizar el dedo en las entradas en el móvil",
     "form.prefs.label.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos",
     "form.prefs.label.custom_css": "CSS personalizado",
+    "form.prefs.label.entry_order": "Columna de clasificación de entradas",
     "form.import.label.file": "Archivo OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Activar API de Fever",

+ 3 - 0
locale/translations/fr_FR.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Autonome",
     "form.prefs.select.minimal_ui": "Minimal",
     "form.prefs.select.browser": "Navigateur",
+    "form.prefs.select.publish_time": "Heure de publication de l'entrée",
+    "form.prefs.select.created_time": "Heure de création de l'entrée",
     "form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
     "form.prefs.label.entry_swipe": "Activer le geste de balayage sur les entrées sur mobile",
     "form.prefs.label.show_reading_time": "Afficher le temps de lecture estimé des articles",
     "form.prefs.label.custom_css": "CSS personnalisé",
+    "form.prefs.label.entry_order": "Colonne de tri des entrées",
     "form.import.label.file": "Fichier OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Activer l'API de Fever",

+ 3 - 0
locale/translations/it_IT.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Autonoma",
     "form.prefs.select.minimal_ui": "Minimale",
     "form.prefs.select.browser": "Browser",
+    "form.prefs.select.publish_time": "Ora di pubblicazione dell'entrata",
+    "form.prefs.select.created_time": "Tempo di creazione dell'entrata",
     "form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
     "form.prefs.label.entry_swipe": "Abilita il gesto di scorrimento sulle voci sul cellulare",
     "form.prefs.label.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli",
     "form.prefs.label.custom_css": "CSS personalizzati",
+    "form.prefs.label.entry_order": "Colonna di ordinamento delle voci",
     "form.import.label.file": "File OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Abilita l'API di Fever",

+ 3 - 0
locale/translations/ja_JP.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "スタンドアロン",
     "form.prefs.select.minimal_ui": "最小限",
     "form.prefs.select.browser": "ブラウザ",
+    "form.prefs.select.publish_time": "エントリー公開時間",
+    "form.prefs.select.created_time": "エントリー作成時間",
     "form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
     "form.prefs.label.entry_swipe": "モバイルのエントリでスワイプジェスチャーを有効にする",
     "form.prefs.label.show_reading_time": "記事の推定読書時間を表示する",
     "form.prefs.label.custom_css": "カスタムCSS",
+    "form.prefs.label.entry_order": "エントリーソートカラム",
     "form.import.label.file": "OPML ファイル",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Fever API を有効にする",

+ 3 - 0
locale/translations/nl_NL.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Standalone",
     "form.prefs.select.minimal_ui": "Minimaal",
     "form.prefs.select.browser": "Browser",
+    "form.prefs.select.publish_time": "Tijd van binnenkomst",
+    "form.prefs.select.created_time": "Tijdstip van binnenkomst",
     "form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
     "form.prefs.label.entry_swipe": "Schakel veegbewegingen in voor items op mobiel",
     "form.prefs.label.show_reading_time": "Toon geschatte leestijd voor artikelen",
     "form.prefs.label.custom_css": "Aangepaste CSS",
+    "form.prefs.label.entry_order": "Ingang Sorteerkolom",
     "form.import.label.file": "OPML-bestand",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Activeer Fever API",

+ 3 - 0
locale/translations/pl_PL.json

@@ -300,7 +300,10 @@
     "form.prefs.select.standalone": "Samodzielny",
     "form.prefs.select.minimal_ui": "Minimalny",
     "form.prefs.select.browser": "Przeglądarka",
+    "form.prefs.select.publish_time": "Czas publikacji wpisu",
+    "form.prefs.select.created_time": "Czas utworzenia wpisu",
     "form.prefs.label.custom_css": "Niestandardowy CSS",
+    "form.prefs.label.entry_order": "Kolumna sortowania wpisów",
     "form.import.label.file": "Plik OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Aktywuj Fever API",

+ 3 - 0
locale/translations/pt_BR.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Autônomo",
     "form.prefs.select.minimal_ui": "Mínimo",
     "form.prefs.select.browser": "Navegador",
+    "form.prefs.select.publish_time": "Entrada hora de publicação",
+    "form.prefs.select.created_time": "Entrada tempo criado",
     "form.prefs.label.keyboard_shortcuts": "Habilitar atalhos do teclado",
     "form.prefs.label.entry_swipe": "Ativar gesto de deslizar nas entradas no celular",
     "form.prefs.label.show_reading_time": "Mostrar tempo estimado de leitura de artigos",
     "form.prefs.label.custom_css": "CSS customizado",
+    "form.prefs.label.entry_order": "Coluna de Ordenação de Entrada",
     "form.import.label.file": "Arquivo OPML",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Ativar API do Fever",

+ 3 - 0
locale/translations/ru_RU.json

@@ -297,10 +297,13 @@
     "form.prefs.select.standalone": "Автономный",
     "form.prefs.select.minimal_ui": "Минимальный",
     "form.prefs.select.browser": "Браузер",
+    "form.prefs.select.publish_time": "Время публикации заявки",
+    "form.prefs.select.created_time": "Время создания записи",
     "form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
     "form.prefs.label.entry_swipe": "Включить жест смахивания для записей на мобильном устройстве",
     "form.prefs.label.show_reading_time": "Показать примерное время чтения статей",
     "form.prefs.label.custom_css": "Пользовательские CSS",
+    "form.prefs.label.entry_order": "Колонка сортировки ввода",
     "form.import.label.file": "OPML файл",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Активировать Fever API",

+ 3 - 0
locale/translations/tr_TR.json

@@ -295,10 +295,13 @@
     "form.prefs.select.standalone": "Bağımsız",
     "form.prefs.select.minimal_ui": "Minimal",
     "form.prefs.select.browser": "Tarayıcı",
+    "form.prefs.select.publish_time": "Giriş yayınlanma zamanı",
+    "form.prefs.select.created_time": "Girişin oluşturulma zamanı",
     "form.prefs.label.keyboard_shortcuts": "Klavye kısayollarını etkinleştir",
     "form.prefs.label.entry_swipe": "Mobil cihazlarda iletiler için kaydırma hareketlerini etkinleştir",
     "form.prefs.label.show_reading_time": "Makaleler için tahmini okuma süresini göster",
     "form.prefs.label.custom_css": "Özel CSS",
+    "form.prefs.label.entry_order": "Giriş Sıralama Sütunu",
     "form.import.label.file": "OPML dosyası",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "Fever API'yi Etkinleştir",

+ 3 - 0
locale/translations/zh_CN.json

@@ -293,10 +293,13 @@
     "form.prefs.select.standalone": "独立",
     "form.prefs.select.minimal_ui": "最小",
     "form.prefs.select.browser": "浏览器",
+    "form.prefs.select.publish_time": "参赛作品公布时间",
+    "form.prefs.select.created_time": "条目创建时间",
     "form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
     "form.prefs.label.entry_swipe": "在移动设备上启用滑动手势",
     "form.prefs.label.show_reading_time": "显示文章的预计阅读时间",
     "form.prefs.label.custom_css": "自定义 CSS",
+    "form.prefs.label.entry_order": "条目排序栏",
     "form.import.label.file": "OPML 文件",
     "form.import.label.url": "URL",
     "form.integration.fever_activate": "启用 Fever API",

+ 6 - 0
model/user.go

@@ -20,6 +20,7 @@ type User struct {
 	Language          string     `json:"language"`
 	Timezone          string     `json:"timezone"`
 	EntryDirection    string     `json:"entry_sorting_direction"`
+	EntryOrder        string     `json:"entry_sorting_order"`
 	Stylesheet        string     `json:"stylesheet"`
 	GoogleID          string     `json:"google_id"`
 	OpenIDConnectID   string     `json:"openid_connect_id"`
@@ -48,6 +49,7 @@ type UserModificationRequest struct {
 	Language          *string `json:"language"`
 	Timezone          *string `json:"timezone"`
 	EntryDirection    *string `json:"entry_sorting_direction"`
+	EntryOrder        *string `json:"entry_sorting_order"`
 	Stylesheet        *string `json:"stylesheet"`
 	GoogleID          *string `json:"google_id"`
 	OpenIDConnectID   *string `json:"openid_connect_id"`
@@ -89,6 +91,10 @@ func (u *UserModificationRequest) Patch(user *User) {
 		user.EntryDirection = *u.EntryDirection
 	}
 
+	if u.EntryOrder != nil {
+		user.EntryOrder = *u.EntryOrder
+	}
+
 	if u.Stylesheet != nil {
 		user.Stylesheet = *u.Stylesheet
 	}

+ 23 - 10
storage/user.go

@@ -84,7 +84,8 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
 			stylesheet,
 			google_id,
 			openid_connect_id,
-			display_mode
+			display_mode,
+			entry_order
 	`
 
 	tx, err := s.db.Begin()
@@ -116,6 +117,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
 		&user.GoogleID,
 		&user.OpenIDConnectID,
 		&user.DisplayMode,
+		&user.EntryOrder,
 	)
 	if err != nil {
 		tx.Rollback()
@@ -165,9 +167,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
 				stylesheet=$12,
 				google_id=$13,
 				openid_connect_id=$14,
-				display_mode=$15
+				display_mode=$15,
+				entry_order=$16
 			WHERE
-				id=$16
+				id=$17
 		`
 
 		_, err = s.db.Exec(
@@ -187,6 +190,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
 			user.GoogleID,
 			user.OpenIDConnectID,
 			user.DisplayMode,
+			user.EntryOrder,
 			user.ID,
 		)
 		if err != nil {
@@ -208,9 +212,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
 				stylesheet=$11,
 				google_id=$12,
 				openid_connect_id=$13,
-				display_mode=$14
+				display_mode=$14,
+				entry_order=$15
 			WHERE
-				id=$15
+				id=$16
 		`
 
 		_, err := s.db.Exec(
@@ -229,6 +234,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
 			user.GoogleID,
 			user.OpenIDConnectID,
 			user.DisplayMode,
+			user.EntryOrder,
 			user.ID,
 		)
 
@@ -269,7 +275,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
 			stylesheet,
 			google_id,
 			openid_connect_id,
-			display_mode
+			display_mode,
+			entry_order
 		FROM
 			users
 		WHERE
@@ -297,7 +304,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
 			stylesheet,
 			google_id,
 			openid_connect_id,
-			display_mode
+			display_mode,
+			entry_order
 		FROM
 			users
 		WHERE
@@ -325,7 +333,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) {
 			stylesheet,
 			google_id,
 			openid_connect_id,
-			display_mode
+			display_mode,
+			entry_order
 		FROM
 			users
 		WHERE
@@ -360,7 +369,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) {
 			u.stylesheet,
 			u.google_id,
 			u.openid_connect_id,
-			u.display_mode
+			u.display_mode,
+			u.entry_order
 		FROM
 			users u
 		LEFT JOIN
@@ -390,6 +400,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
 		&user.GoogleID,
 		&user.OpenIDConnectID,
 		&user.DisplayMode,
+		&user.EntryOrder,
 	)
 
 	if err == sql.ErrNoRows {
@@ -480,7 +491,8 @@ func (s *Storage) Users() (model.Users, error) {
 			stylesheet,
 			google_id,
 			openid_connect_id,
-			display_mode
+			display_mode,
+			entry_order
 		FROM
 			users
 		ORDER BY username ASC
@@ -511,6 +523,7 @@ func (s *Storage) Users() (model.Users, error) {
 			&user.GoogleID,
 			&user.OpenIDConnectID,
 			&user.DisplayMode,
+			&user.EntryOrder,
 		)
 
 		if err != nil {

+ 6 - 0
template/templates/views/settings.html

@@ -57,6 +57,12 @@
         <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
     </select>
 
+    <label for="form-entry-order">{{ t "form.prefs.label.entry_order" }}</label>
+    <select id="form-entry-order" name="entry_order">
+        <option value="published_at" {{ if eq "published_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.publish_time" }}</option>
+        <option value="created_at" {{ if eq "created_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.created_time" }}</option>
+    </select>
+
     <label for="form-entries-per-page">{{ t "form.prefs.label.entries_per_page" }}</label>
     <input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
 

+ 15 - 0
tests/user_test.go

@@ -401,6 +401,21 @@ func TestUpdateUserEntryDirectionWithInvalidValue(t *testing.T) {
 	}
 }
 
+func TestUpdateUserEntryOrderWithInvalidValue(t *testing.T) {
+	username := getRandomUsername()
+	client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword)
+	user, err := client.CreateUser(username, testStandardPassword, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	entryOrder := "invalid"
+	_, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{EntryOrder: &entryOrder})
+	if err == nil {
+		t.Fatal(`Updating a user EntryOrder with an invalid value should raise an error`)
+	}
+}
+
 func TestUpdateUserPasswordWithInvalidValue(t *testing.T) {
 	username := getRandomUsername()
 	client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword)

+ 1 - 1
ui/bookmark_entries.go

@@ -26,7 +26,7 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithoutStatus(model.EntryStatusRemoved)
 	builder.WithStarred()
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithOffset(offset)
 	builder.WithLimit(user.EntriesPerPage)

+ 1 - 1
ui/category_entries.go

@@ -37,7 +37,7 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
 	offset := request.QueryIntParam(r, "offset", 0)
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithCategoryID(category.ID)
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithStatus(model.EntryStatusUnread)
 	builder.WithOffset(offset)

+ 1 - 1
ui/category_entries_all.go

@@ -37,7 +37,7 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
 	offset := request.QueryIntParam(r, "offset", 0)
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithCategoryID(category.ID)
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithoutStatus(model.EntryStatusRemoved)
 	builder.WithOffset(offset)

+ 1 - 1
ui/feed_entries.go

@@ -38,7 +38,7 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithFeedID(feed.ID)
 	builder.WithStatus(model.EntryStatusUnread)
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithOffset(offset)
 	builder.WithLimit(user.EntriesPerPage)

+ 1 - 1
ui/feed_entries_all.go

@@ -38,7 +38,7 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithFeedID(feed.ID)
 	builder.WithoutStatus(model.EntryStatusRemoved)
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithOffset(offset)
 	builder.WithLimit(user.EntriesPerPage)

+ 3 - 0
ui/form/settings.go

@@ -21,6 +21,7 @@ type SettingsForm struct {
 	Language          string
 	Timezone          string
 	EntryDirection    string
+	EntryOrder        string
 	EntriesPerPage    int
 	KeyboardShortcuts bool
 	ShowReadingTime   bool
@@ -36,6 +37,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
 	user.Language = s.Language
 	user.Timezone = s.Timezone
 	user.EntryDirection = s.EntryDirection
+	user.EntryOrder = s.EntryOrder
 	user.EntriesPerPage = s.EntriesPerPage
 	user.KeyboardShortcuts = s.KeyboardShortcuts
 	user.ShowReadingTime = s.ShowReadingTime
@@ -84,6 +86,7 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
 		Language:          r.FormValue("language"),
 		Timezone:          r.FormValue("timezone"),
 		EntryDirection:    r.FormValue("entry_direction"),
+		EntryOrder:        r.FormValue("entry_order"),
 		EntriesPerPage:    int(entriesPerPage),
 		KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
 		ShowReadingTime:   r.FormValue("show_reading_time") == "1",

+ 1 - 0
ui/settings_show.go

@@ -32,6 +32,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
 		Language:          user.Language,
 		Timezone:          user.Timezone,
 		EntryDirection:    user.EntryDirection,
+		EntryOrder:        user.EntryOrder,
 		EntriesPerPage:    user.EntriesPerPage,
 		KeyboardShortcuts: user.KeyboardShortcuts,
 		ShowReadingTime:   user.ShowReadingTime,

+ 1 - 2
ui/shared_entries.go

@@ -9,7 +9,6 @@ import (
 
 	"miniflux.app/http/request"
 	"miniflux.app/http/response/html"
-	"miniflux.app/model"
 	"miniflux.app/ui/session"
 	"miniflux.app/ui/view"
 )
@@ -23,7 +22,7 @@ func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) {
 
 	builder := h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithShareCodeNotEmpty()
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 
 	entries, err := builder.GetEntries()

+ 1 - 1
ui/unread_entries.go

@@ -49,7 +49,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
 	m = timing.NewMetric("sql_fetch_unread_entries").Start()
 	builder = h.store.NewEntryQueryBuilder(user.ID)
 	builder.WithStatus(model.EntryStatusUnread)
-	builder.WithOrder(model.DefaultSortingOrder)
+	builder.WithOrder(user.EntryOrder)
 	builder.WithDirection(user.EntryDirection)
 	builder.WithOffset(offset)
 	builder.WithLimit(user.EntriesPerPage)