Added functioning search
This commit is contained in:
parent
1598ebd93b
commit
65d43d2b43
1
main.go
1
main.go
@ -58,6 +58,7 @@ func main() {
|
|||||||
app.Ajax("/user/:nick/animelist/:id", animelistitem.Get)
|
app.Ajax("/user/:nick/animelist/:id", animelistitem.Get)
|
||||||
app.Ajax("/settings", settings.Get)
|
app.Ajax("/settings", settings.Get)
|
||||||
app.Ajax("/admin", admin.Get)
|
app.Ajax("/admin", admin.Get)
|
||||||
|
app.Ajax("/search/:term", search.Get)
|
||||||
app.Ajax("/users", users.Get)
|
app.Ajax("/users", users.Get)
|
||||||
app.Ajax("/airing", airing.Get)
|
app.Ajax("/airing", airing.Get)
|
||||||
app.Ajax("/webdev", webdev.Get)
|
app.Ajax("/webdev", webdev.Get)
|
||||||
|
@ -32,7 +32,7 @@ component LoggedInMenu(user *arn.User)
|
|||||||
NavigationButtonNoAJAX("Logout", "/logout", "sign-out")
|
NavigationButtonNoAJAX("Logout", "/logout", "sign-out")
|
||||||
|
|
||||||
component FuzzySearch
|
component FuzzySearch
|
||||||
input#search(type="text", placeholder="Search...", title="Shortcut: Ctrl + Q")
|
input#search.action(data-action="search", data-trigger="input", type="text", placeholder="Search...", title="Shortcut: Ctrl + Q")
|
||||||
|
|
||||||
component NavigationButton(name string, target string, icon string)
|
component NavigationButton(name string, target string, icon string)
|
||||||
a.navigation-link.ajax(href=target, aria-label=name)
|
a.navigation-link.ajax(href=target, aria-label=name)
|
||||||
|
@ -26,7 +26,7 @@ component Anime(anime *arn.Anime, user *arn.User)
|
|||||||
Icon("pencil")
|
Icon("pencil")
|
||||||
span Edit in collection
|
span Edit in collection
|
||||||
else
|
else
|
||||||
button.action(data-action="addAnimeToCollection", data-anime-id=anime.ID, data-user-id=user.ID, data-user-nick=user.Nick)
|
button.action(data-action="addAnimeToCollection", data-trigger="click", data-anime-id=anime.ID, data-user-id=user.ID, data-user-nick=user.Nick)
|
||||||
Icon("plus")
|
Icon("plus")
|
||||||
span Add to collection
|
span Add to collection
|
||||||
|
|
||||||
|
@ -17,6 +17,6 @@ component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.
|
|||||||
a.ajax.button(href=anime.Link())
|
a.ajax.button(href=anime.Link())
|
||||||
Icon("search-plus")
|
Icon("search-plus")
|
||||||
span View anime
|
span View anime
|
||||||
button.action(data-action="removeAnimeFromCollection", data-anime-id=anime.ID, data-user-id=viewUser.ID, data-user-nick=viewUser.Nick)
|
button.action(data-action="removeAnimeFromCollection", data-trigger="click", data-anime-id=anime.ID, data-user-id=viewUser.ID, data-user-nick=viewUser.Nick)
|
||||||
Icon("trash")
|
Icon("trash")
|
||||||
span Remove from collection
|
span Remove from collection
|
29
pages/popular-anime/popular.go
Normal file
29
pages/popular-anime/popular.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package popularanime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerogo/aero"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get search page.
|
||||||
|
func Get(ctx *aero.Context) string {
|
||||||
|
// titleCount := 0
|
||||||
|
// animeCount := 0
|
||||||
|
|
||||||
|
// // let info: any = await bluebird.props({
|
||||||
|
// // popular: arn.db.get('Cache', 'popularAnime'),
|
||||||
|
// // stats: arn.db.get('Cache', 'animeStats')
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// // return response.render({
|
||||||
|
// // user,
|
||||||
|
// // popularAnime: info.popular.anime,
|
||||||
|
// // animeCount: info.stats.animeCount,
|
||||||
|
// // titleCount: info.stats.titleCount,
|
||||||
|
// // anime: null
|
||||||
|
// // })
|
||||||
|
|
||||||
|
// popular, _ := arn.GetPopularCache()
|
||||||
|
|
||||||
|
// return ctx.HTML(components.Search(popular.Anime, titleCount, animeCount))
|
||||||
|
return ctx.HTML("Coming soon.")
|
||||||
|
}
|
16
pages/popular-anime/popular.pixy
Normal file
16
pages/popular-anime/popular.pixy
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
component Search(popularAnime []*arn.Anime, titleCount int, animeCount int)
|
||||||
|
h2 Anime
|
||||||
|
|
||||||
|
#search-container
|
||||||
|
input#search(type="text", placeholder="Search...", onkeyup="$.searchAnime();", onfocus="this.select();", disabled="disabled", data-count=titleCount, data-anime-count=animeCount)
|
||||||
|
|
||||||
|
#search-results-container
|
||||||
|
#search-results
|
||||||
|
|
||||||
|
if popularAnime != nil
|
||||||
|
h3.popular-title Popular
|
||||||
|
|
||||||
|
.popular-anime-list
|
||||||
|
each anime in popularAnime
|
||||||
|
a.popular-anime.ajax(href="/anime/" + toString(anime.ID), title=anime.Title.Romaji + " (" + arn.Plural(anime.Watching(), "user") + " watching)")
|
||||||
|
img.anime-image.popular-anime-image(src=anime.Image, alt=anime.Title.Romaji)
|
@ -1,29 +1,97 @@
|
|||||||
package search
|
package search
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxUsers = 18
|
||||||
|
const maxAnime = 18
|
||||||
|
|
||||||
|
type AnimeID = string
|
||||||
|
type UserID = string
|
||||||
|
|
||||||
|
var animeSearchIndex = make(map[string]AnimeID)
|
||||||
|
var userSearchIndex = make(map[string]UserID)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
updateSearchIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSearchIndex() {
|
||||||
|
updateAnimeIndex()
|
||||||
|
updateUserIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAnimeIndex() {
|
||||||
|
// Anime
|
||||||
|
animeStream, err := arn.AllAnime()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for anime := range animeStream {
|
||||||
|
animeSearchIndex[strings.ToLower(anime.Title.Canonical)] = anime.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUserIndex() {
|
||||||
|
// Users
|
||||||
|
userStream, err := arn.AllUsers()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for user := range userStream {
|
||||||
|
userSearchIndex[strings.ToLower(user.Nick)] = user.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get search page.
|
// Get search page.
|
||||||
func Get(ctx *aero.Context) string {
|
func Get(ctx *aero.Context) string {
|
||||||
// titleCount := 0
|
term := strings.ToLower(ctx.Get("term"))
|
||||||
// animeCount := 0
|
|
||||||
|
|
||||||
// // let info: any = await bluebird.props({
|
var users []*arn.User
|
||||||
// // popular: arn.db.get('Cache', 'popularAnime'),
|
var animeResults []*arn.Anime
|
||||||
// // stats: arn.db.get('Cache', 'animeStats')
|
|
||||||
// // })
|
|
||||||
|
|
||||||
// // return response.render({
|
aero.Parallel(func() {
|
||||||
// // user,
|
for name, id := range userSearchIndex {
|
||||||
// // popularAnime: info.popular.anime,
|
if strings.Index(name, term) != -1 {
|
||||||
// // animeCount: info.stats.animeCount,
|
user, err := arn.GetUser(id)
|
||||||
// // titleCount: info.stats.titleCount,
|
|
||||||
// // anime: null
|
|
||||||
// // })
|
|
||||||
|
|
||||||
// popular, _ := arn.GetPopularCache()
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// return ctx.HTML(components.Search(popular.Anime, titleCount, animeCount))
|
users = append(users, user)
|
||||||
return ctx.HTML("Coming soon.")
|
|
||||||
|
if len(users) >= maxUsers {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, func() {
|
||||||
|
for title, id := range animeSearchIndex {
|
||||||
|
if strings.Index(title, term) != -1 {
|
||||||
|
anime, err := arn.GetAnime(id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
animeResults = append(animeResults, anime)
|
||||||
|
|
||||||
|
if len(animeResults) >= maxAnime {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx.HTML(components.Search(users, animeResults))
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
component Search(popularAnime []*arn.Anime, titleCount int, animeCount int)
|
component Search(users []*arn.User, animeResults []*arn.Anime)
|
||||||
h2 Anime
|
.widgets
|
||||||
|
.widget
|
||||||
|
h3 Users
|
||||||
|
.user-avatars.user-search
|
||||||
|
if len(users) == 0
|
||||||
|
p No users found.
|
||||||
|
else
|
||||||
|
each user in users
|
||||||
|
Avatar(user)
|
||||||
|
//- a.ajax(href=user.Link())= user.Nick
|
||||||
|
|
||||||
#search-container
|
.widget
|
||||||
input#search(type="text", placeholder="Search...", onkeyup="$.searchAnime();", onfocus="this.select();", disabled="disabled", data-count=titleCount, data-anime-count=animeCount)
|
h3 Anime
|
||||||
|
.profile-watching-list.anime-search
|
||||||
#search-results-container
|
if len(animeResults) == 0
|
||||||
#search-results
|
p No anime found.
|
||||||
|
else
|
||||||
if popularAnime != nil
|
each anime in animeResults
|
||||||
h3.popular-title Popular
|
a.profile-watching-list-item.ajax(href=anime.Link(), title=anime.Title.Canonical)
|
||||||
|
img.anime-cover-image.anime-search-result(src=anime.Image.Tiny, alt=anime.Title.Canonical)
|
||||||
.popular-anime-list
|
|
||||||
each anime in popularAnime
|
|
||||||
a.popular-anime.ajax(href="/anime/" + toString(anime.ID), title=anime.Title.Romaji + " (" + arn.Plural(anime.Watching(), "user") + " watching)")
|
|
||||||
img.anime-image.popular-anime-image(src=anime.Image, alt=anime.Title.Romaji)
|
|
@ -1,7 +1,2 @@
|
|||||||
// #search-container
|
.anime-search-result
|
||||||
// horizontal
|
width 55px !important
|
||||||
// justify-content center
|
|
||||||
|
|
||||||
// #search
|
|
||||||
// width 100%
|
|
||||||
// max-width 560px
|
|
@ -38,9 +38,11 @@ export class AnimeNotifier {
|
|||||||
for(let element of findAll(".action")) {
|
for(let element of findAll(".action")) {
|
||||||
let actionName = element.dataset.action
|
let actionName = element.dataset.action
|
||||||
|
|
||||||
element.onclick = () => {
|
element.addEventListener(element.dataset.trigger, e => {
|
||||||
actions[actionName](this, element)
|
actions[actionName](this, element, e)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
element.classList.remove("action")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(url: string): Promise<string> {
|
get(url: string): Promise<string> {
|
||||||
|
if(this.lastRequest) {
|
||||||
|
this.lastRequest.abort()
|
||||||
|
this.lastRequest = null
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let request = new XMLHttpRequest()
|
let request = new XMLHttpRequest()
|
||||||
|
|
||||||
@ -52,11 +57,6 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load(url: string, options?: LoadOptions) {
|
load(url: string, options?: LoadOptions) {
|
||||||
if(this.lastRequest) {
|
|
||||||
this.lastRequest.abort()
|
|
||||||
this.lastRequest = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!options) {
|
if(!options) {
|
||||||
options = new LoadOptions()
|
options = new LoadOptions()
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,38 @@ import { Application } from "./Application"
|
|||||||
import { AnimeNotifier } from "./AnimeNotifier"
|
import { AnimeNotifier } from "./AnimeNotifier"
|
||||||
import { Diff } from "./Diff"
|
import { Diff } from "./Diff"
|
||||||
|
|
||||||
|
// Search
|
||||||
|
export function search(arn: AnimeNotifier, search: HTMLInputElement, e: KeyboardEvent) {
|
||||||
|
if(e.ctrlKey || e.altKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let term = search.value
|
||||||
|
|
||||||
|
if(!window.location.pathname.startsWith("/search/")) {
|
||||||
|
history.pushState("search", null, "/search/" + term)
|
||||||
|
} else {
|
||||||
|
history.replaceState("search", null, "/search/" + term)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!term) {
|
||||||
|
arn.app.content.innerHTML = "No search term."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
arn.app.content.innerHTML = "<h2>" + term + "</h2><div id='results'></div>"
|
||||||
|
|
||||||
|
arn.app.get("/_/search/" + encodeURI(term))
|
||||||
|
.then(html => {
|
||||||
|
if(!search.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
arn.app.find("results").innerHTML = html
|
||||||
|
arn.app.emit("DOMContentLoaded")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Add anime to collection
|
// Add anime to collection
|
||||||
export function addAnimeToCollection(arn: AnimeNotifier, button: HTMLElement) {
|
export function addAnimeToCollection(arn: AnimeNotifier, button: HTMLElement) {
|
||||||
button.innerText = "Adding..."
|
button.innerText = "Adding..."
|
||||||
|
Loading…
Reference in New Issue
Block a user