Commit Diff


commit - ed95fc8404c32829d0715ad12d6150cb663c049c
commit + 874b706b1cb8542a2aa62bd35d6da2ba7bc16f78
blob - 569fb214121210f5a197fcb76bb0289a2499044a
blob + 3a6e23323fb3edd9d1ef72cfec4be4c458b63e9e
--- internal/service/search.go
+++ internal/service/search.go
@@ -1,53 +1,38 @@
 package service
 
 import (
-	"html/template"
-	"log"
 	"net/http"
 	"net/url"
 
 	"github.com/streatCodes/rss/rss"
 )
 
-type TemplateData struct {
-	Results []string
-}
-
-func render(w http.ResponseWriter, name string, data any) {
-	tmpl := template.Must(template.ParseGlob("internal/templates/*.tmpl"))
-	tmpl.ExecuteTemplate(w, name, data)
-}
-
-func (service *Service) homeHandler(w http.ResponseWriter, r *http.Request) {
-	render(w, "home", nil)
-}
-
-func (service *Service) searchHandler(w http.ResponseWriter, r *http.Request) {
-	isHtmx := r.Header.Get("HX-Request")
-	searchResult := r.URL.Query().Get("search")
-
-	log.Printf("Search: %s\n", searchResult)
-
+func (service *Service) findChannel(query string) ([]rss.Channel, error) {
 	//If the search query is a URL then ingest the feed
-	if parsedURL, err := url.ParseRequestURI(searchResult); err == nil {
-		log.Println("Found URL")
-		res, err := http.Get(searchResult)
+	if parsedURL, err := url.ParseRequestURI(query); err == nil {
+		//Check to see if we have the feed in the database
+		if channel, err := service.db.GetFeed([]byte(query)); channel != nil && err == nil {
+			return []rss.Channel{*channel}, nil
+		}
+
+		//Fetch from the internet
+		res, err := http.Get(query)
 		if err != nil {
-			panic("TODO")
+			return nil, err
 		}
 
-		f, err := rss.Decode(res.Body)
+		feed, err := rss.Decode(res.Body)
 		if err != nil {
-			panic("TODO")
+			return nil, err
 		}
 
 		key := []byte(parsedURL.String())
-		service.db.SaveFeed(key, &f.Channel)
+		err = service.db.SaveFeed(key, &feed.Channel)
+		if err != nil {
+			return nil, err
+		}
+		return []rss.Channel{feed.Channel}, nil
 	}
 
-	if isHtmx == "true" {
-		render(w, "results", []string{searchResult})
-	} else {
-		render(w, "home", TemplateData{Results: []string{searchResult}})
-	}
+	return []rss.Channel{}, nil
 }
blob - /dev/null
blob + 4b18ed7b92c24b94ded69ac7c9f72d3b415a8587 (mode 644)
--- /dev/null
+++ internal/service/handlers.go
@@ -0,0 +1,37 @@
+package service
+
+import (
+	"html/template"
+	"net/http"
+
+	"github.com/streatCodes/rss/rss"
+)
+
+type TemplateData struct {
+	Results []rss.Channel
+}
+
+func render(w http.ResponseWriter, name string, data any) {
+	tmpl := template.Must(template.ParseGlob("internal/templates/*.tmpl"))
+	tmpl.ExecuteTemplate(w, name, data)
+}
+
+func (service *Service) homeHandler(w http.ResponseWriter, r *http.Request) {
+	render(w, "home", nil)
+}
+
+func (service *Service) searchHandler(w http.ResponseWriter, r *http.Request) {
+	isHtmx := r.Header.Get("HX-Request")
+	searchQuery := r.URL.Query().Get("search")
+
+	channels, err := service.findChannel(searchQuery)
+	if err != nil {
+		panic("TODO")
+	}
+
+	if isHtmx == "true" {
+		render(w, "results", channels)
+	} else {
+		render(w, "home", TemplateData{Results: channels})
+	}
+}
blob - 01923330f89d7dcde4d7ccf0cc1c3525e8813ab4
blob + d74a5939f20dbb31284e8318c329c0f2bace4fc1
--- internal/templates/results.tmpl
+++ internal/templates/results.tmpl
@@ -1,7 +1,24 @@
 {{ define "results" }}
     <div class="results">
     {{ range . }}
-        <div class="result">{{ . }}</div>
+        <div class="result">
+            <img src="{{ .Image.Href }}" />
+            <div>
+                <h3>{{ .Title }}</h3>
+                <p>{{ .Description }}</p>
+                {{ range .Link }}
+                    <a href="{{.}}">{{ . }}</a>
+                {{ end }}
+                <!-- <div>Copyright - {{ .Copyright }}</div> -->
+                <div>PubDate - {{ .PubDate }}</div>
+                <div>Items - {{ len .Items }}</div>
+                {{ range .Categories }}
+                    <div>Category - {{ .Text }}</div>
+                {{ end }}
+                <div>Owner - {{ .Owner.Email }}</div>
+                <!-- <div>Explicit - {{ .Explicit }}</div> -->
+            </div>
+        </div>
     {{ end }}
     </div>
 {{ end }}
\ No newline at end of file
blob - da992a297e97bd57d0db1dac3d28069d234372b2
blob + b54f0b31a6885e0c6d5655fff30222904da89b17
--- rss/rss.go
+++ rss/rss.go
@@ -37,25 +37,31 @@ type Channel struct {
 	PubDate       RFC1123Time `xml:"pubDate"`
 	TTL           int         `xml:"ttl"`
 	Items         []Item      `xml:"item"`
+	Language      string      `xml:"language"`
 
-	ITunesImage      string           `xml:"itunes:image"`
-	ITunesAuthor     string           `xml:"itunes:author"`
-	ITunesCategories []ItunesCategory `xml:"itunes:category"`
-	ITunesOwner      []ItunesOwner    `xml:"itunes:owner"`
-	ITunesExplicit   bool             `xml:"itunes:explicit"`
+	//Additional  metadata
+	Image      Image      `xml:"image"`
+	Author     string     `xml:"author"`
+	Categories []Category `xml:"category"`
+	Owner      Owner      `xml:"owner"`
+	Explicit   bool       `xml:"explicit"`
 }
 
 type AtomLink struct {
 	Href string `xml:"href,attr"`
 }
 
-type ItunesCategory struct {
+type Image struct {
+	Href string `xml:"href,attr"`
+}
+
+type Category struct {
 	Text string `xml:"text,attr"`
 }
 
-type ItunesOwner struct {
-	Name  string `xml:"itunes:name"`
-	Email string `xml:"itunes:email"`
+type Owner struct {
+	Name  string `xml:"name"`
+	Email string `xml:"email"`
 }
 
 type Item struct {
blob - e5edb9bb43d9a16eb0624c1483c7e177a9ec46d6
blob + 29868590b90b48eeb4158681a5840cb26c12e1ff
--- rss/rss_test.go
+++ rss/rss_test.go
@@ -26,13 +26,21 @@ func TestDecode(t *testing.T) {
 	if feed.Channel.Link[0] != "https://risky.biz/" {
 		t.Errorf("Channel.Link: expected %q, got %q", "https://risky.biz/", feed.Channel.Link)
 	}
-	// Fri, 11 Apr 2025 14:42:00 +1000
 	if feed.Channel.PubDate.String() != "2025-04-11 14:42:00 +1000 AEST" {
 		t.Errorf("Channel.PubDate: expected %q, got %q", "2025-04-11 14:42:00 +1000 AEST", feed.Channel.PubDate.String())
 	}
-	if feed.Channel.ITunesExplicit != false {
-		t.Errorf("Channel.ITunesExplicit: expected %t, got %t", false, feed.Channel.ITunesExplicit)
+	if feed.Channel.Image.Href != "https://risky.biz/static/img/rb-news.png" {
+		t.Errorf("feed.Channel.Image: expected %q, got %q", "https://risky.biz/static/img/rb-news.png", feed.Channel.Image.Href)
 	}
+	if feed.Channel.Explicit != false {
+		t.Errorf("Channel.Explicit: expected %t, got %t", false, feed.Channel.Explicit)
+	}
+	if feed.Channel.Categories[0].Text != "News" {
+		t.Errorf("Channel.Explicit: expected %q, got %q", "News", feed.Channel.Categories[0].Text)
+	}
+	if feed.Channel.Categories[1].Text != "Technology" {
+		t.Errorf("Channel.Explicit: expected %q, got %q", "Technology", feed.Channel.Categories[0].Text)
+	}
 
 	item := feed.Channel.Items[0]
 	if item.Title != "Risky Bulletin: Trump orders investigation into former CISA director Chris Krebs" {
blob - d68dc54d6642e3d88afa288017eb85d2a38994cd
blob + b3c398b78b58e18fc417a6119e785b66f98aa4f5
--- web/style.css
+++ web/style.css
@@ -199,6 +199,7 @@ main:has(.result)>hgroup.search-animation {
     box-shadow: var(--shadow);
     display: flex;
     align-items: center;
+    margin-bottom: 2rem;
 
     input {
         all: unset;
@@ -218,4 +219,20 @@ main:has(.result)>hgroup.search-animation {
     button:focus, button:hover {
         color: var(--blue);
     }
+}
+
+.results .result {
+    background-color: var(--black);
+    display: flex;
+    border-radius: 2rem;
+    padding: 1rem;
+    box-shadow: var(--shadow);
+    margin-bottom: 1rem;
+
+    img {
+        width: 200px;
+        height: 200px;
+        border-radius: 2rem;
+        margin-right: 1rem;
+    }
 }
\ No newline at end of file