From 35886f7c19ef00f7c86c2e63ca335ae13ae7aa81 Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Fri, 5 May 2017 22:19:02 +0100 Subject: Show users' avatars plus other image work. - Shows users' avatars (makes only sense if Emacs is built with imagemagick) - Scales media attachement previews to a max size (if Emacs is built with imagemagick) - Enable cacheing of image fetches Known issues: - We should really cache the avatars to avoid having multiple identical images in memory. --- lisp/mastodon-media.el | 91 ++++++++++++++++++++++++++++++-------------------- lisp/mastodon-tl.el | 30 ++++++++++++----- lisp/mastodon.el | 10 ++++++ 3 files changed, 86 insertions(+), 45 deletions(-) diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 23fbc79..93ff1b7 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -1,4 +1,4 @@ -;;; mastodon-media.el --- Functions for inlining Mastodon media +;;; mastodon-media.el --- Functions for inlining Mastodon media -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen @@ -32,66 +32,85 @@ ;;; Code: (require 'mastodon-http nil t) +(require 'mastodon) (defgroup mastodon-media nil "Inline Mastadon media." :prefix "mastodon-media-" :group 'mastodon) -(defun mastodon-media--image-from-url (url) - "Takes a URL and return an image." - (let ((buffer (url-retrieve-synchronously url))) +(defvar mastodon-media-show-avatars-p + (image-type-available-p 'imagemagick) + "A boolean value stating whether to show avatars in timelines.") + +(defun mastodon-media--image-from-url (url media-type) + "Takes a URL and MEDIA-TYPE and return an image. + +MEDIA-TYPE is a symbol and either 'avatar or 'media-link." + ;; TODO: Cache the avatars + (let* ((url-automatic-caching t) + (buffer (url-retrieve-synchronously url)) + (image-options (when (image-type-available-p 'imagemagick) + (case media-type + ('avatar `(:height ,mastodon-avatar-height)) + ('media-link `(:max-height ,mastodon-preview-max-height)))))) (unwind-protect (let ((data (with-current-buffer buffer (goto-char (point-min)) (search-forward "\n\n") (buffer-substring (point) (point-max))))) - (insert "\n") - (insert-image (create-image data nil t))) + (apply #'create-image data (when image-options 'imagemagick) + t image-options)) (kill-buffer buffer)))) (defun mastodon-media--select-next-media-line () - "Find coordinates of a line that contains `Media_Links::' - -Returns the cons of (`start' . `end') points of that line or nil no -more media links were found." - (let ((foundp (search-forward-regexp "Media_Link::" nil t))) - (when foundp - (let ((start (progn (move-beginning-of-line nil) (point))) - (end (progn (move-end-of-line nil) (point)))) - (cons start end))))) + "Find coordinates of the next media to load. + +Returns the list of (`start' . `end', `media-symbol') points of +that line and string found or nil no more media links were +found." + (let ((next-pos (point))) + (while (and (setq next-pos (next-single-property-change next-pos 'media-state)) + (or (not (eq 'needs-loading (get-text-property next-pos 'media-state))) + (null (get-text-property next-pos 'media-url)) + (null (get-text-property next-pos 'media-type)))) + ;; do nothing - the loop will proceed + ) + (when next-pos + (case (get-text-property next-pos 'media-type) + ;; Avatars are just one character in the buffer + ('avatar (list next-pos (+ next-pos 1) 'avatar)) + ;; Media links are 5 character ("[img]") + ('media-link (list next-pos (+ next-pos 5) 'media-link)))))) (defun mastodon-media--valid-link-p (link) "Checks to make sure that the missing string has not been returned." (let ((missing "/files/small/missing.png")) - (not (equal link missing)))) - -(defun mastodon-media--line-to-link (line-points) - "Returns the url of the media link given at the given point. - -`LINE-POINTS' is a cons of (`start' . `end') positions of the line with -the `Media_Link:: ' text." - (replace-regexp-in-string "Media_Link:: " "" - (buffer-substring - (car line-points) - (cdr line-points)))) - -(defun mastodon-media--delete-line (line) - "Deletes the current media line" - (delete-region (car line) (cdr line))) + (and link + (not (equal link missing))))) (defun mastodon-media--inline-images () "Find all `Media_Links:' in the buffer replacing them with the referenced image." (interactive) (goto-char (point-min)) - (let (line-coordinates) - (while (setq line-coordinates (mastodon-media--select-next-media-line)) - (let ((link (mastodon-media--line-to-link line-coordinates))) - (when (mastodon-media--valid-link-p link) - (mastodon-media--image-from-url link) - (mastodon-media--delete-line line-coordinates)))))) + (let (line-details) + (while (setq line-details (mastodon-media--select-next-media-line)) + (let* ((start (car line-details)) + (end (cadr line-details)) + (media-type (caddr line-details)) + (image-url (get-text-property start 'media-url))) + + (if (not (mastodon-media--valid-link-p image-url)) + (put-text-property start end 'media-state 'invalid-url) + (put-text-property start end 'media-state 'loading) + (let ((image (mastodon-media--image-from-url image-url media-type))) + (put-text-property start end 'media-state 'loaded) + (put-text-property start end + 'display (or + image + (format "Failed to load %s" image-url))))))))) (provide 'mastodon-media) ;;; mastodon-media.el ends here diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index e025a6e..1a5d9ae 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -104,8 +104,18 @@ Optionally start from POS." "Propertize author of TOOT." (let* ((account (cdr (assoc 'account toot))) (handle (cdr (assoc 'acct account))) - (name (cdr (assoc 'display_name account)))) + (name (cdr (assoc 'display_name account))) + (avatar-url (cdr (assoc 'avatar account)))) (concat + (when mastodon-media-show-avatars-p + ;; We use just an empty space as the textual representation. + ;; This is what a user will see on a non-graphical display + ;; where not showing an avatar at all is preferable. + (concat (propertize " " + 'media-url avatar-url + 'media-state 'needs-loading + 'media-type 'avatar) + " ")) (propertize name 'face 'warning) " (@" handle @@ -177,14 +187,16 @@ also render the html" (defun mastodon-tl--media (toot) "Retrieve a media attachment link for TOOT if one exists." - (let ((media (mastodon-tl--field 'media_attachments toot))) - (mapconcat - (lambda (media-preview) - (concat "Media_Link:: " - (mastodon-tl--set-face - (cdr (assoc 'preview_url media-preview)) - 'mouse-face nil))) - media "\n"))) + (let ((media-attachements (mastodon-tl--field 'media_attachments toot))) + (mapconcat + (lambda (media-attachement) + (let ((preview-url (cdr (assoc 'preview_url media-attachement)))) + (concat (propertize "[img]" + 'media-url preview-url + 'media-state 'needs-loading + 'media-type 'media-link) + " "))) + media-attachements ""))) (defun mastodon-tl--content (toot) "Retrieve text content from TOOT." diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 947cc6a..0dd7f10 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -60,6 +60,16 @@ Use. e.g. \"%c\" for your locale's date and time format." :group 'mastodon :type 'string) +(defcustom mastodon-avatar-height 30 + "Height of the user avatar images (if shown)." + :group 'mastodon + :type 'integer) + +(defcustom mastodon-preview-max-height 250 + "Max height of any media attachment preview to be shown." + :group 'mastodon + :type 'integer) + (defvar mastodon-mode-map (make-sparse-keymap) "Keymap for `mastodon-mode'.") -- cgit v1.2.3 From 0fc0d53dee2513b5923553531a8b6a9c5db10975 Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Fri, 5 May 2017 22:19:02 +0100 Subject: Make the image loading asynchronous. Now that we are also loading avatars there is a lot of image loading to do to show the timeline. We can do the loading asynchronously to let the user have a look at the toots already while image loading is incrementally proceeding. We can no longer enforce caching of avatar loading since the variable is consulted when the response parsing happens at which point the dynamic binding we had used so far has gone out of scope again. --- lisp/mastodon-media.el | 70 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 93ff1b7..289637e 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -43,25 +43,51 @@ (image-type-available-p 'imagemagick) "A boolean value stating whether to show avatars in timelines.") -(defun mastodon-media--image-from-url (url media-type) +(defun mastodon-media--process-image-response (status-plist marker image-options region-length image-url) + "Callback function processing the url retrieve response for URL. + +STATUS-PLIST is the usual plist of status events as per `url-retrieve'. +IMAGE-OPTIONS are the precomputed options to apply to the image. +MARKER is the marker to where the response should be visible. +REGION-LENGTH is the length of the region that should be replaced with the image. +IMAGE-URL is the URL that was retrieved. +" + (let ((url-buffer (current-buffer)) + (is-error-response-p (eq :error (car status-plist)))) + (unwind-protect + (let* ((data (unless is-error-response-p + (goto-char (point-min)) + (search-forward "\n\n") + (buffer-substring (point) (point-max)))) + (image (when data + (apply #'create-image data (when image-options 'imagemagick) + t image-options)))) + (switch-to-buffer (marker-buffer marker)) + ;; Save narrowing in our buffer + (let ((inhibit-read-only t)) + (save-restriction + (widen) + (put-text-property marker (+ marker region-length) 'media-state 'loaded) + (put-text-property marker (+ marker region-length) + 'display (or + image + (format "Failed to load %s" image-url))) + ;; We are done with the marker; release it: + (set-marker marker nil))) + (kill-buffer url-buffer))))) + +(defun mastodon-media--load-image-from-url (url media-type start region-length) "Takes a URL and MEDIA-TYPE and return an image. MEDIA-TYPE is a symbol and either 'avatar or 'media-link." - ;; TODO: Cache the avatars - (let* ((url-automatic-caching t) - (buffer (url-retrieve-synchronously url)) - (image-options (when (image-type-available-p 'imagemagick) - (case media-type - ('avatar `(:height ,mastodon-avatar-height)) - ('media-link `(:max-height ,mastodon-preview-max-height)))))) - (unwind-protect - (let ((data (with-current-buffer buffer - (goto-char (point-min)) - (search-forward "\n\n") - (buffer-substring (point) (point-max))))) - (apply #'create-image data (when image-options 'imagemagick) - t image-options)) - (kill-buffer buffer)))) + ;; TODO: Cache the avatars + (let ((image-options (when (image-type-available-p 'imagemagick) + (pcase media-type + ('avatar `(:height ,mastodon-avatar-height)) + ('media-link `(:max-height ,mastodon-preview-max-height)))))) + (url-retrieve url + #'mastodon-media--process-image-response + (list (copy-marker start) image-options region-length url)))) (defun mastodon-media--select-next-media-line () "Find coordinates of the next media to load. @@ -77,7 +103,7 @@ found." ;; do nothing - the loop will proceed ) (when next-pos - (case (get-text-property next-pos 'media-type) + (pcase (get-text-property next-pos 'media-type) ;; Avatars are just one character in the buffer ('avatar (list next-pos (+ next-pos 1) 'avatar)) ;; Media links are 5 character ("[img]") @@ -101,16 +127,12 @@ not been returned." (end (cadr line-details)) (media-type (caddr line-details)) (image-url (get-text-property start 'media-url))) - (if (not (mastodon-media--valid-link-p image-url)) + ;; mark it at least as not needing loading any more (put-text-property start end 'media-state 'invalid-url) + ;; proceed to load this image asynchronously (put-text-property start end 'media-state 'loading) - (let ((image (mastodon-media--image-from-url image-url media-type))) - (put-text-property start end 'media-state 'loaded) - (put-text-property start end - 'display (or - image - (format "Failed to load %s" image-url))))))))) + (mastodon-media--load-image-from-url image-url media-type start (- end start))))))) (provide 'mastodon-media) ;;; mastodon-media.el ends here -- cgit v1.2.3 From b20265eea37884bde663aa6d1d498c9180b89947 Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Mon, 8 May 2017 22:17:39 +0100 Subject: Move the rendering of images fully into mastodon-media.el and use default images. Having all the logic in one file reduces interdependencies. Having default images is more pleasing during the incremental loading. --- lisp/mastodon-media.el | 118 +++++++++++++++++++++++++++++++++++++++++++--- lisp/mastodon-tl.el | 15 +----- test/mastodon-tl-tests.el | 52 +++++++++++++++++--- 3 files changed, 160 insertions(+), 25 deletions(-) diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 289637e..734e11f 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -32,7 +32,6 @@ ;;; Code: (require 'mastodon-http nil t) -(require 'mastodon) (defgroup mastodon-media nil "Inline Mastadon media." @@ -43,6 +42,84 @@ (image-type-available-p 'imagemagick) "A boolean value stating whether to show avatars in timelines.") +(defvar mastodon-media--generic-avatar-data + (base64-decode-string + "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") + "The PNG data for a generic 100x100 avatar") + +(defvar mastodon-media--generic-broken-image-data + (base64-decode-string + "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA +B3RJTUUH4QUIFQUVFt+0LQAAABZ0RVh0Q29tbWVudABHZW5lcmljIGF2YXRhcsyCnMsAAAdoSURB +VHja7d1NSFRrAIfx//iB6ZDSMJYVkWEk0ceYFUkkhhQlEUhEg0FlC1eBoRTUwlbRok0TgRQURZAE +FgpjJmFajpK4kggxpXHRQEGWUJZizpy7uPfC5eKiV+dD5zw/mN05jrxnnjnfcxyWZVkCMKc0SXI4 +HIwEMIcUhgAgEIBAAAIBCAQgEIBAAAIBCAQgEAAEAhAIQCAAgQAEAhAIQCAAgQA2kBaNP8Jt7ViM +onErOWsQgEAAAgEIBCAQgEAAAgEIBCAQgEAAEAhAIACBAAQCEAhAIACBAAQCEAhAIAAIBCAQgEAA +AgEIBCAQgEAAAgEIBACBAAQCEAhAIACBAAQCEAhAIACBAAQCgEAAAgEIBCAQgECAxSyNIYitz58/ +a3BwUIODgxoZGVEoFFIoFNK3b980NTWlX79+SZIyMzOVlZWlVatWae3atSooKJDH49HOnTvl8XiU +ksJ3WSI4LMuyHA7Hgv6IZVmM5D8mJyf1/PlzdXZ2qrOzU8FgcMF/0+126+DBg6qqqlJFRYXS0vhe ++6MP9wI/1wQSJeFwWH6/X01NTWpra9PU1FTM3isvL0/nz5/XuXPntHz5ciqIcSCy/v50L+hlV+Pj +49a1a9esdevWLXgMTV8ul8u6c+eOFYlELMwtKmNNIOa+fv1qXbp0yXI6nXEP4/+v0tJS6+PHj9RA +IIk3PT1tXb161crOzk54GP995ebmWt3d3RRBIInj9/utgoKCRRXGf18ZGRmW3++niigHwk56PHf4 +Yiw9PV0dHR0qLy9nD52jWAQylxUrVmhgYEAbN24kkCgsM84+JZmJiQmdPn1akUiEweBE4eL/NsrN +zVVZWZlKSkpUWFioTZs2yeVyKTs7W7Ozs5qYmNDExITev3+v/v5+9fX1qb+/f8FjevPmTdXW1rIG +IZDFN9gbNmyQ1+uV1+uVx+MxXlAjIyNqbGzU3bt39fPnz3n9vytXrlQwGJTT6SQQThQm/ohIamqq +VVlZaXV1dUXtPT98+GCVlZXNe7n4fD6OYnGYN7GDnZ6ebtXU1FhjY2Mxed9IJGLV19fPa7kUFRUR +CIEkZrAdDod15syZmIXxf7W1tfNaNqOjowSygBdHseZh7969GhgY0IMHD5Sfnx+X97xx44Z2795t +PF93dzcLjMO88TvHcP/+ffX19WnXrl3xXVApKbp9+7bxfSFv3rxhwRFI7B07dkxDQ0Oqrq5O2P9Q +XFysffv2Gc0zOjrKwiOQ2Hv69Kny8vIS/n8cP37caPqxsTEWHoHYa//HxPfv3xk0ArGP1atXG03/ +7z3vIBBbyM3NNZo+KyuLQSMQ+5icnDSaPicnh0EjEPsYHh42mp7L3gnEVnp6eoymLyoqYtAIxD4e +PXpkNP3+/fsZtAXgcvclpL29XUeOHPnj6Z1Op8bHx7Vs2TJ7fri5o9A+ZmZmdPHiRaN5vF6vbeNg +E8tmGhoaNDQ0ZPTteeHCBQaOQJLfkydPdP36daN5Tp48qc2bNzN47IMkt9evX+vw4cOanp7+43ly +cnI0PDy8KK4dYx8EMRMIBHT06FGjOCTJ5/PZPg42sZJce3u7Dh06pB8/fhjNV11dndBL8tnEYhMr +5lpaWuT1evX792+j+YqLixUIBLj+ik2s5NXc3KwTJ04Yx5Gfn69nz54RB5tYyaupqUlVVVWanZ01 +ms/tdqujo4P9DgJJXg8fPtSpU6cUDoeN43j58qUKCwsZRAJJTvfu3dPZs2eNf0/X7Xarq6tL27dv +ZxAJJDn5fD7V1NQYx7FmzRq9evVK27ZtYxAJJDk1NDSorq7O+ChgQUGBent7tWXLFgYxxniecILU +1dXJ5/MZz7d161a9ePHC+N50sAZZMq5cuTKvOEpKStTT00McccSJwji7devWvJ7bceDAAbW2ttr6 +cQbGH26eD7K0BAIBlZeXG5/nqKioUEtLizIyMhhEAklOX758kcfj0adPn4zXHG1tbcSRoEDYB4mT +y5cvG8exZ88etba2Egf7IMnt7du32rFjh9G5jvz8fA0MDBj/UBxYgyw5jY2NRnGkpqaqubmZOBYB +AomxmZkZPX782Gie+vr6uD9/BGxiJURvb69KS0v/ePrMzEyFQiG5XC4Gj02s5BcIBIymr6ysJA42 +sezj3bt3RtObPv8DBLKkBYNBo+m5r4NAbCUUChlNv379egaNQOzD9FdJ2P8gEFsxfQQaFyMuLhzm +jfUAG45tOBw2fhY6ojP2rEGWwiqdONjEAggEIBCAQAACAUAgAIEA0cIPx8UYJ1FZgwAEAhAIAAIB +CAQgEIBAAAIBFiNOFMaY6V1tnFhkDQIQCEAgAIEABAKAQAACAQgEIBCAQAACAQgEIBCAQABIXO4e +c1y+zhoEIBCAQAAQCEAgAIEABAIQCEAgAIEABAIQCEAgAAgEIBCAQAACAQgEIBCAQAACAQgEAIEA +BAIQCEAgAIEABAIsJVH58WqHw8FIgjUIQCAACAQgEIBAAAIBCAQgEIBAAAIBCAQgEAAEAhAIQCBA +fKRJkmVZjAQwh78A6vCRWJE8K+8AAAAASUVORK5CYII=") + "The PNG data for a generic 200x200 'broken image' view") + (defun mastodon-media--process-image-response (status-plist marker image-options region-length image-url) "Callback function processing the url retrieve response for URL. @@ -68,16 +145,18 @@ IMAGE-URL is the URL that was retrieved. (save-restriction (widen) (put-text-property marker (+ marker region-length) 'media-state 'loaded) - (put-text-property marker (+ marker region-length) - 'display (or - image - (format "Failed to load %s" image-url))) + (when image + ;; We only set the image to display if we could load + ;; it; we already have set a default image when we + ;; added the tag. + (put-text-property marker (+ marker region-length) + 'display image)) ;; We are done with the marker; release it: (set-marker marker nil))) (kill-buffer url-buffer))))) (defun mastodon-media--load-image-from-url (url media-type start region-length) - "Takes a URL and MEDIA-TYPE and return an image. + "Takes a URL and MEDIA-TYPE and load the image asynchronously. MEDIA-TYPE is a symbol and either 'avatar or 'media-link." ;; TODO: Cache the avatars @@ -134,5 +213,32 @@ not been returned." (put-text-property start end 'media-state 'loading) (mastodon-media--load-image-from-url image-url media-type start (- end start))))))) +(defun mastodon-media--get-avatar-rendering (avatar-url) + "Returns the string to be written that renders the avatar at AVATAR-URL." + ;; We use just an empty space as the textual representation. + ;; This is what a user will see on a non-graphical display + ;; where not showing an avatar at all is preferable. + (let ((image-options (when (image-type-available-p 'imagemagick) + `(:height ,mastodon-avatar-height)))) + (concat + (propertize " " + 'media-url avatar-url + 'media-state 'needs-loading + 'media-type 'avatar + 'display (apply #'create-image mastodon-media--generic-avatar-data + (when image-options 'imagemagick) + t image-options)) + " "))) + +(defun mastodon-media--get-media-link-rendering (media-url) + "Returns the string to be written that renders the image at MEDIA-URL." + (concat + (propertize "[img]" + 'media-url media-url + 'media-state 'needs-loading + 'media-type 'media-link + 'display (create-image mastodon-media--generic-broken-image-data nil t)) + " ")) + (provide 'mastodon-media) ;;; mastodon-media.el ends here diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 1a5d9ae..b3550c6 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -108,14 +108,7 @@ Optionally start from POS." (avatar-url (cdr (assoc 'avatar account)))) (concat (when mastodon-media-show-avatars-p - ;; We use just an empty space as the textual representation. - ;; This is what a user will see on a non-graphical display - ;; where not showing an avatar at all is preferable. - (concat (propertize " " - 'media-url avatar-url - 'media-state 'needs-loading - 'media-type 'avatar) - " ")) + (mastodon-media--get-avatar-rendering avatar-url)) (propertize name 'face 'warning) " (@" handle @@ -191,11 +184,7 @@ also render the html" (mapconcat (lambda (media-attachement) (let ((preview-url (cdr (assoc 'preview_url media-attachement)))) - (concat (propertize "[img]" - 'media-url preview-url - 'media-state 'needs-loading - 'media-type 'media-link) - " "))) + (mastodon-media--get-media-link-rendering preview-url))) media-attachements ""))) (defun mastodon-tl--content (toot) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index e89d313..0d0458d 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -105,7 +105,8 @@ (ert-deftest mastodon-tl--byline-regular () "Should format the regular toot correctly." - (let ((timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) + (let ((mastodon-media-show-avatars-p nil) + (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") @@ -116,9 +117,24 @@ | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 ------------"))))) +(ert-deftest mastodon-tl--byline-regular-with-avatar () + "Should format the regular toot correctly." + (let ((mastodon-media-show-avatars-p t) + (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) + (with-mock + (mock (date-to-time timestamp) => '(22782 21551)) + (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") + + (should (string= (substring-no-properties + (mastodon-tl--byline mastodon-tl-test-base-toot)) + " + | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 + ------------"))))) + (ert-deftest mastodon-tl--byline-boosted () "Should format the boosted toot correctly." - (let* ((toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -131,7 +147,8 @@ (ert-deftest mastodon-tl--byline-favorited () "Should format the favourited toot correctly." - (let* ((toot (cons '(favourited . t) mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot (cons '(favourited . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -145,7 +162,8 @@ (ert-deftest mastodon-tl--byline-boosted/favorited () "Should format the boosted & favourited toot correctly." - (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -158,7 +176,8 @@ (ert-deftest mastodon-tl--byline-reblogged () "Should format the reblogged toot correctly." - (let* ((toot mastodon-tl-test-base-boosted-toot) + (let* ((mastodon-media-show-avatars-p nil) + (toot mastodon-tl-test-base-boosted-toot) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) @@ -175,9 +194,30 @@ | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time ------------"))))) +(ert-deftest mastodon-tl--byline-reblogged-with-avatars () + "Should format the reblogged toot correctly." + (let* ((mastodon-media-show-avatars-p t) + (toot mastodon-tl-test-base-boosted-toot) + (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) + (timestamp (cdr (assoc 'created_at toot))) + (original-timestamp (cdr (assoc 'created_at original-toot)))) + (with-mock + ;; We don't expect to use the toot's timestamp but the timestamp of the + ;; reblogged toot: + (mock (date-to-time timestamp) => '(1 2)) + (mock (format-time-string mastodon-toot-timestamp-format '(1 2)) => "reblogging time") + (mock (date-to-time original-timestamp) => '(3 4)) + (mock (format-time-string mastodon-toot-timestamp-format '(3 4)) => "original time") + + (should (string= (substring-no-properties (mastodon-tl--byline toot)) + " + | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time + ------------"))))) + (ert-deftest mastodon-tl--byline-reblogged-boosted/favorited () "Should format the reblogged toot that was also boosted & favoritedcorrectly." - (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) -- cgit v1.2.3 From d154f9fa762a258601015b44895eed5cbbdc7d78 Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Fri, 12 May 2017 01:18:19 -0400 Subject: closed #79 --- lisp/mastodon-tl.el | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index e025a6e..12b739b 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -177,14 +177,16 @@ also render the html" (defun mastodon-tl--media (toot) "Retrieve a media attachment link for TOOT if one exists." - (let ((media (mastodon-tl--field 'media_attachments toot))) - (mapconcat - (lambda (media-preview) - (concat "Media_Link:: " - (mastodon-tl--set-face - (cdr (assoc 'preview_url media-preview)) - 'mouse-face nil))) - media "\n"))) + (let* ((media (mastodon-tl--field 'media_attachments toot)) + (media-string (mapconcat + (lambda (media-preview) + (concat "Media_Link:: " + (mastodon-tl--set-face + (cdr (assoc 'preview_url media-preview)) + 'mouse-face nil))) + media "\n"))) + (if (not (equal media-string "")) + (concat "\n" media-string ) ""))) (defun mastodon-tl--content (toot) "Retrieve text content from TOOT." @@ -201,8 +203,9 @@ also render the html" (insert (concat (mastodon-tl--spoiler toot) - (mastodon-tl--content toot) + (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) (mastodon-tl--media toot) + "\n\n" (mastodon-tl--byline toot) "\n\n"))) -- cgit v1.2.3 From 91d488571bf796b61d275def376603975f127fde Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Wed, 10 May 2017 21:26:43 +0100 Subject: Add tests for mastodon-media.el MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also includes tweaks to make Travis happy — tests previously did pass on my laptop but Travis's environment is different. --- lisp/mastodon-media.el | 23 +++--- test/mastodon-media-tests.el | 179 +++++++++++++++++++++++++++++++++++++++++++ test/mastodon-tl-tests.el | 2 + 3 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 test/mastodon-media-tests.el diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 734e11f..d1ec871 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -1,4 +1,4 @@ -;;; mastodon-media.el --- Functions for inlining Mastodon media -*- lexical-binding: t -*- +;;; mastodon-media.el --- Functions for inlining Mastodon media ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen @@ -161,9 +161,11 @@ IMAGE-URL is the URL that was retrieved. MEDIA-TYPE is a symbol and either 'avatar or 'media-link." ;; TODO: Cache the avatars (let ((image-options (when (image-type-available-p 'imagemagick) - (pcase media-type - ('avatar `(:height ,mastodon-avatar-height)) - ('media-link `(:max-height ,mastodon-preview-max-height)))))) + (cond + ((eq media-type 'avatar) + `(:height ,mastodon-avatar-height)) + ((eq media-type 'media-link) + `(:max-height ,mastodon-preview-max-height)))))) (url-retrieve url #'mastodon-media--process-image-response (list (copy-marker start) image-options region-length url)))) @@ -182,11 +184,14 @@ found." ;; do nothing - the loop will proceed ) (when next-pos - (pcase (get-text-property next-pos 'media-type) - ;; Avatars are just one character in the buffer - ('avatar (list next-pos (+ next-pos 1) 'avatar)) + (let ((media-type (get-text-property next-pos 'media-type))) + (cond + ;; Avatars are just one character in the buffer + ((eq media-type 'avatar) + (list next-pos (+ next-pos 1) 'avatar)) ;; Media links are 5 character ("[img]") - ('media-link (list next-pos (+ next-pos 5) 'media-link)))))) + ((eq media-type 'media-link) + (list next-pos (+ next-pos 5) 'media-link))))))) (defun mastodon-media--valid-link-p (link) "Checks to make sure that the missing string has @@ -204,7 +209,7 @@ not been returned." (while (setq line-details (mastodon-media--select-next-media-line)) (let* ((start (car line-details)) (end (cadr line-details)) - (media-type (caddr line-details)) + (media-type (cadr (cdr line-details))) (image-url (get-text-property start 'media-url))) (if (not (mastodon-media--valid-link-p image-url)) ;; mark it at least as not needing loading any more diff --git a/test/mastodon-media-tests.el b/test/mastodon-media-tests.el new file mode 100644 index 0000000..9cd06b7 --- /dev/null +++ b/test/mastodon-media-tests.el @@ -0,0 +1,179 @@ +(require 'el-mock) + +(ert-deftest mastodon-media:get-avatar-rendering () + "Should return text with all expected properties." + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * 'imagemagick t :height 123) => :mock-image) + + (let* ((mastodon-avatar-height 123) + (result (mastodon-media--get-avatar-rendering "http://example.org/img.png")) + (result-no-properties (substring-no-properties result)) + (properties (text-properties-at 0 result))) + (should (string= " " result-no-properties)) + (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) + (should (eq 'needs-loading (plist-get properties 'media-state))) + (should (eq 'avatar (plist-get properties 'media-type))) + (should (eq :mock-image (plist-get properties 'display)))))) + +(ert-deftest mastodon-media:get-media-link-rendering () + "Should return text with all expected properties." + (with-mock + (mock (create-image * nil t) => :mock-image) + + (let* ((mastodon-preview-max-height 123) + (result (mastodon-media--get-media-link-rendering "http://example.org/img.png")) + (result-no-properties (substring-no-properties result)) + (properties (text-properties-at 0 result))) + (should (string= "[img] " result-no-properties)) + (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) + (should (eq 'needs-loading (plist-get properties 'media-state))) + (should (eq 'media-link (plist-get properties 'media-type))) + (should (eq :mock-image (plist-get properties 'display)))))) + +(ert-deftest mastodon-media:load-image-from-url:avatar-with-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png") + (mastodon-avatar-height 123)) + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * 'imagemagick t :height 123) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + url + #'mastodon-media--process-image-response + '(:my-marker (:height 123) 1 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering "http://example.org/img.png") + ":rest")) + + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) + +(ert-deftest mastodon-media:load-image-from-url:avatar-without-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => nil) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + url + #'mastodon-media--process-image-response + '(:my-marker () 1 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering "http://example.org/img.png") + ":rest")) + + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) + +(ert-deftest mastodon-media:load-image-from-url:media-link-with-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + "http://example.org/image.png" + #'mastodon-media--process-image-response + '(:my-marker (:max-height 321) 5 "http://example.org/image.png")) + => :called-as-expected) + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-media-link-rendering url) + ":rest")) + (let ((mastodon-preview-max-height 321)) + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) + +(ert-deftest mastodon-media:load-image-from-url:media-link-without-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => nil) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + "http://example.org/image.png" + #'mastodon-media--process-image-response + '(:my-marker () 5 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering url) + ":rest")) + (let ((mastodon-preview-max-height 321)) + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) + +(ert-deftest mastodon-media:process-image-response () + "Should process the HTTP response and adjust the source buffer." + (with-temp-buffer + (with-mock + (let ((source-buffer (current-buffer)) + used-marker + saved-marker) + (insert "start:") + (setq used-marker (copy-marker (point)) + saved-marker (copy-marker (point))) + ;; Mock needed for the preliminary image created in mastodon-media--get-avatar-rendering + (stub create-image => :fake-image) + (insert (mastodon-media--get-avatar-rendering "http://example.org/image.png") + ":end") + (with-temp-buffer + (insert "some irrelevant\n" + "http headers\n" + "which will be ignored\n\n" + "fake\nimage\ndata") + (goto-char (point-min)) + + (mock (create-image "fake\nimage\ndata" 'imagemagick t ':image :option) => :fake-image) + + (mastodon-media--process-image-response () used-marker '(:image :option) 1 "the-url") + + ;; the used marker has been unset: + (should (null (marker-position used-marker))) + ;; the media-state has been set to loaded and the image is being displayed + (should (eq 'loaded (get-text-property saved-marker 'media-state source-buffer))) + (should (eq ':fake-image (get-text-property saved-marker 'display source-buffer)))))))) + +(ert-deftest mastodon-media:inline-images () + "Should process all media in buffer." + (with-mock + ;; Stub needed for the test setup: + (stub create-image => '(image ignored)) + + (let (marker-media-link marker-media-link-bad-url marker-false-media marker-avatar) + (with-temp-buffer + (insert "Some text before\n") + (setq marker-media-link (copy-marker (point))) + (insert (mastodon-media--get-media-link-rendering "http://example.org/i.jpg") + " some more text ") + (setq marker-media-link-bad-url (copy-marker (point))) + (insert (mastodon-media--get-media-link-rendering "/files/small/missing.png") + " some more text ") + (setq marker-false-media (copy-marker (point))) + (insert + ;; text that looks almost like an avatar but lacks the media-url property + (propertize "this won't be processed" + 'media-state 'needs-loading + 'media-type 'avatar) + "even more text ") + (setq marker-avatar (copy-marker (point))) + (insert (mastodon-media--get-avatar-rendering "http://example.org/avatar.png") + " end of text") + (goto-char (point-min)) + + ;; stub for the actual test: + (stub mastodon-media--load-image-from-url) + (mastodon-media--inline-images) + + (should (eq 'loading (get-text-property marker-media-link 'media-state))) + (should (eq 'invalid-url (get-text-property marker-media-link-bad-url 'media-state))) + (should (eq 'loading (get-text-property marker-avatar 'media-state))) + (should (eq 'needs-loading (get-text-property marker-false-media 'media-state))))))) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index 0d0458d..a91d6d5 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -122,6 +122,7 @@ (let ((mastodon-media-show-avatars-p t) (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock + (stub create-image => '(image "fake data")) (mock (date-to-time timestamp) => '(22782 21551)) (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") @@ -204,6 +205,7 @@ (with-mock ;; We don't expect to use the toot's timestamp but the timestamp of the ;; reblogged toot: + (stub create-image => '(image "fake data")) (mock (date-to-time timestamp) => '(1 2)) (mock (format-time-string mastodon-toot-timestamp-format '(1 2)) => "reblogging time") (mock (date-to-time original-timestamp) => '(3 4)) -- cgit v1.2.3 From b0ead06b42ed06218e87a0bb75dae41570592423 Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Fri, 12 May 2017 22:53:51 -0400 Subject: Adjusted for async image changes --- lisp/mastodon-media.el | 232 +++++++++++++++++++++++++++++++++++-------- lisp/mastodon-tl.el | 23 +++-- lisp/mastodon.el | 10 ++ test/mastodon-media-tests.el | 179 +++++++++++++++++++++++++++++++++ test/mastodon-tl-tests.el | 54 ++++++++-- 5 files changed, 442 insertions(+), 56 deletions(-) create mode 100644 test/mastodon-media-tests.el diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 23fbc79..d1ec871 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -38,60 +38,212 @@ :prefix "mastodon-media-" :group 'mastodon) -(defun mastodon-media--image-from-url (url) - "Takes a URL and return an image." - (let ((buffer (url-retrieve-synchronously url))) +(defvar mastodon-media-show-avatars-p + (image-type-available-p 'imagemagick) + "A boolean value stating whether to show avatars in timelines.") + +(defvar mastodon-media--generic-avatar-data + (base64-decode-string + "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") + "The PNG data for a generic 100x100 avatar") + +(defvar mastodon-media--generic-broken-image-data + (base64-decode-string + "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") + "The PNG data for a generic 200x200 'broken image' view") + +(defun mastodon-media--process-image-response (status-plist marker image-options region-length image-url) + "Callback function processing the url retrieve response for URL. + +STATUS-PLIST is the usual plist of status events as per `url-retrieve'. +IMAGE-OPTIONS are the precomputed options to apply to the image. +MARKER is the marker to where the response should be visible. +REGION-LENGTH is the length of the region that should be replaced with the image. +IMAGE-URL is the URL that was retrieved. +" + (let ((url-buffer (current-buffer)) + (is-error-response-p (eq :error (car status-plist)))) (unwind-protect - (let ((data (with-current-buffer buffer - (goto-char (point-min)) - (search-forward "\n\n") - (buffer-substring (point) (point-max))))) - (insert "\n") - (insert-image (create-image data nil t))) - (kill-buffer buffer)))) + (let* ((data (unless is-error-response-p + (goto-char (point-min)) + (search-forward "\n\n") + (buffer-substring (point) (point-max)))) + (image (when data + (apply #'create-image data (when image-options 'imagemagick) + t image-options)))) + (switch-to-buffer (marker-buffer marker)) + ;; Save narrowing in our buffer + (let ((inhibit-read-only t)) + (save-restriction + (widen) + (put-text-property marker (+ marker region-length) 'media-state 'loaded) + (when image + ;; We only set the image to display if we could load + ;; it; we already have set a default image when we + ;; added the tag. + (put-text-property marker (+ marker region-length) + 'display image)) + ;; We are done with the marker; release it: + (set-marker marker nil))) + (kill-buffer url-buffer))))) + +(defun mastodon-media--load-image-from-url (url media-type start region-length) + "Takes a URL and MEDIA-TYPE and load the image asynchronously. + +MEDIA-TYPE is a symbol and either 'avatar or 'media-link." + ;; TODO: Cache the avatars + (let ((image-options (when (image-type-available-p 'imagemagick) + (cond + ((eq media-type 'avatar) + `(:height ,mastodon-avatar-height)) + ((eq media-type 'media-link) + `(:max-height ,mastodon-preview-max-height)))))) + (url-retrieve url + #'mastodon-media--process-image-response + (list (copy-marker start) image-options region-length url)))) (defun mastodon-media--select-next-media-line () - "Find coordinates of a line that contains `Media_Links::' - -Returns the cons of (`start' . `end') points of that line or nil no -more media links were found." - (let ((foundp (search-forward-regexp "Media_Link::" nil t))) - (when foundp - (let ((start (progn (move-beginning-of-line nil) (point))) - (end (progn (move-end-of-line nil) (point)))) - (cons start end))))) + "Find coordinates of the next media to load. + +Returns the list of (`start' . `end', `media-symbol') points of +that line and string found or nil no more media links were +found." + (let ((next-pos (point))) + (while (and (setq next-pos (next-single-property-change next-pos 'media-state)) + (or (not (eq 'needs-loading (get-text-property next-pos 'media-state))) + (null (get-text-property next-pos 'media-url)) + (null (get-text-property next-pos 'media-type)))) + ;; do nothing - the loop will proceed + ) + (when next-pos + (let ((media-type (get-text-property next-pos 'media-type))) + (cond + ;; Avatars are just one character in the buffer + ((eq media-type 'avatar) + (list next-pos (+ next-pos 1) 'avatar)) + ;; Media links are 5 character ("[img]") + ((eq media-type 'media-link) + (list next-pos (+ next-pos 5) 'media-link))))))) (defun mastodon-media--valid-link-p (link) "Checks to make sure that the missing string has not been returned." (let ((missing "/files/small/missing.png")) - (not (equal link missing)))) - -(defun mastodon-media--line-to-link (line-points) - "Returns the url of the media link given at the given point. - -`LINE-POINTS' is a cons of (`start' . `end') positions of the line with -the `Media_Link:: ' text." - (replace-regexp-in-string "Media_Link:: " "" - (buffer-substring - (car line-points) - (cdr line-points)))) - -(defun mastodon-media--delete-line (line) - "Deletes the current media line" - (delete-region (car line) (cdr line))) + (and link + (not (equal link missing))))) (defun mastodon-media--inline-images () "Find all `Media_Links:' in the buffer replacing them with the referenced image." (interactive) (goto-char (point-min)) - (let (line-coordinates) - (while (setq line-coordinates (mastodon-media--select-next-media-line)) - (let ((link (mastodon-media--line-to-link line-coordinates))) - (when (mastodon-media--valid-link-p link) - (mastodon-media--image-from-url link) - (mastodon-media--delete-line line-coordinates)))))) + (let (line-details) + (while (setq line-details (mastodon-media--select-next-media-line)) + (let* ((start (car line-details)) + (end (cadr line-details)) + (media-type (cadr (cdr line-details))) + (image-url (get-text-property start 'media-url))) + (if (not (mastodon-media--valid-link-p image-url)) + ;; mark it at least as not needing loading any more + (put-text-property start end 'media-state 'invalid-url) + ;; proceed to load this image asynchronously + (put-text-property start end 'media-state 'loading) + (mastodon-media--load-image-from-url image-url media-type start (- end start))))))) + +(defun mastodon-media--get-avatar-rendering (avatar-url) + "Returns the string to be written that renders the avatar at AVATAR-URL." + ;; We use just an empty space as the textual representation. + ;; This is what a user will see on a non-graphical display + ;; where not showing an avatar at all is preferable. + (let ((image-options (when (image-type-available-p 'imagemagick) + `(:height ,mastodon-avatar-height)))) + (concat + (propertize " " + 'media-url avatar-url + 'media-state 'needs-loading + 'media-type 'avatar + 'display (apply #'create-image mastodon-media--generic-avatar-data + (when image-options 'imagemagick) + t image-options)) + " "))) + +(defun mastodon-media--get-media-link-rendering (media-url) + "Returns the string to be written that renders the image at MEDIA-URL." + (concat + (propertize "[img]" + 'media-url media-url + 'media-state 'needs-loading + 'media-type 'media-link + 'display (create-image mastodon-media--generic-broken-image-data nil t)) + " ")) (provide 'mastodon-media) ;;; mastodon-media.el ends here diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 12b739b..7efaf11 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -104,8 +104,11 @@ Optionally start from POS." "Propertize author of TOOT." (let* ((account (cdr (assoc 'account toot))) (handle (cdr (assoc 'acct account))) - (name (cdr (assoc 'display_name account)))) + (name (cdr (assoc 'display_name account))) + (avatar-url (cdr (assoc 'avatar account)))) (concat + (when mastodon-media-show-avatars-p + (mastodon-media--get-avatar-rendering avatar-url)) (propertize name 'face 'warning) " (@" handle @@ -177,14 +180,14 @@ also render the html" (defun mastodon-tl--media (toot) "Retrieve a media attachment link for TOOT if one exists." - (let* ((media (mastodon-tl--field 'media_attachments toot)) - (media-string (mapconcat - (lambda (media-preview) - (concat "Media_Link:: " - (mastodon-tl--set-face - (cdr (assoc 'preview_url media-preview)) - 'mouse-face nil))) - media "\n"))) + (let* ((media-attachements (mastodon-tl--field 'media_attachments toot)) + (media-string + (mapconcat + (lambda (media-attachement) + (let ((preview-url + (cdr (assoc 'preview_url media-attachement)))) + (mastodon-media--get-media-link-rendering preview-url))) + media-attachements ""))) (if (not (equal media-string "")) (concat "\n" media-string ) ""))) @@ -203,7 +206,7 @@ also render the html" (insert (concat (mastodon-tl--spoiler toot) - (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) + (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) (mastodon-tl--media toot) "\n\n" (mastodon-tl--byline toot) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 947cc6a..0dd7f10 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -60,6 +60,16 @@ Use. e.g. \"%c\" for your locale's date and time format." :group 'mastodon :type 'string) +(defcustom mastodon-avatar-height 30 + "Height of the user avatar images (if shown)." + :group 'mastodon + :type 'integer) + +(defcustom mastodon-preview-max-height 250 + "Max height of any media attachment preview to be shown." + :group 'mastodon + :type 'integer) + (defvar mastodon-mode-map (make-sparse-keymap) "Keymap for `mastodon-mode'.") diff --git a/test/mastodon-media-tests.el b/test/mastodon-media-tests.el new file mode 100644 index 0000000..9cd06b7 --- /dev/null +++ b/test/mastodon-media-tests.el @@ -0,0 +1,179 @@ +(require 'el-mock) + +(ert-deftest mastodon-media:get-avatar-rendering () + "Should return text with all expected properties." + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * 'imagemagick t :height 123) => :mock-image) + + (let* ((mastodon-avatar-height 123) + (result (mastodon-media--get-avatar-rendering "http://example.org/img.png")) + (result-no-properties (substring-no-properties result)) + (properties (text-properties-at 0 result))) + (should (string= " " result-no-properties)) + (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) + (should (eq 'needs-loading (plist-get properties 'media-state))) + (should (eq 'avatar (plist-get properties 'media-type))) + (should (eq :mock-image (plist-get properties 'display)))))) + +(ert-deftest mastodon-media:get-media-link-rendering () + "Should return text with all expected properties." + (with-mock + (mock (create-image * nil t) => :mock-image) + + (let* ((mastodon-preview-max-height 123) + (result (mastodon-media--get-media-link-rendering "http://example.org/img.png")) + (result-no-properties (substring-no-properties result)) + (properties (text-properties-at 0 result))) + (should (string= "[img] " result-no-properties)) + (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) + (should (eq 'needs-loading (plist-get properties 'media-state))) + (should (eq 'media-link (plist-get properties 'media-type))) + (should (eq :mock-image (plist-get properties 'display)))))) + +(ert-deftest mastodon-media:load-image-from-url:avatar-with-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png") + (mastodon-avatar-height 123)) + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * 'imagemagick t :height 123) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + url + #'mastodon-media--process-image-response + '(:my-marker (:height 123) 1 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering "http://example.org/img.png") + ":rest")) + + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) + +(ert-deftest mastodon-media:load-image-from-url:avatar-without-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => nil) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + url + #'mastodon-media--process-image-response + '(:my-marker () 1 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering "http://example.org/img.png") + ":rest")) + + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) + +(ert-deftest mastodon-media:load-image-from-url:media-link-with-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => t) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + "http://example.org/image.png" + #'mastodon-media--process-image-response + '(:my-marker (:max-height 321) 5 "http://example.org/image.png")) + => :called-as-expected) + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-media-link-rendering url) + ":rest")) + (let ((mastodon-preview-max-height 321)) + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) + +(ert-deftest mastodon-media:load-image-from-url:media-link-without-imagemagic () + "Should make the right call to url-retrieve." + (let ((url "http://example.org/image.png")) + (with-mock + (mock (image-type-available-p 'imagemagick) => nil) + (mock (create-image * nil t) => '(image foo)) + (mock (copy-marker 7) => :my-marker ) + (mock (url-retrieve + "http://example.org/image.png" + #'mastodon-media--process-image-response + '(:my-marker () 5 "http://example.org/image.png")) + => :called-as-expected) + + (with-temp-buffer + (insert (concat "Start:" + (mastodon-media--get-avatar-rendering url) + ":rest")) + (let ((mastodon-preview-max-height 321)) + (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) + +(ert-deftest mastodon-media:process-image-response () + "Should process the HTTP response and adjust the source buffer." + (with-temp-buffer + (with-mock + (let ((source-buffer (current-buffer)) + used-marker + saved-marker) + (insert "start:") + (setq used-marker (copy-marker (point)) + saved-marker (copy-marker (point))) + ;; Mock needed for the preliminary image created in mastodon-media--get-avatar-rendering + (stub create-image => :fake-image) + (insert (mastodon-media--get-avatar-rendering "http://example.org/image.png") + ":end") + (with-temp-buffer + (insert "some irrelevant\n" + "http headers\n" + "which will be ignored\n\n" + "fake\nimage\ndata") + (goto-char (point-min)) + + (mock (create-image "fake\nimage\ndata" 'imagemagick t ':image :option) => :fake-image) + + (mastodon-media--process-image-response () used-marker '(:image :option) 1 "the-url") + + ;; the used marker has been unset: + (should (null (marker-position used-marker))) + ;; the media-state has been set to loaded and the image is being displayed + (should (eq 'loaded (get-text-property saved-marker 'media-state source-buffer))) + (should (eq ':fake-image (get-text-property saved-marker 'display source-buffer)))))))) + +(ert-deftest mastodon-media:inline-images () + "Should process all media in buffer." + (with-mock + ;; Stub needed for the test setup: + (stub create-image => '(image ignored)) + + (let (marker-media-link marker-media-link-bad-url marker-false-media marker-avatar) + (with-temp-buffer + (insert "Some text before\n") + (setq marker-media-link (copy-marker (point))) + (insert (mastodon-media--get-media-link-rendering "http://example.org/i.jpg") + " some more text ") + (setq marker-media-link-bad-url (copy-marker (point))) + (insert (mastodon-media--get-media-link-rendering "/files/small/missing.png") + " some more text ") + (setq marker-false-media (copy-marker (point))) + (insert + ;; text that looks almost like an avatar but lacks the media-url property + (propertize "this won't be processed" + 'media-state 'needs-loading + 'media-type 'avatar) + "even more text ") + (setq marker-avatar (copy-marker (point))) + (insert (mastodon-media--get-avatar-rendering "http://example.org/avatar.png") + " end of text") + (goto-char (point-min)) + + ;; stub for the actual test: + (stub mastodon-media--load-image-from-url) + (mastodon-media--inline-images) + + (should (eq 'loading (get-text-property marker-media-link 'media-state))) + (should (eq 'invalid-url (get-text-property marker-media-link-bad-url 'media-state))) + (should (eq 'loading (get-text-property marker-avatar 'media-state))) + (should (eq 'needs-loading (get-text-property marker-false-media 'media-state))))))) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index e89d313..a91d6d5 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -105,7 +105,8 @@ (ert-deftest mastodon-tl--byline-regular () "Should format the regular toot correctly." - (let ((timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) + (let ((mastodon-media-show-avatars-p nil) + (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") @@ -116,9 +117,25 @@ | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 ------------"))))) +(ert-deftest mastodon-tl--byline-regular-with-avatar () + "Should format the regular toot correctly." + (let ((mastodon-media-show-avatars-p t) + (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) + (with-mock + (stub create-image => '(image "fake data")) + (mock (date-to-time timestamp) => '(22782 21551)) + (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") + + (should (string= (substring-no-properties + (mastodon-tl--byline mastodon-tl-test-base-toot)) + " + | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 + ------------"))))) + (ert-deftest mastodon-tl--byline-boosted () "Should format the boosted toot correctly." - (let* ((toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -131,7 +148,8 @@ (ert-deftest mastodon-tl--byline-favorited () "Should format the favourited toot correctly." - (let* ((toot (cons '(favourited . t) mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot (cons '(favourited . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -145,7 +163,8 @@ (ert-deftest mastodon-tl--byline-boosted/favorited () "Should format the boosted & favourited toot correctly." - (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -158,7 +177,8 @@ (ert-deftest mastodon-tl--byline-reblogged () "Should format the reblogged toot correctly." - (let* ((toot mastodon-tl-test-base-boosted-toot) + (let* ((mastodon-media-show-avatars-p nil) + (toot mastodon-tl-test-base-boosted-toot) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) @@ -175,9 +195,31 @@ | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time ------------"))))) +(ert-deftest mastodon-tl--byline-reblogged-with-avatars () + "Should format the reblogged toot correctly." + (let* ((mastodon-media-show-avatars-p t) + (toot mastodon-tl-test-base-boosted-toot) + (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) + (timestamp (cdr (assoc 'created_at toot))) + (original-timestamp (cdr (assoc 'created_at original-toot)))) + (with-mock + ;; We don't expect to use the toot's timestamp but the timestamp of the + ;; reblogged toot: + (stub create-image => '(image "fake data")) + (mock (date-to-time timestamp) => '(1 2)) + (mock (format-time-string mastodon-toot-timestamp-format '(1 2)) => "reblogging time") + (mock (date-to-time original-timestamp) => '(3 4)) + (mock (format-time-string mastodon-toot-timestamp-format '(3 4)) => "original time") + + (should (string= (substring-no-properties (mastodon-tl--byline toot)) + " + | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time + ------------"))))) + (ert-deftest mastodon-tl--byline-reblogged-boosted/favorited () "Should format the reblogged toot that was also boosted & favoritedcorrectly." - (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) + (let* ((mastodon-media-show-avatars-p nil) + (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) -- cgit v1.2.3 From 92a40a6bd6b524ee29385dc8a27c4b05b89b2cea Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Fri, 12 May 2017 22:56:05 -0400 Subject: Revert "Adjusted for async image changes" This reverts commit b0ead06b42ed06218e87a0bb75dae41570592423. --- lisp/mastodon-media.el | 232 ++++++++----------------------------------- lisp/mastodon-tl.el | 23 ++--- lisp/mastodon.el | 10 -- test/mastodon-media-tests.el | 179 --------------------------------- test/mastodon-tl-tests.el | 54 ++-------- 5 files changed, 56 insertions(+), 442 deletions(-) delete mode 100644 test/mastodon-media-tests.el diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index d1ec871..23fbc79 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -38,212 +38,60 @@ :prefix "mastodon-media-" :group 'mastodon) -(defvar mastodon-media-show-avatars-p - (image-type-available-p 'imagemagick) - "A boolean value stating whether to show avatars in timelines.") - -(defvar mastodon-media--generic-avatar-data - (base64-decode-string - "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") - "The PNG data for a generic 100x100 avatar") - -(defvar mastodon-media--generic-broken-image-data - (base64-decode-string - "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA -B3RJTUUH4QUIFQUVFt+0LQAAABZ0RVh0Q29tbWVudABHZW5lcmljIGF2YXRhcsyCnMsAAAdoSURB -VHja7d1NSFRrAIfx//iB6ZDSMJYVkWEk0ceYFUkkhhQlEUhEg0FlC1eBoRTUwlbRok0TgRQURZAE -FgpjJmFajpK4kggxpXHRQEGWUJZizpy7uPfC5eKiV+dD5zw/mN05jrxnnjnfcxyWZVkCMKc0SXI4 -HIwEMIcUhgAgEIBAAAIBCAQgEIBAAAIBCAQgEAAEAhAIQCAAgQAEAhAIQCAAgQA2kBaNP8Jt7ViM -onErOWsQgEAAAgEIBCAQgEAAAgEIBCAQgEAAEAhAIACBAAQCEAhAIACBAAQCEAhAIAAIBCAQgEAA -AgEIBCAQgEAAAgEIBACBAAQCEAhAIACBAAQCEAhAIACBAAQCgEAAAgEIBCAQgECAxSyNIYitz58/ -a3BwUIODgxoZGVEoFFIoFNK3b980NTWlX79+SZIyMzOVlZWlVatWae3atSooKJDH49HOnTvl8XiU -ksJ3WSI4LMuyHA7Hgv6IZVmM5D8mJyf1/PlzdXZ2qrOzU8FgcMF/0+126+DBg6qqqlJFRYXS0vhe -+6MP9wI/1wQSJeFwWH6/X01NTWpra9PU1FTM3isvL0/nz5/XuXPntHz5ciqIcSCy/v50L+hlV+Pj -49a1a9esdevWLXgMTV8ul8u6c+eOFYlELMwtKmNNIOa+fv1qXbp0yXI6nXEP4/+v0tJS6+PHj9RA -IIk3PT1tXb161crOzk54GP995ebmWt3d3RRBIInj9/utgoKCRRXGf18ZGRmW3++niigHwk56PHf4 -Yiw9PV0dHR0qLy9nD52jWAQylxUrVmhgYEAbN24kkCgsM84+JZmJiQmdPn1akUiEweBE4eL/NsrN -zVVZWZlKSkpUWFioTZs2yeVyKTs7W7Ozs5qYmNDExITev3+v/v5+9fX1qb+/f8FjevPmTdXW1rIG -IZDFN9gbNmyQ1+uV1+uVx+MxXlAjIyNqbGzU3bt39fPnz3n9vytXrlQwGJTT6SQQThQm/ohIamqq -VVlZaXV1dUXtPT98+GCVlZXNe7n4fD6OYnGYN7GDnZ6ebtXU1FhjY2Mxed9IJGLV19fPa7kUFRUR -CIEkZrAdDod15syZmIXxf7W1tfNaNqOjowSygBdHseZh7969GhgY0IMHD5Sfnx+X97xx44Z2795t -PF93dzcLjMO88TvHcP/+ffX19WnXrl3xXVApKbp9+7bxfSFv3rxhwRFI7B07dkxDQ0Oqrq5O2P9Q -XFysffv2Gc0zOjrKwiOQ2Hv69Kny8vIS/n8cP37caPqxsTEWHoHYa//HxPfv3xk0ArGP1atXG03/ -7z3vIBBbyM3NNZo+KyuLQSMQ+5icnDSaPicnh0EjEPsYHh42mp7L3gnEVnp6eoymLyoqYtAIxD4e -PXpkNP3+/fsZtAXgcvclpL29XUeOHPnj6Z1Op8bHx7Vs2TJ7fri5o9A+ZmZmdPHiRaN5vF6vbeNg -E8tmGhoaNDQ0ZPTteeHCBQaOQJLfkydPdP36daN5Tp48qc2bNzN47IMkt9evX+vw4cOanp7+43ly -cnI0PDy8KK4dYx8EMRMIBHT06FGjOCTJ5/PZPg42sZJce3u7Dh06pB8/fhjNV11dndBL8tnEYhMr -5lpaWuT1evX792+j+YqLixUIBLj+ik2s5NXc3KwTJ04Yx5Gfn69nz54RB5tYyaupqUlVVVWanZ01 -ms/tdqujo4P9DgJJXg8fPtSpU6cUDoeN43j58qUKCwsZRAJJTvfu3dPZs2eNf0/X7Xarq6tL27dv -ZxAJJDn5fD7V1NQYx7FmzRq9evVK27ZtYxAJJDk1NDSorq7O+ChgQUGBent7tWXLFgYxxniecILU -1dXJ5/MZz7d161a9ePHC+N50sAZZMq5cuTKvOEpKStTT00McccSJwji7devWvJ7bceDAAbW2ttr6 -cQbGH26eD7K0BAIBlZeXG5/nqKioUEtLizIyMhhEAklOX758kcfj0adPn4zXHG1tbcSRoEDYB4mT -y5cvG8exZ88etba2Egf7IMnt7du32rFjh9G5jvz8fA0MDBj/UBxYgyw5jY2NRnGkpqaqubmZOBYB -AomxmZkZPX782Gie+vr6uD9/BGxiJURvb69KS0v/ePrMzEyFQiG5XC4Gj02s5BcIBIymr6ysJA42 -sezj3bt3RtObPv8DBLKkBYNBo+m5r4NAbCUUChlNv379egaNQOzD9FdJ2P8gEFsxfQQaFyMuLhzm -jfUAG45tOBw2fhY6ojP2rEGWwiqdONjEAggEIBCAQAACAUAgAIEA0cIPx8UYJ1FZgwAEAhAIAAIB -CAQgEIBAAAIBFiNOFMaY6V1tnFhkDQIQCEAgAIEABAKAQAACAQgEIBCAQAACAQgEIBCAQABIXO4e -c1y+zhoEIBCAQAAQCEAgAIEABAIQCEAgAIEABAIQCEAgAAgEIBCAQAACAQgEIBCAQAACAQgEAIEA -BAIQCEAgAIEABAIsJVH58WqHw8FIgjUIQCAACAQgEIBAAAIBCAQgEIBAAAIBCAQgEAAEAhAIQCBA -fKRJkmVZjAQwh78A6vCRWJE8K+8AAAAASUVORK5CYII=") - "The PNG data for a generic 200x200 'broken image' view") - -(defun mastodon-media--process-image-response (status-plist marker image-options region-length image-url) - "Callback function processing the url retrieve response for URL. - -STATUS-PLIST is the usual plist of status events as per `url-retrieve'. -IMAGE-OPTIONS are the precomputed options to apply to the image. -MARKER is the marker to where the response should be visible. -REGION-LENGTH is the length of the region that should be replaced with the image. -IMAGE-URL is the URL that was retrieved. -" - (let ((url-buffer (current-buffer)) - (is-error-response-p (eq :error (car status-plist)))) +(defun mastodon-media--image-from-url (url) + "Takes a URL and return an image." + (let ((buffer (url-retrieve-synchronously url))) (unwind-protect - (let* ((data (unless is-error-response-p - (goto-char (point-min)) - (search-forward "\n\n") - (buffer-substring (point) (point-max)))) - (image (when data - (apply #'create-image data (when image-options 'imagemagick) - t image-options)))) - (switch-to-buffer (marker-buffer marker)) - ;; Save narrowing in our buffer - (let ((inhibit-read-only t)) - (save-restriction - (widen) - (put-text-property marker (+ marker region-length) 'media-state 'loaded) - (when image - ;; We only set the image to display if we could load - ;; it; we already have set a default image when we - ;; added the tag. - (put-text-property marker (+ marker region-length) - 'display image)) - ;; We are done with the marker; release it: - (set-marker marker nil))) - (kill-buffer url-buffer))))) - -(defun mastodon-media--load-image-from-url (url media-type start region-length) - "Takes a URL and MEDIA-TYPE and load the image asynchronously. - -MEDIA-TYPE is a symbol and either 'avatar or 'media-link." - ;; TODO: Cache the avatars - (let ((image-options (when (image-type-available-p 'imagemagick) - (cond - ((eq media-type 'avatar) - `(:height ,mastodon-avatar-height)) - ((eq media-type 'media-link) - `(:max-height ,mastodon-preview-max-height)))))) - (url-retrieve url - #'mastodon-media--process-image-response - (list (copy-marker start) image-options region-length url)))) + (let ((data (with-current-buffer buffer + (goto-char (point-min)) + (search-forward "\n\n") + (buffer-substring (point) (point-max))))) + (insert "\n") + (insert-image (create-image data nil t))) + (kill-buffer buffer)))) (defun mastodon-media--select-next-media-line () - "Find coordinates of the next media to load. - -Returns the list of (`start' . `end', `media-symbol') points of -that line and string found or nil no more media links were -found." - (let ((next-pos (point))) - (while (and (setq next-pos (next-single-property-change next-pos 'media-state)) - (or (not (eq 'needs-loading (get-text-property next-pos 'media-state))) - (null (get-text-property next-pos 'media-url)) - (null (get-text-property next-pos 'media-type)))) - ;; do nothing - the loop will proceed - ) - (when next-pos - (let ((media-type (get-text-property next-pos 'media-type))) - (cond - ;; Avatars are just one character in the buffer - ((eq media-type 'avatar) - (list next-pos (+ next-pos 1) 'avatar)) - ;; Media links are 5 character ("[img]") - ((eq media-type 'media-link) - (list next-pos (+ next-pos 5) 'media-link))))))) + "Find coordinates of a line that contains `Media_Links::' + +Returns the cons of (`start' . `end') points of that line or nil no +more media links were found." + (let ((foundp (search-forward-regexp "Media_Link::" nil t))) + (when foundp + (let ((start (progn (move-beginning-of-line nil) (point))) + (end (progn (move-end-of-line nil) (point)))) + (cons start end))))) (defun mastodon-media--valid-link-p (link) "Checks to make sure that the missing string has not been returned." (let ((missing "/files/small/missing.png")) - (and link - (not (equal link missing))))) + (not (equal link missing)))) + +(defun mastodon-media--line-to-link (line-points) + "Returns the url of the media link given at the given point. + +`LINE-POINTS' is a cons of (`start' . `end') positions of the line with +the `Media_Link:: ' text." + (replace-regexp-in-string "Media_Link:: " "" + (buffer-substring + (car line-points) + (cdr line-points)))) + +(defun mastodon-media--delete-line (line) + "Deletes the current media line" + (delete-region (car line) (cdr line))) (defun mastodon-media--inline-images () "Find all `Media_Links:' in the buffer replacing them with the referenced image." (interactive) (goto-char (point-min)) - (let (line-details) - (while (setq line-details (mastodon-media--select-next-media-line)) - (let* ((start (car line-details)) - (end (cadr line-details)) - (media-type (cadr (cdr line-details))) - (image-url (get-text-property start 'media-url))) - (if (not (mastodon-media--valid-link-p image-url)) - ;; mark it at least as not needing loading any more - (put-text-property start end 'media-state 'invalid-url) - ;; proceed to load this image asynchronously - (put-text-property start end 'media-state 'loading) - (mastodon-media--load-image-from-url image-url media-type start (- end start))))))) - -(defun mastodon-media--get-avatar-rendering (avatar-url) - "Returns the string to be written that renders the avatar at AVATAR-URL." - ;; We use just an empty space as the textual representation. - ;; This is what a user will see on a non-graphical display - ;; where not showing an avatar at all is preferable. - (let ((image-options (when (image-type-available-p 'imagemagick) - `(:height ,mastodon-avatar-height)))) - (concat - (propertize " " - 'media-url avatar-url - 'media-state 'needs-loading - 'media-type 'avatar - 'display (apply #'create-image mastodon-media--generic-avatar-data - (when image-options 'imagemagick) - t image-options)) - " "))) - -(defun mastodon-media--get-media-link-rendering (media-url) - "Returns the string to be written that renders the image at MEDIA-URL." - (concat - (propertize "[img]" - 'media-url media-url - 'media-state 'needs-loading - 'media-type 'media-link - 'display (create-image mastodon-media--generic-broken-image-data nil t)) - " ")) + (let (line-coordinates) + (while (setq line-coordinates (mastodon-media--select-next-media-line)) + (let ((link (mastodon-media--line-to-link line-coordinates))) + (when (mastodon-media--valid-link-p link) + (mastodon-media--image-from-url link) + (mastodon-media--delete-line line-coordinates)))))) (provide 'mastodon-media) ;;; mastodon-media.el ends here diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 7efaf11..12b739b 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -104,11 +104,8 @@ Optionally start from POS." "Propertize author of TOOT." (let* ((account (cdr (assoc 'account toot))) (handle (cdr (assoc 'acct account))) - (name (cdr (assoc 'display_name account))) - (avatar-url (cdr (assoc 'avatar account)))) + (name (cdr (assoc 'display_name account)))) (concat - (when mastodon-media-show-avatars-p - (mastodon-media--get-avatar-rendering avatar-url)) (propertize name 'face 'warning) " (@" handle @@ -180,14 +177,14 @@ also render the html" (defun mastodon-tl--media (toot) "Retrieve a media attachment link for TOOT if one exists." - (let* ((media-attachements (mastodon-tl--field 'media_attachments toot)) - (media-string - (mapconcat - (lambda (media-attachement) - (let ((preview-url - (cdr (assoc 'preview_url media-attachement)))) - (mastodon-media--get-media-link-rendering preview-url))) - media-attachements ""))) + (let* ((media (mastodon-tl--field 'media_attachments toot)) + (media-string (mapconcat + (lambda (media-preview) + (concat "Media_Link:: " + (mastodon-tl--set-face + (cdr (assoc 'preview_url media-preview)) + 'mouse-face nil))) + media "\n"))) (if (not (equal media-string "")) (concat "\n" media-string ) ""))) @@ -206,7 +203,7 @@ also render the html" (insert (concat (mastodon-tl--spoiler toot) - (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) + (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) (mastodon-tl--media toot) "\n\n" (mastodon-tl--byline toot) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 0dd7f10..947cc6a 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -60,16 +60,6 @@ Use. e.g. \"%c\" for your locale's date and time format." :group 'mastodon :type 'string) -(defcustom mastodon-avatar-height 30 - "Height of the user avatar images (if shown)." - :group 'mastodon - :type 'integer) - -(defcustom mastodon-preview-max-height 250 - "Max height of any media attachment preview to be shown." - :group 'mastodon - :type 'integer) - (defvar mastodon-mode-map (make-sparse-keymap) "Keymap for `mastodon-mode'.") diff --git a/test/mastodon-media-tests.el b/test/mastodon-media-tests.el deleted file mode 100644 index 9cd06b7..0000000 --- a/test/mastodon-media-tests.el +++ /dev/null @@ -1,179 +0,0 @@ -(require 'el-mock) - -(ert-deftest mastodon-media:get-avatar-rendering () - "Should return text with all expected properties." - (with-mock - (mock (image-type-available-p 'imagemagick) => t) - (mock (create-image * 'imagemagick t :height 123) => :mock-image) - - (let* ((mastodon-avatar-height 123) - (result (mastodon-media--get-avatar-rendering "http://example.org/img.png")) - (result-no-properties (substring-no-properties result)) - (properties (text-properties-at 0 result))) - (should (string= " " result-no-properties)) - (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) - (should (eq 'needs-loading (plist-get properties 'media-state))) - (should (eq 'avatar (plist-get properties 'media-type))) - (should (eq :mock-image (plist-get properties 'display)))))) - -(ert-deftest mastodon-media:get-media-link-rendering () - "Should return text with all expected properties." - (with-mock - (mock (create-image * nil t) => :mock-image) - - (let* ((mastodon-preview-max-height 123) - (result (mastodon-media--get-media-link-rendering "http://example.org/img.png")) - (result-no-properties (substring-no-properties result)) - (properties (text-properties-at 0 result))) - (should (string= "[img] " result-no-properties)) - (should (string= "http://example.org/img.png" (plist-get properties 'media-url))) - (should (eq 'needs-loading (plist-get properties 'media-state))) - (should (eq 'media-link (plist-get properties 'media-type))) - (should (eq :mock-image (plist-get properties 'display)))))) - -(ert-deftest mastodon-media:load-image-from-url:avatar-with-imagemagic () - "Should make the right call to url-retrieve." - (let ((url "http://example.org/image.png") - (mastodon-avatar-height 123)) - (with-mock - (mock (image-type-available-p 'imagemagick) => t) - (mock (create-image * 'imagemagick t :height 123) => '(image foo)) - (mock (copy-marker 7) => :my-marker ) - (mock (url-retrieve - url - #'mastodon-media--process-image-response - '(:my-marker (:height 123) 1 "http://example.org/image.png")) - => :called-as-expected) - - (with-temp-buffer - (insert (concat "Start:" - (mastodon-media--get-avatar-rendering "http://example.org/img.png") - ":rest")) - - (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) - -(ert-deftest mastodon-media:load-image-from-url:avatar-without-imagemagic () - "Should make the right call to url-retrieve." - (let ((url "http://example.org/image.png")) - (with-mock - (mock (image-type-available-p 'imagemagick) => nil) - (mock (create-image * nil t) => '(image foo)) - (mock (copy-marker 7) => :my-marker ) - (mock (url-retrieve - url - #'mastodon-media--process-image-response - '(:my-marker () 1 "http://example.org/image.png")) - => :called-as-expected) - - (with-temp-buffer - (insert (concat "Start:" - (mastodon-media--get-avatar-rendering "http://example.org/img.png") - ":rest")) - - (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'avatar 7 1))))))) - -(ert-deftest mastodon-media:load-image-from-url:media-link-with-imagemagic () - "Should make the right call to url-retrieve." - (let ((url "http://example.org/image.png")) - (with-mock - (mock (image-type-available-p 'imagemagick) => t) - (mock (create-image * nil t) => '(image foo)) - (mock (copy-marker 7) => :my-marker ) - (mock (url-retrieve - "http://example.org/image.png" - #'mastodon-media--process-image-response - '(:my-marker (:max-height 321) 5 "http://example.org/image.png")) - => :called-as-expected) - (with-temp-buffer - (insert (concat "Start:" - (mastodon-media--get-media-link-rendering url) - ":rest")) - (let ((mastodon-preview-max-height 321)) - (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) - -(ert-deftest mastodon-media:load-image-from-url:media-link-without-imagemagic () - "Should make the right call to url-retrieve." - (let ((url "http://example.org/image.png")) - (with-mock - (mock (image-type-available-p 'imagemagick) => nil) - (mock (create-image * nil t) => '(image foo)) - (mock (copy-marker 7) => :my-marker ) - (mock (url-retrieve - "http://example.org/image.png" - #'mastodon-media--process-image-response - '(:my-marker () 5 "http://example.org/image.png")) - => :called-as-expected) - - (with-temp-buffer - (insert (concat "Start:" - (mastodon-media--get-avatar-rendering url) - ":rest")) - (let ((mastodon-preview-max-height 321)) - (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) - -(ert-deftest mastodon-media:process-image-response () - "Should process the HTTP response and adjust the source buffer." - (with-temp-buffer - (with-mock - (let ((source-buffer (current-buffer)) - used-marker - saved-marker) - (insert "start:") - (setq used-marker (copy-marker (point)) - saved-marker (copy-marker (point))) - ;; Mock needed for the preliminary image created in mastodon-media--get-avatar-rendering - (stub create-image => :fake-image) - (insert (mastodon-media--get-avatar-rendering "http://example.org/image.png") - ":end") - (with-temp-buffer - (insert "some irrelevant\n" - "http headers\n" - "which will be ignored\n\n" - "fake\nimage\ndata") - (goto-char (point-min)) - - (mock (create-image "fake\nimage\ndata" 'imagemagick t ':image :option) => :fake-image) - - (mastodon-media--process-image-response () used-marker '(:image :option) 1 "the-url") - - ;; the used marker has been unset: - (should (null (marker-position used-marker))) - ;; the media-state has been set to loaded and the image is being displayed - (should (eq 'loaded (get-text-property saved-marker 'media-state source-buffer))) - (should (eq ':fake-image (get-text-property saved-marker 'display source-buffer)))))))) - -(ert-deftest mastodon-media:inline-images () - "Should process all media in buffer." - (with-mock - ;; Stub needed for the test setup: - (stub create-image => '(image ignored)) - - (let (marker-media-link marker-media-link-bad-url marker-false-media marker-avatar) - (with-temp-buffer - (insert "Some text before\n") - (setq marker-media-link (copy-marker (point))) - (insert (mastodon-media--get-media-link-rendering "http://example.org/i.jpg") - " some more text ") - (setq marker-media-link-bad-url (copy-marker (point))) - (insert (mastodon-media--get-media-link-rendering "/files/small/missing.png") - " some more text ") - (setq marker-false-media (copy-marker (point))) - (insert - ;; text that looks almost like an avatar but lacks the media-url property - (propertize "this won't be processed" - 'media-state 'needs-loading - 'media-type 'avatar) - "even more text ") - (setq marker-avatar (copy-marker (point))) - (insert (mastodon-media--get-avatar-rendering "http://example.org/avatar.png") - " end of text") - (goto-char (point-min)) - - ;; stub for the actual test: - (stub mastodon-media--load-image-from-url) - (mastodon-media--inline-images) - - (should (eq 'loading (get-text-property marker-media-link 'media-state))) - (should (eq 'invalid-url (get-text-property marker-media-link-bad-url 'media-state))) - (should (eq 'loading (get-text-property marker-avatar 'media-state))) - (should (eq 'needs-loading (get-text-property marker-false-media 'media-state))))))) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index a91d6d5..e89d313 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -105,8 +105,7 @@ (ert-deftest mastodon-tl--byline-regular () "Should format the regular toot correctly." - (let ((mastodon-media-show-avatars-p nil) - (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) + (let ((timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") @@ -117,25 +116,9 @@ | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 ------------"))))) -(ert-deftest mastodon-tl--byline-regular-with-avatar () - "Should format the regular toot correctly." - (let ((mastodon-media-show-avatars-p t) - (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) - (with-mock - (stub create-image => '(image "fake data")) - (mock (date-to-time timestamp) => '(22782 21551)) - (mock (format-time-string mastodon-toot-timestamp-format '(22782 21551)) => "2999-99-99 00:11:22") - - (should (string= (substring-no-properties - (mastodon-tl--byline mastodon-tl-test-base-toot)) - " - | Account 42 (@acct42@example.space) 2999-99-99 00:11:22 - ------------"))))) - (ert-deftest mastodon-tl--byline-boosted () "Should format the boosted toot correctly." - (let* ((mastodon-media-show-avatars-p nil) - (toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) + (let* ((toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -148,8 +131,7 @@ (ert-deftest mastodon-tl--byline-favorited () "Should format the favourited toot correctly." - (let* ((mastodon-media-show-avatars-p nil) - (toot (cons '(favourited . t) mastodon-tl-test-base-toot)) + (let* ((toot (cons '(favourited . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -163,8 +145,7 @@ (ert-deftest mastodon-tl--byline-boosted/favorited () "Should format the boosted & favourited toot correctly." - (let* ((mastodon-media-show-avatars-p nil) - (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) + (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -177,8 +158,7 @@ (ert-deftest mastodon-tl--byline-reblogged () "Should format the reblogged toot correctly." - (let* ((mastodon-media-show-avatars-p nil) - (toot mastodon-tl-test-base-boosted-toot) + (let* ((toot mastodon-tl-test-base-boosted-toot) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) @@ -195,31 +175,9 @@ | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time ------------"))))) -(ert-deftest mastodon-tl--byline-reblogged-with-avatars () - "Should format the reblogged toot correctly." - (let* ((mastodon-media-show-avatars-p t) - (toot mastodon-tl-test-base-boosted-toot) - (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) - (timestamp (cdr (assoc 'created_at toot))) - (original-timestamp (cdr (assoc 'created_at original-toot)))) - (with-mock - ;; We don't expect to use the toot's timestamp but the timestamp of the - ;; reblogged toot: - (stub create-image => '(image "fake data")) - (mock (date-to-time timestamp) => '(1 2)) - (mock (format-time-string mastodon-toot-timestamp-format '(1 2)) => "reblogging time") - (mock (date-to-time original-timestamp) => '(3 4)) - (mock (format-time-string mastodon-toot-timestamp-format '(3 4)) => "original time") - - (should (string= (substring-no-properties (mastodon-tl--byline toot)) - " - | Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time - ------------"))))) - (ert-deftest mastodon-tl--byline-reblogged-boosted/favorited () "Should format the reblogged toot that was also boosted & favoritedcorrectly." - (let* ((mastodon-media-show-avatars-p nil) - (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) + (let* ((toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) (original-timestamp (cdr (assoc 'created_at original-toot)))) -- cgit v1.2.3 From 99f720964dea03651890ee1e2cddff3ab186b962 Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Sat, 13 May 2017 11:00:50 -0400 Subject: Added face customization closes #124 --- lisp/mastodon-tl.el | 19 +++++++++++-------- lisp/mastodon.el | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index b3550c6..c48ca2d 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -109,10 +109,11 @@ Optionally start from POS." (concat (when mastodon-media-show-avatars-p (mastodon-media--get-avatar-rendering avatar-url)) - (propertize name 'face 'warning) - " (@" - handle - ")"))) + (propertize name 'face 'mastodon-display-name-face) + (propertize (concat " (@" + handle + ")") + 'face 'mastodon-handle-face)))) (defun mastodon-tl--byline-boosted (toot) "Add byline for boosted data from TOOT." @@ -120,7 +121,7 @@ Optionally start from POS." (when reblog (concat " " - (propertize "Boosted" 'face 'highlight) + (propertize "Boosted" 'face 'mastodon-boosted-face) " " (mastodon-tl--byline-author reblog))))) @@ -140,9 +141,11 @@ Return value from boosted content if available." (propertize (concat (propertize "\n | " 'face 'default) (when boosted - (format "(%s) " (propertize "B" 'face 'success))) + (format "(%s) " + (propertize "B" 'face 'mastodon-boost-fave-face))) (when faved - (format "(%s) " (propertize "F" 'face 'success))) + (format "(%s) " + (propertize "F" 'face 'mastodon-boost-fave-face))) (mastodon-tl--byline-author toot) (mastodon-tl--byline-boosted toot) " " @@ -172,7 +175,7 @@ also render the html" (message (concat "\n ---------------" "\n Content Warning" "\n ---------------\n")) - (cw (mastodon-tl--set-face message 'success nil))) + (cw (mastodon-tl--set-face message 'mastodon-cw-face nil))) (if (> (length string) 0) (replace-regexp-in-string "\n\n\n ---------------" "\n ---------------" (concat string cw)) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 0dd7f10..92220dc 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -82,6 +82,32 @@ Use. e.g. \"%c\" for your locale's date and time format." :options '(provide-discover-context-menu) :group 'mastodon) +(defcustom mastodon-mode-hook nil + "Hook run when entering Mastodon mode." + :type 'hook + :options '(provide-discover-context-menu) + :group 'mastodon) + +(defface mastodon-handle-face + '((t :inherit default)) + "Face used for user display names.") + +(defface mastodon-display-name-face + '((t :inherit warning)) + "Face used for user display names.") + +(defface mastodon-boosted-face + '((t :inherit highlight :weight bold)) + "Face to indicate that a toot is boosted.") + +(defface mastodon-boost-fave-face + '((t :inherit success)) + "Face to indicate that you have boosted or favourited a toot.") + +(defface mastodon-cw-face + '((t :inherit success)) + "Face used for content warning.") + (defun mastodon-version () "Message package version." (interactive) -- cgit v1.2.3 From 58e88a3c1389546e4eac3be9e3de6ed1060175f2 Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Sat, 13 May 2017 11:03:34 -0400 Subject: removed accidentally duplicated mastodon-mode-hook --- lisp/mastodon.el | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 92220dc..d17eba4 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -76,12 +76,6 @@ Use. e.g. \"%c\" for your locale's date and time format." (defvar mastodon--api-version "v1") -(defcustom mastodon-mode-hook nil - "Hook run when entering Mastodon mode." - :type 'hook - :options '(provide-discover-context-menu) - :group 'mastodon) - (defcustom mastodon-mode-hook nil "Hook run when entering Mastodon mode." :type 'hook -- cgit v1.2.3 From 2f4dc17ac2732e266bbf333132da20e9223cccd8 Mon Sep 17 00:00:00 2001 From: Johnson Denen Date: Mon, 15 May 2017 09:15:54 -0400 Subject: Close #76 * Delete version variable and function * Use package-file --- Cask | 2 +- lisp/mastodon.el | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Cask b/Cask index 263998e..599efa2 100644 --- a/Cask +++ b/Cask @@ -1,7 +1,7 @@ (source gnu) (source melpa) -(package "mastodon" "0.6.2" "Emacs client for Mastodon") +(package-file "lisp/mastodon.el") (files "lisp/*.el") (development diff --git a/lisp/mastodon.el b/lisp/mastodon.el index d17eba4..b41405e 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -38,9 +38,6 @@ :prefix "mastodon-" :group 'external) -(defconst mastodon-version "0.6.2" - "Current `mastodon' package version.") - (defcustom mastodon-instance-url "https://mastodon.social" "Base URL for the Masto instance from which you toot." :group 'mastodon @@ -102,11 +99,6 @@ Use. e.g. \"%c\" for your locale's date and time format." '((t :inherit success)) "Face used for content warning.") -(defun mastodon-version () - "Message package version." - (interactive) - (message "Mastodon version %s" mastodon-version)) - ;;;###autoload (defun mastodon () "Connect Mastodon client to `mastodon-instance-url' instance." @@ -121,7 +113,7 @@ Use. e.g. \"%c\" for your locale's date and time format." If USER is non-nil, insert after @ symbol to begin new toot. If REPLY-TO-ID is non-nil, attach new toot to a conversation." (interactive) - (require 'mastodon-toot nil t) + (require 'mastodon-toot nil t) (mastodon-toot--compose-buffer user reply-to-id)) ;;;###autoload -- cgit v1.2.3 From df9a7194fc22d499d6fb45383fc98e6bb9b35dd6 Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Fri, 5 May 2017 22:02:56 +0100 Subject: Use lexical-binding in all files. We only support Emacs 24 and 25 so are free to use sane bindings by default. To keep the linter happy, we need to declare dependency on Emacs 24 in all files. --- lisp/mastodon-auth.el | 3 ++- lisp/mastodon-client.el | 3 ++- lisp/mastodon-http.el | 2 +- lisp/mastodon-inspect.el | 2 +- lisp/mastodon-media.el | 3 ++- lisp/mastodon-tl.el | 3 ++- lisp/mastodon-toot.el | 3 ++- lisp/mastodon.el | 2 +- 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el index 1ceb652..093c5f1 100644 --- a/lisp/mastodon-auth.el +++ b/lisp/mastodon-auth.el @@ -1,9 +1,10 @@ -;;; mastodon-auth.el --- Auth functions for mastodon.el +;;; mastodon-auth.el --- Auth functions for mastodon.el -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen ;; Version: 0.6.2 ;; Homepage: https://github.com/jdenen/mastodon.el +;; Package-Requires: ((emacs "24.4")) ;; This file is not part of GNU Emacs. diff --git a/lisp/mastodon-client.el b/lisp/mastodon-client.el index 8097bf5..ebfb844 100644 --- a/lisp/mastodon-client.el +++ b/lisp/mastodon-client.el @@ -1,9 +1,10 @@ -;;; mastodon-client.el --- Client functions for mastodon.el +;;; mastodon-client.el --- Client functions for mastodon.el -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen ;; Version: 0.6.2 ;; Homepage: https://github.com/jdenen/mastodon.el +;; Package-Requires: ((emacs "24.4")) ;; This file is not part of GNU Emacs. diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index eb70a88..48be898 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -1,4 +1,4 @@ -;;; mastodon-http.el --- HTTP request/response functions for mastodon.el +;;; mastodon-http.el --- HTTP request/response functions for mastodon.el -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index 9ae5049..97d7660 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -1,4 +1,4 @@ -;;; mastodon-inspect.el --- Client for Mastodon +;;; mastodon-inspect.el --- Client for Mastodon -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index d1ec871..04293c5 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -1,9 +1,10 @@ -;;; mastodon-media.el --- Functions for inlining Mastodon media +;;; mastodon-media.el --- Functions for inlining Mastodon media -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen ;; Version: 0.6.2 ;; Homepage: https://github.com/jdenen/mastodon.el +;; Package-Requires: ((emacs "24.4")) ;; This file is not part of GNU Emacs. diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index c48ca2d..5e09cf1 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -1,9 +1,10 @@ -;;; mastodon-tl.el --- HTTP request/response functions for mastodon.el +;;; mastodon-tl.el --- HTTP request/response functions for mastodon.el -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen ;; Version: 0.6.2 ;; Homepage: https://github.com/jdenen/mastodon.el +;; Package-Requires: ((emacs "24.4")) ;; This file is not part of GNU Emacs. diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index 230277c..ace7d70 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -1,9 +1,10 @@ -;;; mastodon-toot.el --- Minor mode for sending Mastodon toots +;;; mastodon-toot.el --- Minor mode for sending Mastodon toots -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen ;; Version: 0.6.2 ;; Homepage: https://github.com/jdenen/mastodon.el +;; Package-Requires: ((emacs "24.4")) ;; This file is not part of GNU Emacs. diff --git a/lisp/mastodon.el b/lisp/mastodon.el index b41405e..2b8f1ea 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -1,4 +1,4 @@ -;;; mastodon.el --- Client for Mastodon +;;; mastodon.el --- Client for Mastodon -*- lexical-binding: t -*- ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -- cgit v1.2.3 From 23c233d2bb2cbc9139087b53d95cd3b23e05e336 Mon Sep 17 00:00:00 2001 From: Johnson Denen Date: Mon, 15 May 2017 09:28:14 -0400 Subject: Bump to v0.6.3 --- lisp/mastodon-auth.el | 2 +- lisp/mastodon-client.el | 2 +- lisp/mastodon-http.el | 2 +- lisp/mastodon-inspect.el | 2 +- lisp/mastodon-media.el | 2 +- lisp/mastodon-tl.el | 2 +- lisp/mastodon-toot.el | 2 +- lisp/mastodon.el | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el index 093c5f1..013487c 100644 --- a/lisp/mastodon-auth.el +++ b/lisp/mastodon-auth.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-client.el b/lisp/mastodon-client.el index ebfb844..f8e8a5c 100644 --- a/lisp/mastodon-client.el +++ b/lisp/mastodon-client.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index 48be898..dbbad4f 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index 97d7660..c12273e 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 04293c5..b3565d0 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 5e09cf1..42cb252 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index ace7d70..fc31a5b 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 2b8f1ea..b75c608 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.2 +;; Version: 0.6.3 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el -- cgit v1.2.3 From 38d3b3c1850b38129c097ac316971ba34a8c4245 Mon Sep 17 00:00:00 2001 From: Johnson Denen Date: Mon, 15 May 2017 10:24:58 -0400 Subject: Add Matrix notification to Travis --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index c7e0516..5f5796c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,3 +13,10 @@ script: - cask clean-elc - cask exec ert-runner -l test/ert-helper.el test/*-tests.el - cask emacs --batch -Q -l package-lint.el -f package-lint-batch-and-exit lisp/*.el +notifications: + webhooks: + urls: + - "https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MGpvaG5zb24lM0FtYXRyaXgub3JnLyUyMVpSbGVnVEFCTHBTQmJ2c01tTiUzQW1hdHJpeC5vcmc" + on_success: always # always|never|change + on_failure: always + on_start: never -- cgit v1.2.3 From 588c07b170549762713e6c920c21c3a8396cbfba Mon Sep 17 00:00:00 2001 From: Johnson Denen Date: Mon, 15 May 2017 12:45:18 -0400 Subject: Fix #131 with refined regexp pattern --- lisp/mastodon-tl.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 85e0519..b48c8fe 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -211,7 +211,7 @@ also render the html" (insert (concat (mastodon-tl--spoiler toot) - (replace-regexp-in-string "\n*$" "" (mastodon-tl--content toot)) + (replace-regexp-in-string "^\n+$" "" (mastodon-tl--content toot)) (mastodon-tl--media toot) "\n\n" (mastodon-tl--byline toot) -- cgit v1.2.3 From 72b2688030b8e1e2c81d1a9772aad35d1bf8ec7f Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Tue, 16 May 2017 00:41:43 -0400 Subject: fixed #133 replaced regex with a trim function --- lisp/mastodon-tl.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index b48c8fe..b00622b 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -211,7 +211,8 @@ also render the html" (insert (concat (mastodon-tl--spoiler toot) - (replace-regexp-in-string "^\n+$" "" (mastodon-tl--content toot)) + ;; remove two trailing newlines + (substring (mastodon-tl--content toot) 0 -2) (mastodon-tl--media toot) "\n\n" (mastodon-tl--byline toot) -- cgit v1.2.3 From 626e0e3ba4f435ea1c13507f2d4f66aee96aa8f8 Mon Sep 17 00:00:00 2001 From: Alexander Griffith Date: Thu, 11 May 2017 19:38:16 -0400 Subject: Fix for #84 changed more-json in mastodon-tl-tests to recieve an endpont more evocative names and fixed whitespace replaced remaining end eith endpoint-plist added basic unit tests for endpoint selection replaced equalp with equal in tests --- lisp/mastodon-tl.el | 88 ++++++++++++++++++++++++++++++++++++++--------- test/mastodon-tl-tests.el | 46 ++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 17 deletions(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index b00622b..2361b93 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -224,22 +224,72 @@ also render the html" (replace-regexp "\n\n\n | " "\n | " nil (point-min) (point-max)) (mastodon-media--inline-images)) -(defun mastodon-tl--more-json (timeline id) - "Return JSON for TIMELINE before ID." - (let ((url (mastodon-http--api (concat "timelines/" - timeline - "?max_id=" - (number-to-string id))))) +(defun mastodon-tl--get-endpoint (buffer-string) + "Match BUFFER-STRING against buffer-names. + +Returns the matching plist" + (let* ((mastodon-buffer-endpoints ;; can be made a global var + '((buffer-name "home" endpoint "timelines/home" + update-function mastodon-tl--timeline) + (buffer-name "local" endpoint "timelines/public?local=true" + update-function mastodon-tl--timeline) + (buffer-name "federated" endpoint "timelines/public" + update-function mastodon-tl--timeline) + (buffer-name "public?local=true" + endpoint "timelines/public?local=true" + update-function mastodon-tl--timeline) + (buffer-name "public" + endpoint "timelines/public" + update-function mastodon-tl--timeline) + (buffer-name "tag/.*" + endpoint (lambda (x) (concat "timelines/" x)) + update-function mastodon-tl--timeline + endpoint-function) + (buffer-name "notifications" + endpoint "notifications" + update-function + mastodon-notifications--notifications))) + (match-endpoint-string + (lambda (endpoint-plist) + (when (string-match + (plist-get endpoint-plist 'buffer-name) buffer-string) + (unless (functionp + (plist-get endpoint-plist 'update-function)) + (error + (format "%s not a function" + (plist-get endpoint-plist 'update-function)))) + (cond + ((stringp (plist-get endpoint-plist 'endpoint)) + endpoint-plist) + ((functionp (plist-get endpoint-plist 'endpoint)) + (plist-put endpoint-plist 'endpoint + (funcall (plist-get endpoint-plist 'endpoint) + buffer-string))) + (t (error + (format "%s not a string or function" + (plist-get endpoint-plist 'endpoint)))))))) + (endpoint (remove-if 'null + (mapcar match-endpoint-string + mastodon-buffer-endpoints)))) + (or (car endpoint) + (error "%s is not a valid mastodon endpoint" string)))) + +(defun mastodon-tl--more-json (endpoint id) + "Return JSON for timeline ENDPOINT before ID." + (let* ((url (mastodon-http--api (concat + endpoint + "?max_id=" + (number-to-string id))))) (mastodon-http--get-json url))) ;; TODO ;; Look into the JSON returned here by Local -(defun mastodon-tl--updated-json (timeline id) - "Return JSON for TIMELINE since ID." - (let ((url (mastodon-http--api (concat "timelines/" - timeline - "?since_id=" - (number-to-string id))))) +(defun mastodon-tl--updated-json (endpoint id) + "Return JSON for timeline ENDPOINT since ID." + (let ((url (mastodon-http--api (concat + endpoint + "?since_id=" + (number-to-string id))))) (mastodon-http--get-json url))) (defun mastodon-tl--property (prop &optional backward) @@ -284,13 +334,16 @@ Move forward (down) the timeline unless BACKWARD is non-nil." (interactive) (let* ((point-before (point)) (tl (mastodon-tl--timeline-name)) + (endpoint-plist (mastodon-tl--get-endpoint tl)) + (endpoint (plist-get endpoint-plist 'endpoint)) + (update-function (plist-get endpoint-plist 'update-function)) (id (mastodon-tl--oldest-id)) - (json (mastodon-tl--more-json tl id))) + (json (mastodon-tl--more-json endpoint id))) (when json (with-current-buffer (current-buffer) (let ((inhibit-read-only t)) (goto-char (point-max)) - (mastodon-tl--timeline json) + (funcall update-function json) (goto-char point-before) (mastodon-tl--goto-next-toot)))))) @@ -298,13 +351,16 @@ Move forward (down) the timeline unless BACKWARD is non-nil." "Update timeline with new toots." (interactive) (let* ((tl (mastodon-tl--timeline-name)) + (endpoint-plist (mastodon-tl--get-endpoint tl)) + (endpoint (plist-get endpoint-plist 'endpoint)) + (update-function (plist-get endpoint-plist 'update-function)) (id (mastodon-tl--newest-id)) - (json (mastodon-tl--updated-json tl id))) + (json (mastodon-tl--updated-json endpoint id))) (when json (with-current-buffer (current-buffer) (let ((inhibit-read-only t)) (goto-char (point-min)) - (mastodon-tl--timeline json)))))) + (funcall update-function json)))))) (defun mastodon-tl--get (timeline) "Display TIMELINE in buffer." diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index a91d6d5..3fb2c3b 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -101,7 +101,7 @@ (let ((mastodon-instance-url "https://instance.url")) (with-mock (mock (mastodon-http--get-json "https://instance.url/api/v1/timelines/foo?max_id=12345")) - (mastodon-tl--more-json "foo" 12345)))) + (mastodon-tl--more-json "timelines/foo" 12345)))) (ert-deftest mastodon-tl--byline-regular () "Should format the regular toot correctly." @@ -236,3 +236,47 @@ | (B) (F) Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time ------------"))))) +(ert-deftest mastodon-tl--endpoint-notifications () + "Should return the appropriate endpoint string and update function format + +notifications." + (should + (and + (equal + 'mastodon-notifications--notifications + (plist-get (mastodon-tl--get-endpoint "notifications") + 'update-function)) + (equal + "notifications" + (plist-get (mastodon-tl--get-endpoint "notifications") + 'endpoint))))) + +(ert-deftest mastodon-tl--endpoint-tag () + "Should return the appropriate endpoint string and update function for + +tags." + (should + (and + (equal + 'mastodon-tl--timeline + (plist-get (mastodon-tl--get-endpoint "tag/test") + 'update-function)) + (equal + "timelines/tag/test" + (plist-get (mastodon-tl--get-endpoint "tag/test") + 'endpoint))))) + +(ert-deftest mastodon-tl--endpoint-local () + "Should return the appropriate endpoint string and update function for + +the local timeline." + (should + (and + (equal + 'mastodon-tl--timeline + (plist-get (mastodon-tl--get-endpoint "public?local=true") + 'update-function)) + (equal + "timelines/public?local=true" + (plist-get (mastodon-tl--get-endpoint "public?local=true") + 'endpoint))))) -- cgit v1.2.3 From 5f41086d3a03e8781aab77ab17f4d4f95263e07c Mon Sep 17 00:00:00 2001 From: alexjgriffith Date: Mon, 15 May 2017 16:20:25 -0400 Subject: Putting buffer name and update function in buffer local var removed tests for previous version documented mastodon-buffer-spec getters moved mastodon-buffer-spec to mastodon.el combined mastodon-tl--get and mastodon-tl--init, along with other cleanups --- lisp/mastodon-tl.el | 175 ++++++++++++++++++++-------------------------- lisp/mastodon.el | 5 +- test/mastodon-tl-tests.el | 44 ------------ 3 files changed, 80 insertions(+), 144 deletions(-) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 2361b93..5b06119 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -42,17 +42,20 @@ (defun mastodon-tl--get-federated-timeline () "Opens federated timeline." (interactive) - (mastodon-tl--get "public")) + (mastodon-tl--init + "federated" "timelines/public" 'mastodon-tl--timeline)) (defun mastodon-tl--get-home-timeline () "Opens home timeline." (interactive) - (mastodon-tl--get "home")) + (mastodon-tl--init + "home" "timelines/home" 'mastodon-tl--timeline)) (defun mastodon-tl--get-local-timeline () "Opens local timeline." (interactive) - (mastodon-tl--get "public?local=true")) + (mastodon-tl--init + "local" "timelines/public?local=true" 'mastodon-tl--timeline)) (defun mastodon-tl--get-tag-timeline () "Prompts for tag and opens its timeline." @@ -60,8 +63,8 @@ (let* ((word (or (word-at-point) "")) (input (read-string (format "Tag(%s): " word))) (tag (if (equal input "") word input))) - (print tag) - (mastodon-tl--get (concat "tag/" tag)))) + (mastodon-tl--init + (concat "tag-" tag) (concat "timelines/tag/" tag) 'mastodon-tl--timeline))) (defun mastodon-tl--goto-toot-pos (find-pos refresh &optional pos) "Search for toot with FIND-POS. @@ -90,11 +93,6 @@ Optionally start from POS." (mastodon-tl--goto-toot-pos 'previous-single-property-change 'mastodon-tl--update)) -(defun mastodon-tl--timeline-name () - "Determine timeline from `buffer-name'." - (replace-regexp-in-string "\*" "" - (replace-regexp-in-string "mastodon-" "" (buffer-name)))) - (defun mastodon-tl--remove-html (toot) "Remove unrendered tags from TOOT." (let* ((t1 (replace-regexp-in-string "<\/p>" "\n\n" toot)) @@ -112,9 +110,9 @@ Optionally start from POS." (mastodon-media--get-avatar-rendering avatar-url)) (propertize name 'face 'mastodon-display-name-face) (propertize (concat " (@" - handle - ")") - 'face 'mastodon-handle-face)))) + handle + ")") + 'face 'mastodon-handle-face)))) (defun mastodon-tl--byline-boosted (toot) "Add byline for boosted data from TOOT." @@ -143,10 +141,10 @@ Return value from boosted content if available." (concat (propertize "\n | " 'face 'default) (when boosted (format "(%s) " - (propertize "B" 'face 'mastodon-boost-fave-face))) + (propertize "B" 'face 'mastodon-boost-fave-face))) (when faved (format "(%s) " - (propertize "F" 'face 'mastodon-boost-fave-face))) + (propertize "F" 'face 'mastodon-boost-fave-face))) (mastodon-tl--byline-author toot) (mastodon-tl--byline-boosted toot) " " @@ -186,12 +184,12 @@ also render the html" "Retrieve a media attachment link for TOOT if one exists." (let* ((media-attachements (mastodon-tl--field 'media_attachments toot)) (media-string (mapconcat - (lambda (media-attachement) - (let ((preview-url - (cdr (assoc 'preview_url media-attachement)))) - (mastodon-media--get-media-link-rendering - preview-url))) - media-attachements ""))) + (lambda (media-attachement) + (let ((preview-url + (cdr (assoc 'preview_url media-attachement)))) + (mastodon-media--get-media-link-rendering + preview-url))) + media-attachements ""))) (if (not (equal media-string "")) (concat "\n" media-string ) ""))) @@ -224,62 +222,35 @@ also render the html" (replace-regexp "\n\n\n | " "\n | " nil (point-min) (point-max)) (mastodon-media--inline-images)) -(defun mastodon-tl--get-endpoint (buffer-string) - "Match BUFFER-STRING against buffer-names. - -Returns the matching plist" - (let* ((mastodon-buffer-endpoints ;; can be made a global var - '((buffer-name "home" endpoint "timelines/home" - update-function mastodon-tl--timeline) - (buffer-name "local" endpoint "timelines/public?local=true" - update-function mastodon-tl--timeline) - (buffer-name "federated" endpoint "timelines/public" - update-function mastodon-tl--timeline) - (buffer-name "public?local=true" - endpoint "timelines/public?local=true" - update-function mastodon-tl--timeline) - (buffer-name "public" - endpoint "timelines/public" - update-function mastodon-tl--timeline) - (buffer-name "tag/.*" - endpoint (lambda (x) (concat "timelines/" x)) - update-function mastodon-tl--timeline - endpoint-function) - (buffer-name "notifications" - endpoint "notifications" - update-function - mastodon-notifications--notifications))) - (match-endpoint-string - (lambda (endpoint-plist) - (when (string-match - (plist-get endpoint-plist 'buffer-name) buffer-string) - (unless (functionp - (plist-get endpoint-plist 'update-function)) - (error - (format "%s not a function" - (plist-get endpoint-plist 'update-function)))) - (cond - ((stringp (plist-get endpoint-plist 'endpoint)) - endpoint-plist) - ((functionp (plist-get endpoint-plist 'endpoint)) - (plist-put endpoint-plist 'endpoint - (funcall (plist-get endpoint-plist 'endpoint) - buffer-string))) - (t (error - (format "%s not a string or function" - (plist-get endpoint-plist 'endpoint)))))))) - (endpoint (remove-if 'null - (mapcar match-endpoint-string - mastodon-buffer-endpoints)))) - (or (car endpoint) - (error "%s is not a valid mastodon endpoint" string)))) +(defun mastodon-tl--get-update-function (&optional buffer) + "Get the UPDATE-FUNCTION stored in `mastodon-buffer-spec'" + (mastodon-tl--get-buffer-property 'update-function buffer)) + +(defun mastodon-tl--get-endpoint (&optional buffer) + "Get the ENDPOINT stored in `mastodon-buffer-spec'" + (mastodon-tl--get-buffer-property 'endpoint buffer)) + +(defun mastodon-tl--buffer-name (&optional buffer) + "Get the BUFFER-NAME stored in `mastodon-buffer-spec'" + (mastodon-tl--get-buffer-property 'buffer-name buffer )) + +(defun mastodon-tl--get-buffer-property (property &optional buffer) + "Get `MASTODON-BUFFER-SPEC' in BUFFER or `CURRENT-BUFFER'" + (with-current-buffer (or buffer (current-buffer)) + (if (plist-get mastodon-buffer-spec property) + (plist-get mastodon-buffer-spec property) + (error "mastodon-buffer-spec is not defined for buffer %s" + (or buffer (current-buffer)))))) (defun mastodon-tl--more-json (endpoint id) "Return JSON for timeline ENDPOINT before ID." (let* ((url (mastodon-http--api (concat endpoint - "?max_id=" - (number-to-string id))))) + (if (string-match-p "?" endpoint) + "&" + "?") + "max_id=" + (number-to-string id))))) (mastodon-http--get-json url))) ;; TODO @@ -288,7 +259,10 @@ Returns the matching plist" "Return JSON for timeline ENDPOINT since ID." (let ((url (mastodon-http--api (concat endpoint - "?since_id=" + (if (string-match-p "?" endpoint) + "&" + "?") + "since_id=" (number-to-string id))))) (mastodon-http--get-json url))) @@ -329,48 +303,51 @@ Move forward (down) the timeline unless BACKWARD is non-nil." (cdr (assoc 'descendants context))))) (mastodon-mode))) -(defun mastodon-tl--more () +(defun mastodon-tl--more (&optional buffer) "Append older toots to timeline." (interactive) (let* ((point-before (point)) - (tl (mastodon-tl--timeline-name)) - (endpoint-plist (mastodon-tl--get-endpoint tl)) - (endpoint (plist-get endpoint-plist 'endpoint)) - (update-function (plist-get endpoint-plist 'update-function)) + (endpoint (mastodon-tl--get-endpoint)) + (update-function (mastodon-tl--get-update-function)) (id (mastodon-tl--oldest-id)) (json (mastodon-tl--more-json endpoint id))) (when json - (with-current-buffer (current-buffer) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (funcall update-function json) - (goto-char point-before) - (mastodon-tl--goto-next-toot)))))) + (let ((inhibit-read-only t)) + (goto-char (point-max)) + (funcall update-function json) + (goto-char point-before))))) (defun mastodon-tl--update () "Update timeline with new toots." (interactive) - (let* ((tl (mastodon-tl--timeline-name)) - (endpoint-plist (mastodon-tl--get-endpoint tl)) - (endpoint (plist-get endpoint-plist 'endpoint)) - (update-function (plist-get endpoint-plist 'update-function)) + (let* ((endpoint (mastodon-tl--get-endpoint)) + (update-function (mastodon-tl--get-update-function)) (id (mastodon-tl--newest-id)) (json (mastodon-tl--updated-json endpoint id))) (when json - (with-current-buffer (current-buffer) - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (funcall update-function json)))))) - -(defun mastodon-tl--get (timeline) - "Display TIMELINE in buffer." - (let* ((url (mastodon-http--api (concat "timelines/" timeline))) - (buffer (concat "*mastodon-" timeline "*")) + (let ((inhibit-read-only t)) + (goto-char (point-min)) + (funcall update-function json))))) + + +(defun mastodon-tl--init (buffer-name endpoint update-function) + "Initialize BUFFER-NAME with timeline targeted by ENDPOINT. + +UPDATE-FUNCTION is used to recieve more toots." + (let* ((url (mastodon-http--api endpoint)) + (buffer (concat "*mastodon-" buffer-name "*")) (json (mastodon-http--get-json url))) (with-output-to-temp-buffer buffer (switch-to-buffer buffer) - (mastodon-tl--timeline json)) - (mastodon-mode))) + (funcall update-function json)) + (mastodon-mode) + (with-current-buffer buffer + (make-local-variable 'mastodon-buffer-spec) + (setq mastodon-buffer-spec + `(buffer-name ,buffer-name + endpoint ,endpoint update-function + ,update-function))) + buffer)) (provide 'mastodon-tl) ;;; mastodon-tl.el ends here diff --git a/lisp/mastodon.el b/lisp/mastodon.el index b75c608..a0dd732 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -73,6 +73,9 @@ Use. e.g. \"%c\" for your locale's date and time format." (defvar mastodon--api-version "v1") +(defvar mastodon-buffer-spec nil + "A unique identifier and functions for each Mastodon buffer.") + (defcustom mastodon-mode-hook nil "Hook run when entering Mastodon mode." :type 'hook @@ -104,7 +107,7 @@ Use. e.g. \"%c\" for your locale's date and time format." "Connect Mastodon client to `mastodon-instance-url' instance." (interactive) (require 'mastodon-tl nil t) - (mastodon-tl--get "home")) + (mastodon-tl--get-home-timeline)) ;;;###autoload (defun mastodon-toot (&optional user reply-to-id) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index 3fb2c3b..caea476 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -236,47 +236,3 @@ | (B) (F) Account 42 (@acct42@example.space) Boosted Account 43 (@acct43@example.space) original time ------------"))))) -(ert-deftest mastodon-tl--endpoint-notifications () - "Should return the appropriate endpoint string and update function format - -notifications." - (should - (and - (equal - 'mastodon-notifications--notifications - (plist-get (mastodon-tl--get-endpoint "notifications") - 'update-function)) - (equal - "notifications" - (plist-get (mastodon-tl--get-endpoint "notifications") - 'endpoint))))) - -(ert-deftest mastodon-tl--endpoint-tag () - "Should return the appropriate endpoint string and update function for - -tags." - (should - (and - (equal - 'mastodon-tl--timeline - (plist-get (mastodon-tl--get-endpoint "tag/test") - 'update-function)) - (equal - "timelines/tag/test" - (plist-get (mastodon-tl--get-endpoint "tag/test") - 'endpoint))))) - -(ert-deftest mastodon-tl--endpoint-local () - "Should return the appropriate endpoint string and update function for - -the local timeline." - (should - (and - (equal - 'mastodon-tl--timeline - (plist-get (mastodon-tl--get-endpoint "public?local=true") - 'update-function)) - (equal - "timelines/public?local=true" - (plist-get (mastodon-tl--get-endpoint "public?local=true") - 'endpoint))))) -- cgit v1.2.3 From fe8e4386eacb358df0e16dc5bd37dde4f4d6d57c Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Tue, 16 May 2017 21:09:58 +0100 Subject: Remove most byte-compile warnings. We do this by - moving vars into the files where they are (mostly) used - "declaring" vars used elsewhere with the (defvar ) pattern, - declaring functions defined in others functions rather than loading the file via require. --- lisp/mastodon-auth.el | 5 ++++- lisp/mastodon-client.el | 30 +++++++++++++++-------------- lisp/mastodon-http.el | 11 ++++++----- lisp/mastodon-inspect.el | 8 ++++++-- lisp/mastodon-media.el | 25 +++++++++++++----------- lisp/mastodon-tl.el | 45 ++++++++++++++++++++++++++++--------------- lisp/mastodon-toot.el | 32 +++++++++++++++--------------- lisp/mastodon.el | 38 ++++++++++++++---------------------- test/mastodon-client-tests.el | 10 +++++----- test/mastodon-media-tests.el | 20 +++++++++---------- test/mastodon-tl-tests.el | 16 +++++++-------- 11 files changed, 130 insertions(+), 110 deletions(-) diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el index 013487c..68c4d21 100644 --- a/lisp/mastodon-auth.el +++ b/lisp/mastodon-auth.el @@ -30,7 +30,10 @@ ;;; Code: (require 'plstore) -(require 'mastodon-client nil t) + +(declare-function mastodon-client "mastodon-client") +(declare-function mastodon-http--post "mastodon-http") +(defvar mastodon-instance-url) (defgroup mastodon-auth nil "Authenticate with Mastodon." diff --git a/lisp/mastodon-client.el b/lisp/mastodon-client.el index f8e8a5c..3a9a606 100644 --- a/lisp/mastodon-client.el +++ b/lisp/mastodon-client.el @@ -30,14 +30,16 @@ ;;; Code: (require 'plstore) -(require 'mastodon-http nil t) +(declare-function mastodon-http--api "mastodon-http") +(declare-function mastodon-http--post "mastodon-http") -(defgroup mastodon-client nil - "Register your client with Mastodon." - :prefix "mastodon-client-" - :group 'mastodon) -(defvar mastodon-client nil +(defcustom mastodon-client--token-file (concat user-emacs-directory "mastodon.plstore") + "File path where Mastodon access tokens are stored." + :group 'mastodon + :type 'file) + +(defvar mastodon-client--client-details nil "Client id and secret.") (defun mastodon-client--register () @@ -62,11 +64,11 @@ (json-read-from-string json-string)))) (defun mastodon-client--token-file () - "Return `mastodon-token-file'." - mastodon-token-file) + "Return `mastodon-client--token-file'." + mastodon-client--token-file) (defun mastodon-client--store () - "Store client_id and client_secret in `mastodon-token-file'. + "Store client_id and client_secret in `mastodon-client--token-file'. Make `mastodon-client--fetch' call to determine client values." (let ((plstore (plstore-open (mastodon-client--token-file))) @@ -77,19 +79,19 @@ Make `mastodon-client--fetch' call to determine client values." client)) (defun mastodon-client--read () - "Retrieve client_id and client_secret from `mastodon-token-file'." + "Retrieve client_id and client_secret from `mastodon-client--token-file'." (let* ((plstore (plstore-open (mastodon-client--token-file))) (mastodon (plstore-get plstore "mastodon"))) (when mastodon (delete "mastodon" mastodon)))) (defun mastodon-client () - "Return variable `mastodon-client' plist. + "Return variable `mastodon-client--client-details' plist. -Read plist from `mastodon-token-file' if variable is nil. +Read plist from `mastodon-client--token-file' if variable is nil. Fetch and store plist if `mastodon-client--read' returns nil." - (or mastodon-client - (setq mastodon-client + (or mastodon-client--client-details + (setq mastodon-client--client-details (or (mastodon-client--read) (mastodon-client--store))))) diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index dbbad4f..9f178c4 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -30,15 +30,16 @@ ;;; Code: (require 'json) +(defvar mastodon-instance-url) +(defvar mastodon-auth--token) +(declare-function mastodon-auth--access-token "mastodon-auth") -(defgroup mastodon-http nil - "HTTP requests and responses for Mastodon." - :prefix "mastodon-http-" - :group 'mastodon) +(defvar mastodon-http--api-version "v1") (defun mastodon-http--api (endpoint) "Return Mastondon API URL for ENDPOINT." - (concat mastodon-instance-url "/api/" mastodon--api-version "/" endpoint)) + (concat mastodon-instance-url "/api/" + mastodon-http--api-version "/" endpoint)) (defun mastodon-http--response () "Capture response buffer content as string." diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index c12273e..f5fcd81 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -28,8 +28,12 @@ ;; Some tools to help inspect / debug mastodon.el ;;; Code: - -(require 'mastodon-tl nil t) +(declare-function mastodon-http--api "mastodon-http") +(declare-function mastodon-http--get-json "mastodon-http") +(declare-function mastodon-media--inline-images "mastodon-media") +(declare-function mastodon-mode "mastodon") +(declare-function mastodon-tl--property "mastodon-tl") +(declare-function mastodon-tl--toot "mastodon-tl") (defgroup mastodon-inspect nil "Tools to help inspect toots." diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index b3565d0..671f159 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -32,16 +32,20 @@ ;; required by the server and client. ;;; Code: -(require 'mastodon-http nil t) - (defgroup mastodon-media nil "Inline Mastadon media." :prefix "mastodon-media-" :group 'mastodon) -(defvar mastodon-media-show-avatars-p - (image-type-available-p 'imagemagick) - "A boolean value stating whether to show avatars in timelines.") +(defcustom mastodon-media--avatar-height 30 + "Height of the user avatar images (if shown)." + :group 'mastodon-media + :type 'integer) + +(defcustom mastodon-media--preview-max-height 250 + "Max height of any media attachment preview to be shown." + :group 'mastodon-media + :type 'integer) (defvar mastodon-media--generic-avatar-data (base64-decode-string @@ -121,14 +125,13 @@ BAIQCEAgAIEABAIsJVH58WqHw8FIgjUIQCAACAQgEIBAAAIBCAQgEIBAAAIBCAQgEAAEAhAIQCBA fKRJkmVZjAQwh78A6vCRWJE8K+8AAAAASUVORK5CYII=") "The PNG data for a generic 200x200 'broken image' view") -(defun mastodon-media--process-image-response (status-plist marker image-options region-length image-url) +(defun mastodon-media--process-image-response (status-plist marker image-options region-length) "Callback function processing the url retrieve response for URL. STATUS-PLIST is the usual plist of status events as per `url-retrieve'. IMAGE-OPTIONS are the precomputed options to apply to the image. MARKER is the marker to where the response should be visible. REGION-LENGTH is the length of the region that should be replaced with the image. -IMAGE-URL is the URL that was retrieved. " (let ((url-buffer (current-buffer)) (is-error-response-p (eq :error (car status-plist)))) @@ -164,12 +167,12 @@ MEDIA-TYPE is a symbol and either 'avatar or 'media-link." (let ((image-options (when (image-type-available-p 'imagemagick) (cond ((eq media-type 'avatar) - `(:height ,mastodon-avatar-height)) + `(:height ,mastodon-media--avatar-height)) ((eq media-type 'media-link) - `(:max-height ,mastodon-preview-max-height)))))) + `(:max-height ,mastodon-media--preview-max-height)))))) (url-retrieve url #'mastodon-media--process-image-response - (list (copy-marker start) image-options region-length url)))) + (list (copy-marker start) image-options region-length)))) (defun mastodon-media--select-next-media-line () "Find coordinates of the next media to load. @@ -225,7 +228,7 @@ not been returned." ;; This is what a user will see on a non-graphical display ;; where not showing an avatar at all is preferable. (let ((image-options (when (image-type-available-p 'imagemagick) - `(:height ,mastodon-avatar-height)))) + `(:height ,mastodon-media--avatar-height)))) (concat (propertize " " 'media-url avatar-url diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 5b06119..edefae9 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -29,16 +29,31 @@ ;;; Code: -(require 'mastodon-http) -(require 'mastodon-toot) -(require 'mastodon-media) +(require 'shr) +(require 'thingatpt) ;; for word-at-point (require 'time-date) +(declare-function mastodon-http--api "mastodon-http") +(declare-function mastodon-http--get-json "mastodon-http") +(declare-function mastodon-media--get-avatar-rendering "mastodon-media") +(declare-function mastodon-media--get-media-link-rendering "mastodon-media") +(declare-function mastodon-media--inline-images "mastodon-media") +(declare-function mastodon-mode "mastodon") +(defvar mastodon-toot-timestamp-format) + (defgroup mastodon-tl nil "Timelines in Mastodon." :prefix "mastodon-tl-" :group 'mastodon) +(defvar mastodon-tl--buffer-spec nil + "A unique identifier and functions for each Mastodon buffer.") + +(defvar mastodon-tl--show-avatars-p + (image-type-available-p 'imagemagick) + "A boolean value stating whether to show avatars in timelines.") + + (defun mastodon-tl--get-federated-timeline () "Opens federated timeline." (interactive) @@ -106,7 +121,7 @@ Optionally start from POS." (name (cdr (assoc 'display_name account))) (avatar-url (cdr (assoc 'avatar account)))) (concat - (when mastodon-media-show-avatars-p + (when mastodon-tl--show-avatars-p (mastodon-media--get-avatar-rendering avatar-url)) (propertize name 'face 'mastodon-display-name-face) (propertize (concat " (@" @@ -218,28 +233,28 @@ also render the html" (defun mastodon-tl--timeline (toots) "Display each toot in TOOTS." - (mapcar 'mastodon-tl--toot toots) + (mapc 'mastodon-tl--toot toots) (replace-regexp "\n\n\n | " "\n | " nil (point-min) (point-max)) (mastodon-media--inline-images)) (defun mastodon-tl--get-update-function (&optional buffer) - "Get the UPDATE-FUNCTION stored in `mastodon-buffer-spec'" + "Get the UPDATE-FUNCTION stored in `mastodon-tl--buffer-spec'" (mastodon-tl--get-buffer-property 'update-function buffer)) (defun mastodon-tl--get-endpoint (&optional buffer) - "Get the ENDPOINT stored in `mastodon-buffer-spec'" + "Get the ENDPOINT stored in `mastodon-tl--buffer-spec'" (mastodon-tl--get-buffer-property 'endpoint buffer)) (defun mastodon-tl--buffer-name (&optional buffer) - "Get the BUFFER-NAME stored in `mastodon-buffer-spec'" + "Get the BUFFER-NAME stored in `mastodon-tl--buffer-spec'" (mastodon-tl--get-buffer-property 'buffer-name buffer )) (defun mastodon-tl--get-buffer-property (property &optional buffer) - "Get `MASTODON-BUFFER-SPEC' in BUFFER or `CURRENT-BUFFER'" + "Get `MASTODON-TL--BUFFER-SPEC' in BUFFER or `CURRENT-BUFFER'" (with-current-buffer (or buffer (current-buffer)) - (if (plist-get mastodon-buffer-spec property) - (plist-get mastodon-buffer-spec property) - (error "mastodon-buffer-spec is not defined for buffer %s" + (if (plist-get mastodon-tl--buffer-spec property) + (plist-get mastodon-tl--buffer-spec property) + (error "mastodon-tl--buffer-spec is not defined for buffer %s" (or buffer (current-buffer)))))) (defun mastodon-tl--more-json (endpoint id) @@ -303,7 +318,7 @@ Move forward (down) the timeline unless BACKWARD is non-nil." (cdr (assoc 'descendants context))))) (mastodon-mode))) -(defun mastodon-tl--more (&optional buffer) +(defun mastodon-tl--more () "Append older toots to timeline." (interactive) (let* ((point-before (point)) @@ -342,8 +357,8 @@ UPDATE-FUNCTION is used to recieve more toots." (funcall update-function json)) (mastodon-mode) (with-current-buffer buffer - (make-local-variable 'mastodon-buffer-spec) - (setq mastodon-buffer-spec + (make-local-variable 'mastodon-tl--buffer-spec) + (setq mastodon-tl--buffer-spec `(buffer-name ,buffer-name endpoint ,endpoint update-function ,update-function))) diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index fc31a5b..ffe6454 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -29,16 +29,26 @@ ;;; Code: -(require 'mastodon-auth nil t) - -(defgroup mastodon-toot nil - "Capture Mastodon toots." - :prefix "mastodon-toot-" - :group 'mastodon) - (defvar mastodon-toot--reply-to-id nil) (defvar mastodon-toot--content-warning nil) +(declare-function mastodon-http--api "mastodon-http") +(declare-function mastodon-http--post "mastodon-http") +(declare-function mastodon-http--triage "mastodon-http") +(declare-function mastodon-tl--field "mastodon-tl") +(declare-function mastodon-tl--goto-next-toot "mastodon-tl") +(declare-function mastodon-tl--property "mastodon-tl") +(declare-function mastodon-toot "mastodon") + +(defvar mastodon-toot-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") #'mastodon-toot--send) + (define-key map (kbd "C-c C-k") #'mastodon-toot--cancel) + (define-key map (kbd "C-c C-w") #'mastodon-toot--toggle-warning) + map) + "Keymap for `mastodon-toot'.") + + (defun mastodon-toot--action-success (marker &optional rm) "Insert MARKER with 'success face in byline. @@ -211,14 +221,6 @@ If REPLY-TO-ID is provided, set the MASTODON-TOOT--REPLY-TO-ID var." (mastodon-toot--setup-as-reply reply-to-user reply-to-id)) (mastodon-toot-mode t))) -(defvar mastodon-toot-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-c") #'mastodon-toot--send) - (define-key map (kbd "C-c C-k") #'mastodon-toot--cancel) - (define-key map (kbd "C-c C-w") #'mastodon-toot--toggle-warning) - map) - "Keymap for `mastodon-toot'.") - (define-minor-mode mastodon-toot-mode "Minor mode to capture Mastodon toots." :group 'mastodon-toot diff --git a/lisp/mastodon.el b/lisp/mastodon.el index a0dd732..9f7257e 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -30,8 +30,20 @@ ;; it is a labor of love. ;;; Code: - -(require 'mastodon-auth nil t) +(declare-function discover-add-context-menu "discover") +(declare-function emojify-mode "emojify") +(declare-function mastodon-tl--get-federated-timeline "mastodon-tl") +(declare-function mastodon-tl--get-home-timeline "mastodon-tl") +(declare-function mastodon-tl--get-local-timeline "mastodon-tl") +(declare-function mastodon-tl--get-tag-timeline "mastodon-tl") +(declare-function mastodon-tl--goto-next-toot "mastodon-tl") +(declare-function mastodon-tl--goto-prev-toot "mastodon-tl") +(declare-function mastodon-tl--thread "mastodon-tl") +(declare-function mastodon-tl--update "mastodon-tl") +(declare-function mastodon-toot--compose-buffer "mastodon-toot") +(declare-function mastodon-toot--reply "mastodon-toot") +(declare-function mastodon-toot--toggle-boost "mastodon-toot") +(declare-function mastodon-toot--toggle-favourite "mastodon-toot") (defgroup mastodon nil "Interface with Mastodon." @@ -43,11 +55,6 @@ :group 'mastodon :type 'string) -(defcustom mastodon-token-file (concat user-emacs-directory "mastodon.plstore") - "File path where Mastodon access tokens are stored." - :group 'mastodon - :type 'file) - (defcustom mastodon-toot-timestamp-format "%F %T" "Format to use for timestamps. @@ -57,25 +64,10 @@ Use. e.g. \"%c\" for your locale's date and time format." :group 'mastodon :type 'string) -(defcustom mastodon-avatar-height 30 - "Height of the user avatar images (if shown)." - :group 'mastodon - :type 'integer) - -(defcustom mastodon-preview-max-height 250 - "Max height of any media attachment preview to be shown." - :group 'mastodon - :type 'integer) - (defvar mastodon-mode-map (make-sparse-keymap) "Keymap for `mastodon-mode'.") -(defvar mastodon--api-version "v1") - -(defvar mastodon-buffer-spec nil - "A unique identifier and functions for each Mastodon buffer.") - (defcustom mastodon-mode-hook nil "Hook run when entering Mastodon mode." :type 'hook @@ -106,7 +98,6 @@ Use. e.g. \"%c\" for your locale's date and time format." (defun mastodon () "Connect Mastodon client to `mastodon-instance-url' instance." (interactive) - (require 'mastodon-tl nil t) (mastodon-tl--get-home-timeline)) ;;;###autoload @@ -116,7 +107,6 @@ Use. e.g. \"%c\" for your locale's date and time format." If USER is non-nil, insert after @ symbol to begin new toot. If REPLY-TO-ID is non-nil, attach new toot to a conversation." (interactive) - (require 'mastodon-toot nil t) (mastodon-toot--compose-buffer user reply-to-id)) ;;;###autoload diff --git a/test/mastodon-client-tests.el b/test/mastodon-client-tests.el index e1f92f8..c339efa 100644 --- a/test/mastodon-client-tests.el +++ b/test/mastodon-client-tests.el @@ -54,22 +54,22 @@ (ert-deftest client-1 () "Should return `mastondon-client' if non-nil." - (let ((mastodon-client t)) + (let ((mastodon-client--client-details t)) (should (eq (mastodon-client) t)))) (ert-deftest client-2 () "Should read from `mastodon-token-file' if available." - (let ((mastodon-client nil)) + (let ((mastodon-client--client-details nil)) (with-mock (mock (mastodon-client--read) => '(:client_id "foo" :client_secret "bar")) (should (equal (mastodon-client) '(:client_id "foo" :client_secret "bar"))) - (should (equal mastodon-client '(:client_id "foo" :client_secret "bar")))))) + (should (equal mastodon-client--client-details '(:client_id "foo" :client_secret "bar")))))) (ert-deftest client-3 () "Should store client data in plstore if it can't be read." - (let ((mastodon-client nil)) + (let ((mastodon-client--client-details nil)) (with-mock (mock (mastodon-client--read)) (mock (mastodon-client--store) => '(:client_id "foo" :client_secret "baz")) (should (equal (mastodon-client) '(:client_id "foo" :client_secret "baz"))) - (should (equal mastodon-client '(:client_id "foo" :client_secret "baz")))))) + (should (equal mastodon-client--client-details '(:client_id "foo" :client_secret "baz")))))) diff --git a/test/mastodon-media-tests.el b/test/mastodon-media-tests.el index 9cd06b7..4bb89c7 100644 --- a/test/mastodon-media-tests.el +++ b/test/mastodon-media-tests.el @@ -6,7 +6,7 @@ (mock (image-type-available-p 'imagemagick) => t) (mock (create-image * 'imagemagick t :height 123) => :mock-image) - (let* ((mastodon-avatar-height 123) + (let* ((mastodon-media--avatar-height 123) (result (mastodon-media--get-avatar-rendering "http://example.org/img.png")) (result-no-properties (substring-no-properties result)) (properties (text-properties-at 0 result))) @@ -21,7 +21,7 @@ (with-mock (mock (create-image * nil t) => :mock-image) - (let* ((mastodon-preview-max-height 123) + (let* ((mastodon-media--preview-max-height 123) (result (mastodon-media--get-media-link-rendering "http://example.org/img.png")) (result-no-properties (substring-no-properties result)) (properties (text-properties-at 0 result))) @@ -34,7 +34,7 @@ (ert-deftest mastodon-media:load-image-from-url:avatar-with-imagemagic () "Should make the right call to url-retrieve." (let ((url "http://example.org/image.png") - (mastodon-avatar-height 123)) + (mastodon-media--avatar-height 123)) (with-mock (mock (image-type-available-p 'imagemagick) => t) (mock (create-image * 'imagemagick t :height 123) => '(image foo)) @@ -42,7 +42,7 @@ (mock (url-retrieve url #'mastodon-media--process-image-response - '(:my-marker (:height 123) 1 "http://example.org/image.png")) + '(:my-marker (:height 123) 1)) => :called-as-expected) (with-temp-buffer @@ -62,7 +62,7 @@ (mock (url-retrieve url #'mastodon-media--process-image-response - '(:my-marker () 1 "http://example.org/image.png")) + '(:my-marker () 1)) => :called-as-expected) (with-temp-buffer @@ -82,13 +82,13 @@ (mock (url-retrieve "http://example.org/image.png" #'mastodon-media--process-image-response - '(:my-marker (:max-height 321) 5 "http://example.org/image.png")) + '(:my-marker (:max-height 321) 5)) => :called-as-expected) (with-temp-buffer (insert (concat "Start:" (mastodon-media--get-media-link-rendering url) ":rest")) - (let ((mastodon-preview-max-height 321)) + (let ((mastodon-media--preview-max-height 321)) (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) (ert-deftest mastodon-media:load-image-from-url:media-link-without-imagemagic () @@ -101,14 +101,14 @@ (mock (url-retrieve "http://example.org/image.png" #'mastodon-media--process-image-response - '(:my-marker () 5 "http://example.org/image.png")) + '(:my-marker () 5)) => :called-as-expected) (with-temp-buffer (insert (concat "Start:" (mastodon-media--get-avatar-rendering url) ":rest")) - (let ((mastodon-preview-max-height 321)) + (let ((mastodon-media--preview-max-height 321)) (should (eq :called-as-expected (mastodon-media--load-image-from-url url 'media-link 7 5)))))))) (ert-deftest mastodon-media:process-image-response () @@ -134,7 +134,7 @@ (mock (create-image "fake\nimage\ndata" 'imagemagick t ':image :option) => :fake-image) - (mastodon-media--process-image-response () used-marker '(:image :option) 1 "the-url") + (mastodon-media--process-image-response () used-marker '(:image :option) 1) ;; the used marker has been unset: (should (null (marker-position used-marker))) diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el index caea476..8c706f5 100644 --- a/test/mastodon-tl-tests.el +++ b/test/mastodon-tl-tests.el @@ -105,7 +105,7 @@ (ert-deftest mastodon-tl--byline-regular () "Should format the regular toot correctly." - (let ((mastodon-media-show-avatars-p nil) + (let ((mastodon-tl--show-avatars-p nil) (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock (mock (date-to-time timestamp) => '(22782 21551)) @@ -119,7 +119,7 @@ (ert-deftest mastodon-tl--byline-regular-with-avatar () "Should format the regular toot correctly." - (let ((mastodon-media-show-avatars-p t) + (let ((mastodon-tl--show-avatars-p t) (timestamp (cdr (assoc 'created_at mastodon-tl-test-base-toot)))) (with-mock (stub create-image => '(image "fake data")) @@ -134,7 +134,7 @@ (ert-deftest mastodon-tl--byline-boosted () "Should format the boosted toot correctly." - (let* ((mastodon-media-show-avatars-p nil) + (let* ((mastodon-tl--show-avatars-p nil) (toot (cons '(reblogged . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock @@ -148,7 +148,7 @@ (ert-deftest mastodon-tl--byline-favorited () "Should format the favourited toot correctly." - (let* ((mastodon-media-show-avatars-p nil) + (let* ((mastodon-tl--show-avatars-p nil) (toot (cons '(favourited . t) mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock @@ -163,7 +163,7 @@ (ert-deftest mastodon-tl--byline-boosted/favorited () "Should format the boosted & favourited toot correctly." - (let* ((mastodon-media-show-avatars-p nil) + (let* ((mastodon-tl--show-avatars-p nil) (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-toot)) (timestamp (cdr (assoc 'created_at toot)))) (with-mock @@ -177,7 +177,7 @@ (ert-deftest mastodon-tl--byline-reblogged () "Should format the reblogged toot correctly." - (let* ((mastodon-media-show-avatars-p nil) + (let* ((mastodon-tl--show-avatars-p nil) (toot mastodon-tl-test-base-boosted-toot) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) @@ -197,7 +197,7 @@ (ert-deftest mastodon-tl--byline-reblogged-with-avatars () "Should format the reblogged toot correctly." - (let* ((mastodon-media-show-avatars-p t) + (let* ((mastodon-tl--show-avatars-p t) (toot mastodon-tl-test-base-boosted-toot) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) @@ -218,7 +218,7 @@ (ert-deftest mastodon-tl--byline-reblogged-boosted/favorited () "Should format the reblogged toot that was also boosted & favoritedcorrectly." - (let* ((mastodon-media-show-avatars-p nil) + (let* ((mastodon-tl--show-avatars-p nil) (toot `((favourited . t) (reblogged . t) ,@mastodon-tl-test-base-boosted-toot)) (original-toot (cdr (assoc 'reblog mastodon-tl-test-base-boosted-toot))) (timestamp (cdr (assoc 'created_at toot))) -- cgit v1.2.3 From c0708e9338de45415c0b70019cf45ee9a5bad230 Mon Sep 17 00:00:00 2001 From: Holger Dürer Date: Tue, 16 May 2017 21:44:10 +0100 Subject: Replace uses of replace-regexp with search-forward and replace-match. --- lisp/mastodon-inspect.el | 6 ++++-- lisp/mastodon-tl.el | 4 +++- lisp/mastodon-toot.el | 5 ++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index f5fcd81..0457e74 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -71,8 +71,10 @@ (with-current-buffer buffer (let ((toot (mastodon-inspect--download-single-toot toot-id ))) (mastodon-tl--toot toot) - (replace-regexp "\n\n\n | " "\n | " nil (point-min) (point-max)) - (mastodon-media--inline-images))) + (goto-char (point-min)) + (while (search-forward "\n\n\n | " nil t) + (replace-match "\n | ")) + (mastodon-media--inline-images))) (switch-to-buffer-other-window buffer) (mastodon-mode))) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index edefae9..72883fc 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -234,7 +234,9 @@ also render the html" (defun mastodon-tl--timeline (toots) "Display each toot in TOOTS." (mapc 'mastodon-tl--toot toots) - (replace-regexp "\n\n\n | " "\n | " nil (point-min) (point-max)) + (goto-char (point-min)) + (while (search-forward "\n\n\n | " nil t) + (replace-match "\n | ")) (mastodon-media--inline-images)) (defun mastodon-tl--get-update-function (&optional buffer) diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index ffe6454..296f95f 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -56,7 +56,10 @@ Remove MARKER if RM is non-nil." (let ((inhibit-read-only t) (bol (progn (move-beginning-of-line nil) (point))) (eol (progn (move-end-of-line nil) (point)))) - (when rm (replace-regexp (format "(%s) " marker) "" nil bol eol)) + (when rm + (goto-char bol) + (if (search-forward (format "(%s) " marker) eol t) + (replace-match ""))) (move-beginning-of-line nil) (mastodon-tl--goto-next-toot) (unless rm -- cgit v1.2.3 From 349a46befedafae39ab05598c1005f9f2c82fadf Mon Sep 17 00:00:00 2001 From: alexjgriffith Date: Wed, 17 May 2017 10:34:34 -0400 Subject: replaced declare-function with autoload --- lisp/mastodon-auth.el | 4 ++-- lisp/mastodon-client.el | 4 ++-- lisp/mastodon-http.el | 2 +- lisp/mastodon-inspect.el | 12 ++++++------ lisp/mastodon-tl.el | 12 ++++++------ lisp/mastodon-toot.el | 30 +++++++++++++++--------------- lisp/mastodon.el | 24 ++++++++++++------------ 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el index 68c4d21..513fbc2 100644 --- a/lisp/mastodon-auth.el +++ b/lisp/mastodon-auth.el @@ -31,8 +31,8 @@ (require 'plstore) -(declare-function mastodon-client "mastodon-client") -(declare-function mastodon-http--post "mastodon-http") +(autoload 'mastodon-client "mastodon-client") +(autoload 'mastodon-http--post "mastodon-http") (defvar mastodon-instance-url) (defgroup mastodon-auth nil diff --git a/lisp/mastodon-client.el b/lisp/mastodon-client.el index 3a9a606..b4fcbdb 100644 --- a/lisp/mastodon-client.el +++ b/lisp/mastodon-client.el @@ -30,8 +30,8 @@ ;;; Code: (require 'plstore) -(declare-function mastodon-http--api "mastodon-http") -(declare-function mastodon-http--post "mastodon-http") +(autoload 'mastodon-http--api "mastodon-http") +(autoload 'mastodon-http--post "mastodon-http") (defcustom mastodon-client--token-file (concat user-emacs-directory "mastodon.plstore") diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index 9f178c4..0f71f68 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -32,7 +32,7 @@ (require 'json) (defvar mastodon-instance-url) (defvar mastodon-auth--token) -(declare-function mastodon-auth--access-token "mastodon-auth") +(autoload 'mastodon-auth--access-token "mastodon-auth") (defvar mastodon-http--api-version "v1") diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index 0457e74..9aee7dc 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -28,12 +28,12 @@ ;; Some tools to help inspect / debug mastodon.el ;;; Code: -(declare-function mastodon-http--api "mastodon-http") -(declare-function mastodon-http--get-json "mastodon-http") -(declare-function mastodon-media--inline-images "mastodon-media") -(declare-function mastodon-mode "mastodon") -(declare-function mastodon-tl--property "mastodon-tl") -(declare-function mastodon-tl--toot "mastodon-tl") +(autoload 'mastodon-http--api "mastodon-http") +(autoload 'mastodon-http--get-json "mastodon-http") +(autoload 'mastodon-media--inline-images "mastodon-media") +(autoload 'mastodon-mode "mastodon") +(autoload 'mastodon-tl--property "mastodon-tl") +(autoload 'mastodon-tl--toot "mastodon-tl") (defgroup mastodon-inspect nil "Tools to help inspect toots." diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 72883fc..b84bc94 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -33,12 +33,12 @@ (require 'thingatpt) ;; for word-at-point (require 'time-date) -(declare-function mastodon-http--api "mastodon-http") -(declare-function mastodon-http--get-json "mastodon-http") -(declare-function mastodon-media--get-avatar-rendering "mastodon-media") -(declare-function mastodon-media--get-media-link-rendering "mastodon-media") -(declare-function mastodon-media--inline-images "mastodon-media") -(declare-function mastodon-mode "mastodon") +(autoload 'mastodon-http--api "mastodon-http") +(autoload 'mastodon-http--get-json "mastodon-http") +(autoload 'mastodon-media--get-avatar-rendering "mastodon-media") +(autoload 'mastodon-media--get-media-link-rendering "mastodon-media") +(autoload 'mastodon-media--inline-images "mastodon-media") +(autoload 'mastodon-mode "mastodon") (defvar mastodon-toot-timestamp-format) (defgroup mastodon-tl nil diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index 296f95f..eaf5339 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -32,13 +32,13 @@ (defvar mastodon-toot--reply-to-id nil) (defvar mastodon-toot--content-warning nil) -(declare-function mastodon-http--api "mastodon-http") -(declare-function mastodon-http--post "mastodon-http") -(declare-function mastodon-http--triage "mastodon-http") -(declare-function mastodon-tl--field "mastodon-tl") -(declare-function mastodon-tl--goto-next-toot "mastodon-tl") -(declare-function mastodon-tl--property "mastodon-tl") -(declare-function mastodon-toot "mastodon") +(autoload 'mastodon-http--api "mastodon-http") +(autoload 'mastodon-http--post "mastodon-http") +(autoload 'mastodon-http--triage "mastodon-http") +(autoload 'mastodon-tl--field "mastodon-tl") +(autoload 'mastodon-tl--goto-next-toot "mastodon-tl") +(autoload 'mastodon-tl--property "mastodon-tl") +(autoload 'mastodon-toot "mastodon") (defvar mastodon-toot-mode-map (let ((map (make-sparse-keymap))) @@ -166,28 +166,28 @@ Set `mastodon-toot--content-warning' to nil." (mapcar (lambda (b) (setf (car b) (vector prefix (car b))) b) - bindings))) + bindings))) (defun mastodon-toot--format-kbind-command (cmd) "Format CMD to be more readable. e.g. mastodon-toot--send -> Send." (let* ((str (symbol-name cmd)) - (re "--\\(.*\\)$") - (str2 (save-match-data - (string-match re str) - (match-string 1 str)))) + (re "--\\(.*\\)$") + (str2 (save-match-data + (string-match re str) + (match-string 1 str)))) (capitalize (replace-regexp-in-string "-" " " str2)))) (defun mastodon-toot--format-kbind (kbind) "Format a single keybinding, KBIND, for display in documentation." (let ((key (help-key-description (car kbind) nil)) - (command (mastodon-toot--format-kbind-command (cdr kbind)))) + (command (mastodon-toot--format-kbind-command (cdr kbind)))) (format "\t%s - %s" key command))) (defun mastodon-toot--format-kbinds (kbinds) "Format a list keybindings, KBINDS, for display in documentation." (mapconcat 'identity (cons "" (mapcar #'mastodon-toot--format-kbind kbinds)) - "\n")) + "\n")) (defun mastodon-toot--make-mode-docs () "Create formatted documentation text for the mastodon-toot-mode." @@ -217,7 +217,7 @@ If REPLY-TO-ID is provided, set the MASTODON-TOOT--REPLY-TO-ID var." If REPLY-TO-USER is provided, inject their handle into the message. If REPLY-TO-ID is provided, set the MASTODON-TOOT--REPLY-TO-ID var." (let* ((buffer-exists (get-buffer "*new toot*")) - (buffer (or buffer-exists (get-buffer-create "*new toot*")))) + (buffer (or buffer-exists (get-buffer-create "*new toot*")))) (switch-to-buffer-other-window buffer) (when (not buffer-exists) (mastodon-toot--display-docs) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 9f7257e..6e3a5dc 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -32,18 +32,18 @@ ;;; Code: (declare-function discover-add-context-menu "discover") (declare-function emojify-mode "emojify") -(declare-function mastodon-tl--get-federated-timeline "mastodon-tl") -(declare-function mastodon-tl--get-home-timeline "mastodon-tl") -(declare-function mastodon-tl--get-local-timeline "mastodon-tl") -(declare-function mastodon-tl--get-tag-timeline "mastodon-tl") -(declare-function mastodon-tl--goto-next-toot "mastodon-tl") -(declare-function mastodon-tl--goto-prev-toot "mastodon-tl") -(declare-function mastodon-tl--thread "mastodon-tl") -(declare-function mastodon-tl--update "mastodon-tl") -(declare-function mastodon-toot--compose-buffer "mastodon-toot") -(declare-function mastodon-toot--reply "mastodon-toot") -(declare-function mastodon-toot--toggle-boost "mastodon-toot") -(declare-function mastodon-toot--toggle-favourite "mastodon-toot") +(autoload 'mastodon-tl--get-federated-timeline "mastodon-tl") +(autoload 'mastodon-tl--get-home-timeline "mastodon-tl") +(autoload 'mastodon-tl--get-local-timeline "mastodon-tl") +(autoload 'mastodon-tl--get-tag-timeline "mastodon-tl") +(autoload 'mastodon-tl--goto-next-toot "mastodon-tl") +(autoload 'mastodon-tl--goto-prev-toot "mastodon-tl") +(autoload 'mastodon-tl--thread "mastodon-tl") +(autoload 'mastodon-tl--update "mastodon-tl") +(autoload 'mastodon-toot--compose-buffer "mastodon-toot") +(autoload 'mastodon-toot--reply "mastodon-toot") +(autoload 'mastodon-toot--toggle-boost "mastodon-toot") +(autoload 'mastodon-toot--toggle-favourite "mastodon-toot") (defgroup mastodon nil "Interface with Mastodon." -- cgit v1.2.3 From 8c841d2ad3d60997f7aea725da86b83ac80dcd61 Mon Sep 17 00:00:00 2001 From: Johnson Denen Date: Thu, 18 May 2017 13:28:40 -0400 Subject: Bump to v0.7.0 --- lisp/mastodon-auth.el | 2 +- lisp/mastodon-client.el | 2 +- lisp/mastodon-http.el | 2 +- lisp/mastodon-inspect.el | 2 +- lisp/mastodon-media.el | 2 +- lisp/mastodon-tl.el | 2 +- lisp/mastodon-toot.el | 2 +- lisp/mastodon.el | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el index 513fbc2..ed756f8 100644 --- a/lisp/mastodon-auth.el +++ b/lisp/mastodon-auth.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-client.el b/lisp/mastodon-client.el index b4fcbdb..cbb276b 100644 --- a/lisp/mastodon-client.el +++ b/lisp/mastodon-client.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index 0f71f68..1e6e037 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el index 9aee7dc..29368a9 100644 --- a/lisp/mastodon-inspect.el +++ b/lisp/mastodon-inspect.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index 671f159..f5d67ca 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index b84bc94..eeed6b5 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index eaf5339..7d33116 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Homepage: https://github.com/jdenen/mastodon.el ;; Package-Requires: ((emacs "24.4")) diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 6e3a5dc..2bf5e84 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2017 Johnson Denen ;; Author: Johnson Denen -;; Version: 0.6.3 +;; Version: 0.7.0 ;; Package-Requires: ((emacs "24.4")) ;; Homepage: https://github.com/jdenen/mastodon.el -- cgit v1.2.3