diff --git a/_test-site/posts/2026/02/24-it-may-have.md b/_test-site/posts/2026/02/24-it-may-have.md
new file mode 100644
index 0000000..a5cb835
--- /dev/null
+++ b/_test-site/posts/2026/02/24-it-may-have.md
@@ -0,0 +1,10 @@
+---
+id: Ed6vIR86gspx
+title: ""
+date: 2026-02-24T10:55:39Z
+tags: []
+slug: /2026/02/24/it-may-have
+---
+It may have been an issue with the call to air? Hmm. Will need to make sure to check that is working correctly.
+
+I am now updating this. I am also updating this too.
\ No newline at end of file
diff --git a/_test-site/posts/2026/02/24-it-will-even.md b/_test-site/posts/2026/02/24-it-will-even.md
new file mode 100644
index 0000000..6abd989
--- /dev/null
+++ b/_test-site/posts/2026/02/24-it-will-even.md
@@ -0,0 +1,8 @@
+---
+id: bzAVD55SB2LE
+title: ""
+date: 2026-02-24T11:20:23Z
+tags: []
+slug: /2026/02/24/it-will-even
+---
+It will even do it for new posts.
\ No newline at end of file
diff --git a/_test-site/posts/2026/02/24-this-is-a.md b/_test-site/posts/2026/02/24-this-is-a.md
new file mode 100644
index 0000000..6497e96
--- /dev/null
+++ b/_test-site/posts/2026/02/24-this-is-a.md
@@ -0,0 +1,12 @@
+---
+id: MFHzBhJwJCQ3
+title: ""
+date: 2026-02-24T11:20:11Z
+tags: []
+slug: /2026/02/24/this-is-a
+---
+This is a new post, and will be saved as a draft.
+
+It's still a draft. But the minute I publish it, it will be updated as an updated post. You see? I'm still writing in this.
+
+But the minute I press enter, it will always publish.
\ No newline at end of file
diff --git a/_test-site/posts/2026/02/24-this-was-a.md b/_test-site/posts/2026/02/24-this-was-a.md
new file mode 100644
index 0000000..00439fe
--- /dev/null
+++ b/_test-site/posts/2026/02/24-this-was-a.md
@@ -0,0 +1,10 @@
+---
+id: -sU1lmmL7i56
+title: ""
+date: 2026-02-24T11:20:44Z
+tags: []
+slug: /2026/02/24/this-was-a
+---
+This was a draft.
+
+But now it's a published post.
\ No newline at end of file
diff --git a/assets/js/controllers/postedit.js b/assets/js/controllers/postedit.js
new file mode 100644
index 0000000..dfb3b55
--- /dev/null
+++ b/assets/js/controllers/postedit.js
@@ -0,0 +1,79 @@
+import { Controller } from "@hotwired/stimulus"
+import { showToast } from "../services/toast";
+
+export default class PosteditController extends Controller {
+ static values = {
+ saveAction: String,
+ };
+
+ connect() {
+ console.log("connected");
+ }
+
+ async save(ev) {
+ ev.preventDefault();
+
+ try {
+ await this._postForm(this.saveActionValue);
+
+ showToast({
+ title: "💾 Post Saved",
+ body: (this.saveActionValue === "Save Draft") ? "Post saved as draft." : "Post updated.",
+ });
+ } catch (e) {
+ console.error(e);
+ showToast({
+ title: "❌ Error",
+ body: "Unable to save post. Please try again later.",
+ });
+ }
+ }
+
+ async publish(ev) {
+ ev.preventDefault();
+
+ try {
+ await this._postForm("Publish");
+
+ window.location.href = this.element.getAttribute("action");
+ } catch (e) {
+ console.error(e);
+ showToast({
+ title: "❌ Error",
+ body: "Unable to publish post. Please try again later.",
+ });
+ }
+ }
+
+ async _postForm(action) {
+ if (this._isPosting) {
+ return;
+ }
+ this._isPosting = true;
+
+ try {
+ const formData = new FormData(this.element);
+ let data = Object.fromEntries(formData.entries());
+ data = {...data, action: action || 'save'};
+
+ const response = await fetch(this.element.getAttribute("action"), {
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return response.json();
+ } finally {
+ this._isPosting = false;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/assets/js/controllers/postlist.js b/assets/js/controllers/postlist.js
index 40e3d44..82a319f 100644
--- a/assets/js/controllers/postlist.js
+++ b/assets/js/controllers/postlist.js
@@ -1,5 +1,4 @@
import { Controller } from "@hotwired/stimulus"
-
import { showToast } from "../services/toast";
export default class PostlistController extends Controller {
diff --git a/assets/js/main.js b/assets/js/main.js
index b831564..a9bcc2a 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -2,7 +2,9 @@ import { Application } from "@hotwired/stimulus";
import ToastController from "./controllers/toast";
import PostlistController from "./controllers/postlist";
+import PosteditController from "./controllers/postedit";
window.Stimulus = Application.start()
Stimulus.register("toast", ToastController);
Stimulus.register("postlist", PostlistController);
+Stimulus.register("postedit", PosteditController);
diff --git a/handlers/middleware/user.go b/handlers/middleware/user.go
index d36891d..94c0a83 100644
--- a/handlers/middleware/user.go
+++ b/handlers/middleware/user.go
@@ -13,6 +13,7 @@ func AuthUser() func(c fiber.Ctx) error {
user := models.User{
ID: 1,
Username: "testuser",
+ TimeZone: "Australia/Melbourne",
}
c.Locals("user", user)
diff --git a/handlers/posts.go b/handlers/posts.go
index ff9f609..041ec9d 100644
--- a/handlers/posts.go
+++ b/handlers/posts.go
@@ -39,7 +39,8 @@ func (ph PostsHandler) Index(c fiber.Ctx) error {
func (ph PostsHandler) New(c fiber.Ctx) error {
p := models.Post{
- GUID: models.NewNanoID(),
+ GUID: models.NewNanoID(),
+ State: models.StateDraft,
}
return c.Render("posts/edit", fiber.Map{
@@ -77,7 +78,7 @@ func (ph PostsHandler) Update(c fiber.Ctx) error {
return err
}
- post, err := ph.PostService.PublishPost(c.Context(), req)
+ post, err := ph.PostService.UpdatePost(c.Context(), req)
if err != nil {
return err
}
diff --git a/models/users.go b/models/users.go
index 13c6452..2a9da1d 100644
--- a/models/users.go
+++ b/models/users.go
@@ -1,7 +1,31 @@
package models
+import "time"
+
type User struct {
ID int64
Username string
PasswordHashed []byte
+ TimeZone string
+}
+
+func (u User) FormatTime(t time.Time) string {
+ if loc := getLocation(u.TimeZone); loc != nil {
+ return t.In(loc).Format("2006-01-02 15:04:05")
+ }
+ return t.Format("2006-01-02 15:04:05")
+}
+
+var loadedLocation = map[string]*time.Location{}
+
+func getLocation(tz string) *time.Location {
+ if loc, ok := loadedLocation[tz]; ok {
+ return loc
+ }
+ loc, err := time.LoadLocation(tz)
+ if err != nil {
+ loc = time.Local
+ }
+ loadedLocation[tz] = loc
+ return loc
}
diff --git a/services/posts/create.go b/services/posts/create.go
index 6d4cd94..1dc69a1 100644
--- a/services/posts/create.go
+++ b/services/posts/create.go
@@ -2,6 +2,7 @@ package posts
import (
"context"
+ "strings"
"time"
"lmika.dev/lmika/weiro/models"
@@ -9,12 +10,13 @@ import (
)
type CreatePostParams struct {
- GUID string `form:"guid" json:"guid"`
- Title string `form:"title" json:"title"`
- Body string `form:"body" json:"body"`
+ GUID string `form:"guid" json:"guid"`
+ Title string `form:"title" json:"title"`
+ Body string `form:"body" json:"body"`
+ Action string `form:"action" json:"action"`
}
-func (s *Service) PublishPost(ctx context.Context, params CreatePostParams) (*models.Post, error) {
+func (s *Service) UpdatePost(ctx context.Context, params CreatePostParams) (*models.Post, error) {
site, ok := models.GetSite(ctx)
if !ok {
return nil, models.SiteRequiredError
@@ -27,14 +29,28 @@ func (s *Service) PublishPost(ctx context.Context, params CreatePostParams) (*mo
post.Title = params.Title
post.Body = params.Body
- post.PublishedAt = time.Now()
+ post.UpdatedAt = time.Now()
post.Slug = post.BestSlug()
+ oldState := post.State
+
+ switch strings.ToLower(params.Action) {
+ case "publish":
+ post.State = models.StatePublished
+ post.PublishedAt = time.Now()
+ case "save draft":
+ post.State = models.StateDraft
+ post.PublishedAt = time.Time{}
+ default:
+ // Leave unchanged
+ }
if err := s.db.SavePost(ctx, post); err != nil {
return nil, err
}
- s.publisher.Queue(site)
+ if oldState != post.State || post.State == models.StatePublished {
+ s.publisher.Queue(site)
+ }
return post, nil
}
@@ -56,6 +72,7 @@ func (s *Service) fetchOrCreatePost(ctx context.Context, site models.Site, param
GUID: params.GUID,
Title: params.Title,
Body: params.Body,
+ State: models.StateDraft,
CreatedAt: time.Now(),
}
return post, nil
diff --git a/views/posts/edit.html b/views/posts/edit.html
index 6e1f10e..871f691 100644
--- a/views/posts/edit.html
+++ b/views/posts/edit.html
@@ -1,5 +1,9 @@
+{{ $isPublished := ne .post.State 1 }}