Skip to content

Commit

Permalink
Add Smart live replays (FAForever#2529)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivan <v23620@gmail.com>
  • Loading branch information
2 people authored and Chris Haggan committed Apr 15, 2022
1 parent a2146cc commit a4dd4e9
Show file tree
Hide file tree
Showing 52 changed files with 1,307 additions and 319 deletions.
Expand Up @@ -16,7 +16,6 @@
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

Expand All @@ -29,7 +28,7 @@ public class ChatCategoryItemController implements Controller<Node> {
private final I18n i18n;
private final UiService uiService;
private final PreferencesService preferencesService;
private final ApplicationContext applicationContext;
private final ContextMenuBuilder contextMenuBuilder;

public Label chatUserCategoryRoot;
private ChatUserCategory chatUserCategory;
Expand All @@ -56,7 +55,7 @@ void setChatUserCategory(@Nullable ChatUserCategory chatUserCategory) {
}

public void onContextMenuRequested(ContextMenuEvent event) {
ContextMenuBuilder.newBuilder(applicationContext)
contextMenuBuilder.newBuilder()
.addCustomItem(uiService.loadFxml("theme/chat/color_picker_menu_item.fxml", ChatCategoryColorPickerCustomMenuItemController.class), chatUserCategory)
.build()
.show(chatUserCategoryRoot.getScene().getWindow(), event.getScreenX(), event.getScreenY());
Expand Down
Expand Up @@ -47,15 +47,16 @@
import javafx.scene.layout.Pane;
import javafx.stage.PopupWindow;
import javafx.util.Duration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Slf4j
@RequiredArgsConstructor
public class ChatUserItemController implements Controller<Node> {

private static final PseudoClass COMPACT = PseudoClass.getPseudoClass("compact");
Expand All @@ -66,11 +67,11 @@ public class ChatUserItemController implements Controller<Node> {
private final EventBus eventBus;
private final PlayerService playerService;
private final PlatformService platformService;
private final ApplicationContext applicationContext;
private final ContextMenuBuilder contextMenuBuilder;

private final InvalidationListener formatChangeListener;
private final InvalidationListener chatUserPropertyInvalidationListener;
private final InvalidationListener chatUserGamePropertyInvalidationListener;
private final InvalidationListener formatChangeListener = observable -> updateFormat();
private final InvalidationListener chatUserPropertyInvalidationListener = observable -> updateChatUserDisplay();
private final InvalidationListener chatUserGamePropertyInvalidationListener = observable -> updateChatUserGame();

public ImageView playerMapImage;
public ImageView playerStatusIndicator;
Expand All @@ -86,22 +87,6 @@ public class ChatUserItemController implements Controller<Node> {
private GameTooltipController gameInfoController;
private ChatChannelUser chatUser;

public ChatUserItemController(PreferencesService preferencesService,
I18n i18n, UiService uiService, EventBus eventBus, PlayerService playerService,
PlatformService platformService, ApplicationContext applicationContext) {
this.platformService = platformService;
this.preferencesService = preferencesService;
this.playerService = playerService;
this.i18n = i18n;
this.uiService = uiService;
this.eventBus = eventBus;
this.applicationContext = applicationContext;

formatChangeListener = observable -> updateFormat();
chatUserPropertyInvalidationListener = observable -> updateChatUserDisplay();
chatUserGamePropertyInvalidationListener = observable -> updateChatUserGame();
}

private void updateFormat() {
ChatFormat chatFormat = preferencesService.getPreferences().getChat().getChatFormat();
getRoot().pseudoClassStateChanged(
Expand Down Expand Up @@ -188,7 +173,7 @@ private EventHandler<MouseEvent> eventHandlerRemoveGameInfoTooltip() {

public void onContextMenuRequested(ContextMenuEvent event) {
PlayerBean player = chatUser.getPlayer().orElse(null);
ContextMenuBuilder.newBuilder(applicationContext)
contextMenuBuilder.newBuilder()
.addItem(ShowPlayerInfoMenuItem.class, player)
.addItem(SendPrivateMessageMenuItem.class, chatUser.getUsername())
.addItem(CopyUsernameMenuItem.class, chatUser.getUsername())
Expand Down
11 changes: 0 additions & 11 deletions src/main/java/com/faforever/client/fx/JavaFxUtil.java
Expand Up @@ -155,17 +155,6 @@ public static void centerOnScreen(Stage stage) {
stage.setY((screenBounds.getMaxY() - screenBounds.getMinY() - height) / 2);
}

public static void addCopyLabelContextMenus(ApplicationContext applicationContext, Label... labels) {
Arrays.stream(labels).forEach(label -> addCopyLabelContextMenuToLabel(applicationContext, label));
}

public static void addCopyLabelContextMenuToLabel(ApplicationContext applicationContext, Label label) {
label.setOnContextMenuRequested(event -> ContextMenuBuilder.newBuilder(applicationContext)
.addItem(CopyLabelMenuItem.class, label)
.build()
.show(label.getScene().getWindow(), event.getScreenX(), event.getScreenY()));
}

public static void assertApplicationThread() {
Assert.state(Platform.isFxApplicationThread(), "Must run in FX Application thread");
}
Expand Down
@@ -0,0 +1,45 @@
package com.faforever.client.fx.contextmenu;

import com.faforever.client.domain.GameBean;
import com.faforever.client.i18n.I18n;
import com.faforever.client.replay.LiveReplayService;
import com.faforever.client.replay.TrackingLiveReplay;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Optional;

import static com.faforever.client.replay.TrackingLiveReplayAction.NOTIFY_ME;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class CancelActionNotifyMeMenuItem extends AbstractMenuItem<GameBean> {

private final I18n i18n;
private final LiveReplayService liveReplayService;

@Override
protected void onClicked() {
liveReplayService.stopTrackingLiveReplay();
}

@Override
protected boolean isItemVisible() {
boolean isValid = object != null && object.getStartTime() != null;
if (!isValid) {
return false;
}

Optional<TrackingLiveReplay> trackingLiveReplayOptional = liveReplayService.getTrackingLiveReplay();
return trackingLiveReplayOptional.stream().anyMatch(trackingLiveReplay -> trackingLiveReplay.getGameId().equals(object.getId())
&& trackingLiveReplay.getAction() == NOTIFY_ME);
}

@Override
protected String getItemText() {
return i18n.get("vault.liveReplays.contextMenu.notifyMe.cancel");
}
}
@@ -0,0 +1,45 @@
package com.faforever.client.fx.contextmenu;

import com.faforever.client.domain.GameBean;
import com.faforever.client.i18n.I18n;
import com.faforever.client.replay.LiveReplayService;
import com.faforever.client.replay.TrackingLiveReplay;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Optional;

import static com.faforever.client.replay.TrackingLiveReplayAction.RUN_REPLAY;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class CancelActionRunReplayImmediatelyMenuItem extends AbstractMenuItem<GameBean> {

private final I18n i18n;
private final LiveReplayService liveReplayService;

@Override
protected void onClicked() {
liveReplayService.stopTrackingLiveReplay();
}

@Override
protected boolean isItemVisible() {
boolean isValid = object != null && object.getStartTime() != null;
if (!isValid) {
return false;
}

Optional<TrackingLiveReplay> trackingLiveReplayOptional = liveReplayService.getTrackingLiveReplay();
return trackingLiveReplayOptional.stream().anyMatch(trackingLiveReplay -> trackingLiveReplay.getGameId().equals(object.getId())
&& trackingLiveReplay.getAction() == RUN_REPLAY);
}

@Override
protected String getItemText() {
return i18n.get("vault.liveReplays.contextMenu.runReplayImmediately.cancel");
}
}
Expand Up @@ -4,44 +4,62 @@
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.SeparatorMenuItem;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Lazy
@Component
@RequiredArgsConstructor
public class ContextMenuBuilder {

public static Builder newBuilder(ApplicationContext context) {
return new Builder(context);
private final ApplicationContext applicationContext;

public MenuItemBuilder newBuilder() {
return new MenuItemBuilder(applicationContext);
}

public void addCopyLabelContextMenu(Label... labels) {
for (Label label : labels) {
label.setOnContextMenuRequested(event -> newBuilder()
.addItem(CopyLabelMenuItem.class, label)
.build()
.show(label.getScene().getWindow(), event.getScreenX(), event.getScreenY()));
}
}

public static class Builder {
public static class MenuItemBuilder {

private final ApplicationContext applicationContext;

private final ContextMenu contextMenu = new ContextMenu();
private SeparatorMenuItem separator;
private BooleanBinding totalVisibleBinding;

private Builder(ApplicationContext applicationContext) {
private MenuItemBuilder(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

public <T, B extends AbstractMenuItem<T>> Builder addItem(Class<B> clazz) {
public <T, B extends AbstractMenuItem<T>> MenuItemBuilder addItem(Class<B> clazz) {
return addItem(clazz, null);
}

public <T, B extends AbstractMenuItem<T>> Builder addItem(Class<B> clazz, T object) {
public <T, B extends AbstractMenuItem<T>> MenuItemBuilder addItem(Class<B> clazz, T object) {
B item = applicationContext.getBean(clazz);
item.setObject(object);
contextMenu.getItems().add(item);
bindVisiblePropertyToSeparator(item.visibleProperty());
return this;
}

public <T> Builder addCustomItem(AbstractCustomMenuItemController<T> item) {
public <T> MenuItemBuilder addCustomItem(AbstractCustomMenuItemController<T> item) {
return addCustomItem(item, null);
}

public <T> Builder addCustomItem(AbstractCustomMenuItemController<T> item, T object) {
public <T> MenuItemBuilder addCustomItem(AbstractCustomMenuItemController<T> item, T object) {
item.setObject(object);
contextMenu.getItems().add(item.getRoot());
bindVisiblePropertyToSeparator(item.getRoot().visibleProperty());
Expand All @@ -60,7 +78,7 @@ private void bindVisiblePropertyToSeparator(BooleanProperty visibleProperty) {
}
}

public Builder addSeparator() {
public MenuItemBuilder addSeparator() {
separator = new SeparatorMenuItem();
totalVisibleBinding = null;
contextMenu.getItems().add(separator);
Expand Down
@@ -0,0 +1,45 @@
package com.faforever.client.fx.contextmenu;

import com.faforever.client.domain.GameBean;
import com.faforever.client.i18n.I18n;
import com.faforever.client.replay.LiveReplayService;
import com.faforever.client.replay.TrackingLiveReplay;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Optional;

import static com.faforever.client.replay.TrackingLiveReplayAction.NOTIFY_ME;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class NotifyMeMenuItem extends AbstractMenuItem<GameBean> {

private final I18n i18n;
private final LiveReplayService liveReplayService;

@Override
protected void onClicked() {
liveReplayService.performActionWhenAvailable(object, NOTIFY_ME);
}

@Override
protected boolean isItemVisible() {
boolean isValid = object != null && object.getStartTime() != null;
if (!isValid) {
return false;
}

Optional<TrackingLiveReplay> trackingLiveReplayOptional = liveReplayService.getTrackingLiveReplay();
return trackingLiveReplayOptional.isEmpty() || trackingLiveReplayOptional.stream()
.anyMatch(trackingLiveReplay -> !trackingLiveReplay.getGameId().equals(object.getId()) || trackingLiveReplay.getAction() != NOTIFY_ME);
}

@Override
protected String getItemText() {
return i18n.get("vault.liveReplays.contextMenu.notifyMe");
}
}
@@ -0,0 +1,45 @@
package com.faforever.client.fx.contextmenu;

import com.faforever.client.domain.GameBean;
import com.faforever.client.i18n.I18n;
import com.faforever.client.replay.LiveReplayService;
import com.faforever.client.replay.TrackingLiveReplay;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Optional;

import static com.faforever.client.replay.TrackingLiveReplayAction.RUN_REPLAY;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class RunReplayImmediatelyMenuItem extends AbstractMenuItem<GameBean> {

private final I18n i18n;
private final LiveReplayService liveReplayService;

@Override
protected void onClicked() {
liveReplayService.performActionWhenAvailable(object, RUN_REPLAY);
}

@Override
protected boolean isItemVisible() {
boolean isValid = object != null && object.getStartTime() != null;
if (!isValid) {
return false;
}

Optional<TrackingLiveReplay> trackingLiveReplayOptional = liveReplayService.getTrackingLiveReplay();
return trackingLiveReplayOptional.isEmpty() || trackingLiveReplayOptional.stream()
.anyMatch(trackingLiveReplay -> !trackingLiveReplay.getGameId().equals(object.getId()) || trackingLiveReplay.getAction() != RUN_REPLAY);
}

@Override
protected String getItemText() {
return i18n.get("vault.liveReplays.contextMenu.runReplayImmediately");
}
}
Expand Up @@ -4,7 +4,7 @@
import com.faforever.client.game.PlayerStatus;
import com.faforever.client.i18n.I18n;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.replay.ReplayService;
import com.faforever.client.replay.LiveReplayService;
import com.faforever.client.util.Assert;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -19,14 +19,14 @@
public class WatchGameMenuItem extends AbstractMenuItem<PlayerBean> {

private final I18n i18n;
private final ReplayService replayService;
private final LiveReplayService liveReplayService;
private final NotificationService notificationService;

@Override
protected void onClicked() {
Assert.checkNullIllegalState(object, "no player has been set");
try {
replayService.runLiveReplay(object.getGame().getId());
liveReplayService.runLiveReplay(object.getGame().getId());
} catch (Exception e) {
log.error("Cannot display live replay", e);
notificationService.addImmediateErrorNotification(e, "replays.live.loadFailure.message");
Expand Down

0 comments on commit a4dd4e9

Please sign in to comment.