UI: update Group on User name change

This commit is contained in:
Patrick Haßel 2024-11-06 12:13:13 +01:00
parent e7dba8139b
commit 19fd6cee7b
15 changed files with 183 additions and 86 deletions

View File

@ -23,7 +23,7 @@ export class UserPublic {
return a.name.localeCompare(b.name);
}
equals(user: UserPublic | UserPrivate | null) {
is(user: UserPublic | UserPrivate | null) {
return user !== null && this.publicUuid === user.publicUuid;
}

View File

@ -85,7 +85,7 @@ export class UserService {
}
iOwn(group: Group): boolean {
return group.owner.equals(this.user);
return group.owner.is(this.user);
}
private setUser(user: UserPrivate | null) {
@ -100,7 +100,7 @@ export class UserService {
}
iAm(user: UserPublic | UserPrivate | null) {
return this.user !== null && this.user.equals(user);
return this.user !== null && this.user.is(user);
}
subscribePush<T>(TYPE: Type<T>, next: Next<T>): Subscription {

View File

@ -53,12 +53,16 @@ export class Group extends GroupUuid {
}
isOwnedBy(user: UserPublic | UserPrivate | null) {
return user !== null && user.equals(this.owner);
return user !== null && user.is(this.owner);
}
bannedByName() {
return this.banned.sort(UserPublic.compareName);
}
hasUser(user: UserPublic) {
return this.users.some(u => u.is(user));
}
}

View File

@ -60,7 +60,9 @@
<div class="numbers">
<table>
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.usersByNameOwnerFirst()">
<td (click)="userService.goto(user)">{{ user.name }}</td>
<td (click)="userService.goto(user)">
{{ user.name }}
</td>
<td>
<span class="owner" *ngIf="group.isOwnedBy(user)">
Admin
@ -87,8 +89,10 @@
<div class="tileContent">
<div class="numbers">
<table>
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.bannedByName()" (click)="userService.goto(user)">
<td>{{ user.name }}</td>
<tr [class.user_owner]="group.isOwner(user)" *ngFor="let user of group.bannedByName()">
<td (click)="userService.goto(user)">
{{ user.name }}
</td>
<td>
<div class="buttons">
<ng-container *ngIf="userService.iOwn(group) && !userService.iAm(user)">

View File

@ -14,6 +14,7 @@ import {RelativePipe} from "../../../shared/relative.pipe";
import {Subscription, timer} from "rxjs";
import {GroupDeletedEvent} from "../../../api/group/events/GroupDeletedEvent";
import {GroupLeftEvent} from "../../../api/group/events/GroupLeftEvent";
import {UserPublic} from "../../../api/User/UserPublic";
@Component({
selector: 'app-group',
@ -35,6 +36,8 @@ export class GroupComponent implements OnInit, OnDestroy {
protected readonly subs: Subscription[] = [];
private readonly userSubs: Subscription[] = [];
protected group: Group | null = null;
protected numbersList: Page<Numbers> = Page.EMPTY;
@ -62,7 +65,7 @@ export class GroupComponent implements OnInit, OnDestroy {
}));
this.subs.push(this.userService.subscribePush(Group, group => {
if (group.is(this.group)) {
this.group = group;
this.setGroup(group);
}
}));
this.subs.push(this.userService.subscribePush(GroupDeletedEvent, event => {
@ -83,37 +86,43 @@ export class GroupComponent implements OnInit, OnDestroy {
this.groupService.canAccess(groupUuid, granted => {
this.granted = granted;
if (granted) {
this.groupService.get(groupUuid, group => {
this.group = group;
this.updateNumbersList();
});
this.updateGroup();
}
});
}
}));
}
ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe());
this.userSubs.forEach(sub => sub.unsubscribe());
}
private setGroup(group: Group) {
this.group = group;
this.userSubs.forEach(sub => sub.unsubscribe());
if (this.group !== null) {
this.group.users.forEach(_ => this.userSubs.push(this.userService.subscribePush(UserPublic, u => this.updateUser(u))));
}
}
private updateNumbersList() {
if (this.group) {
this.numbersService.page(this.group.uuid, 0, 10, numbersList => this.numbersList = numbersList);
}
}
ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe());
}
protected changeTitle(group: Group, title: string): void {
this.groupService.changeTitle(group, title, group => this.group = group);
this.groupService.changeTitle(group, title, group => this.setGroup(group));
}
protected changePassword(group: Group, password: string): void {
this.groupService.changePassword(group, password, group => this.group = group);
this.groupService.changePassword(group, password, group => this.setGroup(group));
}
protected join(password: string): void {
if (this.uuid) {
this.groupService.join(this.uuid, password, group => this.group = group);
this.groupService.join(this.uuid, password, group => this.setGroup(group));
}
}
@ -123,4 +132,21 @@ export class GroupComponent implements OnInit, OnDestroy {
}
}
private updateUser(user: UserPublic) {
if (this.group?.hasUser(user)) {
this.updateGroup();
}
}
private updateGroup() {
if (this.uuid === null) {
this.group = null;
return;
}
this.groupService.get(this.uuid, group => {
this.setGroup(group);
this.updateNumbersList();
});
}
}

