small pixel drawing of a pufferfish vore

ux
Jes Olson j3s@c3f.net
Mon, 20 Mar 2023 07:36:38 -0700
commit

19ac01ca21d3f6b2b6183181c138342f6a06d6ef

parent

e4e3daf57fb2f9d9c04fea4ce86a9deb553fb9af

M files/feeds.tmpl.htmlfiles/feeds.tmpl.html

@@ -1,25 +1,13 @@

{{ define "feeds" }} {{ template "head" . }} {{ template "nav" . }} +<h3>subscribed feeds</h3> {{ if .LoggedIn }} {{ $length := len .Data }} {{ if eq $length 1 }} <p>you're subscribed to 1 feed -{{ else if eq $length 0 }} -<p>you haven't subscribed to any feeds yet ⁉️ -<p>here's some urls to play with. copypasta them into the text box and click update. -<pre> -https://100r.co/links/rss.xml -https://begriffs.com/atom.xml -https://cyberia.club/blog/blog.xml -https://davebucklin.com/feed.xml -https://herman.bearblog.dev/feed/ -https://j3s.sh/feed.atom -https://katherine.cox-buday.com/rss.xml -https://sequentialread.com/rss -</pre> {{ else }} -<p>you're subscribed to {{ len .Data }} feeds 🗿 +<p>you're subscribed to {{ len .Data }} feeds {{ end }} <form method="POST" action="/feeds/submit"> <textarea name="submit" rows="10" cols="50">

@@ -30,18 +18,57 @@ </textarea>

<br> <input type="submit" value="update feeds"> </form> -{{ range .Data }} +{{ $length := len .Data }} +{{ if eq $length 0 }} <pre> -==> {{ .Title }} + ‼️ tutorial ‼️ + +vore supports one type of action: submitting feeds + +when you submit feeds: + - the feeds are validated and fetched + - the feeds are saved to the vore database + - your user is subscribed to the feeds + +once you have subscribed to some feeds, +their posts will appear on your <a href="/{{ .Username }}">homepage</a> + +note that your homepage is public ‼️ + +here are some feed urls to play with - copy them into +the text box above, then press [update feeds] + +https://100r.co/links/rss.xml +https://begriffs.com/atom.xml +https://cyberia.club/blog/blog.xml +https://davebucklin.com/feed.xml +https://facklambda.dev/atom.xml +https://herman.bearblog.dev/feed/ +https://j3s.sh/feed.atom +https://katherine.cox-buday.com/rss.xml +https://sequentialread.com/rss - title: {{ .Title }} - url: {{ .UpdateURL }} - items: {{ len .Items }} + + /)/) +( . .) +( づ♡ +</pre> +{{ else if gt $length 0 }} +<h3>feed details</h3> +{{ end }} +{{ range .Data }} +<pre> +{{ if .Title }}{{ .Title }}{{ else }}{{ .UpdateURL }}{{ end }} { + title = {{ .Title }} + desc = {{ .Description }} + url = {{ .UpdateURL }} + items = {{ len .Items }} +} </pre> {{ end }} {{ else }} - <p>⚠️ unauthorized: you are not logged in + <p>⚠️ unauthorized: please log in {{ end }} {{ template "tail" . }} {{ end }}
M files/index.tmpl.htmlfiles/index.tmpl.html

@@ -2,37 +2,37 @@ {{ define "index" }}

{{ template "head" . }} {{ template "nav" . }} <pre> - hey! hi. you. - yes, you. - - you might be wondering what's going on. - i'm here to help clear things up. - - first of all: where are we? what sort - of new-york-times-ass looking pile of - shit has just rendered on your liquid - crystal display? - - don't worry, it's all quite simple. - - that piece of shit has a name. - and that name is vore. - - - vore - a feed reader + hello friend + + you might be wondering what's going on. + i'm here to help clear things up. + + first of all: where are we? what sort + of new-york-times-ass looking pile of + shit has just rendered on your liquid + crystal display? + + don't worry, it's all quite simple. + + that piece of shit has a name. + and that name is vore. + + + vore + a feed reader - features: - - minimal & reliable - - handles rss & atom feeds - - updates all feeds periodically - - shows you a date-ordered list of posts - - open source & free of charge forever - (and not in the shitty open core kind of way) - - <a href="https://j3s.sh">jes</a> built it + - minimal & reliable + - handles rss & atom feeds + - updates all feeds periodically + - shows you a date-ordered list of posts + - open source & free of charge forever + (and not in the shitty open core kind of way) + - <a href="https://j3s.sh">jes</a> built it anti-features: - no tags - no options + - no javascript - no unread indicators or push notifs - no comments, upvotes, or ranks </pre>
A files/login.tmpl.html

