branch: elpa/mastodon
commit 1dfe4368488d2b0539b7626b0c105343ca92322e
Merge: e2443f1cd4 38f814c3e5
Author: marty hiatt <[email protected]>
Commit: marty hiatt <[email protected]>
Merge branch 'develop'
---
README.org | 4 ++
lisp/mastodon-auth.el | 2 +-
lisp/mastodon-http.el | 7 +-
lisp/mastodon-media.el | 4 +-
lisp/mastodon-notifications.el | 146 +++++++++++++++++++++++++++++++++++++----
lisp/mastodon-profile.el | 43 ++++++------
lisp/mastodon-search.el | 15 ++---
lisp/mastodon-tl.el | 111 ++++++++++++++++++-------------
lisp/mastodon-toot.el | 13 ++--
lisp/mastodon-transient.el | 79 +++++++++++++++++++---
lisp/mastodon-views.el | 10 ++-
lisp/mastodon.el | 10 ++-
mastodon-index.org | 9 ++-
13 files changed, 342 insertions(+), 111 deletions(-)
diff --git a/README.org b/README.org
index 5096729c05..3daa3b8ad1 100644
--- a/README.org
+++ b/README.org
@@ -418,6 +418,10 @@ layout for =mastodon.el=.
The repo is at [[https://github.com/rougier/mastodon-alt][mastodon-alt]].
+**** Org links, archive search
+
+[[https://codeberg.org/chrmoe/toot-suite][toot-suite]] implements an org link
type for fediverse posts, and also provides a way to browse an offline archive
of your account.
+
**** Mastodon hydra
A user made a hydra for handling basic =mastodon.el= commands. It's
diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el
index 01639fb2bd..6e90b5348f 100644
--- a/lisp/mastodon-auth.el
+++ b/lisp/mastodon-auth.el
@@ -32,7 +32,7 @@
(require 'plstore)
(require 'auth-source)
(require 'json)
-(eval-when-compile (require 'subr-x)) ; for if-let
+(eval-when-compile (require 'subr-x)) ; for if-let*
(autoload 'mastodon-client "mastodon-client")
(autoload 'mastodon-client--active-user "mastodon-client")
diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el
index 47b96fed12..2c4c49d1bb 100644
--- a/lisp/mastodon-http.el
+++ b/lisp/mastodon-http.el
@@ -379,7 +379,12 @@ item uploaded, and `mastodon-toot--update-status-fields'
is run."
(when data
(let* ((id (alist-get 'id data)))
;; update ids:
- (push id mastodon-toot--media-attachment-ids)
+ (if (not mastodon-toot--media-attachment-ids)
+ ;; add first id:
+ (push id mastodon-toot--media-attachment-ids)
+ ;; add new id to end of list to preserve order:
+ (push id (cdr
+ (last mastodon-toot--media-attachment-ids))))
;; pleroma, PUT the description:
;; this is how the mangane akkoma web client does it
;; and it seems easier than the other options!
diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el
index cfb20de028..09692c311b 100644
--- a/lisp/mastodon-media.el
+++ b/lisp/mastodon-media.el
@@ -338,7 +338,7 @@ image-data prop so it can be toggled."
"Callback function processing the `url-retrieve' response for URL.
URL is a full-sized image URL attached to a timeline image.
STATUS-PLIST is a plist of status events as per `url-retrieve'."
- (if-let (error-response (plist-get status-plist :error))
+ (if-let* ((error-response (plist-get status-plist :error)))
(user-error "error in loading image: %S" error-response)
(when mastodon-media--enable-image-caching
(unless (url-is-cached url) ;; cache if not already cached
@@ -467,7 +467,7 @@ START and END are the beginning and end of the media item
to overlay."
(propertize ""
'help-echo "Video"
'face
- '((:height 3.5 :inherit font-lock-comment-face))))))
+ '((:height 3.5 :inherit mastodon-toot-docs-face))))))
;; (cl-pushnew ov mastodon-media--overlays)))
(defun mastodon-media--get-avatar-rendering (avatar-url)
diff --git a/lisp/mastodon-notifications.el b/lisp/mastodon-notifications.el
index 5790757c00..5730a95e08 100644
--- a/lisp/mastodon-notifications.el
+++ b/lisp/mastodon-notifications.el
@@ -68,6 +68,9 @@
(autoload 'mastodon-tl--buffer-type-eq "mastodon-tl")
(autoload 'mastodon-tl--buffer-property "mastodon-tl")
(autoload 'mastodon-http--patch "mastodon-http")
+(autoload 'mastodon-views--minor-view "mastodon-views")
+(autoload 'mastodon-tl--goto-first-item "mastodon-tl")
+(autoload 'mastodon-tl--init-sync "mastodon-tl")
;; notifications defcustoms moved into mastodon.el
;; as some need to be available without loading this file
@@ -85,6 +88,7 @@
(defvar mastodon-profile-note-in-foll-reqs-max-length)
(defvar mastodon-group-notifications)
(defvar mastodon-notifications-grouped-names-count)
+(defvar mastodon-tl--link-keymap)
;;; VARIABLES
@@ -298,8 +302,7 @@ Can be called in notifications view or in follow-requests
view."
str))))
(status (mastodon-tl--field 'status note))
(follower (alist-get 'account note))
- (follower-name (or (alist-get 'display_name follower)
- (alist-get 'username follower)))
+ (follower-name (mastodon-notifications--follower-name follower))
(filtered (mastodon-tl--field 'filtered status))
(filters (when filtered
(mastodon-tl--current-filters filtered))))
@@ -336,8 +339,7 @@ ACCOUNTS is data of the accounts that have reacted to the
notification."
str))))
(follower (when (member type '(follow follow_request))
(car accounts)))
- (follower-name (or (alist-get 'display_name follower)
- (alist-get 'username follower)))
+ (follower-name (mastodon-notifications--follower-name follower))
(filtered (mastodon-tl--field 'filtered status))
(filters (when filtered
(mastodon-tl--current-filters filtered))))
@@ -358,6 +360,12 @@ ACCOUNTS is data of the accounts that have reacted to the
notification."
status)
folded group accounts))))))
+(defun mastodon-notifications--follower-name (follower)
+ "Return display_name or username of FOLLOWER."
+ (if (not (string= "" (alist-get 'display_name follower)))
+ (alist-get 'display_name follower)
+ (alist-get 'username follower)))
+
(defun mastodon-notifications--comment-note-text (str)
"Add comment face to all text in STR with `shr-text' face only."
(with-temp-buffer
@@ -367,7 +375,7 @@ ACCOUNTS is data of the accounts that have reacted to the
notification."
(while (setq prop (text-property-search-forward 'face 'shr-text t))
(add-text-properties (prop-match-beginning prop)
(prop-match-end prop)
- '(face (font-lock-comment-face shr-text)))))
+ '(face (mastodon-toot-docs-face shr-text)))))
(buffer-string)))
(defun mastodon-notifications--body-arg
@@ -377,7 +385,7 @@ The string returned is passed to
`mastodon-notifications--insert-note'.
TYPE is a symbol, a member of `mastodon-notifiations--types'.
FILTERS STATUS PROFILE-NOTE FOLLOWER-NAME GROUP NOTE."
(let ((body
- (if-let ((match (assoc "warn" filters)))
+ (if-let* ((match (assoc "warn" filters)))
(mastodon-tl--spoiler status (cadr match))
(mastodon-tl--clean-tabs-and-nl
(cond ((mastodon-tl--has-spoiler status)
@@ -796,23 +804,139 @@ Status notifications are created when you call
(resp (mastodon-http--get-json url)))
(alist-get 'count resp)))
-(defvar mastodon-notifications--policy-vals
+;;; NOTIFICATION REQUESTS / FILTERING / POLICY
+
+(defvar mastodon-notifications--requests-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map mastodon-mode-map)
+ (define-key map (kbd "j") #'mastodon-notifications--request-reject)
+ (define-key map (kbd "a") #'mastodon-notifications--request-accept)
+ (define-key map (kbd "g") #'mastodon-notifications--requests)
+ map)
+ "Keymap for viewing follow requests.")
+
+;; FIXME: these are only for grouped notifs, else the fields are JSON bools
+(defvar mastodon-notifications-policy-vals
'("accept" "filter" "drop"))
(defun mastodon-notifications--get-policy ()
"Return the notification filtering policy."
(interactive)
- (let ((url
- (mastodon-notifications--api "notifications/policy")))
+ (let ((url (mastodon-notifications--api "notifications/policy")))
(mastodon-http--get-json url)))
+(defun mastodon-notifications--pending-p ()
+ "Non-nil if there are any pending requests or notifications."
+ (let* ((json (mastodon-notifications--get-policy))
+ (summary (alist-get 'summary json)))
+ (or (not (= 0 (alist-get 'pending_requests_count summary)))
+ (not (= 0 (alist-get 'pending_notifications_count summary))))))
+
(defun mastodon-notifications--update-policy (&optional params)
"Update notifications filtering policy.
PARAMS is an alist of parameters."
;;
https://docs.joinmastodon.org/methods/notifications/#update-the-filtering-policy-for-notifications
- (let ((url
- (mastodon-notifications--api "notifications/policy")))
+ (let ((url (mastodon-notifications--api "notifications/policy")))
(mastodon-http--patch url params)))
+(defun mastodon-notifications--get-requests (&optional params)
+ "Get a list of notification requests data from the server.
+PARAMS is an alist of parameters."
+ ;; NB: link header pagination
+ (let ((url (mastodon-notifications--api "notifications/requests")))
+ (mastodon-http--get-json url params)))
+
+(defun mastodon-notifications--request-accept (&optional reject)
+ "Accept a notification request for a user.
+This will merge any filtered notifications from them into the main
+notifications and accept any future notification from them.
+REJECT means reject notifications instead."
+ ;; POST /api/v1/notifications/requests/:id/accept
+ (interactive)
+ (let* ((id (mastodon-tl--property 'item-id))
+ (user (mastodon-tl--property 'notif-req-user))
+ (url (mastodon-http--api
+ (format "notifications/requests/%s/%s"
+ id (if reject "dismiss" "accept"))))
+ (resp (mastodon-http--post url)))
+ (mastodon-http--triage
+ resp
+ (lambda (_resp)
+ (message "%s notifications from %s"
+ (if reject "Not accepting" "Accepting") user)))))
+
+(defun mastodon-notifications--request-reject ()
+ "Reject a notification request for a user.
+Rejecting a request means any notifications from them will continue to
+be filtered."
+ (interactive)
+ (mastodon-notifications--request-accept :reject))
+
+(defun mastodon-notifications--requests ()
+ "Open a new buffer displaying the user's notification requests."
+ ;; calqued off `mastodon-views--view-follow-requests'
+ (interactive)
+ (mastodon-tl--init-sync
+ "notification-requests"
+ "notifications/requests"
+ 'mastodon-views--insert-notification-requests
+ nil
+ '(("limit" . "40")) ; server max is 80
+ :headers
+ "notification requests"
+ "a/j - accept/reject request at point\n\
+ n/p - go to next/prev request")
+ (mastodon-tl--goto-first-item)
+ (with-current-buffer "*mastodon-notification-requests*"
+ (use-local-map mastodon-notifications--requests-map)))
+
+(defun mastodon-views--insert-notification-requests (json)
+ "Insert the user's current notification requests.
+JSON is the data returned by the server."
+ (mastodon-views--minor-view
+ "notification requests"
+ #'mastodon-notifications--insert-users
+ json))
+;; masto-notif-req))
+
+(defun mastodon-notifications--insert-users (json)
+ "Insert users list into the buffer.
+JSON is the data from the server."
+ ;; calqued off `mastodon-views--insert-users-propertized-note'
+ ;; and `mastodon-search--insert-users-propertized'
+ (mapc (lambda (req)
+ (insert
+ (concat
+ (mastodon-notifications--format-req-user req)
+ mastodon-tl--horiz-bar "\n\n")))
+ json))
+
+(defun mastodon-notifications--format-req-user (req &optional note)
+ "Format a notification request user, REQ.
+NOTE means to include a profile note."
+ ;; calqued off `mastodon-search--propertize-user'
+ (let-alist req
+ (propertize
+ (concat
+ (propertize .account.username
+ 'face 'mastodon-display-name-face
+ 'byline t
+ 'notif-req-user .account.username
+ 'item-type 'notif-req
+ 'item-id .id) ;; notif req id
+ " : \n : "
+ (propertize (concat "@" .account.acct)
+ 'face 'mastodon-handle-face
+ 'mouse-face 'highlight
+ 'mastodon-tab-stop 'user-handle
+ 'keymap mastodon-tl--link-keymap
+ 'mastodon-handle (concat "@" .account.acct)
+ 'help-echo (concat "Browse user profile of @" .account.acct))
+ " : \n"
+ (when note
+ (mastodon-tl--render-text .account.note .account))
+ "\n")
+ 'item-json req)))
+
(provide 'mastodon-notifications)
;;; mastodon-notifications.el ends here
diff --git a/lisp/mastodon-profile.el b/lisp/mastodon-profile.el
index 417a787294..8852806d91 100644
--- a/lisp/mastodon-profile.el
+++ b/lisp/mastodon-profile.el
@@ -178,7 +178,8 @@ MAX-ID is a flag to include the max_id pagination
parameter."
(map-keys mastodon-profile--account-view-alist))
(defun mastodon-profile--account-view-cycle (&optional prefix)
- "Cycle through profile view: toots, toot sans boosts, followers, and
following."
+ "Cycle through profile view: toots, toot sans boosts, followers, and
following.
+If a PREFIX argument is provided, prompt for a view type and load."
(interactive "P")
(if prefix
(let* ((choice
@@ -335,7 +336,7 @@ If value is :json-false, return nil."
'display nil)
"/500 characters")
'read-only t
- 'face 'font-lock-comment-face
+ 'face 'mastodon-toot-docs-face
'note-header t)
"\n")
(make-local-variable 'after-change-functions)
@@ -729,25 +730,25 @@ MAX-ID is a flag to include the max_id pagination
parameter."
;; sharkey has no relationships endpoint, returns 500.
;; or poss it has a different endpoint
""
- (let* ((followsp (mastodon-profile--follows-p
- (list .requested_by .following
.followed_by)))
- (rels (mastodon-profile--relationships-get .id))
- (langs-filtered (if-let ((langs (alist-get 'languages
rels)))
- (concat " ("
- (mapconcat #'identity
langs " ")
- ")")
- "")))
- (if followsp
- (mastodon-tl--set-face
- (concat (when (eq .following t)
- (format " | FOLLOWED BY YOU%s"
langs-filtered))
- (when (eq .followed_by t)
- " | FOLLOWS YOU")
- (when (eq .requested_by t)
- " | REQUESTED TO FOLLOW YOU")
- "\n\n")
- 'success)
- ""))))) ; for insert call
+ (let* ((followsp (mastodon-profile--follows-p
+ (list .requested_by .following .followed_by)))
+ (rels (mastodon-profile--relationships-get .id))
+ (langs-filtered (if-let* ((langs (alist-get 'languages
rels)))
+ (concat " ("
+ (mapconcat #'identity langs
" ")
+ ")")
+ "")))
+ (if followsp
+ (mastodon-tl--set-face
+ (concat (when (eq .following t)
+ (format " | FOLLOWED BY YOU%s" langs-filtered))
+ (when (eq .followed_by t)
+ " | FOLLOWS YOU")
+ (when (eq .requested_by t)
+ " | REQUESTED TO FOLLOW YOU")
+ "\n\n")
+ 'success)
+ ""))))) ; for insert call
(mastodon-media--inline-images (point-min) (point))
;; widget items description
(mastodon-widget--create
diff --git a/lisp/mastodon-search.el b/lisp/mastodon-search.el
index 5f1e98090d..bb6a388bb3 100644
--- a/lisp/mastodon-search.el
+++ b/lisp/mastodon-search.el
@@ -201,13 +201,12 @@ is used for pagination."
(limit (or limit "40"))
(offset (or offset "0"))
(buffer (format "*mastodon-search-%s-%s*" type query))
- (params (cl-remove nil
- `(("q" . ,query)
- ,(when type `("type" . ,type))
- ,(when limit `("limit" . ,limit))
- ,(when offset `("offset" . ,offset))
- ,(when following `("following" . ,following))
- ,(when account-id `("account_id" .
,account-id)))))
+ (params `(("q" . ,query)
+ ,@(when type `(("type" . ,type)))
+ ,@(when limit `(("limit" . ,limit)))
+ ,@(when offset `(("offset" . ,offset)))
+ ,@(when following `(("following" . ,following)))
+ ,@(when account-id `(("account_id" . ,account-id)))))
(response (mastodon-http--get-json url params))
(items (alist-get (intern type) response)))
(with-mastodon-buffer buffer #'mastodon-mode nil
@@ -243,7 +242,7 @@ is used for pagination."
(let ((thing (or thing "items")))
(insert
(propertize (format "Looks like search returned no %s." thing)
- 'face 'font-lock-comment-face))))
+ 'face 'mastodon-toot-docs-face))))
(defun mastodon-search--render-response (data type buffer params
insert-fun update-fun)
diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el
index 781c27363b..6e755fb046 100644
--- a/lisp/mastodon-tl.el
+++ b/lisp/mastodon-tl.el
@@ -471,15 +471,13 @@ With a single PREFIX arg, hide-replies.
With a double PREFIX arg, only show posts with media."
(interactive "p")
(let ((params
- (cl-remove
- nil
- `(("limit" . ,mastodon-tl--timeline-posts-count)
- ,(when (eq prefix 16)
- '("only_media" . "true"))
- ,(when local
- '("local" . "true"))
- ,(when max-id
- `("max_id" . ,(mastodon-tl--buffer-property 'max-id)))))))
+ `(("limit" . ,mastodon-tl--timeline-posts-count)
+ ,@(when (eq prefix 16)
+ '(("only_media" . "true")))
+ ,@(when local
+ '(("local" . "true")))
+ ,@(when max-id
+ `(("max_id" . ,(mastodon-tl--buffer-property 'max-id)))))))
(message "Loading federated timeline...")
(mastodon-tl--init (if local "local" "federated")
"timelines/public" 'mastodon-tl--timeline nil
@@ -574,13 +572,11 @@ If TAG is a list, show a timeline for all tags.
With a single PREFIX arg, only show posts with media.
With a double PREFIX arg, limit results to your own instance."
(let ((params
- (cl-remove
- nil
- `(("limit" . ,mastodon-tl--timeline-posts-count)
- ,(when (eq prefix 4)
- '("only_media" . "true"))
- ,(when (eq prefix 16)
- '("local" . "true"))))))
+ `(("limit" . ,mastodon-tl--timeline-posts-count)
+ ,@(when (eq prefix 4)
+ '(("only_media" . "true")))
+ ,@(when (eq prefix 16)
+ '(("local" . "true"))))))
(when (listp tag)
(let ((list (mastodon-http--build-array-params-alist "any[]" (cdr tag))))
(while list
@@ -897,10 +893,10 @@ TS is a timestamp from the server, if any."
'face 'mastodon-display-name-face
'follow-link t
'mouse-face 'highlight
- 'mastodon-tab-stop 'shr-url
- 'shr-url app-url
+ 'mastodon-tab-stop 'shr-url
+ 'shr-url app-url
'help-echo app-url
- 'keymap mastodon-tl--shr-map-replacement)))))
+ 'keymap mastodon-tl--shr-map-replacement)))))
;; edited:
(when edited-time
(concat
@@ -910,7 +906,7 @@ TS is a timestamp from the server, if any."
(propertize
(format-time-string mastodon-toot-timestamp-format
edited-parsed)
- 'face 'font-lock-comment-face
+ 'face 'mastodon-toot-docs-face
'timestamp edited-parsed
'display (if mastodon-tl--enable-relative-timestamps
(mastodon-tl--relative-time-description edited-parsed)
@@ -1014,7 +1010,7 @@ links in the text. If TOOT is nil no parsing occurs."
(defun mastodon-tl--format-card-author (data)
"Render card author DATA."
- (when-let ((account (alist-get 'account data))) ;.account
+ (when-let* ((account (alist-get 'account data))) ;.account
(let-alist account ;.account
;; FIXME: replace with refactored handle render fun
;; in byline refactor branch:
@@ -1027,10 +1023,10 @@ links in the text. If TOOT is nil no parsing occurs."
(propertize (concat "@" .acct)
'face 'mastodon-handle-face
'mouse-face 'highlight
- 'mastodon-tab-stop 'user-handle
- 'keymap mastodon-tl--link-keymap
+ 'mastodon-tab-stop 'user-handle
+ 'keymap mastodon-tl--link-keymap
'mastodon-handle (concat "@" .acct)
- 'help-echo (concat "Browse user profile of @" .acct))))))
+ 'help-echo (concat "Browse user profile of @"
.acct))))))
(defun mastodon-tl--process-link (toot start end url)
"Process link URL in TOOT as hashtag, userhandle, or normal link.
@@ -1474,13 +1470,13 @@ LENGTH is of the longest option, for formatting."
(.vote_count
(format "%s votes | " .vote_count))
(t ""))
- 'face 'font-lock-comment-face)
+ 'face 'mastodon-toot-docs-face)
(let ((str (if (eq .expired :json-false)
(if (eq .expires_at nil)
""
(mastodon-tl--format-poll-expiry .expires_at))
"Poll expired.")))
- (propertize str 'face 'font-lock-comment-face))
+ (propertize str 'face 'mastodon-toot-docs-face))
"\n"))))
(defconst mastodon-tl--time-units
@@ -1653,7 +1649,7 @@ in which case play first video or gif from current toot."
(defun mastodon-tl--copy-image-caption ()
"Copy the caption of the image at point."
(interactive)
- (if-let ((desc (get-text-property (point) 'image-description)))
+ (if-let* ((desc (get-text-property (point) 'image-description)))
(progn
(kill-new desc)
(message "Image caption copied."))
@@ -1811,7 +1807,7 @@ CW-EXPANDED means treat content warnings as unfolded."
(filtered (mastodon-tl--field 'filtered toot))
(filters (when filtered
(mastodon-tl--current-filters filtered)))
- (spoiler-or-content (if-let ((match (assoc "warn" filters)))
+ (spoiler-or-content (if-let* ((match (assoc "warn" filters)))
(mastodon-tl--spoiler toot (cadr match))
(if (mastodon-tl--has-spoiler toot)
(mastodon-tl--spoiler toot)
@@ -1837,8 +1833,8 @@ NO-BYLINE means just insert toot body, used for folding."
(mastodon-tl--buffer-property 'hide-replies nil :no-error)
;; loading a tl with a prefix arg:
(mastodon-tl--hide-replies-p current-prefix-arg))
- (cl-remove-if-not #'mastodon-tl--is-reply toots)
- toots))))
+ (cl-remove-if-not #'mastodon-tl--is-reply toots)
+ toots))))
(mapc (lambda (toot)
(mastodon-tl--toot toot nil thread domain nil no-byline))
toots)
@@ -1976,19 +1972,19 @@ To disable showing the stats, customize
'favourited-p (eq t .favourited)
'favourites-field t
'help-echo (format "%s favourites"
.favourites_count)
- 'face 'font-lock-comment-face)
- (propertize " | " 'face 'font-lock-comment-face)
+ 'face 'mastodon-toot-docs-face)
+ (propertize " | " 'face 'mastodon-toot-docs-face)
(propertize boosts
'boosted-p (eq t .reblogged)
'boosts-field t
'help-echo (format "%s boosts" .reblogs_count)
- 'face 'font-lock-comment-face)
- (propertize " | " 'face 'font-lock-comment-face)
+ 'face 'mastodon-toot-docs-face)
+ (propertize " | " 'face 'mastodon-toot-docs-face)
(propertize replies
'replies-field t
'replies-count .replies_count
'help-echo (format "%s replies" .replies_count)
- 'face 'font-lock-comment-face)))
+ 'face 'mastodon-toot-docs-face)))
(right-spacing
(propertize " "
'display
@@ -2101,6 +2097,8 @@ call this function after it is set or use something else."
'mentions)
((mastodon-tl--endpoint-str-= "notifications")
'notifications)
+ ((mastodon-tl--endpoint-str-= "notifications/requests")
+ 'notification-requests)
;; threads:
((mastodon-tl--endpoint-str-= "context" :suffix)
'thread)
@@ -2414,7 +2412,7 @@ programmatically and not crash into
(when unfolded-state
(plist-put mastodon-tl--buffer-spec
'thread-unfolded unfolded-state))
- (when-let ((ancestors (alist-get 'ancestors context)))
+ (when-let* ((ancestors (alist-get 'ancestors context)))
(mastodon-tl--timeline ancestors :thread))
(goto-char (point-max))
(move-marker marker (point))
@@ -2425,7 +2423,7 @@ programmatically and not crash into
(when mastodon-tl--display-media-p
(mastodon-media--inline-images marker ;start-pos
(point)))
- (when-let ((descendants (alist-get 'descendants context)))
+ (when-let* ((descendants (alist-get 'descendants context)))
(mastodon-tl--timeline descendants :thread))
;; put point at the toot:
(goto-char (marker-position marker))
@@ -2877,6 +2875,28 @@ PREFIX is for `mastodon-tl--show-tag-timeline', which
see."
tags)))
(mastodon-tl--show-tag-timeline prefix selection)))
+(defcustom mastodon-tl--tags-groups nil
+ "A list containing lists of up to four tags each.
+You can load a tag timeline list with one of these by calling
+`mastodon-tl--tag-group-timeline'."
+ :group 'mastodon-tl
+ :type '(repeat (list string string string string)))
+
+(defun mastodon-tl--tag-group-timeline (&optional prefix)
+ "Load a timeline of a tag group from `mastodon-tl--tags-groups'.
+PREFIX is for `mastodon-tl--show-tag-timeline', which see."
+ (interactive "P")
+ (if (not mastodon-tl--tags-groups)
+ (user-error
+ "Set `mastodon-tl--tags-groups' to view tag group timelines")
+ (let* ((list-strs (mapcar (lambda (x)
+ ;; cons of list-as-string and list:
+ (cons (prin1-to-string x) x))
+ mastodon-tl--tags-groups))
+ (choice (completing-read "Tag group: " list-strs))
+ (choice-list (cdr (assoc choice list-strs #'equal))))
+ (mastodon-tl--show-tag-timeline prefix choice-list))))
+
;;; REPORT TO MODERATORS
@@ -2904,13 +2924,12 @@ ACCOUNT and TOOT are the data to use."
"Build the parameters alist based on user responses.
ACCOUNT-ID, COMMENT, ITEM-ID, FORWARD-P, CAT, and RULES are all from
`mastodon-tl--report-params', which see."
- (let ((params (cl-remove
- nil
- `(("account_id" . ,account-id)
- ,(when comment `("comment" . ,comment))
- ,(when item-id `("status_ids[]" . ,item-id))
- ,(when forward-p `("forward" . ,forward-p))
- ,(when cat `("category" . ,cat))))))
+ (let ((params
+ `(("account_id" . ,account-id)
+ ,@(when comment `(("comment" . ,comment)))
+ ,@(when item-id `(("status_ids[]" . ,item-id)))
+ ,@(when forward-p `(("forward" . ,forward-p)))
+ ,@(when cat `(("category" . ,cat))))))
(if (not rules)
params
(let ((alist
@@ -3072,7 +3091,7 @@ and profile pages when showing followers or accounts
followed."
(defun mastodon-tl--get-link-header-from-response (headers)
"Get http Link header from list of http HEADERS."
;; pleroma uses "link", so case-insensitive match required:
- (when-let ((link-headers (alist-get "Link" headers nil nil #'cl-equalp)))
+ (when-let* ((link-headers (alist-get "Link" headers nil nil #'cl-equalp)))
(split-string link-headers ", ")))
(defun mastodon-tl--more ()
@@ -3459,7 +3478,7 @@ ENDPOINT-VERSION is a string, format Vx, e.g. V2."
(mastodon-search--insert-heading view-name))
(when binding-str
(insert (mastodon-tl--set-face (concat "[" binding-str "]\n\n")
- 'font-lock-comment-face)))
+ 'mastodon-toot-docs-face)))
(mastodon-tl--set-buffer-spec
buffer endpoint update-function
link-header params nil
diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el
index b80dc468b8..6d9295f6d9 100644
--- a/lisp/mastodon-toot.el
+++ b/lisp/mastodon-toot.el
@@ -611,7 +611,7 @@ base toot."
Uses `lingva.el'."
(interactive)
(if mastodon-tl--buffer-spec
- (if-let ((toot (mastodon-tl--property 'item-json)))
+ (if-let* ((toot (mastodon-tl--property 'item-json)))
(condition-case x
(lingva-translate nil
(mastodon-tl--content toot)
@@ -1006,7 +1006,8 @@ instance to edit a toot."
;; adopt reply-to-id, visibility, CW, language, and media:
(mastodon-toot--set-toot-properties .in_reply_to_id .visibility
source-cw .language nil nil
- .media_attachments .poll)
+ ;; maintain media order:
+ (reverse .media_attachments)
.poll)
(setq mastodon-toot--edit-item-id id))))))))
(defun mastodon-toot--get-toot-source (id)
@@ -1033,7 +1034,7 @@ instance to edit a toot."
do (insert (propertize (if (= count 1)
(format "%s [original]:\n" count)
(format "%s:\n" count))
- 'face 'font-lock-comment-face)
+ 'face 'mastodon-toot-docs-face)
(mastodon-toot--insert-toot-iter x)
"\n"))
(goto-char (point-min))
@@ -1042,7 +1043,7 @@ instance to edit a toot."
(format "Edits to toot by %s:"
(alist-get 'username
(alist-get 'account (car history))))
- 'face 'font-lock-comment-face))
+ 'face 'mastodon-toot-docs-face))
(mastodon-tl--set-buffer-spec (buffer-name (current-buffer))
(format "statuses/%s/history" id)
nil)))))
@@ -1627,7 +1628,7 @@ e.g. `mastodon-toot--send' -> Send."
(substitute-command-keys
(format
(concat (mastodon-toot--comment " ")
- "%s"
+ "%-10s"
(mastodon-toot--comment " - %s"))
key command))))
@@ -1979,7 +1980,7 @@ Added to `after-change-functions'."
(save-match-data
(let* ((fill-column 67))
(goto-char (point-min))
- (when-let ((prop (text-property-search-forward 'toot-reply)))
+ (when-let* ((prop (text-property-search-forward 'toot-reply)))
(fill-region (prop-match-beginning prop)
(point)))))))
diff --git a/lisp/mastodon-transient.el b/lisp/mastodon-transient.el
index 3e8ba5f820..ccd6d91b33 100644
--- a/lisp/mastodon-transient.el
+++ b/lisp/mastodon-transient.el
@@ -45,6 +45,7 @@
(autoload 'mastodon-toot--read-poll-expiry "mastodon-toot")
(autoload 'mastodon-toot--poll-expiry-options-alist "mastodon-toot")
(autoload 'mastodon-toot--clear-poll "mastodon-toot")
+(autoload 'mastodon-notifications--get-policy "mastodon-notifications")
;;; UTILS
@@ -131,7 +132,7 @@ the format fields.X.keyname."
(resp (mastodon-http--patch url strs))) ;; :json fails
(mastodon-http--triage
resp
- (lambda (_)
+ (lambda (_resp)
(message "Settings updated!\n%s" (pp-to-string strs))))))
(transient-define-prefix mastodon-user-settings ()
@@ -155,19 +156,17 @@ the format fields.X.keyname."
("d" "discoverable" "discoverable" :alist-key discoverable :class tp-bool)
("c" "hide follower/following lists" "source.hide_collections"
:alist-key source.hide_collections :class tp-bool)
- ("i" "indexable" "source.indexable" :alist-key source.indexable :class
tp-bool)
- ]
+ ("i" "indexable" "source.indexable" :alist-key source.indexable :class
tp-bool)]
["Tooting options"
("p" "default privacy" "source.privacy" :alist-key source.privacy
:class tp-option
:choices (lambda () mastodon-toot-visibility-settings-list))
("s" "mark sensitive" "source.sensitive" :alist-key source.sensitive :class
tp-bool)
("g" "default language" "source.language" :alist-key source.language :class
tp-option
- :choices (lambda () mastodon-iso-639-regional))
- ]
+ :choices (lambda () mastodon-iso-639-regional))]
["Update"
("C-c C-c" "Save settings" mastodon-user-settings-update)
- ("C-c C-k" :info "Revert all changes")]
+ ("C-x C-k" :info "Revert all changes")]
(interactive)
(if (or (not (boundp 'mastodon-active-user))
(not mastodon-active-user))
@@ -192,7 +191,7 @@ the format fields.X.keyname."
(url (mastodon-http--api endpoint))
(resp (mastodon-http--patch url arrays))) ; :json)))
(mastodon-http--triage
- resp (lambda (_) (message "Fields updated!")))))
+ resp (lambda (_resp) (message "Fields updated!")))))
(defun mastodon-transient-fetch-fields ()
"Fetch profile fields (metadata)."
@@ -217,7 +216,7 @@ the format fields.X.keyname."
("4 v" "" "fields.4.value" :alist-key fields.4.value :class
mastodon-transient-field)]]
["Update"
("C-c C-c" "Save settings" mastodon-profile-fields-update)
- ("C-c C-k" :info "Revert all changes")]
+ ("C-x C-k" :info "Revert all changes")]
(interactive)
(if (not mastodon-active-user)
(user-error "User not set")
@@ -345,8 +344,72 @@ Do not add more than the server's maximum setting."
(tp-bools-to-strs args)))
(mastodon-toot--update-status-fields))))
+(defvar mastodon-notifications-policy-vals)
+(declare-function mastodon-notifications--get-policy "mastodon-notifications")
+(declare-function mastodon-notifications--update-policy
"mastodon-notifications")
+
+(transient-define-prefix mastodon-notifications-policy ()
+ "A transient to set notifications policy options."
+ ;; https://docs.joinmastodon.org/methods/notifications/#get-policy
+ :value (lambda () (tp-return-data #'mastodon-notifications--get-policy))
+ ["Notification policy options"
+ ("f" "people you don't follow" "for_not_following"
+ :alist-key for_not_following :class mastodon-transient-policy)
+ ("F" "people not following you" "for_not_followers"
+ :alist-key for_not_followers :class mastodon-transient-policy)
+ ("n" "New accounts" "for_new_accounts"
+ :alist-key for_new_accounts :class mastodon-transient-policy)
+ ("p" "Unsolicited private mentions" "for_private_mentions"
+ :alist-key for_private_mentions :class mastodon-transient-policy)
+ ("l" "Moderated accounts" "for_limited_accounts"
+ :alist-key for_limited_accounts :class mastodon-transient-policy)
+ (:info "")
+ (:info "\"accept\" = receive notifications")
+ (:info "\"filter\" = mark as filtered")
+ (:info "\"drop\" = do not receive any notifications")]
+ ["Notification requests"
+ (:info #'mastodon-notifications-requests-count)
+ (:info #'mastodon-notifications-filtered-count)]
+ ["Update"
+ ("C-c C-c" "Save settings" mastodon-notifications-policy-update)
+ ("C-x C-k" :info "Revert all changes")])
+
+(defun mastodon-notifications-requests-count ()
+ "Format a string for pending requests."
+ (let ((val (oref transient--prefix value)))
+ (format "Pending requests: %d"
+ (or (map-nested-elt
+ val
+ '(summary pending_requests_count))
+ 0))))
+
+(defun mastodon-notifications-filtered-count ()
+ "Format a string for pending notifications."
+ (let ((val (oref transient--prefix value)))
+ (format "Pending notifications: %d"
+ (or (map-nested-elt
+ val
+ '(summary pending_notifications_count))
+ 0))))
+
+(transient-define-suffix mastodon-notifications-policy-update (args)
+ "Send updated notification policy settings."
+ :transient 'transient--do-exit
+ ;; TODO:
+ (interactive (list (transient-args 'mastodon-notifications-policy)))
+ (let* ((parsed (tp-parse-args-for-send args))
+ (resp (mastodon-notifications--update-policy parsed)))
+ (mastodon-http--triage
+ resp
+ (lambda (_resp)
+ (message "Settings updated!\n%s" (pp-to-string parsed))))))
+
;;; CLASSES
+(defclass mastodon-transient-policy (tp-cycle)
+ ((choices :initarg :choices :initform 'mastodon-notifications-policy-vals))
+ "An option class for mastodon notification policy options.")
+
(defclass mastodon-transient-field (tp-option-str)
((always-read :initarg :always-read :initform t))
"An infix option class for our options.
diff --git a/lisp/mastodon-views.el b/lisp/mastodon-views.el
index 8d356fb1b3..a36af10f74 100644
--- a/lisp/mastodon-views.el
+++ b/lisp/mastodon-views.el
@@ -171,7 +171,7 @@ provides the JSON data."
(if (seq-empty-p data)
(insert (propertize
(format "Looks like you have no %s for now." view-name)
- 'face 'font-lock-comment-face
+ 'face 'mastodon-toot-docs-face
'byline t
'item-type 'no-item ; for nav
'item-id "0")) ; so point can move here when no item
@@ -232,7 +232,7 @@ a: add account to this list, r: remove account from this
list"
(propertize (format " [replies: %s, exclusive %s]"
.replies_policy
(when (eq t .exclusive) "true"))
- 'face 'font-lock-comment-face)
+ 'face 'mastodon-toot-docs-face)
(propertize "\n\n"
'list t
'keymap mastodon-views--list-name-keymap
@@ -516,7 +516,7 @@ JSON is the data returned by the server."
(mastodon-toot--iso-to-human .scheduled_at))
'byline t ; so we nav here
'item-type 'scheduled ; so we nav here
- 'face 'font-lock-comment-face
+ 'face 'mastodon-toot-docs-face
'keymap mastodon-views--scheduled-map
'item-json toot
'id .id)
@@ -875,6 +875,10 @@ If INSTANCE is given, use that."
((string-suffix-p "profile/" (url-basepath url))
(string-remove-suffix "/profile/"
(url-basepath url)))
+ ;; snac is https://instance.com/user
+ ((not (string-match-p "@" url))
+ ;; cull trailing slash:
+ (string-trim-right (url-basepath url) "/"))
;; mastodon is https://instance.com/@user
(t
(string-remove-suffix (concat "/@" username)
diff --git a/lisp/mastodon.el b/lisp/mastodon.el
index 5092decbd0..dcf8132d3f 100644
--- a/lisp/mastodon.el
+++ b/lisp/mastodon.el
@@ -6,7 +6,7 @@
;; Author: Johnson Denen <[email protected]>
;; Marty Hiatt <[email protected]>
;; Maintainer: Marty Hiatt <[email protected]>
-;; Version: 1.1.8
+;; Version: 1.1.9
;; Package-Requires: ((emacs "28.1") (request "0.3.0") (persist "0.4") (tp
"0.6"))
;; Homepage: https://codeberg.org/martianh/mastodon.el
@@ -103,6 +103,9 @@
(autoload 'mastodon-tl--scroll-up-command "mastodon-tl")
(autoload 'special-mode "simple")
(autoload 'mastodon-tl--thread-do "mastodon-tl")
+(autoload 'mastodon-notifications-policy "mastodon-notifications")
+(autoload 'mastodon-notifications--requests "mastodon-notifications")
+(autoload 'mastodon-tl--tag-group-timeline "mastodon-tl")
(defvar mastodon-tl--highlight-current-toot)
(defvar mastodon-notifications--map)
@@ -207,11 +210,13 @@ and X others...\"."
(define-key map (kbd "#") #'mastodon-tl--get-tag-timeline)
(define-key map (kbd "\"") #'mastodon-tl--list-followed-tags)
(define-key map (kbd "'") #'mastodon-tl--followed-tags-timeline)
+ (define-key map (kbd "C-'") #'mastodon-tl--tag-group-timeline)
(define-key map (kbd "A") #'mastodon-profile--get-toot-author)
(define-key map (kbd "F") #'mastodon-tl--get-federated-timeline)
(define-key map (kbd "H") #'mastodon-tl--get-home-timeline)
(define-key map (kbd "L") #'mastodon-tl--get-local-timeline)
(define-key map (kbd "N") #'mastodon-notifications-get)
+ (define-key map (kbd "S-C-n") #'mastodon-notifications--requests)
(define-key map (kbd "@") #'mastodon-notifications--get-mentions)
(define-key map (kbd "P") #'mastodon-profile--show-user)
(define-key map (kbd "s") #'mastodon-search--query)
@@ -264,6 +269,7 @@ and X others...\"."
(define-key map (kbd "V") #'mastodon-profile--view-favourites)
(define-key map (kbd "K") #'mastodon-profile--view-bookmarks)
(define-key map (kbd ":") #'mastodon-user-settings)
+ (define-key map (kbd "C-:") #'mastodon-notifications-policy)
;; minor views
(define-key map (kbd "R") #'mastodon-views--view-follow-requests)
(define-key map (kbd "S") #'mastodon-views--view-scheduled-toots)
@@ -302,7 +308,7 @@ and X others...\"."
"Face used for content warning.")
(defface mastodon-toot-docs-face
- `((t :inherit font-lock-comment-face))
+ `((t :inherit shadow))
"Face used for documentation in toot compose buffer.
If `mastodon-tl--enable-proportional-fonts' is changed,
mastodon.el needs to be re-loaded for this to be correctly set.")
diff --git a/mastodon-index.org b/mastodon-index.org
index 7ef9f13b05..8ec6c5cbe9 100644
--- a/mastodon-index.org
+++ b/mastodon-index.org
@@ -71,7 +71,12 @@
| | mastodon-notifications--get-single-notif |
Return a single notification JSON for v2 notifs. |
| | mastodon-notifications--get-statuses |
Display status notifications in buffer. |
| | mastodon-notifications--get-type | Read
a notification type and load its timeline. |
+| | mastodon-notifications--request-accept |
Accept a notification request for a user. |
+| | mastodon-notifications--request-reject |
Reject a notification request for a user. |
+| C-S-n | mastodon-notifications--requests | Open
a new buffer displaying the user's notification requests. |
| N | mastodon-notifications-get |
Display NOTIFICATIONS in buffer. |
+| C-: | mastodon-notifications-policy | A
transient to set notifications policy options. |
+| | mastodon-notifications-policy-update | Send
updated notification policy settings. |
| | mastodon-profile--account-bot-toggle |
Toggle the bot status of your account. |
| | mastodon-profile--account-discoverable-toggle |
Toggle the discoverable status of your account. |
| | mastodon-profile--account-locked-toggle |
Toggle the locked status of your account. |
@@ -154,6 +159,7 @@
| SPC | mastodon-tl--scroll-up-command | Call
`scroll-up-command', loading more toots if necessary. |
| | mastodon-tl--single-toot | View
toot at point in separate buffer. |
| | mastodon-tl--some-followed-tags-timeline |
Prompt for some tags, and open a timeline for them. |
+| C-' | mastodon-tl--tag-group-timeline | Load
a timeline of a tag group from `mastodon-tl--tags-groups'. |
| RET, T | mastodon-tl--thread | Open
thread buffer for toot at point. |
| | mastodon-tl--toggle-sensitive-image |
Toggle dislay of sensitive image at point. |
| | mastodon-tl--toggle-spoiler-in-thread |
Toggler content warning for all posts in current thread. |
@@ -280,9 +286,7 @@
| mastodon-media--hide-sensitive-media | Whether media marked as
sensitive should be hidden. |
| mastodon-media--preview-max-height | Max height of any media
attachment preview to be shown in timelines. |
| mastodon-mode-hook | Hook run when entering
Mastodon mode. |
-| mastodon-notifications-check-for-updates | Whether to regularly
check for new notifications. |
| mastodon-notifications-grouped-names-count | The number of
notification authors to display. |
-| mastodon-notifications-updates-interval | How often to check for
new notifications, in seconds. |
| mastodon-profile-mode-hook | Hook run after entering
or leaving `mastodon-profile-mode'. |
| mastodon-profile-note-in-foll-reqs | If non-nil, show a
user's profile note in follow request notifications. |
| mastodon-profile-note-in-foll-reqs-max-length | The max character
length for user profile note in follow requests. |
@@ -303,6 +307,7 @@
| mastodon-tl--show-stats | Whether to show toot
stats (faves, boosts, replies counts). |
| mastodon-tl--symbols | A set of symbols (and
fallback strings) to be used in timeline. |
| mastodon-tl--tag-timeline-tags | A list of up to four
tags for use with `mastodon-tl--followed-tags-timeline'. |
+| mastodon-tl--tags-groups | A list containing lists
of up to four tags each. |
| mastodon-tl--timeline-posts-count | Number of posts to
display when loading a timeline. |
| mastodon-tl-position-after-update | Defines where `point'
should be located after a timeline update. |
| mastodon-toot--attachment-height | Height of the attached
images preview in the toot draft buffer. |