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,12 +1,22 @@
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)
else else
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