This commit is contained in:
Eduard Urbach 2017-10-13 13:55:33 +02:00
parent 0021ff17df
commit 892cb3eb31
7 changed files with 17 additions and 108 deletions

View File

@ -1,15 +1,11 @@
package main
import (
"errors"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"github.com/aerogo/aero"
"github.com/aerogo/api"
"github.com/animenotifier/arn"
"github.com/fatih/color"
)
@ -33,52 +29,3 @@ func TestRoutes(t *testing.T) {
}
}
}
func TestInterfaceImplementations(t *testing.T) {
// API interfaces
var creatable = reflect.TypeOf((*api.Creatable)(nil)).Elem()
var editable = reflect.TypeOf((*api.Editable)(nil)).Elem()
var actionable = reflect.TypeOf((*api.Actionable)(nil)).Elem()
var collection = reflect.TypeOf((*api.Collection)(nil)).Elem()
// Required interface implementations
var interfaceImplementations = map[string][]reflect.Type{
"User": []reflect.Type{
editable,
},
"Thread": []reflect.Type{
creatable,
editable,
actionable,
},
"Post": []reflect.Type{
creatable,
editable,
actionable,
},
"SoundTrack": []reflect.Type{
creatable,
editable,
},
"Analytics": []reflect.Type{
creatable,
},
"AnimeList": []reflect.Type{
collection,
},
"PushSubscriptions": []reflect.Type{
collection,
},
"UserFollows": []reflect.Type{
collection,
},
}
for typeName, interfaces := range interfaceImplementations {
for _, requiredInterface := range interfaces {
if !reflect.PtrTo(arn.DB.Type(typeName)).Implements(requiredInterface) {
panic(errors.New(typeName + " does not implement interface " + requiredInterface.Name()))
}
}
}
}

View File

@ -51,13 +51,13 @@ func Get(ctx *aero.Context) string {
for i := range friends {
j := i - deleted
friendAnimeList := friends[j].AnimeList()
obj, err := friendAnimeList.Get(anime.ID)
friendAnimeListItem := friendAnimeList.Find(anime.ID)
if err != nil {
if friendAnimeListItem == nil {
friends = friends[:j+copy(friends[j:], friends[j+1:])]
deleted++
} else {
friendsAnimeListItems[friends[j]] = obj.(*arn.AnimeListItem)
friendsAnimeListItems[friends[j]] = friendAnimeListItem
}
}

View File

@ -31,7 +31,7 @@ component Anime(anime *arn.Anime, friends []*arn.User, listItems map[*arn.User]*
Icon("pencil")
span Edit in collection
else
button.action(data-action="addAnimeToCollection", data-trigger="click", data-anime-id=anime.ID, data-user-id=user.ID, data-user-nick=user.Nick)
button.action(data-api="/api/animelist/" + user.ID, data-action="arrayAppend", data-trigger="click", data-field="Items", data-object="{\"AnimeID\": \"" + anime.ID + "\"}")
Icon("plus")
span Add to collection

View File

@ -27,12 +27,12 @@ component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.
InputTextArea("Notes", item.Notes, "Notes", "Your notes")
.buttons.mountable
a.ajax.button(href="/+" + viewUser.Nick + "/animelist/" + item.Status)
a.ajax.button(href="/animelist/" + item.Status)
Icon("list")
span View collection
a.ajax.button(href=anime.Link())
Icon("search-plus")
span View anime
button.action(data-action="removeAnimeFromCollection", data-trigger="click", data-anime-id=anime.ID, data-user-id=viewUser.ID, data-user-nick=viewUser.Nick)
button.action(data-action="removeAnimeFromCollection", data-trigger="click", data-api="/api/animelist/" + viewUser.ID, data-field="Items", data-index="AnimeID=\"" + anime.ID + "\"", data-nick=viewUser.Nick)
Icon("trash")
span Remove from collection

View File

@ -54,11 +54,11 @@ component ProfileHeader(viewUser *arn.User, user *arn.User, uri string)
.profile-actions
if user.ID != viewUser.ID
if !user.Follows().Contains(viewUser.ID)
button.profile-action.action(data-action="followUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/add", data-view-user-id=viewUser.ID)
button.profile-action.action(data-action="followUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/add/" + viewUser.ID)
Icon("user-plus")
span Follow
else
button.profile-action.action(data-action="unfollowUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/remove", data-view-user-id=viewUser.ID)
button.profile-action.action(data-action="unfollowUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/remove/" + viewUser.ID)
Icon("user-times")
span Unfollow

View File

@ -5,7 +5,7 @@ import { findAll } from "./Utils"
// Follow user
export function followUser(arn: AnimeNotifier, elem: HTMLElement) {
return arn.post(elem.dataset.api, elem.dataset.viewUserId)
return arn.post(elem.dataset.api, "")
.then(() => arn.reloadContent())
.then(() => arn.statusMessage.showInfo("You are now following " + arn.app.find("nick").innerText + "."))
.catch(err => arn.statusMessage.showError(err))
@ -13,7 +13,7 @@ export function followUser(arn: AnimeNotifier, elem: HTMLElement) {
// Unfollow user
export function unfollowUser(arn: AnimeNotifier, elem: HTMLElement) {
return arn.post(elem.dataset.api, elem.dataset.viewUserId)
return arn.post(elem.dataset.api, "")
.then(() => arn.reloadContent())
.then(() => arn.statusMessage.showInfo("You stopped following " + arn.app.find("nick").innerText + "."))
.catch(err => arn.statusMessage.showError(err))
@ -254,52 +254,14 @@ export function testNotification(arn: 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,
credentials: "same-origin"
})
.then(response => response.text())
.then(body => {
if(body !== "ok") {
throw body
}
return arn.reloadContent()
})
.catch(err => arn.statusMessage.showError(err))
.then(() => arn.loading(false))
}
// Remove anime from collection
export function removeAnimeFromCollection(arn: AnimeNotifier, button: HTMLElement) {
button.innerText = "Removing..."
arn.loading(true)
export function removeAnimeFromCollection(arn: AnimeNotifier, element: HTMLElement) {
let {field, index, nick} = element.dataset
let apiEndpoint = arn.findAPIEndpoint(element)
let {animeId, userId, userNick} = button.dataset
fetch("/api/animelist/" + userId + "/remove", {
method: "POST",
body: animeId,
credentials: "same-origin"
})
.then(response => response.text())
.then(body => {
if(body !== "ok") {
throw body
}
return arn.app.load("/+" + userNick + "/animelist/" + (arn.app.find("Status") as HTMLSelectElement).value)
})
arn.post(apiEndpoint + "/field/" + field + "/remove/" + index, "")
.then(() => arn.app.load("/+" + nick + "/animelist/" + (arn.app.find("Status") as HTMLSelectElement).value))
.catch(err => arn.statusMessage.showError(err))
.then(() => arn.loading(false))
}
// Charge up
@ -368,7 +330,7 @@ export function buyItem(arn: AnimeNotifier, button: HTMLElement) {
// Append new element to array
export function arrayAppend(arn: AnimeNotifier, element: HTMLElement) {
let field = element.dataset.field
let object = element.dataset.object ? JSON.parse(element.dataset.object) : {}
let object = element.dataset.object || ""
let apiEndpoint = arn.findAPIEndpoint(element)
arn.post(apiEndpoint + "/field/" + field + "/append", object)

View File

@ -665,7 +665,7 @@ export class AnimeNotifier {
.catch(console.error)
}
post(url, body) {
post(url: string, body: any) {
if(typeof body !== "string") {
body = JSON.stringify(body)
}