| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- //go:build js && wasm
- package ultraviolet
- import (
- "context"
- "fmt"
- "sync"
- fw "git.buran.team/fairwind"
- )
- type Parameters map[string]any
- type NodeBuilder func(uvc *UVContext, parameters Parameters) (*Node, error)
- type Route struct {
- Default bool
- Builder NodeBuilder
- name string // NOTE: sets automatically
- parameters Parameters // NOTE: sets automatically
- }
- type Routing map[string]*Route
- type Router struct {
- ctx context.Context
- log *fw.Log
- mutex sync.Mutex
- uvc *UVContext
- root *Node
- routing Routing
- stack []Route
- }
- func NewRouter(ctx context.Context, log *fw.Log, uvc *UVContext, routing Routing) (*Router, error) {
- defaultRoute := ""
- for name := range routing {
- if routing[name].Default {
- defaultRoute = name
- }
- routing[name].name = name
- }
- if defaultRoute == "" {
- return nil, ErrNoDefaultRoute
- }
- this := &Router{
- ctx: ctx,
- log: log,
- uvc: uvc,
- root: &Node{
- Component: uvc.Factory().NewRoot(
- RootComponentOptions{},
- ),
- },
- routing: routing,
- stack: []Route{},
- }
- this.Push(defaultRoute, Parameters{}) // NOTE: default router haven't parameters
- return this, nil
- }
- func (this *Router) Update() {
- this.root.Update()
- }
- func (this *Router) Push(name string, parameters Parameters) error {
- this.mutex.Lock()
- defer this.mutex.Unlock()
- route, ok := this.routing[name]
- if !ok {
- return ErrRouteNotFound
- }
- this.stack = append(
- this.stack,
- Route{
- name: name,
- parameters: parameters,
- Builder: route.Builder,
- },
- )
- go func() {
- err := this.refresh()
- if err != nil {
- this.log.Error("can't refresh", fw.LogError(err))
- }
- }()
- return nil
- }
- func (this *Router) Pop() (Route, error) {
- this.mutex.Lock()
- defer this.mutex.Unlock()
- if len(this.stack) == 0 {
- return Route{}, ErrStackUnderflow
- }
- result := this.stack[len(this.stack)-1]
- this.stack = this.stack[:len(this.stack)-1]
- go func() {
- err := this.refresh()
- if err != nil {
- this.log.Error("can't refresh", fw.LogError(err))
- }
- }()
- return result, nil
- }
- func (this *Router) Back() {
- _, err := this.Pop()
- if err != nil {
- this.log.Error("can't back on router stack", fw.LogError(err))
- }
- }
- func (this *Router) Set(name string, parameters Parameters) error {
- this.mutex.Lock()
- defer this.mutex.Unlock()
- route, ok := this.routing[name]
- if !ok {
- return ErrRouteNotFound
- }
- this.stack = []Route{
- {
- name: name,
- parameters: parameters,
- Builder: route.Builder,
- },
- }
- go func() {
- err := this.refresh()
- if err != nil {
- this.log.Error("can't refresh", fw.LogError(err))
- }
- }()
- return nil
- }
- func (this *Router) refresh() error {
- if len(this.stack) == 0 {
- return ErrStackUnderflow
- }
- // Get route
- route := this.stack[len(this.stack)-1]
- // Build new window
- child, err := route.Builder(
- this.uvc,
- route.parameters,
- )
- if err != nil {
- return fmt.Errorf("can't refresh router: %w", err)
- }
- // Set new window
- this.root.Build(child)
- return nil
- }
|