Implement portrait prompt API and UI enhancements. Added a new endpoint to fetch portrait prompts, updated the client to handle prompt display, and improved localization for prompt-related text. Enhanced styling for prompt elements in the UI.
This commit is contained in:
@@ -50,6 +50,38 @@ internal sealed class PortraitService(
|
||||
return outcome.Error;
|
||||
}
|
||||
|
||||
public async Task<PortraitPromptBuildResult> BuildPromptAsync(
|
||||
int schoolId,
|
||||
string personId,
|
||||
PortraitKind kind,
|
||||
string? promptExtra,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var resolved = ResolveCustomPromptExtra(schoolId, personId, kind, promptExtra);
|
||||
if (kind == PortraitKind.Custom && resolved is null)
|
||||
{
|
||||
return PortraitPromptBuildResult.InvalidPrompt;
|
||||
}
|
||||
|
||||
var outcome = await LookupPersonAsync(schoolId, personId, cancellationToken);
|
||||
if (outcome.Error == PersonLookupError.UnknownPerson)
|
||||
{
|
||||
return PortraitPromptBuildResult.UnknownPerson;
|
||||
}
|
||||
|
||||
if (outcome.Error != PersonLookupError.None || outcome.Card is null)
|
||||
{
|
||||
return PortraitPromptBuildResult.UnknownSchool;
|
||||
}
|
||||
|
||||
var (positive, negative) = PortraitPromptBuilder.Build(outcome.Card, settings, kind, resolved);
|
||||
return PortraitPromptBuildResult.Succeeded(
|
||||
kind,
|
||||
positive,
|
||||
negative,
|
||||
kind == PortraitKind.Custom ? resolved : null);
|
||||
}
|
||||
|
||||
public async Task<PortraitGenerationResult> GenerateAsync(
|
||||
int schoolId,
|
||||
string personId,
|
||||
@@ -76,14 +108,7 @@ internal sealed class PortraitService(
|
||||
}
|
||||
}
|
||||
|
||||
var command = new GameCommand.GetPerson(
|
||||
schoolId,
|
||||
personId,
|
||||
PromptLocale,
|
||||
NewCompletion<PersonCardResult>());
|
||||
commands.Enqueue(command);
|
||||
|
||||
var outcome = await command.Result.Task.WaitAsync(PersonLookupTimeout, cancellationToken);
|
||||
var outcome = await LookupPersonAsync(schoolId, personId, cancellationToken);
|
||||
if (outcome.Error == PersonLookupError.UnknownPerson)
|
||||
{
|
||||
return PortraitGenerationResult.UnknownPerson;
|
||||
@@ -126,10 +151,83 @@ internal sealed class PortraitService(
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<PersonCardResult> LookupPersonAsync(
|
||||
int schoolId,
|
||||
string personId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = new GameCommand.GetPerson(
|
||||
schoolId,
|
||||
personId,
|
||||
PromptLocale,
|
||||
NewCompletion<PersonCardResult>());
|
||||
commands.Enqueue(command);
|
||||
return await command.Result.Task.WaitAsync(PersonLookupTimeout, cancellationToken);
|
||||
}
|
||||
|
||||
private string? ResolveCustomPromptExtra(int schoolId, string personId, PortraitKind kind, string? promptExtra)
|
||||
{
|
||||
if (kind != PortraitKind.Custom)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
promptExtra = promptExtra?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(promptExtra))
|
||||
{
|
||||
promptExtra = store.TryReadCustomPortraitPrompt(schoolId, personId)?.Trim();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(promptExtra) || promptExtra.Length > MaxCustomPromptLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return promptExtra;
|
||||
}
|
||||
|
||||
private static TaskCompletionSource<T> NewCompletion<T>() =>
|
||||
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
}
|
||||
|
||||
internal enum PortraitPromptBuildOutcome
|
||||
{
|
||||
Succeeded,
|
||||
UnknownSchool,
|
||||
UnknownPerson,
|
||||
InvalidPrompt,
|
||||
}
|
||||
|
||||
internal sealed record PortraitPromptBuildResult(
|
||||
PortraitPromptBuildOutcome Outcome,
|
||||
PortraitKind Kind,
|
||||
string Positive,
|
||||
string Negative,
|
||||
string? PromptExtra)
|
||||
{
|
||||
public static PortraitPromptBuildResult UnknownSchool { get; } =
|
||||
new(PortraitPromptBuildOutcome.UnknownSchool, default, string.Empty, string.Empty, null);
|
||||
|
||||
public static PortraitPromptBuildResult UnknownPerson { get; } =
|
||||
new(PortraitPromptBuildOutcome.UnknownPerson, default, string.Empty, string.Empty, null);
|
||||
|
||||
public static PortraitPromptBuildResult InvalidPrompt { get; } =
|
||||
new(PortraitPromptBuildOutcome.InvalidPrompt, default, string.Empty, string.Empty, null);
|
||||
|
||||
public static PortraitPromptBuildResult Succeeded(
|
||||
PortraitKind kind,
|
||||
string positive,
|
||||
string negative,
|
||||
string? promptExtra) =>
|
||||
new(PortraitPromptBuildOutcome.Succeeded, kind, positive, negative, promptExtra);
|
||||
}
|
||||
|
||||
internal sealed record PortraitPromptResponse(
|
||||
string Kind,
|
||||
string Positive,
|
||||
string Negative,
|
||||
string? PromptExtra);
|
||||
|
||||
internal enum PortraitGenerationOutcome
|
||||
{
|
||||
Succeeded,
|
||||
|
||||
Reference in New Issue
Block a user