@@ -0,0 +1,25 @@

+{{ define "login" }} +{{ template "head" . }} +{{ template "nav" . }} +<h3>login</h3> +<form method="POST" action="/login"> + <label for="username">username:</label> + <input type="text" name="username" required> + <br> + <label for="password">password:</label> + <input type="password" name="password" required> + <br> + <input type="submit" value="login"> +</form> +<h3>register</h3> +<form method="POST" action="/register"> + <label for="username">username:</label> + <input type="text" name="username" required> + <br> + <label for="password">password:</label> + <input type="password" name="password" required> + <br> + <input type="submit" value="register"> +</form> +{{ template "tail" . }} +{{ end }}
M files/nav.tmpl.htmlfiles/nav.tmpl.html

@@ -1,16 +1,14 @@

{{ define "nav" }} <nav> <h2><a href="/{{ .Username }}">vore</a></h2> - <hr>| - {{ if .LoggedIn }} - nom nom feeds (๑ᵔ⤙ᵔ๑) - {{ else }} - devouring feeds since 2023 - {{ end }} - <span class=float-right> + <hr> + <span class=left-text> + | {{ .CutePhrase }} + </span> + <span class=right-text> {{ if .LoggedIn }} <a href="/feeds">my feeds</a></h3> | - {{ .Username }}(<a href="/logout">logout</a>) + <a href="/logout">logout</a> {{ else }} <a href="/login">login</a> {{ end }}
M files/style.cssfiles/style.css

@@ -6,26 +6,50 @@ /* make sure the scrollbar is always shown so

the width doesnt change between pages */ overflow-y: scroll; font-size: 120%; + line-height: 1.6; +} + +a { + text-decoration: none; +} + +a:hover { + background-color: #eaddca; +} + +ul a:hover li { + background-color: #eaddca; } h2 { text-align: center; } -.float-right { - float: right; +ul { + list-style-type: none; + padding-left: 20px; + padding-right: 20px; } -nav { - font-weight: bold; +ul li { + padding: 8px 0; + border-bottom: 1px dotted #888; + padding: 0.3rem; + font-size: 1.05rem; } nav a { - text-decoration: underline; + border-bottom: 1px dotted #000; color: #000; } -nav a:hover { - background-color: #000; - color: #fff; +.left-text { + display: inline-block; + overflow-wrap: break-word; + max-width: 80%; +} + +.right-text { + float: right; + display: inline-block; }
M files/user.tmpl.htmlfiles/user.tmpl.html

@@ -2,15 +2,22 @@ {{ define "user" }}

{{ template "head" . }} {{ template "nav" . }} -<!-- <h3> {{ .Data.User }}'s feed</h3> --> {{ $length := len .Data.Items }} {{ if eq $length 0 }} -<p> no feeds found 😭</p> -<p> consider <a href="/feeds">adding some</a></p> +{{ if .LoggedIn }} +<p> -> <a href="/feeds">add your first feed</a></p> +{{ end }} {{ end }} +<ul> {{ range .Data.Items }} -<p> <a href="{{ .Link }}">{{ .Title }}</a> +<a href="{{ .Link }}"> + <li> + <span class=left-text>{{ .Title }}</span> + <span class=right-text><small>{{ .Date.Format "2006-01-02" }}</small></span> + </li> +</a> {{ end }} +</ul> {{ template "tail" . }} {{ end }}
M main.gomain.go

@@ -30,6 +30,6 @@ mux.HandleFunc("/login", s.loginHandler)

mux.HandleFunc("/logout", s.logoutHandler) mux.HandleFunc("/register", s.registerHandler) - fmt.Println("vore: listening on http://localhost:5544") + fmt.Println("main: listening on http://localhost:5544") panic(http.ListenAndServe(":5544", mux)) }
M readmereadme

@@ -1,98 +1,23 @@

