include full appmetadata object in user status broadcast

This commit is contained in:
2026-02-04 19:11:30 +08:00
parent 8a7c5568c5
commit 7191035748
3 changed files with 82 additions and 10 deletions

View File

@@ -0,0 +1,51 @@
import {
IsOptional,
IsString,
MaxLength,
ValidateBy,
ValidationOptions,
} from 'class-validator';
/**
* Custom validator to ensure at least one of localized or unlocalized is a non-empty string
*/
function IsAtLeastOneNameProvided(validationOptions?: ValidationOptions) {
return ValidateBy(
{
name: 'isAtLeastOneNameProvided',
validator: {
validate(value, args) {
const object = args?.object as AppMetadataDto;
const hasLocalized =
typeof object.localized === 'string' &&
object.localized.trim().length > 0;
const hasUnlocalized =
typeof object.unlocalized === 'string' &&
object.unlocalized.trim().length > 0;
return hasLocalized || hasUnlocalized;
},
defaultMessage() {
return 'At least one of localized or unlocalized must be a non-empty string';
},
},
},
validationOptions,
);
}
export class AppMetadataDto {
@IsOptional()
@IsString()
@MaxLength(200)
@IsAtLeastOneNameProvided()
localized: string | null;
@IsOptional()
@IsString()
@MaxLength(200)
unlocalized: string | null;
@IsOptional()
@IsString()
appIconB64: string | null;
}

View File

@@ -1,4 +1,6 @@
import { IsEnum, IsNotEmpty, IsString, MaxLength } from 'class-validator'; import { IsEnum, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { AppMetadataDto } from './app-metadata.dto';
export enum UserState { export enum UserState {
IDLE = 'idle', IDLE = 'idle',
@@ -6,10 +8,9 @@ export enum UserState {
} }
export class UserStatusDto { export class UserStatusDto {
@IsString() @ValidateNested()
@IsNotEmpty() @Type(() => AppMetadataDto)
@MaxLength(100) appMetadata: AppMetadataDto;
activeApp: string;
@IsEnum(UserState) @IsEnum(UserState)
state: UserState; state: UserState;

View File

@@ -495,7 +495,11 @@ describe('StateGateway', () => {
]); ]);
const data: UserStatusDto = { const data: UserStatusDto = {
activeApp: 'VS Code', appMetadata: {
localized: null,
unlocalized: 'VS Code',
appIconB64: null,
},
state: UserState.IDLE, state: UserState.IDLE,
}; };
@@ -527,7 +531,11 @@ describe('StateGateway', () => {
}; };
const data: UserStatusDto = { const data: UserStatusDto = {
activeApp: 'VS Code', appMetadata: {
localized: null,
unlocalized: 'VS Code',
appIconB64: null,
},
state: UserState.IDLE, state: UserState.IDLE,
}; };
@@ -550,7 +558,11 @@ describe('StateGateway', () => {
}; };
const data: UserStatusDto = { const data: UserStatusDto = {
activeApp: 'VS Code', appMetadata: {
localized: null,
unlocalized: 'VS Code',
appIconB64: null,
},
state: UserState.IDLE, state: UserState.IDLE,
}; };
@@ -569,7 +581,11 @@ describe('StateGateway', () => {
data: {}, data: {},
}; };
const data: UserStatusDto = { const data: UserStatusDto = {
activeApp: 'VS Code', appMetadata: {
localized: null,
unlocalized: 'VS Code',
appIconB64: null,
},
state: UserState.IDLE, state: UserState.IDLE,
}; };
@@ -598,7 +614,11 @@ describe('StateGateway', () => {
]); ]);
const data: UserStatusDto = { const data: UserStatusDto = {
activeApp: 'VS Code', appMetadata: {
localized: null,
unlocalized: 'VS Code',
appIconB64: null,
},
state: UserState.IDLE, state: UserState.IDLE,
}; };