From fbcaa886c1d903cbaf75e07479a6578848797510 Mon Sep 17 00:00:00 2001 From: alterdekim Date: Mon, 26 Feb 2024 03:50:52 +0300 Subject: [PATCH] Settings page, Avatars, Displaynames, pronouns --- .../component/processors/ChatProcessor.java | 7 +++- .../component/processors/FriendProcessor.java | 2 +- .../component/processors/RoomProcessor.java | 4 +- .../game/controller/APIController.java | 15 ++++++- .../game/controller/ImageController.java | 10 +---- .../game/controller/StaticController.java | 27 ++++++++++++- .../alterdekim/game/dto/FriendPageResult.java | 1 + .../com/alterdekim/game/dto/UserResult.java | 4 +- .../com/alterdekim/game/entities/Image.java | 6 +-- .../com/alterdekim/game/entities/User.java | 6 +++ .../game/repository/UserRepository.java | 13 ++++++ .../game/service/UserServiceImpl.java | 11 ++++- .../game/util/AuthenticationUtil.java | 2 +- src/main/resources/static/javascript/games.js | 9 ++++- .../resources/static/javascript/settings.js | 40 +++++++++++++++++++ .../resources/templates/fragments/navbar.html | 2 +- src/main/resources/templates/friends.html | 2 +- src/main/resources/templates/settings.html | 20 ++++++---- 18 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 src/main/resources/static/javascript/settings.js diff --git a/src/main/java/com/alterdekim/game/component/processors/ChatProcessor.java b/src/main/java/com/alterdekim/game/component/processors/ChatProcessor.java index 07a60b6..d05f3eb 100644 --- a/src/main/java/com/alterdekim/game/component/processors/ChatProcessor.java +++ b/src/main/java/com/alterdekim/game/component/processors/ChatProcessor.java @@ -7,6 +7,7 @@ import com.alterdekim.game.component.result.LongPollResultType; import com.alterdekim.game.dto.ChatResult; import com.alterdekim.game.dto.UserResult; import com.alterdekim.game.entities.Chat; +import com.alterdekim.game.entities.User; import com.alterdekim.game.service.ChatServiceImpl; import com.alterdekim.game.service.UserServiceImpl; import com.alterdekim.game.util.StringUtil; @@ -30,7 +31,9 @@ public class ChatProcessor extends Processor { return new LongPollResultSingle<>(getType(), results.stream() .peek(c -> c.setMessage(StringUtil.escapeTags(getParent().getUserService(), c.getMessage()))) - .map(c -> new ChatResult(c, new UserResult( c.getUserId(), getParent().getUserService().findById(c.getUserId()).getUsername()))) - .collect(Collectors.toList())); + .map(c -> { + User u1 = getParent().getUserService().findById(c.getUserId()); + return new ChatResult(c, new UserResult( c.getUserId(), u1.getUsername(), u1.getAvatarId())); + }).collect(Collectors.toList())); } } diff --git a/src/main/java/com/alterdekim/game/component/processors/FriendProcessor.java b/src/main/java/com/alterdekim/game/component/processors/FriendProcessor.java index ab16e88..391dac6 100644 --- a/src/main/java/com/alterdekim/game/component/processors/FriendProcessor.java +++ b/src/main/java/com/alterdekim/game/component/processors/FriendProcessor.java @@ -61,7 +61,7 @@ public class FriendProcessor extends Processor { private List friends2Users(List friendIds) { return friendIds.stream() .map(id -> getParent().getUserService().findById(id)) - .map(u -> new UserResult(u.getId(), u.getUsername())) + .map(u -> new UserResult(u.getId(), u.getDisplayName(), u.getAvatarId())) .filter(f -> getParent().getMap().keySet().stream().anyMatch(l -> l.longValue() == f.getId().longValue())) .collect(Collectors.toList()); } diff --git a/src/main/java/com/alterdekim/game/component/processors/RoomProcessor.java b/src/main/java/com/alterdekim/game/component/processors/RoomProcessor.java index ce85e6b..e0118a8 100644 --- a/src/main/java/com/alterdekim/game/component/processors/RoomProcessor.java +++ b/src/main/java/com/alterdekim/game/component/processors/RoomProcessor.java @@ -62,7 +62,7 @@ public class RoomProcessor extends Processor { return rooms.stream() .map( r -> new RoomResult(r.getId(), r.getPlayerCount(), getParent().getRoomPlayerService().findByRoomId(r.getId()).stream() .map(p -> getParent().getUserService().findById(p.getUserId())) - .map(p -> new UserResult(p.getId(), p.getUsername())) + .map(p -> new UserResult(p.getId(), p.getDisplayName(), p.getAvatarId())) .collect(Collectors.toList()))) .collect(Collectors.toList()); } @@ -71,7 +71,7 @@ public class RoomProcessor extends Processor { return rooms.stream() .map( r -> new RoomResultV2(RoomResultState.ADD_CHANGE, r.getId(), r.getPlayerCount(), getParent().getRoomPlayerService().findByRoomId(r.getId()).stream() .map(p -> getParent().getUserService().findById(p.getUserId())) - .map(p -> new UserResult(p.getId(), p.getUsername())) + .map(p -> new UserResult(p.getId(), p.getDisplayName(), p.getAvatarId())) .collect(Collectors.toList()))) .collect(Collectors.toList()); } diff --git a/src/main/java/com/alterdekim/game/controller/APIController.java b/src/main/java/com/alterdekim/game/controller/APIController.java index 16a05a1..19aff9c 100644 --- a/src/main/java/com/alterdekim/game/controller/APIController.java +++ b/src/main/java/com/alterdekim/game/controller/APIController.java @@ -63,7 +63,10 @@ public class APIController { List results = chatService.getLastChats(count); return ResponseEntity.ok(results.stream() .peek(c -> c.setMessage(StringUtil.escapeTags(userService, c.getMessage()))) - .map(c -> new ChatResult(c, new UserResult( c.getUserId(), userService.findById(c.getUserId()).getUsername()))) + .map(c -> { + User u1 = userService.findById(c.getUserId()); + return new ChatResult(c, new UserResult( c.getUserId(), u1.getUsername(), u1.getAvatarId())); + }) .collect(Collectors.toList())); } @@ -156,6 +159,16 @@ public class APIController { return ResponseEntity.ok().build(); } + @PostMapping("/api/v1/settings/profile/save") + public ResponseEntity saveProfile( @RequestParam("display_name") String displayName, + @RequestParam("nickname") String nickname, + @RequestParam("pronouns") String pronouns ) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + Long userId = userService.findByUsername(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername()).getId(); + userService.updateProfileInfo(userId, displayName, nickname, pronouns); + return ResponseEntity.ok().build(); + } + @PostMapping("/async/notify/get/") @ResponseBody public DeferredResult getNotify(@RequestParam("last_chat_id") Long last_chat_id, diff --git a/src/main/java/com/alterdekim/game/controller/ImageController.java b/src/main/java/com/alterdekim/game/controller/ImageController.java index 9cc1627..268ab43 100644 --- a/src/main/java/com/alterdekim/game/controller/ImageController.java +++ b/src/main/java/com/alterdekim/game/controller/ImageController.java @@ -15,15 +15,7 @@ import org.springframework.web.server.ResponseStatusException; class ImageController { @Autowired - ImageRepository imageRepository; - - @PostMapping(value = "/image/upload/") - private Long uploadImage(@RequestParam MultipartFile multipartImage) throws Exception { - Image dbImage = new Image(); - dbImage.setContent(multipartImage.getBytes()); - return imageRepository.save(dbImage) - .getId(); - } + private ImageRepository imageRepository; @GetMapping(value = "/image/store/{imageId}", produces = MediaType.IMAGE_JPEG_VALUE) Resource downloadImage(@PathVariable Long imageId) { diff --git a/src/main/java/com/alterdekim/game/controller/StaticController.java b/src/main/java/com/alterdekim/game/controller/StaticController.java index 42de855..c0f2e79 100644 --- a/src/main/java/com/alterdekim/game/controller/StaticController.java +++ b/src/main/java/com/alterdekim/game/controller/StaticController.java @@ -2,7 +2,9 @@ package com.alterdekim.game.controller; import com.alterdekim.game.dto.AuthApiObject; import com.alterdekim.game.dto.FriendPageResult; +import com.alterdekim.game.entities.Image; import com.alterdekim.game.entities.User; +import com.alterdekim.game.repository.ImageRepository; import com.alterdekim.game.service.FriendServiceImpl; import com.alterdekim.game.service.UserServiceImpl; import com.alterdekim.game.util.AuthenticationUtil; @@ -14,6 +16,9 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; @Slf4j @Controller @@ -25,6 +30,9 @@ public class StaticController { @Autowired private FriendServiceImpl friendService; + @Autowired + private ImageRepository imageRepository; + @GetMapping("/rules") public String rulesPage(Model model) { AuthenticationUtil.authProfile(model, userService); @@ -53,7 +61,7 @@ public class StaticController { Long userId = AuthenticationUtil.authProfile(model, userService).getId(); model.addAttribute("friends", friendService.getFriendsOfUserId(userId).stream() .map(l -> userService.findById(l)) - .map(u -> new FriendPageResult("/profile/"+u.getId(), "background-image: url("https://i.dogecdn.wtf/wxEpEiovduvcXadQ");", u.getUsername(), u.getId()))); + .map(u -> new FriendPageResult("/profile/"+u.getId(), "background-image: url(\"/image/store/"+u.getAvatarId()+"\");", u.getUsername(), u.getId(), u.getDisplayName()))); return "friends"; } @@ -63,6 +71,23 @@ public class StaticController { return "settings"; } + @PostMapping(value = "/settings") + private String uploadImage(@RequestParam(required = true, name = "file-0") MultipartFile multipartImage) throws Exception { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if( authentication.isAuthenticated() ) { + try { + User u = userService.findByUsername(((org.springframework.security.core.userdetails.User) authentication.getPrincipal()).getUsername()); + Image dbImage = new Image(); + dbImage.setContent(multipartImage.getBytes()); + userService.setAvatar(u.getId(), imageRepository.save(dbImage) + .getId()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + return "redirect:/settings"; + } + @GetMapping("/") public String homePage(Model model) { AuthenticationUtil.authProfile(model, userService); diff --git a/src/main/java/com/alterdekim/game/dto/FriendPageResult.java b/src/main/java/com/alterdekim/game/dto/FriendPageResult.java index 8899c52..3454912 100644 --- a/src/main/java/com/alterdekim/game/dto/FriendPageResult.java +++ b/src/main/java/com/alterdekim/game/dto/FriendPageResult.java @@ -12,4 +12,5 @@ public class FriendPageResult { private String avatar; private String username; private Long id; + private String displayName; } diff --git a/src/main/java/com/alterdekim/game/dto/UserResult.java b/src/main/java/com/alterdekim/game/dto/UserResult.java index af2534a..9442f17 100644 --- a/src/main/java/com/alterdekim/game/dto/UserResult.java +++ b/src/main/java/com/alterdekim/game/dto/UserResult.java @@ -10,9 +10,11 @@ import lombok.NoArgsConstructor; public class UserResult { private Long id; private String username; + private Long avatarId; public boolean equals(UserResult r2) { return this.getId().longValue() == r2.getId().longValue() && - this.getUsername().equals(r2.getUsername()); + this.getUsername().equals(r2.getUsername()) && + this.getAvatarId().longValue() == r2.getAvatarId().longValue(); } } diff --git a/src/main/java/com/alterdekim/game/entities/Image.java b/src/main/java/com/alterdekim/game/entities/Image.java index e812ccd..3670d12 100644 --- a/src/main/java/com/alterdekim/game/entities/Image.java +++ b/src/main/java/com/alterdekim/game/entities/Image.java @@ -1,9 +1,6 @@ package com.alterdekim.game.entities; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Lob; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,5 +18,6 @@ public class Image { Long id; @Lob + @Column(length=16777211) byte[] content; } diff --git a/src/main/java/com/alterdekim/game/entities/User.java b/src/main/java/com/alterdekim/game/entities/User.java index 68599d6..9360f57 100644 --- a/src/main/java/com/alterdekim/game/entities/User.java +++ b/src/main/java/com/alterdekim/game/entities/User.java @@ -31,6 +31,12 @@ public class User { @Column(nullable = false) private Long avatarId; + @Column(nullable = false) + private String displayName; + + @Column(nullable = false) + private String pronouns; + @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL) @JoinTable( name="users_roles", diff --git a/src/main/java/com/alterdekim/game/repository/UserRepository.java b/src/main/java/com/alterdekim/game/repository/UserRepository.java index e120f47..5cfb3d5 100644 --- a/src/main/java/com/alterdekim/game/repository/UserRepository.java +++ b/src/main/java/com/alterdekim/game/repository/UserRepository.java @@ -11,4 +11,17 @@ import org.springframework.transaction.annotation.Transactional; @Repository public interface UserRepository extends JpaRepository { User findByUsername(String username); + + @Transactional + @Modifying + @Query(value = "UPDATE User u SET u.avatarId = :avatarId WHERE u.id = :userId") + void updateAvatar(@Param("userId") Long userId, @Param("avatarId") Long avatarId); + + @Transactional + @Modifying + @Query(value = "UPDATE User u SET u.displayName = :displayName, u.pronouns = :pronouns, u.username = :username WHERE u.id = :userId") + void updateProfileInfo(@Param("userId") Long userId, + @Param("displayName") String displayName, + @Param("username") String username, + @Param("pronouns") String pronouns); } diff --git a/src/main/java/com/alterdekim/game/service/UserServiceImpl.java b/src/main/java/com/alterdekim/game/service/UserServiceImpl.java index 92d4308..de5debb 100644 --- a/src/main/java/com/alterdekim/game/service/UserServiceImpl.java +++ b/src/main/java/com/alterdekim/game/service/UserServiceImpl.java @@ -31,7 +31,8 @@ public class UserServiceImpl implements UserService { public void saveUser(UserDTO userDto) { User user = new User(); user.setUsername(userDto.getUsername()); - + user.setPronouns("she/her"); + user.setDisplayName(userDto.getUsername()); user.setPassword(passwordEncoder.encode(userDto.getPassword())); Role role = roleRepository.findByName("ROLE_ADMIN"); if(role == null){ @@ -69,5 +70,13 @@ public class UserServiceImpl implements UserService { role.setName("ROLE_ADMIN"); return roleRepository.save(role); } + + public void updateProfileInfo(Long userId, String displayName, String nickname, String pronouns) { + userRepository.updateProfileInfo(userId, displayName, nickname, pronouns); + } + + public void setAvatar(Long userId, Long imageId) { + userRepository.updateAvatar(userId, imageId); + } } diff --git a/src/main/java/com/alterdekim/game/util/AuthenticationUtil.java b/src/main/java/com/alterdekim/game/util/AuthenticationUtil.java index f6eb2b7..3f2c6d2 100644 --- a/src/main/java/com/alterdekim/game/util/AuthenticationUtil.java +++ b/src/main/java/com/alterdekim/game/util/AuthenticationUtil.java @@ -15,7 +15,7 @@ public class AuthenticationUtil { if( authentication.isAuthenticated() ) { try { User u = userService.findByUsername(((org.springframework.security.core.userdetails.User) authentication.getPrincipal()).getUsername()); - model.addAttribute("profile", new FriendPageResult("/profile/" + u.getId(), "", u.getUsername(), u.getId())); + model.addAttribute("profile", new FriendPageResult("/profile/" + u.getId(), "/image/store/"+u.getAvatarId(), u.getUsername(), u.getId(), u.getDisplayName())); return u; } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/src/main/resources/static/javascript/games.js b/src/main/resources/static/javascript/games.js index 3ba6d0a..85e8570 100644 --- a/src/main/resources/static/javascript/games.js +++ b/src/main/resources/static/javascript/games.js @@ -111,7 +111,7 @@ function successPolling(data) { let room_p_html = ''; for( let u = 0; u < room.players.length; u++ ) { let room_player = room.players[u]; - room_p_html += ''; + room_p_html += ''; } for( let u = 0; u < (room.playerCount - room.players.length); u++ ) { room_p_html += '
Join
'; @@ -141,6 +141,11 @@ function successPolling(data) { }); } } + $(".games-room-one-body-members-one-avatar").each(function() { + if($(this).attr("data-avatar-id") != undefined) { + $(this).css("background-image", "url('/image/store/"+$(this).attr("data-avatar-id")+"')"); + } + }); for( let i = 0; i < friends.length; i++ ) { let friend = friends[i]; if( friend.action == 'ADD' ) { @@ -151,7 +156,7 @@ function successPolling(data) { } } fids.push({id: friend.id, username: friend.username}); - let fr_html = '
'+friend.username+'
'; + let fr_html = '
'+friend.username+'
'; $(".friends-online-list").append(fr_html); } else if( friend.action == 'REMOVE' ) { for( let u = 0; u < fids.length; u++ ) { diff --git a/src/main/resources/static/javascript/settings.js b/src/main/resources/static/javascript/settings.js new file mode 100644 index 0000000..5c24174 --- /dev/null +++ b/src/main/resources/static/javascript/settings.js @@ -0,0 +1,40 @@ +function triggerFile(obj) { + $("#avatarFile").trigger('click'); +} + +function saveProfileInfo() { + let display_name = $("#displayname-input").val(); + let nickname = $("#nickname-input").val(); + let pronouns = $('#pronouns-select').find(":selected").text(); + $.ajax({ + url: "/api/v1/settings/profile/save", + data: { + display_name: display_name, + nickname: nickname, + pronouns: pronouns + }, + method: "POST" + }).done(function() { + window.location.reload(); + }); +} + +$(document).ready(function() { + $('#avatarFile').change(function(evt) { + var data = new FormData(); + $.each($(this)[0].files, function(i, file) { + data.append('file-'+i, file); + }); + + $.ajax({ + url: "/settings", + method: "POST", + cache: false, + contentType: false, + processData: false, + data: data + }).done(function() { + window.location.reload(); + }); + }); +}); \ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index 7ccb1c3..19aad15 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -33,7 +33,7 @@