diff options
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/mastodon-media.el | 34 | ||||
| -rw-r--r-- | lisp/mastodon-notifications.el | 26 | ||||
| -rw-r--r-- | lisp/mastodon-tl.el | 256 | ||||
| -rw-r--r-- | lisp/mastodon-toot.el | 5 | ||||
| -rw-r--r-- | lisp/mastodon.el | 4 | 
5 files changed, 182 insertions, 143 deletions
| diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el index f7386c6..457628f 100644 --- a/lisp/mastodon-media.el +++ b/lisp/mastodon-media.el @@ -292,21 +292,27 @@ Replace them with the referenced image."                                   t image-options))       " "))) -(defun mastodon-media--get-media-link-rendering (media-url &optional full-remote-url) +(defun mastodon-media--get-media-link-rendering (media-url &optional full-remote-url type)    "Return the string to be written that renders the image at MEDIA-URL. -FULL-REMOTE-URL is used for `shr-browse-image'." -  (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) -               'mouse-face 'highlight -               'mastodon-tab-stop 'image ; for do-link-action-at-point -               'image-url full-remote-url ; for shr-browse-image -               'keymap mastodon-tl--shr-image-map-replacement -               'help-echo (concat "RET/i: load full image (prefix: copy URL), +/-: zoom, r: rotate, o: save preview")) -   " ")) +FULL-REMOTE-URL is used for `shr-browse-image'. +TYPE is the attachment's type field on the server." +  (let ((help-echo +         "RET/i: load full image (prefix: copy URL), +/-: zoom, r: rotate, o: save preview")) +    (concat +     (propertize "[img]" +                 'media-url media-url +                 'media-state 'needs-loading +                 'media-type 'media-link +                 'mastodon-media-type type +                 'display (create-image mastodon-media--generic-broken-image-data nil t) +                 'mouse-face 'highlight +                 'mastodon-tab-stop 'image ; for do-link-action-at-point +                 'image-url full-remote-url ; for shr-browse-image +                 'keymap mastodon-tl--shr-image-map-replacement +                 'help-echo (if (string= type "image") +                                help-echo +                              (concat help-echo "\ntype: " type))) +                 " ")))  (provide 'mastodon-media)  ;;; mastodon-media.el ends here diff --git a/lisp/mastodon-notifications.el b/lisp/mastodon-notifications.el index 4437635..15633be 100644 --- a/lisp/mastodon-notifications.el +++ b/lisp/mastodon-notifications.el @@ -54,7 +54,8 @@      ("follow" . mastodon-notifications--follow)      ("favourite" . mastodon-notifications--favourite)      ("reblog" . mastodon-notifications--reblog) -    ("follow_request" . mastodon-notifications--follow-request)) +    ("follow_request" . mastodon-notifications--follow-request) +    ("status" . mastodon-notifications--status))    "Alist of notification types and their corresponding function.")  (defvar mastodon-notifications--response-alist @@ -62,7 +63,8 @@      ("Followed" . "you")      ("Favourited" . "your status from")      ("Boosted" . "your status from") -    ("Requested to follow" . "you")) +    ("Requested to follow" . "you") +    ("Posted" . "a post"))    "Alist of subjects for notification types.")  (defun mastodon-notifications--byline-concat (message) @@ -204,6 +206,26 @@          "Boosted"))       id))) +(defun mastodon-notifications--status (note) +  "Format for a `status' NOTE. +Status notifications are given when +`mastodon-tl--notify-user-posts' has been set." +  (let ((id (cdr (assoc 'id note))) +        (status (mastodon-tl--field 'status note))) +    (mastodon-notifications--insert-status +     status +     (mastodon-tl--clean-tabs-and-nl +      (if (mastodon-tl--has-spoiler status) +          (mastodon-tl--spoiler status) +        (mastodon-tl--content status))) +     (lambda (_status) +       (mastodon-tl--byline-author +        note)) +     (lambda (_status) +       (mastodon-notifications--byline-concat +        "Posted")) +     id))) +  (defun mastodon-notifications--insert-status (toot body author-byline action-byline &optional id)    "Display the content and byline of timeline element TOOT. diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el index 5418374..46cd1d6 100644 --- a/lisp/mastodon-tl.el +++ b/lisp/mastodon-tl.el @@ -287,13 +287,13 @@ Optionally start from POS."       (propertize (concat "@" handle)                   'face 'mastodon-handle-face                   'mouse-face 'highlight -		 ;; TODO: Replace url browsing with native profile viewing -		 'mastodon-tab-stop 'user-handle +		         ;; TODO: Replace url browsing with native profile viewing +		         'mastodon-tab-stop 'user-handle                   'account account -		 'shr-url profile-url -		 'keymap mastodon-tl--link-keymap +		         'shr-url profile-url +		         'keymap mastodon-tl--link-keymap                   'mastodon-handle (concat "@" handle) -		 'help-echo (concat "Browse user profile of @" handle)) +		         'help-echo (concat "Browse user profile of @" handle))       ")")))  (defun mastodon-tl--byline-boosted (toot) @@ -676,10 +676,11 @@ message is a link which unhides/hides the main body."                                   (if (alist-get 'remote_url media-attachement)                                       (alist-get 'remote_url media-attachement)                                     ;; fallback b/c notifications don't have remote_url -                                   (alist-get 'url media-attachement)))) +                                   (alist-get 'url media-attachement))) +                                (type (alist-get 'type media-attachement)))                              (if mastodon-tl--display-media-p                                  (mastodon-media--get-media-link-rendering -                                 preview-url remote-url) ; 2nd arg for shr-browse-url +                                 preview-url remote-url type) ; 2nd arg for shr-browse-url                                (concat "Media::" preview-url "\n"))))                          media-attachements "")))      (if (not (and mastodon-tl--display-media-p @@ -695,9 +696,9 @@ message is a link which unhides/hides the main body."                       (alist-get 'poll reblog)                     (alist-get 'poll toot))))      (concat +     (mastodon-tl--render-text content toot)       (when poll-p         (mastodon-tl--get-poll toot)) -     (mastodon-tl--render-text content toot)       (mastodon-tl--media toot))))  (defun mastodon-tl--insert-status (toot body author-byline action-byline) @@ -729,16 +730,30 @@ takes a single function. By default it is    "If post TOOT is a poll, return a formatted string of poll."    (let* ((poll (mastodon-tl--field 'poll toot))           (options (mastodon-tl--field 'options poll)) +         (option-titles (mapcar (lambda (x) +                                  (alist-get 'title x)) +                                options)) +         (longest-option (car (sort option-titles +                                    (lambda (x y) +                                      (> (length x) +                                         (length y))))))           (option-counter 0)) -    (concat "Poll: \n\n" +    (concat "\nPoll: \n\n"              (mapconcat (lambda (option)                           (progn -                           (format "Option %s: %s, %s votes.\n" +                           (format "Option %s: %s%s [%s votes].\n"                                     (setq option-counter (1+ option-counter))                                     (alist-get 'title option) +                                   (make-string +                                    (1+ +                                     (- (length longest-option) +                                        (length (alist-get 'title +                                                           option)))) +                                    ?\ )                                     (alist-get 'votes_count option))))                         options -                       "\n") "\n"))) +                       "\n") +            "\n")))  (defun mastodon-tl--poll-vote (option)    "If there is a poll at point, prompt user for OPTION to vote on it." @@ -936,149 +951,138 @@ webapp"                                      (alist-get 'descendants context)))))        (message "No Thread!")))) -(defun mastodon-tl--follow-user (user-handle) -  "Query for USER-HANDLE from current status and follow that user." +(defun mastodon-tl--follow-user (user-handle &optional notify) +  "Query for USER-HANDLE from current status and follow that user. +If NOTIFY is \"true\", enable notifications when that user posts. +If NOTIFY is \"false\", disable notifications when that user posts. +This can be called to toggle NOTIFY on users already being followed."    (interactive     (list -    (let ((user-handles (mastodon-profile--extract-users-handles -                         (mastodon-profile--toot-json)))) -      (completing-read "Handle of user to follow: " -                       user-handles -                       nil ; predicate -                       'confirm)))) -  (let* ((account (mastodon-profile--lookup-account-in-status -                   user-handle (mastodon-profile--toot-json))) -         (user-id (mastodon-profile--account-field account 'id)) -         (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/follow" user-id)))) -    (if account -        (let ((response (mastodon-http--post url nil nil))) -          (mastodon-http--triage response -                                 (lambda () -                                   (message "User %s (@%s) followed!" name user-handle)))) -      (message "Cannot find a user with handle %S" user-handle)))) +    (mastodon-tl--interactive-user-handles-get "follow"))) +  (mastodon-tl--do-user-action-and-response user-handle "follow" nil notify)) -(defun mastodon-tl--unfollow-user (user-handle) -  "Query for USER-HANDLE from current status and unfollow that user." +(defun mastodon-tl--enable-notify-user-posts (user-handle) +  "Query for USER-HANDLE and enable notifications when they post."    (interactive     (list -    (let ((user-handles (mastodon-profile--extract-users-handles -                         (mastodon-profile--toot-json)))) -      (completing-read "Handle of user to unfollow: " -                       user-handles -                       nil ; predicate -                       'confirm)))) -  (let* ((account (mastodon-profile--lookup-account-in-status -                   user-handle (mastodon-profile--toot-json))) -         (user-id (mastodon-profile--account-field account 'id)) -         (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/unfollow" user-id)))) -    (if account -        (when (y-or-n-p (format "Unfollow user %s? " name)) -          (let ((response (mastodon-http--post url nil nil))) -            (mastodon-http--triage response -                                   (lambda () -                                     (message "User %s (@%s) unfollowed!" name user-handle))))) -      (message "Cannot find a user with handle %S" user-handle)))) +    (mastodon-tl--interactive-user-handles-get "enable"))) +  (mastodon-tl--follow-user user-handle "true")) -(defun mastodon-tl--mute-user (user-handle) -  "Query for USER-HANDLE from current status and mute that user." +(defun mastodon-tl--disable-notify-user-posts (user-handle) +  "Query for USER-HANDLE and disable notifications when they post."    (interactive     (list -    (let ((user-handles (mastodon-profile--extract-users-handles -                         (mastodon-profile--toot-json)))) -      (completing-read "Handle of user to mute: " -                       user-handles -                       nil ; predicate -                       'confirm)))) -  (let* ((account (mastodon-profile--lookup-account-in-status -                   user-handle (mastodon-profile--toot-json))) -         (user-id (mastodon-profile--account-field account 'id)) -         (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/mute" user-id)))) -    (if account -        (when (y-or-n-p (format "Mute user %s? " name)) -          (let ((response (mastodon-http--post url nil nil))) -            (mastodon-http--triage response -                                   (lambda () -                                     (message "User %s (@%s) muted!" name user-handle))))) -      (message "Cannot find a user with handle %S" user-handle)))) +    (mastodon-tl--interactive-user-handles-get "disable"))) +  (mastodon-tl--follow-user user-handle "false")) -(defun mastodon-tl--unmute-user (user-handle) -  "Query for USER-HANDLE from list of muted users and unmute that user." +(defun mastodon-tl--unfollow-user (user-handle) +  "Query for USER-HANDLE from current status and unfollow that user."    (interactive     (list -    (let* ((mutes-url (mastodon-http--api (format "mutes"))) -           (mutes-json (mastodon-http--get-json mutes-url)) -           (muted-accts (mapcar (lambda (muted) -                                  (alist-get 'acct muted)) -                                mutes-json))) -      (completing-read "Handle of user to unmute: " -                       muted-accts -                       nil ; predicate -                       t)))) -  (let* ((account (mastodon-profile--search-account-by-handle -                   user-handle)) -         (user-id (mastodon-profile--account-field account 'id)) -         (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/unmute" user-id)))) -    (if account -        (when (y-or-n-p (format "Unmute user %s? " name)) -          (let ((response (mastodon-http--post url nil nil))) -            (mastodon-http--triage response -                                   (lambda () -                                     (message "User %s (@%s) unmuted!" name user-handle))))) -      (message "Cannot find a user with handle %S" user-handle)))) +    (mastodon-tl--interactive-user-handles-get "unfollow"))) +  (mastodon-tl--do-user-action-and-response user-handle "unfollow" t))  (defun mastodon-tl--block-user (user-handle)    "Query for USER-HANDLE from current status and block that user."    (interactive     (list -    (let ((user-handles (mastodon-profile--extract-users-handles -                         (mastodon-profile--toot-json)))) -      (completing-read "Handle of user to block: " -                       user-handles -                       nil ; predicate -                       'confirm)))) -  (let* ((account (mastodon-profile--lookup-account-in-status -                   user-handle (mastodon-profile--toot-json))) -         (user-id (mastodon-profile--account-field account 'id)) -         (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/block" user-id)))) -    (if account -        (when (y-or-n-p (format "Block user %s? " name)) -          (let ((response (mastodon-http--post url nil nil))) -            (mastodon-http--triage response -                                   (lambda () -                                     (message "User %s (@%s) blocked!" name user-handle))))) -      (message "Cannot find a user with handle %S" user-handle)))) +    (mastodon-tl--interactive-user-handles-get "block"))) +  (mastodon-tl--do-user-action-and-response user-handle "block"))  (defun mastodon-tl--unblock-user (user-handle)    "Query for USER-HANDLE from list of blocked users and unblock that user."    (interactive     (list -    (let* ((blocks-url (mastodon-http--api (format "blocks"))) -           (blocks-json (mastodon-http--get-json blocks-url)) -           (blocked-accts (mapcar (lambda (blocked) -                                    (alist-get 'acct blocked)) -                                  blocks-json))) -      (completing-read "Handle of user to unblock: " -                       blocked-accts +    (mastodon-tl--interactive-blocks-or-mutes-list-get "unblock"))) +  (if (not user-handle) +      (message "Looks like you have no blocks to unblock!") +    (mastodon-tl--do-user-action-and-response user-handle "unblock" t))) + +(defun mastodon-tl--mute-user (user-handle) +  "Query for USER-HANDLE from current status and mute that user." +  (interactive +   (list +    (mastodon-tl--interactive-user-handles-get "mute"))) +  (mastodon-tl--do-user-action-and-response user-handle "mute")) + +(defun mastodon-tl--unmute-user (user-handle) +  "Query for USER-HANDLE from list of muted users and unmute that user." +  (interactive +   (list +    (mastodon-tl--interactive-blocks-or-mutes-list-get "unmute"))) +  (if (not user-handle) +      (message "Looks like you have no mutes to unmute!") +    (mastodon-tl--do-user-action-and-response user-handle "unmute" t))) + +(defun mastodon-tl--interactive-user-handles-get (action) +  "Get the list of user-handles for ACTION from the current toot." +  (let ((user-handles (mastodon-profile--extract-users-handles +                       (mastodon-profile--toot-json)))) +    (completing-read (if (or (equal action "disable") +                             (equal action "enable")) +                         (format "%s notifications when user posts: " action) +                       (format "Handle of user to %s: " action)) +                     user-handles +                     nil ; predicate +                     'confirm))) + +(defun mastodon-tl--interactive-blocks-or-mutes-list-get (action) +  "Fetch the list of accounts for ACTION from the server. +Action must be either \"unblock\" or \"mute\"." +  (let* ((endpoint (cond ((equal action "unblock") +                          "blocks") +                         ((equal action "unmute") +                          "mutes"))) +         (url (mastodon-http--api endpoint)) +         (json (mastodon-http--get-json url)) +         (accts (mapcar (lambda (user) +                          (alist-get 'acct user)) +                        json))) +    (when accts +      (completing-read (format "Handle of user to %s: " action) +                       accts                         nil ; predicate                         t)))) -  (let* ((account (mastodon-profile--search-account-by-handle -                   user-handle)) + +(defun mastodon-tl--do-user-action-and-response (user-handle action &optional negp notify) +  "Do ACTION on user NAME/USER-HANDLE. +NEGP is whether the action involves un-doing something. +If NOTIFY is \"true\", enable notifications when that user posts. +If NOTIFY is \"false\", disable notifications when that user posts. +NOTIFY is only non-nil when called by `mastodon-tl--follow-user'." +  (let* ((account (if negp +                      ;; TODO check if both are actually needed +                      (mastodon-profile--search-account-by-handle +                       user-handle) +                    (mastodon-profile--lookup-account-in-status +                     user-handle (mastodon-profile--toot-json))))           (user-id (mastodon-profile--account-field account 'id))           (name (mastodon-profile--account-field account 'display_name)) -         (url (mastodon-http--api (format "accounts/%s/unblock" user-id)))) +         (url (mastodon-http--api +               (if notify +                   (format "accounts/%s/%s?notify=%s" user-id action notify) +                 (format "accounts/%s/%s" user-id action)))))      (if account -        (when (y-or-n-p (format "Unblock user %s? " name)) -          (let ((response (mastodon-http--post url nil nil))) -            (mastodon-http--triage response -                                   (lambda () -                                     (message "User %s (@%s) unblocked!" name user-handle))))) +        (if (equal action "follow") ; y-or-n for all but follow +            (mastodon-tl--do-user-action-function url name user-handle action notify) +          (when (y-or-n-p (format "%s user %s? " action name)) +            (mastodon-tl--do-user-action-function url name user-handle action)))        (message "Cannot find a user with handle %S" user-handle)))) +(defun mastodon-tl--do-user-action-function (url name user-handle action &optional notify) +  "Post ACTION on user NAME/USER-HANDLE to URL." +  (let ((response (mastodon-http--post url nil nil))) +    (mastodon-http--triage response +                           (lambda () +                             (cond ((string-equal notify "true") +                                    (message "Receiving notifications for user %s (@%s)!" +                                             name user-handle)) +                                   ((string-equal notify "false") +                                    (message "Not receiving notifications for user %s (@%s)!" +                                             name user-handle)) +                                   ((eq notify nil) +                                    (message "User %s (@%s) %sed!" name user-handle action))))))) +  ;; TODO: add this to new posts in some cases, e.g. in thread view.  (defun mastodon-tl--reload-timeline-or-profile ()    "Reload the current timeline or profile page. diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el index 9a88bd5..8d2df60 100644 --- a/lisp/mastodon-toot.el +++ b/lisp/mastodon-toot.el @@ -106,6 +106,11 @@ This is only used if company mode is installed."            (const :tag "following only" "following")            (const :tag "all users" "all"))) +(defcustom mastodon-toot--enable-custom-instance-emoji nil +  "Whether to enable your instance's custom emoji by default." +  :group 'mastodon-toot +  :type 'boolean) +  (defvar-local mastodon-toot--content-warning nil    "A flag whether the toot should be marked with a content warning.") diff --git a/lisp/mastodon.el b/lisp/mastodon.el index f9c18a0..662b691 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -223,7 +223,9 @@ If REPLY-JSON is the json of the toot being replied to."  ;;;###autoload  (add-hook 'mastodon-mode-hook (lambda ()                                  (when (require 'emojify nil :noerror) -                                  (emojify-mode t)))) +                                  (emojify-mode t) +                                  (when mastodon-toot--enable-custom-instance-emoji +                                    (mastodon-toot--enable-custom-emoji)))))  (define-derived-mode mastodon-mode special-mode "Mastodon"    "Major mode for Mastodon, the federated microblogging network." | 
