diff --git a/pages/anime/character.pixy b/pages/anime/character.pixy index 95334b20..079627f0 100644 --- a/pages/anime/character.pixy +++ b/pages/anime/character.pixy @@ -1,9 +1,9 @@ component Character(character *arn.Character) 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-text= character.Name.Canonical component CharacterSmall(character *arn.Character) 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) \ No newline at end of file + img.character-image-small.lazy(data-src=character.ImageLink("small"), data-webp="true", data-color=character.AverageColor(), alt=character.Name.Canonical) \ No newline at end of file diff --git a/pages/anime/character.scarlet b/pages/anime/character.scarlet index 05d06f71..f6fc06c9 100644 --- a/pages/anime/character.scarlet +++ b/pages/anime/character.scarlet @@ -12,13 +12,16 @@ .image-title opacity 1 -.character-image +.character-image-medium + width 112px + height 112px border-radius 5% box-shadow shadow-medium object-fit cover - width 112px - height 112px .character-image-small width 56px - height 56px \ No newline at end of file + height 56px + border-radius ui-element-border-radius + box-shadow shadow-light + object-fit cover \ No newline at end of file diff --git a/pages/character/character.pixy b/pages/character/character.pixy index 747a4a1f..1c5dda31 100644 --- a/pages/character/character.pixy +++ b/pages/character/character.pixy @@ -2,6 +2,7 @@ component CharacterTabs(character *arn.Character, user *arn.User) .tabs Tab("Character", "user", character.Link()) Tab("Edit", "pencil", character.Link() + "/edit") + Tab("Images", "image", character.Link() + "/edit/images") 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) diff --git a/pages/character/character.scarlet b/pages/character/character.scarlet index 1229d86b..1e785ff9 100644 --- a/pages/character/character.scarlet +++ b/pages/character/character.scarlet @@ -21,6 +21,7 @@ .character-image-large width 219px height 344px + object-fit cover border-radius ui-element-border-radius box-shadow shadow-light diff --git a/pages/character/edit.go b/pages/character/edit.go index cf16dc34..87cc4bec 100644 --- a/pages/character/edit.go +++ b/pages/character/edit.go @@ -22,3 +22,16 @@ func Edit(ctx *aero.Context) string { 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)) +} diff --git a/pages/character/images.pixy b/pages/character/images.pixy new file mode 100644 index 00000000..b8596926 --- /dev/null +++ b/pages/character/images.pixy @@ -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") \ No newline at end of file diff --git a/pages/editanime/images.pixy b/pages/editanime/images.pixy index 08b08384..9d98a813 100644 --- a/pages/editanime/images.pixy +++ b/pages/editanime/images.pixy @@ -12,4 +12,4 @@ component EditAnimeImages(anime *arn.Anime) InputFileUpload("anime-image-input", "File", "image", "/api/upload/anime/" + anime.ID + "/image") .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") \ No newline at end of file + img.anime-image-input-preview.anime-cover-image.lazy(data-src=anime.ImageLink("large"), data-webp="true", data-color=anime.AverageColor(), alt="Anime image") \ No newline at end of file diff --git a/pages/index.go b/pages/index.go index de537b43..3529394e 100644 --- a/pages/index.go +++ b/pages/index.go @@ -142,6 +142,7 @@ func Configure(app *aero.Application) { // Characters l.Page("/character/:id", character.Get) l.Page("/character/:id/edit", character.Edit) + l.Page("/character/:id/edit/images", character.EditImages) l.Page("/character/:id/history", character.History) // AMVs @@ -274,6 +275,7 @@ func Configure(app *aero.Application) { app.Post("/api/upload/avatar", upload.Avatar) app.Post("/api/upload/cover", upload.Cover) 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) // Admin diff --git a/pages/settings/settings.pixy b/pages/settings/settings.pixy index 472c48cb..97bc7d75 100644 --- a/pages/settings/settings.pixy +++ b/pages/settings/settings.pixy @@ -59,9 +59,9 @@ component SettingsPersonal(user *arn.User) .profile-image-container.avatar-preview 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 - 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 SVGProfileImage(user) @@ -74,7 +74,7 @@ component SettingsPersonal(user *arn.User) InputFileUpload("cover-input", "File", "image", "/api/upload/cover") .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() .footer diff --git a/pages/settings/settings.scarlet b/pages/settings/settings.scarlet index bf9578a0..b6113f40 100644 --- a/pages/settings/settings.scarlet +++ b/pages/settings/settings.scarlet @@ -23,7 +23,7 @@ position relative margin 0 auto -#cover-input-preview +.cover-input-preview border-radius ui-element-border-radius filter none diff --git a/pages/upload/character.go b/pages/upload/character.go new file mode 100644 index 00000000..7c5078af --- /dev/null +++ b/pages/upload/character.go @@ -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" +} diff --git a/scripts/Actions/Upload.ts b/scripts/Actions/Upload.ts index fdbbb1e5..4b31e0d6 100644 --- a/scripts/Actions/Upload.ts +++ b/scripts/Actions/Upload.ts @@ -37,10 +37,10 @@ export function selectFile(arn: AnimeNotifier, button: HTMLButtonElement) { // Preview image if(fileType === "image") { - let preview = document.getElementById(button.id + "-preview") as HTMLImageElement + let previews = document.getElementsByClassName(button.id + "-preview") - if(preview) { - previewImage(file, endpoint, preview) + for(let preview of previews) { + previewImage(file, endpoint, previews) } } @@ -94,7 +94,7 @@ function uploadFile(file: File, fileType: string, endpoint: string, arn: AnimeNo } // Preview image -function previewImage(file: File, endpoint: string, preview: HTMLImageElement) { +function previewImage(file: File, endpoint: string, previews: HTMLCollectionOf) { let reader = new FileReader() reader.onloadend = () => { @@ -106,8 +106,11 @@ function previewImage(file: File, endpoint: string, preview: HTMLImageElement) { } } - preview.classList.remove("hidden") - preview.src = reader.result + for(let preview of previews) { + let img = preview as HTMLImageElement + img.classList.remove("hidden") + img.src = reader.result + } } reader.readAsDataURL(file) diff --git a/utils/editform/editform.go b/utils/editform/editform.go index 8acdf44f..1a05598d 100644 --- a/utils/editform/editform.go +++ b/utils/editform/editform.go @@ -170,7 +170,7 @@ func RenderField(b *bytes.Buffer, v *reflect.Value, field reflect.StructField, i character, err := arn.GetCharacter(characterID) if err == nil { - b.WriteString(components.EditFormImagePreview(character.Link(), character.ImageLink("small"), false)) + b.WriteString(components.EditFormImagePreview(character.Link(), character.ImageLink("medium"), false)) } case "":