Skip to content
18 changes: 13 additions & 5 deletions TODO.org
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
* TODO
** Features
- [ ] search
- [ ] upvote/downvote posts
- [ ] upvote/downvote comments
- [X] upvote/downvote posts
- [X] upvote/downvote comments
- [ ] upvote/downvote current post in dank-mode-comments
- [ ] browse url of current post in dank-mode-comments
- [ ] reply comments
- [ ] mouse interaction
- [ ] open post comments
- [ ] open post link
- [ ] upvote/downvote post
- [ ] upvote/downvote comment
- [ ] fold comment
- [ ] search
** Enhancements
** Bug fixes
- [ ] Switching subreddits in dank-mode-comments mode is not available

* Ideas

- Use plstore.el for oauth tokens
- Use web-server.el to do the oauth dance
- [X] Use plstore.el for oauth tokens
- [X] Use web-server.el to do the oauth dance
http://eschulte.github.io/emacs-web-server

* Known bugs
Expand Down
9 changes: 8 additions & 1 deletion lisp/dank-mode-backend.el
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

;;; Commentary:

;; This file defines functions for making requests to Reddit.
;; This file defines wrapper functions for making requests to Reddit.

;;; Code:

Expand Down Expand Up @@ -151,6 +151,13 @@ REQUEST-PARAMS is plist of request parameters that Reddit's 'subreddits' API tak
(resp (dank-mode-backend-request "GET" url params)))
(plist-get (plist-get resp :data) :children)))

