I can now see a user's feed, see the message, and have everything ready for posting and replying.

master
Zed A. Shaw 2 weeks ago
parent 2f6fd88434
commit c5b62bf491
  1. 6
      Makefile
  2. 37
      api/handlers.go
  3. 17
      data/models.go
  4. 13
      migrations/20250827022830_feeds_posts_replies.sql
  5. 5
      static/js/code.js
  6. 3
      static/style.css
  7. 61
      views/feed.html
  8. 15
      views/post/view.html

@ -4,12 +4,12 @@ ifeq '$(OS)' 'Windows_NT'
GO_IS_STUPID_EXE=.exe
endif
all: tailwind site
echo "yay!"
build:
go build .
all: tailwind site
echo "yay!"
site:
go tool ssgod

@ -23,6 +23,40 @@ func GetApiLogout(c *fiber.Ctx) error {
return c.Redirect("/")
}
func GetApiFeed(c *fiber.Ctx) error {
sql, args, err := sq.Select("*").From("message").
Where(sq.Eq{"user_id": c.Params("user_id")}).ToSql()
if err != nil { return IfErrNil(err, c) }
err = data.SelectJson[data.Message](c, err, sql, args...)
return IfErrNil(err, c)
}
func GetApiMessage(c *fiber.Ctx) error {
sql, args, err := sq.Select("*").From("message").
Where(sq.Eq{"id": c.Params("id")}).ToSql()
if err != nil { return IfErrNil(err, c) }
err = data.GetJson[data.Message](c, err, sql, args...)
return IfErrNil(err, c)
}
func GetApiReplies(c *fiber.Ctx) error {
message_id := c.Params("message_id")
sql, args, err := sq.Select("*").From("message").
Where(sq.Eq{"replying_to": message_id}).ToSql()
log.Println("SQL", sql, args);
if err != nil { return IfErrNil(err, c) }
err = data.SelectJson[data.Message](c, err, sql, args...)
return IfErrNil(err, c)
}
func PostApiRegister(c *fiber.Ctx) error {
user, err := ReceivePost[data.User](c)
if err != nil { return IfErrNil(err, c) }
@ -76,6 +110,9 @@ func Setup(app *fiber.App) {
app.Get("/api/logout", GetApiLogout)
app.Post("/api/login", PostApiLogin)
app.Post("/api/register", PostApiRegister)
app.Get("/api/feed/:user_id", GetApiFeed)
app.Get("/api/message/:id", GetApiMessage)
app.Get("/api/replies/:message_id", GetApiReplies)
app.Get("/feed/", Page("feed"))
app.Get("/post/view/:id/", Page("post/view"))

@ -14,8 +14,25 @@ type User struct {
Password string `db:"password" validate:"required,min=8,max=64"`
}
type Message struct {
Id int `db:"id" json:"id" validate:"numeric"`
Text string `db:"text" json:"text" validate:"required,max=512"`
UserId int `db:"user_id" json:"user_id" validate:"numeric"`
CreatedAt string `db:"created_at" json:"created_at"`
Likes int `db:"likes" json:"likes" validate:"numeric"`
Bookmarks int `db:"bookmarks" json:"bookmarks" validate:"numeric"`
ReplyingTo int `db:"replying_to" json:"replying_to" validate:"numeric"`
}
type Bookmark struct {
MessageId int `db:"message_id" json:"message_id" validate:"required,numeric"`
UserId int `db:"user_id" json:"user_id" validate:"required,numeric"`
}
func Models() map[string]reflect.Type {
return map[string]reflect.Type{
"user": reflect.TypeFor[User](),
"message": reflect.TypeFor[Message](),
"bookmark": reflect.TypeFor[Bookmark](),
}
}

@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE message (id INTEGER PRIMARY KEY, text TEXT NOT NULL, user_id INTEGER NOT NULL, created_at DATETIME DEFAULT current_timestamp, likes INTEGER DEFAULT 0, bookmarks INTEGER DEFAULT 0, replying_to INTEGER);
CREATE TABLE bookmark (user_id INTEGER NOT NULL, message_id INTEGER NOT NULL);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE message;
DROP TABLE reply;
DROP TABLE bookmark;
-- +goose StatementEnd

@ -33,12 +33,15 @@ class PaginateTable {
class GetJson {
constructor(url) {
console.assert(url !== undefined, "Invalid url is undefined");
console.log("GetJson url", url);
this.item;
this.url = url;
}
async item() {
const resp = await fetch(`${this.url}`);
const the_url = this.url;
const resp = await fetch(this.url);
console.assert(resp.status == 200, "failed to get it");
this.item = await resp.json();

@ -518,9 +518,6 @@
.p-4 {
padding: calc(var(--spacing) * 4);
}
.\!pt-0 {
padding-top: calc(var(--spacing) * 0) !important;
}
.\!pt-1 {
padding-top: calc(var(--spacing) * 1) !important;
}

@ -27,10 +27,13 @@
</bar>
{{end}}
<script>
let theFeed = new PaginateTable("/api/feed/1");
</script>
<div class="p-1">
<div class="p-1" x-data="theFeed">
<bar class="justify-evenly">
<shape class="tiny rounded-full">Avatar</shape>
<img src="/favicon.ico" />
<b>Notifications</b>
<svg xmlns="http://www.w3.org/2000/svg"
width="2rem"
@ -40,52 +43,18 @@
</svg>
</bar>
<block class="!gap-0 border-t-1 border-gray-600">
<bar class="justify-between !p-0 mb-3">
<img src="/favicon.ico" />
<span>13s</span>
</bar>
<template x-for="message in contents">
<block class="!gap-0 border-t-1 border-gray-600">
<bar class="justify-between !p-0 mb-3">
<img src="/favicon.ico" />
<span>13s</span>
</bar>
<block class="!p-0 gap-2 mb-2">
<p><a href="/post/view/1/">Chillwave bruh cred, succulents skateboard activated charcoal shaman freegan. Hell of four loko succulents, tote bag affogato asymmetrical disrupt selvage. 3 wolf moon post-ironic bodega boys fanny pack big mood godard offal cold-pressed sriracha before they sold out pabst mukbang readymade.</a></p>
</block>
{{template "post-action-bar"}}
</block>
<block class="!gap-0 border-t-1 border-gray-600">
<bar class="justify-between !p-0 mb-3">
<img src="/favicon.ico" />
<span>13s</span>
</bar>
<block class="!p-0 gap-2">
<p>Update the docs to document almost everything.</p>
<pre class="!mb-0"><code>for(i = u in whatever) {
print("whatever")
}</code></pre>
</block>
{{template "post-action-bar"}}
</block>
<block class="!gap-0 border-t-1 border-gray-600">
<bar class="!p-0 mb-3">
<img src="/logo.png" style="width: 50px; height: 50px" class="aspect-square" />
<block class="justify-between !p-0 !mb-0 !gap-1 *:text-gray-500 *:text-sm">
<div>
<span>@ssgod</span>
<span>13s</span>
</div>
<span>Replying to @zedshaw</span>
<block class="!p-0 gap-2 mb-2">
<p><a x-bind:href="`/post/view/${message.id}/`" x-text="message.text"></a></p>
</block>
</bar>
<block class="!p-0 gap-2">
<p><a href="/post/view/1/">Chillwave bruh cred, succulents skateboard activated charcoal shaman freegan. Hell of four loko succulents, tote bag affogato asymmetrical disrupt selvage. 3 wolf moon post-ironic bodega boys fanny pack big mood godard offal cold-pressed sriracha before they sold out pabst mukbang readymade.</a></p>
{{template "post-action-bar"}}
</block>
{{template "post-action-bar"}}
</block>
</template>
</div>

@ -1,4 +1,11 @@
<block class="!gap-0 border-t-1 border-gray-600">
<script>
let theMessage = new GetJson("/api/message/1");
</script>
<block class="!gap-0 border-t-1 border-gray-600"
x-data="{item: {}}"
x-init="item = await theMessage.item()">
<bar class="!p-0 mb-3">
<img src="/logo.png" style="width: 50px; height: 50px" class="aspect-square" />
<block class="justify-between !p-0 !mb-0 !gap-1 *:text-gray-500 *:text-sm">
@ -8,11 +15,13 @@
</bar>
<block class="!p-0 gap-2 mb-2">
<p>Chillwave bruh cred, succulents skateboard activated charcoal shaman freegan. Hell of four loko succulents, tote bag affogato asymmetrical disrupt selvage. 3 wolf moon post-ironic bodega boys fanny pack big mood godard offal cold-pressed sriracha before they sold out pabst mukbang readymade.</p>
<p x-text="item.text"></p>
</block>
<div class="text-sm text-gray-400 mt-2 mb-2">
10:00AM / Aug 21, 2025 / 10k Views
<span x-text="item.created_at"></span>
Likes: <span x-text="item.likes"></span>
Bookmarks: <span x-text="item.bookmarks"></span>
</div>
<bar class="!p-1 mt-2 bg-gray-400 justify-evenly rounded-full">

Loading…
Cancel
Save