tree_router.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. //go:build js && wasm
  2. package ultraviolet
  3. import (
  4. "context"
  5. "fmt"
  6. "sync"
  7. fw "git.buran.team/fairwind"
  8. )
  9. type Parameters map[string]any
  10. type NodeBuilder func(uvc *UVContext, parameters Parameters) (*Node, error)
  11. type Route struct {
  12. Default bool
  13. Builder NodeBuilder
  14. name string // NOTE: sets automatically
  15. parameters Parameters // NOTE: sets automatically
  16. }
  17. type Routing map[string]*Route
  18. type Router struct {
  19. ctx context.Context
  20. log *fw.Log
  21. mutex sync.Mutex
  22. uvc *UVContext
  23. root *Node
  24. routing Routing
  25. stack []Route
  26. }
  27. func NewRouter(ctx context.Context, log *fw.Log, uvc *UVContext, routing Routing) (*Router, error) {
  28. defaultRoute := ""
  29. for name := range routing {
  30. if routing[name].Default {
  31. defaultRoute = name
  32. }
  33. routing[name].name = name
  34. }
  35. if defaultRoute == "" {
  36. return nil, ErrNoDefaultRoute
  37. }
  38. this := &Router{
  39. ctx: ctx,
  40. log: log,
  41. uvc: uvc,
  42. root: &Node{
  43. Component: uvc.Factory().NewRoot(
  44. RootComponentOptions{},
  45. ),
  46. },
  47. routing: routing,
  48. stack: []Route{},
  49. }
  50. this.Push(defaultRoute, Parameters{}) // NOTE: default router haven't parameters
  51. return this, nil
  52. }
  53. func (this *Router) Update() {
  54. this.root.Update()
  55. }
  56. func (this *Router) Push(name string, parameters Parameters) error {
  57. this.mutex.Lock()
  58. defer this.mutex.Unlock()
  59. route, ok := this.routing[name]
  60. if !ok {
  61. return ErrRouteNotFound
  62. }
  63. this.stack = append(
  64. this.stack,
  65. Route{
  66. name: name,
  67. parameters: parameters,
  68. Builder: route.Builder,
  69. },
  70. )
  71. go func() {
  72. err := this.refresh()
  73. if err != nil {
  74. this.log.Error("can't refresh", fw.LogError(err))
  75. }
  76. }()
  77. return nil
  78. }
  79. func (this *Router) Pop() (Route, error) {
  80. this.mutex.Lock()
  81. defer this.mutex.Unlock()
  82. if len(this.stack) == 0 {
  83. return Route{}, ErrStackUnderflow
  84. }
  85. result := this.stack[len(this.stack)-1]
  86. this.stack = this.stack[:len(this.stack)-1]
  87. go func() {
  88. err := this.refresh()
  89. if err != nil {
  90. this.log.Error("can't refresh", fw.LogError(err))
  91. }
  92. }()
  93. return result, nil
  94. }
  95. func (this *Router) Back() {
  96. _, err := this.Pop()
  97. if err != nil {
  98. this.log.Error("can't back on router stack", fw.LogError(err))
  99. }
  100. }
  101. func (this *Router) Set(name string, parameters Parameters) error {
  102. this.mutex.Lock()
  103. defer this.mutex.Unlock()
  104. route, ok := this.routing[name]
  105. if !ok {
  106. return ErrRouteNotFound
  107. }
  108. this.stack = []Route{
  109. {
  110. name: name,
  111. parameters: parameters,
  112. Builder: route.Builder,
  113. },
  114. }
  115. go func() {
  116. err := this.refresh()
  117. if err != nil {
  118. this.log.Error("can't refresh", fw.LogError(err))
  119. }
  120. }()
  121. return nil
  122. }
  123. func (this *Router) refresh() error {
  124. if len(this.stack) == 0 {
  125. return ErrStackUnderflow
  126. }
  127. // Get route
  128. route := this.stack[len(this.stack)-1]
  129. // Build new window
  130. child, err := route.Builder(
  131. this.uvc,
  132. route.parameters,
  133. )
  134. if err != nil {
  135. return fmt.Errorf("can't refresh router: %w", err)
  136. }
  137. // Set new window
  138. this.root.Build(child)
  139. return nil
  140. }