diff --git a/main.go b/main.go index e69bb90..cfb70b7 100644 --- a/main.go +++ b/main.go @@ -174,6 +174,29 @@ func main() { // {{{ os.Exit(1) } } // }}} +func scheduleHandler() { // {{{ + // Wait for the approximate minute. + wait := 60000 - time.Now().Sub(time.Now().Truncate(time.Minute)).Milliseconds() + logger.Info("schedule", "wait", wait/1000) + time.Sleep(time.Millisecond * time.Duration(wait)) + tick := time.NewTicker(time.Minute) + for { + for _, event := range ExpiredSchedules() { + notificationManager.Send( + event.UserID, + event.ScheduleUUID, + []byte( + fmt.Sprintf( + "%s\n%s", + event.Time.Format("2006-01-02 15:04"), + event.Description, + ), + ), + ) + } + <-tick.C + } +} // }}} func nodeTree(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{ logger.Info("webserver", "request", "/node/tree") @@ -760,20 +783,8 @@ func scheduleList(w http.ResponseWriter, r *http.Request, sess *session.T) { // var err error w.Header().Add("Access-Control-Allow-Origin", "*") - request := struct { - NodeID int - }{} - body, _ := io.ReadAll(r.Body) - if len(body) > 0 { - err = json.Unmarshal(body, &request) - if err != nil { - responseError(w, err) - return - } - } - var schedules []Schedule - schedules, err = FutureSchedules(sess.UserID, request.NodeID) + schedules, err = FutureSchedules(sess.UserID) if err != nil { responseError(w, err) return diff --git a/node.go b/node.go index 66b5a3e..f453392 100644 --- a/node.go +++ b/node.go @@ -326,7 +326,7 @@ func CreateNode(userID, parentID int, name string) (node Node, err error) { // { } // }}} func UpdateNode(userID, nodeID, timeOffset int, content string, cryptoKeyID int, markdown bool) (err error) { // {{{ var timezone string - row := service.Db.Conn.QueryRow(`SELECT timezone FROM _webservice.user WHERE id=$1`, userID) + row := service.Db.Conn.QueryRow(`SELECT timezone FROM public.user WHERE id=$1`, userID) err = row.Scan(&timezone) if err != nil { err = werr.Wrap(err).WithCode("002-000F") diff --git a/request_response.go b/request_response.go index 5eaea5b..c2c6ba5 100644 --- a/request_response.go +++ b/request_response.go @@ -1,9 +1,6 @@ package main import ( - // External - werr "git.gibonuddevalla.se/go/wrappederror" - // Standard "encoding/json" "io" @@ -21,8 +18,6 @@ func responseError(w http.ResponseWriter, err error) { } resJSON, _ := json.Marshal(res) - - werr.Wrap(err).Log() w.Header().Add("Content-Type", "application/json") w.Write(resJSON) } diff --git a/schedule.go b/schedule.go index 823126a..3520283 100644 --- a/schedule.go +++ b/schedule.go @@ -28,32 +28,6 @@ type Schedule struct { Acknowledged bool } -func scheduleHandler() { // {{{ - // Wait for the approximate minute. - wait := 60000 - time.Now().Sub(time.Now().Truncate(time.Minute)).Milliseconds() - logger.Info("schedule", "wait", wait/1000) - time.Sleep(time.Millisecond * time.Duration(wait)) - tick := time.NewTicker(time.Minute) - for { - schedules := ExpiredSchedules() - logger.Info("FOO", "schedules", schedules) - for _, event := range schedules { - notificationManager.Send( - event.UserID, - event.ScheduleUUID, - []byte( - fmt.Sprintf( - "%s\n%s", - event.Time.Format("2006-01-02 15:04"), - event.Description, - ), - ), - ) - } - <-tick.C - } -} // }}} - func ScanForSchedules(timezone string, content string) (schedules []Schedule) { // {{{ schedules = []Schedule{} @@ -115,7 +89,7 @@ func RetrieveSchedules(userID int, nodeID int) (schedules []Schedule, err error) s.description, s.acknowledged FROM schedule s - INNER JOIN _webservice.user u ON s.user_id = u.id + INNER JOIN public.user u ON s.user_id = u.id WHERE user_id=$1 AND CASE @@ -146,7 +120,6 @@ func (a Schedule) IsEqual(b Schedule) bool { // {{{ return a.UserID == b.UserID && a.Node.ID == b.Node.ID && a.Time.Equal(b.Time) && - a.RemindMinutes == b.RemindMinutes && a.Description == b.Description } // }}} func (s *Schedule) Insert(queryable Queryable) error { // {{{ @@ -193,9 +166,9 @@ func ExpiredSchedules() (schedules []Schedule) { // {{{ (s.time - MAKE_INTERVAL(mins => s.remind_minutes)) AT TIME ZONE u.timezone AS time, s.description FROM schedule s - INNER JOIN _webservice.user u ON s.user_id = u.id + INNER JOIN public.user u ON s.user_id = u.id WHERE - (time - MAKE_INTERVAL(mins => remind_minutes)) AT TIME ZONE u.timezone < NOW() AND + (time - MAKE_INTERVAL(mins => remind_minutes)) < NOW() AND NOT acknowledged ORDER BY time ASC @@ -216,7 +189,7 @@ func ExpiredSchedules() (schedules []Schedule) { // {{{ } return } // }}} -func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) {// {{{ +func FutureSchedules(userID int) (schedules []Schedule, err error) {// {{{ schedules = []Schedule{} res := service.Db.Conn.QueryRow(` @@ -224,27 +197,16 @@ func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) { SELECT s.id, s.user_id, - jsonb_build_object( - 'id', n.id, - 'name', n.name, - 'updated', n.updated - ) AS node, + to_jsonb(n.*) AS node, s.schedule_uuid, - time AT TIME ZONE 'UTC' AS time, + (time - MAKE_INTERVAL(mins => s.remind_minutes)) AT TIME ZONE u.timezone AS time, s.description, - s.acknowledged, - s.remind_minutes AS RemindMinutes + s.acknowledged FROM schedule s - INNER JOIN _webservice.user u ON s.user_id = u.id + INNER JOIN public.user u ON s.user_id = u.id INNER JOIN node n ON s.node_id = n.id WHERE - s.user_id = $1 AND - ( - CASE - WHEN $2 > 0 THEN n.id = $2 - ELSE true - END - ) AND + s.user_id=$1 AND time >= NOW() AND NOT acknowledged ) @@ -253,7 +215,6 @@ func FutureSchedules(userID int, nodeID int) (schedules []Schedule, err error) { FROM schedule_events s `, userID, - nodeID, ) var j []byte err = res.Scan(&j) diff --git a/sql/00021.sql b/sql/00021.sql deleted file mode 100644 index 88d7364..0000000 --- a/sql/00021.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE public.node ALTER COLUMN updated TYPE timestamptz USING updated::timestamptz; diff --git a/static/css/main.css b/static/css/main.css index b4a783f..edc0cd1 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -29,17 +29,13 @@ body { height: 100%; } h1 { - font-size: 1.25em; + font-size: 1.5em; color: #518048; - border-bottom: 1px solid #ccc; } h2 { - font-size: 1em; + font-size: 1.25em; color: #518048; } -h3 { - font-size: 1em; -} button { font-size: 1em; padding: 6px; @@ -207,7 +203,6 @@ header .menu { padding: 16px; background-color: #333; color: #ddd; - z-index: 100; } #tree .node { display: grid; @@ -526,7 +521,7 @@ header .menu { grid-area: 1 / 1 / 2 / 2; } /* ============================================================= */ -#schedule-section { +#file-section { grid-area: files; justify-self: center; width: calc(100% - 32px); @@ -536,23 +531,6 @@ header .menu { border-radius: 8px; margin-top: 32px; margin-bottom: 32px; - color: #000; -} -#schedule-section .header { - font-weight: bold; - color: #000; - margin-bottom: 16px; -} -#file-section { - grid-area: schedule; - justify-self: center; - width: calc(100% - 32px); - max-width: 900px; - padding: 32px; - background: #f5f5f5; - border-radius: 8px; - margin-top: 32px; - margin-bottom: 16px; } #file-section .header { font-weight: bold; @@ -651,9 +629,9 @@ header .menu { } .layout-tree { display: grid; - grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree schedule" "tree files" "tree blank"; + grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree files" "tree blank"; grid-template-columns: min-content 1fr; - grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* schedule */ min-content /* files */ 1fr; + grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr; /* blank */ color: #fff; min-height: 100%; @@ -686,9 +664,9 @@ header .menu { display: block; } .layout-crumbs { - grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "schedule" "files" "blank"; + grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank"; grid-template-columns: 1fr; - grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* schedule */ min-content /* files */ 1fr; + grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr; /* blank */ } .layout-crumbs #tree { @@ -735,17 +713,17 @@ header .menu { } #app.node { display: grid; - grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree schedule" "tree files" "tree blank"; + grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree files" "tree blank"; grid-template-columns: min-content 1fr; - grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* schedule */ min-content /* files */ 1fr; + grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr; /* blank */ color: #fff; min-height: 100%; } #app.node.toggle-tree { - grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "schedule" "files" "blank"; + grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank"; grid-template-columns: 1fr; - grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* schedule */ min-content /* files */ 1fr; + grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr; /* blank */ } #app.node.toggle-tree #tree { @@ -767,22 +745,11 @@ header .menu { #profile-settings .passwords div { white-space: nowrap; } -#schedule-events { - display: grid; - grid-template-columns: repeat(5, min-content); - grid-gap: 4px 12px; - margin: 32px; - color: #000; - white-space: nowrap; -} -#schedule-events .header { - font-weight: bold; -} @media only screen and (max-width: 932px) { #app.node { - grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "schedule" "files" "blank"; + grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank"; grid-template-columns: 1fr; - grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* schedule */ min-content /* files */ 1fr; + grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr; /* blank */ } #app.node #tree { diff --git a/static/js/app.mjs b/static/js/app.mjs index ff2e7b8..8a486bf 100644 --- a/static/js/app.mjs +++ b/static/js/app.mjs @@ -113,7 +113,7 @@ class App extends Component { this.websocket.register('open', ()=>console.log('websocket connected')) this.websocket.register('close', ()=>console.log('websocket disconnected')) this.websocket.register('error', msg=>console.log(msg)) - this.websocket.register('message', msg=>this.websocketMessage(msg)) + this.websocket.register('message', this.websocketMessage) this.websocket.start() }//}}} websocketMessage(data) {//{{{ @@ -121,7 +121,7 @@ class App extends Component { switch (msg.Op) { case 'css_reload': - this.websocket.refreshCSS() + refreshCSS() break; } }//}}} diff --git a/static/js/node.mjs b/static/js/node.mjs index be03626..3608ab7 100644 --- a/static/js/node.mjs +++ b/static/js/node.mjs @@ -14,6 +14,7 @@ export class NodeUI extends Component { this.nodeContent = createRef() this.nodeProperties = createRef() this.keys = signal([]) + this.page = signal('node') window.addEventListener('popstate', evt => { if (evt.state && evt.state.hasOwnProperty('nodeID')) @@ -58,7 +59,7 @@ export class NodeUI extends Component { case 'node': if (node.ID == 0) { page = html` -