mayu-live / framework

Mayu is a live updating server-side component-based VDOM rendering framework written in Ruby
https://mayu.live
GNU Affero General Public License v3.0
130 stars 4 forks source link

Integrate stores into the VDOM #4

Open aalin opened 1 year ago

aalin commented 1 year ago

stores/Auth.rb

initial_state(
  logging_in: false,
  current_user: nil,
  error: nil,
)

selectors do
 CurrentUser = selector do |state|
    state[:current_user]
  end

  LoginState = selector do |state|
    state.slice(:logging_in, :error)
  end
end

actions do
  LogIn = async(:log_in) do |store, username:, password:|
    sleep 1

    if username == 'foo' && password == 'bar'
      { username: 'foo' }
    else
      raise InvalidCredentials
    end
  end

  LogOut = action(:log_out)
end

reducer(LogOut) do |state|
  state[:current_user] = nil
  state[:error] = nil
  state[:logging_in] = false
end

reducer(LogIn.pending) do |state|
  state[:error] = nil
  state[:logging_in] = true
end

reducer(LogIn.fulfilled) do |state, payload|
  state[:logging_in] = false
  state[:current_user] = payload[:user]
end

The actions will be available in components under Actions::Auth::, like Actions::Auth::LogIn.

The selectors will be available in components under Selectors::Auth::, like Actions::Auth::CurrentUser.

components/CurrentUser.rb

select(
  current_user: Selectors::Auth::CurrentUser,
  log_in_state: Selectors::Auth::
)

render do
  if user = selected[:current_user]
    return h.p("Logged in as #{user.name}")
  end

  h.p "Not logged in"
end

You should also be able to call dispatch anywhere in a component (except in render)...

handler(:log_in) do |form|
  username, password = form["fields"].fetch_values("useername", "password")
  dispatch(Actions::Auth::LogIn, username:, password:)
end

The idea with selectors is that sometime in the future, it would be possible to update each selector once every time the state updates, and keep track of which components are subscribed to each selector, and only rerender the components that have changed.

This also has to work with the module system, so whenever a store is updated, components should be updated too