Skip to content

UX: Further refinement of title edit UX #32846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ export default class TopicTitle extends Component {
id="topic-title"
class="container"
>
<div class="title-wrapper">
{{yield}}
</div>

{{yield}}
<PluginOutlet
@name="topic-title"
@connectorTagName="div"
Expand Down
28 changes: 24 additions & 4 deletions app/assets/javascripts/discourse/app/controllers/topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function registerCustomPostMessageCallback(type, callback) {

export default class TopicController extends Controller {
@service composer;
@service capabilities;
@service dialog;
@service documentTitle;
@service screenTrack;
Expand Down Expand Up @@ -157,6 +158,12 @@ export default class TopicController extends Controller {
return loaded && isSharedDraft;
}

@discourseComputed("model.details.can_edit")
showEditButton(canEdit) {
const canHover = window.matchMedia("(hover: hover)").matches;
return !canHover && canEdit;
}

@discourseComputed("site.mobileView", "model.posts_count")
showSelectedPostsAtBottom(mobileView, postsCount) {
return mobileView && postsCount > 3;
Expand Down Expand Up @@ -371,13 +378,26 @@ export default class TopicController extends Controller {
jumpTop(event) {
if (event && wantsNewWindow(event)) {
return;
} else {
DiscourseURL.routeTo(this.get("model.firstPostUrl"), {
skipIfOnScreen: false,
keepFilter: true,
});
}
}

@action
titleClick(event) {
if (event && wantsNewWindow(event)) {
return;
}
event?.preventDefault();
DiscourseURL.routeTo(this.get("model.firstPostUrl"), {
skipIfOnScreen: false,
keepFilter: true,
});
// If the user is selecting the title, don't go into edit mode
const selection = window.getSelection();
if (selection.toString().length > 0) {
return;
}
this.editTopic(event);
}

@action
Expand Down
271 changes: 138 additions & 133 deletions app/assets/javascripts/discourse/app/templates/topic.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import TopicTimerInfo from "discourse/components/topic-timer-info";
import TopicTitle from "discourse/components/topic-title";
import ageWithTooltip from "discourse/helpers/age-with-tooltip";
import bodyClass from "discourse/helpers/body-class";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import hideApplicationFooter from "discourse/helpers/hide-application-footer";
import htmlSafe from "discourse/helpers/html-safe";
Expand Down Expand Up @@ -98,162 +99,166 @@ export default RouteTemplate(
@save={{@controller.finishedEditingTopic}}
@model={{@controller.model}}
>
{{#if @controller.editingTopic}}
<div class="edit-topic-title">
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
/>

<div class="edit-title__wrapper">
<PluginOutlet
@name="edit-topic-title"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
>
<TextField
@id="edit-title"
@value={{@controller.buffered.title}}
@maxlength={{@controller.siteSettings.max_topic_title_length}}
@autofocus="true"
/>
</PluginOutlet>
</div>
<div
class={{concatClass
"title-wrapper"
(if @controller.editingTopic "editing-topic")
(if @controller.model.details.can_edit "can-edit-topic")
}}
>
{{#if @controller.editingTopic}}
<div class="edit-topic-title">
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
/>

{{#if @controller.showCategoryChooser}}
<div class="edit-category__wrapper">
<div class="edit-title__wrapper">
<PluginOutlet
@name="edit-topic-category"
@name="edit-topic-title"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
>
<CategoryChooser
@value={{@controller.buffered.category_id}}
@onChange={{@controller.topicCategoryChanged}}
class="small"
<TextField
@id="edit-title"
@value={{@controller.buffered.title}}
@maxlength={{@controller.siteSettings.max_topic_title_length}}
@autofocus="true"
/>
</PluginOutlet>
</div>
{{/if}}

{{#if @controller.canEditTags}}
<div class="edit-tags__wrapper">
<PluginOutlet
@name="edit-topic-tags"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
>
<MiniTagChooser
@value={{@controller.buffered.tags}}
@onChange={{@controller.topicTagsChanged}}
@options={{hash
filterable=true
categoryId=@controller.buffered.category_id
minimum=@controller.minimumRequiredTags
filterPlaceholder="tagging.choose_for_topic"
useHeaderFilter=true
{{#if @controller.showCategoryChooser}}
<div class="edit-category__wrapper">
<PluginOutlet
@name="edit-topic-category"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
/>
</PluginOutlet>
</div>
{{/if}}
>
<CategoryChooser
@value={{@controller.buffered.category_id}}
@onChange={{@controller.topicCategoryChanged}}
class="small"
/>
</PluginOutlet>
</div>
{{/if}}

<PluginOutlet
@name="edit-topic"
@connectorTagName="div"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
/>
{{#if @controller.canEditTags}}
<div class="edit-tags__wrapper">
<PluginOutlet
@name="edit-topic-tags"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
>
<MiniTagChooser
@value={{@controller.buffered.tags}}
@onChange={{@controller.topicTagsChanged}}
@options={{hash
filterable=true
categoryId=@controller.buffered.category_id
minimum=@controller.minimumRequiredTags
filterPlaceholder="tagging.choose_for_topic"
useHeaderFilter=true
}}
/>
</PluginOutlet>
</div>
{{/if}}

<div class="edit-controls">
<DButton
@action={{@controller.finishedEditingTopic}}
@icon="check"
@ariaLabel="composer.save_edit"
class="btn-primary submit-edit"
/>
<DButton
@action={{@controller.cancelEditingTopic}}
@icon="xmark"
@ariaLabel="composer.cancel"
class="btn-default cancel-edit"
<PluginOutlet
@name="edit-topic"
@connectorTagName="div"
@outletArgs={{lazyHash
model=@controller.model
buffered=@controller.buffered
}}
/>

{{#if @controller.canRemoveTopicFeaturedLink}}
<a
href
{{on "click" @controller.removeFeaturedLink}}
class="remove-featured-link"
title={{i18n "composer.remove_featured_link"}}
>
{{icon "circle-xmark"}}
{{@controller.featuredLinkDomain}}
</a>
{{/if}}
</div>
</div>

{{else}}
<h1 data-topic-id={{@controller.model.id}}>
{{#unless @controller.model.is_warning}}
{{#if @controller.canSendPms}}
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
@href={{@controller.pmPath}}
@title="topic_statuses.personal_message.title"
@ariaLabel="user.messages.inbox"
<div class="edit-controls">
<DButton
@action={{@controller.finishedEditingTopic}}
@icon="check"
@ariaLabel="composer.save_edit"
class="btn-primary submit-edit"
/>
{{else}}
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
<DButton
@action={{@controller.cancelEditingTopic}}
@icon="xmark"
@ariaLabel="composer.cancel"
class="btn-default cancel-edit"
/>

{{#if @controller.canRemoveTopicFeaturedLink}}
<a
href
{{on "click" @controller.removeFeaturedLink}}
class="remove-featured-link"
title={{i18n "composer.remove_featured_link"}}
>
{{icon "circle-xmark"}}
{{@controller.featuredLinkDomain}}
</a>
{{/if}}
</div>
</div>
{{else}}
<h1
data-topic-id={{@controller.model.id}}
role="button"
class={{concatClass "title-wrapper edit-topic"}}
aria-label={{i18n "edit_topic"}}
{{on "click" @controller.titleClick}}
title={{i18n "edit_topic"}}
>
{{#unless @controller.model.is_warning}}
{{#if @controller.canSendPms}}
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
@href={{@controller.pmPath}}
@title="topic_statuses.personal_message.title"
@ariaLabel="user.messages.inbox"
/>
{{else}}
<PrivateMessageGlyph
@shouldShow={{@controller.model.isPrivateMessage}}
/>
{{/if}}
{{/unless}}

{{#if @controller.model.details.loaded}}
<TopicStatus @topic={{@controller.model}} />
<span class="fancy-title">
{{htmlSafe @controller.model.fancyTitle}}
</span>
{{/if}}
{{/unless}}

{{#if @controller.model.details.loaded}}
<TopicStatus @topic={{@controller.model}} />
<a
href={{@controller.model.url}}
{{on "click" @controller.jumpTop}}
class="fancy-title"
>
{{htmlSafe @controller.model.fancyTitle}}
</a>
{{/if}}
{{#if @controller.showEditButton}}
{{icon "pencil"}}
{{/if}}

{{#if @controller.model.details.can_edit}}
<a
href
{{on "click" @controller.editTopic}}
class="edit-topic"
title={{i18n "edit_topic"}}
>{{icon "pencil"}}</a>
{{/if}}
<PluginOutlet
@name="topic-title-suffix"
@outletArgs={{lazyHash model=@controller.model}}
/>
</h1>

<PluginOutlet
@name="topic-title-suffix"
@outletArgs={{lazyHash model=@controller.model}}
/>
</h1>

<PluginOutlet
@name="topic-category-wrapper"
@outletArgs={{lazyHash topic=@controller.model}}
>
<TopicCategory
@topic={{@controller.model}}
class="topic-category"
/>
</PluginOutlet>
@name="topic-category-wrapper"
@outletArgs={{lazyHash topic=@controller.model}}
>
<TopicCategory
@topic={{@controller.model}}
class="topic-category"
/>
</PluginOutlet>

{{/if}}
{{/if}}
</div>
</TopicTitle>

{{#if @controller.model.publishedPage}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ acceptance("Composer Actions", function (needs) {

await visit("/t/internationalization-localization/280");

await click("#topic-title .d-icon-pencil");
await click("#topic-title .can-edit-topic .edit-topic");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(4);
await click("#topic-title .submit-edit");
Expand Down
Loading
Loading