diff --git a/pages/anime/anime.pixy b/pages/anime/anime.pixy index d7b7a83c..97173475 100644 --- a/pages/anime/anime.pixy +++ b/pages/anime/anime.pixy @@ -22,9 +22,11 @@ component Anime(anime *arn.Anime, user *arn.User) if user != nil .anime-actions 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 - 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 .anime-rating-categories diff --git a/pages/animelist/animelist.pixy b/pages/animelist/animelist.pixy index bf01304a..f7d8e1d0 100644 --- a/pages/animelist/animelist.pixy +++ b/pages/animelist/animelist.pixy @@ -8,6 +8,7 @@ component AnimeList(animeList *arn.AnimeList) tbody each item in animeList.Items 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= item.FinalRating() \ No newline at end of file diff --git a/pages/animelistitem/animelistitem.go b/pages/animelistitem/animelistitem.go index 85e891c1..5afa5a56 100644 --- a/pages/animelistitem/animelistitem.go +++ b/pages/animelistitem/animelistitem.go @@ -1,6 +1,7 @@ package animelistitem import ( + "errors" "net/http" "github.com/aerogo/aero" @@ -29,12 +30,12 @@ func Get(ctx *aero.Context) string { item := animeList.Find(animeID) 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() - return ctx.HTML(components.AnimeListItem(item, anime)) + return ctx.HTML(components.AnimeListItem(animeList.User(), item, anime)) } // t := reflect.TypeOf(item).Elem() diff --git a/pages/animelistitem/animelistitem.pixy b/pages/animelistitem/animelistitem.pixy index 9f18db52..d3efdad2 100644 --- a/pages/animelistitem/animelistitem.pixy +++ b/pages/animelistitem/animelistitem.pixy @@ -1,12 +1,22 @@ -component AnimeListItem(item *arn.AnimeListItem, anime *arn.Anime) +component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.Anime) .widgets .widget.anime-list-item-view - h2 - a.ajax(href=anime.Link())= anime.Title.Canonical + h2= anime.Title.Canonical if anime.EpisodeCount == 0 InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, 10000) else InputNumber("episodes", item.Episodes, "Episodes", "Number of episodes you watched", 0, anime.EpisodeCount) - InputTextArea("notes", item.Notes, "Notes", "Notes") \ No newline at end of file + 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 \ No newline at end of file diff --git a/pages/profile/profile.pixy b/pages/profile/profile.pixy index b39a05e0..2e93e19f 100644 --- a/pages/profile/profile.pixy +++ b/pages/profile/profile.pixy @@ -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 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") span= arn.Capitalize(viewUser.Role) - //- nav.light-button-group - //- a.light-button(href="#") Bio - //- a.light-button(href="#") Anime - //- a.light-button(href="#") Forum +component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread) + ProfileHeader(viewUser, user, animeList, threads) .profile-category h3 diff --git a/scripts/AnimeNotifier.ts b/scripts/AnimeNotifier.ts index a4755489..32dddff2 100644 --- a/scripts/AnimeNotifier.ts +++ b/scripts/AnimeNotifier.ts @@ -1,4 +1,6 @@ import { Application } from "./Application" +import { findAll } from "./utils" +import * as actions from "./actions" export class AnimeNotifier { app: Application @@ -21,15 +23,29 @@ export class AnimeNotifier { 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() { this.updateAvatars() + + for(let element of findAll(".action")) { + let actionName = element.dataset.action + + element.onclick = () => { + actions[actionName](this, element) + } + } } updateAvatars() { - let images = document.querySelectorAll('.user-image') - - for(let i = 0; i < images.length; ++i) { - let img = images[i] as HTMLImageElement + for(let element of findAll(".user-image")) { + let img = element as HTMLImageElement if(img.naturalWidth === 0) { img.onload = function() { diff --git a/scripts/Application.ts b/scripts/Application.ts index 913c6c68..b4a75d85 100644 --- a/scripts/Application.ts +++ b/scripts/Application.ts @@ -4,13 +4,13 @@ export class Application { activeLinkClass: string content: HTMLElement loading: HTMLElement - currentURL: string - originalURL: string + currentPath: string + originalPath: string lastRequest: XMLHttpRequest constructor() { - this.currentURL = window.location.pathname - this.originalURL = window.location.pathname + this.currentPath = window.location.pathname + this.originalPath = window.location.pathname this.ajaxClass = "ajax" this.activeLinkClass = "active" this.fadeOutClass = "fade-out" @@ -52,7 +52,7 @@ export class Application { this.lastRequest = null } - this.currentURL = url + this.currentPath = url // Start sending a network request let request = this.get("/_" + url) @@ -91,6 +91,8 @@ export class Application { this.content.classList.add(this.fadeOutClass) this.loading.classList.remove(this.fadeOutClass) this.markActiveLinks() + + return request } setContent(html: string) { @@ -109,7 +111,7 @@ export class Application { let link = links[i] let href = link.getAttribute("href") - if(href === this.currentURL) + if(href === this.currentPath) link.classList.add(this.activeLinkClass) else link.classList.remove(this.activeLinkClass) @@ -138,7 +140,7 @@ export class Application { e.preventDefault() e.stopPropagation() - if(!url || url === self.currentURL) + if(!url || url === self.currentPath) return // Load requested page diff --git a/scripts/actions.ts b/scripts/actions.ts new file mode 100644 index 00000000..fee6bec2 --- /dev/null +++ b/scripts/actions.ts @@ -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)) +} \ No newline at end of file diff --git a/scripts/main.ts b/scripts/main.ts index b76789a6..bdeeab3a 100644 --- a/scripts/main.ts +++ b/scripts/main.ts @@ -10,6 +10,6 @@ document.addEventListener("readystatechange", arn.onReadyStateChange.bind(arn)) window.onpopstate = e => { if(e.state) app.load(e.state, false) - else if(app.currentURL !== app.originalURL) - app.load(app.originalURL, false) + else if(app.currentPath !== app.originalPath) + app.load(app.originalPath, false) } \ No newline at end of file diff --git a/scripts/utils.ts b/scripts/utils.ts new file mode 100644 index 00000000..f9a9e2d6 --- /dev/null +++ b/scripts/utils.ts @@ -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 + } +} \ No newline at end of file diff --git a/styles/actions.scarlet b/styles/actions.scarlet new file mode 100644 index 00000000..73bff483 --- /dev/null +++ b/styles/actions.scarlet @@ -0,0 +1,6 @@ +.actions + horizontal-wrap + justify-content space-around + + button, .button + margin 0.5rem \ No newline at end of file diff --git a/styles/forms.scarlet b/styles/forms.scarlet index 4b5c8e01..b679bd3e 100644 --- a/styles/forms.scarlet +++ b/styles/forms.scarlet @@ -21,6 +21,7 @@ input, textarea button, .button ui-element + horizontal font-size 1rem line-height 1rem padding 0.75rem 1rem