bug5927: Created a new list box premium element to move the colored-tails selection behind the paywall.

- added reset of view in case of user event status change
This commit is contained in:
Udo Wessels committed 2023-12-18 16:50:53 +01:00
1 parent 93c075edd2
commit 3ab4e414aa
5 files changed
+310 -18

No files matched your search

@@ -66,7 +66,8 @@ public class SecuredDomainType extends HasPermissionsImpl {
EXPORT,
SIMULATOR,
VIEWSTREAMLETS,
VIEWANALYSISCHARTS;
VIEWANALYSISCHARTS,
DETAILTYPES;
private static final Action[] ALL_ACTIONS = DefaultActions.plus(TrackedRaceActions.values());
@@ -0,0 +1,35 @@
package com.sap.sailing.gwt.common.client.premium;
import static com.sap.sailing.gwt.common.client.premium.SailingPremiumIconRessource.INSTANCE;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Image;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
import com.sap.sse.security.shared.HasPermissions.Action;
import com.sap.sse.security.shared.dto.SecuredDTO;
import com.sap.sse.security.ui.client.premium.PaywallResolver;
import com.sap.sse.security.ui.client.premium.PremiumListBox;
public class SailingPremiumListBox extends PremiumListBox {
public SailingPremiumListBox(final String label, final String emptyValue, final Action action, final PaywallResolver paywallResolver, SecuredDTO contextDTO) {
super(label, emptyValue, action, paywallResolver, contextDTO);
}
@Override
protected Image createPremiumIcon() {
return new Image();
}
@Override
protected void onUserPermissionUpdate(final boolean isPermitted) {
super.onUserPermissionUpdate(isPermitted);
image.setUrl((isPermitted ? INSTANCE.premiumIconPermitted() : INSTANCE.premiumIcon()).getSafeUri());
}
@Override
protected void onSubscribeDialogConfirmation(final Iterable<String> unlockingPlans) {
Window.open(EntryPointLinkFactory.createSubscriptionPageLink(unlockingPlans), "_blank", "");
}
}
@@ -68,7 +68,6 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.AnimationType;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
@@ -91,8 +90,10 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.common.security.SecuredDomainType.TrackedRaceActions;
import com.sap.sailing.domain.common.windfinder.SpotDTO;
import com.sap.sailing.gwt.common.client.FullscreenUtil;
import com.sap.sailing.gwt.common.client.premium.SailingPremiumListBox;
import com.sap.sailing.gwt.common.client.sharing.FloatingSharingButtonsResources;
import com.sap.sailing.gwt.ui.actions.GetBoatPositionsAction;
import com.sap.sailing.gwt.ui.actions.GetBoatPositionsCallback;
@@ -181,6 +182,8 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
import com.sap.sse.gwt.client.shared.settings.ComponentContext;
import com.sap.sse.gwt.shared.ClientConfiguration;
import com.sap.sse.gwt.shared.DebugConstants;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.ui.client.UserStatusEventHandler;
import com.sap.sse.security.ui.client.premium.PaywallResolver;
public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> implements TimeListener, CompetitorSelectionChangeListener,
@@ -2866,23 +2869,27 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
return vPanel;
}
private ListBox createDetailTypeDropdown(CompetitorDTO competitor) {
final ListBox lb = new ListBox();
final NodeList<OptionElement> options = DOMUtils.getOptions(lb);
lb.addItem(stringMessages.none(), "none");
if (sortedAvailableDetailTypes != null) {
for (int i = 0; i < sortedAvailableDetailTypes.size(); i++) {
final DetailType detail = sortedAvailableDetailTypes.get(i);
lb.addItem(DetailTypeFormatter.format(detail), detail.name());
final String tooltip = DetailTypeFormatter.getTooltip(detail);
if (Util.hasLength(tooltip)) {
options.getItem(options.getLength()-1).setTitle(tooltip);
}
if (detail == selectedDetailType) {
lb.setSelectedIndex(i + 1);
}
private VerticalPanel createDetailTypeDropdown(CompetitorDTO competitor) {
final VerticalPanel vPanel = new VerticalPanel();
vPanel.add(createDetailTypePremiumList(competitor));
// reset component if user changes (login/out)
paywallResolver.registerUserStatusEventHandler(new UserStatusEventHandler() {
@Override
public void onUserStatusChange(UserDTO user, boolean preAuthenticated) {
vPanel.clear();
vPanel.add(createDetailTypePremiumList(competitor));
}
}
});
return vPanel;
}
private SailingPremiumListBox createDetailTypePremiumList(CompetitorDTO competitor) {
// reset metric overlay
selectedDetailType = null;
metricOverlay.setVisible(false);
// create new premium list box
SailingPremiumListBox lb = new SailingPremiumListBox(stringMessages.none(), "none", TrackedRaceActions.DETAILTYPES, paywallResolver, competitor);
fillItemsFromAvailableDetailTypes(lb);
lb.setVisibleItemCount(1);
lb.addChangeHandler(new ChangeHandler() {
@Override
@@ -2914,6 +2921,25 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
return lb;
}
private void fillItemsFromAvailableDetailTypes(SailingPremiumListBox lb) {
final NodeList<OptionElement> options = DOMUtils.getOptions(lb.getListBox());
if (lb.isEnabled() && sortedAvailableDetailTypes != null) {
for (int i = 0; i < sortedAvailableDetailTypes.size(); i++) {
final DetailType detail = sortedAvailableDetailTypes.get(i);
lb.addItem(DetailTypeFormatter.format(detail), detail.name());
final String tooltip = DetailTypeFormatter.getTooltip(detail);
if (Util.hasLength(tooltip)) {
options.getItem(options.getLength()-1).setTitle(tooltip);
}
if (detail == selectedDetailType) {
lb.setSelectedIndex(i + 1);
}
}
} else {
lb.reset();
}
}
/**
* @return the {@link CompetitorSelectionProvider#getSelectedCompetitors()} if
* {@link RaceMapSettings#isShowOnlySelectedCompetitors() only selected competitors are to be shown}, the
@@ -0,0 +1,182 @@
package com.sap.sse.security.ui.client.premium;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.HasAllKeyHandlers;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.sap.sse.gwt.client.dialog.ConfirmationDialog;
import com.sap.sse.security.shared.HasPermissions.Action;
import com.sap.sse.security.shared.dto.SecuredDTO;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.ui.client.UserStatusEventHandler;
import com.sap.sse.security.ui.client.i18n.StringMessages;
public abstract class PremiumListBox extends PremiumUiElement implements HasAllKeyHandlers {
private static PremiumCheckBoxUiBinder uiBinder = GWT.create(PremiumCheckBoxUiBinder.class);
interface PremiumCheckBoxUiBinder extends UiBinder<FocusPanel, PremiumListBox> {
}
interface Style extends CssResource {
@ClassName("premium-container")
String premiumContainer();
@ClassName("premium-list-box")
String premiumListBox();
@ClassName("premium-permitted")
String premiumPermitted();
@ClassName("premium-icon")
String premiumIcon();
}
@UiField
StringMessages i18n;
@UiField
Style style;
@UiField
FocusPanel container;
@UiField(provided = true)
protected final Image image;
@UiField(provided = true)
protected final ListBox listBox;
private final ConfirmationDialog subscribeDialog;
/**
* A Composite component, that includes a list box and an additional premium icon, indicating that the feature to be
* enabled is a premium feature if the user does not have the permission. By definition there is always minimum one
* (empty) item with index 0.
*/
protected PremiumListBox(final String emptyLabel, String emptyValue, final Action action,
final PaywallResolver paywallResolver, final SecuredDTO contextDTO) {
super(action, paywallResolver, contextDTO);
this.listBox = new ListBox();
this.listBox.addItem(emptyLabel, emptyValue);
this.listBox.setSelectedIndex(0);
this.image = createPremiumIcon();
initWidget(uiBinder.createAndBindUi(this));
this.subscribeDialog = ConfirmationDialog.create(i18n.subscriptionSuggestionTitle(),
i18n.pleaseSubscribeToUse(), i18n.takeMeToSubscriptions(), i18n.cancel(), () -> paywallResolver
.getUnlockingSubscriptionPlans(action, contextDTO, this::onSubscribeDialogConfirmation));
updateUserPermission();
paywallResolver.registerUserStatusEventHandler(new UserStatusEventHandler() {
@Override
public void onUserStatusChange(UserDTO user, boolean preAuthenticated) {
}
});
}
public void reset() {
while (this.listBox.getItemCount() > 1) {
this.listBox.removeItem(1);
}
}
protected abstract void onSubscribeDialogConfirmation(Iterable<String> unlockingPlans);
@Override
protected void onEnsureDebugId(final String baseID) {
this.listBox.ensureDebugId(baseID);
}
@UiHandler("container")
void onContainerClicked(final ClickEvent event) {
if (!hasPermission()) {
this.updateUserPermission();
subscribeDialog.center();
}
}
@Override
protected void onUserPermissionUpdate(final boolean isPermitted) {
listBox.setSelectedIndex(getSelectedIndex());
listBox.setEnabled(isEnabled() && isPermitted);
container.setStyleName(style.premiumPermitted(), isPermitted);
}
public void addItem(String item, String value) {
if (hasPermission()) {
this.listBox.addItem(item, value);
}
}
public void setSelectedIndex(final int index) {
if (hasPermission()) {
this.listBox.setSelectedIndex(index);
}
}
public int getSelectedIndex() {
int selectedIndex = 0;
if (hasPermission()) {
selectedIndex = this.listBox.getSelectedIndex();
}
return selectedIndex;
}
/**
* If NO permission is granted select empty item (index 0) and return the default empty value.
*
* @return
*/
public String getSelectedValue() {
if (!hasPermission()) {
this.listBox.setSelectedIndex(0);
}
return this.listBox.getSelectedValue();
}
public void setVisibleItemCount(int visibleItems) {
if (hasPermission()) {
this.listBox.setVisibleItemCount(visibleItems);
}
}
public HandlerRegistration addChangeHandler(final ChangeHandler handler) {
return listBox.addChangeHandler(handler);
}
@Override
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
listBox.setEnabled(enabled);
}
@Override
public HandlerRegistration addKeyDownHandler(final KeyDownHandler handler) {
return container.addKeyDownHandler(handler);
}
@Override
public HandlerRegistration addKeyPressHandler(final KeyPressHandler handler) {
return container.addKeyPressHandler(handler);
}
@Override
public HandlerRegistration addKeyUpHandler(final KeyUpHandler handler) {
return container.addKeyUpHandler(handler);
}
public FocusWidget getFocusWidget() {
return this.listBox;
}
public ListBox getListBox() {
return this.listBox;
}
}
@@ -0,0 +1,48 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:with field="i18n" type="com.sap.sse.security.ui.client.i18n.StringMessages" />
<ui:style type="com.sap.sse.security.ui.client.premium.PremiumListBox.Style">
.premium-listbox {
display: inline-block;
}
.premium-container,
.premium-container select:disabled.premium-list-box + label {
cursor: pointer;
}
.premium-container .premium-list-box {
padding-right: 0;
}
.premium-container select.premium-list-box {
border-radius: 4px;
background-color: rgba(255,255,255,0.75);
border: 1px solid #efaa00;
}
.premium-container {
padding-right: 10px;
top: -3px;
position: relative;
}
.premium-permitted .premium-container,
.premium-permitted .premium-container select:disabled.premium-list-box + label {
cursor: default;
}
.premium-container select:disabled.premium-list-box {
pointer-events: none;
}
.premium-container .premium-icon {
width: 17px;
height: 17px;
margin: 2px;
vertical-align: top;
}
.premium-permitted .premium-container .premium-icon {
display: none;
}
</ui:style>
<g:FocusPanel ui:field="container" addStyleNames="{style.premium-listbox}">
<g:FlowPanel addStyleNames="{style.premium-container}">
<g:ListBox ui:field="listBox" addStyleNames="{style.premium-list-box}" />
<g:Image ui:field="image" addStyleNames="{style.premium-icon}"/>
</g:FlowPanel>
</g:FocusPanel>
</ui:UiBinder>