# Conflicts: # blog/sx/sx_components.py # federation/sx/profile.sx # federation/sx/sx_components.py # orders/sx/sx_components.py
106 lines
4.6 KiB
Plaintext
106 lines
4.6 KiB
Plaintext
;; Profile and actor timeline components
|
|
|
|
(defcomp ~federation-actor-profile-header (&key avatar display-name username domain summary follow)
|
|
(div :class "bg-white rounded-lg shadow-sm border border-stone-200 p-6 mb-6"
|
|
(div :class "flex items-center gap-4"
|
|
avatar
|
|
(div :class "flex-1"
|
|
(h1 :class "text-xl font-bold" display-name)
|
|
(div :class "text-stone-500" "@" username "@" domain)
|
|
summary)
|
|
follow)))
|
|
|
|
(defcomp ~federation-actor-timeline-layout (&key header timeline)
|
|
header
|
|
(div :id "timeline" timeline))
|
|
|
|
(defcomp ~federation-follow-form (&key action csrf actor-url label cls)
|
|
(div :class "flex-shrink-0"
|
|
(form :method "post" :action action
|
|
(input :type "hidden" :name "csrf_token" :value csrf)
|
|
(input :type "hidden" :name "actor_url" :value actor-url)
|
|
(button :type "submit" :class cls label))))
|
|
|
|
(defcomp ~federation-profile-summary (&key summary)
|
|
(div :class "text-sm text-stone-600 mt-2" (~rich-text :html summary)))
|
|
|
|
;; Public profile page
|
|
|
|
(defcomp ~federation-activity-obj-type (&key obj-type)
|
|
(span :class "text-sm text-stone-500" obj-type))
|
|
|
|
(defcomp ~federation-activity-card (&key activity-type published obj-type)
|
|
(div :class "bg-white rounded-lg shadow p-4"
|
|
(div :class "flex justify-between items-start"
|
|
(span :class "font-medium" activity-type)
|
|
(span :class "text-sm text-stone-400" published))
|
|
obj-type))
|
|
|
|
(defcomp ~federation-activities-list (&key items)
|
|
(div :class "space-y-4" items))
|
|
|
|
(defcomp ~federation-activities-empty ()
|
|
(p :class "text-stone-500" "No activities yet."))
|
|
|
|
(defcomp ~federation-profile-page (&key display-name username domain summary activities-heading activities)
|
|
(div :class "py-8"
|
|
(div :class "bg-white rounded-lg shadow p-6 mb-6"
|
|
(h1 :class "text-2xl font-bold" display-name)
|
|
(p :class "text-stone-500" "@" username "@" domain)
|
|
summary)
|
|
(h2 :class "text-xl font-bold mb-4" activities-heading)
|
|
activities))
|
|
|
|
(defcomp ~federation-profile-summary-text (&key text)
|
|
(p :class "mt-2" text))
|
|
|
|
;; Assembled actor timeline content — replaces Python _actor_timeline_content_sx
|
|
(defcomp ~federation-actor-timeline-content (&key remote-actor items is-following actor)
|
|
(let* ((display-name (or (get remote-actor "display_name") (get remote-actor "preferred_username") ""))
|
|
(icon-url (get remote-actor "icon_url"))
|
|
(summary (get remote-actor "summary"))
|
|
(actor-url (or (get remote-actor "actor_url") ""))
|
|
(csrf (csrf-token))
|
|
(initial (if (and (not icon-url) display-name)
|
|
(upper (slice display-name 0 1)) "?")))
|
|
(~federation-actor-timeline-layout
|
|
:header (~federation-actor-profile-header
|
|
:avatar (~avatar
|
|
:src icon-url
|
|
:cls (if icon-url "w-16 h-16 rounded-full"
|
|
"w-16 h-16 rounded-full bg-stone-300 flex items-center justify-center text-stone-600 font-bold text-xl")
|
|
:initial (when (not icon-url) initial))
|
|
:display-name (escape display-name)
|
|
:username (escape (or (get remote-actor "preferred_username") ""))
|
|
:domain (escape (or (get remote-actor "domain") ""))
|
|
:summary (when summary (~federation-profile-summary :summary summary))
|
|
:follow (when actor
|
|
(if is-following
|
|
(~federation-follow-form
|
|
:action (url-for "social.unfollow") :csrf csrf :actor-url actor-url
|
|
:label "Unfollow"
|
|
:cls "border border-stone-300 rounded px-4 py-2 hover:bg-stone-100")
|
|
(~federation-follow-form
|
|
:action (url-for "social.follow") :csrf csrf :actor-url actor-url
|
|
:label "Follow"
|
|
:cls "bg-stone-800 text-white rounded px-4 py-2 hover:bg-stone-700"))))
|
|
:timeline (~federation-timeline-items
|
|
:items items :timeline-type "actor" :actor actor
|
|
:next-url (when (not (empty? items))
|
|
(url-for "social.actor_timeline_page"
|
|
:id (get remote-actor "id")
|
|
:before (get (last items) "before_cursor")))))))
|
|
|
|
;; Data-driven activities list (replaces Python loop in render_profile_page)
|
|
(defcomp ~federation-activities-from-data (&key activities)
|
|
(if (empty? (or activities (list)))
|
|
(~federation-activities-empty)
|
|
(~federation-activities-list
|
|
:items (<> (map (lambda (a)
|
|
(~federation-activity-card
|
|
:activity-type (get a "activity_type")
|
|
:published (get a "published")
|
|
:obj-type (when (get a "object_type")
|
|
(~federation-activity-obj-type :obj-type (get a "object_type")))))
|
|
activities)))))
|