Try to add online chat

This commit is contained in:
redtea committed 2022-08-31 12:57:47 +03:00
1 parent 428ab4127b
commit 90df65f8f9
31 files changed
+469 -489

No files matched your search

+2
View File
@@ -20,6 +20,8 @@ repositories {
} }
dependencies { dependencies {
implementation 'org.springframework:spring-websocket:5.3.22'
implementation 'org.springframework:spring-messaging:5.3.22'
implementation 'org.modelmapper:modelmapper:3.1.0' implementation 'org.modelmapper:modelmapper:3.1.0'
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.google.guava:guava:31.1-jre' implementation 'com.google.guava:guava:31.1-jre'
Whitespace-only changes.
@@ -1,28 +0,0 @@
package site.easystartup.web.chat.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import site.easystartup.web.chat.domain.model.ChatMessage;
import site.easystartup.web.chat.domain.model.ChatNotification;
@Controller
public class ChatController {
@MessageMapping("/chat.sendMessage")
@SendTo("/topic/public")
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
return chatMessage;
}
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
public ChatMessage addUser(@Payload ChatMessage chatMessage,
SimpMessageHeaderAccessor headerAccessor) {
// Add username in web socket session
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
return chatMessage;
}
}
@@ -1,22 +1,46 @@
package site.easystartup.web.chat.config; package site.easystartup.web.chat.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.DefaultContentTypeResolver;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import java.util.List;
@Configuration @Configuration
@EnableWebSocketMessageBroker @EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker( "/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
@Override @Override
public void registerStompEndpoints(StompEndpointRegistry registry) { public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS(); registry
.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
} }
@Override @Override
public void configureMessageBroker(MessageBrokerRegistry registry) { public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
registry.setApplicationDestinationPrefixes("/app"); DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
registry.enableSimpleBroker("/topic"); resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(new ObjectMapper());
converter.setContentTypeResolver(resolver);
messageConverters.add(converter);
return false;
} }
} }
@@ -0,0 +1,38 @@
package site.easystartup.web.chat.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import site.easystartup.web.chat.domain.model.ChatMessage;
import site.easystartup.web.chat.domain.model.ChatNotification;
import site.easystartup.web.chat.domain.request.ChatMessageRequest;
import site.easystartup.web.chat.repo.ChatMessageRepo;
import site.easystartup.web.chat.service.ChatService;
@Controller
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ChatController {
private final SimpMessagingTemplate messagingTemplate;
private final ChatService chatService;
@MessageMapping("/chat")
public void processMessage(@Payload ChatMessageRequest chatMessageRequest) {
ChatMessage chatMessage = chatService.getByRequest(chatMessageRequest);
var room = chatService
.getChatRoom(chatMessage.getRecipientName(), chatMessage.getRecipientId());
chatMessage.setChatId(room.getChatId());
ChatMessage saved = chatService.save(chatMessage);
messagingTemplate.convertAndSendToUser(
chatMessage.getRecipientId(),"/queue/messages",
new ChatNotification(
saved.getId(),
saved.getSenderId(),
saved.getSenderName()));
}
}
@@ -0,0 +1,52 @@
package site.easystartup.web.chat.controller;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import site.easystartup.web.chat.dto.ChatMessageDto;
import site.easystartup.web.chat.dto.ChatRoomDto;
import site.easystartup.web.chat.service.ChatService;
import site.easystartup.web.exception.NoPermissionException;
import site.easystartup.web.service.UserService;
import java.security.Principal;
import java.util.List;
@RestController
@RequestMapping("/chat")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ChatRestController {
private final ChatService chatService;
private final ModelMapper modelMapper;
private final UserService userService;
@GetMapping("/{chat_id}")
public ResponseEntity<List<ChatMessageDto>> chatId(@PathVariable String chat_id,
Principal principal) {
var chatMessages = chatService.getByChatId(chat_id, principal)
.stream().map(message -> modelMapper.map(message, ChatMessageDto.class)).toList();
return ResponseEntity.ok().body(chatMessages);
}
@GetMapping("/see")
public ResponseEntity<List<ChatMessageDto>> chat(@RequestParam String sender,
@RequestParam String recipient,
Principal principal) {
var chatMessages = chatService.getChatMessages(sender, recipient, principal)
.stream().map(message -> modelMapper.map(message, ChatMessageDto.class)).toList();
return ResponseEntity.ok().body(chatMessages);
}
@GetMapping("/user/{username}")
public ResponseEntity<List<ChatRoomDto>> user(@PathVariable String username,
Principal principal) {
var chatMessages = chatService.getChats(username, principal)
.stream().map(message -> modelMapper.map(message, ChatRoomDto.class)).toList();
return ResponseEntity.ok().body(chatMessages);
}
}
Whitespace-only changes.
@@ -0,0 +1,14 @@
package site.easystartup.web.chat.domain.exception;
import java.util.function.Supplier;
public class ChatRoomNotFoundException extends RuntimeException implements Supplier<ChatRoomNotFoundException> {
public ChatRoomNotFoundException(String message) {
super(message);
}
@Override
public ChatRoomNotFoundException get() {
return this;
}
}
@@ -1,15 +1,51 @@
package site.easystartup.web.chat.domain.model; package site.easystartup.web.chat.domain.model;
import lombok.Data; import lombok.*;
import org.hibernate.Hibernate;
import site.easystartup.web.forum.domain.model.DiscussionMessage;
@Data import javax.persistence.Entity;
public class ChatMessage { import javax.persistence.GeneratedValue;
private MessageType type; import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class ChatMessage implements Comparable<ChatMessage> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String chatId;
private String senderId;
private String recipientId;
private String senderName;
private String recipientName;
private String content; private String content;
private String sender; private Date timestamp;
public enum MessageType { private MessageStatus status;
CHAT,
JOIN, @Override
LEAVE public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
ChatMessage that = (ChatMessage) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public int compareTo(ChatMessage o) {
if (getTimestamp() == null || o.getTimestamp() == null)
return 0;
return getTimestamp().compareTo(o.getTimestamp());
} }
} }
@@ -1,11 +1,10 @@
package site.easystartup.web.chat.domain.model; package site.easystartup.web.chat.domain.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor @AllArgsConstructor
public class ChatNotification { public class ChatNotification {
private String id; private long id;
private String senderId;
private String senderName; private String senderName;
} }
@@ -1,11 +1,46 @@
package site.easystartup.web.chat.domain.model; package site.easystartup.web.chat.domain.model;
import lombok.Data; import lombok.*;
import org.hibernate.Hibernate;
@Data import javax.persistence.Entity;
public class ChatRoom { import javax.persistence.GeneratedValue;
private String id; import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class ChatRoom implements Comparable<ChatRoom> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String chatId; private String chatId;
private String senderId; private String sender;
private String recipientId; private String recipient;
private Date last_update;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
ChatRoom chatRoom = (ChatRoom) o;
return Objects.equals(id, chatRoom.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public int compareTo(ChatRoom o) {
if (getLast_update() == null || o.getLast_update() == null)
return 0;
return getLast_update().compareTo(o.getLast_update());
}
} }
@@ -0,0 +1,27 @@
package site.easystartup.web.chat.domain.request;
import lombok.Data;
import site.easystartup.web.chat.domain.model.MessageStatus;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
@Data
public class ChatMessageRequest {
private long id;
private String chatId;
@NotNull
private String senderId;
@NotNull
private String recipientId;
@NotNull
private String senderName;
@NotNull
private String recipientName;
@NotNull
@Size(min = 1, max = 255, message = "Text should be no less 1 and no more 255 signs")
private String content;
private Date timestamp;
private MessageStatus status;
}
@@ -0,0 +1,28 @@
package site.easystartup.web.chat.dto;
import lombok.Data;
import site.easystartup.web.chat.domain.model.MessageStatus;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date;
@Data
public class ChatMessageDto implements Serializable {
private long id;
private String chatId;
@NotNull
private String senderId;
@NotNull
private String recipientId;
@NotNull
private String senderName;
@NotNull
private String recipientName;
@NotNull
@Size(min = 1, max = 255, message = "Text should be no less 1 and no more 255 signs")
private String content;
private Date timestamp;
private MessageStatus status;
}
@@ -0,0 +1,11 @@
package site.easystartup.web.chat.dto;
import lombok.Data;
@Data
public class ChatRoomDto {
private long id;
private String chatId;
private String sender;
private String recipient;
}
@@ -1,35 +0,0 @@
package site.easystartup.web.chat.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import site.easystartup.web.chat.domain.model.ChatMessage;
@Component
public class WebSocketEventListener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class);
@Autowired
private SimpMessageSendingOperations messagingTemplate;
@EventListener
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
logger.info("Received a new web socket connection");
}
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
String username = (String) headerAccessor.getSessionAttributes().get("username");
if(username != null) {
logger.info("User Disconnected : " + username);
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessage.MessageType.LEAVE);
chatMessage.setSender(username);
messagingTemplate.convertAndSend("/topic/public", chatMessage);
}
}
}
@@ -0,0 +1,8 @@
package site.easystartup.web.chat.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import site.easystartup.web.chat.domain.model.ChatMessage;
public interface ChatMessageRepo extends JpaRepository<ChatMessage, Long> {
}
@@ -0,0 +1,8 @@
package site.easystartup.web.chat.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import site.easystartup.web.chat.domain.model.ChatRoom;
public interface ChatRoomRepo extends JpaRepository<ChatRoom, Long> {
}
@@ -0,0 +1,101 @@
package site.easystartup.web.chat.service;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import site.easystartup.web.chat.domain.exception.ChatRoomNotFoundException;
import site.easystartup.web.chat.domain.model.ChatMessage;
import site.easystartup.web.chat.domain.model.ChatRoom;
import site.easystartup.web.chat.domain.request.ChatMessageRequest;
import site.easystartup.web.chat.dto.ChatRoomDto;
import site.easystartup.web.chat.repo.ChatMessageRepo;
import site.easystartup.web.chat.repo.ChatRoomRepo;
import site.easystartup.web.exception.NoPermissionException;
import site.easystartup.web.service.UserService;
import java.security.Principal;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Component
@Scope("singleton")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ChatService {
private final ChatMessageRepo chatMessageRepo;
private final ChatRoomRepo chatRoomRepo;
private final UserService userService;
public ChatRoom getChatRoom(String sender, String recipient) {
return chatRoomRepo.findAll().stream()
.filter(chatRoom -> chatRoom.getSender().equals(sender)
&& chatRoom.getRecipient().equals(recipient)).findAny()
.orElse(createChatRoom(sender, recipient));
}
public List<ChatMessage> getChatMessages(String sender, String recipient, Principal principal) {
if(!getChatRoom(sender, recipient).getSender().equals(principal.getName())
|| !(userService.getUserByPrincipal(principal)).isAdmin())
throw new NoPermissionException(
String.format("User %s dont have perms to see chat %s / %s", sender, recipient));
List<ChatMessage> result = new java.util.ArrayList<>(chatMessageRepo.findAll().stream().filter(
chatMessage -> chatMessage.getSenderName().equals(sender) && chatMessage.getSenderName().equals(recipient)
).toList());
result.sort(ChatMessage::compareTo);
return result;
}
public List<ChatRoom> getChats(String sender, Principal principal) {
if(!principal.getName().equals(sender)
|| !(userService.getUserByPrincipal(principal)).isAdmin())
throw new NoPermissionException(
String.format("User %s dont have perms to see chats %s", sender));
List<ChatRoom> result = new java.util.ArrayList<>(chatRoomRepo.findAll().stream().filter(
chatRoom -> chatRoom.getSender().equals(sender)
).toList());
result.sort(ChatRoom::compareTo);
return result;
}
public ChatRoom getChatRoom(String chat_id) {
return chatRoomRepo.findAll().stream()
.filter(chatRoom -> chatRoom.getChatId().equals(chat_id)).findAny()
.orElseThrow(new ChatRoomNotFoundException("Chat with id " + chat_id + " dont exists!"));
}
public ChatMessage getByRequest(ChatMessageRequest chatMessageRequest) {
ChatMessage result = new ChatMessage();
result.setContent(chatMessageRequest.getContent());
result.setRecipientId(chatMessageRequest.getRecipientId());
result.setRecipientName(chatMessageRequest.getRecipientName());
result.setSenderId(chatMessageRequest.getSenderId());
result.setSenderName(chatMessageRequest.getSenderName());
result.setContent(chatMessageRequest.getContent());
result.setTimestamp(chatMessageRequest.getTimestamp() != null ? chatMessageRequest.getTimestamp() : new Date());
return result;
}
public ChatMessage save(ChatMessage chatMessage) {
return chatMessageRepo.save(chatMessage);
}
public List<ChatMessage> getByChatId(String chat_id, Principal principal) {
if(!getChatRoom(chat_id).getSender().equals(principal.getName())
|| !(userService.getUserByPrincipal(principal)).isAdmin())
throw new NoPermissionException(
String.format("User %s dont have perms to see chat %s", principal.getName(), chat_id));
return chatMessageRepo.findAll().stream().filter(chatMessage -> chatMessage.getChatId().equals(chat_id)).toList();
}
public ChatRoom createChatRoom(String sender, String recipient) {
ChatRoom chatRoom = new ChatRoom();
chatRoom.setSender(sender);
chatRoom.setRecipient(recipient);
chatRoom.setChatId(UUID.randomUUID().toString());
return chatRoomRepo.save(chatRoom);
}
}
@@ -139,7 +139,7 @@ public class ForumController {
return ResponseEntity.ok().body(modelMapper.map(messageUpdated, DiscussionMessageDto.class)); return ResponseEntity.ok().body(modelMapper.map(messageUpdated, DiscussionMessageDto.class));
} }
@DeleteMapping("/forum/message/{message_id}/delete") @DeleteMapping("/message/{message_id}/delete")
public ResponseEntity<Object> messageDelete(@PathVariable long message_id, Principal principal) { public ResponseEntity<Object> messageDelete(@PathVariable long message_id, Principal principal) {
DiscussionMessage message = forumService.getDiscussionMessageById(message_id); DiscussionMessage message = forumService.getDiscussionMessageById(message_id);
User user = userService.getUserByPrincipal(principal); User user = userService.getUserByPrincipal(principal);
@@ -13,7 +13,7 @@ import java.util.Objects;
@ToString @ToString
@RequiredArgsConstructor @RequiredArgsConstructor
@Entity @Entity
public class DiscussionMessage { public class DiscussionMessage implements Comparable<DiscussionMessage>{
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private long id; private long id;
@@ -40,4 +40,12 @@ public class DiscussionMessage {
public int hashCode() { public int hashCode() {
return getClass().hashCode(); return getClass().hashCode();
} }
@Override
public int compareTo(DiscussionMessage o) {
if (getDate() == null || o.getDate() == null)
return 0;
return getDate().compareTo(o.getDate());
}
} }
@@ -5,11 +5,12 @@ import site.easystartup.web.forum.domain.model.DiscussionStatus;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@Data @Data
public class DiscussionDto { public class DiscussionDto implements Serializable {
private long id; private long id;
@Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs") @Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs")
@NotNull @NotNull
@@ -4,10 +4,11 @@ import lombok.Data;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
@Data @Data
public class DiscussionMessageDto { public class DiscussionMessageDto implements Serializable {
private long id; private long id;
@Size(min = 3, max = 1024, message = "Text should be no less 3 and no more 1024 signs") @Size(min = 3, max = 1024, message = "Text should be no less 3 and no more 1024 signs")
@NotNull @NotNull
@@ -4,10 +4,11 @@ import lombok.Data;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
@Data @Data
public class TopicDto { public class TopicDto implements Serializable {
private long id; private long id;
@Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs") @Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs")
@NotNull @NotNull
@@ -1,7 +1,8 @@
package site.easystartup.web.forum.repo; package site.easystartup.web.forum.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import site.easystartup.web.forum.domain.model.DiscussionMessage; import site.easystartup.web.forum.domain.model.DiscussionMessage;
public interface DiscussionMessageRepo extends CrudRepository<DiscussionMessage, Long> { public interface DiscussionMessageRepo extends JpaRepository<DiscussionMessage, Long> {
} }
@@ -1,7 +1,8 @@
package site.easystartup.web.forum.repo; package site.easystartup.web.forum.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import site.easystartup.web.forum.domain.model.Discussion; import site.easystartup.web.forum.domain.model.Discussion;
public interface DiscussionRepo extends CrudRepository<Discussion, Long> { public interface DiscussionRepo extends JpaRepository<Discussion, Long> {
} }
@@ -1,7 +1,8 @@
package site.easystartup.web.forum.repo; package site.easystartup.web.forum.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import site.easystartup.web.forum.domain.model.Topic; import site.easystartup.web.forum.domain.model.Topic;
public interface TopicRepo extends CrudRepository<Topic, Long> { public interface TopicRepo extends JpaRepository<Topic, Long> {
} }
@@ -45,8 +45,10 @@ public class ForumService {
} }
public List<DiscussionMessage> getMessagesByDiscussionId(long discussionId) { public List<DiscussionMessage> getMessagesByDiscussionId(long discussionId) {
return Lists.newArrayList(discussionMessageRepo.findAll()).stream() List<DiscussionMessage> result = new ArrayList<DiscussionMessage>(Lists.newArrayList(discussionMessageRepo.findAll()).stream()
.filter(m -> m.getDiscussion_id() == discussionId).toList(); .filter(m -> m.getDiscussion_id() == discussionId).toList());
result.sort(DiscussionMessage::compareTo);
return result;
} }
public Discussion getDiscussionById(long discussion_id) { public Discussion getDiscussionById(long discussion_id) {
@@ -55,14 +57,18 @@ public class ForumService {
} }
public List<Discussion> getDiscussionsByAuthor(Principal principal) { public List<Discussion> getDiscussionsByAuthor(Principal principal) {
return Lists.newArrayList(discussionRepo.findAll()).stream() List<Discussion> result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream()
.filter(d -> d.getAuthor().equals(principal.getName())).toList(); .filter(d -> d.getAuthor().equals(principal.getName())).toList());
result.sort(Discussion::compareTo);
return result;
} }
public List<Discussion> getDiscussionsByMember(Principal principal) { public List<Discussion> getDiscussionsByMember(Principal principal) {
return Lists.newArrayList(discussionRepo.findAll()).stream() List<Discussion> result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream()
.filter(d -> getMessagesByDiscussionId(d.getId()).stream() .filter(d -> getMessagesByDiscussionId(d.getId()).stream()
.anyMatch(m -> m.getSender().equals(principal.getName()))).toList(); .anyMatch(m -> m.getSender().equals(principal.getName()))).toList());
result.sort(Discussion::compareTo);
return result;
} }
public List<Discussion> getPinnedDiscussionsByTopicId(long topic_id) { public List<Discussion> getPinnedDiscussionsByTopicId(long topic_id) {
@@ -72,10 +78,10 @@ public class ForumService {
} }
public List<Discussion> getSortedDiscussionsByTopicId(long topic_id) { public List<Discussion> getSortedDiscussionsByTopicId(long topic_id) {
List<Discussion> result = Lists.newArrayList(discussionRepo.findAll()).stream() List<Discussion> result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream()
.filter(d -> !d.isPinned()) .filter(d -> !d.isPinned())
.filter(d -> d.getTopic() == topic_id).toList(); .filter(d -> d.getTopic() == topic_id).toList());
Collections.sort(result, Discussion::compareTo); result.sort(Discussion::compareTo);
return result; return result;
} }
-251
View File
@@ -1,251 +0,0 @@
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,body {
height: 100%;
overflow: hidden;
}
body {
margin: 0;
padding: 0;
font-weight: 400;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
line-height: 1.58;
color: #333;
background-color: #f4f4f4;
height: 100%;
}
body:before {
height: 50%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: #128ff2;
content: "";
z-index: 0;
}
.clearfix:after {
display: block;
content: "";
clear: both;
}
.hidden {
display: none;
}
.form-control {
width: 100%;
min-height: 38px;
font-size: 15px;
border: 1px solid #c8c8c8;
}
.form-group {
margin-bottom: 15px;
}
input {
padding-left: 10px;
outline: none;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
font-size: 1.7em;
}
a {
color: #128ff2;
}
button {
box-shadow: none;
border: 1px solid transparent;
font-size: 14px;
outline: none;
line-height: 100%;
white-space: nowrap;
vertical-align: middle;
padding: 0.6rem 1rem;
border-radius: 2px;
transition: all 0.2s ease-in-out;
cursor: pointer;
min-height: 38px;
}
button.default {
background-color: #e8e8e8;
color: #333;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
}
button.primary {
background-color: #128ff2;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
color: #fff;
}
button.accent {
background-color: #ff4743;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
color: #fff;
}
#username-page {
text-align: center;
}
.username-page-container {
background: #fff;
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
border-radius: 2px;
width: 100%;
max-width: 500px;
display: inline-block;
margin-top: 42px;
vertical-align: middle;
position: relative;
padding: 35px 55px 35px;
min-height: 250px;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin: 0 auto;
margin-top: -160px;
}
.username-page-container .username-submit {
margin-top: 10px;
}
#chat-page {
position: relative;
height: 100%;
}
.chat-container {
max-width: 700px;
margin-left: auto;
margin-right: auto;
background-color: #fff;
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
margin-top: 30px;
height: calc(100% - 60px);
max-height: 600px;
position: relative;
}
#chat-page ul {
list-style-type: none;
background-color: #FFF;
margin: 0;
overflow: auto;
overflow-y: scroll;
padding: 0 20px 0px 20px;
height: calc(100% - 150px);
}
#chat-page #messageForm {
padding: 20px;
}
#chat-page ul li {
line-height: 1.5rem;
padding: 10px 20px;
margin: 0;
border-bottom: 1px solid #f4f4f4;
}
#chat-page ul li p {
margin: 0;
}
#chat-page .event-message {
width: 100%;
text-align: center;
clear: both;
}
#chat-page .event-message p {
color: #777;
font-size: 14px;
word-wrap: break-word;
}
#chat-page .chat-message {
padding-left: 68px;
position: relative;
}
#chat-page .chat-message i {
position: absolute;
width: 42px;
height: 42px;
overflow: hidden;
left: 10px;
display: inline-block;
vertical-align: middle;
font-size: 18px;
line-height: 42px;
color: #fff;
text-align: center;
border-radius: 50%;
font-style: normal;
text-transform: uppercase;
}
#chat-page .chat-message span {
color: #333;
font-weight: 600;
}
#chat-page .chat-message p {
color: #43464b;
}
#messageForm .input-group input {
float: left;
width: calc(100% - 85px);
}
#messageForm .input-group button {
float: left;
width: 80px;
height: 38px;
margin-left: 5px;
}
.chat-header {
text-align: center;
padding: 15px;
border-bottom: 1px solid #ececec;
}
.chat-header h2 {
margin: 0;
font-weight: 500;
}
.connecting {
padding-top: 5px;
text-align: center;
color: #777;
position: absolute;
top: 65px;
width: 100%;
}
@media screen and (max-width: 730px) {
.chat-container {
margin-left: 10px;
margin-right: 10px;
margin-top: 10px;
}
}
@media screen and (max-width: 480px) {
.chat-container {
height: calc(100% - 30px);
}
.username-page-container {
width: auto;
margin-left: 15px;
margin-right: 15px;
padding: 25px;
}
#chat-page ul {
height: calc(100% - 120px);
}
#messageForm .input-group button {
width: 65px;
}
#messageForm .input-group input {
width: calc(100% - 70px);
}
.chat-header {
padding: 10px;
}
.connecting {
top: 60px;
}
.chat-header h2 {
font-size: 1.1em;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

-49
View File
@@ -1,49 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<title>Spring Boot WebSocket Chat Application</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<noscript>
<h2>Sorry! Your browser doesn't support Javascript</h2>
</noscript>
<div id="username-page">
<div class="username-page-container">
<h1 class="title">Type your username</h1>
<form id="usernameForm" name="usernameForm">
<div class="form-group">
<input type="text" id="name" placeholder="Username" autocomplete="off" class="form-control" />
</div>
<div class="form-group">
<button type="submit" class="accent username-submit">Start Chatting</button>
</div>
</form>
</div>
</div>
<div id="chat-page" class="hidden">
<div class="chat-container">
<div class="chat-header">
<h2>Spring WebSocket Chat Demo</h2>
</div>
<div class="connecting">
Connecting...
</div>
<ul id="messageArea">
</ul>
<form id="messageForm" name="messageForm">
<div class="form-group">
<div class="input-group clearfix">
<input type="text" id="message" placeholder="Type a message..." autocomplete="off" class="form-control"/>
<button type="submit" class="primary">Send</button>
</div>
</div>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script src="/js/main.js"></script>
</body>
</html>
+27 -87
View File
@@ -1,90 +1,30 @@
'use strict'; const connect = () => {
var usernamePage = document.querySelector('#username-page'); const Stomp = require("stompjs");
var chatPage = document.querySelector('#chat-page'); var SockJS = require("sockjs-client");
var usernameForm = document.querySelector('#usernameForm'); SockJS = new SockJS("http://localhost:8080/ws");
var messageForm = document.querySelector('#messageForm'); stompClient = Stomp.over(SockJS);
var messageInput = document.querySelector('#message');
var messageArea = document.querySelector('#messageArea');
var connectingElement = document.querySelector('.connecting');
var stompClient = null;
var username = null;
var colors = [
'#2196F3', '#32c787', '#00BCD4', '#ff5652',
'#ffc107', '#ff85af', '#FF9800', '#39bbb0'
];
function connect(event) {
username = document.querySelector('#name').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
var socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError); stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}
function onConnected() {
// Subscribe to the Public Topic
stompClient.subscribe('/topic/public', onMessageReceived);
// Tell your username to the server
stompClient.send("/app/chat.addUser",
{},
JSON.stringify({sender: username, type: 'JOIN'})
)
connectingElement.classList.add('hidden');
}
function onError(error) {
connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!';
connectingElement.style.color = 'red';
}
function sendMessage(event) {
var messageContent = messageInput.value.trim();
if(messageContent && stompClient) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
}; };
stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(chatMessage));
messageInput.value = ''; const onConnected = () => {
console.log("connected");
stompClient.subscribe(
"/user/" + currentUser.id + "/queue/messages",
onMessageReceived
);
};
const sendMessage = (msg) => {
if (msg.trim() !== "") {
const message = {
senderId: currentUser.id,
recipientId: activeContact.id,
senderName: currentUser.name,
recipientName: activeContact.name,
content: msg,
};
stompClient.send("/app/chat", {}, JSON.stringify(message));
} }
event.preventDefault(); };
}
function onMessageReceived(payload) {
var message = JSON.parse(payload.body);
var messageElement = document.createElement('li');
if(message.type === 'JOIN') {
messageElement.classList.add('event-message');
message.content = message.sender + ' joined!';
} else if (message.type === 'LEAVE') {
messageElement.classList.add('event-message');
message.content = message.sender + ' left!';
} else {
messageElement.classList.add('chat-message');
var avatarElement = document.createElement('i');
var avatarText = document.createTextNode(message.sender[0]);
avatarElement.appendChild(avatarText);
avatarElement.style['background-color'] = getAvatarColor(message.sender);
messageElement.appendChild(avatarElement);
var usernameElement = document.createElement('span');
var usernameText = document.createTextNode(message.sender);
usernameElement.appendChild(usernameText);
messageElement.appendChild(usernameElement);
}
var textElement = document.createElement('p');
var messageText = document.createTextNode(message.content);
textElement.appendChild(messageText);
messageElement.appendChild(textElement);
messageArea.appendChild(messageElement);
messageArea.scrollTop = messageArea.scrollHeight;
}
function getAvatarColor(messageSender) {
var hash = 0;
for (var i = 0; i < messageSender.length; i++) {
hash = 31 * hash + messageSender.charCodeAt(i);
}
var index = Math.abs(hash % colors.length);
return colors[index];
}
usernameForm.addEventListener('submit', connect, true)
messageForm.addEventListener('submit', sendMessage, true)