Skip to content

UIKit modal: a viewSubmit response with no type field fails to close the modal on mobile (regression from #7057) #7503

Description

@userjp7

When a Rocket.Chat App responds to a viewSubmit UIKit interaction with a bare
success response ({ "success": true }, no type field — this is what
apps-engine's UIKitInteractionResponder.successResponse() produces, and is a
documented valid response shape for "done, nothing more to render"), the modal
fails to close on the mobile app. No error is shown to the user; the submit
spinner simply stops and the form stays open.

This worked correctly before PR #7057 ("feat: Voice message blocks"), and still
works correctly on the desktop/web client.

Steps to reproduce

  1. Build (or use) a Rocket.Chat App that opens a UIKit modal (e.g. via a slash
    command) and, on viewSubmit, returns
    context.getInteractionResponder().successResponse().
  2. Open the modal on the mobile app and tap Submit.

Expected: the modal closes, same as it does on desktop/web.
Actual: the modal stays open. Nothing is shown to the user — no error, no
update — the loading indicator just clears.

Root cause

In app/lib/methods/actions.ts, triggerAction() parses the response body and
does:

const { type: interactionType, ...data } = parsed;
const modalType = toServerModalInteractionType(interactionType ?? '');
if (!modalType) {
throw new Error(Unknown modal interaction type: ${interactionType ?? 'undefined'});
}

toServerModalInteractionType (app/containers/UIKit/interactionAdapters.ts) is
a strict allow-list of 'modal.open' | 'modal.update' | 'modal.close' | 'errors'.
A bare { success: true } response has interactionType === undefined, so
toServerModalInteractionType('') returns null, and the code above throws.

That exception propagates out of triggerAction, through triggerSubmitView
(app/lib/methods/triggerActions.ts), which has no try/catch of its own — so
its Navigation.back() call (gated on triggerAction's resolved value) is
never reached. The exception is only caught by ModalBlockView.submit()'s
outer catch (e) { /* do nothing */ }, which swallows it silently.

Before #7057, triggerAction/handlePayloadUserInteraction treated any
unrecognized or missing type as an implicit ModalActions.CLOSE by fallthrough
(no allow-list, no throw), so this exact response used to close the modal.

Cancel is unaffected because ModalBlockView.cancel() calls Navigation.back()
unconditionally before making the network call, so the same exception (thrown
inside triggerCancel → triggerAction) no longer matters by the time it fires.

Web is unaffected because ActionManager.emitInteraction's finally block
closes the view on any response type outside an exclusion list
(errors/modal.update/contextual_bar.update), rather than requiring the
type to be on an allow-list.

Proposed fix

In triggerAction, treat a successful response with no type field as an
explicit modal.close before consulting the allow-list, instead of throwing:

const { type: interactionType, ...data } = parsed;
if (interactionType === undefined) {
return ModalActions.CLOSE;
}
const modalType = toServerModalInteractionType(interactionType);
if (!modalType) {
throw new Error(Unknown modal interaction type: ${interactionType});
}

This restores the old behavior for the specific case that regressed, without
weakening the throw for a genuinely unrecognized type string.

Environment

  • Rocket.Chat.ReactNative: develop (reproduced against a build including PR feat: Voice message blocks #7057)
  • Rocket.Chat Server: 8.6.0
  • Platform: Android v15 (code path is platform-independent, likely affects iOS too)
  • Apps-Engine version: 1.64.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions