Make search great again
This commit is contained in:
parent
b21329538c
commit
28dc9001da
@ -205,6 +205,12 @@ func Configure(app *aero.Application) {
|
|||||||
|
|
||||||
// Search
|
// Search
|
||||||
l.Page("/search/*term", search.Get)
|
l.Page("/search/*term", search.Get)
|
||||||
|
l.Page("/empty-search", search.GetEmptySearch)
|
||||||
|
l.Page("/anime-search/*term", search.Anime)
|
||||||
|
l.Page("/character-search/*term", search.Characters)
|
||||||
|
l.Page("/forum-search/*term", search.Forum)
|
||||||
|
l.Page("/soundtrack-search/*term", search.SoundTracks)
|
||||||
|
l.Page("/user-search/*term", search.Users)
|
||||||
|
|
||||||
// Shop
|
// Shop
|
||||||
l.Page("/support", support.Get)
|
l.Page("/support", support.Get)
|
||||||
|
@ -3,6 +3,8 @@ package search
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aerogo/flow"
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
"github.com/aerogo/aero"
|
||||||
"github.com/animenotifier/arn"
|
"github.com/animenotifier/arn"
|
||||||
"github.com/animenotifier/notify.moe/components"
|
"github.com/animenotifier/notify.moe/components"
|
||||||
@ -12,7 +14,7 @@ const maxUsers = 25
|
|||||||
const maxAnime = 25
|
const maxAnime = 25
|
||||||
const maxPosts = 2
|
const maxPosts = 2
|
||||||
const maxThreads = 2
|
const maxThreads = 2
|
||||||
const maxTracks = 4
|
const maxSoundTracks = 4
|
||||||
const maxCharacters = 22
|
const maxCharacters = 22
|
||||||
|
|
||||||
// Get search page.
|
// Get search page.
|
||||||
@ -20,6 +22,64 @@ func Get(ctx *aero.Context) string {
|
|||||||
term := ctx.Get("term")
|
term := ctx.Get("term")
|
||||||
term = strings.TrimPrefix(term, "/")
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
users, animes, posts, threads, tracks, characters := arn.Search(term, maxUsers, maxAnime, maxPosts, maxThreads, maxTracks, maxCharacters)
|
users, animes, posts, threads, tracks, characters := arn.Search(term, maxUsers, maxAnime, maxPosts, maxThreads, maxSoundTracks, maxCharacters)
|
||||||
return ctx.HTML(components.SearchResults(term, users, animes, posts, threads, tracks, characters))
|
return ctx.HTML(components.SearchResults(term, users, animes, posts, threads, tracks, characters))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEmptySearch renders the search page with no contents.
|
||||||
|
func GetEmptySearch(ctx *aero.Context) string {
|
||||||
|
return ctx.HTML(components.SearchResults("", nil, nil, nil, nil, nil, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anime search.
|
||||||
|
func Anime(ctx *aero.Context) string {
|
||||||
|
term := ctx.Get("term")
|
||||||
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
|
animes := arn.SearchAnime(term, maxAnime)
|
||||||
|
return ctx.HTML(components.AnimeSearchResults(animes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Characters search.
|
||||||
|
func Characters(ctx *aero.Context) string {
|
||||||
|
term := ctx.Get("term")
|
||||||
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
|
characters := arn.SearchCharacters(term, maxCharacters)
|
||||||
|
return ctx.HTML(components.CharacterSearchResults(characters))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forum search.
|
||||||
|
func Forum(ctx *aero.Context) string {
|
||||||
|
term := ctx.Get("term")
|
||||||
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
|
var posts []*arn.Post
|
||||||
|
var threads []*arn.Thread
|
||||||
|
|
||||||
|
flow.Parallel(func() {
|
||||||
|
posts = arn.SearchPosts(term, maxPosts)
|
||||||
|
}, func() {
|
||||||
|
threads = arn.SearchThreads(term, maxThreads)
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx.HTML(components.ForumSearchResults(posts, threads))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SoundTracks search.
|
||||||
|
func SoundTracks(ctx *aero.Context) string {
|
||||||
|
term := ctx.Get("term")
|
||||||
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
|
tracks := arn.SearchSoundTracks(term, maxSoundTracks)
|
||||||
|
return ctx.HTML(components.SoundTrackSearchResults(tracks))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users search.
|
||||||
|
func Users(ctx *aero.Context) string {
|
||||||
|
term := ctx.Get("term")
|
||||||
|
term = strings.TrimPrefix(term, "/")
|
||||||
|
|
||||||
|
users := arn.SearchUsers(term, maxUsers)
|
||||||
|
return ctx.HTML(components.UserSearchResults(users))
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ component SearchResults(term string, users []*arn.User, animes []*arn.Anime, pos
|
|||||||
Icon("tv")
|
Icon("tv")
|
||||||
span Anime
|
span Anime
|
||||||
|
|
||||||
|
#anime-search-results
|
||||||
AnimeSearchResults(animes)
|
AnimeSearchResults(animes)
|
||||||
|
|
||||||
.widget
|
.widget
|
||||||
@ -14,6 +15,7 @@ component SearchResults(term string, users []*arn.User, animes []*arn.Anime, pos
|
|||||||
Icon("user")
|
Icon("user")
|
||||||
span Characters
|
span Characters
|
||||||
|
|
||||||
|
#character-search-results
|
||||||
CharacterSearchResults(characters)
|
CharacterSearchResults(characters)
|
||||||
|
|
||||||
.widget
|
.widget
|
||||||
@ -21,6 +23,7 @@ component SearchResults(term string, users []*arn.User, animes []*arn.Anime, pos
|
|||||||
Icon("comment")
|
Icon("comment")
|
||||||
span Forum
|
span Forum
|
||||||
|
|
||||||
|
#forum-search-results
|
||||||
ForumSearchResults(posts, threads)
|
ForumSearchResults(posts, threads)
|
||||||
|
|
||||||
.widget
|
.widget
|
||||||
@ -28,6 +31,7 @@ component SearchResults(term string, users []*arn.User, animes []*arn.Anime, pos
|
|||||||
Icon("music")
|
Icon("music")
|
||||||
span Soundtracks
|
span Soundtracks
|
||||||
|
|
||||||
|
#soundtrack-search-results
|
||||||
SoundTrackSearchResults(tracks)
|
SoundTrackSearchResults(tracks)
|
||||||
|
|
||||||
.widget
|
.widget
|
||||||
@ -35,6 +39,7 @@ component SearchResults(term string, users []*arn.User, animes []*arn.Anime, pos
|
|||||||
Icon("user")
|
Icon("user")
|
||||||
span Users
|
span Users
|
||||||
|
|
||||||
|
#user-search-results
|
||||||
UserSearchResults(users)
|
UserSearchResults(users)
|
||||||
|
|
||||||
component AnimeSearchResults(animes []*arn.Anime)
|
component AnimeSearchResults(animes []*arn.Anime)
|
||||||
|
@ -1,19 +1,126 @@
|
|||||||
import { AnimeNotifier } from "../AnimeNotifier"
|
import { AnimeNotifier } from "../AnimeNotifier"
|
||||||
|
|
||||||
|
// Search page reference
|
||||||
|
var emptySearchHTML = ""
|
||||||
|
var searchPage: HTMLElement
|
||||||
|
var correctResponseRendered = {
|
||||||
|
"anime": false,
|
||||||
|
"character": false,
|
||||||
|
"forum": false,
|
||||||
|
"soundtrack": false,
|
||||||
|
"user": false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Containers for all the search results
|
||||||
|
var animeSearchResults: HTMLElement
|
||||||
|
var characterSearchResults: HTMLElement
|
||||||
|
var forumSearchResults: HTMLElement
|
||||||
|
var soundtrackSearchResults: HTMLElement
|
||||||
|
var userSearchResults: HTMLElement
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
export function search(arn: AnimeNotifier, search: HTMLInputElement, e: KeyboardEvent) {
|
export async function search(arn: AnimeNotifier, search: HTMLInputElement, e: KeyboardEvent) {
|
||||||
if(e.ctrlKey || e.altKey) {
|
if(e.ctrlKey || e.altKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let term = search.value
|
let term = search.value.trim()
|
||||||
|
let searchPageActivated = (searchPage === arn.app.content.children[0])
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
correctResponseRendered.anime = false
|
||||||
|
correctResponseRendered.character = false
|
||||||
|
correctResponseRendered.forum = false
|
||||||
|
correctResponseRendered.soundtrack = false
|
||||||
|
correctResponseRendered.user = false
|
||||||
|
|
||||||
|
// Set browser URL
|
||||||
|
let url = "/search/" + term
|
||||||
|
history.pushState(url, null, url)
|
||||||
|
arn.app.currentPath = url
|
||||||
|
|
||||||
|
// Unmount mountables to improve visual responsiveness on key press
|
||||||
|
arn.unmountMountables()
|
||||||
|
|
||||||
|
// Show loading spinner
|
||||||
|
arn.loading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch empty search frame if needed
|
||||||
|
if(emptySearchHTML === "") {
|
||||||
|
let response = await fetch("/_/empty-search")
|
||||||
|
emptySearchHTML = await response.text()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!searchPageActivated) {
|
||||||
|
if(!searchPage) {
|
||||||
|
searchPage = document.createElement("div")
|
||||||
|
searchPage.innerHTML = emptySearchHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
arn.app.content.innerHTML = ""
|
||||||
|
arn.app.content.appendChild(searchPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!animeSearchResults) {
|
||||||
|
animeSearchResults = document.getElementById("anime-search-results")
|
||||||
|
characterSearchResults = document.getElementById("character-search-results")
|
||||||
|
forumSearchResults = document.getElementById("forum-search-results")
|
||||||
|
soundtrackSearchResults = document.getElementById("soundtrack-search-results")
|
||||||
|
userSearchResults = document.getElementById("user-search-results")
|
||||||
|
}
|
||||||
|
|
||||||
if(!term || term.length < 1) {
|
if(!term || term.length < 1) {
|
||||||
arn.app.content.innerHTML = "Please enter at least 1 character to start searching."
|
await arn.innerHTML(searchPage, emptySearchHTML)
|
||||||
|
arn.app.emit("DOMContentLoaded")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
arn.diff("/search/" + term)
|
// Start searching
|
||||||
|
fetch("/_/anime-search/" + term)
|
||||||
|
.then(showResponseInElement(arn, url, "anime", animeSearchResults))
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
fetch("/_/character-search/" + term)
|
||||||
|
.then(showResponseInElement(arn, url, "character", characterSearchResults))
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
fetch("/_/forum-search/" + term)
|
||||||
|
.then(showResponseInElement(arn, url, "forum", forumSearchResults))
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
fetch("/_/soundtrack-search/" + term)
|
||||||
|
.then(showResponseInElement(arn, url, "soundtrack", soundtrackSearchResults))
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
fetch("/_/user-search/" + term)
|
||||||
|
.then(showResponseInElement(arn, url, "user", userSearchResults))
|
||||||
|
.catch(console.error)
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err)
|
||||||
|
} finally {
|
||||||
|
arn.loading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showResponseInElement(arn: AnimeNotifier, url: string, typeName: string, element: HTMLElement) {
|
||||||
|
return async response => {
|
||||||
|
let html = await response.text()
|
||||||
|
|
||||||
|
if(arn.app.currentPath !== url) {
|
||||||
|
// Return if this result would overwrite the already arrived correct result
|
||||||
|
if(correctResponseRendered[typeName]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
correctResponseRendered[typeName] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
await arn.innerHTML(element, html)
|
||||||
|
|
||||||
|
// Emit content loaded event
|
||||||
|
arn.app.emit("DOMContentLoaded")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search database
|
// Search database
|
||||||
|
@ -710,6 +710,10 @@ export class AnimeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
innerHTML(element: HTMLElement, html: string) {
|
||||||
|
return Diff.innerHTML(element, html)
|
||||||
|
}
|
||||||
|
|
||||||
post(url: string, body: any) {
|
post(url: string, body: any) {
|
||||||
if(this.isLoading) {
|
if(this.isLoading) {
|
||||||
return Promise.resolve(null)
|
return Promise.resolve(null)
|
||||||
|
Loading…
Reference in New Issue
Block a user