From 552c9f527a778443a593d11e5ce991a8513a18c1 Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch Date: Mon, 25 Dec 2023 07:52:46 +0100 Subject: Tag search --- public/gallery.js | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) (limited to 'public/gallery.js') diff --git a/public/gallery.js b/public/gallery.js index 52a92ab..3c64bac 100644 --- a/public/gallery.js +++ b/public/gallery.js @@ -43,6 +43,18 @@ let Header = { }, ], + search: [ + { + route: '/search', + render: () => m(m.route.Link, { + href: `/search/:key`, + params: {key: m.route.param('key')}, + class: m.route.get().startsWith('/search') + ? 'active' : undefined, + }, "Search"), + }, + ], + view(vnode) { const route = m.route.get() const main = this.global.map(x => @@ -54,6 +66,8 @@ let Header = { let context if (this.image.some(x => route.startsWith(x.route))) context = this.image.map(x => x.render()) + if (this.search.some(x => route.startsWith(x.route))) + context = this.search.map(x => x.render()) return m('.header', {}, [ m('nav', main), @@ -90,7 +104,7 @@ let BrowseModel = { this.entries = [] } - let resp = await call('browse', {path: path}) + let resp = await call('browse', {path}) this.subdirectories = resp.subdirectories this.entries = resp.entries.sort((a, b) => this.collator.compare(a.name, b.name)) @@ -175,8 +189,10 @@ let Browse = { return m('.container', {}, [ m(Header), m('.body', {}, [ - m('ul.sidebar', BrowseModel.getBrowseLinks().map(link => - m(BrowseBarLink, {link}))), + m('.sidebar', [ + m('ul.path', BrowseModel.getBrowseLinks() + .map(link => m(BrowseBarLink, {link}))), + ]), m(BrowseView), ]), ]) @@ -428,9 +444,14 @@ let ViewBar = { m("ul.tags", Object.entries(tags) .sort(([t1, w1], [t2, w2]) => (w2 - w1)) .map(([tag, weight]) => m("li", [ + // XXX: Duplicated with SearchRelated. m("meter[max=1.0]", {value: weight, title: weight}, weight), - ` ${tag}`, + ` `, + m(m.route.Link, { + href: `/search/:key`, + params: {key: `${group}:${tag}`}, + }, ` ${tag}`), ]))), ]), ]) @@ -541,6 +562,84 @@ let Similar = { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +let SearchModel = { + query: undefined, + matches: [], + related: {}, + + async reload(query) { + if (this.query !== query) { + this.query = query + this.matches = [] + this.related = {} + } + + let resp = await call('search', {query}) + this.matches = resp.matches + this.related = resp.related + }, +} + +let SearchRelated = { + view(vnode) { + return Object.entries(SearchModel.related) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([space, tags]) => [ + m('h2', space), + m('ul.tags', tags + .sort((a, b) => (b.score - a.score)) + .map(({tag, score}) => m('li', [ + // XXX: Duplicated with ViewBar. + m("meter[max=1.0]", + {value: score, title: score}, score), + ` `, + m(m.route.Link, { + href: `/search/:key`, + params: {key: `${space}:${tag}`}, + }, ` ${tag}`), + ]))), + ]) + }, +} + +let SearchView = { + // See BrowseView. + oncreate(vnode) { vnode.dom.focus() }, + + view(vnode) { + return m('.browser[tabindex=0]', { + // Trying to force the oncreate on path changes. + key: SearchModel.path, + }, SearchModel.matches + .sort((a, b) => b.score - a.score) + .map(info => { + return m(m.route.Link, {href: `/view/${info.sha1}`}, + m(Thumbnail, {info, title: info.score})) + })) + }, +} + +let Search = { + oninit(vnode) { + SearchModel.reload(vnode.attrs.key) + }, + + view(vnode) { + return m('.container', {}, [ + m(Header), + m('.body', {}, [ + m('.sidebar', [ + m('p', SearchModel.query), + m(SearchRelated), + ]), + m(SearchView), + ]), + ]) + }, +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + window.addEventListener('load', () => { m.route(document.body, "/browse/", { // The path doesn't need to be escaped, perhaps change that (":key..."). @@ -554,6 +653,6 @@ window.addEventListener('load', () => { "/view/:key": View, "/similar/:key": Similar, - "/search/:key": undefined, + "/search/:key": Search, }) }) -- cgit v1.2.3-70-g09d2