mirror of
https://github.com/edufeed-org/edufeed-web.git
synced 2025-12-10 00:34:34 +00:00
392 lines
15 KiB
Clojure
392 lines
15 KiB
Clojure
(ns ied.views
|
|
(:require
|
|
[re-frame.core :as re-frame]
|
|
[cljs.pprint :refer [pprint]]
|
|
[ied.events :as events]
|
|
[ied.routes :as routes]
|
|
[ied.subs :as subs]
|
|
[ied.nostr :as nostr]
|
|
[ied.components.icons :as icons]
|
|
[ied.opencard.views :as opencard]
|
|
[ied.views.search :as search]
|
|
[ied.views.resource :as resource]
|
|
[ied.components.modals :as modals]
|
|
[ied.views.add :as add]
|
|
[ied.components.resource :as resource-component]
|
|
[reagent.core :as reagent]))
|
|
|
|
;; event data modal
|
|
(defn event-data-modal []
|
|
(let [visible? @(re-frame/subscribe [::subs/show-event-data-modal])
|
|
selected-event @(re-frame/subscribe [::subs/selected-event])]
|
|
(when visible?
|
|
[:dialog {:open visible? :class "modal"}
|
|
[:div {:class "modal-box relative flex flex-col"}
|
|
[:h3 {:class "text-lg font-bold"} (nostr/get-name-from-metadata-event selected-event)]
|
|
[:pre (with-out-str (pprint selected-event))]
|
|
[:p {:class "py-4"} "Press ESC key or click outside to close"]]
|
|
|
|
[:form {:on-click #(re-frame/dispatch [::events/toggle-show-event-data-modal])
|
|
:method "dialog" :class "modal-backdrop"}
|
|
[:button "close"]]])))
|
|
|
|
;; metadata event component
|
|
;; TODO add author profile name and image
|
|
(defn metadata-event-component [event]
|
|
(let [selected-events @(re-frame/subscribe [::subs/selected-events])]
|
|
[:div {:class "flex flex-row gap-2"}
|
|
[:div {:class "w-3/4 p-2 min-h-full flex flex-col mt-2 justify-between"}
|
|
[:div {:class ""}
|
|
[:div {:class "flex flex-row gap-2 "}
|
|
[:div {:class "w-6 h-6 rounded-full bg-white"}
|
|
(cond
|
|
(= 30004 (:kind event)) [icons/layer-icon]
|
|
(= 30142 (:kind event)) [icons/file-icon])]
|
|
[:a {:href (nostr/get-d-id-from-event event)
|
|
:class "font-bold hover:underline text-ellipsis overflow-hidden"}
|
|
(nostr/get-name-from-metadata-event event)]]
|
|
[:div {:class "flex flex-wrap"}
|
|
(resource-component/about-tags [event])]
|
|
[:p {:class "text-wrap"}
|
|
(nostr/get-description-from-metadata-event event)]]
|
|
|
|
[:div {:class "flex flex-row justify-between items-center"}
|
|
[:button {:class "btn"
|
|
:on-click #(re-frame/dispatch [::events/toggle-show-event-data-modal event])} "Show Event Data"]
|
|
[resource-component/add-to-list event]]]
|
|
[:div {:class "w-1/4"}
|
|
[:figure
|
|
[:img
|
|
{:class "h-48 object-cover mx-auto m-2"
|
|
:loading "lazy"
|
|
:src
|
|
(nostr/get-image-from-metadata-event event)
|
|
:alt ""}]]]]))
|
|
|
|
(defn activity-feed []
|
|
(let [following @(re-frame/subscribe [::subs/following])
|
|
npub @(re-frame/subscribe [::subs/npub])
|
|
_ (when (nil? following)
|
|
(when npub (re-frame/dispatch [::events/get-follow-set-for-npub npub])))
|
|
following-events @(re-frame/subscribe [::subs/posts-from-pks-actor-follows])
|
|
_ (.log js/console (count following-events) (< 5 (count following-events)))
|
|
_ (when (> 5 (count following-events))
|
|
(re-frame/dispatch [::events/events-from-pks-actor-follows following]))]
|
|
|
|
[:div
|
|
(doall
|
|
(for [event following-events]
|
|
[:p {:key (:id event)} (:content event)]))]))
|
|
|
|
;; events
|
|
(defn events-panel []
|
|
(fn []
|
|
(let [events @(re-frame/subscribe [::subs/feed-events])
|
|
selected-events @(re-frame/subscribe [::subs/selected-events])]
|
|
[:div {:class "flex flex-col mx-auto gap-4 "}
|
|
[activity-feed]
|
|
(if (> (count events) 0)
|
|
[:div {:class "divide-y divide-slate-500"}
|
|
(doall
|
|
(for [event events]
|
|
[:div {:key (:id event)
|
|
:class ""}
|
|
[metadata-event-component event]]))]
|
|
|
|
[:p "no events there"])
|
|
[:button {:class "btn"
|
|
:disabled (not (boolean (seq selected-events)))
|
|
:on-click #(re-frame/dispatch [::events/add-metadata-event-to-list [{:d "unique-id-1"
|
|
:name "Test List SC"}
|
|
selected-events]])}
|
|
"Add To Lists"]])))
|
|
|
|
;; event feed component
|
|
(defn event-feed-component [event]
|
|
(let [_ () #_(re-frame/dispatch [::events/add-confetti])]
|
|
[:div
|
|
{:class "animate-flyIn card bg-base-100 w-64 h-64 shadow-xl border border-white border-w "}
|
|
(cond
|
|
(= 30004 (:kind event)) [:p {:class "text-2xl float-right"} "📖"]
|
|
(= 30142 (:kind event)) [:p {:class "text-2xl float-right"} "📚"])
|
|
[:figure
|
|
[:img
|
|
{:class "h-48 object-contain"
|
|
:src
|
|
(nostr/get-image-from-metadata-event event)
|
|
:alt ""}]]
|
|
[:div
|
|
{:class "card-body"}
|
|
[:button {:on-click #(re-frame/dispatch [::events/toggle-show-event-data-modal event])} "Show Event Data"]]]))
|
|
|
|
(defn event-feed-panel []
|
|
(let [events @(re-frame/subscribe [::subs/feed-events])]
|
|
[:div {:class ""}
|
|
[:h1 "Event Feed"]
|
|
[:p (str "Num of events: " (count events))]
|
|
(if (> (count events) 0)
|
|
[:div {:class "flex flex-col"}
|
|
(doall
|
|
(for [event events]
|
|
[:div {:key (:id event)}
|
|
[event-feed-component event]]))]
|
|
[:p "no events there"])]))
|
|
|
|
(defmethod routes/panels :event-feed-panel [] [event-feed-panel])
|
|
|
|
;; relays
|
|
(defn add-relay-form
|
|
[name uri]
|
|
(let [s (reagent/atom {:name name
|
|
:uri uri})]
|
|
(fn []
|
|
[:form {:on-submit (fn [e]
|
|
(.preventDefault e)
|
|
;; do something with the state @s
|
|
)}
|
|
[:label {:for name} "Name: "]
|
|
[:input {:type :text :name :name
|
|
:value (:name @s)
|
|
:on-change (fn [e]
|
|
(swap! s assoc :name (-> e .-target .-value)))}]
|
|
[:label {:for uri} "Uri: "]
|
|
[:input {:type :text :name :uri
|
|
:value (:uri @s)
|
|
:on-change (fn [e]
|
|
(swap! s assoc :uri (-> e .-target .-value)))}]
|
|
[:button {:on-click #(re-frame/dispatch [::events/create-websocket {:name (:name @s)
|
|
:id (random-uuid)
|
|
:uri (:uri @s)}])}
|
|
"Add Relay"]])))
|
|
|
|
(defn relays-panel
|
|
[]
|
|
(let [sockets (re-frame/subscribe [::subs/sockets])]
|
|
[:div
|
|
[add-relay-form]
|
|
|
|
(if (> (count @sockets) 0)
|
|
[:ul
|
|
(doall
|
|
(for [socket @sockets]
|
|
[:li {:key (:id socket)}
|
|
[:span (:name socket)]
|
|
[:span (:status socket)]
|
|
[:button {:class "btn"
|
|
:disabled (not= "connected" (:status socket))
|
|
:on-click #(re-frame/dispatch [::events/load-events (:uri socket)])} "Load events"]
|
|
(if (not= (:status socket) "connected")
|
|
[:button {:class "btn"
|
|
:on-click #(re-frame/dispatch [::events/connect-to-websocket (:uri socket)])} "Connect"]
|
|
[:button {:class "btn"
|
|
:on-click #(re-frame/dispatch [::events/close-connection-to-websocket (:uri socket)])} "Disconnect"])
|
|
|
|
[:button {:class "btn btn-error"
|
|
:on-click #(re-frame/dispatch [::events/remove-websocket socket])} "Remove relay"]]))]
|
|
[:p "No relays found"]
|
|
;(re-frame/dispatch [::events/connect-to-default-relays])
|
|
)]))
|
|
;; shopping cart
|
|
(defn shopping-cart []
|
|
(let [selected-events @(re-frame/subscribe [::subs/selected-events])]
|
|
[:div {:class "dropdown dropdown-end"}
|
|
[:div
|
|
{:tabIndex "0", :role "button", :class "btn btn-ghost btn-circle"}
|
|
[:div
|
|
{:class "indicator"}
|
|
[icons/shopping-cart]
|
|
[:span {:class "badge badge-sm indicator-item"} (count selected-events)]]]
|
|
[:div
|
|
{:tabIndex "0",
|
|
:class
|
|
"card card-compact dropdown-content bg-base-100 z-[1] mt-3 w-52 shadow"}
|
|
[:div
|
|
{:class "card-body"}
|
|
[:span {:class "text-lg font-bold"}
|
|
(str (count selected-events)
|
|
" Items")]
|
|
[:div {:class "card-actions"}
|
|
[:button {:class "btn"
|
|
:on-click #((modals/open-add-to-list-modal)
|
|
|
|
; re-frame/dispatch [::events/toggle-show-lists-modal]
|
|
)}
|
|
"Add To Lists"]]]]]))
|
|
|
|
(defn user-menu []
|
|
(let [pk @(re-frame/subscribe [::subs/pk])
|
|
user-profile (re-frame/subscribe [::subs/profile pk])]
|
|
(if pk
|
|
[:div
|
|
{:class "dropdown dropdown-end"}
|
|
[:div
|
|
{:tabIndex "0",
|
|
:role "button",
|
|
:class "btn btn-ghost btn-circle avatar"}
|
|
[:div
|
|
{:class "w-10 rounded-full"}
|
|
[:img
|
|
{:alt "user image",
|
|
:src
|
|
(nostr/profile-picture @user-profile pk)}]]]
|
|
[:ul
|
|
{:tabIndex "0",
|
|
:class
|
|
"menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow"}
|
|
[:li
|
|
[:a
|
|
{:on-click #(re-frame/dispatch [::events/navigate [:npub-view :npub (nostr/get-npub-from-pk pk)]])}
|
|
"Profile"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/navigate [:settings]])} "Settings"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/navigate [:keys]])} "Keys"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/logout])} "Logout"]]]]
|
|
[:div
|
|
{:class "dropdown dropdown-end"}
|
|
[:div {:tabIndex "0", :role "button", :class "btn m-1"} "Login"]
|
|
[:ul
|
|
{:tabIndex "0",
|
|
:class
|
|
"dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow"}
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/create-sk])}
|
|
"... Anonymously"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/login-with-extension])}
|
|
"... with Extension"]]]])))
|
|
|
|
;; Header
|
|
(defn header []
|
|
(let [pk @(re-frame/subscribe [::subs/pk])]
|
|
[:div
|
|
{:class "navbar sticky z-50 top-0 left-0 bg-base-100"}
|
|
[:div
|
|
{:class "flex-1"}
|
|
[:a {:class "cursor-pointer text-xl font-bold"
|
|
:on-click #(re-frame/dispatch [::events/navigate [:home]])}
|
|
"edufeed"]
|
|
#_[:a {:class "btn btn-circle"
|
|
:on-click #(re-frame/dispatch [::events/navigate [:event-feed]])} "Event-Feed"]]
|
|
[:div
|
|
{:class "flex-none"}
|
|
[:a {:disabled (nil? pk)
|
|
:class "btn btn-circle btn-primary text-xl"
|
|
:on-click #(re-frame/dispatch [::events/navigate [:add-resource]])} "+"]
|
|
(when (not (nil? pk))
|
|
[shopping-cart])
|
|
[user-menu]]]))
|
|
|
|
;; Keys Panel
|
|
(defn keys-panel []
|
|
(let [npub @(re-frame/subscribe [::subs/npub])
|
|
pk (nostr/get-pk-from-npub npub)
|
|
nsec @(re-frame/subscribe [::subs/nsec])] [:div
|
|
[:h1 "Keys"]
|
|
[:p (str "Your Npub: " npub)]
|
|
[:p (str "Your PK: " pk)]
|
|
[:p (str "Your Nsec: " nsec)]]))
|
|
|
|
(defmethod routes/panels :keys-panel [] [keys-panel])
|
|
|
|
;; Settings
|
|
(defn settings-panel []
|
|
[:div
|
|
[:h1 "Settings"]
|
|
[relays-panel]])
|
|
|
|
(defmethod routes/panels :settings-panel [] [settings-panel])
|
|
|
|
(defn sidepanel []
|
|
(let [pk (re-frame/subscribe [::subs/pk])]
|
|
(fn []
|
|
[:div {:class "static flex flex-col m-2 "}
|
|
[:ul {:class "menu rounded-box"}
|
|
[:li
|
|
[:a {:on-click #(re-frame/dispatch [::events/navigate [:home]])}
|
|
"Feed"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/navigate [:search-view]])}
|
|
"Search"]]
|
|
[:li [:a "My Network"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/navigate [:npub-view :npub (nostr/get-npub-from-pk @pk)]])}
|
|
"Bookmarks"]]
|
|
[:li [:a {:on-click #(re-frame/dispatch [::events/navigate [:opencard-view]])} "Opencard"]]]])))
|
|
|
|
;; Home
|
|
(defn home-panel []
|
|
(let [events (re-frame/subscribe [::subs/events])]
|
|
[:div {:class "basis-5/6"}
|
|
[events-panel]
|
|
[:p (count @events)]]))
|
|
|
|
(defmethod routes/panels :home-panel [] [home-panel])
|
|
|
|
;; about
|
|
(defn about-panel []
|
|
[:div
|
|
[:h1 "This is the About Page."]
|
|
[:div
|
|
[:a {:on-click #(re-frame/dispatch [::events/navigate [:home]])}
|
|
"go to Home Page"]]])
|
|
|
|
(defmethod routes/panels :about-panel [] [about-panel])
|
|
|
|
;; Lists
|
|
(defn list-component [list]
|
|
(let [pk @(re-frame/subscribe [::subs/pk])
|
|
ids-in-list (nostr/get-event-ids-from-list list)
|
|
events-in-list @(re-frame/subscribe [::subs/events-in-list ids-in-list]) ;; TODO should be sorted after appeareande in tags
|
|
]
|
|
[:div {:key (:id list)
|
|
:class "p-2"}
|
|
[:details
|
|
{:class "collapse bg-base-200"}
|
|
[:summary
|
|
{:class "collapse-title text-xl font-medium"}
|
|
(nostr/get-list-name list)]
|
|
[:div {:class "collapse-content"}
|
|
|
|
(doall
|
|
(for [event events-in-list]
|
|
[:div {:key (:id event)}
|
|
[metadata-event-component event]
|
|
(when pk
|
|
[:button {:class "btn btn-warning"
|
|
:on-click #(re-frame/dispatch [::events/delete-event-from-list [event list]])}
|
|
"Delete Resource from List"])]))
|
|
|
|
(when pk [:button {:class "btn btn-error"
|
|
:on-click #(re-frame/dispatch [::events/delete-list list])} "Delete List"])]]]))
|
|
|
|
;; npub / Profile
|
|
(defn npub-view-panel []
|
|
(let [sockets @(re-frame/subscribe [::subs/sockets])
|
|
route-params @(re-frame/subscribe [::subs/route-params])
|
|
lists @(re-frame/subscribe [::subs/lists-for-npub])
|
|
_ (when (not (seq lists)) (re-frame/dispatch [::events/get-lists-for-npub (:npub route-params)]))
|
|
missing-events-from-lists @(re-frame/subscribe [::subs/missing-events-from-lists])
|
|
_ (when (seq missing-events-from-lists) (re-frame/dispatch [::events/query-for-event-ids [sockets missing-events-from-lists]]))]
|
|
[:div
|
|
[:h1 (str "Hello Npub: " (:npub route-params))]
|
|
(if-not (seq lists)
|
|
[:p "No lists there...yet"]
|
|
(doall
|
|
(for [l lists]
|
|
^{:key (:id l)} [list-component l])))
|
|
[:button {:class "btn ml-auto mr-0"
|
|
:on-click #((modals/open-create-list-modal))}
|
|
"Create New List"]]))
|
|
|
|
(defmethod routes/panels :npub-view-panel [] [npub-view-panel])
|
|
|
|
;; main
|
|
(defn main-panel []
|
|
(let [active-panel (re-frame/subscribe [::subs/active-panel])]
|
|
[:div {:class "relative"}
|
|
[header]
|
|
[:div {:class "flex flex-row"}
|
|
[:div {:class "w-1/6"}
|
|
[sidepanel]]
|
|
[:div {:class "w-5/6 mt-1"}
|
|
[:div {:class ""}
|
|
(routes/panels @active-panel)]]
|
|
[modals/add-to-lists-modal]
|
|
[modals/create-list-modal]
|
|
[event-data-modal]]]))
|
|
|