Improve UI doc and fix minor UI bugs
This commit is contained in:
parent
a3873394be
commit
6d3971be40
20 changed files with 541 additions and 185 deletions
|
@ -43,6 +43,7 @@ public class MainApp extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws Exception {
|
public void stop() throws Exception {
|
||||||
|
// Stop all threads and active connections before exiting
|
||||||
userList.destroy();
|
userList.destroy();
|
||||||
super.stop();
|
super.stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,14 @@ public class ChatHistory {
|
||||||
private final DatabaseController db;
|
private final DatabaseController db;
|
||||||
private final PeerUser user;
|
private final PeerUser user;
|
||||||
private final ObservableList<Message> history;
|
private final ObservableList<Message> history;
|
||||||
private final ArrayList<HistoryLoadedCallback> historyListener;
|
private HistoryLoadedCallback historyListener;
|
||||||
|
private boolean historyLoaded;
|
||||||
|
|
||||||
public ChatHistory(PeerUser user) {
|
public ChatHistory(PeerUser user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
db = new DatabaseController();
|
db = new DatabaseController();
|
||||||
history = FXCollections.observableArrayList();
|
history = FXCollections.observableArrayList();
|
||||||
historyListener = new ArrayList<>();
|
historyLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,34 +30,29 @@ public class ChatHistory {
|
||||||
*
|
*
|
||||||
* @param listener The listener to add
|
* @param listener The listener to add
|
||||||
*/
|
*/
|
||||||
public void addHistoryLoadedListener(HistoryLoadedCallback listener) {
|
public void setHistoryLoadedListener(HistoryLoadedCallback listener) {
|
||||||
historyListener.add(listener);
|
historyListener = listener;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a listener for history loaded event
|
|
||||||
*
|
|
||||||
* @param listener The listener to remove
|
|
||||||
*/
|
|
||||||
public void removeHistoryLoadedListener(HistoryLoadedCallback listener) {
|
|
||||||
historyListener.remove(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies all listeners of a history loaded event
|
* Notifies all listeners of a history loaded event
|
||||||
*/
|
*/
|
||||||
private void notifyHistoryLoaded() {
|
private void notifyHistoryLoaded() {
|
||||||
historyListener.forEach(l -> l.onHistoryLoaded(user));
|
historyListener.onHistoryLoaded(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads history from database
|
* Loads history from database only if it has not previously been loaded
|
||||||
*/
|
*/
|
||||||
public void load() {
|
public void load() {
|
||||||
final Date from = new Date();
|
if (!historyLoaded) {
|
||||||
// Load whole history
|
final Date from = new Date();
|
||||||
from.setTime(0);
|
// Load whole history
|
||||||
db.getChatHistory(new UserInformation(user), from, new Date(), this::onLoaded);
|
from.setTime(0);
|
||||||
|
db.getChatHistory(new UserInformation(user), from, new Date(), this::onLoaded);
|
||||||
|
} else {
|
||||||
|
notifyHistoryLoaded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +61,7 @@ public class ChatHistory {
|
||||||
* @param newHistory The fetched history
|
* @param newHistory The fetched history
|
||||||
*/
|
*/
|
||||||
private void onLoaded(ArrayList<Message> newHistory) {
|
private void onLoaded(ArrayList<Message> newHistory) {
|
||||||
|
historyLoaded = true;
|
||||||
history.addAll(newHistory);
|
history.addAll(newHistory);
|
||||||
history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
|
history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
|
||||||
Log.v(getClass().getSimpleName(), "Message history loaded");
|
Log.v(getClass().getSimpleName(), "Message history loaded");
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to create callbacks for button press events
|
||||||
|
*/
|
||||||
public interface ButtonPressEvent {
|
public interface ButtonPressEvent {
|
||||||
public void onPress();
|
public void onPress();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,31 @@ import javafx.scene.layout.VBox;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the error screen
|
||||||
|
*/
|
||||||
public class ErrorScreenController implements Initializable {
|
public class ErrorScreenController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private VBox container;
|
private VBox container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly shows the error screen
|
||||||
|
*/
|
||||||
public void show() {
|
public void show() {
|
||||||
container.setVisible(true);
|
container.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly hides the error screen
|
||||||
|
*/
|
||||||
public void hide() {
|
public void hide() {
|
||||||
container.setVisible(false);
|
container.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exits the app on button press
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onPress() {
|
private void onPress() {
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
|
|
|
@ -6,13 +6,16 @@ import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the loading screen
|
||||||
|
*/
|
||||||
public class LoadingScreenController implements Initializable {
|
public class LoadingScreenController implements Initializable {
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public VBox indicatorOnlyContainer;
|
public VBox indicatorOnlyContainer;
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -22,27 +25,45 @@ public class LoadingScreenController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
public StackPane container;
|
public StackPane container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly shows the loading screen
|
||||||
|
*/
|
||||||
public void show() {
|
public void show() {
|
||||||
container.setVisible(true);
|
container.setVisible(true);
|
||||||
setLabel(null);
|
setLabel(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly shows the loading screen
|
||||||
|
* @param label The text to display bellow the loading indicator
|
||||||
|
*/
|
||||||
public void show(String label) {
|
public void show(String label) {
|
||||||
container.setVisible(true);
|
container.setVisible(true);
|
||||||
setLabel(label);
|
setLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly hides the loading screen
|
||||||
|
*/
|
||||||
public void hide() {
|
public void hide() {
|
||||||
container.setVisible(false);
|
container.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLabel(String label) {
|
/**
|
||||||
|
* Sets the text to display bellow the loading indicator
|
||||||
|
* @param label The text to use
|
||||||
|
*/
|
||||||
|
public void setLabel(@Nullable String label) {
|
||||||
loadingLabel.setText(label);
|
loadingLabel.setText(label);
|
||||||
final boolean showLabel = label != null && !label.isEmpty();
|
final boolean showLabel = label != null && !label.isEmpty();
|
||||||
labeledContainer.setVisible(showLabel);
|
labeledContainer.setVisible(showLabel);
|
||||||
indicatorOnlyContainer.setVisible(!showLabel);
|
indicatorOnlyContainer.setVisible(!showLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the main container styles to apply custom styling
|
||||||
|
* @return The style list
|
||||||
|
*/
|
||||||
public ObservableList<String> getRootStyle() {
|
public ObservableList<String> getRootStyle() {
|
||||||
return container.getStyleClass();
|
return container.getStyleClass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,9 +48,11 @@ public class MainController implements Initializable {
|
||||||
private JFXSnackbar snackbar;
|
private JFXSnackbar snackbar;
|
||||||
private UserList userList;
|
private UserList userList;
|
||||||
private boolean historyLoaded;
|
private boolean historyLoaded;
|
||||||
|
private boolean online;
|
||||||
|
|
||||||
public MainController() {
|
public MainController() {
|
||||||
historyLoaded = false;
|
historyLoaded = false;
|
||||||
|
online = false;
|
||||||
currentUser = CurrentUser.getInstance();
|
currentUser = CurrentUser.getInstance();
|
||||||
currentUser.addObserver(propertyChangeEvent -> {
|
currentUser.addObserver(propertyChangeEvent -> {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
||||||
|
@ -60,6 +62,14 @@ public class MainController implements Initializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current user becomes valid, start the chat.
|
||||||
|
* If it is invalid or not set, show the login screen.
|
||||||
|
* <p>
|
||||||
|
* If the history is not yet loaded or the user is not initialized, do nothing.
|
||||||
|
*
|
||||||
|
* @param newState The new user state
|
||||||
|
*/
|
||||||
private void onCurrentUserStateChange(CurrentUser.State newState) {
|
private void onCurrentUserStateChange(CurrentUser.State newState) {
|
||||||
// Only do an action if the history is loaded
|
// Only do an action if the history is loaded
|
||||||
if (historyLoaded) {
|
if (historyLoaded) {
|
||||||
|
@ -75,15 +85,32 @@ public class MainController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the history loaded after the current user, fire the state change event again
|
||||||
|
*/
|
||||||
public void onHistoryLoaded() {
|
public void onHistoryLoaded() {
|
||||||
historyLoaded = true;
|
historyLoaded = true;
|
||||||
final CurrentUser.State userState = CurrentUser.getInstance().getState();
|
final CurrentUser.State userState = CurrentUser.getInstance().getState();
|
||||||
// If the history loaded after the current user, fire the state change event again
|
|
||||||
if (userState != CurrentUser.State.UNINITIALIZED) {
|
if (userState != CurrentUser.State.UNINITIALIZED) {
|
||||||
onCurrentUserStateChange(userState);
|
onCurrentUserStateChange(userState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a dialog allowing the user to set its username.
|
||||||
|
* Depending on the mode, the behavior is different.
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* On initial mode, canceling the dialog exists the app.
|
||||||
|
* Any other mode shows the chat.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* On edit mode, confirming the dialog shows a snackbar.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
*/
|
||||||
private void openEditUsernameDialog(EditUsernameDialogController.Mode mode) {
|
private void openEditUsernameDialog(EditUsernameDialogController.Mode mode) {
|
||||||
final boolean initialMode = mode == EditUsernameDialogController.Mode.INITIAL;
|
final boolean initialMode = mode == EditUsernameDialogController.Mode.INITIAL;
|
||||||
editUserDialogController.setOnCancelListener(() -> {
|
editUserDialogController.setOnCancelListener(() -> {
|
||||||
|
@ -101,6 +128,12 @@ public class MainController implements Initializable {
|
||||||
Platform.runLater(() -> editUserDialogController.show(root, mode));
|
Platform.runLater(() -> editUserDialogController.show(root, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a snackbar
|
||||||
|
*
|
||||||
|
* @param text The text to show
|
||||||
|
* @param mode The mode to use
|
||||||
|
*/
|
||||||
private void showSnackbarEvent(String text, SnackbarController.Mode mode) {
|
private void showSnackbarEvent(String text, SnackbarController.Mode mode) {
|
||||||
try {
|
try {
|
||||||
final FXMLLoader loader = new FXMLLoader(getClass().getResource("dialogs/snackbar.fxml"));
|
final FXMLLoader loader = new FXMLLoader(getClass().getResource("dialogs/snackbar.fxml"));
|
||||||
|
@ -115,12 +148,19 @@ public class MainController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the about dialog
|
||||||
|
*/
|
||||||
private void openAboutDialog() {
|
private void openAboutDialog() {
|
||||||
aboutDialogController.show(root);
|
aboutDialogController.show(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a broadcast over the network to discover active users.
|
||||||
|
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||||
|
*/
|
||||||
private void discoverActiveUsers() {
|
private void discoverActiveUsers() {
|
||||||
if (userList != null) {
|
if (userList != null && online) {
|
||||||
userList.discoverActiveUsers((e) -> {
|
userList.discoverActiveUsers((e) -> {
|
||||||
Log.e(this.getClass().getSimpleName(), "Error discovering users", e);
|
Log.e(this.getClass().getSimpleName(), "Error discovering users", e);
|
||||||
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
||||||
|
@ -128,6 +168,10 @@ public class MainController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start listening to broadcast and chat requests.
|
||||||
|
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||||
|
*/
|
||||||
private void startListening() {
|
private void startListening() {
|
||||||
if (userList != null) {
|
if (userList != null) {
|
||||||
userList.startDiscoveryListening();
|
userList.startDiscoveryListening();
|
||||||
|
@ -138,30 +182,46 @@ public class MainController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopListening() {
|
/**
|
||||||
if (userList != null) {
|
* Starts network related functions to allow for chat functionality.
|
||||||
userList.destroy();
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startChat() {
|
private void startChat() {
|
||||||
|
online = true;
|
||||||
|
listController.setRefreshButtonEnabled(true);
|
||||||
Log.v(this.getClass().getSimpleName(), "Chat started");
|
Log.v(this.getClass().getSimpleName(), "Chat started");
|
||||||
discoverActiveUsers();
|
discoverActiveUsers();
|
||||||
startListening();
|
startListening();
|
||||||
Platform.runLater(this::showChat);
|
Platform.runLater(this::showChat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops any network related functions to disable chat functionality.
|
||||||
|
*/
|
||||||
private void endChat() {
|
private void endChat() {
|
||||||
|
online = false;
|
||||||
|
listController.setRefreshButtonEnabled(false);
|
||||||
Log.v(this.getClass().getSimpleName(), "Chat ended");
|
Log.v(this.getClass().getSimpleName(), "Chat ended");
|
||||||
stopListening();
|
if (userList != null) {
|
||||||
|
userList.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simply shows the chat.
|
||||||
|
* This does not start any network related functions.
|
||||||
|
* Use this for offline browsing.
|
||||||
|
*/
|
||||||
private void showChat() {
|
private void showChat() {
|
||||||
Log.v(this.getClass().getSimpleName(), "Chat shown");
|
Log.v(this.getClass().getSimpleName(), "Chat shown");
|
||||||
loadingController.hide();
|
loadingController.hide();
|
||||||
mainContainer.setVisible(true);
|
mainContainer.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a screen allowing the user to set its username.
|
||||||
|
*
|
||||||
|
* @param isError True is the screen is shown because of an error, false otherwise
|
||||||
|
*/
|
||||||
private void showLogin(boolean isError) {
|
private void showLogin(boolean isError) {
|
||||||
Log.v(this.getClass().getSimpleName(), "Login shown");
|
Log.v(this.getClass().getSimpleName(), "Login shown");
|
||||||
mainContainer.setVisible(false);
|
mainContainer.setVisible(false);
|
||||||
|
@ -180,6 +240,9 @@ public class MainController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an error screen telling the user the app failed to start
|
||||||
|
*/
|
||||||
private void showError() {
|
private void showError() {
|
||||||
Log.v(this.getClass().getSimpleName(), "Error shown");
|
Log.v(this.getClass().getSimpleName(), "Error shown");
|
||||||
mainContainer.setVisible(false);
|
mainContainer.setVisible(false);
|
||||||
|
@ -193,12 +256,15 @@ public class MainController implements Initializable {
|
||||||
snackbar = new JFXSnackbar(root);
|
snackbar = new JFXSnackbar(root);
|
||||||
|
|
||||||
listController.setUserSelectedListener((user) -> chatController.setRemoteUser(user));
|
listController.setUserSelectedListener((user) -> chatController.setRemoteUser(user));
|
||||||
chatController.addAttachmentListener(() -> System.out.println("attach event"));
|
chatController.setAttachmentListener(() -> System.out.println("attach event"));
|
||||||
chatController.addSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
|
chatController.setSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
|
||||||
toolbarController.addEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
|
toolbarController.setEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
|
||||||
toolbarController.addAboutListener(this::openAboutDialog);
|
toolbarController.setAboutListener(this::openAboutDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates database if needed, then init current user, and finally load user list
|
||||||
|
*/
|
||||||
private void initDb() {
|
private void initDb() {
|
||||||
final DatabaseController db = new DatabaseController();
|
final DatabaseController db = new DatabaseController();
|
||||||
db.init(() -> {
|
db.init(() -> {
|
||||||
|
@ -212,6 +278,12 @@ public class MainController implements Initializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user list to use.
|
||||||
|
* We must set it from the MainApp to allow destroying it on exit.
|
||||||
|
*
|
||||||
|
* @param userList The user list to use
|
||||||
|
*/
|
||||||
public void setUserList(UserList userList) {
|
public void setUserList(UserList userList) {
|
||||||
this.userList = userList;
|
this.userList = userList;
|
||||||
listController.setUserList(userList);
|
listController.setUserList(userList);
|
||||||
|
|
|
@ -4,6 +4,11 @@ import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.MultipleSelectionModel;
|
import javafx.scene.control.MultipleSelectionModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model used to disable list selection
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
public class NoSelectionModel<T> extends MultipleSelectionModel<T> {
|
public class NoSelectionModel<T> extends MultipleSelectionModel<T> {
|
||||||
@Override
|
@Override
|
||||||
public ObservableList<Integer> getSelectedIndices() {
|
public ObservableList<Integer> getSelectedIndices() {
|
||||||
|
|
|
@ -11,29 +11,40 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the main screen toolbar
|
||||||
|
*/
|
||||||
public class ToolbarController implements Initializable {
|
public class ToolbarController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label currentUsernameLabel;
|
private Label currentUsernameLabel;
|
||||||
|
|
||||||
private List<ButtonPressEvent> editListeners;
|
private ButtonPressEvent editListeners;
|
||||||
private List<ButtonPressEvent> aboutListeners;
|
private ButtonPressEvent aboutListeners;
|
||||||
|
|
||||||
public void addEditListener(ButtonPressEvent listener) {
|
public void setEditListener(ButtonPressEvent listener) {
|
||||||
editListeners.add(listener);
|
editListeners = listener;
|
||||||
}
|
}
|
||||||
public void addAboutListener(ButtonPressEvent listener) {
|
public void setAboutListener(ButtonPressEvent listener) {
|
||||||
aboutListeners.add(listener);
|
aboutListeners = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEditPress() {
|
public void onEditPress() {
|
||||||
editListeners.forEach(ButtonPressEvent::onPress);
|
if (editListeners != null) {
|
||||||
|
editListeners.onPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAboutPress() {
|
public void onAboutPress() {
|
||||||
aboutListeners.forEach(ButtonPressEvent::onPress);
|
if (aboutListeners != null) {
|
||||||
|
aboutListeners.onPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the text on current username change
|
||||||
|
*
|
||||||
|
* @param propertyChangeEvent The change event
|
||||||
|
*/
|
||||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
||||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||||
|
@ -44,7 +55,5 @@ public class ToolbarController implements Initializable {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
CurrentUser.getInstance().addObserver(this::onUsernameChange);
|
CurrentUser.getInstance().addObserver(this::onUsernameChange);
|
||||||
editListeners = new ArrayList<>();
|
|
||||||
aboutListeners = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ import javafx.scene.layout.VBox;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the chat window
|
||||||
|
*/
|
||||||
public class ChatController implements Initializable {
|
public class ChatController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -32,40 +35,50 @@ public class ChatController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private VBox emptyContainer;
|
private VBox emptyContainer;
|
||||||
private PeerUser remoteUser;
|
private PeerUser remoteUser;
|
||||||
private final ChatHistory.HistoryLoadedCallback onHistoryLoaded = (PeerUser user) -> {
|
|
||||||
|
public void setAttachmentListener(ButtonPressEvent listener) {
|
||||||
|
chatFooterController.setAttachmentListener(listener);
|
||||||
|
}
|
||||||
|
public void setSendErrorListener(ErrorCallback listener) {
|
||||||
|
chatFooterController.setSendErrorListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the user that finished loading is the right one then set the chat state to done
|
||||||
|
* @param user The user that finished loading
|
||||||
|
*/
|
||||||
|
private void onHistoryLoaded (PeerUser user) {
|
||||||
if (user.equals(remoteUser)) {
|
if (user.equals(remoteUser)) {
|
||||||
setState(State.DONE);
|
setState(State.DONE);
|
||||||
|
scrollToEnd();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public void addAttachmentListener(ButtonPressEvent listener) {
|
|
||||||
chatFooterController.addAttachmentListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSendErrorListener(ErrorCallback listener) {
|
|
||||||
chatFooterController.addSendErrorListener(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the remote user to chat with and subscribe to its messages
|
||||||
|
*
|
||||||
|
* @param remoteUser The user to chat with
|
||||||
|
*/
|
||||||
public void setRemoteUser(PeerUser remoteUser) {
|
public void setRemoteUser(PeerUser remoteUser) {
|
||||||
this.chatFooterController.setRemoteUser(remoteUser);
|
this.chatFooterController.setRemoteUser(remoteUser);
|
||||||
this.chatHeaderController.setRemoteUser(remoteUser);
|
this.chatHeaderController.setRemoteUser(remoteUser);
|
||||||
setState(State.LOADING);
|
setState(State.LOADING);
|
||||||
|
|
||||||
if (this.remoteUser != null) {
|
|
||||||
this.remoteUser.getHistory().removeHistoryLoadedListener(onHistoryLoaded);
|
|
||||||
}
|
|
||||||
this.remoteUser = remoteUser;
|
this.remoteUser = remoteUser;
|
||||||
|
|
||||||
final ChatHistory history = remoteUser.getHistory();
|
final ChatHistory history = remoteUser.getHistory();
|
||||||
history.addHistoryLoadedListener(onHistoryLoaded);
|
history.setHistoryLoadedListener(this::onHistoryLoaded);
|
||||||
messageList.setItems(history.getHistory());
|
messageList.setItems(history.getHistory());
|
||||||
messageList.getItems().addListener((ListChangeListener<? super Message>) (c) -> {
|
messageList.getItems().addListener((ListChangeListener<? super Message>) (c) -> {
|
||||||
c.next();
|
c.next();
|
||||||
|
// Make sure we always have the latest item on screen
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
});
|
});
|
||||||
history.load();
|
history.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to the end of the message list
|
||||||
|
*/
|
||||||
private void scrollToEnd() {
|
private void scrollToEnd() {
|
||||||
final int size = messageList.getItems().size();
|
final int size = messageList.getItems().size();
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
|
@ -73,11 +86,26 @@ public class ChatController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the chat state.
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* On Initial state, show an empty screen
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* on Loading state, show the loading screen
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* on Done state, show the chat
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param state The new state to set
|
||||||
|
*/
|
||||||
private void setState(State state) {
|
private void setState(State state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
emptyContainer.setVisible(true);
|
emptyContainer.setVisible(true);
|
||||||
chatFooterController.setEnabled(false);
|
|
||||||
chatContainer.setVisible(false);
|
chatContainer.setVisible(false);
|
||||||
loadingController.hide();
|
loadingController.hide();
|
||||||
messageList.setItems(null);
|
messageList.setItems(null);
|
||||||
|
@ -85,11 +113,9 @@ public class ChatController implements Initializable {
|
||||||
case LOADING:
|
case LOADING:
|
||||||
chatContainer.setVisible(true);
|
chatContainer.setVisible(true);
|
||||||
loadingController.show();
|
loadingController.show();
|
||||||
chatFooterController.setEnabled(false);
|
|
||||||
emptyContainer.setVisible(false);
|
emptyContainer.setVisible(false);
|
||||||
break;
|
break;
|
||||||
case DONE:
|
case DONE:
|
||||||
chatFooterController.setEnabled(true);
|
|
||||||
chatContainer.setVisible(true);
|
chatContainer.setVisible(true);
|
||||||
emptyContainer.setVisible(false);
|
emptyContainer.setVisible(false);
|
||||||
loadingController.hide();
|
loadingController.hide();
|
||||||
|
|
|
@ -6,18 +6,21 @@ import fr.insa.clavardator.ui.ButtonPressEvent;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.users.PeerUser;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.util.ErrorCallback;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.util.Log;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the chat input field and associated buttons
|
||||||
|
*/
|
||||||
public class ChatFooterController implements Initializable {
|
public class ChatFooterController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -27,25 +30,38 @@ public class ChatFooterController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton sendButton;
|
private JFXButton sendButton;
|
||||||
|
|
||||||
private List<ButtonPressEvent> attachmentListeners;
|
private ButtonPressEvent attachmentListeners;
|
||||||
private List<ErrorCallback> sendErrorListeners;
|
private ErrorCallback sendErrorListeners;
|
||||||
|
|
||||||
private PeerUser remoteUser;
|
private PeerUser remoteUser;
|
||||||
private HashMap<PeerUser, String> savedText;
|
private HashMap<PeerUser, String> savedText;
|
||||||
|
|
||||||
|
public void setAttachmentListener(ButtonPressEvent listener) {
|
||||||
public void addAttachmentListener(ButtonPressEvent listener) {
|
attachmentListeners = listener;
|
||||||
attachmentListeners.add(listener);
|
|
||||||
}
|
}
|
||||||
public void addSendErrorListener(ErrorCallback listener) {
|
|
||||||
sendErrorListeners.add(listener);
|
public void setSendErrorListener(ErrorCallback listener) {
|
||||||
|
sendErrorListeners = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAttachmentPress() {
|
public void onAttachmentPress() {
|
||||||
attachmentListeners.forEach(ButtonPressEvent::onPress);
|
if (attachmentListeners != null) {
|
||||||
|
attachmentListeners.onPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onSendError(Exception e) {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Error: Could not send message", e);
|
||||||
|
if (sendErrorListeners != null) {
|
||||||
|
sendErrorListeners.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the input text is not empty and the remote user set, send the message
|
||||||
|
*/
|
||||||
public void onSend() {
|
public void onSend() {
|
||||||
if(!textField.getText().isEmpty()) {
|
if (!isTextFieldEmpty()) {
|
||||||
if (remoteUser != null) {
|
if (remoteUser != null) {
|
||||||
remoteUser.sendTextMessage(textField.getText(), this::onSendError);
|
remoteUser.sendTextMessage(textField.getText(), this::onSendError);
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,15 +71,20 @@ public class ChatFooterController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSendError(Exception e) {
|
/**
|
||||||
Log.e(this.getClass().getSimpleName(), "Error: Could not send message", e);
|
* Enables or disables the chat controls
|
||||||
sendErrorListeners.forEach((l) -> l.onError(e));
|
*
|
||||||
}
|
* @param enabled True to enable, false otherwise
|
||||||
|
*/
|
||||||
public void setEnabled(boolean enabled) {
|
private void setEnabled(boolean enabled) {
|
||||||
container.setDisable(!enabled);
|
container.setDisable(!enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find saved input for the current user
|
||||||
|
*
|
||||||
|
* @return The saved text, or an empty string if none found
|
||||||
|
*/
|
||||||
private String findSavedText() {
|
private String findSavedText() {
|
||||||
String text = null;
|
String text = null;
|
||||||
if (remoteUser != null) {
|
if (remoteUser != null) {
|
||||||
|
@ -72,22 +93,74 @@ public class ChatFooterController implements Initializable {
|
||||||
return text != null ? text : "";
|
return text != null ? text : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the give text for the current user.
|
||||||
|
* This is used to save input when jumping between users
|
||||||
|
*
|
||||||
|
* @param text The text to save
|
||||||
|
*/
|
||||||
private void saveText(String text) {
|
private void saveText(String text) {
|
||||||
if (remoteUser != null) {
|
if (remoteUser != null) {
|
||||||
savedText.put(remoteUser, text);
|
savedText.put(remoteUser, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves text and checks if we should disable the send button on input change.
|
||||||
|
*
|
||||||
|
* @param observable The observed string
|
||||||
|
* @param oldText The previous text
|
||||||
|
* @param newText The new text
|
||||||
|
*/
|
||||||
public void onTextChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
public void onTextChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
||||||
saveText(newText);
|
saveText(newText);
|
||||||
sendButton.setDisable(isTextFieldEmpty());
|
sendButton.setDisable(isTextFieldEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the text field is empty
|
||||||
|
*
|
||||||
|
* @return True if empty, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean isTextFieldEmpty() {
|
||||||
|
return textField.getText().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates shown username and item color when user changes
|
||||||
|
*
|
||||||
|
* @param propertyChangeEvent The change event
|
||||||
|
*/
|
||||||
|
private void onUserStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
|
final String propName = propertyChangeEvent.getPropertyName();
|
||||||
|
if (propName.equals("state")) {
|
||||||
|
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||||
|
Platform.runLater(() -> setEnabled(newState == PeerUser.State.CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the remote user to send messages to, and tries to find saved input.
|
||||||
|
*
|
||||||
|
* @param user The remote user to set
|
||||||
|
*/
|
||||||
|
public void setRemoteUser(PeerUser user) {
|
||||||
|
if (this.remoteUser == null || !this.remoteUser.equals(user)) {
|
||||||
|
if (this.remoteUser != null) {
|
||||||
|
// remove old observer before setting new user
|
||||||
|
this.remoteUser.removeObserver(this::onUserStateChange);
|
||||||
|
}
|
||||||
|
this.remoteUser = user;
|
||||||
|
user.addObserver(this::onUserStateChange);
|
||||||
|
textField.setText(findSavedText());
|
||||||
|
sendButton.setDisable(isTextFieldEmpty());
|
||||||
|
setEnabled(user.isActive());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
savedText = new HashMap<>();
|
savedText = new HashMap<>();
|
||||||
attachmentListeners = new ArrayList<>();
|
|
||||||
sendErrorListeners = new ArrayList<>();
|
|
||||||
textField.textProperty().addListener(this::onTextChange);
|
textField.textProperty().addListener(this::onTextChange);
|
||||||
textField.setOnKeyPressed(event -> {
|
textField.setOnKeyPressed(event -> {
|
||||||
if (event.getCode() == KeyCode.ENTER) {
|
if (event.getCode() == KeyCode.ENTER) {
|
||||||
|
@ -96,14 +169,4 @@ public class ChatFooterController implements Initializable {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTextFieldEmpty() {
|
|
||||||
return textField.getText().equals("");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRemoteUser(PeerUser remoteUser) {
|
|
||||||
this.remoteUser = remoteUser;
|
|
||||||
textField.setText(findSavedText());
|
|
||||||
sendButton.setDisable(isTextFieldEmpty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the chat title
|
||||||
|
*/
|
||||||
public class ChatHeaderController implements Initializable {
|
public class ChatHeaderController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -20,10 +23,11 @@ public class ChatHeaderController implements Initializable {
|
||||||
private UserActiveIndicatorController indicatorController;
|
private UserActiveIndicatorController indicatorController;
|
||||||
private PeerUser user;
|
private PeerUser user;
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
* Updates username on remote user change
|
||||||
}
|
*
|
||||||
|
* @param propertyChangeEvent THe change event
|
||||||
|
*/
|
||||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
||||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||||
|
@ -31,6 +35,11 @@ public class ChatHeaderController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the remote user we are currently chatting to
|
||||||
|
*
|
||||||
|
* @param remoteUser The remote user to set
|
||||||
|
*/
|
||||||
public void setRemoteUser(PeerUser remoteUser) {
|
public void setRemoteUser(PeerUser remoteUser) {
|
||||||
if (this.user != null) {
|
if (this.user != null) {
|
||||||
// remove old observer before setting new user
|
// remove old observer before setting new user
|
||||||
|
@ -42,4 +51,7 @@ public class ChatHeaderController implements Initializable {
|
||||||
indicatorController.setUser(remoteUser);
|
indicatorController.setUser(remoteUser);
|
||||||
indicatorController.setSize(10.0);
|
indicatorController.setSize(10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,25 +24,35 @@ public class MessageListItemController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private Label timestamp;
|
private Label timestamp;
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void initialize(URL url, ResourceBundle rb) {
|
* Sets the message to display
|
||||||
|
*
|
||||||
}
|
* @param message The message to display
|
||||||
|
*/
|
||||||
public void setMessage(Message message) {
|
public void setMessage(Message message) {
|
||||||
if (!message.equals(currentMessage)) {
|
if (!message.equals(currentMessage)) {
|
||||||
currentMessage = message;
|
currentMessage = message;
|
||||||
button.setText(message.getText());
|
button.setText(message.getText());
|
||||||
timestamp.setText(DateFormat.getTimeInstance().format(message.getDate()));
|
timestamp.setText(DateFormat.getTimeInstance().format(message.getDate()));
|
||||||
|
clearBackground();
|
||||||
if (CurrentUser.getInstance().isLocalId(message.getSender().id)) {
|
if (CurrentUser.getInstance().isLocalId(message.getSender().id)) {
|
||||||
container.setAlignment(Pos.CENTER_RIGHT);
|
container.setAlignment(Pos.CENTER_RIGHT);
|
||||||
button.getStyleClass().remove("message-other");
|
|
||||||
button.getStyleClass().add("message-self");
|
button.getStyleClass().add("message-self");
|
||||||
} else {
|
} else {
|
||||||
container.setAlignment(Pos.CENTER_LEFT);
|
container.setAlignment(Pos.CENTER_LEFT);
|
||||||
button.getStyleClass().remove("message-self");
|
|
||||||
button.getStyleClass().add("message-other");
|
button.getStyleClass().add("message-other");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any style applied to the background
|
||||||
|
*/
|
||||||
|
private void clearBackground() {
|
||||||
|
button.getStyleClass().remove("message-self");
|
||||||
|
button.getStyleClass().remove("message-other");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle rb) {}
|
||||||
}
|
}
|
|
@ -1,22 +1,16 @@
|
||||||
package fr.insa.clavardator.ui.dialogs;
|
package fr.insa.clavardator.ui.dialogs;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
|
||||||
import com.jfoenix.controls.JFXDialog;
|
import com.jfoenix.controls.JFXDialog;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
|
||||||
import com.jfoenix.validation.base.ValidatorBase;
|
|
||||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
|
||||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the about dialog
|
||||||
|
*/
|
||||||
public class AboutDialogController implements Initializable {
|
public class AboutDialogController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|
|
@ -18,14 +18,11 @@ import javafx.scene.layout.StackPane;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the username edit dialog
|
||||||
|
*/
|
||||||
public class EditUsernameDialogController implements Initializable {
|
public class EditUsernameDialogController implements Initializable {
|
||||||
|
|
||||||
enum State {
|
|
||||||
VALID,
|
|
||||||
INVALID,
|
|
||||||
NETWORK
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label titleLabel;
|
private Label titleLabel;
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -45,8 +42,6 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
private ButtonPressEvent cancelListener;
|
private ButtonPressEvent cancelListener;
|
||||||
private UserList userList;
|
private UserList userList;
|
||||||
|
|
||||||
private final int MAX_LENGTH = 16;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void onConfirm() {
|
private void onConfirm() {
|
||||||
setLocked(true);
|
setLocked(true);
|
||||||
|
@ -76,6 +71,16 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
this.cancelListener = listener;
|
this.cancelListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays the dialog show animation
|
||||||
|
*
|
||||||
|
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
||||||
|
* Due to JFoenix animations, the dialog will not reopen.
|
||||||
|
* If you must, wait at least 500ms before reopening.
|
||||||
|
*
|
||||||
|
* @param root The dialog's root component
|
||||||
|
* @param mode The dialog display mode
|
||||||
|
*/
|
||||||
public void show(StackPane root, Mode mode) {
|
public void show(StackPane root, Mode mode) {
|
||||||
setLocked(false);
|
setLocked(false);
|
||||||
setFieldError(State.VALID);
|
setFieldError(State.VALID);
|
||||||
|
@ -84,10 +89,22 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
dialog.show(root);
|
dialog.show(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the dialog.
|
||||||
|
*
|
||||||
|
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
||||||
|
* Due to JFoenix animations, the dialog will not reopen.
|
||||||
|
* If you must, wait at least 500ms before reopening.
|
||||||
|
*/
|
||||||
public void hide() {
|
public void hide() {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks the dialog on screen or frees it.
|
||||||
|
*
|
||||||
|
* @param state True to lock, false to unlock
|
||||||
|
*/
|
||||||
private void setLocked(boolean state) {
|
private void setLocked(boolean state) {
|
||||||
confirmButton.setDisable(state || textField.getText().isEmpty());
|
confirmButton.setDisable(state || textField.getText().isEmpty());
|
||||||
cancelButton.setDisable(state);
|
cancelButton.setDisable(state);
|
||||||
|
@ -95,20 +112,38 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
textField.setDisable(state);
|
textField.setDisable(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the input error
|
||||||
|
*
|
||||||
|
* @param state The input state to set
|
||||||
|
*/
|
||||||
private void setFieldError(State state) {
|
private void setFieldError(State state) {
|
||||||
currentState = state;
|
currentState = state;
|
||||||
textField.validate();
|
textField.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables save button if username is not empty, and stop input at max length.
|
||||||
|
*
|
||||||
|
* @param observable The observed string
|
||||||
|
* @param oldText The previous text
|
||||||
|
* @param newText The new text
|
||||||
|
*/
|
||||||
public void onUsernameChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
public void onUsernameChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
||||||
setFieldError(State.VALID);
|
setFieldError(State.VALID);
|
||||||
confirmButton.setDisable(newText.isEmpty());
|
confirmButton.setDisable(newText.isEmpty());
|
||||||
|
final int MAX_LENGTH = 16;
|
||||||
if (textField.getText().length() > MAX_LENGTH) {
|
if (textField.getText().length() > MAX_LENGTH) {
|
||||||
String s = textField.getText().substring(0, MAX_LENGTH);
|
String s = textField.getText().substring(0, MAX_LENGTH);
|
||||||
textField.setText(s);
|
textField.setText(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dialog display mode
|
||||||
|
*
|
||||||
|
* @param mode The mode to set
|
||||||
|
*/
|
||||||
public void setMode(Mode mode) {
|
public void setMode(Mode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
|
@ -132,6 +167,32 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates the username change to active users and unlocks the dialog.
|
||||||
|
*/
|
||||||
|
private void onSuccess() {
|
||||||
|
setFieldError(State.VALID);
|
||||||
|
setLocked(false);
|
||||||
|
hide();
|
||||||
|
final String newName = textField.getText();
|
||||||
|
CurrentUser.getInstance().setUsername(newName);
|
||||||
|
if (successListener != null) {
|
||||||
|
successListener.onPress();
|
||||||
|
}
|
||||||
|
if (userList != null) {
|
||||||
|
userList.propagateUsernameChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the field error and unlocks the dialog
|
||||||
|
*/
|
||||||
|
private void onError() {
|
||||||
|
validator.setMessage("Nom d'utilisateur déjà utilisé");
|
||||||
|
setFieldError(State.INVALID);
|
||||||
|
setLocked(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
validator = new Validator();
|
validator = new Validator();
|
||||||
|
@ -149,33 +210,17 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSuccess() {
|
|
||||||
setFieldError(State.VALID);
|
|
||||||
setLocked(false);
|
|
||||||
hide();
|
|
||||||
final String newName = textField.getText();
|
|
||||||
CurrentUser.getInstance().setUsername(newName);
|
|
||||||
if (successListener != null) {
|
|
||||||
successListener.onPress();
|
|
||||||
}
|
|
||||||
if (userList != null) {
|
|
||||||
userList.propagateUsernameChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onError() {
|
|
||||||
validator.setMessage("Nom d'utilisateur invalide");
|
|
||||||
setFieldError(State.INVALID);
|
|
||||||
setLocked(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
INITIAL,
|
INITIAL,
|
||||||
ERROR,
|
ERROR,
|
||||||
EDIT
|
EDIT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
VALID,
|
||||||
|
INVALID
|
||||||
|
}
|
||||||
|
|
||||||
class Validator extends ValidatorBase {
|
class Validator extends ValidatorBase {
|
||||||
@Override
|
@Override
|
||||||
protected void eval() {
|
protected void eval() {
|
||||||
|
|
|
@ -12,6 +12,9 @@ import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the notification snackbar
|
||||||
|
*/
|
||||||
public class SnackbarController implements Initializable {
|
public class SnackbarController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -21,10 +24,20 @@ public class SnackbarController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
public HBox container;
|
public HBox container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the snackbar text
|
||||||
|
*
|
||||||
|
* @param text The text to display
|
||||||
|
*/
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
label.setText(text);
|
label.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the snackbar display mode
|
||||||
|
*
|
||||||
|
* @param mode The mode to set
|
||||||
|
*/
|
||||||
public void setMode(Mode mode) {
|
public void setMode(Mode mode) {
|
||||||
ObservableList<String> styleClasses = container.getStyleClass();
|
ObservableList<String> styleClasses = container.getStyleClass();
|
||||||
styleClasses.clear();
|
styleClasses.clear();
|
||||||
|
@ -52,6 +65,7 @@ public class SnackbarController implements Initializable {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
setMode(Mode.INFO);
|
setMode(Mode.INFO);
|
||||||
|
// Set a small shadow bellow the snackbar
|
||||||
JFXDepthManager.setDepth(container, 1);
|
JFXDepthManager.setDepth(container, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.ui.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.users.PeerUser;
|
||||||
import fr.insa.clavardator.util.Log;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
@ -11,17 +10,20 @@ import java.beans.PropertyChangeEvent;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the user indicator, showing the connection status
|
||||||
|
*/
|
||||||
public class UserActiveIndicatorController implements Initializable {
|
public class UserActiveIndicatorController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Circle circle;
|
private Circle circle;
|
||||||
private PeerUser user;
|
private PeerUser user;
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
* Sets the new circle color
|
||||||
|
*
|
||||||
}
|
* @param isActive True for active color, false for inactive
|
||||||
|
*/
|
||||||
private void updateState(boolean isActive) {
|
private void updateState(boolean isActive) {
|
||||||
circle.getStyleClass().clear();
|
circle.getStyleClass().clear();
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
@ -31,6 +33,12 @@ public class UserActiveIndicatorController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the indicator color on user state change.
|
||||||
|
* The indicator becomes active only if the user state goes to connected.
|
||||||
|
*
|
||||||
|
* @param propertyChangeEvent The change event
|
||||||
|
*/
|
||||||
private void onStateChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
||||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||||
|
@ -38,6 +46,11 @@ public class UserActiveIndicatorController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user associated to this indicator
|
||||||
|
*
|
||||||
|
* @param user The user to set
|
||||||
|
*/
|
||||||
public void setUser(PeerUser user) {
|
public void setUser(PeerUser user) {
|
||||||
if (this.user != null) {
|
if (this.user != null) {
|
||||||
// remove old observer before setting new user
|
// remove old observer before setting new user
|
||||||
|
@ -48,8 +61,16 @@ public class UserActiveIndicatorController implements Initializable {
|
||||||
updateState(user.isActive());
|
updateState(user.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the indicator's radius
|
||||||
|
*
|
||||||
|
* @param value The radius in pixels
|
||||||
|
*/
|
||||||
public void setSize(double value) {
|
public void setSize(double value) {
|
||||||
circle.setRadius(value);
|
circle.setRadius(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.ui.users;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
import fr.insa.clavardator.ui.ButtonPressEvent;
|
||||||
import fr.insa.clavardator.ui.UserSelectedEvent;
|
import fr.insa.clavardator.ui.UserSelectedEvent;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.users.PeerUser;
|
||||||
|
@ -15,26 +16,34 @@ import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class UserListController implements Initializable {
|
public class UserListController implements Initializable {
|
||||||
|
@FXML
|
||||||
|
private ListView<PeerUser> peerUserListView;
|
||||||
|
@FXML
|
||||||
|
private JFXButton refreshButton;
|
||||||
|
|
||||||
private ButtonPressEvent refreshUserListener;
|
private ButtonPressEvent refreshUserListener;
|
||||||
private UserSelectedEvent userSelectedListener;
|
private UserSelectedEvent userSelectedListener;
|
||||||
@FXML
|
|
||||||
private ListView<PeerUser> peerUserListView;
|
|
||||||
private UserList userList;
|
|
||||||
|
|
||||||
public UserListController() {
|
public UserListController() {}
|
||||||
}
|
|
||||||
|
|
||||||
public void setRefreshUserListener(ButtonPressEvent listener) {
|
public void setRefreshUserListener(ButtonPressEvent listener) {
|
||||||
refreshUserListener = listener;
|
refreshUserListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUserSelectedListener(UserSelectedEvent listener) {
|
public void setUserSelectedListener(UserSelectedEvent listener) {
|
||||||
userSelectedListener = listener;
|
userSelectedListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRefreshUserListPress() {
|
public void onRefreshUserListPress() {
|
||||||
refreshUserListener.onPress();
|
if (refreshUserListener != null) {
|
||||||
|
refreshUserListener.onPress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables the refresh button
|
||||||
|
* @param enabled True to enable, false otherwise
|
||||||
|
*/
|
||||||
|
public void setRefreshButtonEnabled(boolean enabled) {
|
||||||
|
refreshButton.setDisable(!enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUserSelected(@org.jetbrains.annotations.NotNull PeerUser user) {
|
private void onUserSelected(@org.jetbrains.annotations.NotNull PeerUser user) {
|
||||||
|
@ -54,6 +63,10 @@ public class UserListController implements Initializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add user to UI if not already present
|
||||||
|
* @param user The new user to display
|
||||||
|
*/
|
||||||
private void onUserConnected(PeerUser user) {
|
private void onUserConnected(PeerUser user) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
if (!peerUserListView.getItems().contains(user)) {
|
if (!peerUserListView.getItems().contains(user)) {
|
||||||
|
@ -65,8 +78,11 @@ public class UserListController implements Initializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user list to subscribe to
|
||||||
|
* @param userList The user list to use
|
||||||
|
*/
|
||||||
public void setUserList(UserList userList) {
|
public void setUserList(UserList userList) {
|
||||||
this.userList = userList;
|
userList.setNewUserObserver(this::onUserConnected);
|
||||||
userList.addNewUserObserver(this::onUserConnected);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,8 +19,8 @@ public class UserListItemController implements Initializable {
|
||||||
private UserActiveIndicatorController indicatorController;
|
private UserActiveIndicatorController indicatorController;
|
||||||
|
|
||||||
private ButtonPressEvent listener;
|
private ButtonPressEvent listener;
|
||||||
|
|
||||||
private PeerUser user;
|
private PeerUser user;
|
||||||
|
private boolean selected;
|
||||||
|
|
||||||
public void setOnPressListener(ButtonPressEvent listener) {
|
public void setOnPressListener(ButtonPressEvent listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
@ -32,51 +32,83 @@ public class UserListItemController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current item selection state
|
||||||
|
*
|
||||||
|
* @param selected True to select the item, false otherwise
|
||||||
|
*/
|
||||||
public void setSelected(boolean selected) {
|
public void setSelected(boolean selected) {
|
||||||
|
this.selected = selected;
|
||||||
if (selected)
|
if (selected)
|
||||||
setBackgroundSelected();
|
setBackgroundSelected();
|
||||||
else
|
else
|
||||||
resetBackground();
|
resetBackground(this.user.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
* Updates shown username and item color when user changes
|
||||||
resetBackground();
|
*
|
||||||
}
|
* @param propertyChangeEvent The change event
|
||||||
|
*/
|
||||||
|
|
||||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
final String propName = propertyChangeEvent.getPropertyName();
|
||||||
|
if (propName.equals("username")) {
|
||||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||||
Platform.runLater(() -> button.setText(newUsername));
|
Platform.runLater(() -> button.setText(newUsername));
|
||||||
|
} else if (propName.equals("state") && !selected) {
|
||||||
|
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||||
|
Platform.runLater(() -> resetBackground(newState == PeerUser.State.CONNECTED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user to subscribe to for this item.
|
||||||
|
*
|
||||||
|
* @param user The user to subscribe to
|
||||||
|
*/
|
||||||
public void setUser(PeerUser user) {
|
public void setUser(PeerUser user) {
|
||||||
if (this.user != null) {
|
if (this.user == null || !this.user.equals(user)) {
|
||||||
// remove old observer before setting new user
|
if (this.user != null) {
|
||||||
this.user.removeObserver(this::onUsernameChange);
|
// remove old observer before setting new user
|
||||||
|
this.user.removeObserver(this::onUsernameChange);
|
||||||
|
}
|
||||||
|
this.user = user;
|
||||||
|
user.addObserver(this::onUsernameChange);
|
||||||
|
indicatorController.setUser(user);
|
||||||
|
button.setText(user.getUsername());
|
||||||
|
setSelected(false);
|
||||||
}
|
}
|
||||||
this.user = user;
|
|
||||||
user.addObserver(this::onUsernameChange);
|
|
||||||
indicatorController.setUser(user);
|
|
||||||
button.setText(user.getUsername());
|
|
||||||
resetBackground();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetBackground() {
|
/**
|
||||||
if (user != null && user.isActive()) {
|
* Sets the background color to active or inactive
|
||||||
button.getStyleClass().remove("inactive-user-item");
|
*/
|
||||||
|
private void resetBackground(boolean isActive) {
|
||||||
|
clearBackground();
|
||||||
|
if (isActive) {
|
||||||
button.getStyleClass().add("active-user-item");
|
button.getStyleClass().add("active-user-item");
|
||||||
} else {
|
} else {
|
||||||
button.getStyleClass().remove("active-user-item");
|
|
||||||
button.getStyleClass().add("inactive-user-item");
|
button.getStyleClass().add("inactive-user-item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the background color in selected mode
|
||||||
|
*/
|
||||||
private void setBackgroundSelected() {
|
private void setBackgroundSelected() {
|
||||||
button.getStyleClass().remove("inactive-user-item");
|
clearBackground();
|
||||||
button.getStyleClass().remove("active-user-item");
|
|
||||||
button.getStyleClass().add("selected-user-item");
|
button.getStyleClass().add("selected-user-item");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the background of any color class
|
||||||
|
*/
|
||||||
|
private void clearBackground() {
|
||||||
|
button.getStyleClass().remove("inactive-user-item");
|
||||||
|
button.getStyleClass().remove("active-user-item");
|
||||||
|
button.getStyleClass().remove("selected-user-item");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class UserList {
|
||||||
private final Map<Integer, PeerUser> inactiveUsers = new HashMap<>();
|
private final Map<Integer, PeerUser> inactiveUsers = new HashMap<>();
|
||||||
private final Map<Integer, PeerUser> activeUsers = new HashMap<>();
|
private final Map<Integer, PeerUser> activeUsers = new HashMap<>();
|
||||||
|
|
||||||
private final ArrayList<UserConnectionCallback> newUsersObservers = new ArrayList<>();
|
private UserConnectionCallback newUsersObservers = null;
|
||||||
|
|
||||||
private final DatabaseController db = new DatabaseController();
|
private final DatabaseController db = new DatabaseController();
|
||||||
private final NetDiscoverer netDiscoverer = new NetDiscoverer();
|
private final NetDiscoverer netDiscoverer = new NetDiscoverer();
|
||||||
|
@ -33,8 +33,8 @@ public class UserList {
|
||||||
*
|
*
|
||||||
* @param connectionCallback The function to add as listener
|
* @param connectionCallback The function to add as listener
|
||||||
*/
|
*/
|
||||||
public void addNewUserObserver(UserConnectionCallback connectionCallback) {
|
public void setNewUserObserver(UserConnectionCallback connectionCallback) {
|
||||||
newUsersObservers.add(connectionCallback);
|
newUsersObservers = connectionCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +43,9 @@ public class UserList {
|
||||||
* @param user The newly connected user
|
* @param user The newly connected user
|
||||||
*/
|
*/
|
||||||
private void notifyNewUserObservers(PeerUser user) {
|
private void notifyNewUserObservers(PeerUser user) {
|
||||||
newUsersObservers.forEach(callback -> callback.onUserConnected(user));
|
if (newUsersObservers != null) {
|
||||||
|
newUsersObservers.onUserConnected(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +54,7 @@ public class UserList {
|
||||||
* Observers are notified for each new successful connection.
|
* Observers are notified for each new successful connection.
|
||||||
*
|
*
|
||||||
* @param errorCallback The function to call on error
|
* @param errorCallback The function to call on error
|
||||||
* @see UserList#addNewUserObserver(UserConnectionCallback)
|
* @see UserList#setNewUserObserver(UserConnectionCallback)
|
||||||
*/
|
*/
|
||||||
public void discoverActiveUsers(ErrorCallback errorCallback) {
|
public void discoverActiveUsers(ErrorCallback errorCallback) {
|
||||||
netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
|
netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<HBox alignment="CENTER" prefHeight="64.0" spacing="10.0">
|
<HBox alignment="CENTER" prefHeight="64.0" spacing="10.0">
|
||||||
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress">
|
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress" fx:id="refreshButton">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontIcon iconLiteral="fas-sync-alt" iconSize="24"/>
|
<FontIcon iconLiteral="fas-sync-alt" iconSize="24"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|
Loading…
Reference in a new issue