elfeed icon indicating copy to clipboard operation
elfeed copied to clipboard

User Interface suggestions

Open malcook opened this issue 8 years ago • 10 comments

Digging elfeed. Coming from feedly (via Web client and the superior (in most ways) GReader app on Android), previously "Google Reader" (sniff)).

Below are a few personal prefs you might appreciate:

I think elfeed would be better to display date as the last column, and for it to be expressed as aging, in relative time, (i.e. 2d for 2 days ago, 3h for 3 hours ago, etc).

When I read/scan feeds/titles I want know when the feed I am reading before I know the Title of the entry. For this reason, I would prefer to see the feed title before the article Title. In fact, I don't need to see it repeating (at least if sorted by title). My eyes dance right and left reading the current layout. My preferred layout, which GREADER provides, is to have the feed source appear once only, in a different face than the items for all the article from the feed. It takes a little more vertical space, but makes scanning MUCH faster.

Also, I would by default not display the unread tag

So, in summary, my ideal layout would be something like:

Feed1 info .................................................... ......... # unread items | # read items | # tagged .. title1 ................................................................ 2d
.. title2 ................................................................ 3d Feed2 info ............................................................... # unread items | # read items | # tagged .. title3 ................................................................ 1h kittenz .. title4 ................................................................ 1d kittenz

etc

I think this layout allows improved top-down left->right scanning.

If having sections or reorganizing columns like this is infeasible , simply suppressing repeated feed names would reduce much visual clutter and allow the eyes to dance much less.

Thanks for elfeed. Much appreciated!

malcook avatar Jun 14 '17 15:06 malcook

Thanks for sharing your ideas. However, I prefer the listing the way it currently works. You can get some control over the listing with elfeed-search-print-entry-function, but this only works on a line by line basis and won't allow you to group.

Fortunately you could realistically write your own custom front-end to Elfeed. In fact, elfeed-search has nothing special going for it other than being launched with "M-x elfeed". It's practically a separate package. It uses hooks to get notifications about database changes, and you could do exactly the same thing with your own front-end. The filter stuff from elfeed-search should be re-usable, too.

skeeto avatar Jun 20 '17 18:06 skeeto

@malcook You could customize this function, which inserts separators where the date changes:

(cl-defun ap/elfeed-search-add-separators (&key (min-group-size 2))
    "Insert overlay spacers where the current date changes.
If no group has at least MIN-GROUP-SIZE items, no spacers will be
inserted. "
    ;; TODO: Use column-specific functions so that, e.g. date column could be grouped by month/year
    (cl-labels ((count-date-items (date)
                                  (cl-loop for entry in elfeed-search-entries
                                           when (equal date (elfeed-search-format-date (elfeed-entry-date entry)))
                                           count it))
                (insert-date (date &key count)
                             (ov (line-beginning-position) (line-beginning-position)
                                 'before-string (propertize (format "\n%s (%s)\n" date count)
                                                            'face 'elfeed-search-date-face)
                                 'type 'date-separator))
                (entry-date (offset)
                      (when-let ((entry (nth offset elfeed-search-entries)))
                        (elfeed-search-format-date (elfeed-entry-date entry)))))
      (ov-clear)
      (save-excursion
        (goto-char (point-min))
        (cl-loop with largest-group-size = 1
                 with offset = (- 1 elfeed-search--offset) ; 1 is first line
                 with prev-data = (entry-date offset)

                 initially do (insert-date prev-data
                                           :count (count-date-items prev-data))

                 while (not (eobp))
                 do (progn
                      (forward-line 1)
                      (incf offset))

                 for current-data = (entry-date offset)
                 if (not (equal current-data prev-data))
                 do (progn
                      (insert-date current-data
                                   :count (count-date-items current-data))
                      (setq prev-data current-data))
                 else do (incf largest-group-size)

                 finally do (when (< largest-group-size min-group-size)
                              (ov-clear))))))

fun

alphapapa avatar Nov 29 '17 11:11 alphapapa

@alphapapa I really like your setup. Could you be more explicit about how you get elfeed to look like this? How do you add your function to elfeed?

timmli avatar Dec 19 '17 18:12 timmli

@timmli I use elfeed-goodies, so I replace the drawing function like this:

