aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lisp/mastodon-async.el13
-rw-r--r--lisp/mastodon-discover.el2
-rw-r--r--lisp/mastodon-http.el2
-rw-r--r--lisp/mastodon-inspect.el2
-rw-r--r--lisp/mastodon-iso.el370
-rw-r--r--lisp/mastodon-media.el2
-rw-r--r--lisp/mastodon-notifications.el2
-rw-r--r--lisp/mastodon-profile.el150
-rw-r--r--lisp/mastodon-search.el33
-rw-r--r--lisp/mastodon-tl.el207
-rw-r--r--lisp/mastodon-toot.el38
-rw-r--r--test/mastodon-search-tests.el2
-rw-r--r--test/mastodon-tl-tests.el110
13 files changed, 577 insertions, 356 deletions
diff --git a/lisp/mastodon-async.el b/lisp/mastodon-async.el
index 8a08416..58e7b93 100644
--- a/lisp/mastodon-async.el
+++ b/lisp/mastodon-async.el
@@ -229,14 +229,11 @@ ENDPOINT is the endpoint for the stream and timeline."
(mastodon-tl--timeline (mastodon-http--get-json
(mastodon-http--api endpoint))))
(mastodon-mode)
- (setq mastodon-tl--buffer-spec
- `(buffer-name
- ,buffer-name
- endpoint ,endpoint
- update-function
- ,(if (equal name "notifications")
- 'mastodon-notifications--timeline
- 'mastodon-tl--timeline)))
+ (mastodon-tl--set-buffer-spec buffer-name
+ endpoint
+ ,(if (equal name "notifications")
+ 'mastodon-notifications--timeline
+ 'mastodon-tl--timeline))
(setq-local mastodon-tl--enable-relative-timestamps nil)
(setq-local mastodon-tl--display-media-p t)
(current-buffer))))
diff --git a/lisp/mastodon-discover.el b/lisp/mastodon-discover.el
index 08df46e..1b960e5 100644
--- a/lisp/mastodon-discover.el
+++ b/lisp/mastodon-discover.el
@@ -1,7 +1,9 @@
;;; mastodon-discover.el --- Use Mastodon.el with discover.el -*- lexical-binding: t -*-
;; Copyright (C) 2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1"))
diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el
index d677e57..9ef7aec 100644
--- a/lisp/mastodon-http.el
+++ b/lisp/mastodon-http.el
@@ -1,7 +1,9 @@
;;; mastodon-http.el --- HTTP request/response functions for mastodon.el -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1") (request "0.3.0"))
diff --git a/lisp/mastodon-inspect.el b/lisp/mastodon-inspect.el
index cbf6a8e..112a753 100644
--- a/lisp/mastodon-inspect.el
+++ b/lisp/mastodon-inspect.el
@@ -1,7 +1,9 @@
;;; mastodon-inspect.el --- Client for Mastodon -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1"))
diff --git a/lisp/mastodon-iso.el b/lisp/mastodon-iso.el
index 8baff3c..341593c 100644
--- a/lisp/mastodon-iso.el
+++ b/lisp/mastodon-iso.el
@@ -33,191 +33,191 @@
;; https://github.com/Shinmera/language-codes/blob/master/data/iso-639-3.lisp
(defvar mastodon-iso-639-1
- '(("ab" "Abkhazian")
- ("aa" "Afar")
- ("af" "Afrikaans")
- ("ak" "Akan")
- ("sq" "Albanian")
- ("am" "Amharic")
- ("ar" "Arabic")
- ("an" "Aragonese")
- ("hy" "Armenian")
- ("as" "Assamese")
- ("av" "Avaric")
- ("ae" "Avestan")
- ("ay" "Aymara")
- ("az" "Azerbaijani")
- ("bm" "Bambara")
- ("ba" "Bashkir")
- ("eu" "Basque")
- ("be" "Belarusian")
- ("bn" "Bengali")
- ("bh" "Bihari languages")
- ("bi" "Bislama")
- ("bs" "Bosnian")
- ("br" "Breton")
- ("bg" "Bulgarian")
- ("my" "Burmese")
- ("km" "Central Khmer")
- ("ch" "Chamorro")
- ("ce" "Chechen")
- ("zh" "Chinese")
- ("cv" "Chuvash")
- ("kw" "Cornish")
- ("co" "Corsican")
- ("cr" "Cree")
- ("hr" "Croatian")
- ("cs" "Czech")
- ("da" "Danish")
- ("dz" "Dzongkha")
- ("en" "English")
- ("eo" "Esperanto")
- ("et" "Estonian")
- ("ee" "Ewe")
- ("fo" "Faroese")
- ("fj" "Fijian")
- ("fi" "Finnish")
- ("nl" "Dutch" "Flemish")
- ("fr" "French")
- ("ff" "Fulah")
- ("gl" "Galician")
- ("lg" "Ganda")
- ("ka" "Georgian")
- ("de" "German")
- ("el" "Greek")
- ("gn" "Guarani")
- ("gu" "Gujarati")
- ("ht" "Haitian" "Haitian Creole")
- ("ha" "Hausa")
- ("he" "Hebrew")
- ("hz" "Herero")
- ("hi" "Hindi")
- ("ho" "Hiri Motu")
- ("hu" "Hungarian")
- ("is" "Icelandic")
- ("io" "Ido")
- ("ig" "Igbo")
- ("id" "Indonesian")
- ("ia" "Interlingua" "Interlingua (International Auxiliary Language Association)")
- ("iu" "Inuktitut")
- ("ik" "Inupiaq")
- ("ga" "Irish")
- ("it" "Italian")
- ("ja" "Japanese")
- ("jp" "Japanese")
- ("jv" "Javanese")
- ("kl" "Kalaallisut" "Greenlandic")
- ("kn" "Kannada")
- ("kr" "Kanuri")
- ("ks" "Kashmiri")
- ("kk" "Kazakh")
- ("ki" "Kikuyu" "Gikuyu")
- ("rw" "Kinyarwanda")
- ("kv" "Komi")
- ("kg" "Kongo")
- ("ko" "Korean")
- ("ku" "Kurdish")
- ("kj" "Kuanyama" "Kwanyama")
- ("ky" "Kirghiz" "Kyrgyz")
- ("lo" "Lao")
- ("la" "Latin")
- ("lv" "Latvian")
- ("li" "Limburgan" "Limburger" "Limburgish")
- ("ln" "Lingala")
- ("lt" "Lithuanian")
- ("lu" "Luba-Katanga")
- ("lb" "Luxembourgish" "Letzeburgesch")
- ("mk" "Macedonian")
- ("mg" "Malagasy")
- ("ms" "Malay")
- ("ml" "Malayalam")
- ("dv" "Divehi" "Dhivehi" "Maldivian")
- ("mt" "Maltese")
- ("gv" "Manx")
- ("mi" "Maori")
- ("mr" "Marathi")
- ("mh" "Marshallese")
- ("mn" "Mongolian")
- ("na" "Nauru")
- ("nv" "Navajo" "Navaho")
- ("ng" "Ndonga")
- ("ne" "Nepali")
- ("nd" "Ndebele, North" "North Ndebele")
- ("se" "Northern Sami")
- ("no" "Norwegian")
- ("nb" "Bokmål, Norwegian" "Norwegian Bokmål")
- ("ny" "Chichewa" "Chewa" "Nyanja")
- ("nn" "Norwegian Nynorsk" "Nynorsk, Norwegian")
- ("ie" "Interlingue" "Occidental")
- ("oc" "Occitan")
- ("oj" "Ojibwa")
- ("cu" "Church Slavic" "Old Slavonic" "Church Slavonic" "Old Bulgarian" "Old Church Slavonic")
- ("or" "Oriya")
- ("om" "Oromo")
- ("os" "Ossetian" "Ossetic")
- ("pi" "Pali")
- ("fa" "Persian")
- ("pl" "Polish")
- ("pt" "Portuguese")
- ("pa" "Panjabi" "Punjabi")
- ("ps" "Pushto" "Pashto")
- ("qu" "Quechua")
- ("ro" "Romanian" "Moldavian" "Moldovan")
- ("rm" "Romansh")
- ("rn" "Rundi")
- ("ru" "Russian")
- ("sm" "Samoan")
- ("sg" "Sango")
- ("sa" "Sanskrit")
- ("sc" "Sardinian")
- ("gd" "Gaelic" "Scottish Gaelic")
- ("sr" "Serbian")
- ("sn" "Shona")
- ("ii" "Sichuan Yi" "Nuosu")
- ("sd" "Sindhi")
- ("si" "Sinhala" "Sinhalese")
- ("sk" "Slovak")
- ("sl" "Slovenian")
- ("so" "Somali")
- ("st" "Sotho, Southern")
- ("nr" "Ndebele, South" "South Ndebele")
- ("es" "Spanish" "Castilian")
- ("su" "Sundanese")
- ("sw" "Swahili")
- ("ss" "Swati")
- ("sv" "Swedish")
- ("tl" "Tagalog")
- ("ty" "Tahitian")
- ("tg" "Tajik")
- ("ta" "Tamil")
- ("tt" "Tatar")
- ("te" "Telugu")
- ("th" "Thai")
- ("bo" "Tibetan")
- ("ti" "Tigrinya")
- ("to" "Tonga (Tonga Islands)")
- ("ts" "Tsonga")
- ("tn" "Tswana")
- ("tr" "Turkish")
- ("tk" "Turkmen")
- ("tw" "Twi")
- ("uk" "Ukrainian")
- ("ur" "Urdu")
- ("ug" "Uighur" "Uyghur")
- ("uz" "Uzbek")
- ("ca" "Catalan" "Valencian")
- ("ve" "Venda")
- ("vi" "Vietnamese")
- ("vo" "Volapük")
- ("wa" "Walloon")
- ("cy" "Welsh")
- ("fy" "Western Frisian")
- ("wo" "Wolof")
- ("xh" "Xhosa")
- ("yi" "Yiddish")
- ("yo" "Yoruba")
- ("za" "Zhuang" "Chuang")
- ("zu" "Zulu")))
+ '(("Abkhazian" . "ab")
+ ("Afar" . "aa")
+ ("Afrikaans" . "af")
+ ("Akan" . "ak")
+ ("Albanian" . "sq")
+ ("Amharic" . "am")
+ ("Arabic" . "ar")
+ ("Aragonese" . "an")
+ ("Armenian" . "hy")
+ ("Assamese" . "as")
+ ("Avaric" . "av")
+ ("Avestan" . "ae")
+ ("Aymara" . "ay")
+ ("Azerbaijani" . "az")
+ ("Bambara" . "bm")
+ ("Bashkir" . "ba")
+ ("Basque" . "eu")
+ ("Belarusian" . "be")
+ ("Bengali" . "bn")
+ ("Bihari languages" . "bh")
+ ("Bislama" . "bi")
+ ("Bosnian" . "bs")
+ ("Breton" . "br")
+ ("Bulgarian" . "bg")
+ ("Burmese" . "my")
+ ("Central Khmer" . "km")
+ ("Chamorro" . "ch")
+ ("Chechen" . "ce")
+ ("Chinese" . "zh")
+ ("Chuvash" . "cv")
+ ("Cornish" . "kw")
+ ("Corsican" . "co")
+ ("Cree" . "cr")
+ ("Croatian" . "hr")
+ ("Czech" . "cs")
+ ("Danish" . "da")
+ ("Dzongkha" . "dz")
+ ("English" . "en")
+ ("Esperanto" . "eo")
+ ("Estonian" . "et")
+ ("Ewe" . "ee")
+ ("Faroese" . "fo")
+ ("Fijian" . "fj")
+ ("Finnish" . "fi")
+ ("Dutch" . "nl")
+ ("French" . "fr")
+ ("Fulah" . "ff")
+ ("Galician" . "gl")
+ ("Ganda" . "lg")
+ ("Georgian" . "ka")
+ ("German" . "de")
+ ("Greek" . "el")
+ ("Guarani" . "gn")
+ ("Gujarati" . "gu")
+ ("Haitian" . "ht")
+ ("Hausa" . "ha")
+ ("Hebrew" . "he")
+ ("Herero" . "hz")
+ ("Hindi" . "hi")
+ ("Hiri Motu" . "ho")
+ ("Hungarian" . "hu")
+ ("Icelandic" . "is")
+ ("Ido" . "io")
+ ("Igbo" . "ig")
+ ("Indonesian" . "id")
+ ("Interlingua" . "ia")
+ ("Inuktitut" . "iu")
+ ("Inupiaq" . "ik")
+ ("Irish" . "ga")
+ ("Italian" . "it")
+ ("Japanese" . "ja")
+ ("Japanese" . "jp")
+ ("Javanese" . "jv")
+ ("Kalaallisut" . "kl")
+ ("Kannada" . "kn")
+ ("Kanuri" . "kr")
+ ("Kashmiri" . "ks")
+ ("Kazakh" . "kk")
+ ("Kikuyu" . "ki")
+ ("Kinyarwanda" . "rw")
+ ("Komi" . "kv")
+ ("Kongo" . "kg")
+ ("Korean" . "ko")
+ ("Kurdish" . "ku")
+ ("Kuanyama" . "kj")
+ ("Kirghiz" . "ky")
+ ("Lao" . "lo")
+ ("Latin" . "la")
+ ("Latvian" . "lv")
+ ("Limburgan" . "li")
+ ("Lingala" . "ln")
+ ("Lithuanian" . "lt")
+ ("Luba-Katanga" . "lu")
+ ("Luxembourgish" . "lb")
+ ("Macedonian" . "mk")
+ ("Malagasy" . "mg")
+ ("Malay" . "ms")
+ ("Malayalam" . "ml")
+ ("Divehi" . "dv")
+ ("Maltese" . "mt")
+ ("Manx" . "gv")
+ ("Maori" . "mi")
+ ("Marathi" . "mr")
+ ("Marshallese" . "mh")
+ ("Mongolian" . "mn")
+ ("Nauru" . "na")
+ ("Navajo" . "nv")
+ ("Ndonga" . "ng")
+ ("Nepali" . "ne")
+ ("Ndebele, North" . "nd")
+ ("Northern Sami" . "se")
+ ("Norwegian" . "no")
+ ("Bokmål, Norwegian" . "nb")
+ ("Chichewa" . "ny")
+ ("Norwegian Nynorsk" . "nn")
+ ("Interlingue" . "ie")
+ ("Occitan" . "oc")
+ ("Ojibwa" . "oj")
+ ("Church Slavic" . "cu")
+ ("Oriya" . "or")
+ ("Oromo" . "om")
+ ("Ossetian" . "os")
+ ("Pali" . "pi")
+ ("Persian" . "fa")
+ ("Polish" . "pl")
+ ("Portuguese" . "pt")
+ ("Panjabi" . "pa")
+ ("Pushto" . "ps")
+ ("Quechua" . "qu")
+ ("Romanian" . "ro")
+ ("Romansh" . "rm")
+ ("Rundi" . "rn")
+ ("Russian" . "ru")
+ ("Samoan" . "sm")
+ ("Sango" . "sg")
+ ("Sanskrit" . "sa")
+ ("Sardinian" . "sc")
+ ("Gaelic" . "gd")
+ ("Serbian" . "sr")
+ ("Shona" . "sn")
+ ("Sichuan Yi" . "ii")
+ ("Sindhi" . "sd")
+ ("Sinhala" . "si")
+ ("Slovak" . "sk")
+ ("Slovenian" . "sl")
+ ("Somali" . "so")
+ ("Sotho, Southern" . "st")
+ ("Ndebele, South" . "nr")
+ ("Spanish" . "es")
+ ("Sundanese" . "su")
+ ("Swahili" . "sw")
+ ("Swati" . "ss")
+ ("Swedish" . "sv")
+ ("Tagalog" . "tl")
+ ("Tahitian" . "ty")
+ ("Tajik" . "tg")
+ ("Tamil" . "ta")
+ ("Tatar" . "tt")
+ ("Telugu" . "te")
+ ("Thai" . "th")
+ ("Tibetan" . "bo")
+ ("Tigrinya" . "ti")
+ ("Tonga (Tonga Islands)" . "to")
+ ("Tsonga" . "ts")
+ ("Tswana" . "tn")
+ ("Turkish" . "tr")
+ ("Turkmen" . "tk")
+ ("Twi" . "tw")
+ ("Ukrainian" . "uk")
+ ("Urdu" . "ur")
+ ("Uighur" . "ug")
+ ("Uzbek" . "uz")
+ ("Catalan" . "ca")
+ ("Venda" . "ve")
+ ("Vietnamese" . "vi")
+ ("Volapük" . "vo")
+ ("Walloon" . "wa")
+ ("Welsh" . "cy")
+ ("Western Frisian" . "fy")
+ ("Wolof" . "wo")
+ ("Xhosa" . "xh")
+ ("Yiddish" . "yi")
+ ("Yoruba" . "yo")
+ ("Zhuang" . "za")
+ ("Zulu" . "zu")))
;; web UI doesn't respect these for now
(defvar mastodon-iso-639-regional
diff --git a/lisp/mastodon-media.el b/lisp/mastodon-media.el
index c783130..4e50dbc 100644
--- a/lisp/mastodon-media.el
+++ b/lisp/mastodon-media.el
@@ -1,7 +1,9 @@
;;; mastodon-media.el --- Functions for inlining Mastodon media -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1"))
diff --git a/lisp/mastodon-notifications.el b/lisp/mastodon-notifications.el
index f5ddea3..b7fe038 100644
--- a/lisp/mastodon-notifications.el
+++ b/lisp/mastodon-notifications.el
@@ -1,7 +1,9 @@
;;; mastodon-notifications.el --- Notification functions for mastodon.el -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1"))
diff --git a/lisp/mastodon-profile.el b/lisp/mastodon-profile.el
index 54f0e84..7e3262a 100644
--- a/lisp/mastodon-profile.el
+++ b/lisp/mastodon-profile.el
@@ -1,7 +1,9 @@
;;; mastodon-profile.el --- Functions for inspecting Mastodon profiles -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
+;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 1.0.0
;; Package-Requires: ((emacs "27.1"))
@@ -75,6 +77,7 @@
(autoload 'mastodon-tl--get-link-header-from-response "mastodon-tl")
(autoload 'mastodon-tl--set-buffer-spec "mastodon-tl")
(autoload 'mastodon-tl--symbol "mastodon-tl")
+(autoload 'mastodon-auth--get-account-id "mastodon-auth")
(defvar mastodon-instance-url)
(defvar mastodon-tl--buffer-spec)
@@ -129,7 +132,7 @@ extra keybindings."
(defvar mastodon-profile-update-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-c") #'mastodon-profile--user-profile-send-updated)
- (define-key map (kbd "C-c C-k") #'kill-buffer-and-window)
+ (define-key map (kbd "C-c C-k") #'mastodon-profile--update-profile-note-cancel)
map)
"Keymap for `mastodon-profile-update-mode'.")
@@ -287,33 +290,83 @@ JSON is the data returned by the server."
(defun mastodon-profile--update-user-profile-note ()
"Fetch user's profile note and display for editing."
(interactive)
- (let* ((url (mastodon-http--api "accounts/verify_credentials"))
+ (let* ((endpoint "accounts/verify_credentials")
+ (url (mastodon-http--api endpoint))
(json (mastodon-http--get-json url))
(source (alist-get 'source json))
(note (alist-get 'note source))
(buffer (get-buffer-create "*mastodon-update-profile*"))
- (inhibit-read-only t))
+ (inhibit-read-only t)
+ (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)
(setq-local header-line-format
- (propertize
- "Edit your profile note. C-c C-c to send, C-c C-k to cancel."
- 'face font-lock-comment-face))
+ (propertize msg-str
+ 'face font-lock-comment-face))
(mastodon-profile-update-mode t)
- (insert note)
- (goto-char (point-min))
+ (insert (propertize (concat (propertize "0"
+ 'note-counter t
+ 'display nil)
+ "/500 characters")
+ 'read-only t
+ 'face 'font-lock-comment-face
+ 'note-header t)
+ "\n")
+ (make-local-variable 'after-change-functions)
+ (push #'mastodon-profile--update-note-count after-change-functions)
+ (let ((start-point (point)))
+ (insert note)
+ (goto-char start-point))
(delete-trailing-whitespace) ; remove all ^M's
- (message "Edit your profile note. C-c C-c to send, C-c C-k to cancel.")))
+ (message msg-str)))
+
+(defun mastodon-profile--update-note-count (&rest _args)
+ "Display the character count of the profile note buffer."
+ (let ((inhibit-read-only t)
+ (header-region (mastodon-tl--find-property-range 'note-header
+ (point-min)))
+ (count-region (mastodon-tl--find-property-range 'note-counter
+ (point-min))))
+ (add-text-properties (car count-region) (cdr count-region)
+ (list 'display
+ (number-to-string
+ (mastodon-toot--count-toot-chars
+ (buffer-substring-no-properties
+ (cdr header-region) (point-max))))))))
+
+(defun mastodon-profile--update-profile-note-cancel ()
+ "Cancel updating user profile and kill buffer and window."
+ (interactive)
+ (when (y-or-n-p "Cancel updating your profile note?")
+ (kill-buffer-and-window)))
+
+(defun mastodon-profile--note-remove-header ()
+ "Get the body of a toot from the current compose buffer."
+ (let ((header-region (mastodon-tl--find-property-range 'note-header
+ (point-min))))
+ (buffer-substring (cdr header-region) (point-max))))
(defun mastodon-profile--user-profile-send-updated ()
- "Send PATCH request with the updated profile note."
+ "Send PATCH request with the updated profile note.
+Ask for confirmation if length > 500 characters."
(interactive)
- (let* ((note (buffer-substring-no-properties (point-min) (point-max)))
+ (let* ((note (mastodon-profile--note-remove-header))
(url (mastodon-http--api "accounts/update_credentials")))
- (kill-buffer-and-window)
- (let ((response (mastodon-http--patch url `(("note" . ,note)))))
- (mastodon-http--triage response
- (lambda () (message "Profile note updated!"))))))
+ (if (> (mastodon-toot--count-toot-chars note) 500)
+ (when (y-or-n-p "Note is over mastodon's max for profile notes (500). Proceed?")
+ (kill-buffer-and-window)
+ (mastodon-profile--user-profile-send-updated-do url note))
+ (kill-buffer-and-window)
+ (mastodon-profile--user-profile-send-updated-do url note))))
+
+(defun mastodon-profile--user-profile-send-updated-do (url note)
+ "Send PATCH request with the updated profile note."
+ (let ((response (mastodon-http--patch url `(("note" . ,note)))))
+ (mastodon-http--triage response
+ (lambda () (message "Profile note updated!")))))
(defun mastodon-profile--update-preference (pref val &optional source)
"Update account PREF erence to setting VAL.
@@ -497,6 +550,9 @@ This endpoint only holds a few preferences. For others, see
(switch-to-buffer-other-window buf)
(erase-buffer)
(special-mode)
+ (mastodon-tl--set-buffer-spec (buffer-name buf)
+ "preferences"
+ nil)
(let ((inhibit-read-only t))
(while response
(let ((el (pop response)))
@@ -566,18 +622,17 @@ 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 (when no-reblogs '(("exclude_reblogs" . "t"))))
- (url (mastodon-http--api (format "accounts/%s/%s" id endpoint-type)))
+ (endpoint (format "accounts/%s/%s" id endpoint-type))
+ (url (mastodon-http--api endpoint))
(acct (mastodon-profile--account-field account 'acct))
(buffer (concat "*mastodon-" acct "-" endpoint-type "*"))
(response (if headers
(mastodon-http--get-response url args)
(mastodon-http--get-json url args)))
(json (if headers (car response) response))
- (endpoint (format "accounts/%s/%s" id endpoint-type))
(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
@@ -737,14 +792,6 @@ 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--view-author-profile ()
- "View the profile of author of present toot."
- (interactive)
- (let* ((toot-json (mastodon-tl--property 'toot-json))
- (acct (alist-get 'account toot-json))
- (handle (alist-get 'acct acct)))
- (mastodon-profile--show-user handle)))
-
(defun mastodon-profile--account-field (account field)
"Return FIELD from the ACCOUNT.
FIELD is used to identify regions under 'account"
@@ -840,5 +887,56 @@ These include the author, author of reblogged entries and any user mentioned."
(t
(mastodon-profile--search-account-by-handle handle)))))
+(defun mastodon-profile--remove-user-from-followers (&optional id)
+ "Remove a user from your followers.
+Optionally provide the ID of the account to remove."
+ (interactive)
+ (let* ((account (unless id (get-text-property (point) 'toot-json)))
+ (id (or id (alist-get 'id account)))
+ (handle (if account
+ (alist-get 'acct account)
+ (let ((account
+ (mastodon-profile--account-from-id id)))
+ (alist-get 'acct account))))
+ (url (mastodon-http--api
+ (format "accounts/%s/remove_from_followers" id))))
+ (when (y-or-n-p (format "Remove follower %s? " handle))
+ (let ((response (mastodon-http--post url)))
+ (mastodon-http--triage response
+ (lambda ()
+ (message "Follower %s removed!" handle)))))))
+
+(defun mastodon-profile--remove-from-followers-at-point ()
+ "Prompt for a user in the item at point and remove from followers."
+ (interactive)
+ (let* ((handles (mastodon-profile--extract-users-handles
+ (mastodon-profile--toot-json)))
+ (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)))
+ (mastodon-profile--remove-user-from-followers id)))
+
+(defun mastodon-profile--remove-from-followers-list ()
+ "Select a user from your followers and remove from followers.
+Currently limited to 100 handles. If not found, try
+`mastodon-search--search-query'."
+ (interactive)
+ (let* ((endpoint (format "accounts/%s/followers"
+ (mastodon-auth--get-account-id)))
+ (url (mastodon-http--api endpoint))
+ (response (mastodon-http--get-json url
+ `(("limit" . "100"))))
+ (handles (mapcar (lambda (x)
+ (cons
+ (alist-get 'acct x)
+ (alist-get 'id x)))
+ response))
+ (choice (completing-read "Remove from followers: "
+ handles))
+ (id (alist-get choice handles nil nil 'equal)))
+ (mastodon-profile--remove-user-from-followers id)))
+
(provide 'mastodon-profile)
;;; mastodon-profile.el ends here
diff --git a/lisp/mastodon-search.el b/lisp/mastodon-search.el
index 65c5aba..1aed676 100644
--- a/lisp/mastodon-search.el
+++ b/lisp/mastodon-search.el
@@ -40,28 +40,23 @@
(autoload 'mastodon-auth--access-token "mastodon-auth")
(autoload 'mastodon-http--get-search-json "mastodon-http")
(autoload 'mastodon-http--api "mastodon-http")
+(autoload 'mastodon-tl--set-buffer-spec "mastodon-tl")
+
(defvar mastodon-toot--completion-style-for-mentions)
(defvar mastodon-instance-url)
(defvar mastodon-tl--link-keymap)
(defvar mastodon-http--timeout)
(defvar mastodon-toot--enable-completion-for-mentions)
-(defvar mastodon-tl--buffer-spec)
;; functions for completion of mentions in mastodon-toot
-(defun mastodon-search--get-user-info-@-capf (account)
+(defun mastodon-search--get-user-info-@ (account)
"Get user handle, display name and account URL from ACCOUNT."
(list (concat "@" (cdr (assoc 'acct account)))
(cdr (assoc 'url account))
(cdr (assoc 'display_name account))))
-(defun mastodon-search--get-user-info-@ (account)
- "Get user handle, display name and account URL from ACCOUNT."
- (list (cdr (assoc 'display_name account))
- (concat "@" (cdr (assoc 'acct account)))
- (cdr (assoc 'url account))))
-
-(defun mastodon-search--search-accounts-query (query &optional capf)
+(defun mastodon-search--search-accounts-query (query)
"Prompt for a search QUERY and return accounts synchronously.
Returns a nested list containing user handle, display name, and URL."
(interactive "sSearch mastodon for: ")
@@ -69,9 +64,7 @@ Returns a nested list containing user handle, display name, and URL."
(response (if (equal mastodon-toot--completion-style-for-mentions "following")
(mastodon-http--get-json url `(("q" . ,query) ("following" . "true")) :silent)
(mastodon-http--get-json url `(("q" . ,query)) :silent))))
- (if capf
- (mapcar #'mastodon-search--get-user-info-@-capf response)
- (mapcar #'mastodon-search--get-user-info-@ response))))
+ (mapcar #'mastodon-search--get-user-info-@ response)))
;; functions for tags completion:
@@ -101,11 +94,9 @@ QUERY is the string to search."
(mastodon-mode)
(let ((inhibit-read-only t))
(erase-buffer)
- (setq mastodon-tl--buffer-spec
- `(buffer-name ,buffer
- endpoint ,(format "api/v1/trends")
- update-function
- (lambda (toot) (message "Trends."))))
+ (mastodon-tl--set-buffer-spec buffer
+ "api/v1/trends"
+ nil)
;; hashtag results:
(insert (mastodon-tl--set-face
(concat "\n ------------\n"
@@ -141,11 +132,9 @@ QUERY is the string to search."
(mastodon-mode)
(let ((inhibit-read-only t))
(erase-buffer)
- (setq mastodon-tl--buffer-spec
- `(buffer-name ,buffer
- endpoint ,(format "api/v2/search")
- update-function
- (lambda (toot) (message "Searched."))))
+ (mastodon-tl--set-buffer-spec buffer
+ "api/v2/search"
+ nil)
;; user results:
(insert (mastodon-tl--set-face
(concat "\n ------------\n"
diff --git a/lisp/mastodon-tl.el b/lisp/mastodon-tl.el
index ce2062d..a8bccb9 100644
--- a/lisp/mastodon-tl.el
+++ b/lisp/mastodon-tl.el
@@ -1,6 +1,7 @@
;;; mastodon-tl.el --- HTTP request/response functions for mastodon.el -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
@@ -36,6 +37,7 @@
(require 'thingatpt) ; for word-at-point
(require 'time-date)
(require 'cl-lib)
+(require 'mastodon-iso)
(require 'mpv nil :no-error)
@@ -78,6 +80,11 @@
(autoload 'mastodon-http--build-params-string "mastodon-http")
(autoload 'mastodon-notifications--filter-types-list "mastodon-notifications")
(autoload 'mastodon-toot--get-toot-edits "mastodon-toot")
+(autoload 'mastodon-toot--update-status-fields "mastodon-toot")
+(autoload 'mastodon-toot--compose-buffer "mastodon-toot")
+
+(defvar mastodon-toot--visibility)
+(defvar mastodon-active-user)
(when (require 'mpv nil :no-error)
(declare-function mpv-start "mpv"))
@@ -254,7 +261,7 @@ types of mastodon links and not just shr.el-generated ones.")
(when (require 'mpv nil :no-error)
(let ((map (make-sparse-keymap)))
(define-key map (kbd "<C-return>") 'mastodon-tl--mpv-play-video-from-byline)
- (define-key map (kbd "<return>") 'mastodon-profile--view-author-profile)
+ (define-key map (kbd "<return>") 'mastodon-profile--get-toot-author)
(keymap-canonicalize map)))
"The keymap to be set for the author byline.
It is active where point is placed by `mastodon-tl--goto-next-toot.'")
@@ -563,25 +570,25 @@ TIMESTAMP is assumed to be in the past."
(relative-result
(cond
((< seconds-difference 60)
- (cons "less than a minute ago"
+ (cons "just now"
60))
((< seconds-difference (* 1.5 60))
- (cons "one minute ago"
+ (cons "1 minute ago"
90)) ;; at 90 secs
((< seconds-difference (* 60 59.5))
(funcall regular-response seconds-difference 60 "minute"))
((< seconds-difference (* 1.5 60 60))
- (cons "one hour ago"
+ (cons "1 hour ago"
(* 60 90))) ;; at 90 minutes
((< seconds-difference (* 60 60 23.5))
(funcall regular-response seconds-difference (* 60 60) "hour"))
((< seconds-difference (* 1.5 60 60 24))
- (cons "one day ago"
+ (cons "1 day ago"
(* 1.5 60 60 24))) ;; at a day and a half
((< seconds-difference (* 60 60 24 6.5))
(funcall regular-response seconds-difference (* 60 60 24) "day"))
((< seconds-difference (* 1.5 60 60 24 7))
- (cons "one week ago"
+ (cons "1 week ago"
(* 1.5 60 60 24 7))) ;; a week and a half
((< seconds-difference (* 60 60 24 7 52))
(if (= 52 (floor (+ 0.5 (/ seconds-difference 60 60 24 7))))
@@ -589,7 +596,7 @@ TIMESTAMP is assumed to be in the past."
(* 60 60 24 7 52))
(funcall regular-response seconds-difference (* 60 60 24 7) "week")))
((< seconds-difference (* 1.5 60 60 24 365))
- (cons "one year ago"
+ (cons "1 year ago"
(* 60 60 24 365 1.5))) ;; a year and a half
(t
(funcall regular-response seconds-difference (* 60 60 24 365.25) "year")))))
@@ -1156,7 +1163,7 @@ this just means displaying toot client."
(let* ((poll (mastodon-tl--field 'poll toot))
(expiry (mastodon-tl--field 'expires_at poll))
(expired-p (if (eq (mastodon-tl--field 'expired poll) :json-false) nil t))
- (multi (mastodon-tl--field 'multiple poll))
+ ;; (multi (mastodon-tl--field 'multiple poll))
(voters-count (mastodon-tl--field 'voters_count poll))
(vote-count (mastodon-tl--field 'votes_count poll))
(options (mastodon-tl--field 'options poll))
@@ -1368,10 +1375,12 @@ BUFFER is buffer name, ENDPOINT is buffer's enpoint,
UPDATE-FUNCTION is its update function.
LINK-HEADER is the http Link header if present."
(setq mastodon-tl--buffer-spec
- `(buffer-name ,buffer
- endpoint ,endpoint
- update-function ,update-function
- link-header ,link-header)))
+ `(account ,(cons mastodon-active-user
+ mastodon-instance-url)
+ buffer-name ,buffer
+ endpoint ,endpoint
+ update-function ,update-function
+ link-header ,link-header)))
(defun mastodon-tl--more-json (endpoint id)
"Return JSON for timeline ENDPOINT before ID."
@@ -1451,7 +1460,7 @@ ID is that of the toot to view."
(mastodon-mode)
(mastodon-tl--set-buffer-spec buffer
(format "statuses/%s" id)
- (lambda (_toot) (message "END of thread.")))
+ nil)
(let ((inhibit-read-only t))
(mastodon-tl--toot toot :detailed-p))))))
@@ -1466,7 +1475,8 @@ ID is that of the toot to view."
(if (or (string= type "follow_request")
(string= type "follow")) ; no can thread these
(error "No thread")
- (let* ((url (mastodon-http--api (format "statuses/%s/context" id)))
+ (let* ((endpoint (format "statuses/%s/context" id))
+ (url (mastodon-http--api endpoint))
(buffer (format "*mastodon-thread-%s*" id))
(toot
;; refetch current toot in case we just faved/boosted:
@@ -1488,10 +1498,9 @@ ID is that of the toot to view."
(with-output-to-temp-buffer buffer
(switch-to-buffer buffer)
(mastodon-mode)
- (mastodon-tl--set-buffer-spec
- buffer
- (format "statuses/%s/context" id)
- (lambda (_toot) (message "END of thread.")))
+ (mastodon-tl--set-buffer-spec buffer
+ endpoint
+ nil)
(let ((inhibit-read-only t))
(mastodon-tl--timeline (alist-get 'ancestors context))
(goto-char (point-max))
@@ -1505,6 +1514,65 @@ ID is that of the toot to view."
;; else just print the lone toot:
(mastodon-tl--single-toot id)))))))
+
+(defun mastodon-tl--mute-thread ()
+ "Mute the thread displayed in the current buffer.
+Note that you can only (un)mute threads you have posted in."
+ (interactive)
+ (mastodon-tl--mute-or-unmute-thread))
+
+(defun mastodon-tl--unmute-thread ()
+ "Mute the thread displayed in the current buffer.
+Note that you can only (un)mute threads you have posted in."
+ (interactive)
+ (mastodon-tl--mute-or-unmute-thread :unmute))
+
+(defun mastodon-tl--mute-or-unmute-thread (&optional unmute)
+ "Mute a thread.
+If UNMUTE, unmute it."
+ (let ((endpoint (mastodon-tl--get-endpoint)))
+ (if (string-suffix-p "context" endpoint) ; thread view
+ (let* ((id
+ (save-match-data
+ (string-match "statuses/\\(?2:[[:digit:]]+\\)/context"
+ endpoint)
+ (match-string 2 endpoint)))
+ (we-posted-p (mastodon-tl--user-in-thread-p id))
+ (url (mastodon-http--api
+ (if unmute
+ (format "statuses/%s/unmute" id)
+ (format "statuses/%s/mute" id)))))
+ (if (not we-posted-p)
+ (message "You can only (un)mute a thread you have posted in.")
+ (when (if unmute
+ (y-or-n-p "Unnute this thread? ")
+ (y-or-n-p "Mute this thread? "))
+ (let ((response (mastodon-http--post url)))
+ (mastodon-http--triage response
+ (lambda ()
+ (if unmute
+ (message "Thread unmuted!")
+ (message "Thread muted!")))))))))))
+
+(defun mastodon-tl--user-in-thread-p (id)
+ "Return non-nil if the logged-in user has posted to the current thread.
+ID is that of the post the context is currently displayed for."
+ (let* ((context-json (mastodon-http--get-json
+ (mastodon-http--api (format "statuses/%s/context" id))
+ nil :silent))
+ (ancestors (alist-get 'ancestors context-json))
+ (descendants (alist-get 'descendants context-json))
+ (a-ids (mapcar (lambda (status)
+ (alist-get 'id
+ (alist-get 'account status)))
+ ancestors))
+ (d-ids (mapcar (lambda (status)
+ (alist-get 'id
+ (alist-get 'account status)))
+ descendants)))
+ (or (member (mastodon-auth--get-account-id) a-ids)
+ (member (mastodon-auth--get-account-id) d-ids))))
+
;;; LISTS
(defun mastodon-tl--get-users-lists ()
@@ -1951,6 +2019,9 @@ INSTANCE is an instance domain name."
(let ((buf (get-buffer-create "*mastodon-instance*")))
(with-current-buffer buf
(switch-to-buffer-other-window buf)
+ (mastodon-tl--set-buffer-spec (buffer-name buf)
+ "instance"
+ nil)
(let ((inhibit-read-only t))
(erase-buffer)
(special-mode)
@@ -2073,16 +2144,18 @@ IND is the optional indentation level to print at."
;;; FOLLOW/BLOCK/MUTE, ETC
-(defun mastodon-tl--follow-user (user-handle &optional notify)
+(defun mastodon-tl--follow-user (user-handle &optional notify langs)
"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.
-Can be called to toggle NOTIFY on users already being followed."
+Can be called to toggle NOTIFY on users already being followed.
+LANGS is an array parameters alist of languages to filer user's posts by."
(interactive
(list
(mastodon-tl--interactive-user-handles-get "follow")))
(mastodon-tl--do-if-toot
- (mastodon-tl--do-user-action-and-response user-handle "follow" nil notify)))
+ (mastodon-tl--do-user-action-and-response
+ user-handle "follow" nil notify langs)))
(defun mastodon-tl--enable-notify-user-posts (user-handle)
"Query for USER-HANDLE and enable notifications when they post."
@@ -2099,6 +2172,33 @@ Can be called to toggle NOTIFY on users already being followed."
(mastodon-tl--interactive-user-handles-get "disable")))
(mastodon-tl--follow-user user-handle "false"))
+(defun mastodon-tl--filter-user-user-posts-by-language (user-handle)
+ "Query for USER-HANDLE and enable notifications when they post.
+This feature is experimental and for now not easily varified by
+the instance API."
+ (interactive
+ (list
+ (mastodon-tl--interactive-user-handles-get "filter by language")))
+ (let ((langs (mastodon-tl--read-filter-langs)))
+ (mastodon-tl--do-if-toot
+ (mastodon-tl--follow-user user-handle nil langs))))
+
+(defun mastodon-tl--read-filter-langs (&optional langs)
+ "Read language choices and return an alist array parameter.
+LANGS is the accumulated array param alist if we re-run recursively."
+ (let* ((langs-alist langs)
+ (choice (completing-read "Filter user's posts by language: "
+ mastodon-iso-639-1)))
+ (when choice
+ (setq langs-alist
+ (push `("languages[]" . ,(alist-get choice mastodon-iso-639-1
+ nil nil
+ #'string=))
+ langs-alist))
+ (if (y-or-n-p "Filter by another language? ")
+ (mastodon-tl--read-filter-langs langs-alist)
+ langs-alist))))
+
(defun mastodon-tl--unfollow-user (user-handle)
"Query for USER-HANDLE from current status and unfollow that user."
(interactive
@@ -2141,6 +2241,16 @@ Can be called to toggle NOTIFY on users already being followed."
(message "Looks like you have no mutes to unmute!")
(mastodon-tl--do-user-action-and-response user-handle "unmute" t)))
+(defun mastodon-tl--dm-user (user-handle)
+ "Query for USER-HANDLE from current status and compose a message to that user."
+ (interactive
+ (list
+ (mastodon-tl--interactive-user-handles-get "message")))
+ (mastodon-tl--do-if-toot
+ (mastodon-toot--compose-buffer (concat "@" user-handle))
+ (setq mastodon-toot--visibility "direct")
+ (mastodon-toot--update-status-fields)))
+
(defun mastodon-tl--interactive-user-handles-get (action)
"Get the list of user-handles for ACTION from the current toot."
(mastodon-tl--do-if-toot
@@ -2191,12 +2301,13 @@ Action must be either \"unblock\" or \"unmute\"."
nil ; predicate
t))))
-(defun mastodon-tl--do-user-action-and-response (user-handle action &optional negp notify)
+(defun mastodon-tl--do-user-action-and-response (user-handle action &optional negp notify langs)
"Do ACTION on user 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'."
+NOTIFY is only non-nil when called by `mastodon-tl--follow-user'.
+LANGS is an array parameters alist of languages to filer user's posts by."
(let* ((account (if negp
;; if unmuting/unblocking, we got handle from mute/block list
(mastodon-profile--search-account-by-handle
@@ -2212,35 +2323,41 @@ NOTIFY is only non-nil when called by `mastodon-tl--follow-user'."
(name (if (not (string-empty-p (mastodon-profile--account-field account 'display_name)))
(mastodon-profile--account-field account 'display_name)
(mastodon-profile--account-field account 'username)))
- (url (mastodon-http--api
- (if notify
- (format "accounts/%s/%s?notify=%s" user-id action notify)
- (format "accounts/%s/%s" user-id action)))))
+ (args (cond (notify
+ `(("notify" . ,notify)))
+ (langs langs)
+ (t nil)))
+ (url (mastodon-http--api (format "accounts/%s/%s" user-id action))))
(if account
(if (equal action "follow") ; y-or-n for all but follow
- (mastodon-tl--do-user-action-function url name user-handle action notify)
+ (mastodon-tl--do-user-action-function url name user-handle action notify args)
(when (y-or-n-p (format "%s user %s? " action name))
- (mastodon-tl--do-user-action-function url name user-handle action)))
+ (mastodon-tl--do-user-action-function url name user-handle action args)))
(message "Cannot find a user with handle %S" user-handle))))
-(defun mastodon-tl--do-user-action-function (url name user-handle action &optional notify)
+(defun mastodon-tl--do-user-action-function (url name user-handle action &optional notify args)
"Post ACTION on user NAME/USER-HANDLE to URL.
NOTIFY is either \"true\" or \"false\", and used when we have been called
-by `mastodon-tl--follow-user' to enable or disable notifications."
- (let ((response (mastodon-http--post url)))
- (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))
- ((or (string-equal action "mute")
- (string-equal action "unmute"))
- (message "User %s (@%s) %sd!" name user-handle action))
- ((eq notify nil)
- (message "User %s (@%s) %sed!" name user-handle action)))))))
+by `mastodon-tl--follow-user' to enable or disable notifications.
+ARGS is an alist of any parameters to send with the request."
+ (let ((response (mastodon-http--post url args)))
+ (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))
+ ((or (string-equal action "mute")
+ (string-equal action "unmute"))
+ (message "User %s (@%s) %sd!" name user-handle action))
+ ((assoc "languages[]" args #'equal)
+ (message "User %s filtered by language(s): %s" name
+ (mapconcat #'cdr args " ")))
+ ((eq notify nil)
+ (message "User %s (@%s) %sed!" name user-handle action)))))))
;; FOLLOW TAGS
diff --git a/lisp/mastodon-toot.el b/lisp/mastodon-toot.el
index eefceda..42cce36 100644
--- a/lisp/mastodon-toot.el
+++ b/lisp/mastodon-toot.el
@@ -1,6 +1,7 @@
;;; mastodon-toot.el --- Minor mode for sending Mastodon toots -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2020-2022 Marty Hiatt
;; Author: Johnson Denen <johnson.denen@gmail.com>
;; Marty Hiatt <martianhiatus@riseup.net>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
@@ -793,8 +794,8 @@ instance to edit a toot."
(defun mastodon-toot--insert-toot-iter (it)
"Insert iteration IT of toot."
- (let ((content (alist-get 'content it))
- (account (alist-get 'account it)))
+ (let ((content (alist-get 'content it)))
+ ;; (account (alist-get 'account it))
;; TODO: handle polls, media
(mastodon-tl--render-text content)))
@@ -832,7 +833,7 @@ eg. \"feduser@fed.social\" -> \"feduser@fed.social\"."
"")))
(defun mastodon-toot--get-bounds (regex)
- "Get bounds of tag or handle before point."
+ "Get bounds of tag or handle before point using REGEX."
;; needed because # and @ are not part of any existing thing at point
(save-match-data
(save-excursion
@@ -858,8 +859,7 @@ eg. \"feduser@fed.social\" -> \"feduser@fed.social\"."
;; just for the annotation-function?
(setq mastodon-toot-completions
(mastodon-search--search-accounts-query
- (buffer-substring-no-properties start end)
- :capf))))
+ (buffer-substring-no-properties start end)))))
:exclusive 'no
:annotation-function
(lambda (candidate)
@@ -1121,14 +1121,11 @@ LENGTH is the maximum character length allowed for a poll option."
"Prompt for a language and set `mastodon-toot--language'.
Return its two letter ISO 639 1 code."
(interactive)
- (let* ((langs (mapcar (lambda (x)
- (cons (cadr x)
- (car x)))
- mastodon-iso-639-1))
- (choice (completing-read "Language for this toot: "
- langs)))
+ (let* ((choice (completing-read "Language for this toot: "
+ mastodon-iso-639-1)))
(setq mastodon-toot--language
- (alist-get choice langs nil nil 'equal))))
+ (alist-get choice mastodon-iso-639-1 nil nil 'equal))
+ (message "Language set to %s" choice)))
;; we'll need to revisit this if the binds get
;; more diverse than two-chord bindings
@@ -1220,6 +1217,9 @@ REPLY-TEXT is the text of the toot being replied to."
(propertize "Visibility"
'toot-post-visibility t)
" ⋅ "
+ (propertize "Language"
+ 'toot-post-language t)
+ " "
(propertize "CW"
'toot-post-cw-flag t)
" "
@@ -1273,6 +1273,8 @@ REPLY-JSON is the full JSON of the toot being replied to."
(point-min)))
(cw-region (mastodon-tl--find-property-range 'toot-post-cw-flag
(point-min)))
+ (lang-region (mastodon-tl--find-property-range 'toot-post-language
+ (point-min)))
(toot-string (buffer-substring-no-properties (cdr header-region)
(point-max))))
(add-text-properties (car count-region) (cdr count-region)
@@ -1288,10 +1290,16 @@ REPLY-JSON is the full JSON of the toot being replied to."
"private")
"followers-only"
mastodon-toot--visibility))))
+ (add-text-properties (car lang-region) (cdr lang-region)
+ (list 'display
+ (if mastodon-toot--language
+ (format "Language: %s"
+ mastodon-toot--language)
+ "")))
(add-text-properties (car nsfw-region) (cdr nsfw-region)
(list 'display (if mastodon-toot--content-nsfw
(if mastodon-toot--media-attachments
- "NSFW" "NSFW (no effect until attachments added)")
+ "NSFW" "NSFW (for attachments only)")
"")
'face 'mastodon-cw-face))
(add-text-properties (car cw-region) (cdr cw-region)
@@ -1443,7 +1451,9 @@ a draft into the buffer."
'completion-at-point-functions
#'mastodon-toot--tags-capf)
;; company
- (when mastodon-toot--use-company-for-completion
+ (when (and mastodon-toot--use-company-for-completion
+ (require 'company nil :no-error))
+ (declare-function 'company-mode-on "company")
(set (make-local-variable 'company-backends)
(add-to-list 'company-backends 'company-capf))
(company-mode-on)))
diff --git a/test/mastodon-search-tests.el b/test/mastodon-search-tests.el
index e6d4cdb..8dc597a 100644
--- a/test/mastodon-search-tests.el
+++ b/test/mastodon-search-tests.el
@@ -119,7 +119,7 @@
(should
(equal
(mastodon-search--get-user-info-@ mastodon-search--single-account-query)
- '(": ( ) { : | : & } ; :" "@mousebot" "https://todon.nl/@mousebot"))))
+ '("@mousebot" "https://todon.nl/@mousebot" ": ( ) { : | : & } ; :"))))
(ert-deftest mastodon-search--get-user-info ()
"Should build a list from a single account for company completion."
diff --git a/test/mastodon-tl-tests.el b/test/mastodon-tl-tests.el
index d32863b..726e21a 100644
--- a/test/mastodon-tl-tests.el
+++ b/test/mastodon-tl-tests.el
@@ -213,28 +213,28 @@ Strict-Transport-Security: max-age=31536000
(mastodon-tl--relative-time-description timestamp)))
(check (seconds expected)
(should (string= (format-seconds-since seconds) expected))))
- (check 1 "less than a minute ago")
- (check 59 "less than a minute ago")
- (check 60 "one minute ago")
- (check 89 "one minute ago") ;; rounding down
+ (check 1 "just now")
+ (check 59 "just now")
+ (check 60 "1 minute ago")
+ (check 89 "1 minute ago") ;; rounding down
(check 91 "2 minutes ago") ;; rounding up
(check (minutes 3.49) "3 minutes ago") ;; rounding down
(check (minutes 3.52) "4 minutes ago")
(check (minutes 59) "59 minutes ago")
- (check (minutes 60) "one hour ago")
- (check (minutes 89) "one hour ago")
+ (check (minutes 60) "1 hour ago")
+ (check (minutes 89) "1 hour ago")
(check (minutes 91) "2 hours ago")
(check (hours 3.49) "3 hours ago") ;; rounding down
(check (hours 3.51) "4 hours ago") ;; rounding down
(check (hours 23.4) "23 hours ago")
- (check (hours 23.6) "one day ago") ;; rounding up
- (check (days 1.48) "one day ago") ;; rounding down
+ (check (hours 23.6) "1 day ago") ;; rounding up
+ (check (days 1.48) "1 day ago") ;; rounding down
(check (days 1.52) "2 days ago") ;; rounding up
- (check (days 6.6) "one week ago") ;; rounding up
+ (check (days 6.6) "1 week ago") ;; rounding up
(check (weeks 2.49) "2 weeks ago") ;; rounding down
(check (weeks 2.51) "3 weeks ago") ;; rounding down
(check (1- (weeks 52)) "52 weeks ago")
- (check (weeks 52) "one year ago")
+ (check (weeks 52) "1 year ago")
(check (years 2.49) "2 years ago") ;; rounding down
(check (years 2.51) "3 years ago") ;; rounding down
))
@@ -1060,53 +1060,53 @@ correct value for following, as well as notifications enabled or disabled."
(let ((response-buffer-true (current-buffer)))
(insert mastodon-tl--follow-notify-true-response)
(with-mock
- (mock (mastodon-http--post url-follow-only)
- => response-buffer-true)
- (should
- (equal
- (mastodon-tl--do-user-action-function url-follow-only
- user-name
- user-handle
- "follow")
- "User some-user (@some-user@instance.url) followed!"))
- (mock (mastodon-http--post url-mute)
- => response-buffer-true)
- (should
- (equal
- (mastodon-tl--do-user-action-function url-mute
- user-name
- user-handle
- "mute")
- "User some-user (@some-user@instance.url) muted!"))
- (mock (mastodon-http--post url-block)
- => response-buffer-true)
- (should
- (equal
- (mastodon-tl--do-user-action-function url-block
- user-name
- user-handle
- "block")
- "User some-user (@some-user@instance.url) blocked!")))
+ (mock (mastodon-http--post url-follow-only nil)
+ => response-buffer-true)
+ (should
+ (equal
+ (mastodon-tl--do-user-action-function url-follow-only
+ user-name
+ user-handle
+ "follow")
+ "User some-user (@some-user@instance.url) followed!"))
+ (mock (mastodon-http--post url-mute nil)
+ => response-buffer-true)
+ (should
+ (equal
+ (mastodon-tl--do-user-action-function url-mute
+ user-name
+ user-handle
+ "mute")
+ "User some-user (@some-user@instance.url) muted!"))
+ (mock (mastodon-http--post url-block nil)
+ => response-buffer-true)
+ (should
+ (equal
+ (mastodon-tl--do-user-action-function url-block
+ user-name
+ user-handle
+ "block")
+ "User some-user (@some-user@instance.url) blocked!")))
(with-mock
- (mock (mastodon-http--post url-true) => response-buffer-true)
- (should
- (equal
- (mastodon-tl--do-user-action-function url-true
- user-name
- user-handle
- "follow"
- "true")
- "Receiving notifications for user some-user (@some-user@instance.url)!")))))
+ (mock (mastodon-http--post url-true nil) => response-buffer-true)
+ (should
+ (equal
+ (mastodon-tl--do-user-action-function url-true
+ user-name
+ user-handle
+ "follow"
+ "true")
+ "Receiving notifications for user some-user (@some-user@instance.url)!")))))
(with-temp-buffer
(let ((response-buffer-false (current-buffer)))
(insert mastodon-tl--follow-notify-false-response)
(with-mock
- (mock (mastodon-http--post url-false) => response-buffer-false)
- (should
- (equal
- (mastodon-tl--do-user-action-function url-false
- user-name
- user-handle
- "follow"
- "false")
- "Not receiving notifications for user some-user (@some-user@instance.url)!")))))))
+ (mock (mastodon-http--post url-false nil) => response-buffer-false)
+ (should
+ (equal
+ (mastodon-tl--do-user-action-function url-false
+ user-name
+ user-handle
+ "follow"
+ "false")
+ "Not receiving notifications for user some-user (@some-user@instance.url)!")))))))