Add gradient-colored display names in ChannelListComponent: implemented name parsing with gradient support, refactored UI logic to dynamically render colored name segments, and improved message handling in ChatService with null channel filtering.
This commit is contained in:
parent
8f9aea2e0f
commit
d1acf39a22
|
|
@ -4,13 +4,11 @@
|
||||||
<div class="channel"
|
<div class="channel"
|
||||||
[class.selected]="selectedChannel()?.name === channel.name && selectedChannel()?.type === channel.type"
|
[class.selected]="selectedChannel()?.name === channel.name && selectedChannel()?.type === channel.type"
|
||||||
(click)="selectedChannelChange.emit(channel)">
|
(click)="selectedChannelChange.emit(channel)">
|
||||||
@if (channel.type === 'PARTY') {
|
<span class="channel-name">
|
||||||
<span class="channel-name">{{ chatService.partieMap().get(channel.name) ?? channel.name }}</span>
|
@for (segment of parseColoredName(getDisplayName(channel)); track $index) {
|
||||||
} @else if (channel.type === 'DM') {
|
<span [style.color]="segment.color">{{ segment.text }}</span>
|
||||||
<span class="channel-name">{{ chatService.userNameMap().get(channel.name) ?? channel.name }}</span>
|
|
||||||
} @else {
|
|
||||||
<span class="channel-name">{{ channel.name }}</span>
|
|
||||||
}
|
}
|
||||||
|
</span>
|
||||||
@if (channel.unreadMessages > 0) {
|
@if (channel.unreadMessages > 0) {
|
||||||
<div class="notification">
|
<div class="notification">
|
||||||
<svg-circle></svg-circle>
|
<svg-circle></svg-circle>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object';
|
||||||
import {CircleSvgComponent} from '@pages/altitude/chat/svg/circle.svg';
|
import {CircleSvgComponent} from '@pages/altitude/chat/svg/circle.svg';
|
||||||
import {ChatService} from '@pages/altitude/chat/service/chat.service';
|
import {ChatService} from '@pages/altitude/chat/service/chat.service';
|
||||||
|
|
||||||
|
interface ColoredNameSegment {
|
||||||
|
text: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-channel-list',
|
selector: 'app-channel-list',
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -26,4 +31,114 @@ export class ChannelListComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getDisplayName(channel: ChatChannel): string {
|
||||||
|
if (channel.type === 'PARTY') {
|
||||||
|
return this.chatService.partieMap().get(channel.name) ?? channel.name;
|
||||||
|
}
|
||||||
|
if (channel.type === 'DM') {
|
||||||
|
return this.chatService.userNameMap().get(channel.name) ?? channel.name;
|
||||||
|
}
|
||||||
|
return channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected parseColoredName(name: string): ColoredNameSegment[] {
|
||||||
|
const markerRegex = /\{#([0-9a-fA-F]{6})([<>]?)}/g;
|
||||||
|
const segments: ColoredNameSegment[] = [];
|
||||||
|
let currentColor: string | undefined;
|
||||||
|
let gradientStartColor: string | undefined;
|
||||||
|
let gradientStartSegmentIndex: number | undefined;
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
|
||||||
|
while ((match = markerRegex.exec(name)) !== null) {
|
||||||
|
this.addColoredTextSegment(segments, name.slice(lastIndex, match.index), currentColor);
|
||||||
|
|
||||||
|
const markerColor = `#${match[1]}`;
|
||||||
|
const markerType = match[2];
|
||||||
|
|
||||||
|
if (markerType === '>') {
|
||||||
|
currentColor = markerColor;
|
||||||
|
gradientStartColor = markerColor;
|
||||||
|
gradientStartSegmentIndex = segments.length;
|
||||||
|
} else if (markerType === '<') {
|
||||||
|
if (gradientStartColor !== undefined && gradientStartSegmentIndex !== undefined) {
|
||||||
|
this.applyGradient(segments, gradientStartSegmentIndex, gradientStartColor, markerColor);
|
||||||
|
}
|
||||||
|
currentColor = markerColor;
|
||||||
|
gradientStartColor = undefined;
|
||||||
|
gradientStartSegmentIndex = undefined;
|
||||||
|
} else {
|
||||||
|
currentColor = markerColor;
|
||||||
|
gradientStartColor = undefined;
|
||||||
|
gradientStartSegmentIndex = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = markerRegex.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addColoredTextSegment(segments, name.slice(lastIndex), currentColor);
|
||||||
|
|
||||||
|
return segments.length > 0 ? segments : [{text: name}];
|
||||||
|
}
|
||||||
|
|
||||||
|
private addColoredTextSegment(segments: ColoredNameSegment[], text: string, color?: string): void {
|
||||||
|
if (text.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.push({text, color});
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyGradient(
|
||||||
|
segments: ColoredNameSegment[],
|
||||||
|
startSegmentIndex: number,
|
||||||
|
startColor: string,
|
||||||
|
endColor: string
|
||||||
|
): void {
|
||||||
|
const text = segments.slice(startSegmentIndex).map((segment) => segment.text).join('');
|
||||||
|
const length = text.length;
|
||||||
|
const start = this.hexToRgb(startColor);
|
||||||
|
const end = this.hexToRgb(endColor);
|
||||||
|
|
||||||
|
if (length === 0 || start === null || end === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gradientSegments: ColoredNameSegment[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const ratio = length === 1 ? 0 : i / (length - 1);
|
||||||
|
gradientSegments.push({
|
||||||
|
text: text[i],
|
||||||
|
color: this.rgbToHex(
|
||||||
|
start.r + (end.r - start.r) * ratio,
|
||||||
|
start.g + (end.g - start.g) * ratio,
|
||||||
|
start.b + (end.b - start.b) * ratio
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.splice(startSegmentIndex, segments.length - startSegmentIndex, ...gradientSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||||||
|
const match = /^#?([0-9a-fA-F]{6})$/.exec(hex);
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = Number.parseInt(match[1], 16);
|
||||||
|
return {
|
||||||
|
r: (value >> 16) & 255,
|
||||||
|
g: (value >> 8) & 255,
|
||||||
|
b: value & 255
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private rgbToHex(r: number, g: number, b: number): string {
|
||||||
|
return `#${[r, g, b]
|
||||||
|
.map((value) => Math.round(value).toString(16).padStart(2, '0'))
|
||||||
|
.join('')}`;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,10 +90,13 @@ export class ChatService implements OnDestroy {
|
||||||
messageJson: normalizeComponent(JSON.parse(m.messageJson)),
|
messageJson: normalizeComponent(JSON.parse(m.messageJson)),
|
||||||
};
|
};
|
||||||
const channelKey = this.getChannelKey(message);
|
const channelKey = this.getChannelKey(message);
|
||||||
|
if (channelKey === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
message.channelName = channelKey.name;
|
message.channelName = channelKey.name;
|
||||||
message.channelType = channelKey.type;
|
message.channelType = channelKey.type;
|
||||||
return message;
|
return message;
|
||||||
});
|
}).filter((message) => message !== null) as ChatMessage[];
|
||||||
this._messages.update((old) => [...old, ...messages]);
|
this._messages.update((old) => [...old, ...messages]);
|
||||||
|
|
||||||
messages.forEach((message) => {
|
messages.forEach((message) => {
|
||||||
|
|
@ -110,7 +113,7 @@ export class ChatService implements OnDestroy {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getChannelKey(message: ChatMessage): { name: string, type: ChatChannel['type'] } {
|
private getChannelKey(message: ChatMessage): { name: string, type: ChatChannel['type'] } | null {
|
||||||
if (message.type === 'GAC') {
|
if (message.type === 'GAC') {
|
||||||
return {name: 'Global Admin Chat', type: 'GAC'};
|
return {name: 'Global Admin Chat', type: 'GAC'};
|
||||||
}
|
}
|
||||||
|
|
@ -124,12 +127,11 @@ export class ChatService implements OnDestroy {
|
||||||
if (message.type === 'MSG') {
|
if (message.type === 'MSG') {
|
||||||
const uuid = this.authService.getUuid();
|
const uuid = this.authService.getUuid();
|
||||||
if (message.uuid === uuid) {
|
if (message.uuid === uuid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
this.updateUserNameMap(message.receiver)
|
this.updateUserNameMap(message.receiver)
|
||||||
return {name: message.receiver, type: 'DM'};
|
return {name: message.receiver, type: 'DM'};
|
||||||
}
|
}
|
||||||
this.updateUserNameMap(message.uuid)
|
|
||||||
return {name: message.uuid, type: 'DM'};
|
|
||||||
}
|
|
||||||
return {name: message.server, type: 'SERVER'};
|
return {name: message.server, type: 'SERVER'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user