Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add contact feature #28

Merged
merged 8 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bb.edn
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{:deps {com.lambdaisland/launchpad {:mvn/version "0.31.142-alpha"}}}
{:deps {com.lambdaisland/launchpad {:mvn/version "0.33.149-alpha"}}}
14 changes: 8 additions & 6 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
{:paths ["src" "resources"]

:deps
{org.clojure/clojure {:mvn/version "1.11.3"}
{org.clojure/clojure {:mvn/version "1.11.4"}
ring/ring-jetty-adapter {:mvn/version "2.0.0-alpha1"}
fi.metosin/reitit {:mvn/version "0.7.0-alpha6"}
integrant/integrant {:mvn/version "0.10.0"}
integrant/integrant {:mvn/version "0.11.0"}
integrant/repl {:mvn/version "0.3.3"}
aero/aero {:mvn/version "1.1.6"}
mvxcvi/puget {:mvn/version "1.3.4"}
ring/ring-defaults {:mvn/version "0.5.0"}

com.lambdaisland/cli {:mvn/version "0.16.68"}
com.lambdaisland/cli {:mvn/version "0.18.74"}

com.lambdaisland/hiccup {:mvn/version "0.9.48"}
com.lambdaisland/ornament {:git/sha "0af129132bfbfeba940705031f83b60250e71dcf" ;; ornament-next with defprop/defrules/import-tokens
:git/url "https://github.com/lambdaisland/ornament.git"}
#_{:local/root "/home/arne/github/lambdaisland/ornament"}

io.pedestal/pedestal.log {:mvn/version "0.7.0"}
ch.qos.logback/logback-classic {:mvn/version "1.5.6"}
ch.qos.logback/logback-classic {:mvn/version "1.5.7"}

com.datomic/peer {:mvn/version "1.0.7180"
com.datomic/peer {:mvn/version "1.0.7187"
:exclusions [joda-time/joda-time]}
com.cnuernber/charred {:mvn/version "1.034"}

clj.qrgen/clj.qrgen {:mvn/version "0.4.0"}

hato/hato {:mvn/version "1.0.0"}
lambdaisland/uri {:mvn/version "1.19.155"}
cheshire/cheshire {:mvn/version "5.13.0"}
Expand All @@ -42,7 +44,7 @@

:prod
{:extra-deps
{org.postgresql/postgresql {:mvn/version "42.7.3"}}}
{org.postgresql/postgresql {:mvn/version "42.7.4"}}}

:test
{:extra-paths ["test"]
Expand Down
1 change: 1 addition & 0 deletions src/co/gaiwan/compass/db/schema.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
(def schema
[;; Start user entity
[:user/uuid :uuid "Unique user identifier" :identity]
[:user/hash :uuid "Temporary QR code identifier"]
[:user/contacts :ref "People you connected with / accepted a connection
request from. A :u/c B means that user A agrees to show their public profile
to user B. When two people connect we create connections in both directions,
Expand Down
9 changes: 7 additions & 2 deletions src/co/gaiwan/compass/html/navigation.clj
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,15 @@
(for [[href caption] {"/" "Sessions & Activities"
;; "/attendees" "Attendees"
;; "/profile" "Profile & Settings"
"/sessions/new" "Create Activity"}]
"/sessions/new" "Create Activity"
}]
[:li [:a {:href href
:on-click "document.body.classList.toggle('menu-open')"}
caption]])]]))
caption]])]
[:li [:a {:href (url-for :contact/qr)
:hx-target "#modal"
:on-click "document.body.classList.toggle('menu-open')"}
"Add Contact"]]]))

(o/defrules toggle-menu-button)

Expand Down
56 changes: 41 additions & 15 deletions src/co/gaiwan/compass/html/profiles.clj
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,34 @@
[:>* :w-full :aspect-square :rounded-full
{:background-size "cover"
:background-position "50% 50%"}]]
([{:profile/keys [image]} user]
([{:profile/keys [image]}]
[:<>
[:div.img
[:div
{:style {:background-image image}}]]]))

;; UI of attendee list

(o/defstyled attendee-card :div
[image-frame :w-100px]
([{:public-profile/keys [name hidden? bio]
:user/keys [uuid] :as user}]
[:<>
[image-frame {:profile/image (user/avatar-css-value user)}]
[:div.details
[:h3 name]
(if hidden?
[:label "Hide profile from public listing"]
[:label "Show profile from public listing"])
(when (:private-profile/name user)
[:div
[:label "Another Name:"]
[:label (:private-profile/name user)]])
(when bio
[:textarea (m/md->hiccup bio)])]]))

;; UI of profile detail

(o/defstyled edit-profile-btn :button
([user]
[:<>
Expand All @@ -34,28 +56,30 @@
"Edit Profile"]))

