summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2023-12-15 19:20:50 +0100
committerPřemysl Eric Janouch <p@janouch.name>2023-12-15 19:20:50 +0100
commit80136f48c9c91ba0bdcd9911a0eaa0aeae565b8a (patch)
tree82ea38801ecbce8b708e9b9d38460396b34c31e1
parentf144006daea838f3095880444d48e2941447e4e7 (diff)
downloadgallery-80136f48c9c91ba0bdcd9911a0eaa0aeae565b8a.tar.gz
gallery-80136f48c9c91ba0bdcd9911a0eaa0aeae565b8a.tar.xz
gallery-80136f48c9c91ba0bdcd9911a0eaa0aeae565b8a.zip
Port the preliminary UI to Mithril.js
-rw-r--r--main.go111
-rw-r--r--public/gallery.js83
2 files changed, 91 insertions, 103 deletions
diff --git a/main.go b/main.go
index d00dcec..b01e687 100644
--- a/main.go
+++ b/main.go
@@ -149,107 +149,16 @@ var page = template.Must(template.New("/").Parse(`<!DOCTYPE html><html><head>
<link rel=stylesheet href=style.css>
</head><body>
<noscript>This is a web application, and requires Javascript.</noscript>
-
- <h1>{{ .Name }}</h1>
- <ul>
- {{ range .Children }}
- <li><a href="?id={{ . }}">{{ . }}</a></li>
- {{ end }}
- </ul>
-
- {{ range .Entries }}
- <a href="/image/{{ .Sha1 }}">
- <img width={{ .Thumbw }} height={{ .Thumbh }} src="/thumb/{{ .Sha1 }}"
- title="{{ .Name }}">
- </a>
- {{ end }}
-
+ <script src=mithril.js></script>
<script src=gallery.js></script>
</body></html>`))
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-// XXX: This is preliminary.
-type entry struct {
- Parent int64
- Name string
- Mtime int64
- Sha1 string
-
- Thumbw int
- Thumbh int
- Dhash int64
-}
-
-// XXX: This is preliminary.
-type directory struct {
- Id int64
- Name string
- Parent int64
- Children []int64
- Entries []entry
-}
-
-func dbCollectDirectory(id int64) (directory, error) {
- d := directory{Id: id}
- dbID := sql.NullInt64{Int64: id, Valid: id != 0}
- if id != 0 {
- err := db.QueryRow(`SELECT name, IFNULL(parent, 0)
- FROM directory WHERE id IS ?`, dbID).Scan(&d.Name, &d.Parent)
- if err != nil {
- return d, err
- }
- }
-
- rows1, err := db.Query(`SELECT id FROM directory WHERE parent IS ?`, dbID)
- if err != nil {
- return d, err
- }
- defer rows1.Close()
- for rows1.Next() {
- var child int64
- if err := rows1.Scan(&child); err != nil {
- return d, err
- }
- d.Children = append(d.Children, child)
- }
- if err := rows1.Err(); err != nil {
- return d, err
- }
-
- rows2, err := db.Query(`SELECT IFNULL(entry.parent, 0),
- entry.name, entry.mtime, entry.sha1,
- IFNULL(image.thumbw, 0), IFNULL(image.thumbh, 0), IFNULL(image.dhash, 0)
- FROM entry JOIN image ON entry.sha1 = image.sha1
- WHERE entry.parent IS ?`, dbID)
- if err != nil {
- return d, err
- }
- defer rows2.Close()
- for rows2.Next() {
- var e entry
- if err := rows2.Scan(&e.Parent, &e.Name, &e.Mtime, &e.Sha1,
- &e.Thumbw, &e.Thumbh, &e.Dhash); err != nil {
- return d, err
- }
- d.Entries = append(d.Entries, e)
- }
- return d, rows2.Err()
-}
-
func handleRequest(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
staticHandler.ServeHTTP(w, r)
return
}
-
- id, _ := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
- d, err := dbCollectDirectory(id)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if err := page.Execute(w, d); err != nil {
+ if err := page.Execute(w, nil); err != nil {
log.Println(err)
}
}
@@ -274,13 +183,15 @@ func handleThumbs(w http.ResponseWriter, r *http.Request) {
func getSubdirectories(tx *sql.Tx, parent int64) (names []string, err error) {
// TODO: This is like dbCollectStrings(), just needs an argument.
- rows, err := tx.Query(`SELECT name FROM directory WHERE parent = ?`,
- parent)
+ // TODO: Should this return full paths, or not? Clean it up.
+ rows, err := tx.Query(
+ `SELECT name FROM directory WHERE IFNULL(parent, 0) = ?`, parent)
if err != nil {
return nil, err
}
defer rows.Close()
+ names = []string{}
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
@@ -309,6 +220,7 @@ func getSubentries(tx *sql.Tx, parent int64) (entries []webEntry, err error) {
}
defer rows.Close()
+ entries = []webEntry{}
for rows.Next() {
var e webEntry
if err := rows.Scan(
@@ -362,6 +274,8 @@ func handleAPIBrowse(w http.ResponseWriter, r *http.Request) {
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
func getImagePaths(sha1 string) (paths []string, err error) {
rows, err := db.Query(`WITH RECURSIVE paths(parent, path) AS (
SELECT parent, name AS path FROM entry WHERE sha1 = ?
@@ -446,6 +360,8 @@ func handleAPIInfo(w http.ResponseWriter, r *http.Request) {
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
// cmdRun runs a web UI against GD on ADDRESS.
func cmdRun(args []string) error {
if len(args) != 2 {
@@ -514,10 +430,13 @@ func idForPath(tx *sql.Tx, path []string, create bool) (int64, error) {
}
func decodeWebPath(path string) []string {
+ // Trailing slashes don't provide information.
+ path = strings.TrimSuffix(path, "/")
+
// Relative paths could be handled differently,
// but right now, they're assumed to start at the root.
list := strings.Split(path, "/")
- if len(list) > 1 && list[0] == "" {
+ if len(list) > 0 && list[0] == "" {
list = list[1:]
}
return list
diff --git a/public/gallery.js b/public/gallery.js
index 1935220..d25c544 100644
--- a/public/gallery.js
+++ b/public/gallery.js
@@ -3,27 +3,96 @@
function call(method, params) {
return m.request({
method: "POST",
- url: `/api/{method}`,
+ url: `/api/${method}`,
body: params,
})
}
+let BrowseModel = {
+ path: undefined,
+ subdirectories: [],
+ entries: [],
+
+ async reload(path) {
+ this.path = path
+ this.subdirectories = []
+ this.entries = []
+
+ let resp = await call('browse', {path: path})
+ this.subdirectories = resp.subdirectories
+ this.entries = resp.entries
+ },
+}
+
let Browse = {
- view: vnode => {
- return m('')
+ // Reload the model immediately, to improve responsibility.
+ // But we don't need to: https://mithril.js.org/route.html#preloading-data
+ // Also see: https://mithril.js.org/route.html#route-cancellation--blocking
+ oninit(vnode) {
+ let path = vnode.attrs.key || "/"
+ BrowseModel.reload(path)
+ },
+
+ view(vnode) {
+ return m('.container', {}, [
+ m('.header', {}, "Browse"),
+ m('h1', "Root"),
+ m('ul', BrowseModel.subdirectories.map(sd => {
+ const name = sd.split('/').pop()
+ return m('li', m(m.route.Link, {
+ href: `/browse/:key`,
+ params: {key: `${BrowseModel.path}/${sd}`},
+ }, name))
+ })),
+ m('.browser', {}, BrowseModel.entries.map(e => {
+ return m(m.route.Link, {href: `/view/${e.sha1}`},
+ m('img', {src: `/thumb/${e.sha1}`,
+ width: e.thumbW, height: e.thumbH, title: e.name}))
+ })),
+ ])
+ },
+}
+
+let ViewModel = {
+ sha1: undefined,
+ paths: [],
+ tags: {},
+
+ async reload(sha1) {
+ this.sha1 = sha1
+ this.paths = []
+ this.tags = {}
+
+ let resp = await call('info', {sha1: sha1})
+ this.paths = resp.paths
+ this.tags = resp.tags
},
}
let View = {
- view: vnode => {
- return m('')
+ oninit(vnode) {
+ let sha1 = vnode.attrs.key || ""
+ ViewModel.reload(sha1)
+ },
+
+ view(vnode) {
+ // TODO: Show more information.
+ return m('.container', {}, [
+ m('.header', {}, "View"),
+ m('ul', ViewModel.paths.map(sd => m('li', sd))),
+ ViewModel.sha1 !== undefined
+ ? m('img', {src: `/image/${ViewModel.sha1}`})
+ : "No image.",
+ ])
},
}
window.addEventListener('load', () => {
m.route(document.body, "/browse/", {
- "/browse/:path": Browse,
- "/view/:sha1": View,
+ // The path doesn't need to be escaped, perhaps change that (":key...").
+ "/browse/": Browse,
+ "/browse/:key": Browse,
+ "/view/:key": View,
"/similar/:sha1": undefined,
"/tags": undefined,