mirror of
https://github.com/miniflux/v2.git
synced 2025-08-01 17:38:37 +00:00
feat: mark media as read when playback reaches 90%
This commit is contained in:
parent
37309adbc0
commit
4f55361f5f
37 changed files with 278 additions and 76 deletions
|
@ -937,4 +937,9 @@ var migrations = []func(tx *sql.Tx) error{
|
|||
_, err = tx.Exec(sql)
|
||||
return err
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
sql := `ALTER TABLE users ADD COLUMN mark_read_on_media_player_completion bool default 'f';`
|
||||
_, err = tx.Exec(sql)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Standard-Startseite",
|
||||
"form.prefs.label.categories_sorting_order": "Kategorie-Sortierung",
|
||||
"form.prefs.label.mark_read_on_view": "Einträge automatisch als gelesen markieren, wenn sie angezeigt werden",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Anwendungseinstellungen",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentifizierungseinstellungen",
|
||||
"form.prefs.fieldset.reader_settings": "Reader-Einstellungen",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Προεπιλεγμένη αρχική σελίδα",
|
||||
"form.prefs.label.categories_sorting_order": "Ταξινόμηση κατηγοριών",
|
||||
"form.prefs.label.mark_read_on_view": "Αυτόματη επισήμανση καταχωρήσεων ως αναγνωσμένων κατά την προβολή",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Default home page",
|
||||
"form.prefs.label.categories_sorting_order": "Categories sorting",
|
||||
"form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Página de inicio por defecto",
|
||||
"form.prefs.label.categories_sorting_order": "Clasificación por categorías",
|
||||
"form.prefs.label.mark_read_on_view": "Marcar automáticamente las entradas como leídas cuando se vean",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Oletusarvoinen etusivu",
|
||||
"form.prefs.label.categories_sorting_order": "Kategorioiden lajittelu",
|
||||
"form.prefs.label.mark_read_on_view": "Merkitse kohdat automaattisesti luetuiksi, kun niitä tarkastellaan",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Page d'accueil par défaut",
|
||||
"form.prefs.label.categories_sorting_order": "Colonne de tri des catégories",
|
||||
"form.prefs.label.mark_read_on_view": "Marquer automatiquement les entrées comme lues lorsqu'elles sont consultées",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Marquer automatiquement les entrées comme lues lorsqu'elles sont consultées. Pour l'audio/vidéo, marquer comme lues après 90%%",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Marqué les entrées comme lues uniquement après 90%% de lecture de l'audio/vidéo",
|
||||
"form.prefs.label.mark_read_manually": "Marqué les entrées comme lues manuellement",
|
||||
"form.prefs.fieldset.application_settings": "Paramètres de l'application",
|
||||
"form.prefs.fieldset.authentication_settings": "Paramètres d'authentification",
|
||||
"form.prefs.fieldset.reader_settings": "Paramètres du lecteur",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "डिफ़ॉल्ट होमपेज़",
|
||||
"form.prefs.label.categories_sorting_order": "श्रेणियाँ छँटाई",
|
||||
"form.prefs.label.mark_read_on_view": "देखे जाने पर स्वचालित रूप से प्रविष्टियों को पढ़ने के रूप में चिह्नित करें",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -384,6 +384,9 @@
|
|||
"form.prefs.label.default_home_page": "Beranda Baku",
|
||||
"form.prefs.label.categories_sorting_order": "Pengurutan Kategori",
|
||||
"form.prefs.label.mark_read_on_view": "Secara otomatis menandai entri sebagai telah dibaca saat dilihat",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Pagina iniziale predefinita",
|
||||
"form.prefs.label.categories_sorting_order": "Ordinamento delle categorie",
|
||||
"form.prefs.label.mark_read_on_view": "Contrassegna automaticamente le voci come lette quando visualizzate",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -384,6 +384,9 @@
|
|||
"form.prefs.label.default_home_page": "デフォルトのトップページ",
|
||||
"form.prefs.label.categories_sorting_order": "カテゴリの表示順",
|
||||
"form.prefs.label.mark_read_on_view": "表示時にエントリを自動的に既読としてマークします",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Standaard startpagina",
|
||||
"form.prefs.label.categories_sorting_order": "Categorieën sorteren",
|
||||
"form.prefs.label.mark_read_on_view": "Items automatisch markeren als gelezen wanneer ze worden bekeken",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -404,6 +404,9 @@
|
|||
"form.prefs.label.default_home_page": "Domyślna strona główna",
|
||||
"form.prefs.label.categories_sorting_order": "Sortowanie kategorii",
|
||||
"form.prefs.label.mark_read_on_view": "Automatycznie oznaczaj wpisy jako przeczytane podczas przeglądania",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
"form.prefs.label.default_home_page": "Página inicial predefinida",
|
||||
"form.prefs.label.categories_sorting_order": "Classificação das categorias",
|
||||
"form.prefs.label.mark_read_on_view": "Marcar automaticamente as entradas como lidas quando visualizadas",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -404,6 +404,9 @@
|
|||
"form.prefs.label.default_home_page": "Домашняя страница по умолчанию",
|
||||
"form.prefs.label.categories_sorting_order": "Сортировка категорий",
|
||||
"form.prefs.label.mark_read_on_view": "Автоматически отмечать записи как прочитанные при просмотре",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -304,6 +304,9 @@
|
|||
"form.prefs.label.keyboard_shortcuts": "Klavye kısayollarını etkinleştir",
|
||||
"form.prefs.label.language": "Dil",
|
||||
"form.prefs.label.mark_read_on_view": "Makaleler görüntülendiğinde otomatik olarak okundu olarak işaretle",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.label.media_playback_rate": "Ses/video oynatma hızı",
|
||||
"form.prefs.label.show_reading_time": "Makaleler için tahmini okuma süresini göster",
|
||||
"form.prefs.label.theme": "Tema",
|
||||
|
|
|
@ -404,6 +404,9 @@
|
|||
"form.prefs.label.default_home_page": "Домашня сторінка за умовчанням",
|
||||
"form.prefs.label.categories_sorting_order": "Сортування за категоріями",
|
||||
"form.prefs.label.mark_read_on_view": "Автоматично позначати записи як прочитані під час перегляду",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "Application Settings",
|
||||
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
|
||||
"form.prefs.fieldset.reader_settings": "Reader Settings",
|
||||
|
|
|
@ -384,6 +384,9 @@
|
|||
"form.prefs.label.default_home_page": "默认主页",
|
||||
"form.prefs.label.categories_sorting_order": "分类排序",
|
||||
"form.prefs.label.mark_read_on_view": "查看时自动将条目标记为已读",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "应用设置",
|
||||
"form.prefs.fieldset.authentication_settings": "用户认证设置",
|
||||
"form.prefs.fieldset.reader_settings": "阅读器设置",
|
||||
|
|
|
@ -384,6 +384,9 @@
|
|||
"form.prefs.label.default_home_page": "預設主頁",
|
||||
"form.prefs.label.categories_sorting_order": "分類排序",
|
||||
"form.prefs.label.mark_read_on_view": "查看時自動將條目標記為已讀",
|
||||
"form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion",
|
||||
"form.prefs.label.mark_read_on_media_completion": "Only mark as read when audio/video playback reaches 90%% completion",
|
||||
"form.prefs.label.mark_read_manually": "Mark entries as read manually",
|
||||
"form.prefs.fieldset.application_settings": "應用程式設定",
|
||||
"form.prefs.fieldset.authentication_settings": "使用者認證設定",
|
||||
"form.prefs.fieldset.reader_settings": "閱讀器設定",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package model // import "miniflux.app/v2/internal/model"
|
||||
import "strings"
|
||||
|
||||
// Enclosure represents an attachment.
|
||||
type Enclosure struct {
|
||||
|
@ -24,3 +25,12 @@ func (e Enclosure) Html5MimeType() string {
|
|||
|
||||
// EnclosureList represents a list of attachments.
|
||||
type EnclosureList []*Enclosure
|
||||
|
||||
func (el EnclosureList) ContainsAudioOrVideo() bool {
|
||||
for _, enclosure := range el {
|
||||
if strings.Contains(enclosure.MimeType, "audio/") || strings.Contains(enclosure.MimeType, "video/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -50,6 +50,22 @@ func NewEntry() *Entry {
|
|||
}
|
||||
}
|
||||
|
||||
// ShouldMarkAsReadOnView Return whether the entry should be marked as viewed considering all user settings and entry state.
|
||||
func (e *Entry) ShouldMarkAsReadOnView(user *User) bool {
|
||||
// Already read, no need to mark as read again. Removed entries are not marked as read
|
||||
if e.Status != EntryStatusUnread {
|
||||
return false
|
||||
}
|
||||
|
||||
// There is an enclosure, markAsRead will happen at enclosure completion time, no need to mark as read on view
|
||||
if user.MarkReadOnMediaPlayerCompletion && e.Enclosures.ContainsAudioOrVideo() {
|
||||
return false
|
||||
}
|
||||
|
||||
// The user wants to mark as read on view
|
||||
return user.MarkReadOnView
|
||||
}
|
||||
|
||||
// Entries represents a list of entries.
|
||||
type Entries []*Entry
|
||||
|
||||
|
|
|
@ -11,33 +11,34 @@ import (
|
|||
|
||||
// User represents a user in the system.
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"-"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
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"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
GestureNav string `json:"gesture_nav"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage string `json:"default_home_page"`
|
||||
CategoriesSortingOrder string `json:"categories_sorting_order"`
|
||||
MarkReadOnView bool `json:"mark_read_on_view"`
|
||||
MediaPlaybackRate float64 `json:"media_playback_rate"`
|
||||
BlockFilterEntryRules string `json:"block_filter_entry_rules"`
|
||||
KeepFilterEntryRules string `json:"keep_filter_entry_rules"`
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"-"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
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"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
GestureNav string `json:"gesture_nav"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage string `json:"default_home_page"`
|
||||
CategoriesSortingOrder string `json:"categories_sorting_order"`
|
||||
MarkReadOnView bool `json:"mark_read_on_view"`
|
||||
MarkReadOnMediaPlayerCompletion bool `json:"mark_read_on_media_player_completion"`
|
||||
MediaPlaybackRate float64 `json:"media_playback_rate"`
|
||||
BlockFilterEntryRules string `json:"block_filter_entry_rules"`
|
||||
KeepFilterEntryRules string `json:"keep_filter_entry_rules"`
|
||||
}
|
||||
|
||||
// UserCreationRequest represents the request to create a user.
|
||||
|
@ -51,31 +52,32 @@ type UserCreationRequest struct {
|
|||
|
||||
// UserModificationRequest represents the request to update a user.
|
||||
type UserModificationRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Theme *string `json:"theme"`
|
||||
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"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
GestureNav *string `json:"gesture_nav"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage *string `json:"default_home_page"`
|
||||
CategoriesSortingOrder *string `json:"categories_sorting_order"`
|
||||
MarkReadOnView *bool `json:"mark_read_on_view"`
|
||||
MediaPlaybackRate *float64 `json:"media_playback_rate"`
|
||||
BlockFilterEntryRules *string `json:"block_filter_entry_rules"`
|
||||
KeepFilterEntryRules *string `json:"keep_filter_entry_rules"`
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Theme *string `json:"theme"`
|
||||
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"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
GestureNav *string `json:"gesture_nav"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage *string `json:"default_home_page"`
|
||||
CategoriesSortingOrder *string `json:"categories_sorting_order"`
|
||||
MarkReadOnView *bool `json:"mark_read_on_view"`
|
||||
MarkReadOnMediaPlayerCompletion *bool `json:"mark_read_on_media_player_completion"`
|
||||
MediaPlaybackRate *float64 `json:"media_playback_rate"`
|
||||
BlockFilterEntryRules *string `json:"block_filter_entry_rules"`
|
||||
KeepFilterEntryRules *string `json:"keep_filter_entry_rules"`
|
||||
}
|
||||
|
||||
// Patch updates the User object with the modification request.
|
||||
|
@ -168,6 +170,10 @@ func (u *UserModificationRequest) Patch(user *User) {
|
|||
user.MarkReadOnView = *u.MarkReadOnView
|
||||
}
|
||||
|
||||
if u.MarkReadOnMediaPlayerCompletion != nil {
|
||||
user.MarkReadOnMediaPlayerCompletion = *u.MarkReadOnMediaPlayerCompletion
|
||||
}
|
||||
|
||||
if u.MediaPlaybackRate != nil {
|
||||
user.MediaPlaybackRate = *u.MediaPlaybackRate
|
||||
}
|
||||
|
|
|
@ -193,11 +193,12 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
|||
default_home_page=$20,
|
||||
categories_sorting_order=$21,
|
||||
mark_read_on_view=$22,
|
||||
media_playback_rate=$23,
|
||||
block_filter_entry_rules=$24,
|
||||
keep_filter_entry_rules=$25
|
||||
mark_read_on_media_player_completion=$23,
|
||||
media_playback_rate=$24,
|
||||
block_filter_entry_rules=$25,
|
||||
keep_filter_entry_rules=$26
|
||||
WHERE
|
||||
id=$26
|
||||
id=$27
|
||||
`
|
||||
|
||||
_, err = s.db.Exec(
|
||||
|
@ -224,6 +225,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
|||
user.DefaultHomePage,
|
||||
user.CategoriesSortingOrder,
|
||||
user.MarkReadOnView,
|
||||
user.MarkReadOnMediaPlayerCompletion,
|
||||
user.MediaPlaybackRate,
|
||||
user.BlockFilterEntryRules,
|
||||
user.KeepFilterEntryRules,
|
||||
|
@ -256,11 +258,12 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
|||
default_home_page=$19,
|
||||
categories_sorting_order=$20,
|
||||
mark_read_on_view=$21,
|
||||
media_playback_rate=$22,
|
||||
block_filter_entry_rules=$23,
|
||||
keep_filter_entry_rules=$24
|
||||
mark_read_on_media_player_completion=$22,
|
||||
media_playback_rate=$23,
|
||||
block_filter_entry_rules=$24,
|
||||
keep_filter_entry_rules=$25
|
||||
WHERE
|
||||
id=$25
|
||||
id=$26
|
||||
`
|
||||
|
||||
_, err := s.db.Exec(
|
||||
|
@ -286,6 +289,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
|||
user.DefaultHomePage,
|
||||
user.CategoriesSortingOrder,
|
||||
user.MarkReadOnView,
|
||||
user.MarkReadOnMediaPlayerCompletion,
|
||||
user.MediaPlaybackRate,
|
||||
user.BlockFilterEntryRules,
|
||||
user.KeepFilterEntryRules,
|
||||
|
@ -337,6 +341,7 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
|
|||
default_home_page,
|
||||
categories_sorting_order,
|
||||
mark_read_on_view,
|
||||
mark_read_on_media_player_completion,
|
||||
media_playback_rate,
|
||||
block_filter_entry_rules,
|
||||
keep_filter_entry_rules
|
||||
|
@ -375,6 +380,7 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
|
|||
default_home_page,
|
||||
categories_sorting_order,
|
||||
mark_read_on_view,
|
||||
mark_read_on_media_player_completion,
|
||||
media_playback_rate,
|
||||
block_filter_entry_rules,
|
||||
keep_filter_entry_rules
|
||||
|
@ -413,6 +419,7 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) {
|
|||
default_home_page,
|
||||
categories_sorting_order,
|
||||
mark_read_on_view,
|
||||
mark_read_on_media_player_completion,
|
||||
media_playback_rate,
|
||||
block_filter_entry_rules,
|
||||
keep_filter_entry_rules
|
||||
|
@ -458,6 +465,7 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) {
|
|||
u.default_home_page,
|
||||
u.categories_sorting_order,
|
||||
u.mark_read_on_view,
|
||||
u.mark_read_on_media_player_completion,
|
||||
media_playback_rate,
|
||||
u.block_filter_entry_rules,
|
||||
u.keep_filter_entry_rules
|
||||
|
@ -497,6 +505,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
|
|||
&user.DefaultHomePage,
|
||||
&user.CategoriesSortingOrder,
|
||||
&user.MarkReadOnView,
|
||||
&user.MarkReadOnMediaPlayerCompletion,
|
||||
&user.MediaPlaybackRate,
|
||||
&user.BlockFilterEntryRules,
|
||||
&user.KeepFilterEntryRules,
|
||||
|
@ -608,6 +617,7 @@ func (s *Storage) Users() (model.Users, error) {
|
|||
default_home_page,
|
||||
categories_sorting_order,
|
||||
mark_read_on_view,
|
||||
mark_read_on_media_player_completion,
|
||||
media_playback_rate,
|
||||
block_filter_entry_rules,
|
||||
keep_filter_entry_rules
|
||||
|
@ -648,6 +658,7 @@ func (s *Storage) Users() (model.Users, error) {
|
|||
&user.DefaultHomePage,
|
||||
&user.CategoriesSortingOrder,
|
||||
&user.MarkReadOnView,
|
||||
&user.MarkReadOnMediaPlayerCompletion,
|
||||
&user.MediaPlaybackRate,
|
||||
&user.BlockFilterEntryRules,
|
||||
&user.KeepFilterEntryRules,
|
||||
|
|
|
@ -173,6 +173,9 @@
|
|||
<audio controls preload="metadata"
|
||||
data-last-position="{{ .MediaProgression }}"
|
||||
{{ if $.user.MediaPlaybackRate }}data-playback-rate="{{ $.user.MediaPlaybackRate }}"{{ end }}
|
||||
{{ if $.user.MarkReadOnMediaPlayerCompletion }}
|
||||
data-mark-read-on-completion="0.9"
|
||||
{{ end }}
|
||||
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
|
||||
data-enclosure-id="{{.ID}}"
|
||||
>
|
||||
|
@ -189,6 +192,9 @@
|
|||
<video controls preload="metadata"
|
||||
data-last-position="{{ .MediaProgression }}"
|
||||
{{ if $.user.MediaPlaybackRate }}data-playback-rate="{{ $.user.MediaPlaybackRate }}"{{ end }}
|
||||
{{ if $.user.MarkReadOnMediaPlayerCompletion }}
|
||||
data-mark-read-on-completion="0.9"
|
||||
{{ end }}
|
||||
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
|
||||
data-enclosure-id="{{.ID}}"
|
||||
>
|
||||
|
@ -221,6 +227,9 @@
|
|||
<audio controls preload="metadata"
|
||||
data-last-position="{{ .MediaProgression }}"
|
||||
{{ if $.user.MediaPlaybackRate }}data-playback-rate="{{ $.user.MediaPlaybackRate }}"{{ end }}
|
||||
{{ if $.user.MarkReadOnMediaPlayerCompletion }}
|
||||
data-mark-read-on-completion="0.9"
|
||||
{{ end }}
|
||||
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
|
||||
data-enclosure-id="{{.ID}}"
|
||||
>
|
||||
|
@ -237,6 +246,9 @@
|
|||
<video controls preload="metadata"
|
||||
data-last-position="{{ .MediaProgression }}"
|
||||
{{ if $.user.MediaPlaybackRate }}data-playback-rate="{{ $.user.MediaPlaybackRate }}"{{ end }}
|
||||
{{ if $.user.MarkReadOnMediaPlayerCompletion }}
|
||||
data-mark-read-on-completion="0.9"
|
||||
{{ end }}
|
||||
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
|
||||
data-enclosure-id="{{.ID}}"
|
||||
>
|
||||
|
|
|
@ -113,7 +113,14 @@
|
|||
|
||||
<label><input type="checkbox" name="show_reading_time" value="1" {{ if .form.ShowReadingTime }}checked{{ end }}> {{ t "form.prefs.label.show_reading_time" }}</label>
|
||||
|
||||
<label><input type="checkbox" name="mark_read_on_view" value="1" {{ if .form.MarkReadOnView }}checked{{ end }}> {{ t "form.prefs.label.mark_read_on_view" }}</label>
|
||||
<label><input type="radio" name="mark_read_behavior" value="{{ .const.NoAutoMarkAsRead }}"
|
||||
{{ if eq .form.MarkReadBehavior .const.NoAutoMarkAsRead }}checked{{end}} > {{ t "form.prefs.label.mark_read_manually" }}</label>
|
||||
<label><input type="radio" name="mark_read_behavior" value="{{ .const.MarkAsReadOnView }}"
|
||||
{{ if eq .form.MarkReadBehavior .const.MarkAsReadOnView }}checked{{end}} > {{ t "form.prefs.label.mark_read_on_view" }}</label>
|
||||
<label><input type="radio" name="mark_read_behavior" value="{{ .const.MarkAsReadOnViewButWaitForPlayerCompletion }}"
|
||||
{{ if eq .form.MarkReadBehavior .const.MarkAsReadOnViewButWaitForPlayerCompletion }}checked{{end}}> {{ t "form.prefs.label.mark_read_on_view_or_media_completion" }}</label>
|
||||
<label><input type="radio" name="mark_read_behavior" value="{{ .const.MarkAsReadOnlyOnPlayerCompletion }}"
|
||||
{{ if eq .form.MarkReadBehavior .const.MarkAsReadOnlyOnPlayerCompletion }}checked{{end}} > {{ t "form.prefs.label.mark_read_on_media_completion" }}</label>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
|
||||
|
|
|
@ -38,7 +38,7 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -41,7 +41,7 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -41,7 +41,7 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -40,7 +40,7 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -46,7 +46,7 @@ func (h *handler) showTagEntryPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -66,7 +66,7 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
|
|||
prevEntryRoute = route.Path(h.router, "unreadEntry", "entryID", prevEntry.ID)
|
||||
}
|
||||
|
||||
if user.MarkReadOnView {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
entry.Status = model.EntryStatusRead
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,16 @@ import (
|
|||
"miniflux.app/v2/internal/model"
|
||||
)
|
||||
|
||||
// MarkReadBehavior list all possible behaviors for automatically marking an entry as read
|
||||
type MarkReadBehavior string
|
||||
|
||||
var (
|
||||
NoAutoMarkAsRead MarkReadBehavior = "no-auto"
|
||||
MarkAsReadOnView MarkReadBehavior = "on-view"
|
||||
MarkAsReadOnViewButWaitForPlayerCompletion MarkReadBehavior = "on-view-but-wait-for-player-completion"
|
||||
MarkAsReadOnlyOnPlayerCompletion MarkReadBehavior = "on-player-completion"
|
||||
)
|
||||
|
||||
// SettingsForm represents the settings form.
|
||||
type SettingsForm struct {
|
||||
Username string
|
||||
|
@ -33,9 +43,45 @@ type SettingsForm struct {
|
|||
DefaultHomePage string
|
||||
CategoriesSortingOrder string
|
||||
MarkReadOnView bool
|
||||
MediaPlaybackRate float64
|
||||
BlockFilterEntryRules string
|
||||
KeepFilterEntryRules string
|
||||
// MarkReadBehavior is a string representation of the MarkReadOnView and MarkReadOnMediaPlayerCompletion fields together
|
||||
MarkReadBehavior MarkReadBehavior
|
||||
MediaPlaybackRate float64
|
||||
BlockFilterEntryRules string
|
||||
KeepFilterEntryRules string
|
||||
}
|
||||
|
||||
// MarkAsReadBehavior returns the MarkReadBehavior from the given MarkReadOnView and MarkReadOnMediaPlayerCompletion values.
|
||||
// Useful to convert the values from the User model to the form
|
||||
func MarkAsReadBehavior(markReadOnView, markReadOnMediaPlayerCompletion bool) MarkReadBehavior {
|
||||
switch {
|
||||
case markReadOnView && !markReadOnMediaPlayerCompletion:
|
||||
return MarkAsReadOnView
|
||||
case markReadOnView && markReadOnMediaPlayerCompletion:
|
||||
return MarkAsReadOnViewButWaitForPlayerCompletion
|
||||
case !markReadOnView && markReadOnMediaPlayerCompletion:
|
||||
return MarkAsReadOnlyOnPlayerCompletion
|
||||
case !markReadOnView && !markReadOnMediaPlayerCompletion:
|
||||
fallthrough // Explicit defaulting
|
||||
default:
|
||||
return NoAutoMarkAsRead
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractMarkAsReadBehavior returns the MarkReadOnView and MarkReadOnMediaPlayerCompletion values from the given MarkReadBehavior.
|
||||
// Useful to extract the values from the form to the User model
|
||||
func ExtractMarkAsReadBehavior(behavior MarkReadBehavior) (markReadOnView, markReadOnMediaPlayerCompletion bool) {
|
||||
switch behavior {
|
||||
case MarkAsReadOnView:
|
||||
return true, false
|
||||
case MarkAsReadOnViewButWaitForPlayerCompletion:
|
||||
return true, true
|
||||
case MarkAsReadOnlyOnPlayerCompletion:
|
||||
return false, true
|
||||
case NoAutoMarkAsRead:
|
||||
fallthrough // Explicit defaulting
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
// Merge updates the fields of the given user.
|
||||
|
@ -57,11 +103,14 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
|
|||
user.DefaultReadingSpeed = s.DefaultReadingSpeed
|
||||
user.DefaultHomePage = s.DefaultHomePage
|
||||
user.CategoriesSortingOrder = s.CategoriesSortingOrder
|
||||
user.MarkReadOnView = s.MarkReadOnView
|
||||
user.MediaPlaybackRate = s.MediaPlaybackRate
|
||||
user.BlockFilterEntryRules = s.BlockFilterEntryRules
|
||||
user.KeepFilterEntryRules = s.KeepFilterEntryRules
|
||||
|
||||
MarkReadOnView, MarkReadOnMediaPlayerCompletion := ExtractMarkAsReadBehavior(s.MarkReadBehavior)
|
||||
user.MarkReadOnView = MarkReadOnView
|
||||
user.MarkReadOnMediaPlayerCompletion = MarkReadOnMediaPlayerCompletion
|
||||
|
||||
if s.Password != "" {
|
||||
user.Password = s.Password
|
||||
}
|
||||
|
@ -136,6 +185,7 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
|
|||
DefaultHomePage: r.FormValue("default_home_page"),
|
||||
CategoriesSortingOrder: r.FormValue("categories_sorting_order"),
|
||||
MarkReadOnView: r.FormValue("mark_read_on_view") == "1",
|
||||
MarkReadBehavior: MarkReadBehavior(r.FormValue("mark_read_behavior")),
|
||||
MediaPlaybackRate: mediaPlaybackRate,
|
||||
BlockFilterEntryRules: r.FormValue("block_filter_entry_rules"),
|
||||
KeepFilterEntryRules: r.FormValue("keep_filter_entry_rules"),
|
||||
|
|
|
@ -40,7 +40,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
|
|||
CJKReadingSpeed: user.CJKReadingSpeed,
|
||||
DefaultHomePage: user.DefaultHomePage,
|
||||
CategoriesSortingOrder: user.CategoriesSortingOrder,
|
||||
MarkReadOnView: user.MarkReadOnView,
|
||||
MarkReadBehavior: form.MarkAsReadBehavior(user.MarkReadOnView, user.MarkReadOnMediaPlayerCompletion),
|
||||
MediaPlaybackRate: user.MediaPlaybackRate,
|
||||
BlockFilterEntryRules: user.BlockFilterEntryRules,
|
||||
KeepFilterEntryRules: user.KeepFilterEntryRules,
|
||||
|
@ -61,6 +61,13 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
|
|||
sess := session.New(h.store, request.SessionID(r))
|
||||
view := view.New(h.tpl, r, sess)
|
||||
view.Set("form", settingsForm)
|
||||
// In order to keep the continuity between form and model, I pass adhoc constants to the view
|
||||
view.Set("const", map[string]interface{}{
|
||||
"NoAutoMarkAsRead": form.NoAutoMarkAsRead,
|
||||
"MarkAsReadOnView": form.MarkAsReadOnView,
|
||||
"MarkAsReadOnViewButWaitForPlayerCompletion": form.MarkAsReadOnViewButWaitForPlayerCompletion,
|
||||
"MarkAsReadOnlyOnPlayerCompletion": form.MarkAsReadOnlyOnPlayerCompletion,
|
||||
})
|
||||
view.Set("themes", model.Themes())
|
||||
view.Set("languages", locale.AvailableLanguages())
|
||||
view.Set("timezones", timezones)
|
||||
|
|
|
@ -678,9 +678,13 @@ function goToAddSubscription() {
|
|||
* save player position to allow to resume playback later
|
||||
* @param {Element} playerElement
|
||||
*/
|
||||
function handlePlayerProgressionSave(playerElement) {
|
||||
function handlePlayerProgressionSaveAndMarkAsReadOnCompletion(playerElement) {
|
||||
if (!isPlayerPlaying(playerElement)) {
|
||||
return; //If the player is not playing, we do not want to save the progression and mark as read on completion
|
||||
}
|
||||
const currentPositionInSeconds = Math.floor(playerElement.currentTime); // we do not need a precise value
|
||||
const lastKnownPositionInSeconds = parseInt(playerElement.dataset.lastPosition, 10);
|
||||
const markAsReadOnCompletion = parseFloat(playerElement.dataset.markReadOnCompletion); //completion percentage to mark as read
|
||||
const recordInterval = 10;
|
||||
|
||||
// we limit the number of update to only one by interval. Otherwise, we would have multiple update per seconds
|
||||
|
@ -691,9 +695,29 @@ function handlePlayerProgressionSave(playerElement) {
|
|||
const request = new RequestBuilder(playerElement.dataset.saveUrl);
|
||||
request.withBody({ progression: currentPositionInSeconds });
|
||||
request.execute();
|
||||
// Handle the mark as read on completion
|
||||
if (markAsReadOnCompletion >= 0 && playerElement.duration > 0) {
|
||||
const completion = currentPositionInSeconds / playerElement.duration;
|
||||
if (completion >= markAsReadOnCompletion) {
|
||||
handleEntryStatus("none", document.querySelector(":is(a, button)[data-toggle-status]"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player is actually playing a media
|
||||
* @param element the player element itself
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isPlayerPlaying(element) {
|
||||
return element &&
|
||||
element.currentTime > 0 &&
|
||||
!element.paused &&
|
||||
!element.ended &&
|
||||
element.readyState > 2; //https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
|
||||
}
|
||||
|
||||
/**
|
||||
* handle new share entires and already shared entries
|
||||
*/
|
||||
|
|
2
internal/ui/static/js/bootstrap.js
vendored
2
internal/ui/static/js/bootstrap.js
vendored
|
@ -159,7 +159,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
if (element.dataset.lastPosition) {
|
||||
element.currentTime = element.dataset.lastPosition;
|
||||
}
|
||||
element.ontimeupdate = () => handlePlayerProgressionSave(element);
|
||||
element.ontimeupdate = () => handlePlayerProgressionSaveAndMarkAsReadOnCompletion(element);
|
||||
});
|
||||
|
||||
// Set media playback rate
|
||||
|
|
|
@ -41,7 +41,7 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
|
@ -41,7 +41,7 @@ func (h *handler) showUnreadFeedEntryPage(w http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
if user.MarkReadOnView && entry.Status == model.EntryStatusUnread {
|
||||
if entry.ShouldMarkAsReadOnView(user) {
|
||||
err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
html.ServerError(w, r, err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue