diff options
author | marty hiatt <martianhiatus [a t] riseup [d o t] net> | 2023-05-11 09:53:25 +0200 |
---|---|---|
committer | marty hiatt <martianhiatus [a t] riseup [d o t] net> | 2023-05-11 09:53:25 +0200 |
commit | c7779e8e8ad172be64aa98ee62a83b54185e3bce (patch) | |
tree | 3cbc9138471768ac9ff0c51b53df20acc5cc6949 /lisp/mastodon-profile.el | |
parent | ebb44f398037c3bd6aca1c85799ed353c44e9c3d (diff) | |
parent | b19390cd38ba93e527e5961723b46779749f1ee1 (diff) |
Merge branch 'audit' into develop
Diffstat (limited to 'lisp/mastodon-profile.el')
-rw-r--r-- | lisp/mastodon-profile.el | 374 |
1 files changed, 160 insertions, 214 deletions
diff --git a/lisp/mastodon-profile.el b/lisp/mastodon-profile.el index 241fbbe..0c6e3b2 100644 --- a/lisp/mastodon-profile.el +++ b/lisp/mastodon-profile.el @@ -91,11 +91,6 @@ (defvar mastodon-profile-mode-map (let ((map (make-sparse-keymap))) - ;; conflicts with `s' keybinding to translate toot at point - ;; seeing as we now have the C-c C-c cycle functionality, - ;; maybe we can retire both of these awful bindings - ;; (define-key map (kbd "s") #'mastodon-profile--open-followers) - ;; (define-key map (kbd "g") #'mastodon-profile--open-following) (define-key map (kbd "C-c C-c") #'mastodon-profile--account-view-cycle) map) "Keymap for `mastodon-profile-mode'.") @@ -105,7 +100,6 @@ This minor mode is used for mastodon profile pages and adds a couple of extra keybindings." :init-value nil - ;; modeline indicator: :lighter " Profile" :keymap mastodon-profile-mode-map :group 'mastodon @@ -169,7 +163,7 @@ NO-REBLOGS means do not display boosts in statuses." (mastodon-profile--make-profile-buffer-for mastodon-profile--account "following" - #'mastodon-profile--add-author-bylines + #'mastodon-profile--format-user nil :headers) (error "Not in a mastodon profile"))) @@ -181,7 +175,7 @@ NO-REBLOGS means do not display boosts in statuses." (mastodon-profile--make-profile-buffer-for mastodon-profile--account "followers" - #'mastodon-profile--add-author-bylines + #'mastodon-profile--format-user nil :headers) (error "Not in a mastodon profile"))) @@ -213,6 +207,7 @@ NO-REBLOGS means do not display boosts in statuses." (handle (alist-get 'acct profile))) (mastodon-views--add-account-to-list nil id handle)))) + ;;; ACCOUNT PREFERENCES (defun mastodon-profile--get-json-value (val) @@ -247,9 +242,7 @@ NO-REBLOGS means do not display boosts in statuses." (msg-str "Edit your profile note. C-c C-c to send, C-c C-k to cancel.")) (switch-to-buffer-other-window buffer) (text-mode) - (mastodon-tl--set-buffer-spec (buffer-name buffer) - endpoint - nil) + (mastodon-tl--set-buffer-spec (buffer-name buffer) endpoint nil) (setq-local header-line-format (propertize msg-str 'face font-lock-comment-face)) @@ -263,7 +256,7 @@ NO-REBLOGS means do not display boosts in statuses." 'note-header t) "\n") (make-local-variable 'after-change-functions) - (push #'mastodon-profile--update-note-count after-change-functions) + (cl-pushnew #'mastodon-profile--update-note-count after-change-functions) (let ((start-point (point))) (insert note) (goto-char start-point)) @@ -348,9 +341,7 @@ Only do so if `mastodon-profile-account-settings' is nil." Store the values in `mastodon-profile-account-settings'. Run in `mastodon-mode-hook'. If NO-FORCE, only fetch if `mastodon-profile-account-settings' is nil." - (unless - (and no-force - mastodon-profile-account-settings) + (unless (and no-force mastodon-profile-account-settings) (let ((keys '(locked discoverable display_name bot)) (source-keys '(privacy sensitive language))) (mapc (lambda (k) @@ -403,11 +394,10 @@ Current settings are fetched from the server." (mastodon-profile--get-source-value key) (mastodon-profile--get-json-value key))) (prompt (format "Account setting %s is %s. Toggle?" key val))) - (if val - (when (y-or-n-p prompt) - (mastodon-profile--update-preference (symbol-name key) "false" source)) - (when (y-or-n-p prompt) - (mastodon-profile--update-preference (symbol-name key) "true" source))))) + (when (y-or-n-p prompt) + (mastodon-profile--update-preference (symbol-name key) + (if val "false" "true") + source)))) (defun mastodon-profile--edit-string-value (key) "Edit the string for account preference KEY." @@ -430,10 +420,8 @@ Returns an alist." (format "fields_attributes[%s][value]" count))))) (cl-loop for a-pair in keys for b-pair in fields - append (list (cons (car a-pair) - (car b-pair)) - (cons (cdr a-pair) - (cdr b-pair)))))) + append (list (cons (car a-pair) (car b-pair)) + (cons (cdr a-pair) (cdr b-pair)))))) (defun mastodon-profile--update-meta-fields () "Prompt for new metadata fields information and PATCH the server." @@ -469,7 +457,6 @@ Returns the results as an alist." (read-string (format "Metadata value [%s/4] (max. 255 chars): " x) (cdr f)))))) - ;; hack to avoiding using `string-limit', which req. 28.1: (mapcar (lambda (x) (cons (mastodon-profile--limit-to-255 (car x)) (mastodon-profile--limit-to-255 (cdr x)))) @@ -506,30 +493,28 @@ This endpoint only holds a few preferences. For others, see "\n\n"))) (goto-char (point-min))))) -;; PROFILE VIEW DETAILS + +;;; PROFILE VIEW DETAILS (defun mastodon-profile--relationships-get (id) "Fetch info about logged-in user's relationship to user with id ID." (let* ((their-id id) (args `(("id[]" . ,their-id))) (url (mastodon-http--api "accounts/relationships"))) - ;; FIXME: not sure why we need to do this for relationships only! - (car (mastodon-http--get-json url args)))) + (car (mastodon-http--get-json url args)))) ; API takes array, just get 1st (defun mastodon-profile--fields-get (&optional account fields) "Fetch the fields vector (aka profile metadata) from profile of ACCOUNT. Returns an alist. FIELDS means provide a fields vector fetched by other means." - (let ((fields (or fields - (mastodon-profile--account-field account 'fields)))) + (let ((fields (or fields (alist-get 'fields account)))) (when fields (mastodon-tl--map-alist-vals-to-alist 'name 'value fields)))) (defun mastodon-profile--fields-insert (fields) "Format and insert field pairs (a.k.a profile metadata) in FIELDS." (let* ((car-fields (mapcar #'car fields)) - (left-width (cl-reduce - #'max (mapcar #'length car-fields)))) + (left-width (cl-reduce #'max (mapcar #'length car-fields)))) (mapconcat (lambda (field) (mastodon-tl--render-text (concat @@ -541,7 +526,7 @@ FIELDS means provide a fields vector fetched by other means." (defun mastodon-profile--get-statuses-pinned (account) "Fetch the pinned toots for ACCOUNT." - (let* ((id (mastodon-profile--account-field account 'id)) + (let* ((id (alist-get 'id account)) (args `(("pinned" . "true"))) (url (mastodon-http--api (format "accounts/%s/statuses" id)))) (mastodon-http--get-json url args))) @@ -549,143 +534,122 @@ FIELDS means provide a fields vector fetched by other means." (defun mastodon-profile--insert-statuses-pinned (pinned-statuses) "Insert each of the PINNED-STATUSES for a given account." (mapc (lambda (pinned-status) - (insert (mastodon-tl--set-face - " :pinned: " 'success)) + (insert (mastodon-tl--set-face " :pinned: " 'success)) (mastodon-tl--toot pinned-status)) pinned-statuses)) -(defun mastodon-profile--make-profile-buffer-for (account endpoint-type - update-function - &optional no-reblogs headers) +(defun mastodon-profile--follows-p (list) + "T if you have any relationship with the accounts in LIST." + (let (result) + (dolist (x list result) + (when (not (equal :json-false x)) + (setq result x))))) + +(defun mastodon-profile--make-profile-buffer-for + (account endpoint-type update-function &optional no-reblogs headers) "Display profile of ACCOUNT, using ENDPOINT-TYPE and UPDATE-FUNCTION. NO-REBLOGS means do not display boosts in statuses. HEADERS means also fetch link headers for pagination." - (let* ((id (mastodon-profile--account-field account 'id)) - (args `(("limit" . ,mastodon-tl--timeline-posts-count))) - (args (if no-reblogs (push '("exclude_reblogs" . "t") args) args)) - (endpoint (format "accounts/%s/%s" id endpoint-type)) - (url (mastodon-http--api endpoint)) - (acct (mastodon-profile--account-field account 'acct)) - (buffer (concat "*mastodon-" acct "-" - (if no-reblogs - (concat endpoint-type "-no-boosts") - endpoint-type) - "*")) - (response (if headers - (mastodon-http--get-response url args) - (mastodon-http--get-json url args))) - (json (if headers (car response) response)) - (link-header (when headers - (mastodon-tl--get-link-header-from-response - (cdr response)))) - (note (mastodon-profile--account-field account 'note)) - (locked (mastodon-profile--account-field account 'locked)) - (followers-count (mastodon-tl--as-string - (mastodon-profile--account-field - account 'followers_count))) - (following-count (mastodon-tl--as-string - (mastodon-profile--account-field - account 'following_count))) - (toots-count (mastodon-tl--as-string - (mastodon-profile--account-field - account 'statuses_count))) - (relationships (mastodon-profile--relationships-get id)) - (requested-you (when (not (seq-empty-p relationships)) - (alist-get 'requested_by relationships))) - (followed-by-you (when (not (seq-empty-p relationships)) - (alist-get 'following relationships))) - (follows-you (when (not (seq-empty-p relationships)) - (alist-get 'followed_by relationships))) - (followsp (or (equal follows-you 't) (equal followed-by-you 't) - (equal requested-you 't))) - (fields (mastodon-profile--fields-get account)) - (pinned (mastodon-profile--get-statuses-pinned account)) - (joined (mastodon-profile--account-field account 'created_at))) - (with-mastodon-buffer buffer #'mastodon-mode nil - (mastodon-profile-mode) - (setq mastodon-profile--account account) - (mastodon-tl--set-buffer-spec buffer - endpoint - update-function - link-header) - (let* ((inhibit-read-only t) - (is-statuses (string= endpoint-type "statuses")) - (is-followers (string= endpoint-type "followers")) - (is-following (string= endpoint-type "following")) - (endpoint-name (cond - (is-statuses (if no-reblogs - " TOOTS (no boosts)" - " TOOTS ")) - (is-followers " FOLLOWERS ") - (is-following " FOLLOWING ")))) - (insert - (propertize - (concat - "\n" - (mastodon-profile--image-from-account account 'avatar_static) - (mastodon-profile--image-from-account account 'header_static) - "\n" - (propertize (mastodon-profile--account-field - account 'display_name) - 'face 'mastodon-display-name-face) - "\n" - (propertize (concat "@" acct) - 'face 'default) - (if (equal locked t) - (concat " " (mastodon-tl--symbol 'locked)) - "") - "\n " mastodon-tl--horiz-bar "\n" - ;; profile note: - ;; account here to enable tab-stops in profile note - (mastodon-tl--render-text note account) - ;; meta fields: - (if fields - (concat "\n" - (mastodon-tl--set-face - (mastodon-profile--fields-insert fields) - 'success)) - "") - "\n" - ;; Joined date: + (let-alist account + (let* ((args `(("limit" . ,mastodon-tl--timeline-posts-count))) + (args (if no-reblogs (push '("exclude_reblogs" . "t") args) args)) + (endpoint (format "accounts/%s/%s" .id endpoint-type)) + (url (mastodon-http--api endpoint)) + (buffer (concat "*mastodon-" .acct "-" + (if no-reblogs + (concat endpoint-type "-no-boosts") + endpoint-type) + "*")) + (response (if headers + (mastodon-http--get-response url args) + (mastodon-http--get-json url args))) + (json (if headers (car response) response)) + (link-header (when headers + (mastodon-tl--get-link-header-from-response + (cdr response)))) + (fields (mastodon-profile--fields-get account)) + (pinned (mastodon-profile--get-statuses-pinned account)) + (relationships (mastodon-profile--relationships-get .id))) + (with-mastodon-buffer buffer #'mastodon-mode nil + (mastodon-profile-mode) + (setq mastodon-profile--account account) + (mastodon-tl--set-buffer-spec buffer endpoint + update-function link-header) + (let* ((inhibit-read-only t) + (is-statuses (string= endpoint-type "statuses")) + (is-followers (string= endpoint-type "followers")) + (is-following (string= endpoint-type "following")) + (endpoint-name (cond + (is-statuses (if no-reblogs + " TOOTS (no boosts)" + " TOOTS ")) + (is-followers " FOLLOWERS ") + (is-following " FOLLOWING ")))) + (insert (propertize - (mastodon-profile--format-joined-date-string joined) - 'face 'success) - "\n\n") - 'profile-json account) - ;; insert counts - (mastodon-tl--set-face - (concat " " mastodon-tl--horiz-bar "\n" - " TOOTS: " toots-count " | " - "FOLLOWERS: " followers-count " | " - "FOLLOWING: " following-count "\n" - " " mastodon-tl--horiz-bar "\n\n") - 'success) - ;; insert relationship (follows) - (if followsp - (mastodon-tl--set-face - (concat (when (equal follows-you 't) - " | FOLLOWS YOU") - (when (equal followed-by-you 't) - " | FOLLOWED BY YOU") - (when (equal requested-you 't) - " | REQUESTED TO FOLLOW YOU") - "\n\n") - 'success) - "") ; if no followsp we still need str-or-char-p for insert - ;; insert endpoint - (mastodon-tl--set-face - (concat " " mastodon-tl--horiz-bar "\n" - endpoint-name "\n" - " " mastodon-tl--horiz-bar "\n") - 'success)) - (setq mastodon-tl--update-point (point)) - (mastodon-media--inline-images (point-min) (point)) - ;; insert pinned toots first - (when (and pinned (equal endpoint-type "statuses")) - (mastodon-profile--insert-statuses-pinned pinned) - (setq mastodon-tl--update-point (point))) ;updates to follow pinned toots - (funcall update-function json))) - (goto-char (point-min)))) + (concat + "\n" + (mastodon-profile--image-from-account account 'avatar_static) + (mastodon-profile--image-from-account account 'header_static) + "\n" + (propertize .display_name 'face 'mastodon-display-name-face) + "\n" + (propertize (concat "@" .acct) 'face 'default) + (if (equal .locked t) + (concat " " (mastodon-tl--symbol 'locked)) + "") + "\n " mastodon-tl--horiz-bar "\n" + ;; profile note: + (mastodon-tl--render-text .note account) ; account = tab-stops in profile + ;; meta fields: + (if fields + (concat "\n" (mastodon-tl--set-face + (mastodon-profile--fields-insert fields) + 'success)) + "") + "\n" + ;; Joined date: + (propertize + (mastodon-profile--format-joined-date-string .created_at) + 'face 'success) + "\n\n") + 'profile-json account) + ;; insert counts + (mastodon-tl--set-face + (concat " " mastodon-tl--horiz-bar "\n" + " TOOTS: " (mastodon-tl--as-string .statuses_count) " | " + "FOLLOWERS: " (mastodon-tl--as-string .followers_count) " | " + "FOLLOWING: " (mastodon-tl--as-string .following_count) "\n" + " " mastodon-tl--horiz-bar "\n\n") + 'success) + ;; insert relationship (follows) + (let-alist relationships + (let ((followsp (mastodon-profile--follows-p + (list .requested_by .following .followed_by)))) + (if followsp + (mastodon-tl--set-face + (concat (when (equal .following 't) + " | FOLLOWS YOU") + (when (equal .followed_by 't) + " | FOLLOWED BY YOU") + (when (equal .requested_by 't) + " | REQUESTED TO FOLLOW YOU") + "\n\n") + 'success) + ""))) ; for insert call + ;; insert endpoint + (mastodon-tl--set-face (concat " " mastodon-tl--horiz-bar "\n" + endpoint-name "\n" + " " mastodon-tl--horiz-bar "\n") + 'success)) + (setq mastodon-tl--update-point (point)) + (mastodon-media--inline-images (point-min) (point)) + ;; insert pinned toots first + (when (and pinned (equal endpoint-type "statuses")) + (mastodon-profile--insert-statuses-pinned pinned) + (setq mastodon-tl--update-point (point))) ; updates after pinned toots + (funcall update-function json))) + (goto-char (point-min))))) (defun mastodon-profile--format-joined-date-string (joined) "Format a human-readable Joined string from timestamp JOINED. @@ -721,9 +685,9 @@ IMG-TYPE is the JSON key from the account data." user-handles nil ; predicate 'confirm))))) - (if (not (or - ;; own profile has no need for toot-json test: + (if (not (or ; own profile has no need for toot-json test: (equal user-handle (mastodon-auth--get-account-name)) + (mastodon-tl--profile-buffer-p) (mastodon-tl--property 'toot-json :no-move))) (message "Looks like there's no toot or user at point?") (let ((account (mastodon-profile--lookup-account-in-status @@ -741,24 +705,17 @@ IMG-TYPE is the JSON key from the account data." (message "Loading your profile...") (mastodon-profile--show-user (mastodon-auth--get-account-name))) -(defun mastodon-profile--account-field (account field) - "Return FIELD from the ACCOUNT. -FIELD is used to identify regions under `account'." - (cdr (assoc field account))) - -(defun mastodon-profile--add-author-bylines (tootv) - "Convert TOOTV into a author-bylines and insert. +(defun mastodon-profile--format-user (tootv) + "Convert TOOTV into author-bylines and insert. Also insert their profile note. Used to view a user's followers and those they're following." - ;;FIXME change the name of this fun now that we've edited what it does! (let ((inhibit-read-only t)) (unless (seq-empty-p tootv) (mapc (lambda (toot) (let ((start-pos (point))) (insert "\n" (propertize - (mastodon-tl--byline-author `((account . ,toot)) - :avatar) + (mastodon-tl--byline-author `((account . ,toot)) :avatar) 'byline 't 'toot-id (alist-get 'id toot) 'base-toot-id (mastodon-tl--toot-id toot) @@ -767,7 +724,7 @@ Used to view a user's followers and those they're following." (insert "\n" (propertize (mastodon-tl--render-text (alist-get 'note toot) nil) - 'toot-json toot) ' + 'toot-json toot) "\n"))) tootv)))) @@ -778,13 +735,12 @@ If the handle does not match a search return then retun NIL." (substring handle 1 (length handle)) handle)) (args `(("q" . ,handle))) - (matching-account - (seq-remove - (lambda (x) - (not (string= (alist-get 'acct x) handle))) - (mastodon-http--get-json - (mastodon-http--api "accounts/search") - args)))) + (matching-account (seq-remove + (lambda (x) + (not (string= (alist-get 'acct x) handle))) + (mastodon-http--get-json + (mastodon-http--api "accounts/search") + args)))) (when (equal 1 (length matching-account)) (elt matching-account 0)))) @@ -797,21 +753,19 @@ If the handle does not match a search return then retun NIL." "Return all user handles found in STATUS. These include the author, author of reblogged entries and any user mentioned." (when status - (let ((this-account - (or (alist-get 'account status) ; status is a toot - status)) ; status is a user listing - (mentions (or (alist-get 'mentions (alist-get 'status status)) + (let ((this-account (or (alist-get 'account status) ; status is a toot + status)) ; status is a user listing + (mentions (or (alist-get 'mentions (alist-get 'status status)) (alist-get 'mentions status))) - (reblog (or (alist-get 'reblog (alist-get 'status status)) + (reblog (or (alist-get 'reblog (alist-get 'status status)) (alist-get 'reblog status)))) - (seq-filter - #'stringp - (seq-uniq - (seq-concatenate - 'list - (list (alist-get 'acct this-account)) - (mastodon-profile--extract-users-handles reblog) - (mastodon-tl--map-alist 'acct mentions))))))) + (seq-filter #'stringp + (seq-uniq + (seq-concatenate + 'list + (list (alist-get 'acct this-account)) + (mastodon-profile--extract-users-handles reblog) + (mastodon-tl--map-alist 'acct mentions))))))) (defun mastodon-profile--lookup-account-in-status (handle status) "Return account for HANDLE using hints in STATUS if possible." @@ -819,15 +773,12 @@ These include the author, author of reblogged entries and any user mentioned." (reblog-account (alist-get 'account (alist-get 'reblog status))) (mention-id (seq-some (lambda (mention) - (when (string= handle - (alist-get 'acct mention)) + (when (string= handle (alist-get 'acct mention)) (alist-get 'id mention))) (alist-get 'mentions status)))) - (cond ((string= handle - (alist-get 'acct this-account)) + (cond ((string= handle (alist-get 'acct this-account)) this-account) - ((string= handle - (alist-get 'acct reblog-account)) + ((string= handle (alist-get 'acct reblog-account)) reblog-account) (mention-id (mastodon-profile--account-from-id mention-id)) @@ -842,8 +793,7 @@ Optionally provide the ID of the account to remove." (id (or id (alist-get 'id account))) (handle (if account (alist-get 'acct account) - (let ((account - (mastodon-profile--account-from-id id))) + (let ((account (mastodon-profile--account-from-id id))) (alist-get 'acct account)))) (url (mastodon-http--api (format "accounts/%s/remove_from_followers" id)))) @@ -858,8 +808,7 @@ Optionally provide the ID of the account to remove." (interactive) (let* ((handles (mastodon-profile--extract-users-handles (mastodon-profile--toot-json))) - (handle (completing-read "Remove from followers: " - handles nil)) + (handle (completing-read "Remove from followers: " handles nil)) (account (mastodon-profile--lookup-account-in-status handle (mastodon-profile--toot-json))) (id (alist-get 'id account))) @@ -873,11 +822,9 @@ Currently limited to 100 handles. If not found, try (let* ((endpoint (format "accounts/%s/followers" (mastodon-auth--get-account-id))) (url (mastodon-http--api endpoint)) - (response (mastodon-http--get-json url - `(("limit" . "100")))) + (response (mastodon-http--get-json url `(("limit" . "100")))) (handles (mastodon-tl--map-alist-vals-to-alist 'acct 'id response)) - (choice (completing-read "Remove from followers: " - handles)) + (choice (completing-read "Remove from followers: " handles)) (id (alist-get choice handles nil nil 'equal))) (mastodon-profile--remove-user-from-followers id))) @@ -962,16 +909,15 @@ the given account." (defun mastodon-profile--get-familiar-followers (id) "Return JSON data of familiar followers for account ID." - ;; the server can handle multiple IDs, but for now we just handle one. + ;; the server handles multiple IDs, but we just handle one. (let* ((params `(("id" . ,id))) (url (mastodon-http--api "accounts/familiar_followers")) (json (mastodon-http--get-json url params)) - (accounts (alist-get 'accounts (car json))) ; first id result + (accounts (alist-get 'accounts (car json))) ; first id (handles (mastodon-tl--map-alist 'acct accounts))) (if (null handles) (message "Looks like there are no familiar followers for this account") - (let ((choice (completing-read "Show profile of user: " - handles))) + (let ((choice (completing-read "Show profile of user: " handles))) (mastodon-profile--show-user choice))))) (provide 'mastodon-profile) |