Implemented adding and removing anime
This commit is contained in:
parent
6bd57a9135
commit
a0c8fb19ca
@ -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
|
||||||
|
@ -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()
|
@ -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()
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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
48
scripts/actions.ts
Normal 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))
|
||||||
|
}
|
@ -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
7
scripts/utils.ts
Normal 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
6
styles/actions.scarlet
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.actions
|
||||||
|
horizontal-wrap
|
||||||
|
justify-content space-around
|
||||||
|
|
||||||
|
button, .button
|
||||||
|
margin 0.5rem
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user