(o/defstyled profile-detail :div#detail
[image-frame :w-100px]
([{:public-profile/keys [name]
[image-frame :w-100px {--arc-thickness "7%"}]
([{:public-profile/keys [name hidden?]
:user/keys [uuid] :as user}]
[:<>
[image-frame {:profile/image (user/avatar-css-value user)} user]
[image-frame {:profile/image (user/avatar-css-value user)}]
[:div.details
[:h3.title name]]
(if hidden?
[:label "Hide profile from public listing"]
[:label "Show profile from public listing"])
(when (:private-profile/name user)
[:div
[:label "Another Name:"]
[:label (:private-profile/name user)]])
[:div
[:label "Contacts"]
[:ul
(for [c (:user/contacts user)]
[:li (:public-profile/name c)])]]

#_[:div (pr-str user)]
[:div.actions
[edit-profile-btn user]]]))

(o/defstyled attendee-card :div
[image-frame :w-100px]
([{:public-profile/keys [name bio]
:user/keys [uuid] :as user}]
[:<>
[image-frame {:profile/image (user/avatar-css-value user)} user]
[:div.details
[:h3 name]
(when bio
[:textarea (m/md->hiccup bio)])]]))

(o/defstyled private-name :div
([user {:keys [private-name-switch] :as params}]
(if (= "on" private-name-switch)
Expand Down Expand Up @@ -135,6 +159,8 @@
"Show different name to confidantes?"]
[:div.input-block {:id "private-name-block"}]
[:div
(when user
[image-frame {:profile/image (user/avatar-css-value user)}])
[:label {:for "image"} "Avatar"]
[:input {:id "image" :name "image" :type "file" :accept "image/png, image/jpeg"}]]
[:div
Expand Down
70 changes: 68 additions & 2 deletions src/co/gaiwan/compass/routes/profiles.clj
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
(ns co.gaiwan.compass.routes.profiles
"We need a page/route for user's profile"
(:require
[clj.qrgen :as qr]
[clojure.java.io :as io]
[clojure.string :as str]
[co.gaiwan.compass.config :as config]
[co.gaiwan.compass.db :as db]
[co.gaiwan.compass.db.queries :as q]
[co.gaiwan.compass.html.profiles :as h]
[co.gaiwan.compass.http.response :as response]
[co.gaiwan.compass.http.routing :refer [url-for]]
[co.gaiwan.compass.model.assets :as assets]
[co.gaiwan.compass.model.attendees :as attendees]
[ring.util.response :as ring-response]))
Expand Down Expand Up @@ -126,6 +128,41 @@
(ring-response/file-response (.getPath file))
(ring-response/not-found "File not found"))))

(defn eid->qr-hash
"create an uuid as the hash for eid to prevent guessing
store this uuid in the user"
[user-eid]
(let [qr-hash (random-uuid)]
@(db/transact [{:db/id user-eid
:user/hash qr-hash}])
qr-hash))

(defn qr-hash->eid
"Accept a uuid type's qr-hash, and use it to query the
user's eid"
[qr-hash]
(db/q '[:find ?e .
:in $ ?hash
:where
[?e :user/hash ?hash]]
(db/db) qr-hash))

(defn GET-qr-html [req]
{:html/body [:div
[:h2 "Add Contact"]
[:img {:src (url-for :contact/qr-png)}]]
:html/layout false})

(defn GET-qr-code
[{:keys [identity] :as req}]
(let [user-eid (:db/id identity)
host (config/value :compass/origin)
qr-hash (eid->qr-hash user-eid)
url (str host "/attendees/" qr-hash)
qr-image (qr/as-bytes (qr/from url :size [400 400]))]
(-> (ring-response/response qr-image)
(assoc-in [:headers "content-type"] "image/png"))))

(defn GET-attendees [req]
(let [attendees (q/all-users)]
{:html/body
Expand All @@ -134,6 +171,25 @@
(for [atd (attendees/user-list attendees)]
(h/attendee-card atd))]}))

(defn GET-contact [req]
{:html/body [:button {:hx-post (url-for :contact/add {:uuid "123"})}]})

(defn POST-contact
"Part of the url is hash of the contact's user eid
Decode it and add that contact"
[{:keys [identity] :as req}]
(let [user-eid (:db/id identity)
qr-hash (parse-uuid (get-in req [:path-params :qr-hash]))
contact-eid (qr-hash->eid qr-hash)
;; According to the schema
;; A :u/c B means that user A agrees to show their public profile to user B.
;; contact -> A
;; user -> B
_ @(db/transact [{:db/id contact-eid
:user/contacts user-eid}])]
(response/redirect "/profile"
{:flash "Successfully Saved!"})))

(defn routes []
[["/profile"
[""
Expand All @@ -153,6 +209,16 @@
["/uploads/:filename"
{:middleware [[response/wrap-requires-auth]]
:get {:handler file-handler}}]
["/contact"
{:middleware [[response/wrap-requires-auth]]}
["/qr" {:name :contact/qr
:get {:handler GET-qr-html}}]
["/qr.png" {:name :contact/qr-png
:get {:handler GET-qr-code}}]]
["/attendees"
{:middleware [[response/wrap-requires-auth]]
:get {:handler GET-attendees}}]])
[""
{:middleware [[response/wrap-requires-auth]]
:get {:handler GET-attendees}}]
["/:qr-hash"
{:middleware [[response/wrap-requires-auth]]
:get {:handler GET-contact}}]]])
Loading