Skip to content

Commit 0f733ad

Browse files
committed
keep chat status up to date
1 parent ba3fd3b commit 0f733ad

File tree

9 files changed

+112
-56
lines changed

9 files changed

+112
-56
lines changed

src/main/client/src/component/Chat.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ export const Chat = ({chatId, className}) => {
4141
return [...previous, message]
4242
})
4343
})
44+
let intervalId = setInterval(() => {
45+
stompClient.publish({
46+
destination: "/app/chat/status",
47+
body: JSON.stringify({
48+
room: chatId,
49+
}),
50+
})
51+
}, 30 * 1000)
4452

4553
doTry(async () => {
4654
let chat = await tfetch("/api/chat/" + chatId, {
@@ -56,6 +64,7 @@ export const Chat = ({chatId, className}) => {
5664
return () => {
5765
sub1.unsubscribe()
5866
sub2.unsubscribe()
67+
clearInterval(intervalId)
5968
}
6069
}, [stompClient, auth, chatId])
6170

@@ -163,7 +172,7 @@ function SplitPane({className, messageRef, topElement, bottomElement}) {
163172
}
164173
if (!containerRef.current) {
165174
let rect = ref.getBoundingClientRect()
166-
setSplitPos(getSplitPos(rect.top + 2.5 * getRemInPixel(), ref))
175+
setSplitPos(getSplitPos(rect.top + 4.5 * getRemInPixel(), ref))
167176
}
168177
containerRef.current = ref
169178
}}

src/main/client/src/layout.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import {
44
import {
55
produce,
66
} from "immer"
7-
import {
8-
persist,
9-
} from "zustand/middleware"
107
import {
118
getRemInPixel,
129
DELTA,

src/main/java/com/bernd/ChatController.java

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,51 @@
33
import com.bernd.model.Chat;
44
import com.bernd.model.ChatMessage;
55
import com.bernd.model.ChatRequest;
6+
import com.bernd.model.ChatWithUsers;
67
import com.bernd.model.Status;
78
import com.bernd.model.StatusMap;
8-
import com.bernd.model.UserStatus;
9-
import com.bernd.model.UsersMessage;
109
import com.bernd.util.Auth;
10+
import com.bernd.util.RoomManager;
1111
import com.bernd.util.Sender;
12-
import java.security.Principal;
13-
import java.util.List;
14-
import java.util.Map;
1512
import org.springframework.messaging.core.MessageSendingOperations;
1613
import org.springframework.messaging.handler.annotation.MessageMapping;
1714
import org.springframework.stereotype.Controller;
1815
import org.springframework.web.bind.annotation.GetMapping;
1916
import org.springframework.web.bind.annotation.PathVariable;
2017
import org.springframework.web.bind.annotation.ResponseBody;
2118

19+
import java.security.Principal;
20+
2221
@Controller
2322
public class ChatController {
2423

25-
private static final long SCAN_TIMEOUT = 120 * 1000;
24+
private static final long SCAN_TIMEOUT = 15 * 1000;
2625

2726
private final Chats chats;
2827
private final MessageSendingOperations<String> operations;
2928
private final Sender sender;
30-
private final StatusMap statusMap = new StatusMap();
29+
private final StatusMap statusMap;
30+
private final RoomManager roomManager;
3131
private long lastScan;
3232

3333
ChatController(
3434
Chats chats,
3535
MessageSendingOperations<String> operations,
36-
Sender sender) {
36+
Sender sender,
37+
StatusMap statusMap, RoomManager roomManager) {
3738
this.chats = chats;
3839
this.operations = operations;
3940
this.sender = sender;
41+
this.statusMap = statusMap;
42+
this.roomManager = roomManager;
4043
}
4144

4245
@ResponseBody
4346
@GetMapping("/api/chat/{id}")
44-
public Chat getChat(@PathVariable String id) {
45-
return chats.get(id);
47+
public ChatWithUsers getChat(@PathVariable String id) {
48+
String user = Auth.getPrincipal();
49+
roomManager.updateStatus(user, id);
50+
return chats.get(id).withUsers(statusMap.usersInRoom(id));
4651
}
4752

4853
@MessageMapping("/chat/send")
@@ -51,29 +56,13 @@ public void sendChat(ChatRequest chatRequest, Principal principal) {
5156
Chat chat = chats.get(chatRequest.id());
5257
ChatMessage message = new ChatMessage(chat.counter().getAndIncrement(), chatRequest.message(), user);
5358
chat.messages().add(message);
54-
if (chat.users().add(user)) {
55-
operations.convertAndSend("/topic/users/" + chat.id(), new UsersMessage(chat.users()));
56-
}
59+
roomManager.updateRooms(user, chat.id());
5760
operations.convertAndSend("/topic/chat/" + chat.id(), message);
5861
}
5962

6063
@MessageMapping("/chat/status")
6164
public void updateStatus(Status status, Principal principal) {
6265
String user = Auth.getPrincipal(principal);
63-
UserStatus old = statusMap.put(user, UserStatus.create(status.room()));
64-
if (lastScan + SCAN_TIMEOUT < System.currentTimeMillis()) {
65-
Map<String, List<String>> allRooms = statusMap.allRooms();
66-
for (Map.Entry<String, List<String>> e : allRooms.entrySet()) {
67-
String room = e.getKey();
68-
List<String> users = e.getValue();
69-
sender.sendUsers(room, users);
70-
}
71-
lastScan = System.currentTimeMillis();
72-
} else if (old == null) {
73-
sender.sendUsers(status.room(), statusMap.usersInRoom(status.room()));
74-
} else if (!old.room().equals(status.room())) {
75-
sender.sendUsers(status.room(), statusMap.usersInRoom(status.room()));
76-
sender.sendUsers(status.room(), statusMap.usersInRoom(old.room()));
77-
}
66+
roomManager.updateRooms(user, status.room());
7867
}
7968
}

src/main/java/com/bernd/Chats.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package com.bernd;
22

33
import com.bernd.model.Chat;
4+
import org.springframework.stereotype.Component;
5+
46
import java.util.ArrayList;
57
import java.util.LinkedHashMap;
68
import java.util.Map;
7-
import java.util.TreeSet;
89
import java.util.concurrent.atomic.AtomicInteger;
9-
import org.springframework.stereotype.Component;
1010

1111
@Component
1212
public class Chats {
1313
private final Map<String, Chat> map = new LinkedHashMap<>();
1414

1515
Chat get(String id) {
1616
return map.computeIfAbsent(id,
17-
_id -> new Chat(id, new AtomicInteger(0), new ArrayList<>(), new TreeSet<>()));
17+
_id -> new Chat(id, new AtomicInteger(0), new ArrayList<>()));
1818
}
1919

2020
Chat put(Chat chat) {

src/main/java/com/bernd/GameController.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@
44
import com.bernd.game.MoveList;
55
import com.bernd.model.AcceptRequest;
66
import com.bernd.model.ActiveGame;
7-
import com.bernd.model.Chat;
8-
import com.bernd.model.ChatMessage;
97
import com.bernd.model.Game;
108
import com.bernd.model.Move;
119
import com.bernd.model.OpenGame;
12-
import com.bernd.model.UsersMessage;
1310
import com.bernd.model.ViewGame;
14-
import com.bernd.util.Auth;
1511
import com.bernd.util.RandomString;
16-
import java.security.Principal;
1712
import org.springframework.http.HttpStatus;
1813
import org.springframework.http.ResponseEntity;
1914
import org.springframework.messaging.core.MessageSendingOperations;
@@ -26,6 +21,8 @@
2621
import org.springframework.web.bind.annotation.ResponseBody;
2722
import org.springframework.web.server.ResponseStatusException;
2823

24+
import java.security.Principal;
25+
2926
import static com.bernd.util.Auth.getPrincipal;
3027
import static com.bernd.util.Util.COLORS;
3128

@@ -61,10 +58,6 @@ public ViewGame getGame(@PathVariable String id, Principal p) {
6158
if (game == null) {
6259
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "no such game");
6360
}
64-
Chat chat = chats.get(id);
65-
if (chat.users().add(Auth.getPrincipal(p))) {
66-
operations.convertAndSend("/topic/users/" + chat.id(), new UsersMessage(chat.users()));
67-
}
6861
return game.toView();
6962
}
7063

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.bernd.model;
22

33
import java.util.List;
4-
import java.util.Set;
54
import java.util.concurrent.atomic.AtomicInteger;
65

76
public record Chat(
87
String id,
98
AtomicInteger counter,
10-
List<ChatMessage> messages,
11-
Set<String> users) {
9+
List<ChatMessage> messages) {
10+
public ChatWithUsers withUsers(List<String> users) {
11+
return new ChatWithUsers(id, counter, messages, users);
12+
}
1213
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.bernd.model;
2+
3+
import java.util.List;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
public record ChatWithUsers(
7+
String id,
8+
AtomicInteger counter,
9+
List<ChatMessage> messages,
10+
List<String> users) {
11+
}

src/main/java/com/bernd/model/StatusMap.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.bernd.model;
22

3+
import org.springframework.stereotype.Component;
4+
35
import java.util.ArrayList;
6+
import java.util.HashMap;
47
import java.util.LinkedHashMap;
58
import java.util.List;
69
import java.util.Map;
710

11+
@Component
812
public final class StatusMap {
913

10-
private static final long USER_TIMEOUT = 90 * 1000;
14+
private static final long USER_TIMEOUT = 12 * 1000;
1115

1216
private final Map<String, UserStatus> map = new LinkedHashMap<>();
1317

@@ -25,23 +29,28 @@ public List<String> usersInRoom(String room) {
2529
return result;
2630
}
2731

28-
public Map<String, List<String>> allRooms() {
32+
public Map<String, List<String>> prune() {
2933
long current = System.currentTimeMillis();
30-
Map<String, List<String>> result = new LinkedHashMap<>();
31-
List<String> remove = new ArrayList<>();
34+
Map<String, List<String>> tmp = new HashMap<>();
35+
List<RemoveItem> remove = new ArrayList<>();
3236
for (Map.Entry<String, UserStatus> e : map.entrySet()) {
3337
UserStatus status = e.getValue();
38+
String room = status.room();
3439
String user = e.getKey();
3540
if (status.lastSeen() + USER_TIMEOUT < current) {
36-
remove.add(user);
41+
remove.add(new RemoveItem(user, room));
3742
} else {
38-
result.computeIfAbsent(status.room(), key -> new ArrayList<>())
39-
.add(user);
43+
tmp.computeIfAbsent(room, key -> new ArrayList<>()).add(user);
4044
}
4145
}
42-
for (String user : remove) {
43-
map.remove(user);
46+
Map<String, List<String>> result = new LinkedHashMap<>(Math.max(8, (int) (tmp.size() * 1.5)));
47+
for (RemoveItem item : remove) {
48+
result.put(item.room, tmp.get(item.room));
49+
map.remove(item.user);
4450
}
4551
return result;
4652
}
53+
54+
private record RemoveItem(String user, String room) {
55+
}
4756
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.bernd.util;
2+
3+
import com.bernd.model.StatusMap;
4+
import com.bernd.model.UserStatus;
5+
import org.springframework.stereotype.Component;
6+
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
@Component
11+
public class RoomManager {
12+
13+
private static final long SCAN_TIMEOUT = 15 * 1000;
14+
15+
private final Sender sender;
16+
private final StatusMap statusMap;
17+
private long lastScan;
18+
19+
public RoomManager(Sender sender, StatusMap statusMap) {
20+
this.sender = sender;
21+
this.statusMap = statusMap;
22+
}
23+
24+
public void updateRooms(String user, String room) {
25+
updateStatus(user, room);
26+
if (lastScan + SCAN_TIMEOUT < System.currentTimeMillis()) {
27+
Map<String, List<String>> updatedRooms = statusMap.prune();
28+
for (Map.Entry<String, List<String>> e : updatedRooms.entrySet()) {
29+
String r = e.getKey();
30+
List<String> users = e.getValue();
31+
sender.sendUsers(r, users);
32+
}
33+
lastScan = System.currentTimeMillis();
34+
}
35+
}
36+
37+
public void updateStatus(String user, String room) {
38+
UserStatus old = statusMap.put(user, UserStatus.create(room));
39+
List<String> users = statusMap.usersInRoom(room);
40+
if (old == null) {
41+
sender.sendUsers(room, users);
42+
} else if (!old.room().equals(room)) {
43+
sender.sendUsers(room, users);
44+
sender.sendUsers(old.room(), statusMap.usersInRoom(old.room()));
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)