(advice-add #'elfeed-goodies/entry-line-draw :override #'ap/elfeed-goodies/entry-line-draw)

alphapapa avatar Dec 20 '17 02:12 alphapapa

@alphapapa Sorry, I still don't get it. Where does ap/elfeed-goodies/entry-line-draw come from?

timmli avatar Dec 22 '17 16:12 timmli

Oh, that's in my config:

(defun ap/elfeed-goodies/entry-line-draw (entry)
  "My version of this function.  Prints ENTRY to the buffer."

  (cl-flet ((add-faces (str &rest faces)
                       (dolist (face faces str)
                         (add-face-text-property 0 (length str)
                                                 face 'append str)))
            (tags (entry)
                  (seq-difference (--map (substring-no-properties (symbol-name it))
                                         (elfeed-entry-tags entry))
                                  '("unread" "starred"))))
    (let* ( ;; Choose color and faces first
           ;; See https://www.reddit.com/r/emacs/comments/7a976a/face_applied_to_result_of_symbolname_becomes/
           (site (pocket-reader--url-domain (elfeed-entry-link entry)))
           (hash (rainbow-identifiers--hash-function site))
           (site-face (rainbow-identifiers-cie-l*a*b*-choose-face hash))
           (title-faces (elfeed-search--faces (elfeed-entry-tags entry)))

           ;; Feed
           (feed (elfeed-entry-feed entry))
           (feed-title (when feed
                         (or (elfeed-meta feed :title) (elfeed-feed-title feed))))
           (feed-width elfeed-goodies/feed-source-column-width)
           (feed-column (elfeed-format-column feed-title feed-width :left))
           (feed-column (apply #'add-faces feed-column site-face title-faces))

           ;; Tags before title (so title can use the width of the tags column for this item)
           (tags (tags entry))
           (tags-str (s-join "," tags))
           ;; Use raw tag list to check for starred
           (starred-p (member 'starred (elfeed-entry-tags entry)))
           (tags-width (min (length tags-str)
                            elfeed-goodies/tag-column-width))
           (tag-column (elfeed-format-column tags-str tags-width :right))
           (tag-column (apply #'add-faces tag-column site-face title-faces))

           ;; Title
           (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
           (title-width (- (window-width) feed-width tags-width 4))
           (title-column (elfeed-format-column (truncate-string-to-width title title-width nil nil 'ellipsis) title-width :left))
           (title-column (apply #'add-faces title-column site-face title-faces)))

      (insert feed-column " ")
      (insert (if starred-p
                  (propertize "*"
                              'face 'pocket-reader-favorite-star)
                " ")
              " ")
      (insert (propertize title-column 'kbd-help title) " ")
      (insert tag-column))))

alphapapa avatar Dec 23 '17 03:12 alphapapa

@alphapapa Thanks! One more question: How am I supposed to include your function ap/elfeed-search-add-separators there?

timmli avatar Dec 29 '17 22:12 timmli

Just add it to elfeed-search-update-hook, like:

(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)

alphapapa avatar Dec 30 '17 18:12 alphapapa

Just add it to elfeed-search-update-hook, like:

(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)

hy i stumbled upon this and try to use it but its not working as shown in screenshot only first date is shown and after that all feeed come under same date

i have this in doom emacs config

(after! elfeed-goodies

(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)
(cl-defun ap/elfeed-search-add-separators (&key (min-group-size 2))
    "Insert overlay spacers where the current date changes.
If no group has at least MIN-GROUP-SIZE items, no spacers will be
inserted. "
    ;; TODO: Use column-specific functions so that, e.g. date column could be grouped by month/year
    (cl-labels ((count-date-items (date)
                                  (cl-loop for entry in elfeed-search-entries
                                           when (equal date (elfeed-search-format-date (elfeed-entry-date entry)))
                                           count it))
                (insert-date (date &key count)
                             (ov (line-beginning-position) (line-beginning-position)
                                 'before-string (propertize (format "\n%s (%s)\n" date count)
                                                            'face 'elfeed-search-date-face)
                                 'type 'date-separator))
                (entry-date (offset)
                      (when-let ((entry (nth offset elfeed-search-entries)))
                        (elfeed-search-format-date (elfeed-entry-date entry)))))
      (ov-clear)
      (save-excursion
        (goto-char (point-min))
        (cl-loop with largest-group-size = 1
                 with offset = (- 1 elfeed-search--offset) ; 1 is first line
                 with prev-data = (entry-date offset)

                 initially do (insert-date prev-data
                                           :count (count-date-items prev-data))

                 while (not (eobp))
                 do (progn
                      (forward-line 1)
                      (incf offset))

                 for current-data = (entry-date offset)
                 if (not (equal current-data prev-data))
                 do (progn
                      (insert-date current-data
                                   :count (count-date-items current-data))
                      (setq prev-data current-data))
                 else do (incf largest-group-size)

                 finally do (when (< largest-group-size min-group-size)
                              (ov-clear))))))
)

Aneeqasif avatar Feb 12 '24 09:02 Aneeqasif

i found the issue i just have to use cl-incf instead of incf

Aneeqasif avatar Feb 12 '24 15:02 Aneeqasif