The composer says which language a post is in
A post went out in the reader's own interface language whatever it was written in. The composer now has a language picker (each language named in itself), starting at the persona's default from Settings, else the interface's; a reply starts in its thread's language. The e2e poll test picks Italian and finds it on PrivaPub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
d7d836ba39
commit
61631bb0a7
4 files changed
+29
-4
No files matched your search
@@ -129,6 +129,17 @@
|
|||||||
</DDropdown>
|
</DDropdown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!form.IsLocated && GroupId is null && Editing is null)
|
||||||
|
{
|
||||||
|
@* the language the post is written in, as each language names itself *@
|
||||||
|
<DSelect TValue="string" Size="DSize.Sm" class="w-auto" @bind-Value="form.Language" title="@Localizer["Language"]" data-compose-language>
|
||||||
|
@foreach (var (languageCode, languageName) in Languages.All)
|
||||||
|
{
|
||||||
|
<option value="@languageCode">@languageName</option>
|
||||||
|
}
|
||||||
|
</DSelect>
|
||||||
|
}
|
||||||
|
|
||||||
<DButton Size="DSize.Sm" Active="form.HasContentWarning" OnClick="() => form.HasContentWarning = !form.HasContentWarning" title="@Localizer["Content warning"]">
|
<DButton Size="DSize.Sm" Active="form.HasContentWarning" OnClick="() => form.HasContentWarning = !form.HasContentWarning" title="@Localizer["Content warning"]">
|
||||||
<i class="ion-md-warning text-base"></i>
|
<i class="ion-md-warning text-base"></i>
|
||||||
</DButton>
|
</DButton>
|
||||||
@@ -258,6 +269,10 @@
|
|||||||
_ => default
|
_ => default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// a new post starts in the language the persona chose in Settings, else in the reader's
|
||||||
|
string DefaultLanguage => CascadingState.Persona?.Source?.Language is { Length: > 0 } chosen ? chosen
|
||||||
|
: System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
|
||||||
|
|
||||||
// a new post starts with the visibility the persona chose in Settings
|
// a new post starts with the visibility the persona chose in Settings
|
||||||
MessageType DefaultVisibility => CascadingState.Persona?.Source?.Privacy switch
|
MessageType DefaultVisibility => CascadingState.Persona?.Source?.Privacy switch
|
||||||
{
|
{
|
||||||
@@ -270,6 +285,7 @@
|
|||||||
{
|
{
|
||||||
form.GroupId = GroupId;
|
form.GroupId = GroupId;
|
||||||
form.MessageType = DefaultVisibility;
|
form.MessageType = DefaultVisibility;
|
||||||
|
form.Language = DefaultLanguage;
|
||||||
form.QuotedStatusId = Quoting?.Id;
|
form.QuotedStatusId = Quoting?.Id;
|
||||||
if (Editing is not null)
|
if (Editing is not null)
|
||||||
{
|
{
|
||||||
@@ -290,6 +306,8 @@
|
|||||||
"unlisted" => MessageType.Unlisted,
|
"unlisted" => MessageType.Unlisted,
|
||||||
_ => MessageType.Public
|
_ => MessageType.Public
|
||||||
};
|
};
|
||||||
|
// a reply is written in its thread's language, as a rule
|
||||||
|
form.Language = InReplyTo.Language is { Length: 2 } language && Languages.All.Any(l => l.Code == language) ? language : form.Language;
|
||||||
form.HasContentWarning = InReplyTo.SpoilerText is { Length: > 0 };
|
form.HasContentWarning = InReplyTo.SpoilerText is { Length: > 0 };
|
||||||
form.SpoilerText = InReplyTo.SpoilerText;
|
form.SpoilerText = InReplyTo.SpoilerText;
|
||||||
var mentions = InReplyTo.Mentions.Select(m => m.Acct).Prepend(InReplyTo.Account?.Acct)
|
var mentions = InReplyTo.Mentions.Select(m => m.Acct).Prepend(InReplyTo.Account?.Acct)
|
||||||
@@ -430,14 +448,14 @@
|
|||||||
if (result.Data is ScheduledStatus scheduled)
|
if (result.Data is ScheduledStatus scheduled)
|
||||||
{
|
{
|
||||||
Toast.AddResponseSuccess(Localizer["Scheduled for {0}.", scheduled.ScheduledAt.ToLocalTime().ToString(SUtility.dateTimeFormatExt)]);
|
Toast.AddResponseSuccess(Localizer["Scheduled for {0}.", scheduled.ScheduledAt.ToLocalTime().ToString(SUtility.dateTimeFormatExt)]);
|
||||||
form = new() { GroupId = GroupId, MessageType = DefaultVisibility };
|
form = new() { GroupId = GroupId, MessageType = DefaultVisibility, Language = DefaultLanguage };
|
||||||
await OnPublished.InvokeAsync(null);
|
await OnPublished.InvokeAsync(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.AddResponseSuccess(form.IsLocated ? Localizer["Located post published."] : Localizer["Published."]);
|
Toast.AddResponseSuccess(form.IsLocated ? Localizer["Located post published."] : Localizer["Published."]);
|
||||||
var published = result.Data as FeedPost;
|
var published = result.Data as FeedPost;
|
||||||
form = new() { GroupId = GroupId, MessageType = DefaultVisibility };
|
form = new() { GroupId = GroupId, MessageType = DefaultVisibility, Language = DefaultLanguage };
|
||||||
await OnPublished.InvokeAsync(published);
|
await OnPublished.InvokeAsync(published);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public class ComposeForm
|
|||||||
public int PollExpiresIn { get; set; } = 86400;
|
public int PollExpiresIn { get; set; } = 86400;
|
||||||
public bool PollMultiple { get; set; }
|
public bool PollMultiple { get; set; }
|
||||||
|
|
||||||
|
/// <summary>The language the post is written in (ISO 639-1); null leaves it to PrivaPub.</summary>
|
||||||
|
public string Language { get; set; }
|
||||||
|
|
||||||
/// <summary>The post this one quotes.</summary>
|
/// <summary>The post this one quotes.</summary>
|
||||||
public string QuotedStatusId { get; set; }
|
public string QuotedStatusId { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace decePubClient.Services
|
|||||||
Sensitive = form.HasContentWarning,
|
Sensitive = form.HasContentWarning,
|
||||||
SpoilerText = form.HasContentWarning ? form.SpoilerText : null,
|
SpoilerText = form.HasContentWarning ? form.SpoilerText : null,
|
||||||
Visibility = form.Visibility,
|
Visibility = form.Visibility,
|
||||||
Language = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName,
|
Language = form.Language ?? CultureInfo.CurrentUICulture.TwoLetterISOLanguageName,
|
||||||
ScheduledAt = form.ScheduledAtUtc?.ToString("o", CultureInfo.InvariantCulture),
|
ScheduledAt = form.ScheduledAtUtc?.ToString("o", CultureInfo.InvariantCulture),
|
||||||
Poll = form.HasPoll ? new PollForm { Options = [.. form.PollOptions.Select(o => o.Trim()).Where(o => o.Length > 0)], ExpiresIn = form.PollExpiresIn, Multiple = form.PollMultiple } : null,
|
Poll = form.HasPoll ? new PollForm { Options = [.. form.PollOptions.Select(o => o.Trim()).Where(o => o.Length > 0)], ExpiresIn = form.PollExpiresIn, Multiple = form.PollMultiple } : null,
|
||||||
QuotedStatusId = form.QuotedStatusId
|
QuotedStatusId = form.QuotedStatusId
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ using Microsoft.Playwright;
|
|||||||
|
|
||||||
namespace decePubClient.E2E.Actions
|
namespace decePubClient.E2E.Actions
|
||||||
{
|
{
|
||||||
// The composer beyond words: a poll, the persona's own post edited, and a quote, each checked on PrivaPub
|
// The composer beyond words: a poll in a language picked, the persona's own post edited, and a quote, each checked on
|
||||||
|
// PrivaPub
|
||||||
public sealed class ComposerTests(Browser browser)
|
public sealed class ComposerTests(Browser browser)
|
||||||
{
|
{
|
||||||
static async Task OpenHome(IPage page)
|
static async Task OpenHome(IPage page)
|
||||||
@@ -39,12 +40,15 @@ namespace decePubClient.E2E.Actions
|
|||||||
var choices = page.Locator("[data-poll-editor] input[type=text]");
|
var choices = page.Locator("[data-poll-editor] input[type=text]");
|
||||||
await choices.Nth(0).FillAsync("tea");
|
await choices.Nth(0).FillAsync("tea");
|
||||||
await choices.Nth(1).FillAsync("coffee");
|
await choices.Nth(1).FillAsync("coffee");
|
||||||
|
// and in the language picked
|
||||||
|
await page.Locator("[data-compose-language]").First.SelectOptionAsync("it");
|
||||||
await page.GetByRole(AriaRole.Button, new() { Name = "Post" }).Last.ClickAsync();
|
await page.GetByRole(AriaRole.Button, new() { Name = "Post" }).Last.ClickAsync();
|
||||||
JsonNode latest = null;
|
JsonNode latest = null;
|
||||||
Assert.True(await Pasture.Eventually(async () => (latest = await Latest(persona))?["content"]?.GetValue<string>().Contains(text) == true, 15),
|
Assert.True(await Pasture.Eventually(async () => (latest = await Latest(persona))?["content"]?.GetValue<string>().Contains(text) == true, 15),
|
||||||
"the poll was never published");
|
"the poll was never published");
|
||||||
made = latest!["id"]!.GetValue<string>();
|
made = latest!["id"]!.GetValue<string>();
|
||||||
Assert.Equal(new[] { "tea", "coffee" }, latest["poll"]!["options"]!.AsArray().Select(o => o!["title"]!.GetValue<string>()));
|
Assert.Equal(new[] { "tea", "coffee" }, latest["poll"]!["options"]!.AsArray().Select(o => o!["title"]!.GetValue<string>()));
|
||||||
|
Assert.Equal("it", latest["language"]?.GetValue<string>());
|
||||||
await session.AssertGuards();
|
await session.AssertGuards();
|
||||||
}, page);
|
}, page);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user