(defun dank-mode-backend-vote (thing-id direction)
"Cast a vote on THING-ID with DIRECTION (1, 0 or -1)."
(let* ((url "/api/vote")
(resp (dank-mode-backend-request "POST" url `((id . ,thing-id) (dir . ,direction)))))
resp))


(provide 'dank-mode-backend)

;;; dank-mode-backend.el ends here
27 changes: 23 additions & 4 deletions lisp/dank-mode-comment.el
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,30 @@
(require 'dank-mode-backend)
(require 'dank-mode-utils)
(require 'dank-mode-post)
(require 'dank-mode-faces)

(defcustom dank-mode-comment-upvote-symbol "🡅"
"Symbol to use for upvotes. Change this if the emoji doesn't work correctly."
:type 'string
:group 'dank-mode)

(defcustom dank-mode-comment-downvote-symbol "🡇"
"Symbol to use for downvotes. Change this if the emoji doesn't work correctly."
:type 'string
:group 'dank-mode)

(cl-defstruct dank-mode-comment
name id body edited text age date author subreddit score
author_flair gilded replies depth parent_id post_author_p
type more_count children_ids)
type more_count children_ids likes)

(defcustom dank-mode-comments-body-fill-width 120
"Fill width for rendering the comment body."
:type 'integer
:group 'dank-mode)

(defvar dank-mode-comment-metadata-template
"- ${author} (${score} points | ${age})${edited} ")
"- ${author} (${vote}${score} points | ${age})${edited} ")

(defvar dank-mode-comment-more-template
"+ [${count} more comments]")
Expand Down Expand Up @@ -72,7 +83,8 @@ list of children, also parsed."
:author_flair (plist-get comment :author_flair_text)
:gilded (plist-get comment :gilded)
:parent_id (plist-get comment :parent_id)
:post_author_p (string-equal (plist-get comment :author) post-author))))
:post_author_p (string-equal (plist-get comment :author) post-author)
:likes (plist-get comment :likes))))
(if (stringp replies)
parsed-comment
`(,parsed-comment . (,(mapcar (lambda (c) (dank-mode-comment-parse c post-author)) children))))))))
Expand All @@ -85,11 +97,18 @@ formatting/indentation will depend on its position."
(let* ((author-face (if (dank-mode-comment-post_author_p comment) 'dank-mode-faces-comment-author-op 'dank-mode-faces-comment-author))
(author (concat "/u/" (dank-mode-comment-author comment)))
(score (number-to-string (dank-mode-comment-score comment)))
(likes (dank-mode-comment-likes comment))
(vote (format "%s" (cond ((eq :false likes) dank-mode-comment-downvote-symbol)
(likes dank-mode-comment-upvote-symbol) ; likes is t if upvoted
(t ""))))
(vote-face (format "%s" (cond ((eq :false likes) 'dank-mode-faces-downvote)
(likes 'dank-mode-faces-upvote) ; likes is t if upvoted
(t 'dank-mode-faces-comment-metadata))))
(age (dank-mode-comment-age comment))
(edited (or nil ""))
(gilded (number-to-string (dank-mode-comment-gilded comment)))
(depth (dank-mode-comment-depth comment))
(format-context `(author (,author . ,author-face) age ,age score ,score edited ,edited gilded ,gilded depth ,depth))
(format-context `(author (,author . ,author-face) age ,age vote (,vote . ,vote-face) score (,score . ,vote-face) edited ,edited gilded ,gilded depth ,depth))
(formatted-metadata (dank-mode-utils-format-plist dank-mode-comment-metadata-template format-context 'dank-mode-faces-comment-metadata))
(formatted-metadata (concat (make-string (* 2 depth) ?\s) formatted-metadata)))
formatted-metadata))
Expand Down
25 changes: 25 additions & 0 deletions lisp/dank-mode-comments.el
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
(define-key map (kbd "C-x l o") 'dank-mode-comments-browse-post-link)
(define-key map (kbd "C-x l o") 'dank-mode-comments-browse-post-comments)
(define-key map (kbd "TAB") 'dank-mode-comments-toggle-comment-tree-fold-at-point)
(define-key map (kbd "C-x v u") (lambda (point) (interactive "d") (dank-mode-comments-vote-comment-at-point point 1)))
(define-key map (kbd "C-x v d") (lambda (point) (interactive "d") (dank-mode-comments-vote-comment-at-point point -1)))
(define-key map (kbd "C-x q") 'dank-mode-comments-kill-current-buffer)
map))

Expand Down Expand Up @@ -438,5 +440,28 @@ If EWW is non-nil, browse in eww instead of the browser."
(let ((browse-url-browser-function (if eww 'eww-browse-url 'browse-url-default-browser)))
(browse-url (concat (dank-mode-utils-reddit-url) dank-mode-comments-current-permalink))))

(defun dank-mode-comments-vote-comment-at-point (pos direction)
"Vote the comment at POS with DIRECTION.
Sets the comment's `likes' field appropriately."
(interactive "d")
(let* ((ewoc-node (ewoc-locate dank-mode-comments-current-ewoc pos))
(comment-data (dank-mode-utils-ewoc-data dank-mode-comments-current-ewoc pos))
(comment-id (dank-mode-comment-id comment-data))
(comment-current-likes (dank-mode-comment-likes comment-data))
(comment-current-score (dank-mode-comment-score comment-data))
; negate direction (set to 0) if comment is already voted towards the same direction
(dir (cond ((and (eq :false comment-current-likes) (= -1 direction)) 0)
((and comment-current-likes (= 1 direction)) 0)
(t direction)))
(new-likes (cond ((= -1 dir) :false)
((= 1 dir) t)
((= 0 dir) nil)
(t nil))))
(dank-mode-backend-vote (concat "t1_" comment-id) dir)
(setf (dank-mode-comment-likes comment-data) new-likes)
(setf (dank-mode-comment-score comment-data) (+ comment-current-score dir))
(ewoc-set-data ewoc-node comment-data)
(ewoc-invalidate dank-mode-comments-current-ewoc ewoc-node)))

(provide 'dank-mode-comments)
;;; dank-mode-comments.el ends here
9 changes: 7 additions & 2 deletions lisp/dank-mode-faces.el
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@
:group 'dank-mode-faces)

(defface dank-mode-faces-upvote
`((t :foreground ,dank-mode-faces-color-upvote))
`((t :foreground ,dank-mode-faces-color-upvote :weight bold))
"Face for upvotes."
:group 'dank-mode-faces)

(defface dank-mode-faces-downvote
`((t :foreground ,dank-mode-faces-color-downvote))
`((t :foreground ,dank-mode-faces-color-downvote :weight bold))
"Face for downvotes."
:group 'dank-mode-faces)

(defface dank-mode-faces-score
`((t :foreground ,dank-mode-faces-color-upvote))
"Face for score points."
:group 'dank-mode-faces)

(defface dank-mode-faces-post-title
'((t :inherit default :weight bold))
"Face for post titles."
Expand Down
2 changes: 1 addition & 1 deletion lisp/dank-mode-oauth.el
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ to be refreshed."
(defvar dank-mode-oauth-redirect-server nil)
(defvar dank-mode-oauth-auth-url "https://www.reddit.com/api/v1/authorize")
(defvar dank-mode-oauth-token-url "https://www.reddit.com/api/v1/access_token")
(defvar dank-mode-oauth-scope "identity history mysubreddits read wikiread")
(defvar dank-mode-oauth-scope "identity history mysubreddits read wikiread vote edit save submit subscribe report")
(defvar dank-mode-oauth-redirect-url (concat "http://localhost:" (number-to-string dank-mode-oauth-redirect-port) dank-mode-oauth-redirect-path))

(defun dank-mode-oauth--random-string (n)
Expand Down
24 changes: 21 additions & 3 deletions lisp/dank-mode-post.el
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@
(require 'dank-mode-utils)
(require 'dank-mode-faces)

(defcustom dank-mode-post-upvote-symbol "🡅"
"Symbol to use for upvotes. Change this if the emoji doesn't work correctly."
:type 'string
:group 'dank-mode)

(defcustom dank-mode-post-downvote-symbol "🡇"
"Symbol to use for downvotes. Change this if the emoji doesn't work correctly."
:type 'string
:group 'dank-mode)

(cl-defstruct dank-mode-post
"Struct for a Reddit post."
name id title link permalink text age date author subreddit score num_comments
domain post_type nsfw spoiler link_flair author_flair
gilded stickied locked)
gilded stickied locked likes)

(cl-defstruct dank-subreddit
"Struct for a subreddit."
Expand All @@ -29,7 +39,7 @@
;; Rendering

(defvar dank-mode-post-template
"${title}\n | ${score} points | ${num_comments} comments | ${link_flair}${nsfw}${spoiler}${post_type}${domain}\n | ${subreddit} submitted by ${author}${author_flair} ${age}")
"${title}\n | ${vote}${score} points | ${num_comments} comments | ${link_flair}${nsfw}${spoiler}${post_type}${domain}\n | ${subreddit} submitted by ${author}${author_flair} ${age}")

(defun dank-mode-post-format (post)
"Format POST as string using `dank-mode-post-template'.
Expand All @@ -42,6 +52,13 @@ Also applies font-lock properties."
""))
(subreddit (concat "/r/" (dank-mode-post-subreddit post)))
(score (format "%s" (dank-mode-post-score post)))
(likes (dank-mode-post-likes post))
(vote (format "%s" (cond ((eq :false likes) dank-mode-post-downvote-symbol)
(likes dank-mode-post-upvote-symbol) ; likes is t if upvoted
(t ""))))
(vote-face (format "%s" (cond ((eq :false likes) 'dank-mode-faces-downvote)
(likes 'dank-mode-faces-upvote) ; likes is t if upvoted
(t 'dank-mode-faces-upvote))))
(num_comments (format "%s" (dank-mode-post-num_comments post)))
(nsfw (if (dank-mode-post-nsfw post) "NSFW " ""))
(spoiler (if (dank-mode-post-spoiler post) "SPOILERS " ""))
Expand All @@ -51,7 +68,7 @@ Also applies font-lock properties."
(link_flair (if (dank-mode-post-link_flair post)
(concat "[" (dank-mode-post-link_flair post) "] ") ""))
(format-context `(title (,title . dank-mode-faces-post-title) age (,age . dank-mode-faces-age) author (,author . dank-mode-faces-post-author) author_flair (,author_flair . dank-mode-faces-flair)
subreddit (,subreddit . dank-mode-faces-subreddit) score (,score . dank-mode-faces-upvote) num_comments (,num_comments . dank-mode-faces-downvote)
subreddit (,subreddit . dank-mode-faces-subreddit) vote (,vote . ,vote-face) score (,score . dank-mode-faces-upvote) num_comments (,num_comments . dank-mode-faces-downvote)
nsfw (,nsfw . dank-mode-faces-nsfw) spoiler (,spoiler . dank-mode-faces-nsfw) domain (,domain . dank-mode-faces-site-domain) post_type (,post_type . dank-mode-faces-post-type)
link_flair (,link_flair . dank-mode-faces-flair)))
(formatted-post (dank-mode-utils-format-plist dank-mode-post-template format-context)))
Expand All @@ -67,6 +84,7 @@ Also applies font-lock properties."
:text (plist-get post :selftext)
:age (dank-mode-utils-timestamp-ago (plist-get post :created_utc))
:date (plist-get post :created_utc)
:likes (plist-get post :likes)
:score (plist-get post :score)
:author (plist-get post :author)
:subreddit (plist-get post :subreddit)
Expand Down
40 changes: 29 additions & 11 deletions lisp/dank-mode-posts.el
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ When nil, defaults to the frontpage."
(define-key map (kbd "C-x l l") (lambda (point) (interactive "d") (dank-mode-posts-browse-post-link-at-point point t)))
(define-key map (kbd "C-x l b") 'dank-mode-posts-browse-post-link-at-point)
(define-key map (kbd "C-x l o") 'dank-mode-posts-browse-post-comments-at-point)
(define-key map (kbd "C-x v u") (lambda (point) (interactive "d") (dank-mode-posts-vote-post-at-point point 1)))
(define-key map (kbd "C-x v d") (lambda (point) (interactive "d") (dank-mode-posts-vote-post-at-point point -1)))
(define-key map (kbd "C-x q") 'kill-current-buffer)
map))

Expand Down Expand Up @@ -164,17 +166,6 @@ REFRESH-EWOC creates a new ewoc."
"Populate the EWOC with POSTS."
(mapc (lambda (p) (ewoc-enter-last ewoc p)) posts))

(defun dank-mode-posts-append-post-to-buffer (buf post-index post)
"Append POST into BUF.
POST-INDEX is the number (\"position\") of the post."
(when (buffer-live-p buf)
(with-current-buffer buf
(let* ((inhibit-read-only t)
(formatted-post (concat (dank-mode-post-format post post-index) "\n")))
(save-excursion
(goto-char (point-max))
(insert formatted-post))))))

(defun dank-mode-posts-render-error (err)
"Render contents of ERR into the current buffer."
(let ((inhibit-read-only t))
Expand Down Expand Up @@ -338,6 +329,33 @@ If EWW is non-nil, browse in eww instead of the browser."
(browse-url-browser-function (if eww 'eww-browse-url 'browse-url-default-browser)))
(browse-url (concat (dank-mode-utils-reddit-url) post-permalink))))

(defun dank-mode-posts-vote-post-at-point (pos direction)
"Vote the post at POS with DIRECTION.
Sets the post's `likes' field appropriately."
(interactive "d")
(let* ((ewoc-node (ewoc-locate dank-mode-posts-current-ewoc pos))
(post-data (dank-mode-utils-ewoc-data dank-mode-posts-current-ewoc pos))
(post-id (dank-mode-post-id post-data))
(post-current-likes (dank-mode-post-likes post-data))
(post-current-score (dank-mode-post-score post-data))
; negate direction (set to 0) if post is already voted towards the same direction
(dir (cond ((and (eq :false post-current-likes) (= -1 direction)) 0)
((and post-current-likes (= 1 direction)) 0)
(t direction)))
(new-likes (cond ((= -1 dir) :false)
((= 1 dir) t)
((= 0 dir) nil)
(t nil))))
(dank-mode-backend-vote (concat "t3_" post-id) dir)
(setf (dank-mode-post-likes post-data) new-likes)
(setf (dank-mode-post-score post-data) (+ post-current-score dir))
(ewoc-set-data ewoc-node post-data)
(ewoc-invalidate dank-mode-posts-current-ewoc ewoc-node)))

(defun dank-mode-posts--print-ewoc-data-at-point (pos)
(interactive "d")
(message "%s" (dank-mode-utils-ewoc-data dank-mode-posts-current-ewoc pos)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; interaction functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down