Implemented adding and removing anime

This commit is contained in:
Eduard Urbach 2017-06-20 12:41:26 +02:00
parent 6bd57a9135
commit a0c8fb19ca
12 changed files with 119 additions and 27 deletions

View File

@ -22,9 +22,11 @@ component Anime(anime *arn.Anime, user *arn.User)
if user != nil if user != nil
.anime-actions .anime-actions
if user.AnimeList().Contains(anime.ID) if user.AnimeList().Contains(anime.ID)
a.button.ajax(href="/+" + user.Nick + "/animelist/" + anime.ID) View in collection a.button.ajax(href="/+" + user.Nick + "/animelist/" + anime.ID)
Icon("pencil")
span Edit in collection
else else
button Add to collection button.action(data-action="addAnimeToCollection", data-anime-id=anime.ID, data-user-id=user.ID, data-user-nick=user.Nick) Add to collection
h3.anime-section-name Ratings h3.anime-section-name Ratings
.anime-rating-categories .anime-rating-categories

View File

@ -8,6 +8,7 @@ component AnimeList(animeList *arn.AnimeList)
tbody tbody
each item in animeList.Items each item in animeList.Items
tr.anime-list-item tr.anime-list-item
td= item.Anime().Title.Canonical td
a.ajax(href=item.Anime().Link())= item.Anime().Title.Canonical
td= toString(item.Episodes) + " / " + item.Anime().EpisodeCountString() td= toString(item.Episodes) + " / " + item.Anime().EpisodeCountString()
td= item.FinalRating() td= item.FinalRating()

View File

@ -1,6 +1,7 @@
package animelistitem package animelistitem
import ( import (
"errors"
"net/http" "net/http"
"github.com/aerogo/aero" "github.com/aerogo/aero"
@ -29,12 +30,12 @@ func Get(ctx *aero.Context) string {
item := animeList.Find(animeID) item := animeList.Find(animeID)
if item == nil { if item == nil {
return ctx.Error(http.StatusNotFound, "List item not found", err) return ctx.Error(http.StatusNotFound, "List item not found", errors.New("This anime does not exist in "+viewUser.Nick+"'s anime list"))
} }
anime := item.Anime() anime := item.Anime()
return ctx.HTML(components.AnimeListItem(item, anime)) return ctx.HTML(components.AnimeListItem(animeList.User(), item, anime))
} }
// t := reflect.TypeOf(item).Elem() // t := reflect.TypeOf(item).Elem()

View File

@ -1,8 +1,7 @@
component AnimeListItem(item *arn.AnimeListItem, anime *arn.Anime) component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.Anime)
.widgets .widgets
.widget.anime-list-item-view .widget.anime-list-item-view
h2 h2= anime.Title.Canonical
a.ajax(href=anime.Link())= anime.Title.Canonical
if anime.EpisodeCount == 0 if anime.EpisodeCount == 0
InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, 10000) InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, 10000)
@ -10,3 +9,14 @@ component AnimeListItem(item *arn.AnimeListItem, anime *arn.Anime)
InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, anime.EpisodeCount) InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, anime.EpisodeCount)
InputTextArea("notes", item.Notes, "Notes", "Notes") InputTextArea("notes", item.Notes, "Notes", "Notes")
.actions
a.ajax.button(href="/+" + viewUser.Nick + "/animelist")
Icon("list")
span View collection
a.ajax.button(href=anime.Link())
Icon("search-plus")
span View anime
button.action(data-action="removeAnimeFromCollection", data-anime-id=anime.ID, data-user-id=viewUser.ID, data-user-nick=viewUser.Nick)
Icon("trash")
span Remove from collection

View File

@ -1,4 +1,4 @@
component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread) component ProfileHeader(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread)
.profile .profile
img.profile-cover(src=viewUser.CoverImageURL(), alt="Cover image") img.profile-cover(src=viewUser.CoverImageURL(), alt="Cover image")
@ -44,10 +44,8 @@ component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList,
Icon("rocket") Icon("rocket")
span= arn.Capitalize(viewUser.Role) span= arn.Capitalize(viewUser.Role)
//- nav.light-button-group component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread)
//- a.light-button(href="#") Bio ProfileHeader(viewUser, user, animeList, threads)
//- a.light-button(href="#") Anime
//- a.light-button(href="#") Forum
.profile-category .profile-category
h3 h3

View File

