Followers page / Games stats / Chat bugfix

This commit is contained in:
Michael Wain 2024-02-29 02:07:59 +03:00
parent 8408eb874e
commit ccc87d57f4
17 changed files with 185 additions and 32 deletions

View File

@ -7,11 +7,7 @@ import com.alterdekim.game.component.result.LongPollResultType;
import com.alterdekim.game.dto.FriendResult;
import com.alterdekim.game.dto.FriendState;
import com.alterdekim.game.dto.UserResult;
import com.alterdekim.game.service.FriendServiceImpl;
import com.alterdekim.game.service.UserServiceImpl;
import com.alterdekim.game.util.ListUtil;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Comparator;
@ -32,7 +28,7 @@ public class FriendProcessor extends Processor<FriendResult> {
return new LongPollResultSingle<>(getType(), friends2Users(getParent().getFriendService().getFriendsOfUserId(userId))
.stream()
.map(f -> new FriendResult(FriendState.ADD, f.getId(), f.getUsername()))
.map(f -> new FriendResult(FriendState.ADD, f.getId(), f.getUsername(), f.getAvatarId()))
.collect(Collectors.toList()));
}
@ -45,16 +41,16 @@ public class FriendProcessor extends Processor<FriendResult> {
List<FriendResult> fr = new ArrayList<>();
for( UserResult r : clientFriends ) {
if( userResults.stream().noneMatch(t -> t.getId().longValue() == r.getId().longValue()) ) {
fr.add(new FriendResult(FriendState.REMOVE, r.getId(), r.getUsername()));
fr.add(new FriendResult(FriendState.REMOVE, r.getId(), r.getUsername(), r.getAvatarId()));
continue;
}
UserResult r1 = userResults.stream().filter( t -> t.getId().longValue() == r.getId().longValue()).findFirst().get();
if( !r.equals(r1) ) {
fr.add(new FriendResult(FriendState.ADD, r1.getId(), r1.getUsername()));
fr.add(new FriendResult(FriendState.ADD, r1.getId(), r1.getUsername(), r1.getAvatarId()));
}
urr.remove(r1);
}
urr.forEach(r -> fr.add(new FriendResult(FriendState.ADD, r.getId(), r.getUsername())));
urr.forEach(r -> fr.add(new FriendResult(FriendState.ADD, r.getId(), r.getUsername(), r.getAvatarId())));
return fr.stream().sorted(Comparator.comparingLong(FriendResult::getId)).collect(Collectors.toList());
}

View File

