From c3b66fc95102b36bf21eacdc960183216683c270 Mon Sep 17 00:00:00 2001
From: Steven Le Rouzic
Date: Sat, 13 Apr 2024 23:48:00 +0200
Subject: Show timer, set time on create
---
model/time.go | 6 ++---
run.bat | 2 ++
static/ui-components.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++
timer.go | 27 ++++++++++++++++----
view/main.templ | 1 +
view/main_templ.go | 2 +-
view/timer.templ | 18 +++++++++++--
view/timer_templ.go | 53 +++++++++++++++++++++++++++++++++-----
view/timers_list.templ | 2 ++
view/timers_list_templ.go | 4 +--
10 files changed, 160 insertions(+), 20 deletions(-)
create mode 100644 run.bat
create mode 100644 static/ui-components.js
diff --git a/model/time.go b/model/time.go
index b7dc3d2..baf3ce6 100644
--- a/model/time.go
+++ b/model/time.go
@@ -12,12 +12,12 @@ func MakeTimeNow() Time {
return Time(time.Now().UTC())
}
-func (self Time) String() string {
- return time.Time(self).String()
+func (self Time) AsUTCString() string {
+ return time.Time(self).Format(time.RFC3339)
}
func (self Time) Value() (sqldriver.Value, error) {
- return time.Time(self).Format(time.RFC3339), nil
+ return self.AsUTCString(), nil
}
func (self *Time) Scan(value any) error {
diff --git a/run.bat b/run.bat
new file mode 100644
index 0000000..1697457
--- /dev/null
+++ b/run.bat
@@ -0,0 +1,2 @@
+templ generate
+go run .
diff --git a/static/ui-components.js b/static/ui-components.js
new file mode 100644
index 0000000..b825f23
--- /dev/null
+++ b/static/ui-components.js
@@ -0,0 +1,65 @@
+class LocalDate extends HTMLElement {
+ connectedCallback() {
+ let utcDate = new Date(this.innerText);
+ this.innerText = utcDate.toLocaleString();
+ }
+}
+customElements.define("local-date", LocalDate);
+
+class TimerCountdown extends HTMLElement {
+ startTime;
+ endTime;
+ refreshInterval = null;
+
+ refreshCountdown() {
+ let diff = Math.floor((this.endTime - this.startTime) / 1000);
+ if (diff <= 0) {
+ this.innerText = "Finished";
+ return;
+ }
+
+ let days = Math.floor(diff / (24 * 60 * 60));
+ diff -= days * 24 * 60 * 60;
+ let hours = Math.floor(diff / (60 * 60));
+ diff -= hours * 60 * 60;
+ let minutes = Math.floor(diff / 60);
+ diff -= minutes * 60;
+ let seconds = diff;
+
+ let newText = "";
+ if (days > 0) {
+ newText += `${days} days, `;
+ }
+ newText += `${hours.toFixed(0).padStart(2, "0")}:`
+ + `${minutes.toFixed(0).padStart(2, "0")}:`
+ + `${seconds.toFixed(0).padStart(2, "0")}`;
+
+ this.innerText = newText;
+ }
+
+ refreshCountdownNow() {
+ this.startTime = (new Date()).getTime();
+ this.refreshCountdown();
+ }
+
+ connectedCallback() {
+ this.endTime = (new Date(this.getAttribute("end"))).getTime();
+ if (this.hasAttribute("start")) {
+ this.startTime = (new Date(this.getAttribute("start"))).getTime();
+ this.refreshCountdown();
+ }
+ else {
+ this.startTime = (new Date()).getTime();
+ this.refreshCountdown();
+ this.refreshInterval = setInterval(this.refreshCountdownNow.bind(this), 1000);
+ }
+ }
+
+ disconnectedCallback() {
+ if (this.refreshInterval !== null) {
+ clearInterval(this.refreshInterval);
+ }
+ }
+}
+customElements.define("timer-countdown", TimerCountdown);
+
diff --git a/timer.go b/timer.go
index d5e0a67..324f846 100644
--- a/timer.go
+++ b/timer.go
@@ -6,6 +6,8 @@ import (
"database/sql"
"context"
"strings"
+ "time"
+ "strconv"
_ "github.com/mattn/go-sqlite3"
@@ -13,11 +15,12 @@ import (
"stevenlr.com/timer/model"
)
-func insertTimer(tx *sql.Tx, name string) error {
+func insertTimer(tx *sql.Tx, name string, seconds int) error {
now := model.MakeTimeNow()
+ end := model.Time(time.Time(now).Add(time.Duration(seconds) * time.Second))
id := model.MakeUUID()
_, err := tx.Exec(`
- INSERT INTO Timer VALUES ($1, $2, $3, $4)`, id, name, now, now);
+ INSERT INTO Timer VALUES ($1, $2, $3, $4)`, id, name, now, end);
return err
}
@@ -37,10 +40,10 @@ func initializeDatabase(db *sql.DB) error {
`)
if err != nil { return err }
- err = insertTimer(tx, "My timer")
+ err = insertTimer(tx, "My timer", 600)
if err != nil { return err }
- err = insertTimer(tx, "My timer2")
+ err = insertTimer(tx, "My timer2", 600)
if err != nil { return err }
return tx.Commit()
@@ -123,6 +126,20 @@ func (server *MyServer) handleDeleteTimer(w http.ResponseWriter, r *http.Request
func (server* MyServer) handlePutTimer(w http.ResponseWriter, r *http.Request) {
timerName := strings.TrimSpace(r.FormValue("timerName"))
+ days, err := strconv.ParseInt(strings.TrimSpace(r.FormValue("days")), 10, 32)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ view.TimerCreateForm(timerName, "Error parsing days").Render(context.Background(), w)
+ return
+ }
+
+ hours, err := strconv.ParseInt(strings.TrimSpace(r.FormValue("hours")), 10, 32)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ view.TimerCreateForm(timerName, "Error parsing hours").Render(context.Background(), w)
+ return
+ }
+
tx, err := server.db.Begin()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
@@ -137,7 +154,7 @@ func (server* MyServer) handlePutTimer(w http.ResponseWriter, r *http.Request) {
return
}
- err = insertTimer(tx, timerName)
+ err = insertTimer(tx, timerName, int(((max(days, 0) * 24) + max(hours, 0)) * 3600))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
view.TimerCreateForm(timerName, "Internal server error").Render(context.Background(), w)
diff --git a/view/main.templ b/view/main.templ
index b6d94d8..64a365c 100644
--- a/view/main.templ
+++ b/view/main.templ
@@ -6,6 +6,7 @@ templ Main(contents templ.Component) {
Cool timer app
+
diff --git a/view/main_templ.go b/view/main_templ.go
index 109be9c..5b3831d 100644
--- a/view/main_templ.go
+++ b/view/main_templ.go
@@ -23,7 +23,7 @@ func Main(contents templ.Component) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Cool timer app")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Cool timer app")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/view/timer.templ b/view/timer.templ
index fa258eb..8d20933 100644
--- a/view/timer.templ
+++ b/view/timer.templ
@@ -7,6 +7,20 @@ import (
templ TimerView(timer model.Timer) {
This is timer { timer.Name }
Back to list
- Start time: { timer.StartTime.String() }
- End time: { timer.EndTime.String() }
+ Start time: { timer.StartTime.AsUTCString() }
+ End time: { timer.EndTime.AsUTCString() }
+
+ Total time:
+
+
+
+ Remaining time:
+
+
}
+
diff --git a/view/timer_templ.go b/view/timer_templ.go
index 7fee7d3..55e5346 100644
--- a/view/timer_templ.go
+++ b/view/timer_templ.go
@@ -40,33 +40,72 @@ func TimerView(timer model.Timer) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Back to list
Start time: ")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Back to list
Start time: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.String())
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.AsUTCString())
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 10, Col: 45}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 10, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
End time: ")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
End time: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.String())
+ templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString())
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 11, Col: 41}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 11, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Total time:\r
Remaining time:\r
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/view/timers_list.templ b/view/timers_list.templ
index 2d71a52..92d7013 100644
--- a/view/timers_list.templ
+++ b/view/timers_list.templ
@@ -25,6 +25,8 @@ templ TimerCreateForm(timerName string, err string) {
>
+
+
if err != "" {
diff --git a/view/timers_list_templ.go b/view/timers_list_templ.go
index af4922d..955ab9a 100644
--- a/view/timers_list_templ.go
+++ b/view/timers_list_templ.go
@@ -100,7 +100,7 @@ func TimerCreateForm(timerName string, err string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" placeholder=\"Name\"> ")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" placeholder=\"Name\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -112,7 +112,7 @@ func TimerCreateForm(timerName string, err string) templ.Component {
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(err)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timers_list.templ`, Line: 31, Col: 34}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timers_list.templ`, Line: 33, Col: 34}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
--
cgit