Browse Source

Connect ui to db

Arnaud Vergnet 3 years ago
parent
commit
2099b78233

+ 38
- 41
src/main/java/fr/insa/clavardator/chat/ChatHistory.java View File

@@ -1,71 +1,71 @@
1 1
 package fr.insa.clavardator.chat;
2 2
 
3 3
 import fr.insa.clavardator.db.DatabaseController;
4
-import fr.insa.clavardator.users.CurrentUser;
5 4
 import fr.insa.clavardator.users.PeerUser;
5
+import fr.insa.clavardator.users.UserInformation;
6
+import fr.insa.clavardator.util.Log;
6 7
 import javafx.application.Platform;
7 8
 import javafx.collections.FXCollections;
8 9
 import javafx.collections.ObservableList;
9 10
 
10
-import java.text.ParseException;
11
-import java.text.SimpleDateFormat;
12 11
 import java.util.ArrayList;
13
-import java.util.Timer;
14
-import java.util.TimerTask;
12
+import java.util.Date;
15 13
 
16 14
 public class ChatHistory {
17 15
 	private final DatabaseController db;
18 16
 	private final PeerUser user;
19 17
 	private final ObservableList<Message> history;
20 18
 	private final ArrayList<HistoryLoadedCallback> historyListener;
21
-	private final ArrayList<Message> fakeHistory;
22
-
23 19
 
24 20
 	public ChatHistory(PeerUser user) {
25 21
 		this.user = user;
26 22
 		db = new DatabaseController();
27 23
 		history = FXCollections.observableArrayList();
28 24
 		historyListener = new ArrayList<>();
29
-		fakeHistory = new ArrayList<>();
30
-		CurrentUser currentUser = CurrentUser.getInstance();
31
-		try {
32
-			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
33
-			fakeHistory.add(new Message(user, currentUser, format.parse("2021-01-01 10:00:00"), "Coucou toi"));
34
-		} catch (ParseException e) {
35
-			e.printStackTrace();
36
-		}
37 25
 	}
38 26
 
27
+	/**
28
+	 * Adds a listener for history loaded event
29
+	 *
30
+	 * @param listener The listener to add
31
+	 */
39 32
 	public void addHistoryLoadedListener(HistoryLoadedCallback listener) {
40 33
 		historyListener.add(listener);
41 34
 	}
42 35
 
36
+	/**
37
+	 * Removes a listener for history loaded event
38
+	 *
39
+	 * @param listener The listener to remove
40
+	 */
43 41
 	public void removeHistoryLoadedListener(HistoryLoadedCallback listener) {
44 42
 		historyListener.remove(listener);
45 43
 	}
46 44
 
45
+	/**
46
+	 * Notifies all listeners of a history loaded event
47
+	 */
47 48
 	private void notifyHistoryLoaded() {
48 49
 		historyListener.forEach(l -> l.onHistoryLoaded(user));
49 50
 	}
50 51
 
52
+	/**
53
+	 * Loads history from database
54
+	 */
51 55
 	public void load() {
52
-//		db.getChatHistory(new Date(), new Date(),   // TODO: put actual date
53
-//					newHistory -> {
54
-//						history = newHistory;
55
-//						notifyHistoryLoaded();
56
-//					});
57
-		Timer t = new Timer();
58
-		t.schedule(new TimerTask() {
59
-			@Override
60
-			public void run() {
61
-				Platform.runLater(() -> {
62
-					history.addAll(fakeHistory);
63
-					history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
64
-				});
65
-				notifyHistoryLoaded();
66
-				t.cancel();
67
-			}
68
-		}, 1000);
56
+		db.getChatHistory(new UserInformation(user), new Date(), new Date(), this::onLoaded);
57
+	}
58
+
59
+	/**
60
+	 * Adds all fetched messages to the active history
61
+	 *
62
+	 * @param newHistory The fetched history
63
+	 */
64
+	private void onLoaded(ArrayList<Message> newHistory) {
65
+		history.addAll(newHistory);
66
+		history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
67
+		Log.v(getClass().getSimpleName(), "Message history loaded");
68
+		notifyHistoryLoaded();
69 69
 	}
70 70
 
71 71
 	public ObservableList<Message> getHistory() {
@@ -73,17 +73,14 @@ public class ChatHistory {
73 73
 	}
74 74
 
75 75
 	/**
76
-	 * @param message
76
+	 * Saves the given message and adds it to the current history
77
+	 *
78
+	 * @param message The message to add
77 79
 	 */
78 80
 	public void addMessage(Message message) {
79
-		Platform.runLater(() -> history.add(message));
80
-//		db.addMessage(message, () -> {
81
-//			if (history == null) {
82
-//				history = new ArrayList<>();
83
-//			}
84
-//			history.add(message);
85
-//			notifyMessageAdded(message);
86
-//		});
81
+		db.addMessage(message, () -> {
82
+			Platform.runLater(() -> history.add(message));
83
+		});
87 84
 	}
88 85
 
89 86
 	public interface HistoryLoadedCallback {

+ 13
- 3
src/main/java/fr/insa/clavardator/db/DatabaseController.java View File

@@ -15,6 +15,15 @@ public class DatabaseController {
15 15
 	private Connection connection;
16 16
 
17 17
 	public DatabaseController() {
18
+		connect();
19
+	}
20
+
21
+	public DatabaseController(boolean test) {
22
+		if(test) {
23
+			connectToTestDb();
24
+		} else {
25
+			connect();
26
+		}
18 27
 	}
19 28
 
20 29
 	/**
@@ -22,6 +31,7 @@ public class DatabaseController {
22 31
 	 */
23 32
 	public void connect() {
24 33
 		connectToDatabase("clavardator");
34
+		initTables();
25 35
 	}
26 36
 
27 37
 	/**
@@ -97,9 +107,9 @@ public class DatabaseController {
97 107
 	}
98 108
 
99 109
 	/**
100
-	 * Creates all needed tables
110
+	 * Creates all needed tables if non-existent
101 111
 	 */
102
-	private void createTables() {
112
+	private void initTables() {
103 113
 		try {
104 114
 			createMessageTable();
105 115
 			createUserTable();
@@ -143,7 +153,7 @@ public class DatabaseController {
143 153
 	 */
144 154
 	public void resetTables() {
145 155
 		dropTables();
146
-		createTables();
156
+		initTables();
147 157
 	}
148 158
 
149 159
 

+ 22
- 7
src/main/java/fr/insa/clavardator/ui/MainController.java View File

@@ -46,8 +46,10 @@ public class MainController implements Initializable {
46 46
 
47 47
 	private JFXSnackbar snackbar;
48 48
 	private UserList userList;
49
+	private boolean historyLoaded;
49 50
 
50 51
 	public MainController() {
52
+		historyLoaded = false;
51 53
 		currentUser = CurrentUser.getInstance();
52 54
 		currentUser.addObserver(propertyChangeEvent -> {
53 55
 			if (propertyChangeEvent.getPropertyName().equals("state")) {
@@ -58,14 +60,26 @@ public class MainController implements Initializable {
58 60
 	}
59 61
 
60 62
 	private void onCurrentUserStateChange(CurrentUser.State newState) {
61
-		if (newState == CurrentUser.State.VALID)
62
-			startChat();
63
-		else {
64
-			final boolean isError = newState == CurrentUser.State.INVALID;
65
-			if (isError) {
66
-				endChat();
63
+		// Only do an action if the history is loaded
64
+		if (historyLoaded) {
65
+			if (newState == CurrentUser.State.VALID)
66
+				startChat();
67
+			else if (newState != CurrentUser.State.NONE) {
68
+				final boolean isError = newState == CurrentUser.State.INVALID;
69
+				if (isError) {
70
+					endChat();
71
+				}
72
+				Platform.runLater(() -> showLogin(isError));
67 73
 			}
68
-			Platform.runLater(() -> showLogin(isError));
74
+		}
75
+	}
76
+
77
+	public void onHistoryLoaded() {
78
+		historyLoaded = true;
79
+		final CurrentUser.State userState = CurrentUser.getInstance().getState();
80
+		// If the history loaded after the current user, fire the state change event again
81
+		if (userState != CurrentUser.State.NONE) {
82
+			onCurrentUserStateChange(userState);
69 83
 		}
70 84
 	}
71 85
 
@@ -194,6 +208,7 @@ public class MainController implements Initializable {
194 208
 
195 209
 	public void setUserList(UserList userList) {
196 210
 		this.userList = userList;
211
+		userList.retrievedPreviousUsers(this::onHistoryLoaded);
197 212
 		listController.setUserList(userList);
198 213
 		listController.setRefreshUserListener(this::discoverActiveUsers);
199 214
 		editUserDialogController.setUserList(userList);

+ 2
- 3
src/main/java/fr/insa/clavardator/ui/chat/ChatController.java View File

@@ -52,9 +52,9 @@ public class ChatController implements Initializable {
52 52
 		setState(State.LOADING);
53 53
 
54 54
 		if (this.remoteUser != null) {
55
-			final ChatHistory oldHistory = this.remoteUser.getHistory();
56
-			oldHistory.removeHistoryLoadedListener(onHistoryLoaded);
55
+			this.remoteUser.getHistory().removeHistoryLoadedListener(onHistoryLoaded);
57 56
 		}
57
+		this.remoteUser = remoteUser;
58 58
 
59 59
 		final ChatHistory history = remoteUser.getHistory();
60 60
 		history.addHistoryLoadedListener(onHistoryLoaded);
@@ -64,7 +64,6 @@ public class ChatController implements Initializable {
64 64
 			scrollToEnd();
65 65
 		});
66 66
 		history.load();
67
-		this.remoteUser = remoteUser;
68 67
 	}
69 68
 
70 69
 	private void scrollToEnd() {

+ 4
- 0
src/main/java/fr/insa/clavardator/users/CurrentUser.java View File

@@ -64,6 +64,10 @@ public class CurrentUser extends User {
64 64
 		this.state = state;
65 65
 	}
66 66
 
67
+	public State getState() {
68
+		return state;
69
+	}
70
+
67 71
 	public enum State {
68 72
 		UNINITIALIZED,
69 73
 		VALID,

+ 125
- 46
src/main/java/fr/insa/clavardator/users/UserList.java View File

@@ -29,7 +29,27 @@ public class UserList {
29 29
 	}
30 30
 
31 31
 	/**
32
-	 * Discover all active users over the network. Observers are notified for each user discovered.
32
+	 * Adds an observer for new user connection events
33
+	 *
34
+	 * @param connectionCallback The function to add as listener
35
+	 */
36
+	public void addNewUserObserver(UserConnectionCallback connectionCallback) {
37
+		newUsersObservers.add(connectionCallback);
38
+	}
39
+
40
+	/**
41
+	 * Notifies observers the given user is a new connection
42
+	 *
43
+	 * @param user The newly connected user
44
+	 */
45
+	private void notifyNewUserObservers(PeerUser user) {
46
+		newUsersObservers.forEach(callback -> callback.onUserConnected(user));
47
+	}
48
+
49
+	/**
50
+	 * Discovers all active users over the network
51
+	 * and create a connection for each.
52
+	 * Observers are notified for each new successful connection.
33 53
 	 *
34 54
 	 * @param errorCallback The function to call on error
35 55
 	 * @see UserList#addNewUserObserver(UserConnectionCallback)
@@ -45,6 +65,10 @@ public class UserList {
45 65
 		}, errorCallback);
46 66
 	}
47 67
 
68
+	/**
69
+	 * Starts listening for other broadcasts.
70
+	 * Only answers and waits for them to initiate the connection.
71
+	 */
48 72
 	public void startDiscoveryListening() {
49 73
 		netDiscoverer.startDiscoveryListening(
50 74
 				"CLAVARDATOR_RESPONSE",
@@ -52,6 +76,18 @@ public class UserList {
52 76
 				Throwable::printStackTrace);
53 77
 	}
54 78
 
79
+	public void retrievedPreviousUsers(UserListLoadedCallback onFinish) {
80
+		db.getAllUsers(users -> {
81
+			users.forEach(user -> createNewUser(user.id, user.getUsername()));
82
+			onFinish.onLoaded();
83
+		});
84
+	}
85
+
86
+	/**
87
+	 * Starts listening to user connection requests.
88
+	 *
89
+	 * @param errorCallback Callback on error
90
+	 */
55 91
 	public void startUserListening(ErrorCallback errorCallback) {
56 92
 		connectionListener.acceptConnection(
57 93
 				(clientSocket) -> {
@@ -66,11 +102,24 @@ public class UserList {
66 102
 				errorCallback);
67 103
 	}
68 104
 
105
+	/**
106
+	 * Notify observers and subscribe to user changes.
107
+	 *
108
+	 * @param user The newly connected user
109
+	 */
69 110
 	private void onUserConnectionSuccess(PeerUser user) {
70 111
 		notifyNewUserObservers(user);
71 112
 		user.addObserver(evt -> userChangeObserver(user, evt));
72 113
 	}
73 114
 
115
+	/**
116
+	 * Creates a new user from its id and puts it in the inactive user list.
117
+	 * We first try to fetch it from active and inactive users to prevent duplicates.
118
+	 * We do not notify observers yet as the connection may be canceled on username checks.
119
+	 *
120
+	 * @param id The new user's id.
121
+	 * @return A new PeerUser, or null if the user is already connected
122
+	 */
74 123
 	private PeerUser createNewUser(int id) {
75 124
 		// If already connected, warn and return
76 125
 		if (activeUsers.containsKey(id)) {
@@ -83,65 +132,92 @@ public class UserList {
83 132
 		PeerUser user = inactiveUsers.get(id);
84 133
 		// else create it
85 134
 		if (user == null) {
86
-			// Username is set on TCP connection start
135
+			// Username is set on TCP connection start or db fetch
87 136
 			user = new PeerUser(id);
88 137
 			inactiveUsers.put(id, user);
89 138
 		}
139
+		return user;
140
+	}
90 141
 
142
+	/**
143
+	 * Creates a new user from its id and username and put it in inactive user list.
144
+	 * We first try to fetch it from active and inactive users to prevent duplicates.
145
+	 * We DO notify observers as the created user has all the information needed.
146
+	 *
147
+	 * @param id       The new user's id.
148
+	 * @param username The new user's username.
149
+	 * @return A new PeerUser, or null if the user is already connected
150
+	 */
151
+	private PeerUser createNewUser(int id, String username) {
152
+		final PeerUser user = createNewUser(id);
153
+		if (user != null) {
154
+			user.setUsername(username);
155
+			notifyNewUserObservers(user);
156
+		}
91 157
 		return user;
92 158
 	}
93 159
 
94
-	private void userChangeObserver(PeerUser user, PropertyChangeEvent evt) {
95
-		int id = user.id;
160
+	/**
161
+	 * Tries to move the given user from inactive list to active list
162
+	 *
163
+	 * @param user The user to move
164
+	 */
165
+	private void moveUserToActiveList(PeerUser user) {
166
+		final int id = user.id;
167
+		if (!inactiveUsers.containsKey(id)) {
168
+			if (activeUsers.containsKey(id)) {
169
+				Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.CONNECTED + " on an already connected user: user id " + id);
170
+			} else {
171
+				Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.CONNECTED + " on an unknown user: user id " + id);
172
+			}
173
+			return;
174
+		}
175
+		inactiveUsers.remove(user.id);
176
+		activeUsers.put(user.id, user);
177
+	}
96 178
 
179
+	/**
180
+	 * Tries to move the given user from active list to inactive list
181
+	 *
182
+	 * @param user The user to move
183
+	 */
184
+	private void moveUserToInactiveList(PeerUser user) {
185
+		final int id = user.id;
186
+		if (!activeUsers.containsKey(id)) {
187
+			if (inactiveUsers.containsKey(id)) {
188
+				Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.DISCONNECTED + " on an already disconnected user: user id " + id);
189
+			} else {
190
+				Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.DISCONNECTED + " on an unknown user: user id " + id);
191
+			}
192
+			return;
193
+		}
194
+		activeUsers.remove(id);
195
+		inactiveUsers.put(id, user);
196
+	}
197
+
198
+	/**
199
+	 * Move user from active or inactive list when its state changes.
200
+	 *
201
+	 * @param user The user which changed
202
+	 * @param evt  The change event
203
+	 */
204
+	private void userChangeObserver(PeerUser user, PropertyChangeEvent evt) {
97 205
 		if (evt.getPropertyName().equals("state")) {
98 206
 			PeerUser.State oldState = (PeerUser.State) evt.getOldValue();
99 207
 			PeerUser.State newState = (PeerUser.State) evt.getNewValue();
100 208
 
101
-			if ((oldState == PeerUser.State.DISCONNECTED || oldState == PeerUser.State.CONNECTING) &&
102
-					newState == PeerUser.State.CONNECTED) {
103
-				// Connection handling
104
-				if (!inactiveUsers.containsKey(id)) {
105
-					if (activeUsers.containsKey(id)) {
106
-						Log.w(getClass().getSimpleName(), "Tried to set state CONNECTED on an already connected user: user id " + id);
107
-					} else {
108
-						Log.w(getClass().getSimpleName(), "Tried to set state CONNECTED on an unknown user: user id " + id);
109
-					}
110
-					return;
111
-				}
112
-
113
-				inactiveUsers.remove(id);
114
-				activeUsers.put(id, user);
115
-				Log.v(getClass().getSimpleName(), "State of user " + id + " updated from " +
116
-						oldState.toString() + " to " + newState.toString());
209
+			if ((oldState == PeerUser.State.DISCONNECTED ||
210
+					(oldState == PeerUser.State.CONNECTING) && newState == PeerUser.State.CONNECTED)) {
211
+				moveUserToActiveList(user);
117 212
 			} else if (oldState == PeerUser.State.CONNECTED &&
118 213
 					(newState == PeerUser.State.DISCONNECTED || newState == PeerUser.State.CONNECTING)) {
119
-				// Disconnection
120
-				if (!activeUsers.containsKey(id)) {
121
-					if (inactiveUsers.containsKey(id)) {
122
-						Log.w(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an already disconnected user: user id " + id);
123
-					} else {
124
-						Log.w(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an unknown user: user id " + id);
125
-					}
126
-					return;
127
-				}
128
-
129
-				activeUsers.remove(id);
130
-				inactiveUsers.put(id, user);
131
-				Log.v(getClass().getSimpleName(), "State of user " + id + " updated from " +
132
-						oldState.toString() + " to " + newState.toString());
214
+				moveUserToInactiveList(user);
133 215
 			}
216
+			Log.v(getClass().getSimpleName(), "State of user " + user.id + " updated from " +
217
+					oldState.toString() + " to " + newState.toString());
134 218
 		}
135 219
 	}
136 220
 
137
-	public void addNewUserObserver(UserConnectionCallback connectionCallback) {
138
-		newUsersObservers.add(connectionCallback);
139
-	}
140
-
141
-	private void notifyNewUserObservers(PeerUser user) {
142
-		newUsersObservers.forEach(callback -> callback.onUserConnected(user));
143
-	}
144
-
145 221
 	/**
146 222
 	 * Tests locally if a username is available
147 223
 	 *
@@ -155,7 +231,7 @@ public class UserList {
155 231
 	}
156 232
 
157 233
 	/**
158
-	 * Tell all active users that our username changed
234
+	 * Tells all active users that our username changed
159 235
 	 */
160 236
 	public void propagateUsernameChange() {
161 237
 		activeUsers.forEach((id, user) -> {
@@ -164,13 +240,13 @@ public class UserList {
164 240
 	}
165 241
 
166 242
 	/**
167
-	 * Close all running threads, sockets and db connection
168
-	 * Must be called before exiting the app
243
+	 * Closes all running threads, sockets and db connection.
244
+	 * Must be called before exiting the app.
169 245
 	 */
170 246
 	public void destroy() {
171 247
 		netDiscoverer.stopDiscovery();
172 248
 		connectionListener.stopAccepting();
173
-//		db.close();
249
+		db.close();
174 250
 		for (PeerUser user : activeUsers.values()) {
175 251
 			user.disconnect();
176 252
 		}
@@ -180,4 +256,7 @@ public class UserList {
180 256
 		void onUserConnected(PeerUser user);
181 257
 	}
182 258
 
259
+	public interface UserListLoadedCallback {
260
+		void onLoaded();
261
+	}
183 262
 }

+ 1
- 2
src/test/java/fr/insa/clavardator/DatabaseTest.java View File

@@ -10,11 +10,10 @@ import java.util.Date;
10 10
 import static org.junit.jupiter.api.Assertions.assertEquals;
11 11
 
12 12
 public class DatabaseTest {
13
-	private final DatabaseController db = new DatabaseController();
13
+	private final DatabaseController db = new DatabaseController(true);
14 14
 
15 15
 	@Test
16 16
 	void testDB() {
17
-		db.connectToTestDb();
18 17
 		db.resetTables();
19 18
 		db.getAllUsers(users -> {
20 19
 			assertEquals(0, users.size());

Loading…
Cancel
Save