View File

@ -2,7 +2,7 @@ package de.ph87.tools.group;
import de.ph87.tools.group.uuid.GroupUuid;
import de.ph87.tools.user.User;
import de.ph87.tools.user.UserService;
import de.ph87.tools.user.UserAccessService;
import de.ph87.tools.user.uuid.UserPrivateUuid;
import jakarta.annotation.Nullable;
import lombok.NonNull;
@ -21,10 +21,10 @@ public class GroupAccessService {
private final GroupRepository groupRepository;
private final UserService userService;
private final UserAccessService userAccessService;
public boolean canAccess(@Nullable final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) {
final User user = userService.accessOrNull(userPrivateUuid);
final User user = userAccessService.accessOrNull(userPrivateUuid);
if (user == null) {
return false;
}
@ -34,7 +34,7 @@ public class GroupAccessService {
@NonNull
public GroupAccess access(@NonNull final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) {
final User user = userService.access(userPrivateUuid);
final User user = userAccessService.access(userPrivateUuid);
final Group group = getByUuidOrThrow(groupUuid);
return new GroupAccess(user, group);
}

View File

@ -0,0 +1,38 @@
package de.ph87.tools.group;
import de.ph87.tools.group.dto.GroupDto;
import de.ph87.tools.user.UserPublicDto;
import de.ph87.tools.user.push.UserPushService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class GroupMapper {
private final UserPushService userPushService;
@NonNull
public GroupDto toDto(@NonNull final Group group) {
final UserPublicDto owner = new UserPublicDto(group.getOwner());
final Set<UserPublicDto> users = group.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
final Set<UserPublicDto> banned = group.getBanned().stream().map(UserPublicDto::new).collect(Collectors.toSet());
return new GroupDto(group, owner, users, banned);
}
@NonNull
public GroupDto push(@NonNull final Group group) {
final GroupDto dto = toDto(group);
group.getUsers().forEach(user -> userPushService.push(user, dto));
return dto;
}
}

View File

@ -5,6 +5,7 @@ import de.ph87.tools.group.events.GroupLeftEvent;
import de.ph87.tools.group.requests.GroupJoinRequest;
import de.ph87.tools.group.uuid.GroupUuid;
import de.ph87.tools.user.User;
import de.ph87.tools.user.UserAccessService;
import de.ph87.tools.user.UserService;
import de.ph87.tools.user.push.UserPushService;
import de.ph87.tools.user.uuid.UserPrivateUuid;
@ -32,6 +33,10 @@ public class GroupMemberService {
private final UserPushService userPushService;
private final UserAccessService userAccessService;
private final GroupMapper groupMapper;
@NonNull
public GroupDto join(@Nullable final UserPrivateUuid privateUuid, @NonNull final GroupJoinRequest request, @NonNull final HttpServletResponse response) {
final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response);
@ -48,7 +53,7 @@ public class GroupMemberService {
}
public void leave(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) {
final User user = userService.access(privateUuid);
final User user = userAccessService.access(privateUuid);
final Group group = groupReadService.getGroupByGroupUuid(groupUuid);
if (group.isOwnedBy(user)) {
// owner cannot remove itself from group
@ -63,7 +68,7 @@ public class GroupMemberService {
group.touch();
user.touch();
log.info("User joined Group: user={}, group={}", user, group);
return groupReadService.publish(group);
return groupMapper.push(group);
}
public void _remove_user_from_group_unchecked(@NonNull final Group group, @NonNull final User user, @NonNull final BiConsumer<Group, User> beforePush) {
@ -72,7 +77,7 @@ public class GroupMemberService {
user.touch();
beforePush.accept(group, user);
userPushService.push(user, new GroupLeftEvent(group));
groupReadService.publish(group);
groupMapper.push(group);
}
}

View File

@ -37,14 +37,14 @@ public class GroupOwnerService {
private final UserService userService;
private final GroupReadService groupReadService;
private final GroupMemberService groupMemberService;
private final NumbersRepository numbersRepository;
private final UserPushService userPushService;
private final GroupMapper groupMapper;
@NonNull
public GroupDto create(@Nullable final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) {
final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response);
@ -64,13 +64,13 @@ public class GroupOwnerService {
public void kick(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) {
final OwnerRemoveResult result = _removeUser(privateUuid, request, (group, user) -> log.info("User kicked out of group: user={}, group={}", user, group));
groupReadService.publish(result.group);
groupMapper.push(result.group);
}
public void ban(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) {
final OwnerRemoveResult result = _removeUser(privateUuid, request, (group, user) -> log.info("User banned from group: user={}, group={}", user, group));
result.group.getBanned().add(result.kicked);
groupReadService.publish(result.group);
groupMapper.push(result.group);
}
public void unban(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) {
@ -78,7 +78,7 @@ public class GroupOwnerService {
final User user = access.group.getBanned().stream().filter(u -> u.getPublicUuid().equals(request.userPublicUuid)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
access.group.getBanned().remove(user);
log.info("User unbanned from group: user={}, group={}", user, access.group);
groupReadService.publish(access.group);
groupMapper.push(access.group);
}
@NonNull
@ -88,14 +88,14 @@ public class GroupOwnerService {
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
ug.group.setTitle(request.title);
return groupReadService.publish(ug.group);
return groupMapper.push(ug.group);
}
@NonNull
public GroupDto changePassword(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupChangePasswordRequest request) {
final GroupAccess access = groupAccessService.ownerAccess(privateUuid, request.groupUuid);
access.group.setPassword(request.password);
return groupReadService.publish(access.group);
return groupMapper.push(access.group);
}
/* EXECUTORS ------------------------------------------------------------------------------------ */

View File

@ -3,9 +3,8 @@ package de.ph87.tools.group;
import de.ph87.tools.group.dto.GroupDto;
import de.ph87.tools.group.uuid.GroupUuid;
import de.ph87.tools.user.User;
import de.ph87.tools.user.UserPublicDto;
import de.ph87.tools.user.UserAccessService;
import de.ph87.tools.user.UserService;
import de.ph87.tools.user.push.UserPushService;
import de.ph87.tools.user.uuid.UserPrivateUuid;
import de.ph87.tools.user.uuid.UserPublicUuid;
import lombok.NonNull;
@ -29,14 +28,16 @@ public class GroupReadService {
private final GroupRepository groupRepository;
private final UserPushService userPushService;
private final UserService userService;
private final UserAccessService userAccessService;
private final GroupMapper groupMapper;
@NonNull
public GroupDto get(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) {
final GroupAccess ug = groupAccessService.access(privateUuid, groupUuid);
return toDto(ug.group);
return groupMapper.toDto(ug.group);
}
@NonNull
@ -44,41 +45,22 @@ public class GroupReadService {
return groupRepository.findByUuid(groupUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
}
/* DTO ------------------------------------------------------------------------------------------ */
@NonNull
public GroupDto toDto(@NonNull final Group group) {
final UserPublicDto owner = new UserPublicDto(group.getOwner());
final Set<UserPublicDto> users = group.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
final Set<UserPublicDto> banned = group.getBanned().stream().map(UserPublicDto::new).collect(Collectors.toSet());
return new GroupDto(group, owner, users, banned);
}
/* PUBLISH -------------------------------------------------------------------------------------- */
@NonNull
public GroupDto publish(@NonNull final Group group) {
final GroupDto dto = toDto(group);
group.getUsers().forEach(user -> userPushService.push(user, dto));
return dto;
}
/* FIND & GET ----------------------------------------------------------------------------------- */
@NonNull
public Set<GroupDto> findAllCommonGroups(@NonNull final User a, @NonNull final User b) {
return groupRepository.findAllByUsersContainsAndUsersContains(a, b).stream().map(this::toDto).collect(Collectors.toSet());
return groupRepository.findAllByUsersContainsAndUsersContains(a, b).stream().map(groupMapper::toDto).collect(Collectors.toSet());
}
@NonNull
public Set<GroupDto> findAllJoined(@NonNull final UserPrivateUuid privateUuid) {
final User principal = userService.access(privateUuid);
return groupRepository.findAllByUsersContains(principal).stream().map(this::toDto).collect(Collectors.toSet());
final User principal = userAccessService.access(privateUuid);
return groupRepository.findAllByUsersContains(principal).stream().map(groupMapper::toDto).collect(Collectors.toSet());
}
@NonNull
public Set<GroupDto> findAllCommon(@NonNull final UserPrivateUuid privateUuid, @NonNull final UserPublicUuid targetUuid) {
final User principal = userService.access(privateUuid);
final User principal = userAccessService.access(privateUuid);
final User target = userService.getByPublicUuid(targetUuid);
return findAllCommonGroups(principal, target);
}

View File

@ -26,6 +26,7 @@ public class Numbers extends NumbersAbstract {
private String uuid = UUID.randomUUID().toString();
@Transient
@ToString.Exclude
private NumbersUuid _uuid;
@NonNull

View File

@ -2,12 +2,12 @@ package de.ph87.tools.tools.numbers;
import de.ph87.tools.group.GroupAccess;
import de.ph87.tools.group.GroupAccessService;
import de.ph87.tools.group.GroupMapper;
import de.ph87.tools.group.dto.GroupDto;
import de.ph87.tools.group.GroupReadService;
import de.ph87.tools.group.uuid.GroupUuid;
import de.ph87.tools.tools.numbers.uuid.NumbersUuid;
import de.ph87.tools.user.User;
import de.ph87.tools.user.UserService;
import de.ph87.tools.user.UserAccessService;
import de.ph87.tools.user.push.UserPushService;
import de.ph87.tools.user.reference.UserReference;
import de.ph87.tools.user.reference.UserReferenceService;
@ -41,11 +41,11 @@ public class NumbersService {
private final UserReferenceService userReferenceService;
private final GroupReadService groupReadService;
private final UserPushService userPushService;
private final UserService userService;
private final UserAccessService userAccessService;
private final GroupMapper groupMapper;
public void create(@NonNull final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) {
final GroupAccess access = groupAccessService.ownerAccess(userPrivateUuid, groupUuid);
@ -71,13 +71,13 @@ public class NumbersService {
@NonNull
public NumbersDto toDto(@NonNull final Numbers numbers, @NonNull final User user) {
final GroupDto group = groupReadService.toDto(numbers.getGroup());
final GroupDto group = groupMapper.toDto(numbers.getGroup());
final Integer number = numbers.getNumberForUser(user);
return new NumbersDto(numbers, group, number);
}
public boolean canAccess(@NonNull final UserPrivateUuid privateUuid, @NonNull final NumbersUuid numbersUuid) {
final User user = userService.access(privateUuid);
final User user = userAccessService.access(privateUuid);
final Numbers numbers = numbersRepository.findById(numbersUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
return numbers.containsUser(user);
}
@ -98,7 +98,7 @@ public class NumbersService {
@NonNull
private NumbersAccess access(@NonNull final UserPrivateUuid privateUuid, @NonNull final NumbersUuid numbersUuid) {
final User user = userService.access(privateUuid);
final User user = userAccessService.access(privateUuid);
final Numbers numbers = numbersRepository.findById(numbersUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
if (!numbers.containsUser(user)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);

View File

@ -23,6 +23,7 @@ public class User extends UserPublicAbstract {
private String publicUuid = UUID.randomUUID().toString();
@Transient
@ToString.Exclude
private UserPublicUuid _publicUuid;
@NonNull
@ -39,6 +40,7 @@ public class User extends UserPublicAbstract {
private String privateUuid = UUID.randomUUID().toString();
@Transient
@ToString.Exclude
private UserPrivateUuid _privateUuid;
@NonNull

View File

@ -0,0 +1,36 @@
package de.ph87.tools.user;
import de.ph87.tools.user.uuid.UserPrivateUuid;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@Slf4j
@Service
@Transactional
@EnableScheduling
@RequiredArgsConstructor
public class UserAccessService {
private final UserRepository userRepository;
@Nullable
public User accessOrNull(@Nullable final UserPrivateUuid userPrivateUuid) {
if (userPrivateUuid == null) {
return null;
}
return access(userPrivateUuid);
}
@NonNull
public User access(@NonNull final UserPrivateUuid privateUuid) {
return userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
}
}

View File

@ -1,6 +1,9 @@
package de.ph87.tools.user;
import de.ph87.tools.common.uuid.AbstractUuid;
import de.ph87.tools.group.GroupMapper;
import de.ph87.tools.group.GroupRepository;
import de.ph87.tools.user.push.UserPushService;
import de.ph87.tools.user.requests.UserLoginRequest;
import de.ph87.tools.user.uuid.UserPrivateUuid;
import de.ph87.tools.user.uuid.UserPublicUuid;
@ -45,6 +48,14 @@ public class UserService {
private final PasswordEncoder passwordEncoder;
private final GroupRepository groupRepository;
private final UserAccessService userAccessService;
private final GroupMapper groupMapper;
private final UserPushService userPushService;
@NonNull
public UserPrivateDto login(@NonNull final UserLoginRequest loginRequest, @NonNull final HttpServletResponse response) {
final User user = userRepository.findByName(loginRequest.name).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
@ -100,6 +111,7 @@ public class UserService {
}
user.setName(name);
log.info("User name changed: user={}", user);
groupRepository.findAllByUsersContains(user).forEach(groupMapper::push);
});
}
@ -147,9 +159,11 @@ public class UserService {
@NonNull
private UserPrivateDto modify(@NonNull final UserPrivateUuid privateUuid, @NonNull Consumer<User> modifier) {
final User user = access(privateUuid);
final User user = userAccessService.access(privateUuid);
modifier.accept(user);
return new UserPrivateDto(user);
final UserPrivateDto privateDto = new UserPrivateDto(user);
userPushService.push(user, privateDto);
return privateDto;
}
private void deleteUnchecked(final HttpServletResponse response, final User user) {
@ -158,16 +172,6 @@ public class UserService {
writeUserUuidCookie(response, null);
}
/* ACCESS --------------------------------------------------------------------------------------- */
@Nullable
public User accessOrNull(@Nullable final UserPrivateUuid userPrivateUuid) {
if (userPrivateUuid == null) {
return null;
}
return access(userPrivateUuid);
}
/* GETTERS & FINDERS ---------------------------------------------------------------------------- */
@NonNull
@ -175,11 +179,6 @@ public class UserService {
return new UserPublicDto(getByPublicUuid(publicUuid));
}
@NonNull
public User access(@NonNull final UserPrivateUuid privateUuid) {
return userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
}
@NonNull
public User getByPublicUuid(@NonNull final UserPublicUuid publicUuid) {
return userRepository.findByPublicUuid(publicUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));