Refactor user name display logic: integrate MiniMessageComponent in UserListComponent and transition to ProcessedMiniMessageNode. Add server state caching in ChatService with eager state dispatch on subscription.
This commit is contained in:
parent
8c02543bca
commit
1a0bc902ac
|
|
@ -37,6 +37,7 @@ public class ChatService {
|
||||||
private final EventPublisher eventPublisher;
|
private final EventPublisher eventPublisher;
|
||||||
private final NavigableMap<Instant, ChatMessage> chatMessages = new TreeMap<>();
|
private final NavigableMap<Instant, ChatMessage> chatMessages = new TreeMap<>();
|
||||||
private final Map<String, EventUser> eventUserMap = new HashMap<>();
|
private final Map<String, EventUser> eventUserMap = new HashMap<>();
|
||||||
|
private final Map<String, ServerDto> serverStateCache = new HashMap<>();
|
||||||
|
|
||||||
@Value("${chat.allowed-servers}")
|
@Value("${chat.allowed-servers}")
|
||||||
private String[] allowedServers;
|
private String[] allowedServers;
|
||||||
|
|
@ -73,7 +74,9 @@ public class ChatService {
|
||||||
public SseEmitter subscribe(EventUser eventUser, String json) {
|
public SseEmitter subscribe(EventUser eventUser, String json) {
|
||||||
String key = eventUser.uuid().toString();
|
String key = eventUser.uuid().toString();
|
||||||
eventUserMap.put(key, eventUser);
|
eventUserMap.put(key, eventUser);
|
||||||
return eventPublisher.subscribe(key, json, this::handleSessionEnd);
|
SseEmitter emitter = eventPublisher.subscribe(key, json, this::handleSessionEnd);
|
||||||
|
sendServerStateToUser(key, eventUser);
|
||||||
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSessionEnd(String key, Instant sessionStart, Instant sessionEnd) {
|
private void handleSessionEnd(String key, Instant sessionStart, Instant sessionEnd) {
|
||||||
|
|
@ -124,18 +127,26 @@ public class ChatService {
|
||||||
sendMessagesToListeners(chatMessageList);
|
sendMessagesToListeners(chatMessageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateServerState(ServerStateDto serverStateDto) {
|
public synchronized void updateServerState(ServerStateDto serverStateDto) {
|
||||||
sendToUsers("server-state", (eventUser) -> {
|
serverStateDto.getServers().forEach(serverDto -> serverStateCache.put(serverDto.getName(), serverDto));
|
||||||
if (eventUser.hasPermission(PermissionClaimDto.HEAD_MOD)) {
|
sendToUsers("server-state", this::getServerStateJson);
|
||||||
return ServerStateMapper.toJson(serverStateDto);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
List<ServerDto> filteredServers = serverStateDto.getServers().stream()
|
private String getServerStateJson(EventUser eventUser) {
|
||||||
.filter(serverDto -> Arrays.asList(allowedServers).contains(serverDto.getName()))
|
if (eventUser.hasPermission(PermissionClaimDto.HEAD_MOD)) {
|
||||||
.toList();
|
return ServerStateMapper.toJson(new ServerStateDto().servers(serverStateCache.values().stream().toList()));
|
||||||
|
}
|
||||||
|
|
||||||
return ServerStateMapper.toJson(new ServerStateDto().servers(filteredServers));
|
List<ServerDto> filteredServers = serverStateCache.values().stream()
|
||||||
});
|
.filter(serverDto -> Arrays.asList(allowedServers).contains(serverDto.getName()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return ServerStateMapper.toJson(new ServerStateDto().servers(filteredServers));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendServerStateToUser(String key, EventUser eventUser) {
|
||||||
|
String json = getServerStateJson(eventUser);
|
||||||
|
eventPublisher.sendToUser(key, "server-state", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessagesToListeners(List<ChatMessage> chatMessageList) {
|
private void sendMessagesToListeners(List<ChatMessage> chatMessageList) {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@
|
||||||
@for (player of server.players; track player.uuid) {
|
@for (player of server.players; track player.uuid) {
|
||||||
<div class="user indented">
|
<div class="user indented">
|
||||||
<span class="user-name">
|
<span class="user-name">
|
||||||
@for (segment of getDisplayName(player); track $index) {
|
<mini-message [node]="getDisplayName(player)"></mini-message>
|
||||||
<span [style.color]="segment.color">{{ segment.text }}</span>
|
|
||||||
}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import {Component, computed, inject} from '@angular/core';
|
import {Component, computed, inject} from '@angular/core';
|
||||||
import {ChatService} from '@pages/altitude/chat/service/chat.service';
|
import {ChatService} from '@pages/altitude/chat/service/chat.service';
|
||||||
import {NameFormatService} from '@pages/altitude/chat/service/name-format.service';
|
import {NameFormatService} from '@pages/altitude/chat/service/name-format.service';
|
||||||
import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object';
|
|
||||||
import {User} from '@pages/altitude/chat/objects/server-state.object';
|
import {User} from '@pages/altitude/chat/objects/server-state.object';
|
||||||
|
import {ProcessedMiniMessageNode} from '@pages/altitude/chat/mini-message/mini-message.types';
|
||||||
|
import {MiniMessageComponent} from '@pages/altitude/chat/components/mini-message/mini-message.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-list',
|
selector: 'app-user-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [MiniMessageComponent],
|
||||||
templateUrl: './user-list.component.html',
|
templateUrl: './user-list.component.html',
|
||||||
styleUrl: './user-list.component.scss'
|
styleUrl: './user-list.component.scss'
|
||||||
})
|
})
|
||||||
|
|
@ -29,7 +30,7 @@ export class UserListComponent {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
protected getDisplayName(user: User): ColoredNameSegment[] {
|
protected getDisplayName(user: User): ProcessedMiniMessageNode {
|
||||||
return this.nameFormatService.parseColoredName(user.styledName || user.name);
|
return this.nameFormatService.getUserDisplayName(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ import {inject, Injectable} from '@angular/core';
|
||||||
import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object';
|
import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object';
|
||||||
import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object';
|
import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object';
|
||||||
import {ChatService} from "./chat.service";
|
import {ChatService} from "./chat.service";
|
||||||
|
import {User} from '@pages/altitude/chat/objects/server-state.object';
|
||||||
|
import {normalizeComponent} from '@pages/altitude/chat/mini-message/normalize.util';
|
||||||
|
import {precomputeNode} from '@pages/altitude/chat/mini-message/mini-message.util';
|
||||||
|
import {ProcessedMiniMessageNode} from '@pages/altitude/chat/mini-message/mini-message.types';
|
||||||
|
|
||||||
@Injectable({providedIn: 'root'})
|
@Injectable({providedIn: 'root'})
|
||||||
export class NameFormatService {
|
export class NameFormatService {
|
||||||
|
|
@ -12,6 +16,18 @@ export class NameFormatService {
|
||||||
return this.parseColoredName(this.getNameFromType(channel));
|
return this.parseColoredName(this.getNameFromType(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getUserDisplayName(user: User): ProcessedMiniMessageNode {
|
||||||
|
if (user.styledName) {
|
||||||
|
try {
|
||||||
|
return precomputeNode(normalizeComponent(JSON.parse(user.styledName)));
|
||||||
|
} catch {
|
||||||
|
return precomputeNode(normalizeComponent({text: user.name}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return precomputeNode(normalizeComponent({text: user.name}));
|
||||||
|
}
|
||||||
|
|
||||||
private readonly LEGACY_COLOR_MAP: Record<string, string> = {
|
private readonly LEGACY_COLOR_MAP: Record<string, string> = {
|
||||||
'4': '#AA0000',
|
'4': '#AA0000',
|
||||||
'c': '#FF5555',
|
'c': '#FF5555',
|
||||||
|
|
@ -44,7 +60,7 @@ export class NameFormatService {
|
||||||
return channel.name;
|
return channel.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseColoredName(name: string): ColoredNameSegment[] {
|
private parseColoredName(name: string): ColoredNameSegment[] {
|
||||||
const markerRegex = /\{#([0-9a-fA-F]{6})(<>|[<>]?)\}|&([0-9a-fA-F])/g;
|
const markerRegex = /\{#([0-9a-fA-F]{6})(<>|[<>]?)\}|&([0-9a-fA-F])/g;
|
||||||
const segments: ColoredNameSegment[] = [];
|
const segments: ColoredNameSegment[] = [];
|
||||||
let currentColor: string | undefined;
|
let currentColor: string | undefined;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user