@ -1,4 +1,6 @@
import { Application } from "./Application" import { Application } from "./Application"
import { findAll } from "./utils"
import * as actions from "./actions"
export class AnimeNotifier { export class AnimeNotifier {
app: Application app: Application
@ -21,15 +23,29 @@ export class AnimeNotifier {
this.app.run() this.app.run()
} }
loading(isLoading: boolean) {
if(isLoading) {
this.app.loading.classList.remove(this.app.fadeOutClass)
} else {
this.app.loading.classList.add(this.app.fadeOutClass)
}
}
onContentLoaded() { onContentLoaded() {
this.updateAvatars() this.updateAvatars()
for(let element of findAll(".action")) {
let actionName = element.dataset.action
element.onclick = () => {
actions[actionName](this, element)
}
}
} }
updateAvatars() { updateAvatars() {
let images = document.querySelectorAll('.user-image') for(let element of findAll(".user-image")) {
let img = element as HTMLImageElement
for(let i = 0; i < images.length; ++i) {
let img = images[i] as HTMLImageElement
if(img.naturalWidth === 0) { if(img.naturalWidth === 0) {
img.onload = function() { img.onload = function() {

View File

@ -4,13 +4,13 @@ export class Application {
activeLinkClass: string activeLinkClass: string
content: HTMLElement content: HTMLElement
loading: HTMLElement loading: HTMLElement
currentURL: string currentPath: string
originalURL: string originalPath: string
lastRequest: XMLHttpRequest lastRequest: XMLHttpRequest
constructor() { constructor() {
this.currentURL = window.location.pathname this.currentPath = window.location.pathname
this.originalURL = window.location.pathname this.originalPath = window.location.pathname
this.ajaxClass = "ajax" this.ajaxClass = "ajax"
this.activeLinkClass = "active" this.activeLinkClass = "active"
this.fadeOutClass = "fade-out" this.fadeOutClass = "fade-out"
@ -52,7 +52,7 @@ export class Application {
this.lastRequest = null this.lastRequest = null
} }
this.currentURL = url this.currentPath = url
// Start sending a network request // Start sending a network request
let request = this.get("/_" + url) let request = this.get("/_" + url)
@ -91,6 +91,8 @@ export class Application {
this.content.classList.add(this.fadeOutClass) this.content.classList.add(this.fadeOutClass)
this.loading.classList.remove(this.fadeOutClass) this.loading.classList.remove(this.fadeOutClass)
this.markActiveLinks() this.markActiveLinks()
return request
} }
setContent(html: string) { setContent(html: string) {
@ -109,7 +111,7 @@ export class Application {
let link = links[i] let link = links[i]
let href = link.getAttribute("href") let href = link.getAttribute("href")
if(href === this.currentURL) if(href === this.currentPath)
link.classList.add(this.activeLinkClass) link.classList.add(this.activeLinkClass)
else else
link.classList.remove(this.activeLinkClass) link.classList.remove(this.activeLinkClass)
@ -138,7 +140,7 @@ export class Application {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
if(!url || url === self.currentURL) if(!url || url === self.currentPath)
return return
// Load requested page // Load requested page

48
scripts/actions.ts Normal file
View File

@ -0,0 +1,48 @@
import { Application } from "./Application"
import { AnimeNotifier } from "./AnimeNotifier"
// Add anime to collection
export function addAnimeToCollection(arn: AnimeNotifier, button: HTMLElement) {
button.innerText = "Adding..."
arn.loading(true)
let {animeId, userId, userNick} = button.dataset
fetch("/api/animelist/" + userId + "/add", {
method: "POST",
body: animeId
})
.then(response => response.text())
.then(body => {
if(body !== "ok") {
throw body
}
return arn.app.load("/+" + userNick + "/animelist/" + animeId, true)
})
.catch(console.error)
.then(() => arn.loading(false))
}
// Remove anime from collection
export function removeAnimeFromCollection(arn: AnimeNotifier, button: HTMLElement) {
button.innerText = "Removing..."
arn.loading(true)
let {animeId, userId, userNick} = button.dataset
fetch("/api/animelist/" + userId + "/remove", {
method: "POST",
body: animeId
})
.then(response => response.text())
.then(body => {
if(body !== "ok") {
throw body
}
return arn.app.load("/+" + userNick + "/animelist", true)
})
.catch(console.error)
.then(() => arn.loading(false))
}

View File

@ -10,6 +10,6 @@ document.addEventListener("readystatechange", arn.onReadyStateChange.bind(arn))
window.onpopstate = e => { window.onpopstate = e => {
if(e.state) if(e.state)
app.load(e.state, false) app.load(e.state, false)
else if(app.currentURL !== app.originalURL) else if(app.currentPath !== app.originalPath)
app.load(app.originalURL, false) app.load(app.originalPath, false)
} }

7
scripts/utils.ts Normal file
View File

@ -0,0 +1,7 @@
export function* findAll(query: string) {
let elements = document.querySelectorAll(query)
for(let i = 0; i < elements.length; ++i) {
yield elements[i] as HTMLElement
}
}

6
styles/actions.scarlet Normal file
View File

@ -0,0 +1,6 @@
.actions
horizontal-wrap
justify-content space-around
button, .button
margin 0.5rem

View File

@ -21,6 +21,7 @@ input, textarea
button, .button button, .button
ui-element ui-element
horizontal
font-size 1rem font-size 1rem
line-height 1rem line-height 1rem
padding 0.75rem 1rem padding 0.75rem 1rem