Browse Source

feat: Add initial frontend views

Miguel Ángel Moreno 2 years ago
parent
commit
f2f9ab53c2

+ 54 - 0
src/frontend/tau/views.cljs

@@ -0,0 +1,54 @@
+(ns tau.views
+  (:require
+   [tau.views.player :as player]
+   [reitit.frontend.easy :as rfe]
+   [re-frame.core :as rf]))
+
+(defn footer
+  []
+  [:footer.bg-slate-900.text-gray-300.p-5.text-center
+   [:div
+    [:p (str "Tau " (.getFullYear (js/Date.)))]]])
+
+(defn search-bar
+  []
+  (let [global-search @(rf/subscribe [:global-search])
+        services @(rf/subscribe [:services])
+        service-id @(rf/subscribe [:service-id])]
+    [:div.flex
+     [:form {:on-submit (fn [e]
+                          (.preventDefault e)
+                          (rfe/push-state :tau.routes/search {} {:q global-search :serviceId service-id}))}
+      [:input.bg-slate-900.border.border-solid.border-black.rounded.py-2.px-1.mx-2.text-gray-500
+       {:type "text"
+        :value global-search
+        :on-change #(rf/dispatch [:change-global-search (.. % -target -value)])
+        :placeholder "Search for something"}]
+      [:select.mx-2.bg-gray-50.border.border-gray-900.text-gray-900
+       {:on-change #(rf/dispatch [:change-service-id (js/parseInt (.. % -target -value))])}
+       (for [service services]
+         [:option {:value (:id service) :key (:id service) :selected (= (:id service) service-id)}
+          (-> service :info :name)])]
+      [:button..bg-slate-900.border.border-black.rounded.border-solid.text-gray-500.p-2.mx-2
+       {:type "submit"} "Search"]]]))
+
+(defn navbar []
+  [:nav.bg-slate-800.flex.p-2.content-center.sticky.top-0.z-50
+   [:div.px-5.text-white.p-2
+    [:a {:href (rfe/href :tau.routes/home) :dangerouslySetInnerHTML {:__html "τ"}}]]
+   [:ul.flex.content-center.text-white.p-2
+    [:li.px-5 [:a {:href (rfe/href :tau.routes/home)} "Home"]]
+    [:li.px-5 [:a {:href (rfe/href :tau.routes/search)} "Search"]]]
+   [search-bar]])
+
+(defn app
+  []
+  (rf/dispatch [:get-services])
+  (let [current-match @(rf/subscribe [:current-match])]
+    [:div.font-sans.bg-slate-700.min-h-screen.flex.flex-col.h-full
+     [navbar]
+     [:div.flex.flex-col.justify-between {:class "min-h-[calc(100vh-58px)]"}
+      (when-let [view (-> current-match :data :view)]
+        [view current-match])
+      [player/global-player]
+      [footer]]]))

+ 7 - 0
src/frontend/tau/views/home.cljs

@@ -0,0 +1,7 @@
+(ns tau.views.home)
+
+(defn home-page
+  []
+  [:div.flex.justify-center.content-center.flex-col.text-center.text-white.text-lg.flex-auto
+   [:p.text-5xl.p-5 "Welcome to Tau"]
+   [:p.text-2xl "A web front-end for Newpipe"]])

+ 11 - 0
src/frontend/tau/views/player.cljs

@@ -0,0 +1,11 @@
+(ns tau.views.player
+  (:require
+   [re-frame.core :as rf]))
+
+(defn global-player
+  []
+  (let [global-stream @(rf/subscribe [:global-stream])
+        show-global-player? @(rf/subscribe [:show-global-player])]
+    [:div
+     [:audio {:src global-stream
+              :class (when-not show-global-player? "hidden")}]]))

+ 38 - 0
src/frontend/tau/views/search.cljs

@@ -0,0 +1,38 @@
+(ns tau.views.search
+  (:require
+   [re-frame.core :as rf]
+   [reitit.frontend.easy :as rfe]))
+
+(defn search-result
+  [title author url thumbnail id]
+  [:div.w-56.h-64.my-2 {:key id}
+   [:div.p-5.border.rounded.border-slate-900.m-2.bg-slate-600.flex.flex-col.max-w-full.min-h-full.max-h-full
+    [:a.overflow-hidden {:href (rfe/href :tau.routes/stream {} {:url url}) :title title}
+     [:div.flex.justify-center.min-w-full.py-3.box-border
+      [:div.h-28.min-w-full.flex.justify-center
+       [:img.rounded.object-cover.max-h-full {:src thumbnail}]]]
+     [:div.overflow-hidden
+      [:h1.text-gray-300.font-bold author]
+      [:h1 title]]]]])
+
+(defn search
+  [m]
+  (let [search-results (rf/subscribe [:search-results])
+        services (rf/subscribe [:services])
+        service-id (rf/subscribe [:service-id])]
+    [:div.text-gray-300.text-center.py-5.relative
+     [:h2 (str "Showing search results for: \"" (-> m :query-params :q) "\"")]
+     [:h1 (str "Number of search results: " (count (:items @search-results)))]
+     ;; TODO: Create loadable component that wraps other components that need to fetch from API
+     ;; or use a :loading key to show a spinner component instead
+     (if (empty? @search-results)
+       [:p "Loading"]
+       [:div.flex.justify-center.align-center.flex-wrap
+        (for [[i result] (map-indexed vector (:items @search-results))]
+          ;; TODO: Add a component per result type
+          [search-result
+           (:name result)
+           (:upload-author result)
+           (:url result)
+           (:thumbnail-url result)
+           i])])]))

+ 27 - 0
src/frontend/tau/views/stream.cljs

@@ -0,0 +1,27 @@
+(ns tau.views.stream
+  (:require
+   [re-frame.core :as rf]))
+
+(defn stream
+  [m]
+  (let [current-stream @(rf/subscribe [:stream])
+        stream-type (-> (if (empty? (:video-streams current-stream))
+                          (:audio-streams current-stream)
+                          (:video-streams current-stream))
+                        last
+                        :content)]
+       [:div.flex.flex-col.justify-center.p-5.items-center
+        [:div.flex.justify-center.py-2
+         [:div.flex.justify-center {:class "w-4/5"}
+          [:video.min-w-full.h-auto {:src stream-type :controls true}]]]
+        [:div.flex.text-white
+         [:button.border.rounded.border-slate-900.p-2.bg-slate-800
+          {:on-click #(rf/dispatch [:switch-to-global-player current-stream])}
+          "Add to global stream"]
+         [:a {:href (:url current-stream)}
+          "Open original source"]]
+        [:div.flex.flex-col.items-center.py-2 {:class "w-4/5"}
+         [:div.min-w-full.py-2
+          [:h1.text-xl.font-extrabold (:name current-stream)]]
+         [:div.min-w-full.py-2
+          [:p (:description current-stream)]]]]))