From d5b5caf0d01fa0580aab03bfa278348cb0ade4f6 Mon Sep 17 00:00:00 2001
From: tobias <thinkdifferent055@gmail.com>
Date: Sun, 21 Nov 2021 16:54:42 +0100
Subject: [PATCH] #174 - Add remote playwall connection icon to toolbar

---
 .../DesktopMenuToolbarViewController.java     |  3 +-
 .../main/listener/LockedListener.java         |  5 +-
 .../WebApiRemoteConnectionStateListener.java  | 69 +++++++++++++++++++
 .../plugin/webapi/lang/base_de.properties     |  1 +
 .../playpad/plugin/api/WebApiPlugin.scala     |  6 +-
 .../WebApiSettingsViewController.scala        |  7 +-
 6 files changed, 80 insertions(+), 11 deletions(-)
 create mode 100644 PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java

diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
index 3e5ef927..2f3410e6 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
@@ -21,7 +21,6 @@ import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.profile.ProfileNotFoundException;
 import de.tobias.playpad.profile.ProfileSettings;
 import de.tobias.playpad.profile.ref.ProfileReference;
-import de.tobias.playpad.profile.ref.ProfileReferenceManager;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.project.ProjectNotFoundException;
 import de.tobias.playpad.project.ProjectReader.ProjectReaderDelegate.ProfileAbortException;
@@ -148,7 +147,7 @@ public class DesktopMenuToolbarViewController extends BasicMenuToolbarViewContro
 	private ToggleButton colorButton;
 	private Button addPageButton;
 
-	private IMainViewController mainViewController;
+	private final IMainViewController mainViewController;
 
 	private transient ProjectSettingsViewController projectSettingsViewController;
 	private transient ProfileSettingsViewController profileSettingsViewController;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
