package com.faforever.client.chat; import com.faforever.client.fx.JavaFxUtil; import com.faforever.client.user.UserService; import com.faforever.client.util.ProgrammingError; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import org.springframework.context.ApplicationContext; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; public class ChatController { private final Map<String, AbstractChatTabController> nameToChatTabController; @Resource ChatService chatService; @Resource ApplicationContext applicationContext; @Resource UserService userService; @Resource EventBus eventBus; @FXML Node chatRoot; @FXML TabPane chatsTabPane; @FXML Pane connectingProgressPane; @FXML VBox noOpenTabsContainer; @FXML TextField channelNameTextField; public ChatController() { nameToChatTabController = new HashMap<>(); } @PostConstruct void postConstruct() { eventBus.register(this); chatService.addOnMessageListener(this::onChannelMessage); chatService.addOnPrivateChatMessageListener(this::onPrivateMessage); chatService.addChannelsListener(change -> { if (change.wasRemoved()) { onChannelLeft(change.getValueRemoved()); } if (change.wasAdded()) { onChannelJoined(change.getValueAdded()); } }); chatService.connectionStateProperty().addListener((observable, oldValue, newValue) -> { switch (newValue) { case DISCONNECTED: onDisconnected(); break; case CONNECTED: onConnected(); break; case CONNECTING: onConnecting(); break; default: throw new ProgrammingError("Uncovered connection state: " + newValue); } }); userService.loggedInProperty().addListener((observable, oldValue, newValue) -> { if (!newValue) { onLoggedOut(); } }); } private void onChannelLeft(Channel channel) { removeTab(channel.getName()); } private void onChannelJoined(Channel channel) { Platform.runLater(() -> getOrCreateChannelTab(channel.getName())); } private void onDisconnected() { connectingProgressPane.setVisible(true); chatsTabPane.setVisible(false); noOpenTabsContainer.setVisible(false); } private void onConnected() { connectingProgressPane.setVisible(false); chatsTabPane.setVisible(true); noOpenTabsContainer.setVisible(false); } private void onConnecting() { connectingProgressPane.setVisible(true); chatsTabPane.setVisible(false); noOpenTabsContainer.setVisible(false); } private void onLoggedOut() { Platform.runLater(() -> chatsTabPane.getTabs().clear()); } private void removeTab(String playerOrChannelName) { nameToChatTabController.remove(playerOrChannelName); if (nameToChatTabController.containsKey(playerOrChannelName)) { chatsTabPane.getTabs().remove(nameToChatTabController.remove(playerOrChannelName).getRoot()); } } private AbstractChatTabController getOrCreateChannelTab(String channelName) { JavaFxUtil.assertApplicationThread(); if (!nameToChatTabController.containsKey(channelName)) { ChannelTabController tab = applicationContext.getBean(ChannelTabController.class); tab.setChannel(chatService.getOrCreateChannel(channelName)); addTab(channelName, tab); } return nameToChatTabController.get(channelName); } private void addTab(String playerOrChannelName, AbstractChatTabController tabController) { nameToChatTabController.put(playerOrChannelName, tabController); chatsTabPane.getTabs().add(tabController.getRoot()); } @FXML void initialize() { onDisconnected(); chatsTabPane.getTabs().addListener((ListChangeListener<Tab>) change -> { while (change.next()) { change.getRemoved().forEach(tab -> nameToChatTabController.remove(tab.getId())); } }); chatsTabPane.getTabs().addListener((InvalidationListener) observable -> noOpenTabsContainer.setVisible(chatsTabPane.getTabs().isEmpty())); } private void onChannelMessage(ChatMessage chatMessage) { Platform.runLater(() -> getOrCreateChannelTab(chatMessage.getSource()).onChatMessage(chatMessage)); } private void onPrivateMessage(ChatMessage chatMessage) { JavaFxUtil.assertBackgroundThread(); Platform.runLater(() -> addAndGetPrivateMessageTab(chatMessage.getSource()).onChatMessage(chatMessage)); } private AbstractChatTabController addAndGetPrivateMessageTab(String username) { JavaFxUtil.assertApplicationThread(); if (!nameToChatTabController.containsKey(username)) { PrivateChatTabController tab = applicationContext.getBean(PrivateChatTabController.class); tab.setReceiver(username); addTab(username, tab); } return nameToChatTabController.get(username); } public Node getRoot() { return chatRoot; } @Subscribe public void onInitiatePrivateChatEvent(InitiatePrivateChatEvent event) { openPrivateMessageTabForUser(event.getUsername()); } void openPrivateMessageTabForUser(String username) { if (username.equalsIgnoreCase(userService.getUsername())) { return; } AbstractChatTabController controller = addAndGetPrivateMessageTab(username); chatsTabPane.getSelectionModel().select(controller.getRoot()); } @FXML void onJoinChannelButtonClicked() { String channelName = channelNameTextField.getText(); channelNameTextField.clear(); joinChannel(channelName); } private void joinChannel(String channelName) { chatService.addUsersListener(channelName, change -> { if (change.wasRemoved()) { onChatUserLeftChannel(channelName, change.getValueRemoved().getUsername()); } if (change.wasAdded()) { onUserJoinedChannel(channelName, change.getValueAdded()); } }); chatService.joinChannel(channelName); } private void onChatUserLeftChannel(String channelName, String username) { if (!username.equalsIgnoreCase(userService.getUsername())) { return; } AbstractChatTabController chatTab = nameToChatTabController.get(channelName); if (chatTab != null) { chatsTabPane.getTabs().remove(chatTab.getRoot()); } } private void onUserJoinedChannel(String channelName, ChatUser chatUser) { Platform.runLater(() -> { if (isCurrentUser(chatUser)) { onConnected(); } }); } private boolean isCurrentUser(ChatUser chatUser) { return chatUser.getUsername().equalsIgnoreCase(userService.getUsername()); } }