Search code examples
gwtgwtpgwt-platform

GWTP displays default place on application start even if url say to go to other place


I wonder how to change gwtp behaviour. When I start gwt app (enter app url in browser) it always displays for me default place. But when I enter url as follow: localhost/app#settings gwtp should open place Settings, but unfortunatelly it displays me Default place.

Url in web browser address points to Settings but the view is from default place.

I would like to gwtp display for me the view from url.

Here is my configuration:

public class UiModule extends AbstractGinModule {
    @Override
    protected void configure() {

    bind(AppView.Binder.class).in(Singleton.class);
    bind(Footer.Binder.class).in(Singleton.class);
    bind(GatekeeperProtectedMenuPanel.Binder.class).in(Singleton.class);



    install(new GinFactoryModuleBuilder().build(MenuEntryFactory.class));

    }

public class ClientModule extends AbstractPresenterModule {
    @Override
    protected void configure() {
    bind(RestyGwtConfig.class).asEagerSingleton();

    install(new DefaultModule.Builder()//
        .defaultPlace(Routing.HOME.url)//
        .errorPlace(Routing.ERROR.url)//
        .unauthorizedPlace(Routing.LOGIN.url)//
        .tokenFormatter(RouteTokenFormatter.class).build());
    install(new AppModule());
    install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));

    bind(CurrentUser.class).in(Singleton.class);
    bind(IsAdminGatekeeper.class).in(Singleton.class);
    bind(UserLoginGatekeeper.class).in(Singleton.class);



    // Load and inject CSS resources
    bind(ResourceLoader.class).asEagerSingleton();

    }

}

public class AppModule extends AbstractPresenterModule {
    @Override
    protected void configure() {
    install(new UiModule());

    // Application Presenters

    bindPresenter(AppPresenter.class, AppPresenter.MyView.class, AppView.class, AppPresenter.MyProxy.class);
    bindPresenter(HomePresenter.class, HomePresenter.MyView.class, HomeView.class, HomePresenter.MyProxy.class);
    bindPresenter(ErrorPresenter.class, ErrorPresenter.MyView.class, ErrorView.class, ErrorPresenter.MyProxy.class);
    bindPresenter(TestPresenter.class, TestPresenter.MyView.class, TestView.class, TestPresenter.MyProxy.class);
    bindPresenter(PagePresenter.class, PagePresenter.MyView.class, PageView.class, PagePresenter.MyProxy.class);
    bindPresenter(SettingsPresenter.class, SettingsPresenter.MyView.class, SettingsView.class, SettingsPresenter.MyProxy.class);
    bindPresenter(FilesPresenter.class, FilesPresenter.MyView.class, FilesView.class, FilesPresenter.MyProxy.class);
    bindPresenter(AdminAreaPresenter.class, AdminAreaPresenter.MyView.class, AdminAreaView.class, AdminAreaPresenter.MyProxy.class);

    bindPresenter(LoginPresenter.class, LoginPresenter.MyView.class, LoginView.class, LoginPresenter.MyProxy.class);
    }
}

This happens when I have GateKeeper on place's presenter.

Here is code:

public class UserLoginGatekeeper extends UserLoginModel implements Gatekeeper {
    private final CurrentUser currentUser;

    @Inject
    UserLoginGatekeeper(CurrentUser currentUser) {
        this.currentUser = currentUser;
    }

    @Override
    public boolean canReveal() {
        return currentUser.isLoggedIn();
    }
}

In my main app presenter I execute asynhronous call to server to check is user login. If so I set client variable currentUser.setLoggedIn(true);. Base on this Gatekeeper allow access to restricted part of app.

I think the problem is that my asynhronous call is triggered to late. And GWTP redirect to default place.

Here is my app presenter code:

public class AppPresenter extends TabContainerPresenter<AppPresenter.MyView, AppPresenter.MyProxy> implements AppUiHandlers, CurrentUserChangedHandler, AsyncCallStartHandler, AsyncCallFailHandler,
    AsyncCallSucceedHandler {

    @ProxyStandard
    public interface MyProxy extends Proxy<AppPresenter> {
    }


    public interface MyView extends TabView, HasUiHandlers<AppUiHandlers> {
    void refreshTabs();

    void setTopMessage(String string);

    void setLoginButtonVisbility(boolean isVisible);
    }


    @RequestTabs
    public static final Type<RequestTabsHandler> SLOT_REQUEST_TABS = new Type<>();

    @ChangeTab
    public static final Type<ChangeTabHandler> SLOT_CHANGE_TAB = new Type<>();


    public static final NestedSlot SLOT_TAB_CONTENT = new NestedSlot();

    private static final LoginService service = GWT.create(LoginService.class);
    private final PlaceManager placeManager;
    private final CurrentUser currentUser;

    @Inject
    AppPresenter(EventBus eventBus, MyView view, MyProxy proxy, PlaceManager placeManager, CurrentUser currentUser) {
    super(eventBus, view, proxy, SLOT_TAB_CONTENT, SLOT_REQUEST_TABS, SLOT_CHANGE_TAB, RevealType.Root);
    this.placeManager = placeManager;
    this.currentUser = currentUser;
    getView().setUiHandlers(this);
    onStart();
    }

    protected void onStart() {
    service.isCurrentUserLoggedIn(new MethodCallback<Boolean>() {
        @Override
        public void onFailure(Method method, Throwable exception) {
        MaterialToast.fireToast("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
        }

        @Override
        public void onSuccess(Method method, Boolean response) {
        currentUser.setLoggedIn(response);
        getView().setLoginButtonVisbility(response);
        }
    });
    };

    @ProxyEvent
    @Override
    public void onCurrentUserChanged(CurrentUserChangedEvent event) {
    getView().refreshTabs();
    }

    @ProxyEvent
    @Override
    public void onAsyncCallStart(AsyncCallStartEvent event) {
    getView().setTopMessage("Loading...");
    }

    @ProxyEvent
    @Override
    public void onAsyncCallFail(AsyncCallFailEvent event) {
    getView().setTopMessage("Oops, something went wrong...");
    }

    @ProxyEvent
    @Override
    public void onAsyncCallSucceed(AsyncCallSucceedEvent event) {
    getView().setTopMessage(null);
    }


    @Override
    public void onLogoutButtonClick() {
    service.logout(new MethodCallback<Void>() {

        @Override
        public void onFailure(Method method, Throwable exception) {
        MaterialToast.fireToast("Fail to logout " + method + " " + exception.getLocalizedMessage());
        }

        @Override
        public void onSuccess(Method method, Void response) {
        MaterialToast.fireToast("You have been Succefully logout");
        PlaceRequest request = new PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()).nameToken(Routing.Url.login).build();
        placeManager.revealPlace(request);
        currentUser.setLoggedIn(false);
        getView().setLoginButtonVisbility(false);
        }

    });

    }
}

Working solution:

/**
 * 
 */
package pl.korbeldaniel.cms.client.gin;

import gwt.material.design.client.ui.MaterialToast;

import org.fusesource.restygwt.client.Method;
import org.fusesource.restygwt.client.MethodCallback;

import pl.korbeldaniel.cms.client.place.Routing;
import pl.korbeldaniel.cms.client.security.CurrentUser;
import pl.korbeldaniel.cms.client.service.LoginService;

import com.google.gwt.core.shared.GWT;
import com.google.inject.Inject;
import com.gwtplatform.mvp.client.Bootstrapper;
import com.gwtplatform.mvp.client.proxy.PlaceManager;
import com.gwtplatform.mvp.shared.proxy.PlaceRequest;

/**
 * @author korbeldaniel
 *
 */
public class MyBootstrapper implements Bootstrapper {
    private final PlaceManager placeManager;
    private final CurrentUser currentUser;
    private static final LoginService service = GWT.create(LoginService.class);

    @Inject
    public MyBootstrapper(PlaceManager placeManager, CurrentUser currentUser) {
    this.placeManager = placeManager;
    this.currentUser = currentUser;
    }

    @Override
    public void onBootstrap() {
    GWT.log("OnBootstrap");
    service.isCurrentUserLoggedIn(new MethodCallback<Boolean>() {
        @Override
        public void onFailure(Method method, Throwable exception) {
        MaterialToast.fireToast("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
        placeManager.revealErrorPlace("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
        }

        @Override
        public void onSuccess(Method method, Boolean response) {
        // MaterialToast.fireToast("1Current user is logged in: " +
        // response);
        currentUser.setLoggedIn(response);
        if (response == true) {
            placeManager.revealCurrentPlace();
        } else {
            placeManager.revealPlace(new PlaceRequest.Builder().nameToken(Routing.Url.login).build());
        }
        }
    });
    };

}

Solution

  • Yeah, your backend call is asynchronous and most likely the UserLoginGatekeeper code will run before the backend call returns and the user gets redirected to the default page.

    There are two solutions:

    1. Use a dynamically generated host page (index.html) and set a javascript variable to the user details by the backend. You can read out the userdetails in a custom Bootstraper implementation and set the CurrentUser.

    2. If you don't want to use a dynamically generated host page, you can also move the backend call isCurrentUserLoggedIn intot he custom Bootstrapper implementation and in the onSuccess callback reveal the first page (like in the above linked GWTP documentation)