Settings page, Avatars, Displaynames, pronouns
This commit is contained in:
parent
064ded5fe5
commit
fbcaa886c1
@ -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<ChatResult> {
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class FriendProcessor extends Processor<FriendResult> {
|
||||
private List<UserResult> friends2Users(List<Long> 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());
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class RoomProcessor extends Processor<RoomResultV2> {
|
||||
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<RoomResultV2> {
|
||||
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());
|
||||
}
|
||||
|
@ -63,7 +63,10 @@ public class APIController {
|
||||
List<Chat> 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<String> 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<LongPollResult> getNotify(@RequestParam("last_chat_id") Long last_chat_id,
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -12,4 +12,5 @@ public class FriendPageResult {
|
||||
private String avatar;
|
||||
private String username;
|
||||
private Long id;
|
||||
private String displayName;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -11,4 +11,17 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 += '<div class="games-room-one-body-members-one"><div class="games-room-one-body-members-one-avatar" style="background-image: url("https://i.dogecdn.wtf/7lurfckMFrYXm4gf");"><a href="/profile/'+room_player.id+'"></a><div class="_online"></div></div><div class="games-room-one-body-members-one-nick"><a href="/profile/'+room_player.id+'">'+room_player.username+'</a></div></div>';
|
||||
room_p_html += '<div class="games-room-one-body-members-one"><div class="games-room-one-body-members-one-avatar" data-avatar-id="'+room_player.avatarId+'"><a href="/profile/'+room_player.id+'"></a><div class="_online"></div></div><div class="games-room-one-body-members-one-nick"><a href="/profile/'+room_player.id+'">'+room_player.username+'</a></div></div>';
|
||||
}
|
||||
for( let u = 0; u < (room.playerCount - room.players.length); u++ ) {
|
||||
room_p_html += '<div data-room-id="'+room.id+'" onclick="joinRoom(this)" class="games-room-one-body-members-one _slot_join"><div class="games-room-one-body-members-one-avatar"><ion-icon name="add-outline" style="color: #656d78;"></ion-icon></div><div class="games-room-one-body-members-one-nick"><span>Join</span></div></div>';
|
||||
@ -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 = '<div class="friend-one" data-friend-id="'+friend.id+'"><a href="/profile/'+friend.id+'" class="navbar-btn"><img class="navbar-profile-img" src="https://avatars.githubusercontent.com/u/102559365?v=4"></a><span>'+friend.username+'</span><ion-icon onClick="sendInviteMessage('+friend.id+')" name="person-add" role="img" class="md hydrated"></ion-icon></div>';
|
||||
let fr_html = '<div class="friend-one" data-friend-id="'+friend.id+'"><a href="/profile/'+friend.id+'" class="navbar-btn"><img class="navbar-profile-img" src="/image/store/'+friend.avatarId+'"></a><span>'+friend.username+'</span><ion-icon onClick="sendInviteMessage('+friend.id+')" name="person-add" role="img" class="md hydrated"></ion-icon></div>';
|
||||
$(".friends-online-list").append(fr_html);
|
||||
} else if( friend.action == 'REMOVE' ) {
|
||||
for( let u = 0; u < fids.length; u++ ) {
|
||||
|
40
src/main/resources/static/javascript/settings.js
Normal file
40
src/main/resources/static/javascript/settings.js
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
@ -33,7 +33,7 @@
|
||||
<img class="navbar-profile-img" th:src="${profile.avatar}">
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" th:text="${profile.username}"></a></li>
|
||||
<li><a class="dropdown-item" th:text="${profile.displayName}"></a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" th:href="${profile.href}">Profile</a></li>
|
||||
<li><a class="dropdown-item" href="/settings">Settings</a></li>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</a>
|
||||
<div class="friends-list-one-info">
|
||||
<div class="friends-list-one-info-nick">
|
||||
<a th:href="${friend.href}" th:text="${friend.username}"></a>
|
||||
<a th:href="${friend.href}" th:text="${friend.displayName}"></a>
|
||||
</div>
|
||||
<div class="friends-list-one-info-actions" style="display: none;">
|
||||
<button onClick="removeFriend(this)" class="btn btn-danger btn-sm" th:data-friend-id="${friend.id}">Remove</button>
|
||||
|
@ -15,27 +15,32 @@
|
||||
<div class="grid">
|
||||
<div class="g-col-4" style="display: flex; justify-content: center;">
|
||||
<div style="width: 50%;">
|
||||
<img class="profile-img" src="https://tg.dogecdn.wtf/v2/AgACAgIAAx0EWSIHJgAC0MBf64kG8Q99Gm69NezdtLUxTJaBiQACvrExG1sQWEtraYhjusGQAAGJdBmYLgADAQADAgADeAAD8RcFAAEeBA.jpg">
|
||||
<button class="btn btn-primary" style="margin-top: 10px; font-size: 0.7rem;">Update avatar</button>
|
||||
<img class="profile-img" th:src="${profile.avatar}">
|
||||
<input type="file" id="avatarFile" accept="image/png, image/jpeg, image/gif" style="display: none;">
|
||||
<button class="btn btn-primary" onClick="triggerFile(this)" style="margin-top: 10px; font-size: 0.7rem;">Update avatar</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g-col-8" style="padding: 5px;">
|
||||
<div class="mb-3">
|
||||
<label for="displayname-input" class="form-label">Display name (may contain unicode symbols)</label>
|
||||
<input type="text" class="form-control" id="displayname-input" autocomplete="off">
|
||||
<input type="text" th:value="${profile.displayName}" class="form-control" id="displayname-input" autocomplete="off">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="nickname-input" class="form-label">Nickname</label>
|
||||
<input type="text" class="form-control" id="nickname-input" autocomplete="off">
|
||||
<input type="text" th:value="${profile.username}" class="form-control" id="nickname-input" autocomplete="off">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pronouns-select" class="form-label">Pronouns</label>
|
||||
<select class="form-select" id="pronouns-select" aria-label="">
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
<option value="1" selected>she/her</option>
|
||||
<option value="2">he/him</option>
|
||||
<option value="3">they/them</option>
|
||||
<option value="4">idc</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary" onClick="saveProfileInfo()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,5 +95,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/essentials}"></th:block>
|
||||
<script src="/static/javascript/settings.js"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user