index 00748a4a..43127d5e 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
@@ -10,9 +10,8 @@ import javafx.scene.control.Label;
 
 public class LockedListener implements ChangeListener<Boolean> {
 
-	private IMainViewController mainViewController;
-
-	private Label lockedLabel;
+	private final IMainViewController mainViewController;
+	private final Label lockedLabel;
 
 	public LockedListener(IMainViewController mainViewController) {
 		this.mainViewController = mainViewController;
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java
new file mode 100644
index 00000000..1f1dc56f
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java
@@ -0,0 +1,69 @@
+package de.tobias.playpad.plugin.api.client;
+
+import com.neovisionaries.ws.client.WebSocketState;
+import de.thecodelabs.utils.ui.icon.FontAwesomeType;
+import de.thecodelabs.utils.ui.icon.FontIcon;
+import de.thecodelabs.utils.util.Localization;
+import de.tobias.playpad.api.PlayPadClient;
+import de.tobias.playpad.plugin.MainWindowListener;
+import de.tobias.playpad.plugin.api.WebApiPlugin$;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+import javafx.beans.InvalidationListener;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.scene.control.Tooltip;
+
+import java.util.stream.Collectors;
+
+public class WebApiRemoteConnectionStateListener implements MainWindowListener {
+
+	private final FontIcon connectionStateIcon;
+	private final IntegerProperty connectedProperty;
+
+	public WebApiRemoteConnectionStateListener() {
+		connectionStateIcon = new FontIcon(FontAwesomeType.CLOUD);
+		connectionStateIcon.setSize(20);
+
+		connectedProperty = new SimpleIntegerProperty(0);
+		connectedProperty.addListener((observable, oldValue, newValue) -> {
+			boolean allConnected = newValue.intValue() == 0;
+
+			connectionStateIcon.setIcons(allConnected ? FontAwesomeType.CLOUD : FontAwesomeType.EXCLAMATION_CIRCLE);
+			if (!allConnected) {
+				final String disconnectedServers = WebApiPlugin$.MODULE$.connections().entrySet().stream()
+						.filter(entry -> entry.getValue().getPlayPadConnectionState() != WebSocketState.OPEN)
+						.map(entry -> entry.getKey().getName()).collect(Collectors.joining(", "));
+				connectionStateIcon.setTooltip(new Tooltip(Localization.getString("webapi-settings.remote.state.tooltip", disconnectedServers)));
+				connectionStateIcon.setStyle("-fx-text-fill: red;");
+			} else {
+				connectionStateIcon.setTooltip(null);
+				connectionStateIcon.setStyle("");
+			}
+		});
+
+		WebApiPlugin$.MODULE$.connections().addListener((InvalidationListener) observable -> createConnectionStateBinding());
+		createConnectionStateBinding();
+	}
+
+	private void createConnectionStateBinding() {
+		connectedProperty.bind(Bindings.createIntegerBinding(() -> (int) WebApiPlugin$.MODULE$.connections().values().stream()
+						.filter(client -> client.getPlayPadConnectionState() != WebSocketState.OPEN)
+						.count(),
+				WebApiPlugin$.MODULE$.connections().values().stream()
+						.map(PlayPadClient::playPadConnectionState)
+						.toArray(ObjectProperty[]::new))
+		);
+	}
+
+	@Override
+	public void onInit(IMainViewController mainViewController) {
+		mainViewController.performLayoutDependedAction((oldToolbar, newToolbar) -> {
+			if (oldToolbar != null) {
+				oldToolbar.removeToolbarItem(connectionStateIcon);
+			}
+			newToolbar.addToolbarItem(connectionStateIcon);
+		});
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
index 9c330604..7e270638 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
@@ -6,6 +6,7 @@ webapi.settings.server.port=Port
 webapi.settings.server.restart=\u00C4nderungen an den Einstellungen werden erst mit einem Neustart von PlayWall wirksam.
 
 webapi-settings.remote=PlayWall Instanzen fernsteuern
+webapi-settings.remote.state.tooltip=Keine Verbindung zu: {}
 webapi-settings.remote.name=Name
 webapi-settings.remote.address=IP-Address/Host
 webapi-settings.remote.port=Port
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
index e0fb6496..7d05ca35 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
@@ -10,10 +10,12 @@ import de.thecodelabs.utils.threading.Worker
 import de.thecodelabs.utils.util.Localization
 import de.tobias.playpad.PlayPadPlugin
 import de.tobias.playpad.api.{PlayPadClient, PlayPadClientImpl}
+import de.tobias.playpad.plugin.api.client.WebApiRemoteConnectionStateListener
 import de.tobias.playpad.plugin.api.settings.{WebApiRemoteSettings, WebApiSettings, WebApiSettingsViewController}
 import de.tobias.playpad.plugin.api.websocket.WebSocketHandler
 import de.tobias.playpad.plugin.api.websocket.listener.{PadStatusListener, ProjectListener}
 import de.tobias.playpad.plugin.{Module, PlayPadPluginStub}
+import javafx.collections.{FXCollections, ObservableMap}
 import spark.{Request, Response, Spark}
 
 import java.nio.file.{Files, Path}
@@ -66,6 +68,7 @@ class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 		})
 
 		PlayPadPlugin.getInstance().addGlobalSettingsTab(() => new WebApiSettingsViewController(webApiSettings))
+		PlayPadPlugin.getInstance().addMainViewListener(new WebApiRemoteConnectionStateListener)
 	}
 
 	override def shutdown(): Unit = {
@@ -80,7 +83,8 @@ class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 object WebApiPlugin {
 	def getWebApiSettingsPath: Path = ApplicationUtils.getApplication.getPath(PathType.CONFIGURATION, "webapi.json")
 
-	var connections: util.Map[WebApiRemoteSettings, PlayPadClient] = new util.HashMap()
+	var connections: ObservableMap[WebApiRemoteSettings, PlayPadClient] = FXCollections.observableMap(
+		new util.HashMap[WebApiRemoteSettings, PlayPadClient]())
 
 	def getConnection(id: UUID): Optional[PlayPadClient] = {
 		connections.entrySet().stream().filter(entry => entry.getKey.getId == id).findFirst().map(_.getValue)
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala
index 2cf3262c..30de1703 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala
@@ -17,11 +17,8 @@ class WebApiSettingsViewController(val webApiSettings: WebApiSettings) extends G
 
 	load("plugin/webapi/view", "WebApiSettings", Localization.getBundle)
 
-	@FXML
-	var activeCheckbox: CheckBox = _
-
-	@FXML
-	var portTextField: NumberTextField = _
+	@FXML var activeCheckbox: CheckBox = _
+	@FXML var portTextField: NumberTextField = _
 
 	@FXML var remoteListView: ListView[WebApiRemoteSettings] = _
 	@FXML var remoteAddButton: Button = _
-- 
GitLab