-todo + vore + a feed reader + + features: + - minimal & reliable + - handles rss & atom feeds + - updates all feeds periodically + - shows you a date-ordered list of posts + - open source & free of charge forever + - (and not in the shitty open core kind of way) + - jes built it - [ ] GET /discover - (similar to sourcehut's "featured projects") - [ ] GET / - logged out: - describe what vore is & why it's cool, tell ppl to sign up - discover (list of top20 most popular feeds on vore) - logged in: - discover (list of top20 most popular feeds on vore) - [x] GET /login - form (existing account): username & password - form (new account): username & password - built & maintained by jes - [x] POST /login - success: - add session cookie - redirect to /{username} - fail: - log an existing user in, add session cookie, redirect to /{username} - login failure, redirect to /login - [x] POST /logout - delete session cookie if exists - redirect to / - TODO: expire session_token from db eventually - [x] POST /register - success: - add session cookie - redirect to / - fail: - update - add new user & hashed password to database - register failure: redirect to /login - TODO: validate user input + anti-features: + - no tags + - no options + - no javascript + - no unread indicators or push notifs + - no comments, upvotes, or ranks - [ ] GET /feeds - > if no feeds, /discover for ideas - pretty-print your feeds - <text box with pre-populated list of your feed urls, one per line> - button: validate - POST /feeds/validate - logged out: unauthorized. click here to login. - - TODO: POST /feeds/validate - make sure each url is resolvable - check if feed exists in db already - if it does, do nothing - if it doesn't, attempt fetching it - TODO: validate that title exists - redir -> /feeds/validate - logged out: 401 unauthorized - - TODO: GET /feeds/validate - shows feed diff - "if this all looks correct, hit submit:" - button: submit - POST /feeds/submit - logged out: 401 unauthorized - - [ ] POST /feeds/submit - writes desired feeds to database - subscribes user to feeds - redir -> /feeds - logged out: 401 unauthorized - - [ ] GET /{username} - display a users feed items by date, maybe in a table? - -> there is a button to view the feed an article came from - - extra: tool for looking up feed from website - - background: - fetch feeds every ~90m - - headers - unauth'd: login - auth'd: my feeds | login - - sql - user (id, username, password, session_token, created_at) - feed (id, url, fetch_error, created_at, created_by) - subscribe (id, user, feed, created_at, created_by) - - - - - - - - - - - - - - - - + todo + - tool for looking up feeds + - more thorough validation + - handle unfetchable feeds
M site.gosite.go

@@ -3,6 +3,7 @@

import ( "fmt" "io/ioutil" + "math/rand" "net/http" "net/url" "path/filepath"

@@ -57,25 +58,9 @@ return

} if r.Method == "GET" { if s.loggedIn(r) { - fmt.Fprintf(w, "you are already logged in :3\n") + http.Redirect(w, r, "/", http.StatusSeeOther) } else { - fmt.Fprintf(w, `<!DOCTYPE html> - <h3>login</h3> - <form method="POST" action="/login"> - <label for="username">username:</label> - <input type="text" name="username" required><br> - <label for="password">password:</label> - <input type="password" name="password" required><br> - <input type="submit" value="login"> - </form>`) - fmt.Fprintf(w, `<h3>register</h3> - <form method="POST" action="/register"> - <label for="username">username:</label> - <input type="text" name="username" required><br> - <label for="password">password:</label> - <input type="password" name="password" required><br> - <input type="submit" value="register"> - </form>`) + s.renderPage(w, r, "login", nil) } } if r.Method == "POST" {

@@ -84,8 +69,7 @@ password := r.FormValue("password")

err := s.login(w, username, password) if err != nil { - fmt.Fprintf(w, `<!DOCTYPE html> - <p>💀 unauthorized: %s`, err) + s.renderErr(w, err.Error(), http.StatusUnauthorized) return } http.Redirect(w, r, "/", http.StatusSeeOther)

@@ -152,9 +136,6 @@ if s.loggedIn(r) {

feeds = s.reaper.GetUserFeeds(s.username(r)) } s.renderPage(w, r, "feeds", feeds) - - // TODO: textbox with feed.URL - // TODO: validate button } // TODO:

@@ -293,12 +274,14 @@ Title string

Username string LoggedIn bool StyleSheet string + CutePhrase string Data any }{ Title: page + " | " + s.title, Username: s.username(r), LoggedIn: s.loggedIn(r), StyleSheet: string(stylesheet), + CutePhrase: s.randomCutePhrase(), Data: data, }

@@ -346,3 +329,15 @@ s.renderErr(w, r.Method, http.StatusMethodNotAllowed)

} return allowed } + +func (s *Site) randomCutePhrase() string { + phrases := []string{ + "nom nom posts (๑ᵔ⤙ᵔ๑)", + "cthulhu says hi ^(;,;)^", + "( -_•)╦̵̵̿╤─ - - vore", + "devouring feeds since 2023", + "tfw new rss post (⊙ _ ⊙ )", + } + i := rand.Intn(len(phrases)) + return phrases[i] +}