Skip to content

Action

Tuucho provides several built-in actions. You can see the full list here:

object-definition/action/

You can also add middleware around built-in actions by implementing the ActionMiddleware interface.


Example: Logger Middleware for Actions

class LoggerAction(
    private val logger: Logger,
    private val systemInformation: SystemInformationProtocol
) : ActionMiddleware {

    override val priority: Int = ActionMiddleware.Priority.LOW

    override fun accept(
        route: NavigationRoute?,
        action: ActionModelDomain,
    ) = true

    override suspend fun process(
        context: ActionMiddleware.Context,
        next: MiddlewareProtocol.Next<ActionMiddleware.Context, ProcessActionUseCase.Output>?
    ) = with(context.input) {
        logger.debug("THREAD") { systemInformation.currentThreadName() }
        logger.debug("ACTION") { "from ${route?.value}: $action" }
        next?.invoke(context)
    }
}

You can completely intercept an action or alter it before continuing the chain.

Register it in Koin:

module(ModuleGroupDomain.Middleware) {
    factory<LoggerAction> {
        LoggerAction(
            logger = get(),
            systemInformation = get()
        )
    } bind ActionMiddleware::class
}

Action middleware are not ordered. They use a priority system. You can override priority to influence execution order.


Calling Built-In Actions

Example Forward navigation

useCaseExecutor.async(
    useCase = actionHandler,
    input = ProcessActionUseCase.Input.JsonElement(
        route = null,
        action = ActionModelDomain.from(
            command = NavigateAction.command,
            authority = NavigateAction.Url.authority,
            target = "target url",
        )
    ),
)

Example Back navigation

useCaseExecutor.async(
    useCase = actionHandler,
    input = ProcessActionUseCase.Input.JsonElement(
        route = null,
        action = ActionModelDomain.from(
            command = NavigateAction.command,
            authority = NavigateAction.LocalDestination.authority,
            target = "back",
        )
    ),
)

Actions are processed by the ActionExecutor. All actions you register are inserted as middleware.


Creating Your Own Action

You can declare new actions for your application. Here is an example of a custom crash action:

class CrashApplicationActionMiddleware() : ActionMiddleware {

    override val priority: Int
        get() = ActionMiddleware.Priority.HIGH

    override fun accept(
        route: NavigationRoute?,
        action: ActionModelDomain,
    ): Boolean = action.command == "crash-application" && action.authority == "polite"

    override suspend fun process(
        context: ActionMiddleware.Context,
        next: MiddlewareProtocol.Next<ActionMiddleware.Context, ProcessActionUseCase.Output>?
    ) = with(context.input) {

        val message = context.input.action.query?.jsonObject["message"].stringOrNull
        throw RuntimeException("crash action with message $message")

        // can't be reach because we crash the application, but when you create action,
        // you must always call it to continue the chain
        // and allow adding extension to tis action
        next?.invoke(context)
    }
}

Register the custom action in Koin:

module(ModuleGroupDomain.Middleware) {
    factory<CrashApplicationActionMiddleware> {
        CrashApplicationActionMiddleware()
    } bind ActionMiddleware::class
}