Skip to content

Instantly share code, notes, and snippets.

@plexus
Created November 15, 2022 08:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plexus/55f2f16d87ef6aefed6af9845e9fc9e2 to your computer and use it in GitHub Desktop.
Save plexus/55f2f16d87ef6aefed6af9845e9fc9e2 to your computer and use it in GitHub Desktop.
(ns co.gaiwan.slack-widgets.ui.components
(:require
[clojure.string :as str]
[clojure.walk :as walk]
[reagent.core :as reagent]
[reagent.ratom :as ratom]
[lambdaisland.glogi :as log]
[co.gaiwan.slack-widgets.ui.state :as state]
[lambdaisland.ornament :as o])
(:require-macros [co.gaiwan.slack-widgets.ui.macros :refer [for!]]))
(defn emoji [code]
(if-let [unicode-or-url (state/lookup-emoji code)]
(if (str/starts-with? unicode-or-url "http")
[:img.emoji {:src unicode-or-url}]
[:span.emoji {:title code} unicode-or-url])
[:span.emoji code]))
(o/defstyled reaction-bar :div
:mt-1 :flex :items-start :flex-wrap
[:.reaction :rounded :bg-gray-100 :mr-3 :px-2 :py-0.5 :text-xs
[:img :w-4 :inline]]
([{:keys [reactions reply-count]}]
[:<>
(for [[code count] reactions]
(when (> count 0)
^{:key code}
[:div.reaction [emoji code] " " count]))
(when (> reply-count 0)
[:div.replies reply-count " replies"])]))
(defn expand-markup [hiccup]
(walk/postwalk
(fn [item]
(cond
(seq? item)
(into [:<>] item)
(and (vector? item) (keyword? (first item)))
(case (first item)
:slack-widgets/emoji
[emoji (-> item second :name)]
:slack-widgets/user
[:span "@" (:user/real-name (second item))]
:slack-widgets/channel
[:span "#" (:channel/name (second item))]
item)
:else
item))
hiccup))
(o/defstyled message-item-social :div
[:.message :flex :items-start :mb-4
[:>img :w-10 :h-10 :rounded :mr-3]
[:>.right
{:margin-top "-0.5rem"}
:flex-1 :overflow-hidden
[:>.content :text-black :leading-normal]]
[:.title
[:.display-name :font-bold :text-sm]
[:.ts :text-grey :text-xs :ml-2]]]
[:.score :text-gray-400]
([channel-id ts]
(let [{:message/keys [hiccup reactions permalink time replies timestamp]
:user/keys [name real-name]
:user-profile/keys [display-name image-48]
channel-link :channel/link
user-profile-link :user-profile/link
:as message}
@(state/message channel-id ts)]
[:div.message
{:data-ts timestamp}
[:img {:src image-48}]
[:div.right
[:div.title
[:span.display-name display-name]
[:a {:rel "nofollow" :href permalink :target "_blank"}
[:span.ts {:title "Open message in Slack"} @(state/ts->relative-time timestamp)]]
[:small.score (str " Score: " (state/pure-score message)
#_@(state/relevancy channel-id ts))]]
#_(log/trace :hiccup/rendering {:h hiccup
:e (expand-markup hiccup)})
[:p.content (expand-markup hiccup)]
[reaction-bar {:reactions reactions
:reply-count (count replies)}]]])))
(o/defstyled message-item-live :div
[:.message :flex :items-start :flex-col :mb-4 :bg-white :rounded :rounded-lg
:max-w-md :px-8 :py-8 :mx-auto
[:>.top :flex :items-start :items-center :justify-center :flex
[:>img :w-12 :h-12 :rounded :mr-4 :rounded :rounded-full]
[:>right :flex-1 :overflow-hidden
]]
[:>.bottom :mt-4
[:>.content :text-black :leading-normal
{:font-size "1.625rem"}]]
[:.title
[:.display-name :font-bold]
[:.ts :text-grey :text-xs :font-light]]]
([channel-id ts]
(let [{:message/keys [hiccup reactions permalink time replies timestamp]
:user/keys [name real-name]
:user-profile/keys [display-name real-name image-48]
channel-link :channel/link
user-profile-link :user-profile/link
:as message}
@(state/message channel-id ts)]
[:div.message
{:data-ts timestamp}
[:div.top
[:img {:src image-48}]
[:div.right
[:div.title
[:div
[:div.display-name (or display-name real-name)]
[:a {:rel "nofollow" :href permalink :target "_blank"}
[:span.ts {:title "Open message in Slack"} @(state/ts->relative-time timestamp)]]]]]]
[:div.bottom
[:p.content (expand-markup hiccup)]
[reaction-bar {:reactions reactions
:reply-count (count replies)}]]])))
(o/defstyled main :div
:p-2 :w-full :bg-black :mx-auto
[:h2 :text-4xl :text-white :text-center]
[:h2.title :mt-8 :mb-2]
[:h2.title-channel-name :text-2xl :font-bold :mt-8 :mb-8]
([channel-id]
[:<>
#_[:h2.title "top comments from"]
[:h2.title-channel-name (str "#" (:channel/name @(state/channel channel-id)))]
[:div.message-wrapper
(for! [ts @(state/relevant-message-ids channel-id)]
^{:key ts}
[message-item-live channel-id ts])]]))
(o/defstyled rounded-button :button
:inline-block
:px-2 :rounded :rounded-full :text-xs :bg-gray-200 :whitespace-nowrap
:shadow-lg
:leading-4 :py-2 :ml-1)
(o/defstyled tweetable-message-item :div
:relative
:flex
[:>.overlay-buttons :absolute :top-0 :right-0]
[message-item-social :flex-grow]
([channel-id ts]
[:<>
[message-item-social channel-id ts]
(let [{:message/keys [text-rendered]} @(state/message channel-id ts)]
[:div.overlay-buttons
[rounded-button
{:on-click (fn [_] (js/navigator.clipboard.writeText text-rendered))}
"Copy to clipboard"]
[:a {:class [rounded-button]
:href (str "https://twitter.com/intent/tweet?text="
(js/encodeURIComponent (:message/text-rendered @(state/message channel-id ts))))
:target "_blank"}
"Tweet"]])]))
(o/defstyled social-media-widget :div
:lg:container :mx-auto
:p-2
[:h2 :text-lg :font-bold :mb-1]
[:.channel-id :text-gray-300 :text-sm]
[:.chan-name :mr-2 :px-1 :font-bold :text-lg :rounded]
[:.brief-explanation :text-sm :text-gray-600 :mb-2]
[:.active :text-white :bg-black]
[:.messages :mt-2]
([channel-ids !active-channel]
(let [active-channel @!active-channel]
[:<>
[:h2 "Social media tool" [:span.channel-id " #" active-channel]]
[:p.brief-explanation "Lists highest scoring messages, in chronological order. Score is calculated based on number of reactions and replies a message receives."]
[:p
(for! [chan-id channel-ids]
^{:key (str "chan-name-" chan-id)}
[:a.chan-name {:class (cond-> []
(= chan-id active-channel)
(conj "active"))
:on-click #(reset! !active-channel chan-id)}
(:channel/name @(state/channel chan-id))])]
[:div.messages
(for! [ts @(state/channel-message-ids-by-ts active-channel)]
^{:key ts}
[tweetable-message-item active-channel ts])]])))
(defn social-media-page [channel-ids]
(let [active-channel (reagent/atom (first channel-ids))]
(fn [channel-ids]
[social-media-widget channel-ids active-channel])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment