Commit Diff


commit - 93ff9d7f210e985d4d61bdb8dda1ea69c2b2cb43
commit + c4a56c0a8e7accfd981a9b7b19515d5f3302c278
blob - e03df411766d7530d1f97f11d077a14853a3aee0
blob + 99641078e9a491a9611743912649a41225603666
--- internal/service/handlers.go
+++ internal/service/handlers.go
@@ -11,8 +11,13 @@ type TemplateData struct {
 	Results []rss.Channel
 }
 
+var templateFuncs = template.FuncMap{
+	"timeAgo": timeAgo,
+}
+
 func render(w http.ResponseWriter, name string, data any) {
-	tmpl := template.Must(template.ParseGlob("internal/templates/*.tmpl"))
+	tmpl := template.New("").Funcs(templateFuncs)
+	tmpl = template.Must(tmpl.ParseGlob("internal/templates/*.tmpl"))
 	tmpl.ExecuteTemplate(w, name, data)
 }
 
blob - /dev/null
blob + b207f5c5d52efde9845648e85728ecb33f499fb8 (mode 644)
--- /dev/null
+++ internal/service/util.go
@@ -0,0 +1,39 @@
+package service
+
+import (
+	"fmt"
+	"time"
+)
+
+func timeAgo(t time.Time) string {
+	duration := time.Since(t)
+
+	switch {
+	case duration < time.Minute:
+		return "just now"
+	case duration < time.Hour:
+		mins := int(duration.Minutes())
+		if mins == 1 {
+			return "1 minute ago"
+		}
+		return fmt.Sprintf("%d minutes ago", mins)
+	case duration < 24*time.Hour:
+		hrs := int(duration.Hours())
+		if hrs == 1 {
+			return "1 hour ago"
+		}
+		return fmt.Sprintf("%d hours ago", hrs)
+	case duration < 365*24*time.Hour:
+		days := int(duration.Hours()) / 24
+		if days == 1 {
+			return "1 day ago"
+		}
+		return fmt.Sprintf("%d days ago", days)
+	default:
+		years := int(duration.Hours()) / 24 / 365
+		if years == 1 {
+			return "1 year ago"
+		}
+		return fmt.Sprintf("%d years ago", years)
+	}
+}
blob - d74a5939f20dbb31284e8318c329c0f2bace4fc1
blob + 19559976b5fedec3fdbb7b5a905a98abe09800a9
--- internal/templates/results.tmpl
+++ internal/templates/results.tmpl
@@ -2,21 +2,33 @@
     <div class="results">
     {{ range . }}
         <div class="result">
-            <img src="{{ .Image.Href }}" />
+            <img src="{{ .Image.Href }}" width="170" height="170" />
             <div>
-                <h3>{{ .Title }}</h3>
+                <hgroup>
+                    <h2>{{ .Title }}</h2>
+                    <time datetime="{{ .PubDate.Format "2006-01-02T15:04:05Z07:00" }}">
+                        Last updated {{ .PubDate | timeAgo }}
+                    </time>
+                </hgroup>
                 <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 class="tag-line">
+                    {{ if gt (len .Link) 0 }}
+                        <a href="{{index .Link 0}}" class="button">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-globe2" viewBox="0 0 16 16">
+                                <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855q-.215.403-.395.872c.705.157 1.472.257 2.282.287zM4.249 3.539q.214-.577.481-1.078a7 7 0 0 1 .597-.933A7 7 0 0 0 3.051 3.05q.544.277 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9 9 0 0 1-1.565-.667A6.96 6.96 0 0 0 1.018 7.5zm1.4-2.741a12.3 12.3 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332M8.5 5.09V7.5h2.99a12.3 12.3 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.6 13.6 0 0 1 7.5 10.91V8.5zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741zm-3.282 3.696q.18.469.395.872c.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a7 7 0 0 1-.598-.933 9 9 0 0 1-.481-1.079 8.4 8.4 0 0 0-1.198.49 7 7 0 0 0 2.276 1.522zm-1.383-2.964A13.4 13.4 0 0 1 3.508 8.5h-2.49a6.96 6.96 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667m6.728 2.964a7 7 0 0 0 2.275-1.521 8.4 8.4 0 0 0-1.197-.49 9 9 0 0 1-.481 1.078 7 7 0 0 1-.597.933M8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855q.216-.403.395-.872A12.6 12.6 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.96 6.96 0 0 0 14.982 8.5h-2.49a13.4 13.4 0 0 1-.437 3.008M14.982 7.5a6.96 6.96 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008zM11.27 2.461q.266.502.482 1.078a8.4 8.4 0 0 0 1.196-.49 7 7 0 0 0-2.275-1.52c.218.283.418.597.597.932m-.488 1.343a8 8 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"/>
+                            </svg>
+                        </a>
+                    {{ end }}
+                    <a href="mailto:{{.Owner.Email}}" class="button" style="margin-right: 1rem">
+                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-envelope" viewBox="0 0 16 16">
+                            <path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"/>
+                        </svg>
+                    </a>
+
+                    {{ range .Categories }}
+                        <a class="category">#{{ .Text }}</a>
+                    {{ end }}
+                </div>
             </div>
         </div>
     {{ end }}
blob - b3c398b78b58e18fc417a6119e785b66f98aa4f5
blob + f49b78523709356d5ea4991e57b1a5c47965d94c
--- web/style.css
+++ web/style.css
@@ -4,6 +4,7 @@
     --pink: #ee8695;
     --blue: #57c2ff;
     --dark-blue: #468fb9;
+    --light-gray: #919191;
     --gray: #35343d;
     --dark-gray: #292831;
     --black: #1c1b1e;
@@ -86,7 +87,7 @@ main {
     &.search-area {
         margin: 0 auto;
         width: 100%;
-        max-width: 700px;
+        max-width: 900px;
     }
 }
 
@@ -230,9 +231,48 @@ main:has(.result)>hgroup.search-animation {
     margin-bottom: 1rem;
 
     img {
-        width: 200px;
-        height: 200px;
         border-radius: 2rem;
         margin-right: 1rem;
+    }
+
+    hgroup {
+        margin-bottom: 0.5rem;
+
+        h2 {
+            margin-bottom: 0;
+        }
+
+        time {
+            color: var(--light-gray);
+            font-size: .8em;
+        }
+    }
+
+    a {
+        font-size: .9em;
+    }
+
+    .tag-line {
+        display: flex;
+        align-items: center;
+
+        a.button {
+            margin-right: .5rem;
+            display: block;
+            color: var(--black);
+            padding: 4px;
+            border-radius: 100px;
+            line-height: 0;
+            background-color: white;
+        }
+
+        a.category {
+            margin-right: .5rem;
+            background-color: var(--dark-blue);
+            color: white;
+            font-size: .6em;
+            padding: .2rem .4rem;
+            border-radius: 1rem;
+        }
     }
 }
\ No newline at end of file