Add ability to change entry sort order in the UI
This commit is contained in:
parent
27d170cbec
commit
78f6bbe93d
28 changed files with 122 additions and 30 deletions
|
@ -26,6 +26,7 @@ type User struct {
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone"`
|
||||||
EntryDirection string `json:"entry_sorting_direction"`
|
EntryDirection string `json:"entry_sorting_direction"`
|
||||||
|
EntryOrder string `json:"entry_sorting_order"`
|
||||||
Stylesheet string `json:"stylesheet"`
|
Stylesheet string `json:"stylesheet"`
|
||||||
GoogleID string `json:"google_id"`
|
GoogleID string `json:"google_id"`
|
||||||
OpenIDConnectID string `json:"openid_connect_id"`
|
OpenIDConnectID string `json:"openid_connect_id"`
|
||||||
|
@ -59,6 +60,7 @@ type UserModificationRequest struct {
|
||||||
Language *string `json:"language"`
|
Language *string `json:"language"`
|
||||||
Timezone *string `json:"timezone"`
|
Timezone *string `json:"timezone"`
|
||||||
EntryDirection *string `json:"entry_sorting_direction"`
|
EntryDirection *string `json:"entry_sorting_direction"`
|
||||||
|
EntryOrder *string `json:"entry_sorting_order"`
|
||||||
Stylesheet *string `json:"stylesheet"`
|
Stylesheet *string `json:"stylesheet"`
|
||||||
GoogleID *string `json:"google_id"`
|
GoogleID *string `json:"google_id"`
|
||||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||||
|
|
|
@ -555,4 +555,12 @@ var migrations = []func(tx *sql.Tx) error{
|
||||||
_, err = tx.Exec(sql)
|
_, err = tx.Exec(sql)
|
||||||
return err
|
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
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Eigenständige",
|
"form.prefs.select.standalone": "Eigenständige",
|
||||||
"form.prefs.select.minimal_ui": "Minimal",
|
"form.prefs.select.minimal_ui": "Minimal",
|
||||||
"form.prefs.select.browser": "Browser",
|
"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.keyboard_shortcuts": "Tastaturkürzel aktivieren",
|
||||||
"form.prefs.label.entry_swipe": "Wischgeste für Einträge auf dem Handy 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.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen",
|
||||||
"form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
|
"form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
|
||||||
|
"form.prefs.label.entry_order": "Eintrag Sortierspalte",
|
||||||
"form.import.label.file": "OPML Datei",
|
"form.import.label.file": "OPML Datei",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Fever API aktivieren",
|
"form.integration.fever_activate": "Fever API aktivieren",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Μεμονωμένο",
|
"form.prefs.select.standalone": "Μεμονωμένο",
|
||||||
"form.prefs.select.minimal_ui": "Ελάχιστη",
|
"form.prefs.select.minimal_ui": "Ελάχιστη",
|
||||||
"form.prefs.select.browser": "Περιηγητής",
|
"form.prefs.select.browser": "Περιηγητής",
|
||||||
|
"form.prefs.select.publish_time": "Δημοσιευμένος χρόνος εισόδου",
|
||||||
|
"form.prefs.select.created_time": "Χρόνος δημιουργίας καταχώρησης",
|
||||||
"form.prefs.label.keyboard_shortcuts": "Ενεργοποίηση συντομεύσεων πληκτρολογίου",
|
"form.prefs.label.keyboard_shortcuts": "Ενεργοποίηση συντομεύσεων πληκτρολογίου",
|
||||||
"form.prefs.label.entry_swipe": "Ενεργοποιήστε τη χειρονομία σάρωσης στις καταχωρήσεις στο κινητό",
|
"form.prefs.label.entry_swipe": "Ενεργοποιήστε τη χειρονομία σάρωσης στις καταχωρήσεις στο κινητό",
|
||||||
"form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα",
|
"form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα",
|
||||||
"form.prefs.label.custom_css": "Προσαρμοσμένο CSS",
|
"form.prefs.label.custom_css": "Προσαρμοσμένο CSS",
|
||||||
|
"form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου",
|
||||||
"form.import.label.file": "Αρχείο OPML",
|
"form.import.label.file": "Αρχείο OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Ενεργοποιήστε το Fever API",
|
"form.integration.fever_activate": "Ενεργοποιήστε το Fever API",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Standalone",
|
"form.prefs.select.standalone": "Standalone",
|
||||||
"form.prefs.select.minimal_ui": "Minimal",
|
"form.prefs.select.minimal_ui": "Minimal",
|
||||||
"form.prefs.select.browser": "Browser",
|
"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.keyboard_shortcuts": "Enable keyboard shortcuts",
|
||||||
"form.prefs.label.entry_swipe": "Enable swipe gesture on entries on mobile",
|
"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.show_reading_time": "Show estimated reading time for articles",
|
||||||
"form.prefs.label.custom_css": "Custom CSS",
|
"form.prefs.label.custom_css": "Custom CSS",
|
||||||
|
"form.prefs.label.entry_order": "Entry Sorting Column",
|
||||||
"form.import.label.file": "OPML file",
|
"form.import.label.file": "OPML file",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Activate Fever API",
|
"form.integration.fever_activate": "Activate Fever API",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Ser único",
|
"form.prefs.select.standalone": "Ser único",
|
||||||
"form.prefs.select.minimal_ui": "Mínimo",
|
"form.prefs.select.minimal_ui": "Mínimo",
|
||||||
"form.prefs.select.browser": "Navegador",
|
"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.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.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.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos",
|
||||||
"form.prefs.label.custom_css": "CSS personalizado",
|
"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.file": "Archivo OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Activar API de Fever",
|
"form.integration.fever_activate": "Activar API de Fever",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Autonome",
|
"form.prefs.select.standalone": "Autonome",
|
||||||
"form.prefs.select.minimal_ui": "Minimal",
|
"form.prefs.select.minimal_ui": "Minimal",
|
||||||
"form.prefs.select.browser": "Navigateur",
|
"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.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.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.show_reading_time": "Afficher le temps de lecture estimé des articles",
|
||||||
"form.prefs.label.custom_css": "CSS personnalisé",
|
"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.file": "Fichier OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Activer l'API de Fever",
|
"form.integration.fever_activate": "Activer l'API de Fever",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Autonoma",
|
"form.prefs.select.standalone": "Autonoma",
|
||||||
"form.prefs.select.minimal_ui": "Minimale",
|
"form.prefs.select.minimal_ui": "Minimale",
|
||||||
"form.prefs.select.browser": "Browser",
|
"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.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
|
||||||
"form.prefs.label.entry_swipe": "Abilita il gesto di scorrimento sulle voci sul cellulare",
|
"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.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli",
|
||||||
"form.prefs.label.custom_css": "CSS personalizzati",
|
"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.file": "File OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Abilita l'API di Fever",
|
"form.integration.fever_activate": "Abilita l'API di Fever",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "スタンドアロン",
|
"form.prefs.select.standalone": "スタンドアロン",
|
||||||
"form.prefs.select.minimal_ui": "最小限",
|
"form.prefs.select.minimal_ui": "最小限",
|
||||||
"form.prefs.select.browser": "ブラウザ",
|
"form.prefs.select.browser": "ブラウザ",
|
||||||
|
"form.prefs.select.publish_time": "エントリー公開時間",
|
||||||
|
"form.prefs.select.created_time": "エントリー作成時間",
|
||||||
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
|
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
|
||||||
"form.prefs.label.entry_swipe": "モバイルのエントリでスワイプジェスチャーを有効にする",
|
"form.prefs.label.entry_swipe": "モバイルのエントリでスワイプジェスチャーを有効にする",
|
||||||
"form.prefs.label.show_reading_time": "記事の推定読書時間を表示する",
|
"form.prefs.label.show_reading_time": "記事の推定読書時間を表示する",
|
||||||
"form.prefs.label.custom_css": "カスタムCSS",
|
"form.prefs.label.custom_css": "カスタムCSS",
|
||||||
|
"form.prefs.label.entry_order": "エントリーソートカラム",
|
||||||
"form.import.label.file": "OPML ファイル",
|
"form.import.label.file": "OPML ファイル",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Fever API を有効にする",
|
"form.integration.fever_activate": "Fever API を有効にする",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Standalone",
|
"form.prefs.select.standalone": "Standalone",
|
||||||
"form.prefs.select.minimal_ui": "Minimaal",
|
"form.prefs.select.minimal_ui": "Minimaal",
|
||||||
"form.prefs.select.browser": "Browser",
|
"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.keyboard_shortcuts": "Schakel sneltoetsen in",
|
||||||
"form.prefs.label.entry_swipe": "Schakel veegbewegingen in voor items op mobiel",
|
"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.show_reading_time": "Toon geschatte leestijd voor artikelen",
|
||||||
"form.prefs.label.custom_css": "Aangepaste CSS",
|
"form.prefs.label.custom_css": "Aangepaste CSS",
|
||||||
|
"form.prefs.label.entry_order": "Ingang Sorteerkolom",
|
||||||
"form.import.label.file": "OPML-bestand",
|
"form.import.label.file": "OPML-bestand",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Activeer Fever API",
|
"form.integration.fever_activate": "Activeer Fever API",
|
||||||
|
|
|
@ -300,7 +300,10 @@
|
||||||
"form.prefs.select.standalone": "Samodzielny",
|
"form.prefs.select.standalone": "Samodzielny",
|
||||||
"form.prefs.select.minimal_ui": "Minimalny",
|
"form.prefs.select.minimal_ui": "Minimalny",
|
||||||
"form.prefs.select.browser": "Przeglądarka",
|
"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.custom_css": "Niestandardowy CSS",
|
||||||
|
"form.prefs.label.entry_order": "Kolumna sortowania wpisów",
|
||||||
"form.import.label.file": "Plik OPML",
|
"form.import.label.file": "Plik OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Aktywuj Fever API",
|
"form.integration.fever_activate": "Aktywuj Fever API",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Autônomo",
|
"form.prefs.select.standalone": "Autônomo",
|
||||||
"form.prefs.select.minimal_ui": "Mínimo",
|
"form.prefs.select.minimal_ui": "Mínimo",
|
||||||
"form.prefs.select.browser": "Navegador",
|
"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.keyboard_shortcuts": "Habilitar atalhos do teclado",
|
||||||
"form.prefs.label.entry_swipe": "Ativar gesto de deslizar nas entradas no celular",
|
"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.show_reading_time": "Mostrar tempo estimado de leitura de artigos",
|
||||||
"form.prefs.label.custom_css": "CSS customizado",
|
"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.file": "Arquivo OPML",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Ativar API do Fever",
|
"form.integration.fever_activate": "Ativar API do Fever",
|
||||||
|
|
|
@ -297,10 +297,13 @@
|
||||||
"form.prefs.select.standalone": "Автономный",
|
"form.prefs.select.standalone": "Автономный",
|
||||||
"form.prefs.select.minimal_ui": "Минимальный",
|
"form.prefs.select.minimal_ui": "Минимальный",
|
||||||
"form.prefs.select.browser": "Браузер",
|
"form.prefs.select.browser": "Браузер",
|
||||||
|
"form.prefs.select.publish_time": "Время публикации заявки",
|
||||||
|
"form.prefs.select.created_time": "Время создания записи",
|
||||||
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
|
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
|
||||||
"form.prefs.label.entry_swipe": "Включить жест смахивания для записей на мобильном устройстве",
|
"form.prefs.label.entry_swipe": "Включить жест смахивания для записей на мобильном устройстве",
|
||||||
"form.prefs.label.show_reading_time": "Показать примерное время чтения статей",
|
"form.prefs.label.show_reading_time": "Показать примерное время чтения статей",
|
||||||
"form.prefs.label.custom_css": "Пользовательские CSS",
|
"form.prefs.label.custom_css": "Пользовательские CSS",
|
||||||
|
"form.prefs.label.entry_order": "Колонка сортировки ввода",
|
||||||
"form.import.label.file": "OPML файл",
|
"form.import.label.file": "OPML файл",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Активировать Fever API",
|
"form.integration.fever_activate": "Активировать Fever API",
|
||||||
|
|
|
@ -295,10 +295,13 @@
|
||||||
"form.prefs.select.standalone": "Bağımsız",
|
"form.prefs.select.standalone": "Bağımsız",
|
||||||
"form.prefs.select.minimal_ui": "Minimal",
|
"form.prefs.select.minimal_ui": "Minimal",
|
||||||
"form.prefs.select.browser": "Tarayıcı",
|
"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.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.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.show_reading_time": "Makaleler için tahmini okuma süresini göster",
|
||||||
"form.prefs.label.custom_css": "Özel CSS",
|
"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.file": "OPML dosyası",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "Fever API'yi Etkinleştir",
|
"form.integration.fever_activate": "Fever API'yi Etkinleştir",
|
||||||
|
|
|
@ -293,10 +293,13 @@
|
||||||
"form.prefs.select.standalone": "独立",
|
"form.prefs.select.standalone": "独立",
|
||||||
"form.prefs.select.minimal_ui": "最小",
|
"form.prefs.select.minimal_ui": "最小",
|
||||||
"form.prefs.select.browser": "浏览器",
|
"form.prefs.select.browser": "浏览器",
|
||||||
|
"form.prefs.select.publish_time": "参赛作品公布时间",
|
||||||
|
"form.prefs.select.created_time": "条目创建时间",
|
||||||
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
|
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
|
||||||
"form.prefs.label.entry_swipe": "在移动设备上启用滑动手势",
|
"form.prefs.label.entry_swipe": "在移动设备上启用滑动手势",
|
||||||
"form.prefs.label.show_reading_time": "显示文章的预计阅读时间",
|
"form.prefs.label.show_reading_time": "显示文章的预计阅读时间",
|
||||||
"form.prefs.label.custom_css": "自定义 CSS",
|
"form.prefs.label.custom_css": "自定义 CSS",
|
||||||
|
"form.prefs.label.entry_order": "条目排序栏",
|
||||||
"form.import.label.file": "OPML 文件",
|
"form.import.label.file": "OPML 文件",
|
||||||
"form.import.label.url": "URL",
|
"form.import.label.url": "URL",
|
||||||
"form.integration.fever_activate": "启用 Fever API",
|
"form.integration.fever_activate": "启用 Fever API",
|
||||||
|
|
|
@ -20,6 +20,7 @@ type User struct {
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone"`
|
||||||
EntryDirection string `json:"entry_sorting_direction"`
|
EntryDirection string `json:"entry_sorting_direction"`
|
||||||
|
EntryOrder string `json:"entry_sorting_order"`
|
||||||
Stylesheet string `json:"stylesheet"`
|
Stylesheet string `json:"stylesheet"`
|
||||||
GoogleID string `json:"google_id"`
|
GoogleID string `json:"google_id"`
|
||||||
OpenIDConnectID string `json:"openid_connect_id"`
|
OpenIDConnectID string `json:"openid_connect_id"`
|
||||||
|
@ -48,6 +49,7 @@ type UserModificationRequest struct {
|
||||||
Language *string `json:"language"`
|
Language *string `json:"language"`
|
||||||
Timezone *string `json:"timezone"`
|
Timezone *string `json:"timezone"`
|
||||||
EntryDirection *string `json:"entry_sorting_direction"`
|
EntryDirection *string `json:"entry_sorting_direction"`
|
||||||
|
EntryOrder *string `json:"entry_sorting_order"`
|
||||||
Stylesheet *string `json:"stylesheet"`
|
Stylesheet *string `json:"stylesheet"`
|
||||||
GoogleID *string `json:"google_id"`
|
GoogleID *string `json:"google_id"`
|
||||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||||
|
@ -89,6 +91,10 @@ func (u *UserModificationRequest) Patch(user *User) {
|
||||||
user.EntryDirection = *u.EntryDirection
|
user.EntryDirection = *u.EntryDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.EntryOrder != nil {
|
||||||
|
user.EntryOrder = *u.EntryOrder
|
||||||
|
}
|
||||||
|
|
||||||
if u.Stylesheet != nil {
|
if u.Stylesheet != nil {
|
||||||
user.Stylesheet = *u.Stylesheet
|
user.Stylesheet = *u.Stylesheet
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,8 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
|
||||||
stylesheet,
|
stylesheet,
|
||||||
google_id,
|
google_id,
|
||||||
openid_connect_id,
|
openid_connect_id,
|
||||||
display_mode
|
display_mode,
|
||||||
|
entry_order
|
||||||
`
|
`
|
||||||
|
|
||||||
tx, err := s.db.Begin()
|
tx, err := s.db.Begin()
|
||||||
|
@ -116,6 +117,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
|
||||||
&user.GoogleID,
|
&user.GoogleID,
|
||||||
&user.OpenIDConnectID,
|
&user.OpenIDConnectID,
|
||||||
&user.DisplayMode,
|
&user.DisplayMode,
|
||||||
|
&user.EntryOrder,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -165,9 +167,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
||||||
stylesheet=$12,
|
stylesheet=$12,
|
||||||
google_id=$13,
|
google_id=$13,
|
||||||
openid_connect_id=$14,
|
openid_connect_id=$14,
|
||||||
display_mode=$15
|
display_mode=$15,
|
||||||
|
entry_order=$16
|
||||||
WHERE
|
WHERE
|
||||||
id=$16
|
id=$17
|
||||||
`
|
`
|
||||||
|
|
||||||
_, err = s.db.Exec(
|
_, err = s.db.Exec(
|
||||||
|
@ -187,6 +190,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
||||||
user.GoogleID,
|
user.GoogleID,
|
||||||
user.OpenIDConnectID,
|
user.OpenIDConnectID,
|
||||||
user.DisplayMode,
|
user.DisplayMode,
|
||||||
|
user.EntryOrder,
|
||||||
user.ID,
|
user.ID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -208,9 +212,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
||||||
stylesheet=$11,
|
stylesheet=$11,
|
||||||
google_id=$12,
|
google_id=$12,
|
||||||
openid_connect_id=$13,
|
openid_connect_id=$13,
|
||||||
display_mode=$14
|
display_mode=$14,
|
||||||
|
entry_order=$15
|
||||||
WHERE
|
WHERE
|
||||||
id=$15
|
id=$16
|
||||||
`
|
`
|
||||||
|
|
||||||
_, err := s.db.Exec(
|
_, err := s.db.Exec(
|
||||||
|
@ -229,6 +234,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
||||||
user.GoogleID,
|
user.GoogleID,
|
||||||
user.OpenIDConnectID,
|
user.OpenIDConnectID,
|
||||||
user.DisplayMode,
|
user.DisplayMode,
|
||||||
|
user.EntryOrder,
|
||||||
user.ID,
|
user.ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -269,7 +275,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
|
||||||
stylesheet,
|
stylesheet,
|
||||||
google_id,
|
google_id,
|
||||||
openid_connect_id,
|
openid_connect_id,
|
||||||
display_mode
|
display_mode,
|
||||||
|
entry_order
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -297,7 +304,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
|
||||||
stylesheet,
|
stylesheet,
|
||||||
google_id,
|
google_id,
|
||||||
openid_connect_id,
|
openid_connect_id,
|
||||||
display_mode
|
display_mode,
|
||||||
|
entry_order
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -325,7 +333,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) {
|
||||||
stylesheet,
|
stylesheet,
|
||||||
google_id,
|
google_id,
|
||||||
openid_connect_id,
|
openid_connect_id,
|
||||||
display_mode
|
display_mode,
|
||||||
|
entry_order
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -360,7 +369,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) {
|
||||||
u.stylesheet,
|
u.stylesheet,
|
||||||
u.google_id,
|
u.google_id,
|
||||||
u.openid_connect_id,
|
u.openid_connect_id,
|
||||||
u.display_mode
|
u.display_mode,
|
||||||
|
u.entry_order
|
||||||
FROM
|
FROM
|
||||||
users u
|
users u
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
@ -390,6 +400,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
|
||||||
&user.GoogleID,
|
&user.GoogleID,
|
||||||
&user.OpenIDConnectID,
|
&user.OpenIDConnectID,
|
||||||
&user.DisplayMode,
|
&user.DisplayMode,
|
||||||
|
&user.EntryOrder,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
|
@ -480,7 +491,8 @@ func (s *Storage) Users() (model.Users, error) {
|
||||||
stylesheet,
|
stylesheet,
|
||||||
google_id,
|
google_id,
|
||||||
openid_connect_id,
|
openid_connect_id,
|
||||||
display_mode
|
display_mode,
|
||||||
|
entry_order
|
||||||
FROM
|
FROM
|
||||||
users
|
users
|
||||||
ORDER BY username ASC
|
ORDER BY username ASC
|
||||||
|
@ -511,6 +523,7 @@ func (s *Storage) Users() (model.Users, error) {
|
||||||
&user.GoogleID,
|
&user.GoogleID,
|
||||||
&user.OpenIDConnectID,
|
&user.OpenIDConnectID,
|
||||||
&user.DisplayMode,
|
&user.DisplayMode,
|
||||||
|
&user.EntryOrder,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -57,6 +57,12 @@
|
||||||
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
|
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
|
||||||
</select>
|
</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>
|
<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">
|
<input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
func TestUpdateUserPasswordWithInvalidValue(t *testing.T) {
|
||||||
username := getRandomUsername()
|
username := getRandomUsername()
|
||||||
client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword)
|
client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword)
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
|
||||||
builder := h.store.NewEntryQueryBuilder(user.ID)
|
builder := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||||
builder.WithStarred()
|
builder.WithStarred()
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
builder.WithLimit(user.EntriesPerPage)
|
builder.WithLimit(user.EntriesPerPage)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
|
||||||
offset := request.QueryIntParam(r, "offset", 0)
|
offset := request.QueryIntParam(r, "offset", 0)
|
||||||
builder := h.store.NewEntryQueryBuilder(user.ID)
|
builder := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithCategoryID(category.ID)
|
builder.WithCategoryID(category.ID)
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithStatus(model.EntryStatusUnread)
|
builder.WithStatus(model.EntryStatusUnread)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
|
||||||
offset := request.QueryIntParam(r, "offset", 0)
|
offset := request.QueryIntParam(r, "offset", 0)
|
||||||
builder := h.store.NewEntryQueryBuilder(user.ID)
|
builder := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithCategoryID(category.ID)
|
builder.WithCategoryID(category.ID)
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
|
||||||
builder := h.store.NewEntryQueryBuilder(user.ID)
|
builder := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithFeedID(feed.ID)
|
builder.WithFeedID(feed.ID)
|
||||||
builder.WithStatus(model.EntryStatusUnread)
|
builder.WithStatus(model.EntryStatusUnread)
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
builder.WithLimit(user.EntriesPerPage)
|
builder.WithLimit(user.EntriesPerPage)
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
|
||||||
builder := h.store.NewEntryQueryBuilder(user.ID)
|
builder := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithFeedID(feed.ID)
|
builder.WithFeedID(feed.ID)
|
||||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
builder.WithLimit(user.EntriesPerPage)
|
builder.WithLimit(user.EntriesPerPage)
|
||||||
|
|
|
@ -21,6 +21,7 @@ type SettingsForm struct {
|
||||||
Language string
|
Language string
|
||||||
Timezone string
|
Timezone string
|
||||||
EntryDirection string
|
EntryDirection string
|
||||||
|
EntryOrder string
|
||||||
EntriesPerPage int
|
EntriesPerPage int
|
||||||
KeyboardShortcuts bool
|
KeyboardShortcuts bool
|
||||||
ShowReadingTime bool
|
ShowReadingTime bool
|
||||||
|
@ -36,6 +37,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
|
||||||
user.Language = s.Language
|
user.Language = s.Language
|
||||||
user.Timezone = s.Timezone
|
user.Timezone = s.Timezone
|
||||||
user.EntryDirection = s.EntryDirection
|
user.EntryDirection = s.EntryDirection
|
||||||
|
user.EntryOrder = s.EntryOrder
|
||||||
user.EntriesPerPage = s.EntriesPerPage
|
user.EntriesPerPage = s.EntriesPerPage
|
||||||
user.KeyboardShortcuts = s.KeyboardShortcuts
|
user.KeyboardShortcuts = s.KeyboardShortcuts
|
||||||
user.ShowReadingTime = s.ShowReadingTime
|
user.ShowReadingTime = s.ShowReadingTime
|
||||||
|
@ -84,6 +86,7 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
|
||||||
Language: r.FormValue("language"),
|
Language: r.FormValue("language"),
|
||||||
Timezone: r.FormValue("timezone"),
|
Timezone: r.FormValue("timezone"),
|
||||||
EntryDirection: r.FormValue("entry_direction"),
|
EntryDirection: r.FormValue("entry_direction"),
|
||||||
|
EntryOrder: r.FormValue("entry_order"),
|
||||||
EntriesPerPage: int(entriesPerPage),
|
EntriesPerPage: int(entriesPerPage),
|
||||||
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
|
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
|
||||||
ShowReadingTime: r.FormValue("show_reading_time") == "1",
|
ShowReadingTime: r.FormValue("show_reading_time") == "1",
|
||||||
|
|
|
@ -32,6 +32,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
|
||||||
Language: user.Language,
|
Language: user.Language,
|
||||||
Timezone: user.Timezone,
|
Timezone: user.Timezone,
|
||||||
EntryDirection: user.EntryDirection,
|
EntryDirection: user.EntryDirection,
|
||||||
|
EntryOrder: user.EntryOrder,
|
||||||
EntriesPerPage: user.EntriesPerPage,
|
EntriesPerPage: user.EntriesPerPage,
|
||||||
KeyboardShortcuts: user.KeyboardShortcuts,
|
KeyboardShortcuts: user.KeyboardShortcuts,
|
||||||
ShowReadingTime: user.ShowReadingTime,
|
ShowReadingTime: user.ShowReadingTime,
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"miniflux.app/http/request"
|
"miniflux.app/http/request"
|
||||||
"miniflux.app/http/response/html"
|
"miniflux.app/http/response/html"
|
||||||
"miniflux.app/model"
|
|
||||||
"miniflux.app/ui/session"
|
"miniflux.app/ui/session"
|
||||||
"miniflux.app/ui/view"
|
"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 := h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithShareCodeNotEmpty()
|
builder.WithShareCodeNotEmpty()
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
|
|
||||||
entries, err := builder.GetEntries()
|
entries, err := builder.GetEntries()
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
|
||||||
m = timing.NewMetric("sql_fetch_unread_entries").Start()
|
m = timing.NewMetric("sql_fetch_unread_entries").Start()
|
||||||
builder = h.store.NewEntryQueryBuilder(user.ID)
|
builder = h.store.NewEntryQueryBuilder(user.ID)
|
||||||
builder.WithStatus(model.EntryStatusUnread)
|
builder.WithStatus(model.EntryStatusUnread)
|
||||||
builder.WithOrder(model.DefaultSortingOrder)
|
builder.WithOrder(user.EntryOrder)
|
||||||
builder.WithDirection(user.EntryDirection)
|
builder.WithDirection(user.EntryDirection)
|
||||||
builder.WithOffset(offset)
|
builder.WithOffset(offset)
|
||||||
builder.WithLimit(user.EntriesPerPage)
|
builder.WithLimit(user.EntriesPerPage)
|
||||||
|
|
Loading…
Reference in a new issue