aboutsummaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/mastodon-http.el114
-rw-r--r--lisp/mastodon-media.el5
-rw-r--r--lisp/mastodon-profile.el68
-rw-r--r--lisp/mastodon-tl.el26
-rw-r--r--lisp/mastodon.el10
5 files changed, 126 insertions, 97 deletions
diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el
index 086dcec..f32ccd4 100644
--- a/lisp/mastodon-http.el
+++ b/lisp/mastodon-http.el
@@ -73,7 +73,7 @@
"Retrieve URL asynchronously.
This is a thin abstraction over the system
-`url-retrieve-synchronously`. Depending on which version of this
+`url-retrieve-synchronously'. Depending on which version of this
is available we will call it with or without a timeout."
(if (< (cdr (func-arity 'url-retrieve-synchronously)) 4)
(url-retrieve-synchronously url)
@@ -100,6 +100,7 @@ Message status and JSON error from RESPONSE if unsuccessful."
(defmacro mastodon-http--authorized-request (method body &optional unauthenticated-p)
"Make a METHOD type request using BODY, with Mastodon authorization.
Unless UNAUTHENTICATED-P is non-nil."
+ (declare (debug 'body))
`(let ((url-request-method ,method)
(url-request-extra-headers
(unless ,unauthenticated-p
@@ -107,6 +108,18 @@ Unless UNAUTHENTICATED-P is non-nil."
(concat "Bearer " (mastodon-auth--access-token)))))))
,body))
+(defun mastodon-http--build-query-string (args)
+ "Build a request query string from ARGS."
+ ;; (url-build-query-string args nil))
+ ;; url-build-query-string adds 'nil' to empty params so lets stay with our
+ ;; own:
+ (mapconcat (lambda (arg)
+ (concat (url-hexify-string (car arg))
+ "="
+ (url-hexify-string (cdr arg))))
+ args
+ "&"))
+
(defun mastodon-http--post (url args headers &optional unauthenticated-p)
"POST synchronously to URL with ARGS and HEADERS.
@@ -115,12 +128,7 @@ Authorization header is included by default unless UNAUTHENTICATED-P is non-nil.
"POST"
(let ((url-request-data
(when args
- (mapconcat (lambda (arg)
- (concat (url-hexify-string (car arg))
- "="
- (url-hexify-string (cdr arg))))
- args
- "&")))
+ (mastodon-http--build-query-string args)))
(url-request-extra-headers
(append url-request-extra-headers ; auth set in macro
;; pleroma compat:
@@ -165,13 +173,6 @@ Pass response buffer to CALLBACK function."
(with-temp-buffer
(mastodon-http--url-retrieve-synchronously url))))
-(defun mastodon-http--append-query-string (url params)
- "Append PARAMS to URL as query strings and return it.
-
-PARAMS should be an alist as required by `url-build-query-string'."
- (let ((query-string (url-build-query-string params)))
- (concat url "?" query-string)))
-
;; search functions:
(defun mastodon-http--process-json-search ()
"Process JSON returned by a search query to the server."
@@ -215,7 +216,9 @@ Optionally specify the PARAMS to send."
Optionally specify the PARAMS to send."
(mastodon-http--authorized-request
"PATCH"
- (let ((url (mastodon-http--append-query-string base-url params)))
+ (let ((url
+ (concat base-url "?"
+ (mastodon-http--build-query-string params))))
(mastodon-http--url-retrieve-synchronously url))))
;; Asynchronous functions
@@ -244,12 +247,7 @@ Authorization header is included by default unless UNAUTHENTICED-P is non-nil."
(let ((request-timeout 5)
(url-request-data
(when args
- (mapconcat (lambda (arg)
- (concat (url-hexify-string (car arg))
- "="
- (url-hexify-string (cdr arg))))
- args
- "&"))))
+ (mastodon-http--build-query-string args))))
(with-temp-buffer
(url-retrieve url callback cbargs)))))
@@ -262,43 +260,43 @@ item uploaded, and `mastodon-toot--update-status-fields' is run."
(let* ((file (file-name-nondirectory filename))
(request-backend 'curl))
(request
- url
- :type "POST"
- :params `(("description" . ,caption))
- :files `(("file" . (,file :file ,filename
- :mime-type "multipart/form-data")))
- :parser 'json-read
- :headers `(("Authorization" . ,(concat "Bearer "
- (mastodon-auth--access-token))))
- :sync nil
- :success (cl-function
- (lambda (&key data &allow-other-keys)
- (when data
- (push (alist-get 'id data)
- mastodon-toot--media-attachment-ids) ; add ID to list
- (message "%s file %s with id %S and caption '%s' uploaded!"
- (capitalize (alist-get 'type data))
- file
- (alist-get 'id data)
- (alist-get 'description data))
- (mastodon-toot--update-status-fields))))
- :error (cl-function
- (lambda (&key error-thrown &allow-other-keys)
- (cond
- ;; handle curl errors first (eg 26, can't read file/path)
- ;; because the '=' test below fails for them
- ;; they have the form (error . error message 24)
- ((not (proper-list-p error-thrown)) ; not dotted list
- (message "Got error: %s. Shit went south." (cdr error-thrown)))
- ;; handle mastodon api errors
- ;; they have the form (error http 401)
- ((= (car (last error-thrown)) 401)
- (message "Got error: %s Unauthorized: The access token is invalid" error-thrown))
- ((= (car (last error-thrown)) 422)
- (message "Got error: %s Unprocessable entity: file or file type is unsupported or invalid" error-thrown))
- (t
- (message "Got error: %s Shit went south"
- error-thrown))))))))
+ url
+ :type "POST"
+ :params `(("description" . ,caption))
+ :files `(("file" . (,file :file ,filename
+ :mime-type "multipart/form-data")))
+ :parser 'json-read
+ :headers `(("Authorization" . ,(concat "Bearer "
+ (mastodon-auth--access-token))))
+ :sync nil
+ :success (cl-function
+ (lambda (&key data &allow-other-keys)
+ (when data
+ (push (alist-get 'id data)
+ mastodon-toot--media-attachment-ids) ; add ID to list
+ (message "%s file %s with id %S and caption '%s' uploaded!"
+ (capitalize (alist-get 'type data))
+ file
+ (alist-get 'id data)
+ (alist-get 'description data))
+ (mastodon-toot--update-status-fields))))
+ :error (cl-function
+ (lambda (&key error-thrown &allow-other-keys)
+ (cond
+ ;; handle curl errors first (eg 26, can't read file/path)
+ ;; because the '=' test below fails for them
+ ;; they have the form (error . error message 24)
+ ((not (proper-list-p error-thrown)) ; not dotted list
+ (message "Got error: %s. Shit went south." (cdr error-thrown)))
+ ;; handle mastodon api errors
+ ;; they have the form (error http 401)
+ ((= (car (last error-thrown)) 401)
+ (message "Got error: %s Unauthorized: The access token is invalid" error-thrown))
+ ((= (car (last error-thrown)) 422)
+ (message "Got error: %s Unprocessable entity: file or file type is unsupported or invalid" error-thrown))
+ (t
+ (message "Got error: %s Shit went south"
+ error-thrown))))))))
(provide 'mastodon-http)
;;; mastodon-http.el ends here
diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el
index ace15b2..9715a6c 100644
--- a/lisp/mastodon-media.el
+++ b/lisp/mastodon-media.el
@@ -186,7 +186,6 @@ with the image."
MEDIA-TYPE is a symbol and either 'avatar or 'media-link.
START is the position where we start loading the image.
REGION-LENGTH is the range from start to propertize."
- ;; TODO: Cache the avatars
(let ((image-options (when (or (image-type-available-p 'imagemagick)
(image-transforms-p)) ; inbuilt scaling in 27.1
(cond
@@ -196,8 +195,8 @@ REGION-LENGTH is the range from start to propertize."
`(:max-height ,mastodon-media--preview-max-height))))))
(let ((buffer (current-buffer))
(marker (copy-marker start))
- ;; Keep url.el from spamming us with messages about connecting to hosts:
- (url-show-status nil))
+ ;; Keep url.el from spamming us with messages about connecting to hosts:
+ (url-show-status nil))
(condition-case nil
;; catch any errors in url-retrieve so as to not abort
;; whatever called us
diff --git a/lisp/mastodon-profile.el b/lisp/mastodon-profile.el
index 3b6f336..ff729f0 100644
--- a/lisp/mastodon-profile.el
+++ b/lisp/mastodon-profile.el
@@ -274,7 +274,7 @@ Both args are strings.
SOURCE means that the preference is in the 'source' part of the account JSON."
(let* ((url (mastodon-http--api "accounts/update_credentials"))
(pref-formatted (if source (concat "source[" pref "]") pref))
- (response (mastodon-http--patch url `((,pref-formatted ,val)))))
+ (response (mastodon-http--patch url `((,pref-formatted . ,val)))))
(mastodon-http--triage response
(lambda ()
(mastodon-profile-fetch-server-account-settings)
@@ -365,6 +365,15 @@ Current settings are fetched from the server."
(interactive)
(mastodon-profile--edit-string-value 'display_name))
+(defun mastodon-profile--get-preferences-pref (pref)
+ "Fetch PREF from the endpoint \"/preferences\".
+This endpoint only holds a few preferences. For others, see
+`mastodon-profile--update-preference' and its endpoint,
+\"/accounts/update_credentials.\""
+ (alist-get pref
+ (mastodon-http--get-json
+ (mastodon-http--api "preferences"))))
+
(defun mastodon-profile-view-preferences ()
"View user preferences in another window."
(interactive)
@@ -397,11 +406,11 @@ Current settings are fetched from the server."
(defun mastodon-profile--fields-get (account)
"Fetch the fields vector (aka profile metadata) from profile of ACCOUNT.
-Returns a list of lists."
+Returns an alist."
(let ((fields (mastodon-profile--account-field account 'fields)))
(when fields
(mapcar (lambda (el)
- (list (alist-get 'name el)
+ (cons (alist-get 'name el)
(alist-get 'value el)))
fields))))
@@ -415,7 +424,7 @@ Returns a list of lists."
(concat
(format "_ %s " (car field))
(make-string (- (+ 1 left-width) (length (car field))) ?_)
- (format " :: %s" (cadr field)))
+ (format " :: %s" (cdr field)))
field)) ; hack to make links tabstops
fields "")))
@@ -477,30 +486,33 @@ Returns a list of lists."
(is-followers " FOLLOWERS ")
(is-following " FOLLOWING "))))
(insert
- "\n"
- (mastodon-profile--image-from-account account)
- "\n"
- (propertize (mastodon-profile--account-field
- account 'display_name)
- 'face 'mastodon-display-name-face)
- "\n"
- (propertize (concat "@" acct)
- 'face 'default)
- (if (equal locked t)
- (if (fontp (char-displayable-p #10r9993))
- " 🔒"
- " [locked]")
- "")
- "\n ------------\n"
- (mastodon-tl--render-text note account)
- ;; account here to enable tab-stops in profile note
- (if fields
- (concat "\n"
- (mastodon-tl--set-face
- (mastodon-profile--fields-insert fields)
- 'success)
- "\n")
- "")
+ (propertize
+ (concat
+ "\n"
+ (mastodon-profile--image-from-account account)
+ "\n"
+ (propertize (mastodon-profile--account-field
+ account 'display_name)
+ 'face 'mastodon-display-name-face)
+ "\n"
+ (propertize (concat "@" acct)
+ 'face 'default)
+ (if (equal locked t)
+ (if (fontp (char-displayable-p #10r9993))
+ " 🔒"
+ " [locked]")
+ "")
+ "\n ------------\n"
+ (mastodon-tl--render-text note account)
+ ;; account here to enable tab-stops in profile note
+ (if fields
+ (concat "\n"
+ (mastodon-tl--set-face
+ (mastodon-profile--fields-insert fields)
+ 'success)
+ "\n")
+ ""))
+ 'profile-json account)
;; insert counts
(mastodon-tl--set-face
(concat " ------------\n"
diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el
index d8b2baa..158ba5f 100644
--- a/lisp/mastodon-tl.el
+++ b/lisp/mastodon-tl.el
@@ -66,6 +66,7 @@
(autoload 'mastodon-search--get-user-info "mastodon-search")
(autoload 'mastodon-http--delete "mastodon-http")
(autoload 'mastodon-profile--view-author-profile "mastodon-profile")
+(autoload 'mastodon-profile--get-preferences-pref "mastodon-profile")
(when (require 'mpv nil :no-error)
(declare-function mpv-start "mpv"))
@@ -646,7 +647,9 @@ START and END are the boundaries of the link in the toot."
keymap
(help-echo (get-text-property start 'help-echo))
extra-properties
- (toot-url (mastodon-tl--field 'url toot))
+ ;; handle calling this on non-toots, e.g. for profiles:
+ (toot-url (when (proper-list-p toot)
+ (mastodon-tl--field 'url toot)))
(toot-url (when toot-url (url-generic-parse-url toot-url)))
(toot-instance-url (if toot-url
(concat (url-type toot-url) "://"
@@ -665,8 +668,10 @@ START and END are the boundaries of the link in the toot."
(;; User handles:
maybe-userhandle
;; this fails on mentions in profile notes:
- (let ((maybe-userid (mastodon-tl--extract-userid-toot
- toot maybe-userhandle)))
+ (let ((maybe-userid
+ (when (proper-list-p toot)
+ (mastodon-tl--extract-userid-toot
+ toot maybe-userhandle))))
(setq mastodon-tab-stop-type 'user-handle
keymap mastodon-tl--link-keymap
help-echo (concat "Browse user profile of " maybe-userhandle)
@@ -856,7 +861,12 @@ message is a link which unhides/hides the main body."
(concat
cw
(propertize (mastodon-tl--content toot)
- 'invisible t
+ 'invisible
+ ;; check server setting to expand all spoilers:
+ (unless (eq t
+ (mastodon-profile--get-preferences-pref
+ 'reading:expand:spoilers))
+ t)
'mastodon-content-warning-body t))))
(defun mastodon-tl--media (toot)
@@ -1694,7 +1704,13 @@ For use after e.g. deleting a toot."
(mastodon-notifications--get))
((equal (mastodon-tl--buffer-name)
(concat "*mastodon-" (mastodon-auth--get-account-name) "-statuses*"))
- (mastodon-profile--my-profile))))
+ (mastodon-profile--my-profile))
+ ((save-match-data
+ (string-match
+ "statuses/\\(?2:[[:digit:]]+\\)/context"
+ (mastodon-tl--get-endpoint))
+ (mastodon-tl--thread
+ (match-string 2 (mastodon-tl--get-endpoint)))))))
(defun mastodon-tl--more ()
"Append older toots to timeline, asynchronously."
diff --git a/lisp/mastodon.el b/lisp/mastodon.el
index 5e95b35..bc624d9 100644
--- a/lisp/mastodon.el
+++ b/lisp/mastodon.el
@@ -5,7 +5,7 @@
;; Author: Johnson Denen <johnson.denen@gmail.com>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
-;; Package-Requires: ((emacs "27.1") (request "0.3.0"))
+;; Package-Requires: ((emacs "27.1") (request "0.3.0") (persist "0.4"))
;; Homepage: https://codeberg.org/martianh/mastodon.el
;; This file is not part of GNU Emacs.
@@ -283,7 +283,9 @@ not, just browse the URL in the normal fashion."
(get-text-property (point) 'shr-url)
(read-string "Lookup URL: "))))
(if (not (mastodon--masto-url-p query))
- (browse-url query)
+ (if (equal major-mode 'mastodon-mode)
+ (shr-browse-url query) ;; keep our shr keymap
+ (browse-url query))
(message "Performing lookup...")
(let* ((url (format "%s/api/v2/search" mastodon-instance-url))
(param (concat "resolve=t")) ; webfinger
@@ -305,10 +307,12 @@ not, just browse the URL in the normal fashion."
(defun mastodon--masto-url-p (query)
"Check if QUERY resembles a fediverse URL."
;; calqued off https://github.com/tuskyapp/Tusky/blob/c8fc2418b8f5458a817bba221d025b822225e130/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt
+ ;; thx to Conny Duck!
(let* ((uri-parsed (url-generic-parse-url query))
(query (url-filename uri-parsed)))
(save-match-data
- (or (string-match "^/@[[:alnum:]]+/[[:digit:]]+$" query)
+ (or (string-match "^/@[^/]+$" query)
+ (string-match "^/@[^/]+/[[:digit:]]+$" query)
(string-match "^/users/[[:alnum:]]+$" query)
(string-match "^/notice/[[:alnum:]]+$" query)
(string-match "^/objects/[-a-f0-9]+$" query)