workflow-kotlin icon indicating copy to clipboard operation
workflow-kotlin copied to clipboard

Running worker on click on button

Open gammafoxed opened this issue 3 years ago • 3 comments
trafficstars

Hello everyone! Perhaps the question is not to the address, but I still ask. I am implemented by simple workflow authorization on a server with via login and password. I encountered a problem that I can't run Worker by pressing the button.

override fun render(
	renderProps: Unit,
	renderState: StateSignIn,
	context: RenderContext
): RenderingSignIn {

	context.runningWorker(
		WorkerSignIn(
			renderState.email.getOrElse { "" },
			renderState.password.getOrElse { "" })
	) { auth ->
		action {
			Log.d("TAG", "render: $auth")
		}
	}

	return RenderingSignIn(
		email = renderState.email.getOrElse { "" },
		password = renderState.password.getOrElse { "" },
		onEmailChanged = { context.actionSink.send(onChangeEmailAction(it)) },
		onPasswordChanged = { context.actionSink.send(onChangePasswordAction(it)) },
		onSignInButtonTap = {  },
		onSignUpButtonTap = { context.actionSink.send(onSignUpButtonTapAction()) }
	)
}

This code works, but that naturally, the network request occurs at the time of the first rendering.

But if I transfer the worker launch code in the callback click on the key, I get an error IllegalstateException: RenderContext cannot be used after render method returns.

	onSignInButtonTap = {
		context.runningWorker(
			WorkerSignIn(
				renderState.email.getOrElse { "" },
				renderState.password.getOrElse { "" })
		) { auth ->
			action {
				Log.d("TAG", "render: $auth")
			}
		}
	}

Code of my Worker

class WorkerSignIn(
	private val email: String,
	private val password: String
) : Worker<Either<AuthServiceError, Token>>, KoinComponent {
	private val mServiceAuth: ServiceAuth by inject()
	override fun run(): Flow<Either<AuthServiceError, Token>> = flow {
		val res = mServiceAuth.signIn(email, password)
		emit(res)
	}
}

How can I trigger the worker in the time you need? I also do not understand the model of interaction between Workflow and Worker.

gammafoxed avatar Jan 24 '22 15:01 gammafoxed

What you want to do is enter a different state from your click handler. That will lead to a new call to render(), and then you can schedule the worker.

sealed class StateSignIn {
  object LoggedOut : MyState()
  object MakingRequest: MyState()
  class LoggedIn(val auth: String) : MyState()
}
override fun render(
	renderProps: Unit,
	renderState: StateSignIn,
	context: RenderContext
): RenderingSignIn {
  if (renderState == MakingRequest) {
    context.runningWorker(
      WorkerSignIn(
	renderState.email.getOrElse { "" },
	renderState.password.getOrElse { "" })
      ) { auth ->
	action {
	  state = LoggedIn(auth)
	}
      }
  }

  return RenderingSignIn(
    email = renderState.email.getOrElse { "" },
    password = renderState.password.getOrElse { "" },
    onEmailChanged = { context.actionSink.send(onChangeEmailAction(it)) },
    onPasswordChanged = { context.actionSink.send(onChangePasswordAction(it)) },
    onSignInButtonTap = context.eventHandler { state = MakingRequest },
    onSignUpButtonTap = { context.actionSink.send(onSignUpButtonTapAction()) }
  )
}

rjrjr avatar Jan 25 '22 17:01 rjrjr

My code sample there is gibberish, e.g. it doesn't to pass through the email and password, but I hope it gets the important bit across.

rjrjr avatar Jan 25 '22 17:01 rjrjr

Wow! I understand the logic, but it's definitely not what I expected! This means that there is no easy way to call asynchronous subroutines other than running a full cycle: event -> state change -> rendering. Thanks!

gammafoxed avatar Jan 28 '22 17:01 gammafoxed