package view import ( _ "embed" "fmt" "goreader/gallery" "goreader/state" "strconv" "github.com/silva-guimaraes/gtag" ) //go:embed static/styles.css var styles string //go:embed static/reader.js var readerJavascript string //go:embed static/index.js var indexJavascript string //go:embed static/hotkeys.min.js var hotkeys string //go:embed static/htmx.min.js var htmx string func tag(t gallery.Tag, state *state.State) *gtag.Tag { gender := "X" backgroundClass := "any" if t.Sex == gallery.Male { gender = "M" backgroundClass = "male" } if t.Sex == gallery.Female { gender = "F" backgroundClass = "female" } d := gtag.New("a"). Href("javascript:void(0)"). Class("tag"). SetAttr("hx-post", fmt.Sprintf("/filter/%s/%s", t.Name, t.Sex)). SetAttr("hx-target", "#listing"). SetAttr("hx-swap", "outerHTML") { d.Tag("span").Text(gender).Class("name", backgroundClass) d.Tag("span").Text(t.Name).Class("name") d.Tag("span").Text(strconv.Itoa(state.UniqueTags[t])).Class("name", backgroundClass) } return d } func thumbnail(src string) *gtag.Tag { return gtag.NewVoid("img").SetAttr("src", src).SetAttr("loading", "lazy") } func fullThumbnail(g gallery.Gallery) *gtag.Tag { return thumbnail(fmt.Sprintf("/page/%s/1", g.Uuid())) } func smallThumbnail(g gallery.Gallery) *gtag.Tag { return thumbnail(fmt.Sprintf("/thumb/%s", g.Uuid())).Class("thumbnail") } func InspectorInfo(g gallery.Gallery, state *state.State) *gtag.Tag { d := gtag.Div().Class("float") { // d.Append(smallThumbnail(g)) d.Append(fullThumbnail(g)) // d.Tag("div").Id("inspector-image-placeholder") d.Tag("a"). SetAttr("href", fmt.Sprintf("/read/%s", g.Uuid())). SetAttr("hx-boost", "false"). SetAttr("style", "color: white"). Tag("h1").Text(g.Name()) d.Tag("h2").Text(g.JpName()) tags := d.Div() { for _, t := range g.Tags() { tags.Append(tag(t, state)) } } } return d } func cover(g gallery.Gallery) *gtag.Tag { a := gtag.New("a"). Class("cover"). Id(fmt.Sprintf("cover-%s", g.Uuid())). SetAttr("href", fmt.Sprintf("/read/%s", g.Uuid())). SetAttr("hx-get", fmt.Sprintf("/details/%s", g.Uuid())). SetAttr("onmouseenter", "inspectSetTimeout(event)"). SetAttr("onmouseleave", "inspectClearTimeout(event)"). SetAttr("hx-trigger", "inspect"). SetAttr("hx-target", "#inspector"). SetAttr("hx-boost", "false") a.Append(smallThumbnail(g)) return a } const galleriesPerPage = 50 func SearchResults(galleries []gallery.Gallery, page int) *gtag.Tag { glm := max(len(galleries)-1, 0) recent := galleries[min(page*galleriesPerPage, glm):min((page+1)*galleriesPerPage, glm)] m := gtag.New("main") // HTMX faz com que isso receba [InspectorInfo] quando usuário paira o // mouse sobre algum cover m.Tag("section").Id("inspector") results := m.Tag("section").Id("results") { for _, g := range recent { results.Append(cover(g)) } } return m } func Stats(state *state.State) *gtag.Tag { s := gtag.New("div").Id("stats") { s.Tag("p").Text(fmt.Sprintf("Galleries loaded: %d", len(state.Galleries))) s.Tag("p").Text(fmt.Sprintf("Unique tags: %d", len(state.UniqueTags))) s.Tag("p").Text(fmt.Sprintf("Unique artists: %d", len(state.UniqueArtists))) s.Tag("p").Text(fmt.Sprintf("Unique groups: %d", len(state.UniqueGroups))) s.Tag("p").Text(fmt.Sprintf("Unique parodies: %d", len(state.UniqueParodies))) } if !state.Done { s.SetAttr("hx-get", "/stats").SetAttr("hx-trigger", "every 0.01s").SetAttr("hx-swap", "outerHTML") } return s } func Index(state *state.State, page int) *gtag.Tag { html := gtag.Doc() { head := html.Head() { head.Tag("title").Text("index") head.Tag("style").Asis(styles) head.Asis( ``, ) head.Asis("") head.Tag("script").Asis(hotkeys) head.Asis("") head.Tag("script").Asis(htmx) } body := html.Body().Id("index").SetAttr("hx-boost", "true") { nav := body.Tag("nav").Class("container") { nav.Div().Text("omakase v1") } center := body.Div().Id("center") { top := center.Tag("div").Class("top") { top.Div().Id("search-bar").VoidTag("input"). Class("ask-input"). SetAttr("type", "search"). SetAttr("name", "search"). SetAttr("autocomplete", "false"). SetAttr("placeholder", "Slash to search..."). SetAttr("hx-post", "/search"). SetAttr("hx-trigger", "input changed delay:50ms, search"). SetAttr("hx-target", "#search-results") header := top.Tag("header").Class("container") { header.Div().Id("omakase"). Tag("a"). SetAttr("href", "/random"). Text("おまかせ"). SetAttr("target", "_blank") header.Tag("hr").Style("opacity: 0.2;") header.Append(Stats(state)) } } ret := center.Tag("main").Id("search-results") { controls := ret.Tag("section").AddClass("container").Id("controls") { paging := controls.Div() { maxPages := len(state.Galleries) / galleriesPerPage previousPage := gtag.New("a"). AddClass("page-control"). Text("<") if page > 1 { previousPage.SetAttr("href", fmt.Sprintf("?page=%d", page-1), ) } paging.Append(previousPage) p := page for ; p < min(page+3, maxPages); p++ { paging.Tag("a"). AddClass("page-control"). SetAttr("href", fmt.Sprintf("?page=%d", p), ).Text(fmt.Sprint(p)) } if len(state.Galleries) > 0 && p != maxPages { paging.Tag("span").Text("...") } paging.Tag("a"). AddClass("page-control"). SetAttr("href", fmt.Sprintf("?page=%d", maxPages), ).Text(fmt.Sprint(maxPages)) nextPage := gtag.New("a"). AddClass("page-control"). SetAttr("href", "?page=1"). Text(">") if page < maxPages { nextPage.SetAttr("href", fmt.Sprintf("?page=%d", page+1), ) } else if page == maxPages { nextPage.SetAttr("disabled", "true") } paging.Append(nextPage) } } ret.Append(SearchResults(state.Galleries, page)) } } body.Tag("footer").P().Text(randomQuote()) body.Tag("script").Asis(indexJavascript) } } return html } // content := body.Div().Id("content").Class("container"); { // details := content.Tag("details"); { // details.Tag("summary").Text("filter tags...") // tags := details.Div().Id("tags"); { // tags.Tag("a").Href("javascript:void(0)").Text("clear tags"). // Class("name", "tag").Style("display: block;"). // SetAttr("hx-post", "/filter/clear/all"). // SetAttr("hx-target", "#listing"). // SetAttr("hx-swap", "outerHTML") // tags.Text("Tags:") // tagsSpan := tags.Tag("span").SetAttr("hx-boost", "true"); { // for k, v := range uniqueTags { // tagsSpan.Append(tag(k, v)) // } // } // } // } // content.Append(galleriesListing(galleries)) // }