mirror of
https://github.com/flarum/framework.git
synced 2025-02-19 07:13:24 +08:00
fix: messages inconsistencies (#4174)
* fix: messages inconsistencies * fix * chore: message at the page * fix: permission grid styling broken * fix
This commit is contained in:
parent
7136ad01d5
commit
89ff984446
|
@ -51,7 +51,7 @@ return [
|
|||
(new Extend\ApiResource(Resource\UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('canSendAnyMessage')
|
||||
->get(fn (object $model, Context $context) => $context->getActor()->can('sendAnyMessage')),
|
||||
->get(fn (User $user, Context $context) => $user->can('sendAnyMessage')),
|
||||
Schema\Integer::make('messageCount')
|
||||
->get(function (object $model, Context $context) {
|
||||
return Dialog::whereVisibleTo($context->getActor())
|
||||
|
|
|
@ -19,3 +19,9 @@ declare module 'flarum/forum/states/ComposerState' {
|
|||
composingMessageTo(dialog: Dialog): boolean;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'flarum/common/models/User' {
|
||||
export default interface User {
|
||||
canSendAnyMessage(): boolean;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
import DialogMessage from './models/DialogMessage';
|
||||
import Dialog from './models/Dialog';
|
||||
import Extend from 'flarum/common/extenders';
|
||||
import User from 'flarum/common/models/User';
|
||||
|
||||
export default [
|
||||
new Extend.Store()
|
||||
.add('dialogs', Dialog) //
|
||||
.add('dialog-messages', DialogMessage), //
|
||||
|
||||
new Extend.Model(User) //
|
||||
.attribute<boolean>('canSendAnyMessage'),
|
||||
];
|
||||
|
|
|
@ -42,11 +42,14 @@ export default class DialogSection<CustomAttrs extends IDialogStreamAttrs = IDia
|
|||
<div className="DialogSection-header">
|
||||
<Avatar user={recipient} />
|
||||
<div className="DialogSection-header-info">
|
||||
{(recipient && (
|
||||
<Link href={app.route.user(recipient!)}>
|
||||
<h2>{username(recipient)}</h2>
|
||||
</Link>
|
||||
)) || <h2>{username(recipient)}</h2>}
|
||||
<h2 className="DialogSection-header-info-title">
|
||||
{(recipient && <Link href={app.route.user(recipient!)}>{username(recipient)}</Link>) || username(recipient)}
|
||||
{recipient && recipient.canSendAnyMessage() ? null : (
|
||||
<span className="DialogSection-header-info-helperText">
|
||||
{app.translator.trans('flarum-messages.forum.dialog_section.cannot_reply_text')}
|
||||
</span>
|
||||
)}
|
||||
</h2>
|
||||
<div className="badges">{listItems(recipient?.badges().toArray() || [])}</div>
|
||||
</div>
|
||||
<div className="DialogSection-header-actions">{this.actionItems().toArray()}</div>
|
||||
|
|
|
@ -106,9 +106,9 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
|||
|
||||
messages.forEach((message, index) => items.push(this.messageItem(message, index)));
|
||||
|
||||
if (ReplyPlaceholder) {
|
||||
if (app.session.user!.canSendAnyMessage() && ReplyPlaceholder) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="reply" /*data-index={this.attrs.state.count()}*/>
|
||||
<div className="MessageStream-item" key="reply">
|
||||
<ReplyPlaceholder
|
||||
discussion={this.attrs.dialog}
|
||||
onclick={() => {
|
||||
|
|
|
@ -14,8 +14,6 @@ export default class MessagesSidebar<CustomAttrs extends IMessagesSidebarAttrs =
|
|||
items(): ItemList<Mithril.Children> {
|
||||
const items = super.items();
|
||||
|
||||
const canSendAnyMessage = app.session.user!.attribute<boolean>('canSendAnyMessage');
|
||||
|
||||
items.remove('newDiscussion');
|
||||
|
||||
items.add(
|
||||
|
@ -27,9 +25,11 @@ export default class MessagesSidebar<CustomAttrs extends IMessagesSidebarAttrs =
|
|||
onclick={() => {
|
||||
return this.newMessageAction();
|
||||
}}
|
||||
disabled={!canSendAnyMessage}
|
||||
disabled={!app.session.user!.canSendAnyMessage()}
|
||||
>
|
||||
{app.translator.trans('flarum-messages.forum.messages_page.new_message_button')}
|
||||
{app.session.user!.canSendAnyMessage()
|
||||
? app.translator.trans('flarum-messages.forum.messages_page.send_message_button')
|
||||
: app.translator.trans('flarum-messages.forum.messages_page.cannot_send_message_button')}
|
||||
</Button>,
|
||||
10
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@ import Button from 'flarum/common/components/Button';
|
|||
import type Dialog from '../common/models/Dialog';
|
||||
import DialogsDropdown from './components/DialogsDropdown';
|
||||
import DialogListState from './states/DialogListState';
|
||||
import type User from 'flarum/common/models/User';
|
||||
|
||||
export { default as extend } from './extend';
|
||||
|
||||
|
@ -44,14 +45,14 @@ app.initializers.add('flarum-messages', () => {
|
|||
});
|
||||
|
||||
extend(HeaderSecondary.prototype, 'items', function (items) {
|
||||
if (app.session.user?.attribute<boolean>('canSendAnyMessage')) {
|
||||
if (app.session.user?.canSendAnyMessage()) {
|
||||
items.add('messages', <DialogsDropdown state={app.dropdownDialogs} />, 15);
|
||||
}
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
extend(UserControls, 'userControls', (items, user) => {
|
||||
if (app.session.user?.attribute<boolean>('canSendAnyMessage')) {
|
||||
extend(UserControls, 'userControls', (items, user: User) => {
|
||||
if (app.session.user?.canSendAnyMessage()) {
|
||||
items.add(
|
||||
'sendMessage',
|
||||
<Button
|
||||
|
@ -66,6 +67,7 @@ app.initializers.add('flarum-messages', () => {
|
|||
.then(() => app.composer.show());
|
||||
});
|
||||
}}
|
||||
helperText={user.canSendAnyMessage() ? null : app.translator.trans('flarum-messages.forum.user_controls.cannot_reply_text')}
|
||||
>
|
||||
{app.translator.trans('flarum-messages.forum.user_controls.send_message_button')}
|
||||
</Button>
|
||||
|
|
|
@ -232,6 +232,17 @@
|
|||
gap: 6px;
|
||||
}
|
||||
|
||||
.DialogSection-header-info-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.DialogSection-header-info-helperText {
|
||||
font-size: 0.8rem;
|
||||
font-weight: normal;
|
||||
color: var(--control-color);
|
||||
}
|
||||
|
||||
.DialogSection-back {
|
||||
display: flex;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ flarum-messages:
|
|||
|
||||
dialog_section:
|
||||
back_label: Go back
|
||||
cannot_reply_text: This user cannot reply
|
||||
controls:
|
||||
details_button: Details
|
||||
controls_toggle_label: Dialog control actions
|
||||
|
@ -42,14 +43,14 @@ flarum-messages:
|
|||
oldest_button: Oldest
|
||||
|
||||
messages_page:
|
||||
empty_text: You have no messages yet. When you send or receive messages, they
|
||||
will appear here.
|
||||
cannot_send_message_button: Can't Send a Message
|
||||
empty_text: No new messages
|
||||
hero:
|
||||
title: Messages
|
||||
subtitle: Your private conversations with other users
|
||||
mark_all_as_read_tooltip: Mark all as read
|
||||
new_message_button: Send a Message
|
||||
refresh_tooltip: Refresh
|
||||
send_message_button: Send a Message
|
||||
stream:
|
||||
load_previous_button: Load previous messages
|
||||
start_of_the_conversation: Start of the conversation
|
||||
|
@ -64,6 +65,7 @@ flarum-messages:
|
|||
|
||||
user_controls:
|
||||
send_message_button: Send a message
|
||||
cannot_reply_text: This user cannot reply
|
||||
|
||||
notifications:
|
||||
message_received_text: Message Received notification from {user}
|
||||
|
|
|
@ -78,14 +78,14 @@ export default function () {
|
|||
'tag',
|
||||
<Dropdown
|
||||
className="Dropdown--restrictByTag"
|
||||
buttonClassName="Button Button--text"
|
||||
buttonClassName="Button Button--link"
|
||||
label={app.translator.trans('flarum-tags.admin.permissions.restrict_by_tag_heading')}
|
||||
icon="fas fa-plus"
|
||||
caretIcon={null}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<Button icon={true} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{[tagIcon(tag, { className: 'Button-icon' }), ' ', tag.name()]}
|
||||
<Button icon={tagIcon(tag, { className: 'Button-icon' })} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{tag.name()}
|
||||
</Button>
|
||||
))}
|
||||
</Dropdown>
|
||||
|
|
|
@ -11,8 +11,10 @@ export interface IButtonAttrs extends ComponentAttrs {
|
|||
* Class(es) of an optional icon to be rendered within the button.
|
||||
*
|
||||
* If provided, the button will gain a `has-icon` class.
|
||||
*
|
||||
* You may also provide a rendered icon element directly.
|
||||
*/
|
||||
icon?: string;
|
||||
icon?: string | boolean | Mithril.Children;
|
||||
/**
|
||||
* Disables button from user input.
|
||||
*
|
||||
|
@ -42,6 +44,12 @@ export interface IButtonAttrs extends ComponentAttrs {
|
|||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* Helper text. Displayed under the button label.
|
||||
*
|
||||
* Default: `null`
|
||||
*/
|
||||
helperText?: Mithril.Children;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +64,7 @@ export interface IButtonAttrs extends ComponentAttrs {
|
|||
*/
|
||||
export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> {
|
||||
view(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
|
||||
let { type, 'aria-label': ariaLabel, icon: iconName, disabled, loading, className, class: _class, ...attrs } = this.attrs;
|
||||
let { type, 'aria-label': ariaLabel, icon: iconName, disabled, loading, className, class: _class, helperText, ...attrs } = this.attrs;
|
||||
|
||||
// If no `type` attr provided, set to "button"
|
||||
type ||= 'button';
|
||||
|
@ -74,6 +82,7 @@ export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> ext
|
|||
hasIcon: iconName,
|
||||
disabled: disabled || loading,
|
||||
loading: loading,
|
||||
hasSubContent: !!this.getButtonSubContent(),
|
||||
});
|
||||
|
||||
const buttonAttrs = {
|
||||
|
@ -104,12 +113,21 @@ export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> ext
|
|||
* Get the template for the button's content.
|
||||
*/
|
||||
protected getButtonContent(children: Mithril.Children): Mithril.ChildArray {
|
||||
const iconName = this.attrs.icon;
|
||||
const icon = this.attrs.icon;
|
||||
|
||||
return [
|
||||
iconName && <Icon name={iconName} className="Button-icon" />,
|
||||
children && <span className="Button-label">{children}</span>,
|
||||
icon && (typeof icon === 'string' || icon === true ? <Icon name={icon} className="Button-icon" /> : icon),
|
||||
children && (
|
||||
<span className="Button-label">
|
||||
<span className="Button-labelText">{children}</span>
|
||||
{this.getButtonSubContent()}
|
||||
</span>
|
||||
),
|
||||
this.attrs.loading && <LoadingIndicator size="small" display="inline" />,
|
||||
];
|
||||
}
|
||||
|
||||
protected getButtonSubContent(): Mithril.Children {
|
||||
return this.attrs.helperText ? <span className="Button-helperText">{this.attrs.helperText}</span> : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ export interface IDropdownAttrs extends ComponentAttrs {
|
|||
caretIcon?: string;
|
||||
/** The label of the dropdown toggle button. Defaults to 'Controls'. */
|
||||
label: Mithril.Children;
|
||||
/** The helper text to display under the button label. */
|
||||
helperText: Mithril.Children;
|
||||
/** The label used to describe the dropdown toggle button to assistive readers. Defaults to 'Toggle dropdown menu'. */
|
||||
accessibleToggleLabel?: string;
|
||||
/** An optional tooltip to show when hovering over the dropdown toggle button. */
|
||||
|
@ -157,11 +159,18 @@ export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttr
|
|||
getButtonContent(children: Mithril.ChildArray): Mithril.ChildArray {
|
||||
return [
|
||||
this.attrs.icon ? <Icon name={this.attrs.icon} className="Button-icon" /> : '',
|
||||
<span className="Button-label">{this.attrs.label}</span>,
|
||||
<span className="Button-label">
|
||||
<span className="Button-labelText">{this.attrs.label}</span>
|
||||
{this.getButtonSubContent()}
|
||||
</span>,
|
||||
this.attrs.caretIcon ? <Icon name={this.attrs.caretIcon} className="Button-caret" /> : '',
|
||||
];
|
||||
}
|
||||
|
||||
protected getButtonSubContent(): Mithril.Children {
|
||||
return this.attrs.helperText ? <span className="Button-helperText">{this.attrs.helperText}</span> : null;
|
||||
}
|
||||
|
||||
getMenu(items: Mithril.Vnode<any, any>[]): Mithril.Vnode<any, any> {
|
||||
return <ul className={'Dropdown-menu dropdown-menu ' + this.attrs.menuClassName}>{items}</ul>;
|
||||
}
|
||||
|
|
|
@ -94,23 +94,19 @@
|
|||
}
|
||||
}
|
||||
.Dropdown {
|
||||
display: block;
|
||||
|
||||
.Dropdown-toggle {
|
||||
width: 100%;
|
||||
display: block;
|
||||
text-align: left;
|
||||
float: none;
|
||||
margin: -2px 0;
|
||||
}
|
||||
.Dropdown-menu {
|
||||
margin: 0;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
}
|
||||
.Button {
|
||||
text-decoration: none;
|
||||
|
||||
.Badge {
|
||||
margin: -3px 2px -3px 0;
|
||||
margin: 0 2px 0 0;
|
||||
}
|
||||
}
|
||||
td:not(:hover) .Select-caret,
|
||||
|
@ -126,12 +122,8 @@
|
|||
margin: -1px 0;
|
||||
}
|
||||
.PermissionDropdown {
|
||||
.Dropdown-toggle {
|
||||
padding: 5px 0;
|
||||
margin: -5px 0;
|
||||
}
|
||||
.Badge {
|
||||
margin: -3px 3px -3px 0;
|
||||
margin: 0 3px 0 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,6 +259,12 @@
|
|||
line-height: inherit;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.Button-helperText {
|
||||
font-size: 0.73rem;
|
||||
color: var(--muted-more-color);
|
||||
}
|
||||
.Button-icon {
|
||||
line-height: inherit;
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
> a, > button, > span {
|
||||
padding: 8px 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
color: var(--text-color);
|
||||
border-radius: 0;
|
||||
|
@ -51,6 +51,10 @@
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.hasSubContent {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.4;
|
||||
background: none !important;
|
||||
|
|
Loading…
Reference in New Issue
Block a user