684 lines
20 KiB
Go
684 lines
20 KiB
Go
package plugin_ui
|
|
|
|
import (
|
|
"fmt"
|
|
"seanime/internal/util/result"
|
|
|
|
"github.com/dop251/goja"
|
|
"github.com/goccy/go-json"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
MaxActionsPerType = 3 // A plugin can only at most X actions of a certain type
|
|
)
|
|
|
|
// ActionManager
|
|
//
|
|
// Actions are buttons, dropdown items, and context menu items that are displayed in certain places in the UI.
|
|
// They are defined in the plugin code and are used to trigger events.
|
|
//
|
|
// The ActionManager is responsible for registering, rendering, and handling events for actions.
|
|
type ActionManager struct {
|
|
ctx *Context
|
|
|
|
animePageButtons *result.Map[string, *AnimePageButton]
|
|
animePageDropdownItems *result.Map[string, *AnimePageDropdownMenuItem]
|
|
animeLibraryDropdownItems *result.Map[string, *AnimeLibraryDropdownMenuItem]
|
|
mangaPageButtons *result.Map[string, *MangaPageButton]
|
|
mediaCardContextMenuItems *result.Map[string, *MediaCardContextMenuItem]
|
|
episodeCardContextMenuItems *result.Map[string, *EpisodeCardContextMenuItem]
|
|
episodeGridItemMenuItems *result.Map[string, *EpisodeGridItemMenuItem]
|
|
}
|
|
|
|
type BaseActionProps struct {
|
|
ID string `json:"id"`
|
|
Label string `json:"label"`
|
|
Style map[string]string `json:"style,omitempty"`
|
|
}
|
|
|
|
// Base action struct that all action types embed
|
|
type BaseAction struct {
|
|
BaseActionProps
|
|
}
|
|
|
|
// GetProps returns the base action properties
|
|
func (a *BaseAction) GetProps() BaseActionProps {
|
|
return a.BaseActionProps
|
|
}
|
|
|
|
// SetProps sets the base action properties
|
|
func (a *BaseAction) SetProps(props BaseActionProps) {
|
|
a.BaseActionProps = props
|
|
}
|
|
|
|
// UnmountAll unmounts all actions
|
|
// It should be called when the plugin is unloaded
|
|
func (a *ActionManager) UnmountAll() {
|
|
|
|
if a.animePageButtons.ClearN() > 0 {
|
|
a.renderAnimePageButtons()
|
|
}
|
|
if a.animePageDropdownItems.ClearN() > 0 {
|
|
a.renderAnimePageDropdownItems()
|
|
}
|
|
if a.animeLibraryDropdownItems.ClearN() > 0 {
|
|
a.renderAnimeLibraryDropdownItems()
|
|
}
|
|
if a.mangaPageButtons.ClearN() > 0 {
|
|
a.renderMangaPageButtons()
|
|
}
|
|
if a.mediaCardContextMenuItems.ClearN() > 0 {
|
|
a.renderMediaCardContextMenuItems()
|
|
}
|
|
if a.episodeCardContextMenuItems.ClearN() > 0 {
|
|
a.renderEpisodeCardContextMenuItems()
|
|
}
|
|
if a.episodeGridItemMenuItems.ClearN() > 0 {
|
|
a.renderEpisodeGridItemMenuItems()
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type AnimePageButton struct {
|
|
BaseAction
|
|
Intent string `json:"intent,omitempty"`
|
|
}
|
|
|
|
func (a *AnimePageButton) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
|
|
_ = obj.Set("setIntent", func(intent string) {
|
|
a.Intent = intent
|
|
})
|
|
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type EpisodeCardContextMenuItem struct {
|
|
BaseAction
|
|
}
|
|
|
|
func (a *EpisodeCardContextMenuItem) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type EpisodeGridItemMenuItem struct {
|
|
BaseAction
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
func (a *EpisodeGridItemMenuItem) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type MangaPageButton struct {
|
|
BaseAction
|
|
Intent string `json:"intent,omitempty"`
|
|
}
|
|
|
|
func (a *MangaPageButton) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
|
|
_ = obj.Set("setIntent", func(intent string) {
|
|
a.Intent = intent
|
|
})
|
|
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type AnimePageDropdownMenuItem struct {
|
|
BaseAction
|
|
}
|
|
|
|
func (a *AnimePageDropdownMenuItem) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type AnimeLibraryDropdownMenuItem struct {
|
|
BaseAction
|
|
}
|
|
|
|
func (a *AnimeLibraryDropdownMenuItem) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type MediaCardContextMenuItemFor string
|
|
|
|
const (
|
|
MediaCardContextMenuItemForAnime MediaCardContextMenuItemFor = "anime"
|
|
MediaCardContextMenuItemForManga MediaCardContextMenuItemFor = "manga"
|
|
MediaCardContextMenuItemForBoth MediaCardContextMenuItemFor = "both"
|
|
)
|
|
|
|
type MediaCardContextMenuItem struct {
|
|
BaseAction
|
|
For MediaCardContextMenuItemFor `json:"for"` // anime, manga, both
|
|
}
|
|
|
|
func (a *MediaCardContextMenuItem) CreateObject(actionManager *ActionManager) *goja.Object {
|
|
obj := actionManager.ctx.vm.NewObject()
|
|
actionManager.bindSharedToObject(obj, a)
|
|
|
|
_ = obj.Set("setFor", func(_for MediaCardContextMenuItemFor) {
|
|
a.For = _for
|
|
})
|
|
|
|
return obj
|
|
}
|
|
|
|
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func NewActionManager(ctx *Context) *ActionManager {
|
|
return &ActionManager{
|
|
ctx: ctx,
|
|
|
|
animePageButtons: result.NewResultMap[string, *AnimePageButton](),
|
|
animeLibraryDropdownItems: result.NewResultMap[string, *AnimeLibraryDropdownMenuItem](),
|
|
animePageDropdownItems: result.NewResultMap[string, *AnimePageDropdownMenuItem](),
|
|
mangaPageButtons: result.NewResultMap[string, *MangaPageButton](),
|
|
mediaCardContextMenuItems: result.NewResultMap[string, *MediaCardContextMenuItem](),
|
|
episodeCardContextMenuItems: result.NewResultMap[string, *EpisodeCardContextMenuItem](),
|
|
episodeGridItemMenuItems: result.NewResultMap[string, *EpisodeGridItemMenuItem](),
|
|
}
|
|
}
|
|
|
|
// renderAnimePageButtons is called when the client requests the buttons to display on the anime page.
|
|
func (a *ActionManager) renderAnimePageButtons() {
|
|
buttons := make([]*AnimePageButton, 0)
|
|
a.animePageButtons.Range(func(key string, value *AnimePageButton) bool {
|
|
buttons = append(buttons, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderAnimePageButtonsEvent, ServerActionRenderAnimePageButtonsEventPayload{
|
|
Buttons: buttons,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderAnimePageDropdownItems() {
|
|
items := make([]*AnimePageDropdownMenuItem, 0)
|
|
a.animePageDropdownItems.Range(func(key string, value *AnimePageDropdownMenuItem) bool {
|
|
items = append(items, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderAnimePageDropdownItemsEvent, ServerActionRenderAnimePageDropdownItemsEventPayload{
|
|
Items: items,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderAnimeLibraryDropdownItems() {
|
|
items := make([]*AnimeLibraryDropdownMenuItem, 0)
|
|
a.animeLibraryDropdownItems.Range(func(key string, value *AnimeLibraryDropdownMenuItem) bool {
|
|
items = append(items, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderAnimeLibraryDropdownItemsEvent, ServerActionRenderAnimeLibraryDropdownItemsEventPayload{
|
|
Items: items,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderMangaPageButtons() {
|
|
buttons := make([]*MangaPageButton, 0)
|
|
a.mangaPageButtons.Range(func(key string, value *MangaPageButton) bool {
|
|
buttons = append(buttons, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderMangaPageButtonsEvent, ServerActionRenderMangaPageButtonsEventPayload{
|
|
Buttons: buttons,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderMediaCardContextMenuItems() {
|
|
items := make([]*MediaCardContextMenuItem, 0)
|
|
a.mediaCardContextMenuItems.Range(func(key string, value *MediaCardContextMenuItem) bool {
|
|
items = append(items, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderMediaCardContextMenuItemsEvent, ServerActionRenderMediaCardContextMenuItemsEventPayload{
|
|
Items: items,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderEpisodeCardContextMenuItems() {
|
|
items := make([]*EpisodeCardContextMenuItem, 0)
|
|
a.episodeCardContextMenuItems.Range(func(key string, value *EpisodeCardContextMenuItem) bool {
|
|
items = append(items, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderEpisodeCardContextMenuItemsEvent, ServerActionRenderEpisodeCardContextMenuItemsEventPayload{
|
|
Items: items,
|
|
})
|
|
}
|
|
|
|
func (a *ActionManager) renderEpisodeGridItemMenuItems() {
|
|
items := make([]*EpisodeGridItemMenuItem, 0)
|
|
a.episodeGridItemMenuItems.Range(func(key string, value *EpisodeGridItemMenuItem) bool {
|
|
items = append(items, value)
|
|
return true
|
|
})
|
|
|
|
a.ctx.SendEventToClient(ServerActionRenderEpisodeGridItemMenuItemsEvent, ServerActionRenderEpisodeGridItemMenuItemsEventPayload{
|
|
Items: items,
|
|
})
|
|
}
|
|
|
|
// bind binds 'action' to the ctx object
|
|
//
|
|
// Example:
|
|
// ctx.action.newAnimePageButton(...)
|
|
func (a *ActionManager) bind(ctxObj *goja.Object) {
|
|
actionObj := a.ctx.vm.NewObject()
|
|
_ = actionObj.Set("newAnimePageButton", a.jsNewAnimePageButton)
|
|
_ = actionObj.Set("newAnimePageDropdownItem", a.jsNewAnimePageDropdownItem)
|
|
_ = actionObj.Set("newAnimeLibraryDropdownItem", a.jsNewAnimeLibraryDropdownItem)
|
|
_ = actionObj.Set("newMediaCardContextMenuItem", a.jsNewMediaCardContextMenuItem)
|
|
_ = actionObj.Set("newMangaPageButton", a.jsNewMangaPageButton)
|
|
_ = actionObj.Set("newEpisodeCardContextMenuItem", a.jsNewEpisodeCardContextMenuItem)
|
|
_ = actionObj.Set("newEpisodeGridItemMenuItem", a.jsNewEpisodeGridItemMenuItem)
|
|
_ = ctxObj.Set("action", actionObj)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Actions
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewEpisodeCardContextMenuItem
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newEpisodeCardContextMenuItem({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewEpisodeCardContextMenuItem(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &EpisodeCardContextMenuItem{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewEpisodeGridItemMenuItem
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newEpisodeGridItemContextMenuItem({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// type: "library",
|
|
// })
|
|
func (a *ActionManager) jsNewEpisodeGridItemMenuItem(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &EpisodeGridItemMenuItem{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewAnimePageButton
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newAnimePageButton({
|
|
// label: "Download",
|
|
// intent: "primary",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewAnimePageButton(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &AnimePageButton{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewAnimePageDropdownItem
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newAnimePageDropdownItem({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewAnimePageDropdownItem(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &AnimePageDropdownMenuItem{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewAnimeLibraryDropdownItem
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newAnimeLibraryDropdownItem({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewAnimeLibraryDropdownItem(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &AnimeLibraryDropdownMenuItem{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewMediaCardContextMenuItem
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newMediaCardContextMenuItem({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewMediaCardContextMenuItem(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &MediaCardContextMenuItem{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// jsNewMangaPageButton
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newMangaPageButton({
|
|
// label: "Download",
|
|
// onClick: "download-button-clicked",
|
|
// })
|
|
func (a *ActionManager) jsNewMangaPageButton(call goja.FunctionCall) goja.Value {
|
|
// Create a new action
|
|
action := &MangaPageButton{}
|
|
|
|
// Get the props
|
|
a.unmarshalProps(call, action)
|
|
action.ID = uuid.New().String()
|
|
|
|
// Create the object
|
|
obj := action.CreateObject(a)
|
|
return obj
|
|
}
|
|
|
|
// ///////////////////////////////////////////////////////////////////////////////////
|
|
// Shared
|
|
// ///////////////////////////////////////////////////////////////////////////////////
|
|
// bindSharedToObject binds shared methods to action objects
|
|
//
|
|
// Example:
|
|
// const downloadButton = ctx.newAnimePageButton(...)
|
|
// downloadButton.mount()
|
|
// downloadButton.unmount()
|
|
// downloadButton.setLabel("Downloading...")
|
|
func (a *ActionManager) bindSharedToObject(obj *goja.Object, action interface{}) {
|
|
var id string
|
|
var props BaseActionProps
|
|
var mapToUse interface{}
|
|
|
|
switch act := action.(type) {
|
|
case *AnimePageButton:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.animePageButtons
|
|
case *MangaPageButton:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.mangaPageButtons
|
|
case *AnimePageDropdownMenuItem:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.animePageDropdownItems
|
|
case *AnimeLibraryDropdownMenuItem:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.animeLibraryDropdownItems
|
|
case *MediaCardContextMenuItem:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.mediaCardContextMenuItems
|
|
case *EpisodeCardContextMenuItem:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.episodeCardContextMenuItems
|
|
case *EpisodeGridItemMenuItem:
|
|
id = act.ID
|
|
props = act.GetProps()
|
|
mapToUse = a.episodeGridItemMenuItems
|
|
}
|
|
|
|
_ = obj.Set("mount", func() {
|
|
switch m := mapToUse.(type) {
|
|
case *result.Map[string, *AnimePageButton]:
|
|
if btn, ok := action.(*AnimePageButton); ok {
|
|
m.Set(id, btn)
|
|
a.renderAnimePageButtons()
|
|
}
|
|
case *result.Map[string, *MangaPageButton]:
|
|
if btn, ok := action.(*MangaPageButton); ok {
|
|
m.Set(id, btn)
|
|
a.renderMangaPageButtons()
|
|
}
|
|
case *result.Map[string, *AnimePageDropdownMenuItem]:
|
|
if item, ok := action.(*AnimePageDropdownMenuItem); ok {
|
|
m.Set(id, item)
|
|
a.renderAnimePageDropdownItems()
|
|
}
|
|
case *result.Map[string, *AnimeLibraryDropdownMenuItem]:
|
|
if item, ok := action.(*AnimeLibraryDropdownMenuItem); ok {
|
|
m.Set(id, item)
|
|
a.renderAnimeLibraryDropdownItems()
|
|
}
|
|
case *result.Map[string, *MediaCardContextMenuItem]:
|
|
if item, ok := action.(*MediaCardContextMenuItem); ok {
|
|
if item.For == "" {
|
|
item.For = MediaCardContextMenuItemForBoth
|
|
}
|
|
m.Set(id, item)
|
|
a.renderMediaCardContextMenuItems()
|
|
}
|
|
case *result.Map[string, *EpisodeCardContextMenuItem]:
|
|
if item, ok := action.(*EpisodeCardContextMenuItem); ok {
|
|
m.Set(id, item)
|
|
a.renderEpisodeCardContextMenuItems()
|
|
}
|
|
case *result.Map[string, *EpisodeGridItemMenuItem]:
|
|
if item, ok := action.(*EpisodeGridItemMenuItem); ok {
|
|
m.Set(id, item)
|
|
a.renderEpisodeGridItemMenuItems()
|
|
}
|
|
}
|
|
})
|
|
|
|
_ = obj.Set("unmount", func() {
|
|
switch m := mapToUse.(type) {
|
|
case *result.Map[string, *AnimePageButton]:
|
|
m.Delete(id)
|
|
a.renderAnimePageButtons()
|
|
case *result.Map[string, *MangaPageButton]:
|
|
m.Delete(id)
|
|
a.renderMangaPageButtons()
|
|
case *result.Map[string, *AnimePageDropdownMenuItem]:
|
|
m.Delete(id)
|
|
a.renderAnimePageDropdownItems()
|
|
case *result.Map[string, *AnimeLibraryDropdownMenuItem]:
|
|
m.Delete(id)
|
|
a.renderAnimeLibraryDropdownItems()
|
|
case *result.Map[string, *MediaCardContextMenuItem]:
|
|
m.Delete(id)
|
|
a.renderMediaCardContextMenuItems()
|
|
case *result.Map[string, *EpisodeCardContextMenuItem]:
|
|
m.Delete(id)
|
|
a.renderEpisodeCardContextMenuItems()
|
|
case *result.Map[string, *EpisodeGridItemMenuItem]:
|
|
m.Delete(id)
|
|
a.renderEpisodeGridItemMenuItems()
|
|
}
|
|
})
|
|
|
|
_ = obj.Set("setLabel", func(label string) {
|
|
newProps := props
|
|
newProps.Label = label
|
|
|
|
switch act := action.(type) {
|
|
case *AnimePageButton:
|
|
act.SetProps(newProps)
|
|
case *MangaPageButton:
|
|
act.SetProps(newProps)
|
|
case *AnimePageDropdownMenuItem:
|
|
act.SetProps(newProps)
|
|
case *AnimeLibraryDropdownMenuItem:
|
|
act.SetProps(newProps)
|
|
case *MediaCardContextMenuItem:
|
|
act.SetProps(newProps)
|
|
case *EpisodeCardContextMenuItem:
|
|
act.SetProps(newProps)
|
|
case *EpisodeGridItemMenuItem:
|
|
act.SetProps(newProps)
|
|
}
|
|
|
|
})
|
|
|
|
_ = obj.Set("setStyle", func(style map[string]string) {
|
|
newProps := props
|
|
newProps.Style = style
|
|
|
|
switch act := action.(type) {
|
|
case *AnimePageButton:
|
|
act.SetProps(newProps)
|
|
a.renderAnimePageButtons()
|
|
case *MangaPageButton:
|
|
act.SetProps(newProps)
|
|
a.renderMangaPageButtons()
|
|
case *AnimePageDropdownMenuItem:
|
|
act.SetProps(newProps)
|
|
a.renderAnimePageDropdownItems()
|
|
case *AnimeLibraryDropdownMenuItem:
|
|
act.SetProps(newProps)
|
|
a.renderAnimeLibraryDropdownItems()
|
|
case *MediaCardContextMenuItem:
|
|
act.SetProps(newProps)
|
|
a.renderMediaCardContextMenuItems()
|
|
case *EpisodeCardContextMenuItem:
|
|
act.SetProps(newProps)
|
|
a.renderEpisodeCardContextMenuItems()
|
|
case *EpisodeGridItemMenuItem:
|
|
act.SetProps(newProps)
|
|
}
|
|
})
|
|
|
|
_ = obj.Set("onClick", func(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
a.ctx.handleTypeError("onClick requires a callback function")
|
|
}
|
|
|
|
callback, ok := goja.AssertFunction(call.Argument(0))
|
|
if !ok {
|
|
a.ctx.handleTypeError("onClick requires a callback function")
|
|
}
|
|
|
|
eventListener := a.ctx.RegisterEventListener(ClientActionClickedEvent)
|
|
|
|
eventListener.SetCallback(func(event *ClientPluginEvent) {
|
|
payload := ClientActionClickedEventPayload{}
|
|
if event.ParsePayloadAs(ClientActionClickedEvent, &payload) && payload.ActionID == id {
|
|
a.ctx.scheduler.ScheduleAsync(func() error {
|
|
_, err := callback(goja.Undefined(), a.ctx.vm.ToValue(payload.Event))
|
|
return err
|
|
})
|
|
}
|
|
})
|
|
|
|
return goja.Undefined()
|
|
})
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// Utils
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func (a *ActionManager) unmarshalProps(call goja.FunctionCall, ret interface{}) {
|
|
if len(call.Arguments) < 1 {
|
|
a.ctx.handleException(fmt.Errorf("expected 1 argument"))
|
|
}
|
|
|
|
props := call.Arguments[0].Export()
|
|
if props == nil {
|
|
a.ctx.handleException(fmt.Errorf("expected props object"))
|
|
}
|
|
|
|
marshaled, err := json.Marshal(props)
|
|
if err != nil {
|
|
a.ctx.handleException(err)
|
|
}
|
|
|
|
err = json.Unmarshal(marshaled, ret)
|
|
if err != nil {
|
|
a.ctx.handleException(err)
|
|
}
|
|
}
|