Added ability to upload new character images

This commit is contained in:
Eduard Urbach 2018-04-22 15:24:17 +02:00
parent 7807aec249
commit 1f26c44a49
13 changed files with 111 additions and 18 deletions

View File

@ -1,9 +1,9 @@
component Character(character *arn.Character) component Character(character *arn.Character)
a.character(href="/character/" + character.ID) a.character(href="/character/" + character.ID)
img.character-image.lazy(data-src=character.ImageLink("medium"), data-webp="true", data-color=character.AverageColor(), alt=character.Name.Canonical) img.character-image-medium.lazy(data-src=character.ImageLink("medium"), data-webp="true", data-color=character.AverageColor(), alt=character.Name.Canonical)
.image-title .image-title
.image-title-text= character.Name.Canonical .image-title-text= character.Name.Canonical
component CharacterSmall(character *arn.Character) component CharacterSmall(character *arn.Character)
a.character.tip(href="/character/" + character.ID, aria-label=character.Name.Canonical) a.character.tip(href="/character/" + character.ID, aria-label=character.Name.Canonical)
img.character-image.character-image-small.lazy(data-src=character.ImageLink("small"), data-webp="true", data-color=character.AverageColor(), alt=character.Name.Canonical) img.character-image-small.lazy(data-src=character.ImageLink("small"), data-webp="true", data-color=character.AverageColor(), alt=character.Name.Canonical)

View File

@ -12,13 +12,16 @@
.image-title .image-title
opacity 1 opacity 1
.character-image .character-image-medium
width 112px
height 112px
border-radius 5% border-radius 5%
box-shadow shadow-medium box-shadow shadow-medium
object-fit cover object-fit cover
width 112px
height 112px
.character-image-small .character-image-small
width 56px width 56px
height 56px height 56px
border-radius ui-element-border-radius
box-shadow shadow-light
object-fit cover

View File

@ -2,6 +2,7 @@ component CharacterTabs(character *arn.Character, user *arn.User)
.tabs .tabs
Tab("Character", "user", character.Link()) Tab("Character", "user", character.Link())
Tab("Edit", "pencil", character.Link() + "/edit") Tab("Edit", "pencil", character.Link() + "/edit")
Tab("Images", "image", character.Link() + "/edit/images")
Tab("History", "history", character.Link() + "/history") Tab("History", "history", character.Link() + "/history")
component CharacterDetails(character *arn.Character, characterAnime []*arn.Anime, quotes []*arn.Quote, friends []*arn.User, mainQuote *arn.Quote, user *arn.User) component CharacterDetails(character *arn.Character, characterAnime []*arn.Anime, quotes []*arn.Quote, friends []*arn.User, mainQuote *arn.Quote, user *arn.User)

View File

@ -21,6 +21,7 @@
.character-image-large .character-image-large
width 219px width 219px
height 344px height 344px
object-fit cover
border-radius ui-element-border-radius border-radius ui-element-border-radius
box-shadow shadow-light box-shadow shadow-light

View File

@ -22,3 +22,16 @@ func Edit(ctx *aero.Context) string {
return ctx.HTML(components.CharacterTabs(character, user) + editform.Render(character, "Edit character", user)) return ctx.HTML(components.CharacterTabs(character, user) + editform.Render(character, "Edit character", user))
} }
// EditImages renders the form to edit the character images.
func EditImages(ctx *aero.Context) string {
id := ctx.Get("id")
character, err := arn.GetCharacter(id)
user := utils.GetUser(ctx)
if err != nil {
return ctx.Error(http.StatusNotFound, "Character not found", err)
}
return ctx.HTML(components.EditCharacterImages(character, user))
}

View File

@ -0,0 +1,21 @@
component EditCharacterImages(character *arn.Character, user *arn.User)
CharacterTabs(character, user)
.widget-form
h1.mountable Edit character images
.widget.mountable(data-api="/api/character/" + character.ID)
h3.widget-title
Icon("picture-o")
span Image
InputFileUpload("character-image-input", "File", "image", "/api/upload/character/" + character.ID + "/image")
.character-image-container.mountable
img.character-image-input-preview.character-image-large.lazy(data-src=character.ImageLink("large"), data-webp="true", data-color=character.AverageColor(), alt="Character image")
.character-image-container.mountable
img.character-image-input-preview.character-image-medium.lazy(data-src=character.ImageLink("medium"), data-webp="true", data-color=character.AverageColor(), alt="Character image")
.character-image-container.mountable
img.character-image-input-preview.character-image-small.lazy(data-src=character.ImageLink("small"), data-webp="true", data-color=character.AverageColor(), alt="Character image")

View File

@ -12,4 +12,4 @@ component EditAnimeImages(anime *arn.Anime)
InputFileUpload("anime-image-input", "File", "image", "/api/upload/anime/" + anime.ID + "/image") InputFileUpload("anime-image-input", "File", "image", "/api/upload/anime/" + anime.ID + "/image")
.anime-image-container .anime-image-container
img#anime-image-input-preview.anime-cover-image.lazy(data-src=anime.ImageLink("large"), data-webp="true", data-color=anime.AverageColor(), alt="Anime image") img.anime-image-input-preview.anime-cover-image.lazy(data-src=anime.ImageLink("large"), data-webp="true", data-color=anime.AverageColor(), alt="Anime image")

View File

