action.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package app
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. // Action represents a custom event that can be propagated across the app. It
  7. // can contain a payload and be given additional context with tags.
  8. type Action struct {
  9. // The name that identifies the action..
  10. Name string
  11. // The value passed along with the action. Can be nil.
  12. Value any
  13. // Tags that provide some context to the action.
  14. Tags Tags
  15. }
  16. // ActionHandler represents a handler that is executed when an action is created
  17. // with Context.NewAction().
  18. type ActionHandler func(Context, Action)
  19. // Handle registers the handler for the given action name. When an action
  20. // occurs, the handler is executed on its own goroutine.
  21. func Handle(actionName string, h ActionHandler) {
  22. actionHandlers[actionName] = h
  23. }
  24. var actionHandlers = make(map[string]ActionHandler)
  25. type actionHandler struct {
  26. async bool
  27. source UI
  28. function ActionHandler
  29. }
  30. type actionManager struct {
  31. once sync.Once
  32. mutex sync.Mutex
  33. handlers map[string]map[string]actionHandler
  34. }
  35. func (m *actionManager) init() {
  36. m.handlers = make(map[string]map[string]actionHandler)
  37. }
  38. func (m *actionManager) post(a Action) {
  39. m.once.Do(m.init)
  40. m.mutex.Lock()
  41. defer m.mutex.Unlock()
  42. handlers := m.handlers[a.Name]
  43. for key, h := range handlers {
  44. source := h.source
  45. if !source.Mounted() {
  46. delete(handlers, key)
  47. continue
  48. }
  49. ctx := makeContext(source)
  50. function := h.function
  51. if h.async {
  52. ctx.Async(func() { function(ctx, a) })
  53. } else {
  54. ctx.Dispatch(func(ctx Context) { function(ctx, a) })
  55. }
  56. }
  57. }
  58. func (m *actionManager) handle(actionName string, async bool, source UI, h ActionHandler) {
  59. m.once.Do(m.init)
  60. m.mutex.Lock()
  61. defer m.mutex.Unlock()
  62. handlers, isRegistered := m.handlers[actionName]
  63. if !isRegistered {
  64. handlers = make(map[string]actionHandler)
  65. m.handlers[actionName] = handlers
  66. }
  67. key := fmt.Sprintf("/%T:%p/%p", source, source, h)
  68. handlers[key] = actionHandler{
  69. async: async,
  70. source: source,
  71. function: h,
  72. }
  73. }
  74. func (m *actionManager) closeUnusedHandlers() {
  75. m.mutex.Lock()
  76. defer m.mutex.Unlock()
  77. for actionName, handlers := range m.handlers {
  78. for key, h := range handlers {
  79. if !h.source.Mounted() {
  80. delete(handlers, key)
  81. }
  82. }
  83. if len(handlers) == 0 {
  84. delete(m.handlers, actionName)
  85. }
  86. }
  87. }