diff --git a/Components/Composer.razor b/Components/Composer.razor index 78ab2c9..f51b0f3 100644 --- a/Components/Composer.razor +++ b/Components/Composer.razor @@ -17,18 +17,19 @@ } -
- + - @ContentLength/@ComposeForm.MaxContentLength + @* under the words, not over their last line *@ + @ContentLength/@ComposeForm.MaxContentLength
@if (suggestedAccounts.Count > 0 || suggestedTags.Count > 0) {
@foreach (var account in suggestedAccounts) { - + @* alt="": the handle is next to it. *@ @("@" + account.Acct) @@ -36,7 +37,7 @@ } @foreach (var tag in suggestedTags) { - #@tag.Name + #@tag.Name }
} @@ -131,16 +132,18 @@
@if (!form.IsLocated && GroupId is null && Editing is null) { - + - + @foreach (var messageType in Enum.GetValues()) { - - + + + @Localizer[messageType.ToString()] } @@ -293,6 +296,7 @@ if (!typed.Success) return; form.Content = form.Content[..typed.Groups[1].Index] + written + " "; + focusPending = true; } string Placeholder => Editing is not null ? Localizer["Edit your post"] @@ -340,6 +344,18 @@ _ => MessageType.Public }; + DTextarea textarea; + // the words take the focus when the composer opens, and again after a suggestion is picked + bool focusPending = true; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!focusPending) + return; + focusPending = false; + await textarea.Element.FocusAsync(); + } + protected override async Task OnInitializedAsync() { form.GroupId = GroupId; diff --git a/Components/Content.razor b/Components/Content.razor index 92c3f47..7a3fcbc 100644 --- a/Components/Content.razor +++ b/Components/Content.razor @@ -406,18 +406,16 @@
- @if (isReplying) - { + @* under the post on a desktop, as a sheet over the page on a phone, where the card's column is too narrow *@ + - } - @if (isQuoting) - { + + - } - @if (isEditing) - { + + - } + @if (isReporting && status.Account is { } reported) { diff --git a/Components/Daisy/Actions/DButton.razor b/Components/Daisy/Actions/DButton.razor index 5e70cc2..9111f85 100644 --- a/Components/Daisy/Actions/DButton.razor +++ b/Components/Daisy/Actions/DButton.razor @@ -3,7 +3,8 @@ @* daisyUI Button: https://daisyui.com/components/button/ . Renders an when Href is set. *@ @if (Href is null) { - } @@ -27,6 +28,8 @@ else [Parameter] public string Type { get; set; } = "button"; [Parameter] public string Href { get; set; } [Parameter] public EventCallback OnClick { get; set; } + /// Pressing it doesn't take the focus from where it is (a chip that writes into a focused textarea). + [Parameter] public bool KeepFocus { get; set; } string CssClass => Cx("btn", ColorClass, VariantClass, SizeClass, ShapeClass, Active ? "btn-active" : null, Disabled && Href is not null ? "btn-disabled" : null, Class); diff --git a/Components/Daisy/DataInput/DTextarea.razor b/Components/Daisy/DataInput/DTextarea.razor index e77c774..dd6733d 100644 --- a/Components/Daisy/DataInput/DTextarea.razor +++ b/Components/Daisy/DataInput/DTextarea.razor @@ -3,10 +3,13 @@ @* daisyUI Textarea: https://daisyui.com/components/textarea/ . Blazor sets the textarea's value property, so the bound value is the content; rows, placeholder and the like pass through. *@ - @code { + /// The textarea itself, for focusing it. + public ElementReference Element { get; private set; } + [Parameter] public DColor Color { get; set; } [Parameter] public DSize Size { get; set; } [Parameter] public bool Ghost { get; set; } diff --git a/Components/Feed/FeedTabs.razor b/Components/Feed/FeedTabs.razor index 86e4551..afe0984 100644 --- a/Components/Feed/FeedTabs.razor +++ b/Components/Feed/FeedTabs.razor @@ -33,7 +33,7 @@ - + diff --git a/Components/PhoneSheet.razor b/Components/PhoneSheet.razor new file mode 100644 index 0000000..56ad5e6 --- /dev/null +++ b/Components/PhoneSheet.razor @@ -0,0 +1,37 @@ +@inherits LocalizableComponentBase + +@* What opens in place on a desktop (the composer at the top of the feed, a reply under its post) and as a sheet from the + bottom of a phone's screen (below md), over a dimmed page, with its heading and a close button; a tap outside it or + Escape closes it too. Class is the inline look from md (padding, a raised panel). *@ +@if (IsOpen) +{ +
+ +
+
+ @Heading + +
+ @ChildContent +
+
+} + +@code { + [Parameter] public bool IsOpen { get; set; } + [Parameter] public EventCallback OnClose { get; set; } + /// The sheet's heading on a phone. + [Parameter] public string Heading { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter] public string Class { get; set; } + + protected override void OnInitialized() => IsLoading = false; + + Task Close() => OnClose.InvokeAsync(); + + Task OnKeyDown(KeyboardEventArgs e) => e.Key == "Escape" ? Close() : Task.CompletedTask; +} diff --git a/Pages/Index.razor b/Pages/Index.razor index 24ffff3..0fe53ba 100644 --- a/Pages/Index.razor +++ b/Pages/Index.razor @@ -18,19 +18,26 @@
- + + + } +
-
+ @* (on a phone the bottom stays clear of the New post button) *@ +
- @if (isComposing) - { -
- -
- } + + + @if (Filter.TimelineType is TimelineType.Notifications && filteredRequests > 0) { @@ -461,6 +468,14 @@ await InvokeAsync(StateHasChanged); } + // a composer that opens shows at the top of the feed, where it is, however far down the feed was read + async Task ToggleCompose() + { + isComposing = !isComposing; + if (isComposing && feedModule is not null) + await feedModule.InvokeVoidAsync("scrollToTop", ScrollId); + } + async Task ShowPending() { var shown = pending; diff --git a/Resources/AllStrings.it.resx b/Resources/AllStrings.it.resx index b1b6eef..e6b6410 100644 --- a/Resources/AllStrings.it.resx +++ b/Resources/AllStrings.it.resx @@ -1820,4 +1820,7 @@ Menu + + Nuovo post + diff --git a/Resources/AllStrings.resx b/Resources/AllStrings.resx index 406f821..70a33f7 100644 --- a/Resources/AllStrings.resx +++ b/Resources/AllStrings.resx @@ -1820,4 +1820,7 @@ Menu + + New post +