@ -142,6 +142,7 @@ func Configure(app *aero.Application) {
// Characters // Characters
l.Page("/character/:id", character.Get) l.Page("/character/:id", character.Get)
l.Page("/character/:id/edit", character.Edit) l.Page("/character/:id/edit", character.Edit)
l.Page("/character/:id/edit/images", character.EditImages)
l.Page("/character/:id/history", character.History) l.Page("/character/:id/history", character.History)
// AMVs // AMVs
@ -274,6 +275,7 @@ func Configure(app *aero.Application) {
app.Post("/api/upload/avatar", upload.Avatar) app.Post("/api/upload/avatar", upload.Avatar)
app.Post("/api/upload/cover", upload.Cover) app.Post("/api/upload/cover", upload.Cover)
app.Post("/api/upload/anime/:id/image", upload.AnimeImage) app.Post("/api/upload/anime/:id/image", upload.AnimeImage)
app.Post("/api/upload/character/:id/image", upload.CharacterImage)
app.Post("/api/upload/amv/:id/file", upload.AMVFile) app.Post("/api/upload/amv/:id/file", upload.AMVFile)
// Admin // Admin

View File

@ -59,9 +59,9 @@ component SettingsPersonal(user *arn.User)
.profile-image-container.avatar-preview .profile-image-container.avatar-preview
if user.HasAvatar() if user.HasAvatar()
img#avatar-input-preview.profile-image.lazy(data-src=user.AvatarLink("large"), data-webp="true", alt="Profile image", title="Recommended: 560 x 560 | PNG or JPG") img.avatar-input-preview.profile-image.lazy(data-src=user.AvatarLink("large"), data-webp="true", alt="Profile image", title="Recommended: 560 x 560 | PNG or JPG")
else else
img#avatar-input-preview.profile-image.hidden(src=user.AvatarLink("large"), alt="Profile image", title="Recommended: 560 x 560 | PNG or JPG") img.avatar-input-preview.profile-image.hidden(src=user.AvatarLink("large"), alt="Profile image", title="Recommended: 560 x 560 | PNG or JPG")
#avatar-input-preview-svg #avatar-input-preview-svg
SVGProfileImage(user) SVGProfileImage(user)
@ -74,7 +74,7 @@ component SettingsPersonal(user *arn.User)
InputFileUpload("cover-input", "File", "image", "/api/upload/cover") InputFileUpload("cover-input", "File", "image", "/api/upload/cover")
.cover-preview(title="Recommended: 1920 x 450 | PNG or JPG") .cover-preview(title="Recommended: 1920 x 450 | PNG or JPG")
img#cover-input-preview.profile-cover.lazy(data-src=user.CoverLink("small"), data-webp="true", alt="Cover image") img.cover-input-preview.profile-cover.lazy(data-src=user.CoverLink("small"), data-webp="true", alt="Cover image")
if !user.IsPro() if !user.IsPro()
.footer .footer

View File

@ -23,7 +23,7 @@
position relative position relative
margin 0 auto margin 0 auto
#cover-input-preview .cover-input-preview
border-radius ui-element-border-radius border-radius ui-element-border-radius
filter none filter none

49
pages/upload/character.go Normal file
View File

@ -0,0 +1,49 @@
package upload
import (
"net/http"
"github.com/animenotifier/arn"
"github.com/aerogo/aero"
"github.com/animenotifier/notify.moe/utils"
)
// CharacterImage handles the character image upload.
func CharacterImage(ctx *aero.Context) string {
user := utils.GetUser(ctx)
characterID := ctx.Get("id")
if user == nil || (user.Role != "editor" && user.Role != "admin") {
return ctx.Error(http.StatusUnauthorized, "Not authorized", nil)
}
character, err := arn.GetCharacter(characterID)
if err != nil {
return ctx.Error(http.StatusNotFound, "Character not found", err)
}
// Retrieve file from post body
data, err := ctx.Request().Body().Bytes()
if err != nil {
return ctx.Error(http.StatusInternalServerError, "Reading request body failed", err)
}
// Set character image file
err = character.SetImageBytes(data)
if err != nil {
return ctx.Error(http.StatusInternalServerError, "Invalid image format", err)
}
// Save image information
character.Save()
// Write log entry
logEntry := arn.NewEditLogEntry(user.ID, "edit", "Character", character.ID, "Image", "", "")
logEntry.Save()
return "ok"
}

View File

@ -37,10 +37,10 @@ export function selectFile(arn: AnimeNotifier, button: HTMLButtonElement) {
// Preview image // Preview image
if(fileType === "image") { if(fileType === "image") {
let preview = document.getElementById(button.id + "-preview") as HTMLImageElement let previews = document.getElementsByClassName(button.id + "-preview")
if(preview) { for(let preview of previews) {
previewImage(file, endpoint, preview) previewImage(file, endpoint, previews)
} }
} }
@ -94,7 +94,7 @@ function uploadFile(file: File, fileType: string, endpoint: string, arn: AnimeNo
} }
// Preview image // Preview image
function previewImage(file: File, endpoint: string, preview: HTMLImageElement) { function previewImage(file: File, endpoint: string, previews: HTMLCollectionOf<Element>) {
let reader = new FileReader() let reader = new FileReader()
reader.onloadend = () => { reader.onloadend = () => {
@ -106,8 +106,11 @@ function previewImage(file: File, endpoint: string, preview: HTMLImageElement) {
} }
} }
preview.classList.remove("hidden") for(let preview of previews) {
preview.src = reader.result let img = preview as HTMLImageElement
img.classList.remove("hidden")
img.src = reader.result
}
} }
reader.readAsDataURL(file) reader.readAsDataURL(file)

View File

@ -170,7 +170,7 @@ func RenderField(b *bytes.Buffer, v *reflect.Value, field reflect.StructField, i
character, err := arn.GetCharacter(characterID) character, err := arn.GetCharacter(characterID)
if err == nil { if err == nil {
b.WriteString(components.EditFormImagePreview(character.Link(), character.ImageLink("small"), false)) b.WriteString(components.EditFormImagePreview(character.Link(), character.ImageLink("medium"), false))
} }
case "": case "":