Static dir configuration and tree view of notes

This commit is contained in:
Magnus Åhall 2023-06-19 15:02:52 +02:00
parent 99a15aa567
commit cb9d95bcb2
6 changed files with 125 additions and 3 deletions

View File

@ -23,6 +23,10 @@ type Config struct {
Password string Password string
} }
Application struct {
StaticDir string
}
Session struct { Session struct {
DaysValid int DaysValid int
} }

31
main.go
View File

@ -59,11 +59,12 @@ func main() {// {{{
connectionManager = NewConnectionManager() connectionManager = NewConnectionManager()
go connectionManager.BroadcastLoop() go connectionManager.BroadcastLoop()
static = http.FileServer(http.Dir("./static")) static = http.FileServer(http.Dir(config.Application.StaticDir))
http.HandleFunc("/css_updated", cssUpdateHandler) http.HandleFunc("/css_updated", cssUpdateHandler)
http.HandleFunc("/session/create", sessionCreate) http.HandleFunc("/session/create", sessionCreate)
http.HandleFunc("/session/retrieve", sessionRetrieve) http.HandleFunc("/session/retrieve", sessionRetrieve)
http.HandleFunc("/session/authenticate", sessionAuthenticate) http.HandleFunc("/session/authenticate", sessionAuthenticate)
http.HandleFunc("/node/tree", nodeTree)
http.HandleFunc("/node/retrieve", nodeRetrieve) http.HandleFunc("/node/retrieve", nodeRetrieve)
http.HandleFunc("/node/create", nodeCreate) http.HandleFunc("/node/create", nodeCreate)
http.HandleFunc("/node/update", nodeUpdate) http.HandleFunc("/node/update", nodeUpdate)
@ -185,6 +186,32 @@ func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{
}) })
}// }}} }// }}}
func nodeTree(w http.ResponseWriter, r *http.Request) {// {{{
var err error
var session Session
if session, _, err = ValidateSession(r, true); err != nil {
responseError(w, err)
return
}
req := struct { StartNodeID int }{}
if err = parseRequest(r, &req); err != nil {
responseError(w, err)
return
}
nodes, err := session.NodeTree(req.StartNodeID)
if err != nil {
responseError(w, err)
return
}
responseData(w, map[string]interface{}{
"OK": true,
"Nodes": nodes,
})
}// }}}
func nodeRetrieve(w http.ResponseWriter, r *http.Request) {// {{{ func nodeRetrieve(w http.ResponseWriter, r *http.Request) {// {{{
log.Println("/node/retrieve") log.Println("/node/retrieve")
var err error var err error
@ -332,7 +359,7 @@ func newTemplate(requestPath string) (tmpl *template.Template, err error) {// {{
if p[len(p)-1] == '/' { if p[len(p)-1] == '/' {
p += "index.html" p += "index.html"
} }
p = "static" + p p = config.Application.StaticDir + p
base := path.Base(p) base := path.Base(p)
if tmpl, err = template.New(base).ParseFiles(p); err != nil { return } if tmpl, err = template.New(base).ParseFiles(p); err != nil { return }

66
node.go
View File

@ -5,7 +5,7 @@ import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
// Standard // Standard
"time"
) )
type Node struct { type Node struct {
@ -14,11 +14,75 @@ type Node struct {
ParentID int `db:"parent_id"` ParentID int `db:"parent_id"`
Name string Name string
Content string Content string
Updated time.Time
Children []Node Children []Node
Crumbs []Node Crumbs []Node
Complete bool Complete bool
Level int
} }
func (session Session) NodeTree(startNodeID int) (nodes []Node, err error) {// {{{
var rows *sqlx.Rows
rows, err = db.Queryx(`
WITH RECURSIVE nodetree AS (
SELECT
*,
array[name::text] AS path,
0 AS level
FROM node
WHERE
user_id = $1 AND
CASE $2::int
WHEN 0 THEN parent_id IS NULL
ELSE parent_id = $2
END
UNION ALL
SELECT
n.*,
path||n.name::text AS path,
nt.level + 1 AS level
FROM node n
INNER JOIN nodetree nt ON n.parent_id = nt.id
)
SELECT
id,
user_id,
COALESCE(parent_id, 0) AS parent_id,
name,
updated,
level
FROM nodetree
ORDER BY
path ASC
`,
session.UserID,
startNodeID,
)
if err != nil {
return
}
defer rows.Close()
type resultRow struct {
Node
Level int
}
nodes = []Node{}
for rows.Next() {
node := Node{}
node.Complete = false
if err = rows.StructScan(&node); err != nil {
return
}
nodes = append(nodes, node)
}
return
}// }}}
func (session Session) RootNode() (node Node, err error) {// {{{ func (session Session) RootNode() (node Node, err error) {// {{{
var rows *sqlx.Rows var rows *sqlx.Rows
rows, err = db.Queryx(` rows, err = db.Queryx(`

View File

@ -158,6 +158,9 @@ header .menu {
border-radius: 8px; border-radius: 8px;
height: 48px; height: 48px;
} }
.tree {
padding: 16px;
}
@media only screen and (max-width: 100ex) { @media only screen and (max-width: 100ex) {
.node-content { .node-content {
width: 100%; width: 100%;

View File

@ -7,6 +7,7 @@ export class NodeUI extends Component {
constructor() {//{{{ constructor() {//{{{
super() super()
this.menu = signal(false) this.menu = signal(false)
this.tree = signal(null)
this.node = signal(null) this.node = signal(null)
this.nodeContent = createRef() this.nodeContent = createRef()
window.addEventListener('popstate', evt=>{ window.addEventListener('popstate', evt=>{
@ -21,6 +22,11 @@ export class NodeUI extends Component {
return return
let node = this.node.value let node = this.node.value
let tree = this.tree.value
let treeHTML = html`Tree`
if(tree !== null)
treeHTML = this.renderTree(tree)
let crumbs = [ let crumbs = [
html`<div class="crumb" onclick=${()=>this.goToNode(0)}>Start</div>` html`<div class="crumb" onclick=${()=>this.goToNode(0)}>Start</div>`
@ -58,6 +64,10 @@ export class NodeUI extends Component {
<div class="crumbs">${crumbs}</crumbs> <div class="crumbs">${crumbs}</crumbs>
${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``} ${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``}
<div class="tree" onclick=${()=>this.retrieveTree()} style="color: #000">
<div class="node">Start</div>
${treeHTML}
</div>
${node.ID > 0 ? html` ${node.ID > 0 ? html`
<div class="node-name">${node.Name}</div> <div class="node-name">${node.Name}</div>
@ -146,6 +156,16 @@ export class NodeUI extends Component {
}) })
.catch(this.props.app.responseError) .catch(this.props.app.responseError)
}//}}} }//}}}
retrieveTree() {//{{{
this.props.app.request('/node/tree', { StartNodeID: this.node.value.ID })
.then(res=>{
this.tree.value = res.Nodes
})
.catch(this.props.app.responseError)
}//}}}
renderTree(tree) {//{{{
return tree.map(node=>html`<div class="node" style="margin-left: ${(node.Level+1) * 32}px">${node.Name}</div>`)
}//}}}
} }
class NodeContent extends Component { class NodeContent extends Component {

View File

@ -188,6 +188,10 @@ header {
} }
} }
.tree {
padding: 16px;
}
@media only screen and (max-width: 100ex) { @media only screen and (max-width: 100ex) {
.node-content { .node-content {
width: 100%; width: 100%;