mirror of
https://github.com/tanrax/org-social.el
synced 2026-01-09 14:23:33 +01:00
313 lines
13 KiB
EmacsLisp
313 lines
13 KiB
EmacsLisp
;;; org-social-ui-profile.el --- Profile buffer for Org-social -*- lexical-binding: t -*- -*- coding: utf-8 -*-
|
|
|
|
;; SPDX-License-Identifier: GPL-3.0
|
|
;; Author: Andros Fenollosa <hi@andros.dev>
|
|
;; Version: 2.4
|
|
;; URL: https://github.com/tanrax/org-social.el
|
|
|
|
;;; Commentary:
|
|
;; Profile view for user information.
|
|
|
|
;;; Code:
|
|
|
|
(require 'org-social-variables)
|
|
(require 'org-social-ui-core)
|
|
(require 'org-social-ui-utils)
|
|
(require 'org-social-ui-components)
|
|
|
|
;; Forward declarations
|
|
(declare-function request "request" (url &rest args))
|
|
(declare-function request-response-status-code "request" (response))
|
|
(declare-function org-social-parser--get-value "org-social-parser" (feed key))
|
|
(declare-function org-social-parser--get-posts-from-feed "org-social-parser" (feed))
|
|
(declare-function org-social-ui-timeline "org-social-ui-timeline" ())
|
|
(declare-function org-social-ui-thread "org-social-ui-thread" (post-url))
|
|
|
|
;; Thread tracking variables (defined in org-social-ui-thread.el)
|
|
(defvar org-social-ui--thread-stack)
|
|
(defvar org-social-ui--thread-level)
|
|
|
|
(defun org-social-ui-profile (user-url)
|
|
"Display profile screen for USER-URL."
|
|
(interactive "sUser URL: ")
|
|
(setq org-social-ui--current-screen 'profile)
|
|
|
|
;; Show message in minibuffer
|
|
(message "Building profile...")
|
|
|
|
(let ((buffer-name org-social-ui--profile-buffer-name))
|
|
;; Prepare buffer in background
|
|
(with-current-buffer (get-buffer-create buffer-name)
|
|
(kill-all-local-variables)
|
|
|
|
;; Disable read-only mode before modifying buffer
|
|
(setq buffer-read-only nil)
|
|
|
|
(let ((inhibit-read-only t))
|
|
(erase-buffer))
|
|
(remove-overlays)
|
|
|
|
;; Insert header
|
|
(org-social-ui--insert-profile-header)
|
|
|
|
(goto-char (point-min)))
|
|
|
|
;; Fetch and display profile
|
|
(org-social-ui--fetch-and-display-profile user-url)))
|
|
|
|
;;; Profile Screen
|
|
|
|
(defun org-social-ui--insert-profile-header ()
|
|
"Insert profile header with navigation and actions."
|
|
(org-social-ui--insert-logo)
|
|
|
|
;; Back button (kill buffer)
|
|
(widget-create 'push-button
|
|
:notify (lambda (&rest _) (kill-current-buffer))
|
|
:help-echo "Close profile"
|
|
" ← Back ")
|
|
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
|
|
(org-social-ui--insert-separator))
|
|
|
|
(defun org-social-ui--fetch-and-display-profile (user-url)
|
|
"Fetch and display profile data for USER-URL."
|
|
(require 'request nil t)
|
|
(if (not (featurep 'request))
|
|
(progn
|
|
(org-social-ui--insert-formatted-text "Error: request library not available.\n" nil "#ff6b6b")
|
|
(org-social-ui--insert-formatted-text "Profile viewing requires the 'request' package to be installed.\n" nil "#666666"))
|
|
(let ((url user-url)) ; Capture variable for closures
|
|
(request url
|
|
:timeout 15
|
|
:success (cl-function
|
|
(lambda (&key data &allow-other-keys)
|
|
(org-social-ui--display-profile-data url data)))
|
|
:error (cl-function
|
|
(lambda (&key error-thrown response &allow-other-keys)
|
|
(let ((status-code (when response (request-response-status-code response))))
|
|
(if (eq status-code 404)
|
|
;; Handle 404 silently - don't spam the user
|
|
(org-social-ui--display-profile-error url "Profile not found (404)")
|
|
;; Handle other errors normally
|
|
(org-social-ui--display-profile-error url error-thrown)))))))))
|
|
|
|
(defun org-social-ui--display-profile-data (user-url data)
|
|
"Display profile DATA for USER-URL in the current buffer."
|
|
(with-current-buffer org-social-ui--profile-buffer-name
|
|
(let ((inhibit-read-only t))
|
|
(setq buffer-read-only nil)
|
|
|
|
;; Insert profile info
|
|
(goto-char (point-max))
|
|
|
|
;; Parse and display profile data
|
|
(condition-case err
|
|
(org-social-ui--parse-and-display-profile data user-url)
|
|
(error
|
|
(org-social-ui--insert-formatted-text
|
|
(format "Error parsing profile: %s\n" (error-message-string err))
|
|
nil "#ff6b6b")))
|
|
|
|
;; Insert raw content section (collapsed by default)
|
|
(org-social-ui--insert-separator)
|
|
(org-social-ui--insert-formatted-text "📄 Raw Feed Content:\n\n" nil "#666666")
|
|
|
|
;; Display raw content (as plain text, without 'org-mode' formatting)
|
|
(insert data)
|
|
|
|
;; Setup buffer with special mode and centering
|
|
(org-social-ui--setup-centered-buffer)
|
|
(goto-char (point-min))
|
|
(setq buffer-read-only t)))
|
|
;; Switch to buffer now that everything is ready
|
|
(switch-to-buffer org-social-ui--profile-buffer-name)
|
|
(message "Profile ready"))
|
|
|
|
(defun org-social-ui--display-profile-error (user-url error)
|
|
"Display ERROR message for failed profile fetch of USER-URL."
|
|
(with-current-buffer org-social-ui--profile-buffer-name
|
|
(let ((inhibit-read-only t))
|
|
(setq buffer-read-only nil)
|
|
|
|
;; Insert error message
|
|
(goto-char (point-max))
|
|
(org-social-ui--insert-formatted-text
|
|
(format "Failed to fetch profile from %s\n" user-url)
|
|
'bold "#ff6b6b")
|
|
(org-social-ui--insert-formatted-text
|
|
(format "Error: %s\n\n" error)
|
|
nil "#666666")
|
|
(org-social-ui--insert-formatted-text
|
|
"Please check the URL and your internet connection.\n"
|
|
nil "#666666")
|
|
|
|
;; Setup buffer with special mode and centering
|
|
(org-social-ui--setup-centered-buffer)
|
|
(goto-char (point-min))
|
|
(setq buffer-read-only t)))
|
|
;; Switch to buffer now
|
|
(switch-to-buffer org-social-ui--profile-buffer-name)
|
|
(message "Profile error"))
|
|
|
|
(defun org-social-ui--parse-and-display-profile (data user-url)
|
|
"Parse and display profile DATA from USER-URL."
|
|
(let* ((feed-data data)
|
|
(nick (org-social-parser--get-value feed-data "NICK"))
|
|
(description (org-social-parser--get-value feed-data "DESCRIPTION"))
|
|
(avatar (org-social-parser--get-value feed-data "AVATAR"))
|
|
(location (org-social-parser--get-value feed-data "LOCATION"))
|
|
(birthday (org-social-parser--get-value feed-data "BIRTHDAY"))
|
|
(language (org-social-parser--get-value feed-data "LANGUAGE"))
|
|
(pinned-id (org-social-parser--get-value feed-data "PINNED"))
|
|
(links (org-social-parser--get-value feed-data "LINK"))
|
|
(contacts (org-social-parser--get-value feed-data "CONTACT"))
|
|
(follows (org-social-parser--get-value feed-data "FOLLOW"))
|
|
(groups (org-social-parser--get-value feed-data "GROUP"))
|
|
(all-posts (org-social-parser--get-posts-from-feed feed-data)))
|
|
|
|
;; Avatar section (moved above nick)
|
|
(when (and avatar (not (string-empty-p avatar)))
|
|
;; Display image if it's a valid image URL
|
|
(if (org-social-ui--image-p avatar)
|
|
(progn
|
|
(org-social-ui--insert-formatted-text " ")
|
|
(org-social-ui--put-image-from-cache avatar nil 200)
|
|
(org-social-ui--insert-formatted-text "\n ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,avatar))
|
|
:help-echo "Open image in browser"
|
|
"🔗 View in browser")
|
|
(org-social-ui--insert-formatted-text "\n\n"))
|
|
;; If not an image, show as before
|
|
(progn
|
|
(org-social-ui--insert-formatted-text "🖼️ ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,avatar))
|
|
:help-echo "View avatar"
|
|
avatar)
|
|
(org-social-ui--insert-formatted-text "\n\n"))))
|
|
|
|
;; Nick section
|
|
(org-social-ui--insert-formatted-text "Nick: " 'bold "#ffaa00")
|
|
(if nick
|
|
(org-social-ui--insert-formatted-text (format "🧑💻 %s\n" nick))
|
|
(org-social-ui--insert-formatted-text "🧑💻 Anonymous User\n"))
|
|
|
|
;; Description section
|
|
(when (and description (not (string-empty-p description)))
|
|
(org-social-ui--insert-formatted-text "Description: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text (format "%s\n" description)))
|
|
|
|
;; Location section
|
|
(when (and location (not (string-empty-p location)))
|
|
(org-social-ui--insert-formatted-text "Location: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text (format "📍 %s\n" location)))
|
|
|
|
;; Birthday section
|
|
(when (and birthday (not (string-empty-p birthday)))
|
|
(org-social-ui--insert-formatted-text "Birthday: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text (format "🎂 %s\n" birthday)))
|
|
|
|
;; Language section
|
|
(when (and language (not (string-empty-p language)))
|
|
(org-social-ui--insert-formatted-text "Languages: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text (format "🗣️ %s\n" language)))
|
|
|
|
;; Profile URL section
|
|
(org-social-ui--insert-formatted-text "URL: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text "🔗 ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,user-url))
|
|
:help-echo "Open profile URL"
|
|
user-url)
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
|
|
;; Links section
|
|
(when links
|
|
(org-social-ui--insert-formatted-text "\nLinks: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
(let ((links-list (if (listp links) links (list links))))
|
|
(dolist (link links-list)
|
|
(org-social-ui--insert-formatted-text " • ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,link))
|
|
:help-echo "Open link"
|
|
link)
|
|
(org-social-ui--insert-formatted-text "\n"))))
|
|
|
|
;; Contact section
|
|
(when contacts
|
|
(org-social-ui--insert-formatted-text "\nContact: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
(let ((contacts-list (if (listp contacts) contacts (list contacts))))
|
|
(dolist (contact contacts-list)
|
|
(org-social-ui--insert-formatted-text " • ")
|
|
(cond
|
|
((string-prefix-p "mailto:" contact)
|
|
(org-social-ui--insert-formatted-text "✉️ ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,contact))
|
|
:help-echo "Send email"
|
|
(substring contact 7)))
|
|
((string-prefix-p "xmpp:" contact)
|
|
(org-social-ui--insert-formatted-text "💬 ")
|
|
(org-social-ui--insert-formatted-text (substring contact 5)))
|
|
((string-prefix-p "https://" contact)
|
|
(org-social-ui--insert-formatted-text "🌍 ")
|
|
(widget-create 'push-button
|
|
:notify `(lambda (&rest _)
|
|
(eww ,contact))
|
|
:help-echo "Open profile"
|
|
contact))
|
|
(t
|
|
(org-social-ui--insert-formatted-text "📞 ")
|
|
(org-social-ui--insert-formatted-text contact)))
|
|
(org-social-ui--insert-formatted-text "\n"))))
|
|
|
|
;; Follows section
|
|
(when follows
|
|
(org-social-ui--insert-formatted-text "\nFollowing: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
(let ((follows-list (if (listp follows) follows (list follows))))
|
|
(dolist (follow follows-list)
|
|
(org-social-ui--insert-formatted-text " • ")
|
|
(org-social-ui--insert-formatted-text follow)
|
|
(org-social-ui--insert-formatted-text "\n"))))
|
|
|
|
;; Groups section
|
|
(when groups
|
|
(org-social-ui--insert-formatted-text "\nGroups: " 'bold "#ffaa00")
|
|
(org-social-ui--insert-formatted-text "\n")
|
|
(let ((groups-list (if (listp groups) groups (list groups))))
|
|
(dolist (group groups-list)
|
|
(org-social-ui--insert-formatted-text " • ")
|
|
(org-social-ui--insert-formatted-text group)
|
|
(org-social-ui--insert-formatted-text "\n"))))
|
|
|
|
;; Pinned post section
|
|
(when (and pinned-id (not (string-empty-p pinned-id)) all-posts)
|
|
(let ((pinned-post (cl-find-if
|
|
(lambda (post)
|
|
(string= (alist-get 'timestamp post) pinned-id))
|
|
all-posts)))
|
|
(when pinned-post
|
|
(org-social-ui--insert-separator)
|
|
(org-social-ui--insert-formatted-text "\n📌 Pinned Post\n\n" 'bold "#ffaa00")
|
|
;; Add author information to the post
|
|
(let ((post-with-author (append pinned-post
|
|
(list (cons 'author-nick nick)
|
|
(cons 'author-url user-url)
|
|
(cons 'author-avatar avatar)))))
|
|
(org-social-ui--post-component post-with-author nil t)))))))
|
|
|
|
|
|
(provide 'org-social-ui-profile)
|
|
;;; org-social-ui-profile.el ends here
|