@ -6,6 +6,7 @@ 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.GamesService;
import com.alterdekim.game.service.UserServiceImpl;
import com.alterdekim.game.util.AuthenticationUtil;
import com.alterdekim.game.util.Hash;
@ -38,6 +39,9 @@ public class StaticController {
@Autowired
private ImageRepository imageRepository;
@Autowired
private GamesService gamesService;
@GetMapping("/rules")
public String rulesPage(Model model) {
AuthenticationUtil.authProfile(model, userService);
@ -70,6 +74,15 @@ public class StaticController {
return "friends";
}
@GetMapping("/followers")
public String followersPage(Model model) {
Long userId = AuthenticationUtil.authProfile(model, userService).getId();
model.addAttribute("friends", friendService.getFollowersOfUserId(userId).stream()
.map(l -> userService.findById(l))
.map(u -> new FriendPageResult("/profile/"+u.getId(), "background-image: url(\"/image/store/"+u.getAvatarId()+"\");", u.getUsername(), u.getId(), u.getDisplayName())));
return "followers";
}
@GetMapping("/profile/{id}")
public String profilePage(@PathVariable("id") Long id, Model model) {
User self = AuthenticationUtil.authProfile(model, userService);
@ -79,8 +92,8 @@ public class StaticController {
u.getUsername(),
u.getId(),
u.getDisplayName(),
0,
0,
gamesService.getGamesCount(u.getId()),
gamesService.getWonGamesCount(u.getId()),
friendService.getFriendsOfUserId(u.getId()).size(),
friendService.getFriendState(self.getId(), u.getId())));
return "profile";
@ -95,12 +108,7 @@ public class StaticController {
options.add(new SelectOption("they/them", false));
options.add(new SelectOption("idc", false));
String p = u.getPronouns();
options = options.stream().map(o -> {
if(p.equals(o.getText())) {
return new SelectOption(o.getText(), true);
}
return new SelectOption(o.getText(), false);
}).collect(Collectors.toList());
options = options.stream().map(o -> new SelectOption(o.getText(), p.equals(o.getText()))).collect(Collectors.toList());
model.addAttribute("options", options);
return "settings";
}

View File

@ -11,4 +11,5 @@ public class FriendResult {
private FriendState action;
private Long id;
private String username;
private Long avatarId;
}

View File

@ -13,8 +13,8 @@ public class ProfilePageResult {
private String username;
private Long id;
private String displayName;
private Integer games;
private Integer wins;
private Long games;
private Long wins;
private Integer friends_cnt;
private FriendFollowState follow_state;
}

View File

@ -0,0 +1,34 @@
package com.alterdekim.game.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="games")
public class Game {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long userId;
@Column(nullable = false)
private Boolean isDone;
@Column(nullable = false)
private Boolean isWon;
public Game(Long userId, Boolean isDone, Boolean isWon) {
this.userId = userId;
this.isDone = isDone;
this.isWon = isWon;
}
}

View File

@ -16,9 +16,12 @@ public interface FriendRepository extends JpaRepository<FriendStatus, Long> {
@Query(value = "SELECT IF(f.firstUserId = :userId, f.secondUserId, f.firstUserId) FROM FriendStatus f WHERE (f.firstUserId = :userId OR f.secondUserId = :userId) AND f.status = 2")
List<Long> getFriendsOfUserId(@Param("userId") Long userId);
@Query(value = "SELECT f.firstUserId FROM FriendStatus f WHERE f.secondUserId = :userId AND f.status = 1")
List<Long> getFollowersOfUserId(@Param("userId") Long userId);
@Transactional
@Modifying
@Query(value = "DELETE FROM FriendStatus f WHERE ((f.firstUserId = :userId AND f.secondUserId = :friendId) OR (f.firstUserId = :friendId AND f.secondUserId = :userId)) AND f.status = 2")
@Query(value = "DELETE FROM FriendStatus f WHERE ((f.firstUserId = :userId AND f.secondUserId = :friendId) OR (f.firstUserId = :friendId AND f.secondUserId = :userId)) AND f.status > 0")
void removeFriend(@Param("userId") Long userId, @Param("friendId") Long friendId);
@Query(value = "SELECT f FROM FriendStatus f WHERE ((f.firstUserId = :userId AND f.secondUserId = :friendId) OR (f.firstUserId = :friendId AND f.secondUserId = :userId)) AND f.status = 1")

View File

@ -0,0 +1,18 @@
package com.alterdekim.game.repository;
import com.alterdekim.game.entities.FriendStatus;
import com.alterdekim.game.entities.Game;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface GamesRepository extends JpaRepository<Game, Long> {
@Query(value = "SELECT COUNT(g) FROM Game g WHERE g.userId = :userId")
Long getGamesCount(@Param("userId") Long userId);
@Query(value = "SELECT COUNT(g) FROM Game g WHERE g.userId = :userId AND g.isWon = true")
Long getWonGamesCount(@Param("userId") Long userId);
}

View File

@ -40,6 +40,7 @@ public class SpringSecurity {
.requestMatchers("/profile/**").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/api/**").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/friends").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/followers").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/settings").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/static/**").permitAll()
.requestMatchers("/access-denied").permitAll()

View File

@ -4,11 +4,13 @@ import com.alterdekim.game.dto.FriendFollowState;
import com.alterdekim.game.entities.FriendStatus;
import com.alterdekim.game.repository.FriendRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
@AllArgsConstructor
public class FriendServiceImpl {
private final FriendRepository repository;
@ -17,6 +19,10 @@ public class FriendServiceImpl {
return repository.getFriendsOfUserId(userId);
}
public List<Long> getFollowersOfUserId(Long userId) {
return repository.getFollowersOfUserId(userId);
}
public void removeFriend(Long userId, Long friendId) {
repository.removeFriend(userId, friendId);
}
@ -39,11 +45,10 @@ public class FriendServiceImpl {
public FriendFollowState getFriendState(Long userId, Long friendId) {
FriendStatus fs = this.getFollow(userId, friendId);
if( fs != null ) {
if( fs.getFirstUserId().longValue() == userId.longValue() ||
this.getFriend(userId, friendId) != null ) {
return FriendFollowState.FOLLOWED;
}
if( (fs != null && fs.getFirstUserId().longValue() == userId.longValue()) ||
this.getFriend(userId, friendId) != null ) {
return FriendFollowState.FOLLOWED;
} else if( (fs != null && fs.getFirstUserId().longValue() != userId.longValue()) ) {
return FriendFollowState.ACCEPT;
}
return FriendFollowState.NOT_FOLLOWED;

View File

@ -0,0 +1,19 @@
package com.alterdekim.game.service;
import com.alterdekim.game.repository.GamesRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class GamesService {
private final GamesRepository repository;
public Long getGamesCount(Long userId) {
return repository.getGamesCount(userId);
}
public Long getWonGamesCount(Long userId) {
return repository.getWonGamesCount(userId);
}
}

View File

@ -8,6 +8,7 @@ public class StringUtil {
for (int i = 0; i < message.length(); i++) {
if (message.charAt(i) == '@') {
int u = message.substring(i).indexOf(' ') + i;
if( u <= (i + 1) || u > message.length() ) return message;
String username = message.substring(i + 1, u);
User user = userService.findByUsername(username);
if (user != null) {

View File

@ -17,4 +17,17 @@ function removeFriend(obj) {
}).done(function() {
$(obj).parent().parent().parent().remove();
});
}
function acceptFriend(obj) {
let friend_id = $(obj).attr("data-friend-id");
$.ajax({
method: "POST",
url: "/api/v1/friends/follow",
data: {
userId: friend_id
}
}).done(function() {
$(obj).parent().parent().parent().remove();
});
}

View File

@ -51,7 +51,14 @@ function joinRoom(obj) {
}
function takeInviteMessage(obj) {
// attr data-roomId
let roomId = $(obj).attr("data-roomId");
$.ajax({
url: "/api/v1/rooms/join/",
data: {
room_id: roomId
},
method: "POST"
});
}
function successPolling(data) {

View File

@ -9,4 +9,17 @@ function followUser(obj) {
}).done(function() {
window.location.reload();
});
}
function unfollowUser(obj) {
let uid = $(obj).attr('data-profile-id');
$.ajax({
method: "POST",
url: "/api/v1/friends/remove/",
data: {
friend_id: uid
}
}).done(function() {
window.location.reload();
});
}

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<th:block th:insert="~{fragments/head}"></th:block>
<link rel="stylesheet" href="/static/css/friends.css"/>
</head>
<body>
<th:block th:insert="~{fragments/navbar}"></th:block>
<div class="container mt-5 block">
<h2><a href="/friends">Friends</a> > Followers</h2>
<div class="friends-list">
<th:block th:each="friend: ${friends}">
<div class="friends-list-one">
<a th:href="${friend.href}">
<div class="friends-list-one-avatar" th:style="${friend.avatar}"></div>
</a>
<div class="friends-list-one-info">
<div class="friends-list-one-info-nick">
<a th:href="${friend.href}" th:text="${friend.displayName}"></a>
</div>
<div class="friends-list-one-info-actions" style="display: none;">
<button onClick="acceptFriend(this)" class="btn btn-primary btn-sm" th:data-friend-id="${friend.id}">Accept</button>
</div>
</div>
</div>
</th:block>
</div>
</div>
<th:block th:insert="~{fragments/essentials}"></th:block>
<script src="/static/javascript/friends.js"></script>
</body>
</html>

View File

@ -8,7 +8,7 @@
<th:block th:insert="~{fragments/navbar}"></th:block>
<div class="container mt-5 block">
<h2>Friends</h2>
<h2>Friends > <a href="/followers">Followers</a></h2>
<div class="friends-list">
<th:block th:each="friend: ${friends}">
<div class="friends-list-one">

View File

@ -43,7 +43,7 @@
</div>
</div>
<div class="block" id="security" style="margin-top: 15px;">
<!-- <div class="block" id="security" style="margin-top: 15px;">
<div class="title">Security</div>
<div class="block-content">
<div class="grid">
@ -69,25 +69,25 @@
</div>
</div>
</div>
</div>
</div>-->
</div>
<div class="g-col-4">
<div class="block" style="margin-top: 15px;">
<div class="title">Settings</div>
<div class="block-content" style="display: block;">
<a href="#profile" class="menu-item _selected">Profile</a>
<a href="#security" class="menu-item">Security</a>
<a href="#privacy" class="menu-item">Privacy</a>
<!--<a href="#security" class="menu-item">Security</a>
<a href="#privacy" class="menu-item">Privacy</a>-->
</div>
</div>
<div class="block" style="margin-top: 15px;">
<!-- <div class="block" style="margin-top: 15px;">
<div class="title">Data</div>
<div class="block-content" style="display: block;">
<a href="#blacklist" class="menu-item">Black list</a>
<a href="#notes" class="menu-item">Notes</a>
</div>
</div>
</div>-->
</div>
</div>
</div>