Logout button action ignores unsaved changes

Jmix version: 1.2.3
Jmix Studio plugin version: 1.2.3 - 213
IntelliJ IDEA 2022.1.1 (Community Edition)
Build #IC-221.5591.52, built on May 10, 2022
Runtime version: 11.0.14.1+1-b2043.45 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Operating System: macOS 12.3.1 (21E258)
File System: Case-Sensitive Journaled HFS+ (APFS)
Datebase: PostgreSQL 13

Hello Everyone

I would like to know how to configure the Logout button so that it does not ignore my editor screen’s unsaved changes.

The BeforeCloseEvent information here Screen Events :: Jmix Documentation is useful, however, it should not be necessary to add this code to every single editor screen. If I remember correctly, this action was caught in CUBA and the screen’s “You have unsaved changes” dialog was presented, just like when you press the screen’s default Cancel button.

Can you please suggest or provide a solution. Thanks in advance for your feedback.

Best regards
Chris

Hi,

Thank you for reporting the problem, I’ve created a GitHub issue.

As a workaround, create a custom App implementation

import com.google.common.base.Strings;
import com.vaadin.spring.annotation.VaadinSessionScope;
import io.jmix.core.AccessManager;
import io.jmix.core.security.SecurityContextHelper;
import io.jmix.ui.App;
import io.jmix.ui.AppUI;
import io.jmix.ui.accesscontext.UiShowScreenContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component("demo_App")
@VaadinSessionScope
public class CustomApp extends App {

    private static final Logger log = LoggerFactory.getLogger(CustomApp.class);

    @Autowired
    protected AccessManager accessManager;

    @Override
    public void loginOnStart() {
        initializeUi();
    }

    @Override
    protected String routeTopLevelWindowId() {
        if (isAnonymousAuthentication()) {
            String screenId = uiProperties.getLoginScreenId();
            if (!windowConfig.hasWindow(screenId)) {
                screenId = uiProperties.getMainScreenId();
            }

            String initialScreenId = uiProperties.getInitialScreenId();
            if (Strings.isNullOrEmpty(initialScreenId)) {
                return screenId;
            }

            if (!windowConfig.hasWindow(initialScreenId)) {
                log.info("Initial screen '{}' is not found", initialScreenId);
                return screenId;
            }

            UiShowScreenContext context = new UiShowScreenContext(initialScreenId);
            accessManager.applyRegisteredConstraints(context);
            if (!context.isPermitted()) {
                log.info("Initial screen '{}' is not permitted", initialScreenId);
                return screenId;
            }

            return initialScreenId;
        } else {
            return uiProperties.getMainScreenId();
        }
    }

    private void initializeUi() {
        AppUI currentUi = AppUI.getCurrent();
        if (currentUi != null) {
            createTopLevelWindow(currentUi);
        }
    }

    private boolean isAnonymousAuthentication() {
        Authentication authentication = SecurityContextHelper.getAuthentication();
        return authentication == null ||
                authentication instanceof AnonymousAuthenticationToken;
    }
}

And register it as a primary, e.g. in your @SpringBootApplication class:

@SpringBootApplication
public class Sandbox12sApplication {
    
    ...

    @Bean
    @Primary
    App app() {
        return new CustomApp();
    }
}

Regards,
Gleb

1 Like

@gorelov

Hello Gleb

Thank you very much for the solution and your support. It works great.

Best regards
Chris