mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
UX: improve drafts list (#31122)
Improves the layout for the drafts list page, including the addition of icons to represent the content type. Internal ref: /t/129117
This commit is contained in:
parent
503f9b6f02
commit
41ce3d868e
@ -86,6 +86,8 @@ export default class PostList extends Component {
|
||||
@additionalItemClasses={{@additionalItemClasses}}
|
||||
@titleAriaLabel={{@titleAriaLabel}}
|
||||
@showUserInfo={{@showUserInfo}}
|
||||
@resumeDraft={{@resumeDraft}}
|
||||
@removeDraft={{@removeDraft}}
|
||||
>
|
||||
<:abovePostItemHeader>
|
||||
{{yield post to="abovePostItemHeader"}}
|
||||
|
@ -1,8 +1,13 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { or } from "truth-helpers";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import TopicStatus from "discourse/components/topic-status";
|
||||
import avatar from "discourse/helpers/avatar";
|
||||
import categoryLink from "discourse/helpers/category-link";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import formatDate from "discourse/helpers/format-date";
|
||||
import getURL from "discourse/lib/get-url";
|
||||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||
import { i18n } from "discourse-i18n";
|
||||
@ -28,6 +33,10 @@ export default class PostListItemDetails extends Component {
|
||||
: this.args.post.title;
|
||||
}
|
||||
|
||||
get draftTitle() {
|
||||
return this.args.post.title ?? this.args.post.data.title;
|
||||
}
|
||||
|
||||
get titleAriaLabel() {
|
||||
if (this.args.titleAriaLabel) {
|
||||
return this.args.titleAriaLabel;
|
||||
@ -58,14 +67,42 @@ export default class PostListItemDetails extends Component {
|
||||
href={{getURL this.url}}
|
||||
aria-label={{this.titleAriaLabel}}
|
||||
>{{this.topicTitle}}</a>
|
||||
{{else if @isDraft}}
|
||||
<DButton
|
||||
@action={{fn @resumeDraft @post}}
|
||||
class="btn-transparent draft-title"
|
||||
>
|
||||
{{or this.draftTitle (i18n "drafts.dropdown.untitled")}}
|
||||
</DButton>
|
||||
{{else}}
|
||||
{{this.topicTitle}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="category stream-post-category">
|
||||
{{categoryLink @post.category}}
|
||||
<div class="post-list-item__metadata">
|
||||
{{#if @post.category}}
|
||||
<span class="category stream-post-category">
|
||||
{{categoryLink @post.category}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<span class="time">
|
||||
{{formatDate @post.created_at leaveAgo="true"}}
|
||||
</span>
|
||||
|
||||
{{#if @post.deleted_by}}
|
||||
<span class="delete-info">
|
||||
{{icon "trash-can"}}
|
||||
{{avatar
|
||||
@post.deleted_by
|
||||
imageSize="tiny"
|
||||
extraClasses="actor"
|
||||
ignoreTitle="true"
|
||||
}}
|
||||
{{formatDate @item.deleted_at leaveAgo="true"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showUserInfo}}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { fn } from "@ember/helper";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import ExpandPost from "discourse/components/expand-post";
|
||||
import PostListItemDetails from "discourse/components/post-list/item/details";
|
||||
import avatar from "discourse/helpers/avatar";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import formatDate from "discourse/helpers/format-date";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
|
||||
export default class PostListItem extends Component {
|
||||
@ -53,6 +54,22 @@ export default class PostListItem extends Component {
|
||||
: this.args.post.id;
|
||||
}
|
||||
|
||||
get isDraft() {
|
||||
return this.args.post.constructor.name === "UserDraft";
|
||||
}
|
||||
|
||||
get draftIcon() {
|
||||
const key = this.args.post.draft_key;
|
||||
|
||||
if (key.startsWith("new_private_message")) {
|
||||
return "envelope";
|
||||
} else if (key.startsWith("new_topic")) {
|
||||
return "layer-group";
|
||||
} else {
|
||||
return "reply";
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="post-list-item
|
||||
@ -66,20 +83,26 @@ export default class PostListItem extends Component {
|
||||
{{yield to="abovePostItemHeader"}}
|
||||
|
||||
<div class="post-list-item__header info">
|
||||
<a
|
||||
href={{userPath this.user.username}}
|
||||
data-user-card={{this.user.username}}
|
||||
class="avatar-link"
|
||||
>
|
||||
<div class="avatar-wrapper">
|
||||
{{avatar
|
||||
this.user
|
||||
imageSize="large"
|
||||
extraClasses="actor"
|
||||
ignoreTitle="true"
|
||||
}}
|
||||
{{#if this.isDraft}}
|
||||
<div class="draft-icon">
|
||||
{{icon this.draftIcon class="icon"}}
|
||||
</div>
|
||||
</a>
|
||||
{{else}}
|
||||
<a
|
||||
href={{userPath this.user.username}}
|
||||
data-user-card={{this.user.username}}
|
||||
class="avatar-link"
|
||||
>
|
||||
<div class="avatar-wrapper">
|
||||
{{avatar
|
||||
this.user
|
||||
imageSize="large"
|
||||
extraClasses="actor"
|
||||
ignoreTitle="true"
|
||||
}}
|
||||
</div>
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
<PostListItemDetails
|
||||
@post={{@post}}
|
||||
@ -88,33 +111,31 @@ export default class PostListItem extends Component {
|
||||
@urlPath={{@urlPath}}
|
||||
@user={{this.user}}
|
||||
@showUserInfo={{@showUserInfo}}
|
||||
@isDraft={{this.isDraft}}
|
||||
@resumeDraft={{@resumeDraft}}
|
||||
/>
|
||||
|
||||
{{#if @post.draftType}}
|
||||
<span class="draft-type">{{@post.draftType}}</span>
|
||||
{{else}}
|
||||
{{#unless @post.draftType}}
|
||||
<ExpandPost @item={{@post}} />
|
||||
{{/unless}}
|
||||
|
||||
{{#if @post.editableDraft}}
|
||||
<div class="user-stream-item-draft-actions">
|
||||
<DButton
|
||||
@action={{fn @resumeDraft @post}}
|
||||
@icon="pencil"
|
||||
@title="drafts.resume"
|
||||
class="btn-default resume-draft"
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn @removeDraft @post}}
|
||||
@icon="trash-can"
|
||||
@title="drafts.remove"
|
||||
class="btn-danger remove-draft"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="post-list-item__metadata">
|
||||
<span class="time">
|
||||
{{formatDate @post.created_at leaveAgo="true"}}
|
||||
</span>
|
||||
|
||||
{{#if @post.deleted_by}}
|
||||
<span class="delete-info">
|
||||
{{icon "trash-can"}}
|
||||
{{avatar
|
||||
@post.deleted_by
|
||||
imageSize="tiny"
|
||||
extraClasses="actor"
|
||||
ignoreTitle="true"
|
||||
}}
|
||||
{{formatDate @item.deleted_at leaveAgo="true"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{yield to="belowPostItemMetadata"}}
|
||||
</div>
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { later } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { modifier } from "ember-modifier";
|
||||
import $ from "jquery";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import PostActionDescription from "discourse/components/post-action-description";
|
||||
import PostList from "discourse/components/post-list";
|
||||
@ -164,6 +163,8 @@ export default class UserStreamComponent extends Component {
|
||||
@titlePath="titleHtml"
|
||||
@additionalItemClasses="user-stream-item"
|
||||
@showUserInfo={{false}}
|
||||
@resumeDraft={{this.resumeDraft}}
|
||||
@removeDraft={{this.removeDraft}}
|
||||
class={{concatClass "user-stream" this.filterClassName}}
|
||||
{{this.eventListeners @stream}}
|
||||
>
|
||||
@ -216,23 +217,6 @@ export default class UserStreamComponent extends Component {
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#if post.editableDraft}}
|
||||
<div class="user-stream-item-draft-actions">
|
||||
<DButton
|
||||
@action={{fn this.resumeDraft post}}
|
||||
@icon="pencil"
|
||||
@label="drafts.resume"
|
||||
class="btn-default resume-draft"
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn this.removeDraft post}}
|
||||
@icon="trash-can"
|
||||
@title="drafts.remove"
|
||||
class="btn-danger remove-draft"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</:abovePostItemExcerpt>
|
||||
|
||||
<:belowPostItem as |post|>
|
||||
|
@ -47,7 +47,7 @@ acceptance("User Drafts", function (needs) {
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".user-stream-item:nth-child(2) a.avatar-link")
|
||||
.hasAttribute("href", "/u/eviltrout", "has correct avatar link");
|
||||
.dom(".user-stream-item:nth-child(2) .draft-icon .d-icon")
|
||||
.hasClass("d-icon-reply", "has correct icon");
|
||||
});
|
||||
});
|
||||
|
@ -24,31 +24,64 @@
|
||||
display: block;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.user-stream-item__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.user-stream-item__details {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
|
||||
.badge-category__wrapper {
|
||||
width: 100%;
|
||||
.post-list-item__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-list-item__details {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
|
||||
.badge-category__wrapper {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.post-list-item__metadata {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 0.5em;
|
||||
|
||||
span + span {
|
||||
&::before {
|
||||
content: "•";
|
||||
margin-right: 0.25em;
|
||||
vertical-align: middle;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.draft-icon {
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-up-4);
|
||||
margin-right: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stream-topic-title {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.user-stream-item__metadata {
|
||||
align-items: end;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 0.25em;
|
||||
.draft-title {
|
||||
color: var(--tertiary);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.user-stream-item__metadata ul {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--primary-medium);
|
||||
|
||||
li {
|
||||
display: inline-flex;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.type,
|
||||
@ -62,7 +95,7 @@
|
||||
line-height: var(--line-height-small);
|
||||
color: var(--primary-medium);
|
||||
font-size: var(--font-down-2);
|
||||
padding-top: 6px;
|
||||
padding-top: unset !important;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
@ -138,10 +171,6 @@
|
||||
padding: 3px 5px 5px 5px;
|
||||
}
|
||||
|
||||
.remove-draft {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.excerpt {
|
||||
margin: 1em 0 0 0;
|
||||
font-size: var(--font-0);
|
||||
@ -168,6 +197,13 @@
|
||||
.group-member-info {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.user-stream-item .user-stream-item-draft-actions {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
flex-direction: row;
|
||||
gap: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.user-stream .child-actions, /* DEPRECATED: '.user-stream .child-actions' selector*/
|
||||
|
@ -4,3 +4,4 @@
|
||||
@import "sidebar/sidebar-section";
|
||||
@import "user-card";
|
||||
@import "user-info";
|
||||
@import "user-stream-item";
|
||||
|
@ -0,0 +1,7 @@
|
||||
.user-stream-item .draft-icon {
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.user-stream-item .excerpt {
|
||||
margin: 0.75em 0 0 3.5em;
|
||||
}
|
Loading…
Reference in New Issue
Block a user