Show timer, set time on create
This commit is contained in:
@ -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 {
|
||||
|
65
static/ui-components.js
Normal file
65
static/ui-components.js
Normal file
@ -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);
|
||||
|
27
timer.go
27
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)
|
||||
|
@ -6,6 +6,7 @@ templ Main(contents templ.Component) {
|
||||
<head>
|
||||
<title>Cool timer app</title>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
<script type="module" src="/static/ui-components.js"></script>
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<script src="/static/response-targets.js"></script>
|
||||
</head>
|
||||
|
@ -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("<!doctype html><html><head><title>Cool timer app</title><link rel=\"stylesheet\" href=\"/static/style.css\"><script src=\"/static/htmx.min.js\"></script><script src=\"/static/response-targets.js\"></script></head><body hx-boost=\"true\" hx-ext=\"response-targets\">")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html><head><title>Cool timer app</title><link rel=\"stylesheet\" href=\"/static/style.css\"><script type=\"module\" src=\"/static/ui-components.js\"></script><script src=\"/static/htmx.min.js\"></script><script src=\"/static/response-targets.js\"></script></head><body hx-boost=\"true\" hx-ext=\"response-targets\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -7,6 +7,20 @@ import (
|
||||
templ TimerView(timer model.Timer) {
|
||||
<h1>This is timer { timer.Name } </h1>
|
||||
<p><a href="/">Back to list</a></p>
|
||||
<p>Start time: { timer.StartTime.String() }</p>
|
||||
<p>End time: { timer.EndTime.String() }</p>
|
||||
<p>Start time: <local-date>{ timer.StartTime.AsUTCString() }</local-date></p>
|
||||
<p>End time: <local-date>{ timer.EndTime.AsUTCString() }</local-date></p>
|
||||
<p>
|
||||
Total time:
|
||||
<timer-countdown
|
||||
start={ timer.StartTime.AsUTCString() }
|
||||
end={ timer.EndTime.AsUTCString() }
|
||||
></timer-countdown>
|
||||
</p>
|
||||
<p>
|
||||
Remaining time:
|
||||
<timer-countdown
|
||||
end={ timer.EndTime.AsUTCString() }
|
||||
></timer-countdown>
|
||||
</p>
|
||||
}
|
||||
|
||||
|
@ -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("</h1><p><a href=\"/\">Back to list</a></p><p>Start time: ")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><p><a href=\"/\">Back to list</a></p><p>Start time: <local-date>")
|
||||
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("</p><p>End time: ")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</local-date></p><p>End time: <local-date>")
|
||||
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("</p>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</local-date></p><p>Total time:\r <timer-countdown start=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, 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: 15, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" end=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, 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: 16, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></timer-countdown></p><p>Remaining time:\r <timer-countdown end=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, 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: 22, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></timer-countdown></p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ templ TimerCreateForm(timerName string, err string) {
|
||||
>
|
||||
<p>
|
||||
<input type="text" name="timerName" value={ timerName } placeholder="Name" />
|
||||
<input type="number" name="days" placeholder="Days" style="width: 5em;" />
|
||||
<input type="number" name="hours" placeholder="Hours" style="width: 5em;" />
|
||||
<button type="submit">Create</button>
|
||||
</p>
|
||||
if err != "" {
|
||||
|
@ -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\"> <button type=\"submit\">Create</button></p>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" placeholder=\"Name\"> <input type=\"number\" name=\"days\" placeholder=\"Days\" style=\"width: 5em;\"> <input type=\"number\" name=\"hours\" placeholder=\"Hours\" style=\"width: 5em;\"> <button type=\"submit\">Create</button></p>")
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user