Added ability to upload new character images
This commit is contained in:
parent
7807aec249
commit
1f26c44a49
@ -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)
|
@ -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
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
21
pages/character/images.pixy
Normal file
21
pages/character/images.pixy
Normal 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")
|
@ -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")
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
49
pages/upload/character.go
Normal 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"
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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 "":
|
||||||
|
Loading…
Reference in New Issue
Block a user