mirror of
https://github.com/miniflux/v2.git
synced 2025-06-27 16:36:00 +00:00
feat(googlereader): add mark-all-as-read
endpoint
This commit is contained in:
parent
e8c3435bb9
commit
50395f13ca
2 changed files with 112 additions and 4 deletions
|
@ -95,6 +95,8 @@ const (
|
|||
ParamDestination = "dest"
|
||||
// ParamContinuation - name of the parameter for callers to pass to receive the next page of results
|
||||
ParamContinuation = "c"
|
||||
// ParamStreamType - name of the parameter for unix timestamp
|
||||
ParamTimestamp = "ts"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -234,6 +236,7 @@ func Serve(router *mux.Router, store *storage.Storage) {
|
|||
sr.HandleFunc("/subscription/quickadd", handler.quickAddHandler).Methods(http.MethodPost).Name("QuickAdd")
|
||||
sr.HandleFunc("/stream/items/ids", handler.streamItemIDsHandler).Methods(http.MethodGet).Name("StreamItemIDs")
|
||||
sr.HandleFunc("/stream/items/contents", handler.streamItemContentsHandler).Methods(http.MethodPost).Name("StreamItemsContents")
|
||||
sr.HandleFunc("/mark-all-as-read", handler.markAllAsReadHandler).Methods(http.MethodPost).Name("MarkAllAsRead")
|
||||
sr.PathPrefix("/").HandlerFunc(handler.serveHandler).Methods(http.MethodPost, http.MethodGet).Name("GoogleReaderApiEndpoint")
|
||||
}
|
||||
|
||||
|
@ -324,12 +327,12 @@ func checkAndSimplifyTags(addTags []Stream, removeTags []Stream) (map[StreamType
|
|||
switch s.Type {
|
||||
case ReadStream:
|
||||
if _, ok := tags[KeptUnreadStream]; ok {
|
||||
return nil, fmt.Errorf("googlereader: %s ad %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
return nil, fmt.Errorf("googlereader: %s and %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
}
|
||||
tags[ReadStream] = true
|
||||
case KeptUnreadStream:
|
||||
if _, ok := tags[ReadStream]; ok {
|
||||
return nil, fmt.Errorf("googlereader: %s ad %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
return nil, fmt.Errorf("googlereader: %s and %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
}
|
||||
tags[ReadStream] = false
|
||||
case StarredStream:
|
||||
|
@ -344,12 +347,12 @@ func checkAndSimplifyTags(addTags []Stream, removeTags []Stream) (map[StreamType
|
|||
switch s.Type {
|
||||
case ReadStream:
|
||||
if _, ok := tags[ReadStream]; ok {
|
||||
return nil, fmt.Errorf("googlereader: %s ad %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
return nil, fmt.Errorf("googlereader: %s and %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
}
|
||||
tags[ReadStream] = false
|
||||
case KeptUnreadStream:
|
||||
if _, ok := tags[ReadStream]; ok {
|
||||
return nil, fmt.Errorf("googlereader: %s ad %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
return nil, fmt.Errorf("googlereader: %s and %s should not be supplied simultaneously", KeptUnread, Read)
|
||||
}
|
||||
tags[ReadStream] = true
|
||||
case StarredStream:
|
||||
|
@ -1551,3 +1554,82 @@ func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request
|
|||
|
||||
json.OK(w, r, streamIDResponse{itemRefs, continuation})
|
||||
}
|
||||
|
||||
func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
clientIP := request.ClientIP(r)
|
||||
|
||||
slog.Debug("[GoogleReader] Handle /mark-all-as-read",
|
||||
slog.String("handler", "markAllAsReadHandler"),
|
||||
slog.String("client_ip", clientIP),
|
||||
slog.String("user_agent", r.UserAgent()),
|
||||
)
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
json.BadRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
stream, err := getStream(r.Form.Get(ParamStreamID), userID)
|
||||
if err != nil {
|
||||
json.BadRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
var before time.Time
|
||||
if timestampParamValue := r.Form.Get(ParamTimestamp); timestampParamValue != "" {
|
||||
timestampParsedValue, err := strconv.ParseInt(timestampParamValue, 10, 64)
|
||||
if err != nil {
|
||||
json.BadRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if timestampParsedValue > 0 {
|
||||
// It's unclear if the timestamp is in seconds or microseconds, so we try both using a naive approach.
|
||||
if len(timestampParamValue) >= 16 {
|
||||
before = time.UnixMicro(timestampParsedValue)
|
||||
} else {
|
||||
before = time.Unix(timestampParsedValue, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if before.IsZero() {
|
||||
before = time.Now()
|
||||
}
|
||||
|
||||
switch stream.Type {
|
||||
case FeedStream:
|
||||
feedID, err := strconv.ParseInt(stream.ID, 10, 64)
|
||||
if err != nil {
|
||||
json.BadRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
err = h.store.MarkFeedAsRead(userID, feedID, before)
|
||||
if err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
case LabelStream:
|
||||
category, err := h.store.CategoryByTitle(userID, stream.ID)
|
||||
if err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
if category == nil {
|
||||
json.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if err := h.store.MarkCategoryAsRead(userID, category.ID, before); err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
case ReadingListStream:
|
||||
if err = h.store.MarkAllAsReadBeforeDate(userID, before); err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
OK(w, r)
|
||||
}
|
||||
|
|
|
@ -476,6 +476,30 @@ func (s *Storage) MarkAllAsRead(userID int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MarkAllAsReadBeforeDate updates all user entries to the read status before the given date.
|
||||
func (s *Storage) MarkAllAsReadBeforeDate(userID int64, before time.Time) error {
|
||||
query := `
|
||||
UPDATE
|
||||
entries
|
||||
SET
|
||||
status=$1,
|
||||
changed_at=now()
|
||||
WHERE
|
||||
user_id=$2 AND status=$3 AND published_at < $4
|
||||
`
|
||||
result, err := s.db.Exec(query, model.EntryStatusRead, userID, model.EntryStatusUnread, before)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`store: unable to mark all entries as read before %s: %v`, before.Format(time.RFC3339), err)
|
||||
}
|
||||
count, _ := result.RowsAffected()
|
||||
slog.Debug("Marked all entries as read before date",
|
||||
slog.Int64("user_id", userID),
|
||||
slog.Int64("nb_entries", count),
|
||||
slog.String("before", before.Format(time.RFC3339)),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkGloballyVisibleFeedsAsRead updates all user entries to the read status.
|
||||
func (s *Storage) MarkGloballyVisibleFeedsAsRead(userID int64) error {
|
||||
query := `
|
||||
|
@ -527,6 +551,7 @@ func (s *Storage) MarkFeedAsRead(userID, feedID int64, before time.Time) error {
|
|||
slog.Int64("user_id", userID),
|
||||
slog.Int64("feed_id", feedID),
|
||||
slog.Int64("nb_entries", count),
|
||||
slog.String("before", before.Format(time.RFC3339)),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
@ -563,6 +588,7 @@ func (s *Storage) MarkCategoryAsRead(userID, categoryID int64, before time.Time)
|
|||
slog.Int64("user_id", userID),
|
||||
slog.Int64("category_id", categoryID),
|
||||
slog.Int64("nb_entries", count),
|
||||
slog.String("before", before.Format(time.RFC3339)),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue