← Recipe: synchronize Browser and system Back

3. Replace src/main.luau

luastra/navigation · luastra/host · navigation.history · about 20 minutes
one router owns every navigation outcome

The application pushes; the host moves Back; both end by rendering the same validated router.

When to use it

Replace the complete entry module. resolve reports rejected host requests; only history events restore router state, and only system_back events carry platform Back intents.

Focused example

--!strict

local Host = require("luastra/host")
local Navigation = require("luastra/navigation")
local UI = require("luastra/ui")

local routes = Navigation.compile {
    { name = "home", path = "/" },
    { name = "detail", path = "/detail" },
}
local router = Navigation.createRouter {
    compiler = routes,
    initial = { name = "home", params = {}, query = {} },
}
local App = require("luastra/app")
local Root = {}
local message = "At home"

local function location(): string
    return "#" .. router.currentLocation()
end

function Root.restoreHistory(value: string): boolean
    local result = router.restoreEncoded(value)
    if result.success then message = "History restored" end
    return result.success
end

function Root.render(): UI.Node
    return UI.Screen {
        id = "history-recipe",
        UI.Text { id = "history/title", text = "Route: " .. router.current().name, variant = "title" },
        UI.Text { id = "history/location", text = location(), role = "status" },
        UI.Text { id = "history/message", text = message, role = "status" },
        UI.Button { id = "history/open", text = "Open detail", onTap = "route.open" },
    }
end

function Root.handle(action: string, target: string, value: string)
    if action == "lifecycle" and target == "app" and value == "launch" then
        Host.historyReplaceLocation(location(), router.encode())
    elseif action == "route.open" and target == "history/open" then
        local result = router.push { name = "detail", params = {}, query = {} }
        if result.success and result.changed then
            Host.historyPushLocation(location(), router.encode())
            message = "Detail opened"
        end
    elseif action == "history" and target == "app" then
        if not Root.restoreHistory(value) then message = "Rejected history state" end
    elseif action == "system_back" and target == "app" then
        local intent = tonumber(string.match(value, "^([1-9][0-9]*):[01]$"))
        if intent ~= nil then
            if router.canBack() then Host.systemBackHistory(intent)
            else Host.systemBackExit(intent) end
        end
    end
end

function Root.resolve(
    _id: number,
    success: boolean,
    _payload: string,
    code: string,
    diagnostic: string
)
    if not success then
        message = "History request failed: " .. code .. " — " .. diagnostic
    end
end

function Root.snapshot()
    return { name = router.current().name, location = location() }
end

return App.compose {
    render = function(_outputs) return Root.render() end,
    handle = Root.handle,
    resolve = Root.resolve,
    snapshot = Root.snapshot,
}

Complete inventory

View all pages in Recipe: synchronize Browser and system Back
← Previous: 2. Replace luastra.jsonNext: 4. Replace tests/smoke.luau →