Messaging system #150
| @ -16,6 +16,8 @@ repositories { | |||||||
| } | } | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|  | 	compileOnly("org.projectlombok:lombok") | ||||||
|  | 	annotationProcessor("org.projectlombok:lombok") | ||||||
| 	implementation("org.springframework.boot:spring-boot-starter-jdbc") | 	implementation("org.springframework.boot:spring-boot-starter-jdbc") | ||||||
| 	implementation("org.springframework.boot:spring-boot-starter-data-jpa") | 	implementation("org.springframework.boot:spring-boot-starter-data-jpa") | ||||||
| 	implementation("org.springframework.boot:spring-boot-starter-mail") | 	implementation("org.springframework.boot:spring-boot-starter-mail") | ||||||
|  | |||||||
| @ -0,0 +1,34 @@ | |||||||
|  | package ovh.herisson.Clyde.DTO.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file DiscussionDTO.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * File to format a discussion using messageDTO  | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Discussion; | ||||||
|  | import ovh.herisson.Clyde.DTO.Msg.MessagesDTO; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class DiscussionDTO { | ||||||
|  | 	private long id; | ||||||
|  | 	private String name; | ||||||
|  | 	private List<User> members; | ||||||
|  | 	private List<MessagesDTO> msgs; | ||||||
|  |  | ||||||
|  | 	public static DiscussionDTO construct(Discussion d, User u){ | ||||||
|  | 		List<MessagesDTO> msgsdto = new ArrayList<>(); | ||||||
|  | 		d.getMsgs().forEach(x -> msgsdto.add(MessagesDTO.construct(x, u))); | ||||||
|  | 		return new DiscussionDTO(d.getId(), d.getName(), d.getMembers(), msgsdto); | ||||||
|  | 	}  | ||||||
|  | } | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | package ovh.herisson.Clyde.DTO.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file MessagesDTO.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * File to Format the response adding the sender field | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Message; | ||||||
|  | import java.util.Date; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class MessagesDTO { | ||||||
|  | 	private long id; | ||||||
|  | 	private String content;  | ||||||
|  | 	private User author; | ||||||
|  | 	private boolean sender; | ||||||
|  | 	private Date created; | ||||||
|  | 	//TODO: Attachment | ||||||
|  |  | ||||||
|  | 	public static MessagesDTO construct(Message m, User user){ | ||||||
|  | 		boolean sender = false; | ||||||
|  | 		if(m.getAuthor().equals(user)) | ||||||
|  | 			sender = true; | ||||||
|  | 		return new MessagesDTO(m.getId(), m.getContent(), m.getAuthor(), sender, m.getCreated()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,126 @@ | |||||||
|  | package ovh.herisson.Clyde.EndPoints.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file MessagesController.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * Entry point for the messages application | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.http.HttpStatus; | ||||||
|  | import org.springframework.http.ResponseEntity; | ||||||
|  | import org.springframework.web.bind.annotation.CrossOrigin; | ||||||
|  | import org.springframework.web.bind.annotation.GetMapping; | ||||||
|  | import org.springframework.web.bind.annotation.PatchMapping; | ||||||
|  | import org.springframework.web.bind.annotation.PathVariable; | ||||||
|  | import org.springframework.web.bind.annotation.PostMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RequestBody; | ||||||
|  | import org.springframework.web.bind.annotation.RequestHeader; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import ovh.herisson.Clyde.DTO.Msg.DiscussionDTO; | ||||||
|  | import ovh.herisson.Clyde.Repositories.UserRepository; | ||||||
|  | import ovh.herisson.Clyde.Repositories.Msg.DiscussionRepository; | ||||||
|  | import ovh.herisson.Clyde.Responses.UnauthorizedResponse; | ||||||
|  | import ovh.herisson.Clyde.Services.AuthenticatorService; | ||||||
|  | import ovh.herisson.Clyde.Services.UserService; | ||||||
|  | import ovh.herisson.Clyde.Services.Msg.DiscussionService; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Discussion; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Message; | ||||||
|  |  | ||||||
|  | @RestController | ||||||
|  | @CrossOrigin(originPatterns = "*", allowCredentials = "true") | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class MessagesController { | ||||||
|  |  | ||||||
|  | 	private AuthenticatorService authServ; | ||||||
|  | 	private DiscussionService discServ; | ||||||
|  | 	private DiscussionRepository discRepo; | ||||||
|  | 	private UserService userServ; | ||||||
|  |  | ||||||
|  | 	@GetMapping("/discussions") | ||||||
|  | 	public ResponseEntity<Iterable<Discussion>> getDiscussions(@RequestHeader("Authorization") String token ){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		Iterable<Discussion> mock = discServ.getOwned(authServ.getUserFromToken(token)); | ||||||
|  |  | ||||||
|  | 		return new ResponseEntity<>(mock, HttpStatus.OK);  | ||||||
|  | 	}  | ||||||
|  |  | ||||||
|  | 	@GetMapping("/discussion/{id}") | ||||||
|  | 	public ResponseEntity<DiscussionDTO> getDiscussion(@RequestHeader("Authorization") String token, @PathVariable long id){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null || !discServ.hasDiscussion(user, id) ){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  | 		return new ResponseEntity<>(DiscussionDTO.construct(discRepo.findById(id).orElse(null), authServ.getUserFromToken(token)), HttpStatus.OK); | ||||||
|  | 	}  | ||||||
|  |  | ||||||
|  | 	@PatchMapping("/discussion/{id}") | ||||||
|  | 	public ResponseEntity<Discussion> AlterDiscussion(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody Discussion data){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		Discussion disc = discRepo.findById(id).orElse(null); | ||||||
|  | 		disc.setName(data.getName()); | ||||||
|  | 		discRepo.save(disc); | ||||||
|  | 		return new ResponseEntity<>(disc, HttpStatus.OK); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@PatchMapping("/discussion/{id}/add") | ||||||
|  | 	public ResponseEntity<Discussion> invite(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody User data){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		Discussion disc = discRepo.findById(id).orElse(null); | ||||||
|  | 		User invited = userServ.getUserById(data.getRegNo()); | ||||||
|  | 		disc.addMember(invited); | ||||||
|  | 		discRepo.save(disc); | ||||||
|  | 		return new ResponseEntity<>(disc, HttpStatus.OK); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@PatchMapping("/discussion/{id}/remove") | ||||||
|  | 	public ResponseEntity<Discussion> removeMember(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody User data){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		Discussion disc = discRepo.findById(id).orElse(null); | ||||||
|  | 		User member = userServ.getUserById(data.getRegNo()); | ||||||
|  | 		disc.delMember(member); | ||||||
|  | 		discRepo.save(disc); | ||||||
|  | 		return new ResponseEntity<>(disc, HttpStatus.OK); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@PostMapping("/discussion/{id}") | ||||||
|  | 	public ResponseEntity<Discussion> sendMessage(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody Message msg){ | ||||||
|  | 		User user = authServ.getUserFromToken(token); | ||||||
|  | 		if(user == null){ | ||||||
|  | 			return new UnauthorizedResponse<>(null); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		Discussion disc = discRepo.findById(id).orElse(null); | ||||||
|  | 		msg.setAuthor(user); | ||||||
|  | 		if(disc != null) | ||||||
|  | 			discServ.CreateMessage(disc, msg); | ||||||
|  | 		return new ResponseEntity<>(disc, HttpStatus.OK); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	@PostMapping("/discussion") | ||||||
|  | 	public ResponseEntity<Discussion> createDiscussion(@RequestHeader("Authorization") String token, @RequestBody Discussion data){ | ||||||
|  | 		return new ResponseEntity<>(discServ.create(data.getName(), authServ.getUserFromToken(token)), HttpStatus.OK); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | package ovh.herisson.Clyde.Repositories.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file DiscussionRepository.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * Repository of Discussion allowing to fetch discussion by user  | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import org.springframework.data.jpa.repository.Query; | ||||||
|  | import org.springframework.data.repository.CrudRepository; | ||||||
|  |  | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Discussion; | ||||||
|  |  | ||||||
|  | public interface DiscussionRepository extends CrudRepository<Discussion, Long>{ | ||||||
|  |  | ||||||
|  | 	@Query("SELECT d FROM Discussion d INNER JOIN FETCH d.members dm WHERE dm.id = ?1") | ||||||
|  | 	List<Discussion> findByMembership(long userid); | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | package ovh.herisson.Clyde.Repositories.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file MessageRepository.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import org.springframework.data.repository.CrudRepository; | ||||||
|  |  | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Message; | ||||||
|  |  | ||||||
|  | public interface MessageRepository extends CrudRepository<Message, Long> {} | ||||||
| @ -0,0 +1,57 @@ | |||||||
|  | package ovh.herisson.Clyde.Services.Msg; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file DiscussionService.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * Various function utilised by the messages application | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.databind.util.JSONPObject; | ||||||
|  |  | ||||||
|  | import ovh.herisson.Clyde.Repositories.Msg.DiscussionRepository; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Discussion; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Message; | ||||||
|  |  | ||||||
|  | @Service | ||||||
|  | public class DiscussionService { | ||||||
|  |  | ||||||
|  | 	@Autowired | ||||||
|  | 	private DiscussionRepository discRepo; | ||||||
|  |  | ||||||
|  | 	public Discussion create(String name, User author){ | ||||||
|  | 		return discRepo.save(new Discussion(name, author)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * list discussions owned by a certain user | ||||||
|  | 	 */ | ||||||
|  | 	public Iterable<Discussion> getOwned(User author){ | ||||||
|  | 		return discRepo.findByMembership(author.getRegNo()); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Create a message and link it to it's discussion | ||||||
|  | 	 */ | ||||||
|  | 	public Discussion CreateMessage(Discussion disc, Message msg){ | ||||||
|  | 		disc.addMessage(msg); | ||||||
|  | 		return discRepo.save(disc); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Check if a user is in a discussion | ||||||
|  | 	 */ | ||||||
|  |     public boolean hasDiscussion(User user, long id) { | ||||||
|  | 		Discussion disc = discRepo.findById(id).orElse(null); | ||||||
|  | 		List<User> members = disc.getMembers(); | ||||||
|  | 		return members.contains(user); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,72 @@ | |||||||
|  | package ovh.herisson.Clyde.Tables.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file Discussion.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * Discussion allow to regroupe multiple user in and message together | ||||||
|  |  * for the messages application to work | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import jakarta.persistence.CascadeType; | ||||||
|  | import jakarta.persistence.Entity; | ||||||
|  | import jakarta.persistence.GeneratedValue; | ||||||
|  | import jakarta.persistence.GenerationType; | ||||||
|  | import jakarta.persistence.Id; | ||||||
|  | import jakarta.persistence.JoinColumn; | ||||||
|  | import jakarta.persistence.JoinTable; | ||||||
|  | import jakarta.persistence.ManyToMany; | ||||||
|  | import jakarta.persistence.OneToMany; | ||||||
|  | import lombok.Getter; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  | import lombok.Setter; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  |  | ||||||
|  | @Entity | ||||||
|  | @Getter | ||||||
|  | @Setter | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class Discussion{ | ||||||
|  |  | ||||||
|  | 	@Id | ||||||
|  |     @GeneratedValue(strategy = GenerationType.AUTO) | ||||||
|  | 	private long id; | ||||||
|  | 	private String name; | ||||||
|  |  | ||||||
|  |     @ManyToMany | ||||||
|  | 	@JoinTable( | ||||||
|  | 		name = "discussion_members", | ||||||
|  | 		joinColumns = @JoinColumn(name = "discussion_id"), | ||||||
|  | 		inverseJoinColumns = @JoinColumn(name = "user_id") | ||||||
|  | 	) | ||||||
|  | 	private List<User> members; | ||||||
|  |  | ||||||
|  | 	@OneToMany(mappedBy="discussion", orphanRemoval = true, cascade = CascadeType.ALL) | ||||||
|  | 	private List<Message> msgs; | ||||||
|  |  | ||||||
|  | 	public Discussion(String name){ | ||||||
|  | 		this.name = name; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public Discussion(String name, User user){ | ||||||
|  | 		this.name = name; | ||||||
|  | 		this.members = List.of(user); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public void addMessage(Message msg){ | ||||||
|  | 		msg.setDiscussion(this); | ||||||
|  | 		msgs.add(msg); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |     public void addMember(User user) { | ||||||
|  | 		members.add(user); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void delMember(User user) { | ||||||
|  | 		members.remove(user); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | package ovh.herisson.Clyde.Tables.Msg; | ||||||
|  |  | ||||||
|  | /****************************************************** | ||||||
|  |  * @file Message.java | ||||||
|  |  * @author Anthony Debucquoy | ||||||
|  |  * @scope Extension messagerie | ||||||
|  |  * | ||||||
|  |  * Represent a message sent to a discussion | ||||||
|  |  ******************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import org.hibernate.annotations.CreationTimestamp; | ||||||
|  | import org.hibernate.annotations.UpdateTimestamp; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.annotation.JsonIgnore; | ||||||
|  | import java.util.Date; | ||||||
|  |  | ||||||
|  | import jakarta.persistence.CascadeType; | ||||||
|  | import jakarta.persistence.Column; | ||||||
|  | import jakarta.persistence.Entity; | ||||||
|  | import jakarta.persistence.GeneratedValue; | ||||||
|  | import jakarta.persistence.GenerationType; | ||||||
|  | import jakarta.persistence.Id; | ||||||
|  | import jakarta.persistence.ManyToOne; | ||||||
|  | import jakarta.persistence.OneToMany; | ||||||
|  | import jakarta.persistence.OneToOne; | ||||||
|  | import jakarta.persistence.PrePersist; | ||||||
|  | import jakarta.persistence.Temporal; | ||||||
|  | import jakarta.persistence.TemporalType; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Getter; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  | import lombok.Setter; | ||||||
|  | import ovh.herisson.Clyde.Tables.User; | ||||||
|  |  | ||||||
|  | @Getter | ||||||
|  | @Setter | ||||||
|  | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | @Entity | ||||||
|  | public class Message { | ||||||
|  | 	 | ||||||
|  | 	@Id | ||||||
|  | 	@GeneratedValue(strategy = GenerationType.AUTO) | ||||||
|  | 	private long id; | ||||||
|  | 	private String content;  | ||||||
|  |  | ||||||
|  | 	@CreationTimestamp | ||||||
|  | 	@Column(nullable = false) | ||||||
|  | 	private Date created; | ||||||
|  |  | ||||||
|  | 	@ManyToOne | ||||||
|  | 	private User author; | ||||||
|  |  | ||||||
|  | 	public User getAuthor() { | ||||||
|  |         return author; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @OneToOne | ||||||
|  | 	private Message response; | ||||||
|  |  | ||||||
|  | 	@ManyToOne(optional = false) | ||||||
|  | 	@JsonIgnore | ||||||
|  | 	private Discussion discussion; | ||||||
|  | 	 | ||||||
|  | } | ||||||
| @ -1,7 +1,11 @@ | |||||||
| package ovh.herisson.Clyde.Tables; | package ovh.herisson.Clyde.Tables; | ||||||
|  |  | ||||||
| import jakarta.persistence.*; | import jakarta.persistence.*; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Discussion; | ||||||
|  | import ovh.herisson.Clyde.Tables.Msg.Message; | ||||||
|  |  | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  |  | ||||||
| @Entity | @Entity | ||||||
| @ -20,6 +24,15 @@ public class User { | |||||||
|     private String profilePictureUrl; |     private String profilePictureUrl; | ||||||
|     private ovh.herisson.Clyde.Tables.Role role; |     private ovh.herisson.Clyde.Tables.Role role; | ||||||
|     private String password; |     private String password; | ||||||
|  |  | ||||||
|  | 	////// Extension Messagerie ///// | ||||||
|  | 	@OneToMany(mappedBy = "author", cascade = CascadeType.ALL) | ||||||
|  | 	private List<Message> msgs; | ||||||
|  | 	///////////////////////////////// | ||||||
|  |  | ||||||
|  | 	@ManyToMany( mappedBy = "members" ) | ||||||
|  | 	private List<Discussion> discussions; | ||||||
|  |  | ||||||
|     public User(String lastName, String firstName, String email, String address, |     public User(String lastName, String firstName, String email, String address, | ||||||
|                 String country, Date birthDate, String profilePictureUrl, Role role, String password) |                 String country, Date birthDate, String profilePictureUrl, Role role, String password) | ||||||
|     { |     { | ||||||
|  | |||||||
							
								
								
									
										213
									
								
								frontend/src/Apps/Msg.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								frontend/src/Apps/Msg.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,213 @@ | |||||||
|  | <!---------------------------------------------------- | ||||||
|  | 	File:  Msg.vue | ||||||
|  | 	Author: Anthony Debucquoy | ||||||
|  | 	Scope: Extension messagerie | ||||||
|  | 	Description: Main msg page | ||||||
|  | -----------------------------------------------------> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | 	import { ref, reactive } from 'vue' | ||||||
|  | 	import { discussionsList, currentDiscussion, fetchDiscussion, createDiscussion, sendMessage, updateDiscussionName, invite, removeMember} from '@/rest/msg.js' | ||||||
|  |  | ||||||
|  | 	const msgContent = ref(""); | ||||||
|  | 	const addMember = ref(false); | ||||||
|  | 	const currentTitle = ref(""); | ||||||
|  |  | ||||||
|  | 	function formatTime(date){ | ||||||
|  | 		return date.getHours() + ":" + date.getMinutes() + " " + date.getDate() + "/" + date.getMonth(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <template> | ||||||
|  | 	<div id="msg"> | ||||||
|  | 		<div id="discList"> | ||||||
|  | 			<div @click="fetchDiscussion(discussion.id).then(e => currentTitle = currentDiscussion.name)" class="discItem" v-for="discussion in discussionsList" :key="discussion.id">{{ discussion.name }}</div> | ||||||
|  | 			<button id="createDiscussion" @click="createDiscussion('New Discussion')">+</button> | ||||||
|  | 		</div> | ||||||
|  | 		<div id="discussion" v-if="currentDiscussion.length != 0"> | ||||||
|  | 			<h1 id=msgName ><input class="InputTitle" type="text" @change="updateDiscussionName(currentDiscussion.id, currentTitle)" v-model="currentTitle"></h1> | ||||||
|  | 			<div id=msgs> | ||||||
|  | 				<div class="msg" v-for="msg in currentDiscussion.msgs" :sender="msg.sender" :key="msg.id"> | ||||||
|  | 					{{ msg.content }}<br/> | ||||||
|  | 					<span class="sender"><span v-if="!msg.sender">{{ msg.author.firstName }} {{ msg.author.lastName.toUpperCase() }}</span> {{formatTime(new Date(msg.created))}}</span> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 			<div id=messageBox> | ||||||
|  | 				<input type="text" @keyup.enter="sendMessage(currentDiscussion.id, msgContent, null); msgContent = ''" v-model="msgContent"> | ||||||
|  | 				<input type="submit" @click="sendMessage(currentDiscussion.id, msgContent, null); msgContent = ''" value="send"> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 		<div id="members" v-if="currentDiscussion.length != 0"> | ||||||
|  | 			<div class="memberItem" v-for="member in currentDiscussion.members" @click="removeMember(currentDiscussion.id, member.regNo)" :key="member.id"><span>{{ member.firstName }} {{ member.lastName.toUpperCase() }}</span></div> | ||||||
|  | 			<input type=text id="addMembers" @focus="addMember = true" @blur="addMember = false;$event.target.value = ''" @change="invite(currentDiscussion.id, $event.target.value)" :placeholder="addMember ? 'Regno' : '+'"/> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  |  | ||||||
|  | div#msg{ | ||||||
|  | 	position: relative; | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: 100%; | ||||||
|  |  | ||||||
|  | 	display: grid; | ||||||
|  | 	grid-template-columns: 20% auto 10%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div#discList{ | ||||||
|  | 	margin: 30px 0 30px 30px; | ||||||
|  | 	background-color: rgba(255, 255, 255, 0.05); | ||||||
|  | 	border-radius: 10px; | ||||||
|  | 	overflow: hidden; | ||||||
|  | 	padding: 10px; | ||||||
|  | 	display: flex; | ||||||
|  | 	flex-direction: column; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div#members{ | ||||||
|  | 	margin: 30px 0; | ||||||
|  | 	border-radius: 10px 0 0 10px; | ||||||
|  | 	background-color: red; | ||||||
|  | 	background-color: rgba(255, 255, 255, 0.05); | ||||||
|  | 	overflow: hidden; | ||||||
|  | 	display: flex; | ||||||
|  | 	padding: 10px 0 0 10px; | ||||||
|  | 	flex-direction: column; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .InputTitle{ | ||||||
|  | 	all: inherit; | ||||||
|  | 	margin: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .discItem{ | ||||||
|  | 	color: darkorange; | ||||||
|  | 	display: flex; | ||||||
|  | 	font-family: sans-serif; | ||||||
|  | 	font-weight: bold; | ||||||
|  | 	height: 4vh; | ||||||
|  | 	margin: 5px; | ||||||
|  | 	border-radius: 0 30px 30px 0; | ||||||
|  | 	align-items: center; | ||||||
|  | 	justify-content: center; | ||||||
|  | 	border: 1px solid darkorange; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .memberItem{ | ||||||
|  | 	color: darkorange; | ||||||
|  | 	display: flex; | ||||||
|  | 	font-family: sans-serif; | ||||||
|  | 	font-weight: bold; | ||||||
|  | 	height: 4vh; | ||||||
|  | 	margin: 5px; | ||||||
|  | 	border-radius: 30px 0 0 30px; | ||||||
|  | 	align-items: center; | ||||||
|  | 	justify-content: center; | ||||||
|  | 	border: 1px solid darkorange; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .memberItem:hover span{ | ||||||
|  | 	display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .memberItem:hover{ | ||||||
|  | 	background-color: red; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .memberItem:hover:before{ | ||||||
|  | 	color: white; | ||||||
|  | 	content: "X" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #createDiscussion{ | ||||||
|  | 	height: 4vh; | ||||||
|  | 	margin: 5px; | ||||||
|  | 	color: white; | ||||||
|  | 	background-color: green; | ||||||
|  | 	border-radius: 0 30px 30px 0; | ||||||
|  | 	border: none; | ||||||
|  | 	font-weight: 900; | ||||||
|  | 	font-size: 2em; | ||||||
|  | } | ||||||
|  | #addMembers{ | ||||||
|  | 	height: 4vh; | ||||||
|  | 	margin: 5px; | ||||||
|  | 	text-align: center; | ||||||
|  | 	color: white; | ||||||
|  | 	background-color: green; | ||||||
|  | 	border-radius: 30px 0 0 30px; | ||||||
|  | 	border: none; | ||||||
|  | 	font-weight: 900; | ||||||
|  | 	font-size: 2em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div#discussion{ | ||||||
|  | 	display: flex; | ||||||
|  | 	flex-direction: column; | ||||||
|  | 	margin: 30px; | ||||||
|  | 	background-color: rgba(255, 255, 255, 0.05); | ||||||
|  | 	border-radius: 10px; | ||||||
|  | 	overflow: hidden; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #msgName{ | ||||||
|  | 	text-align: center; | ||||||
|  | 	display: block; | ||||||
|  | 	background-color: #2a1981; | ||||||
|  | 	border-radius: 5px; | ||||||
|  | 	color: white; | ||||||
|  | 	width: 75%; | ||||||
|  | 	margin: 30px auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .discItem:hover{ | ||||||
|  | 	background-color: gray; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #msgs{ | ||||||
|  | 	display: flex; | ||||||
|  | 	flex-grow: 1; | ||||||
|  | 	flex-direction: column; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .msg { | ||||||
|  | 	background-color: aliceblue; | ||||||
|  | 	font-family: sans-serif; | ||||||
|  | 	margin: 10px; | ||||||
|  | 	padding: 5px; | ||||||
|  | 	border-radius: 3px; | ||||||
|  | 	max-width: 50%; | ||||||
|  | 	align-self: start; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .sender{ | ||||||
|  | 	display: inline-block; | ||||||
|  | 	color: gray; | ||||||
|  | 	font-size: 0.5em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .msg[sender=true]{ | ||||||
|  | 	background-color: darkorange; | ||||||
|  | 	align-self: end; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #messageBox{ | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: 30px; | ||||||
|  | 	background-color: white; | ||||||
|  | 	display: flex; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #messageBox input[type="text"]{ | ||||||
|  | 	all: inherit; | ||||||
|  | 	padding: 0 10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #messageBox input[type="submit"]{ | ||||||
|  | 	border: inherit; | ||||||
|  | 	padding: 0 10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | </style> | ||||||
| @ -9,6 +9,7 @@ import Profil from "@/Apps/Profil.vue" | |||||||
| import Courses from "@/Apps/ManageCourses.vue" | import Courses from "@/Apps/ManageCourses.vue" | ||||||
| import Users from "@/Apps/UsersList.vue" | import Users from "@/Apps/UsersList.vue" | ||||||
| import Students from "@/Apps/StudentsList.vue" | import Students from "@/Apps/StudentsList.vue" | ||||||
|  | import Msg from "@/Apps/Msg.vue" | ||||||
|  |  | ||||||
| const apps = { | const apps = { | ||||||
| 		'/login': LoginPage, | 		'/login': LoginPage, | ||||||
| @ -17,6 +18,7 @@ const apps = { | |||||||
| 		'/manage-courses' : Courses, | 		'/manage-courses' : Courses, | ||||||
| 		'/users-list' : Users, | 		'/users-list' : Users, | ||||||
| 		'/students-list' : Students, | 		'/students-list' : Students, | ||||||
|  | 		'/msg' : Msg, | ||||||
| } | } | ||||||
|  |  | ||||||
| const appsList = { | const appsList = { | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								frontend/src/rest/msg.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								frontend/src/rest/msg.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | /******************************************************* | ||||||
|  |  * File: msg.js | ||||||
|  |  * Author: Anthony Debucquoy | ||||||
|  |  * Scope: Extension messagerie | ||||||
|  |  * Description: Messages frontend api consumer | ||||||
|  |  *******************************************************/ | ||||||
|  |  | ||||||
|  | import { restGet, restPost, restPatch } from './restConsumer.js' | ||||||
|  | import { ref } from 'vue' | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * - id | ||||||
|  |  * - name | ||||||
|  |  * - members | ||||||
|  |  */ | ||||||
|  | export const discussionsList = ref(); | ||||||
|  | export const currentDiscussion = ref([]); | ||||||
|  | let timerSet = false | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export async function createDiscussion(name){ | ||||||
|  | 	let disc = await restPost("/discussion", {name: name}); | ||||||
|  | 	discussionsList.value.push(disc); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export async function invite(id, regNo){ | ||||||
|  | 	restPatch("/discussion/"+ id+ "/add", {regNo: parseInt(regNo)}).then(() => fetchDiscussion(id)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function removeMember(id, regNo){ | ||||||
|  | 	restPatch("/discussion/"+ id+ "/remove", {regNo: parseInt(regNo)}).then(() => fetchDiscussion(id)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function sendMessage(id, content, responseId){ | ||||||
|  | 	let data = { | ||||||
|  | 		content: content, | ||||||
|  | 		response: responseId, | ||||||
|  | 	} | ||||||
|  | 	restPost("/discussion/" + id, data).then(() => fetchDiscussion(id)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function updateDiscussionName(id, name){ | ||||||
|  | 	restPatch("/discussion/" + id, {name: name}).then(() => fetchDiscussions()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async function fetchDiscussions(){ | ||||||
|  | 	discussionsList.value = await restGet("/discussions"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function fetchDiscussion(id){ | ||||||
|  | 	currentDiscussion.value = await restGet("/discussion/" + id); | ||||||
|  | 	if(!timerSet){ | ||||||
|  | 		timerSet = true; | ||||||
|  | 		setTimeout(() => {timerSet = false;fetchDiscussion(currentDiscussion.value.id)} , 5000); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | await fetchDiscussions(); | ||||||
		Reference in New Issue
	
	Block a user