DEV: Move gjs `<template>` to bottom of class definitions
To satisfy updated eslint configuration
This commit is contained in:
parent
31e4191a9b
commit
ee0fef489f
|
@ -14,6 +14,39 @@ export default class AdminPostMenu extends Component {
|
|||
@service store;
|
||||
@service adminPostMenuButtons;
|
||||
|
||||
get reviewUrl() {
|
||||
return `/review?topic_id=${this.args.data.transformedPost.id}&status=all`;
|
||||
}
|
||||
|
||||
get extraButtons() {
|
||||
return this.adminPostMenuButtons.callbacks
|
||||
.map((callback) => {
|
||||
return callback(this.args.data.transformedPost);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
@action
|
||||
async topicAction(actionName) {
|
||||
await this.args.close();
|
||||
|
||||
try {
|
||||
await this.args.data[actionName]?.();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Unknown error while attempting \`${actionName}\`:`, error);
|
||||
}
|
||||
|
||||
await this.args.data.scheduleRerender();
|
||||
}
|
||||
|
||||
@action
|
||||
async extraAction(button) {
|
||||
await this.args.close();
|
||||
await button.action(this.args.data.post);
|
||||
await this.args.data.scheduleRerender();
|
||||
}
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
{{#if this.currentUser.staff}}
|
||||
|
@ -205,37 +238,4 @@ export default class AdminPostMenu extends Component {
|
|||
{{/each}}
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
get reviewUrl() {
|
||||
return `/review?topic_id=${this.args.data.transformedPost.id}&status=all`;
|
||||
}
|
||||
|
||||
get extraButtons() {
|
||||
return this.adminPostMenuButtons.callbacks
|
||||
.map((callback) => {
|
||||
return callback(this.args.data.transformedPost);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
@action
|
||||
async topicAction(actionName) {
|
||||
await this.args.close();
|
||||
|
||||
try {
|
||||
await this.args.data[actionName]?.();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Unknown error while attempting \`${actionName}\`:`, error);
|
||||
}
|
||||
|
||||
await this.args.data.scheduleRerender();
|
||||
}
|
||||
|
||||
@action
|
||||
async extraAction(button) {
|
||||
await this.args.close();
|
||||
await button.action(this.args.data.post);
|
||||
await this.args.data.scheduleRerender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,19 +9,6 @@ import { resolveAllShortUrls } from "pretty-text/upload-short-url";
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default class CookText extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
...attributes
|
||||
{{didUpdate this.buildOneboxes this.cooked}}
|
||||
{{didUpdate this.resolveShortUrls this.cooked}}
|
||||
{{didUpdate this.calculateOffsetHeight this.cooked}}
|
||||
{{didUpdate this.loadCookedText @rawText}}
|
||||
>
|
||||
{{this.cooked}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service siteSettings;
|
||||
@tracked cooked = null;
|
||||
|
||||
|
@ -63,4 +50,17 @@ export default class CookText extends Component {
|
|||
resolveShortUrls(element) {
|
||||
resolveAllShortUrls(ajax, this.siteSettings, element, this.args.opts);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
...attributes
|
||||
{{didUpdate this.buildOneboxes this.cooked}}
|
||||
{{didUpdate this.resolveShortUrls this.cooked}}
|
||||
{{didUpdate this.calculateOffsetHeight this.cooked}}
|
||||
{{didUpdate this.loadCookedText @rawText}}
|
||||
>
|
||||
{{this.cooked}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -17,62 +17,6 @@ const ACTION_AS_STRING_DEPRECATION_ARGS = [
|
|||
];
|
||||
|
||||
export default class DButton extends GlimmerComponentWithDeprecatedParentView {
|
||||
<template>
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
<button
|
||||
{{! For legacy compatibility. Prefer passing class as attributes. }}
|
||||
class={{concatClass
|
||||
@class
|
||||
(if @isLoading "is-loading")
|
||||
(if this.btnLink "btn-link" "btn")
|
||||
(if this.noText "no-text")
|
||||
this.btnType
|
||||
}}
|
||||
{{! For legacy compatibility. Prefer passing these as html attributes. }}
|
||||
id={{@id}}
|
||||
form={{@form}}
|
||||
aria-controls={{@ariaControls}}
|
||||
aria-expanded={{this.computedAriaExpanded}}
|
||||
tabindex={{@tabindex}}
|
||||
type={{or @type "button"}}
|
||||
...attributes
|
||||
disabled={{this.isDisabled}}
|
||||
title={{this.computedTitle}}
|
||||
aria-label={{this.computedAriaLabel}}
|
||||
{{on "keydown" this.keyDown}}
|
||||
{{on "click" this.click}}
|
||||
{{on "mousedown" this.mouseDown}}
|
||||
>
|
||||
{{#if @isLoading}}
|
||||
{{~icon "spinner" class="loading-icon"~}}
|
||||
{{else}}
|
||||
{{#if @icon}}
|
||||
{{#if @ariaHidden}}
|
||||
<span aria-hidden="true">
|
||||
{{~icon @icon~}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{~icon @icon~}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{~#if this.computedLabel~}}
|
||||
<span class="d-button-label">
|
||||
{{~htmlSafe this.computedLabel~}}
|
||||
{{~#if @ellipsis~}}
|
||||
…
|
||||
{{~/if~}}
|
||||
</span>
|
||||
{{~else~}}
|
||||
​
|
||||
{{! Zero-width space character, so icon-only button height = regular button height }}
|
||||
{{~/if~}}
|
||||
|
||||
{{yield}}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@service router;
|
||||
|
||||
@notEmpty("args.icon") btnIcon;
|
||||
|
@ -199,4 +143,60 @@ export default class DButton extends GlimmerComponentWithDeprecatedParentView {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
<button
|
||||
{{! For legacy compatibility. Prefer passing class as attributes. }}
|
||||
class={{concatClass
|
||||
@class
|
||||
(if @isLoading "is-loading")
|
||||
(if this.btnLink "btn-link" "btn")
|
||||
(if this.noText "no-text")
|
||||
this.btnType
|
||||
}}
|
||||
{{! For legacy compatibility. Prefer passing these as html attributes. }}
|
||||
id={{@id}}
|
||||
form={{@form}}
|
||||
aria-controls={{@ariaControls}}
|
||||
aria-expanded={{this.computedAriaExpanded}}
|
||||
tabindex={{@tabindex}}
|
||||
type={{or @type "button"}}
|
||||
...attributes
|
||||
disabled={{this.isDisabled}}
|
||||
title={{this.computedTitle}}
|
||||
aria-label={{this.computedAriaLabel}}
|
||||
{{on "keydown" this.keyDown}}
|
||||
{{on "click" this.click}}
|
||||
{{on "mousedown" this.mouseDown}}
|
||||
>
|
||||
{{#if @isLoading}}
|
||||
{{~icon "spinner" class="loading-icon"~}}
|
||||
{{else}}
|
||||
{{#if @icon}}
|
||||
{{#if @ariaHidden}}
|
||||
<span aria-hidden="true">
|
||||
{{~icon @icon~}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{~icon @icon~}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{~#if this.computedLabel~}}
|
||||
<span class="d-button-label">
|
||||
{{~htmlSafe this.computedLabel~}}
|
||||
{{~#if @ellipsis~}}
|
||||
…
|
||||
{{~/if~}}
|
||||
</span>
|
||||
{{~else~}}
|
||||
​
|
||||
{{! Zero-width space character, so icon-only button height = regular button height }}
|
||||
{{~/if~}}
|
||||
|
||||
{{yield}}
|
||||
</button>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -6,6 +6,18 @@ import deprecated from "discourse-common/lib/deprecated";
|
|||
|
||||
// Can add a body class from within a component
|
||||
export default class DSection extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
deprecated(
|
||||
`<DSection> is deprecated. Use {{body-class "foo-page" "bar"}} and/or <section></section> instead.`,
|
||||
{
|
||||
since: "3.2.0.beta1",
|
||||
dropFrom: "3.3.0.beta1",
|
||||
id: "discourse.d-section",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @pageClass}}
|
||||
{{bodyClass (concat @pageClass "-page")}}
|
||||
|
@ -21,16 +33,4 @@ export default class DSection extends Component {
|
|||
{{yield}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
deprecated(
|
||||
`<DSection> is deprecated. Use {{body-class "foo-page" "bar"}} and/or <section></section> instead.`,
|
||||
{
|
||||
since: "3.2.0.beta1",
|
||||
dropFrom: "3.3.0.beta1",
|
||||
id: "discourse.d-section",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,29 +11,6 @@ import { on } from "@ember/modifier";
|
|||
import autoFocus from "discourse/modifiers/auto-focus";
|
||||
|
||||
export default class FastEdit extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div class="fast-edit-container" {{on "keydown" this.onKeydown}}>
|
||||
<textarea
|
||||
{{on "input" this.updateValue}}
|
||||
id="fast-edit-input"
|
||||
{{autoFocus}}
|
||||
>{{@initialValue}}</textarea>
|
||||
|
||||
<DButton
|
||||
class="btn-small btn-primary save-fast-edit"
|
||||
@action={{this.save}}
|
||||
@icon="pencil-alt"
|
||||
@label="composer.save_edit"
|
||||
@translatedTitle={{this.buttonTitle}}
|
||||
@isLoading={{this.isSaving}}
|
||||
@disabled={{this.disabled}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@tracked value = this.args.initialValue;
|
||||
@tracked isSaving = false;
|
||||
|
||||
|
@ -81,4 +58,27 @@ export default class FastEdit extends Component {
|
|||
this.args.close();
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div class="fast-edit-container" {{on "keydown" this.onKeydown}}>
|
||||
<textarea
|
||||
{{on "input" this.updateValue}}
|
||||
id="fast-edit-input"
|
||||
{{autoFocus}}
|
||||
>{{@initialValue}}</textarea>
|
||||
|
||||
<DButton
|
||||
class="btn-small btn-primary save-fast-edit"
|
||||
@action={{this.save}}
|
||||
@icon="pencil-alt"
|
||||
@label="composer.save_edit"
|
||||
@translatedTitle={{this.buttonTitle}}
|
||||
@isLoading={{this.isSaving}}
|
||||
@disabled={{this.disabled}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -22,28 +22,6 @@ const FormTemplateField = <template>
|
|||
</template>;
|
||||
|
||||
export default class FormTemplateFieldWrapper extends Component {
|
||||
<template>
|
||||
{{#if this.parsedTemplate}}
|
||||
<div
|
||||
class="form-template-form__wrapper"
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{didUpdate this.refreshTemplate @id}}
|
||||
>
|
||||
{{#each this.parsedTemplate as |content|}}
|
||||
<FormTemplateField
|
||||
@component={{get this.fieldTypes content.type}}
|
||||
@content={{content}}
|
||||
@initialValue={{get this.initialValues content.id}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert alert-error">
|
||||
{{this.error}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@tracked error = null;
|
||||
@tracked parsedTemplate = null;
|
||||
|
||||
|
@ -94,4 +72,26 @@ export default class FormTemplateFieldWrapper extends Component {
|
|||
const templateContent = await response.form_template.template;
|
||||
return this._loadTemplate(templateContent);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.parsedTemplate}}
|
||||
<div
|
||||
class="form-template-form__wrapper"
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{didUpdate this.refreshTemplate @id}}
|
||||
>
|
||||
{{#each this.parsedTemplate as |content|}}
|
||||
<FormTemplateField
|
||||
@component={{get this.fieldTypes content.type}}
|
||||
@content={{content}}
|
||||
@initialValue={{get this.initialValues content.id}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert alert-error">
|
||||
{{this.error}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -10,49 +10,6 @@ const REPLIES_SUBSET = "replies";
|
|||
const TOPICS_SUBSET = "topics";
|
||||
|
||||
export default class DismissNew extends Component {
|
||||
<template>
|
||||
<DModal
|
||||
@closeModal={{@closeModal}}
|
||||
@title={{this.modalTitle}}
|
||||
@inline={{@inline}}
|
||||
>
|
||||
<:body>
|
||||
<p>
|
||||
{{#if this.showDismissNewTopics}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey={{this.dismissNewTopicsLabel}}
|
||||
@labelCount={{this.countNewTopics}}
|
||||
@checked={{this.dismissTopics}}
|
||||
@class="dismiss-topics"
|
||||
/>
|
||||
{{/if}}
|
||||
{{#if this.showDismissNewReplies}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey={{this.dismissNewRepliesLabel}}
|
||||
@labelCount={{this.countNewReplies}}
|
||||
@checked={{this.dismissPosts}}
|
||||
@class="dismiss-posts"
|
||||
/>
|
||||
{{/if}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey="topics.bulk.dismiss_new_modal.untrack"
|
||||
@checked={{this.untrack}}
|
||||
@class="untrack"
|
||||
/>
|
||||
</p>
|
||||
</:body>
|
||||
<:footer>
|
||||
<DButton
|
||||
@action={{this.dismissed}}
|
||||
@icon="check"
|
||||
@label="topics.bulk.dismiss"
|
||||
id="dismiss-read-confirm"
|
||||
class="btn-primary"
|
||||
/>
|
||||
</:footer>
|
||||
</DModal>
|
||||
</template>
|
||||
|
||||
@tracked untrack = false;
|
||||
@tracked dismissTopics = true;
|
||||
@tracked dismissPosts = true;
|
||||
|
@ -141,4 +98,47 @@ export default class DismissNew extends Component {
|
|||
|
||||
this.args.closeModal();
|
||||
}
|
||||
|
||||
<template>
|
||||
<DModal
|
||||
@closeModal={{@closeModal}}
|
||||
@title={{this.modalTitle}}
|
||||
@inline={{@inline}}
|
||||
>
|
||||
<:body>
|
||||
<p>
|
||||
{{#if this.showDismissNewTopics}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey={{this.dismissNewTopicsLabel}}
|
||||
@labelCount={{this.countNewTopics}}
|
||||
@checked={{this.dismissTopics}}
|
||||
@class="dismiss-topics"
|
||||
/>
|
||||
{{/if}}
|
||||
{{#if this.showDismissNewReplies}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey={{this.dismissNewRepliesLabel}}
|
||||
@labelCount={{this.countNewReplies}}
|
||||
@checked={{this.dismissPosts}}
|
||||
@class="dismiss-posts"
|
||||
/>
|
||||
{{/if}}
|
||||
<PreferenceCheckbox
|
||||
@labelKey="topics.bulk.dismiss_new_modal.untrack"
|
||||
@checked={{this.untrack}}
|
||||
@class="untrack"
|
||||
/>
|
||||
</p>
|
||||
</:body>
|
||||
<:footer>
|
||||
<DButton
|
||||
@action={{this.dismissed}}
|
||||
@icon="check"
|
||||
@label="topics.bulk.dismiss"
|
||||
id="dismiss-read-confirm"
|
||||
class="btn-primary"
|
||||
/>
|
||||
</:footer>
|
||||
</DModal>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -22,86 +22,6 @@ export function fixQuotes(str) {
|
|||
}
|
||||
|
||||
export default class PostTextSelectionToolbar extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
<div
|
||||
{{on "mousedown" this.trapEvents}}
|
||||
{{on "mouseup" this.trapEvents}}
|
||||
class={{concatClass "quote-button" "visible"}}
|
||||
{{this.appEventsListeners}}
|
||||
>
|
||||
<div class="buttons">
|
||||
<PluginOutlet
|
||||
@name="post-text-buttons"
|
||||
@defaultGlimmer={{true}}
|
||||
@outletArgs={{hash data=@data}}
|
||||
>
|
||||
{{#if this.embedQuoteButton}}
|
||||
<DButton
|
||||
@icon="quote-left"
|
||||
@label="post.quote_reply"
|
||||
@title="post.quote_reply_shortcut"
|
||||
class="btn-flat insert-quote"
|
||||
@action={{@data.insertQuote}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if @data.canEditPost}}
|
||||
<DButton
|
||||
@icon="pencil-alt"
|
||||
@label="post.quote_edit"
|
||||
@title="post.quote_edit_shortcut"
|
||||
class="btn-flat quote-edit-label"
|
||||
{{on "click" this.toggleFastEdit}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.quoteSharingEnabled}}
|
||||
<span class="quote-sharing">
|
||||
{{#if this.quoteSharingShowLabel}}
|
||||
<DButton
|
||||
@icon="share"
|
||||
@label="post.quote_share"
|
||||
class="btn-flat quote-share-label"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<span class="quote-share-buttons">
|
||||
{{#each this.quoteSharingSources as |source|}}
|
||||
<DButton
|
||||
@action={{fn this.share source}}
|
||||
@translatedTitle={{source.title}}
|
||||
@icon={{source.icon}}
|
||||
class="btn-flat"
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
<PluginOutlet
|
||||
@name="quote-share-buttons-after"
|
||||
@connectorTagName="span"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
{{/if}}
|
||||
</PluginOutlet>
|
||||
</div>
|
||||
|
||||
<div class="extra">
|
||||
{{#if this.isFastEditing}}
|
||||
<FastEdit
|
||||
@initialValue={{@data.quoteState.buffer}}
|
||||
@post={{this.post}}
|
||||
@close={{this.closeFastEdit}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<PluginOutlet @name="quote-button-after" @connectorTagName="div" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service currentUser;
|
||||
@service modal;
|
||||
@service site;
|
||||
|
@ -256,4 +176,84 @@ export default class PostTextSelectionToolbar extends Component {
|
|||
quote: window.getSelection().toString(),
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{! template-lint-disable no-pointer-down-event-binding }}
|
||||
<div
|
||||
{{on "mousedown" this.trapEvents}}
|
||||
{{on "mouseup" this.trapEvents}}
|
||||
class={{concatClass "quote-button" "visible"}}
|
||||
{{this.appEventsListeners}}
|
||||
>
|
||||
<div class="buttons">
|
||||
<PluginOutlet
|
||||
@name="post-text-buttons"
|
||||
@defaultGlimmer={{true}}
|
||||
@outletArgs={{hash data=@data}}
|
||||
>
|
||||
{{#if this.embedQuoteButton}}
|
||||
<DButton
|
||||
@icon="quote-left"
|
||||
@label="post.quote_reply"
|
||||
@title="post.quote_reply_shortcut"
|
||||
class="btn-flat insert-quote"
|
||||
@action={{@data.insertQuote}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if @data.canEditPost}}
|
||||
<DButton
|
||||
@icon="pencil-alt"
|
||||
@label="post.quote_edit"
|
||||
@title="post.quote_edit_shortcut"
|
||||
class="btn-flat quote-edit-label"
|
||||
{{on "click" this.toggleFastEdit}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.quoteSharingEnabled}}
|
||||
<span class="quote-sharing">
|
||||
{{#if this.quoteSharingShowLabel}}
|
||||
<DButton
|
||||
@icon="share"
|
||||
@label="post.quote_share"
|
||||
class="btn-flat quote-share-label"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<span class="quote-share-buttons">
|
||||
{{#each this.quoteSharingSources as |source|}}
|
||||
<DButton
|
||||
@action={{fn this.share source}}
|
||||
@translatedTitle={{source.title}}
|
||||
@icon={{source.icon}}
|
||||
class="btn-flat"
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
<PluginOutlet
|
||||
@name="quote-share-buttons-after"
|
||||
@connectorTagName="span"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
{{/if}}
|
||||
</PluginOutlet>
|
||||
</div>
|
||||
|
||||
<div class="extra">
|
||||
{{#if this.isFastEditing}}
|
||||
<FastEdit
|
||||
@initialValue={{@data.quoteState.buffer}}
|
||||
@post={{this.post}}
|
||||
@close={{this.closeFastEdit}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<PluginOutlet @name="quote-button-after" @connectorTagName="div" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -38,15 +38,6 @@ export function fixQuotes(str) {
|
|||
}
|
||||
|
||||
export default class PostTextSelection extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
{{this.documentListeners}}
|
||||
{{this.appEventsListeners}}
|
||||
{{this.runLoopHandlers}}
|
||||
></div>
|
||||
</template>
|
||||
|
||||
@service appEvents;
|
||||
@service capabilities;
|
||||
@service currentUser;
|
||||
|
@ -284,4 +275,13 @@ export default class PostTextSelection extends Component {
|
|||
await this.args.selectText();
|
||||
await this.hideToolbar();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
{{this.documentListeners}}
|
||||
{{this.appEventsListeners}}
|
||||
{{this.runLoopHandlers}}
|
||||
></div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import Component from "@glimmer/component";
|
|||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class RenderGlimmerContainer extends Component {
|
||||
@service renderGlimmer;
|
||||
|
||||
<template>
|
||||
{{#each this.renderGlimmer._registrations as |info|}}
|
||||
{{#in-element info.element insertBefore=null}}
|
||||
|
@ -12,6 +14,4 @@ export default class RenderGlimmerContainer extends Component {
|
|||
{{/in-element}}
|
||||
{{/each}}
|
||||
</template>
|
||||
|
||||
@service renderGlimmer;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,33 @@ import { action } from "@ember/object";
|
|||
import { isRTL } from "discourse/lib/text-direction";
|
||||
|
||||
export default class ReviewableBundledAction extends Component {
|
||||
@service site;
|
||||
|
||||
get multiple() {
|
||||
return this.args.bundle.actions.length > 1;
|
||||
}
|
||||
|
||||
get first() {
|
||||
return this.args.bundle.actions[0];
|
||||
}
|
||||
|
||||
get placement() {
|
||||
const vertical = this.site.mobileView ? "top" : "bottom";
|
||||
const horizontal = isRTL() ? "end" : "start";
|
||||
|
||||
return `${vertical}-${horizontal}`;
|
||||
}
|
||||
|
||||
@action
|
||||
perform(id) {
|
||||
if (id) {
|
||||
const _action = this.args.bundle.actions.find((a) => a.id === id);
|
||||
this.args.performAction(_action);
|
||||
} else {
|
||||
this.args.performAction(this.first);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.multiple}}
|
||||
<DropdownSelectBox
|
||||
|
@ -41,31 +68,4 @@ export default class ReviewableBundledAction extends Component {
|
|||
/>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service site;
|
||||
|
||||
get multiple() {
|
||||
return this.args.bundle.actions.length > 1;
|
||||
}
|
||||
|
||||
get first() {
|
||||
return this.args.bundle.actions[0];
|
||||
}
|
||||
|
||||
get placement() {
|
||||
const vertical = this.site.mobileView ? "top" : "bottom";
|
||||
const horizontal = isRTL() ? "end" : "start";
|
||||
|
||||
return `${vertical}-${horizontal}`;
|
||||
}
|
||||
|
||||
@action
|
||||
perform(id) {
|
||||
if (id) {
|
||||
const _action = this.args.bundle.actions.find((a) => a.id === id);
|
||||
this.args.performAction(_action);
|
||||
} else {
|
||||
this.args.performAction(this.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,6 @@ import Component from "@glimmer/component";
|
|||
import I18n from "I18n";
|
||||
|
||||
export default class ScoreValue extends Component {
|
||||
<template>
|
||||
{{#if @value}}
|
||||
<span class="op">{{if this.isNegative "-" "+"}}</span>
|
||||
<span class="score-value">
|
||||
<span class="score-number">{{this.numericValue}}</span>
|
||||
{{#if @label}}
|
||||
<span title={{this.explanationTitle}} class="score-value-type">
|
||||
{{this.explanationContent}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
get numericValue() {
|
||||
return parseFloat(Math.abs(this.args.value)).toFixed(1);
|
||||
}
|
||||
|
@ -31,4 +17,18 @@ export default class ScoreValue extends Component {
|
|||
get explanationContent() {
|
||||
return I18n.t(`review.explain.${this.args.label}.name`);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @value}}
|
||||
<span class="op">{{if this.isNegative "-" "+"}}</span>
|
||||
<span class="score-value">
|
||||
<span class="score-number">{{this.numericValue}}</span>
|
||||
{{#if @label}}
|
||||
<span title={{this.explanationTitle}} class="score-value-type">
|
||||
{{this.explanationContent}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -5,6 +5,19 @@ import { action } from "@ember/object";
|
|||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class UserTipContainer extends Component {
|
||||
@service userTips;
|
||||
|
||||
get safeHtmlContent() {
|
||||
return htmlSafe(this.args.data.contentHtml);
|
||||
}
|
||||
|
||||
@action
|
||||
handleDismiss(_, event) {
|
||||
event.preventDefault();
|
||||
this.args.close();
|
||||
this.userTips.hideUserTipForever(this.args.data.id);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="user-tip__container">
|
||||
<div class="user-tip__title">{{@data.titleText}}</div>
|
||||
|
@ -27,17 +40,4 @@ export default class UserTipContainer extends Component {
|
|||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service userTips;
|
||||
|
||||
get safeHtmlContent() {
|
||||
return htmlSafe(this.args.data.contentHtml);
|
||||
}
|
||||
|
||||
@action
|
||||
handleDismiss(_, event) {
|
||||
event.preventDefault();
|
||||
this.args.close();
|
||||
this.userTips.hideUserTipForever(this.args.data.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,15 +10,6 @@ import I18n from "I18n";
|
|||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default class UserTip extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div {{this.registerTip}}>
|
||||
{{#if this.shouldRenderTip}}
|
||||
<span {{this.tip}}></span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service currentUser;
|
||||
@service userTips;
|
||||
@service tooltip;
|
||||
|
@ -80,4 +71,13 @@ export default class UserTip extends Component {
|
|||
get shouldRenderTip() {
|
||||
return this.userTips.renderedId === this.args.id;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div {{this.registerTip}}>
|
||||
{{#if this.shouldRenderTip}}
|
||||
<span {{this.tip}}></span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -11,6 +11,40 @@ import TrapTab from "discourse/modifiers/trap-tab";
|
|||
import DFloatPortal from "float-kit/components/d-float-portal";
|
||||
|
||||
export default class DFloatBody extends Component {
|
||||
closeOnScroll = modifier(() => {
|
||||
const firstScrollParent = getScrollParent(this.trigger);
|
||||
|
||||
const handler = () => {
|
||||
this.args.instance.close();
|
||||
};
|
||||
|
||||
firstScrollParent.addEventListener("scroll", handler, { passive: true });
|
||||
|
||||
return () => {
|
||||
firstScrollParent.removeEventListener("scroll", handler);
|
||||
};
|
||||
});
|
||||
|
||||
get supportsCloseOnClickOutside() {
|
||||
return this.args.instance.expanded && this.options.closeOnClickOutside;
|
||||
}
|
||||
|
||||
get supportsCloseOnEscape() {
|
||||
return this.args.instance.expanded && this.options.closeOnEscape;
|
||||
}
|
||||
|
||||
get supportsCloseOnScroll() {
|
||||
return this.args.instance.expanded && this.options.closeOnScroll;
|
||||
}
|
||||
|
||||
get trigger() {
|
||||
return this.args.instance.trigger;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.args.instance.options;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<DFloatPortal
|
||||
|
@ -48,38 +82,4 @@ export default class DFloatBody extends Component {
|
|||
</div>
|
||||
</DFloatPortal>
|
||||
</template>
|
||||
|
||||
closeOnScroll = modifier(() => {
|
||||
const firstScrollParent = getScrollParent(this.trigger);
|
||||
|
||||
const handler = () => {
|
||||
this.args.instance.close();
|
||||
};
|
||||
|
||||
firstScrollParent.addEventListener("scroll", handler, { passive: true });
|
||||
|
||||
return () => {
|
||||
firstScrollParent.removeEventListener("scroll", handler);
|
||||
};
|
||||
});
|
||||
|
||||
get supportsCloseOnClickOutside() {
|
||||
return this.args.instance.expanded && this.options.closeOnClickOutside;
|
||||
}
|
||||
|
||||
get supportsCloseOnEscape() {
|
||||
return this.args.instance.expanded && this.options.closeOnEscape;
|
||||
}
|
||||
|
||||
get supportsCloseOnScroll() {
|
||||
return this.args.instance.expanded && this.options.closeOnScroll;
|
||||
}
|
||||
|
||||
get trigger() {
|
||||
return this.args.instance.trigger;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.args.instance.options;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ import Component from "@glimmer/component";
|
|||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
export default class DFloatPortal extends Component {
|
||||
get inline() {
|
||||
return this.args.inline ?? isTesting();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.inline}}
|
||||
{{yield}}
|
||||
|
@ -11,8 +15,4 @@ export default class DFloatPortal extends Component {
|
|||
{{/in-element}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
get inline() {
|
||||
return this.args.inline ?? isTesting();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import { MENU } from "float-kit/lib/constants";
|
|||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
|
||||
export default class DInlineMenu extends Component {
|
||||
@service menu;
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
|
@ -22,6 +24,4 @@ export default class DInlineMenu extends Component {
|
|||
@inline={{@inline}}
|
||||
/>
|
||||
</template>
|
||||
|
||||
@service menu;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
|||
import and from "truth-helpers/helpers/and";
|
||||
|
||||
export default class DInlineTooltip extends Component {
|
||||
@service tooltip;
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
|
@ -26,6 +28,4 @@ export default class DInlineTooltip extends Component {
|
|||
@inline={{@inline}}
|
||||
/>
|
||||
</template>
|
||||
|
||||
@service tooltip;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,49 @@ import { getOwner } from "@ember/application";
|
|||
import DMenuInstance from "float-kit/lib/d-menu-instance";
|
||||
|
||||
export default class DMenu extends Component {
|
||||
@service menu;
|
||||
|
||||
@tracked menuInstance = null;
|
||||
|
||||
registerTrigger = modifier((element) => {
|
||||
const options = {
|
||||
...this.args,
|
||||
...{
|
||||
autoUpdate: true,
|
||||
listeners: true,
|
||||
beforeTrigger: () => {
|
||||
this.menu.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
const instance = new DMenuInstance(getOwner(this), element, options);
|
||||
|
||||
this.menuInstance = instance;
|
||||
|
||||
return () => {
|
||||
instance.destroy();
|
||||
|
||||
if (this.isDestroying) {
|
||||
this.menuInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
get menuId() {
|
||||
return `d-menu-${this.menuInstance.id}`;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.menuInstance?.options ?? {};
|
||||
}
|
||||
|
||||
get componentArgs() {
|
||||
return {
|
||||
close: this.menuInstance.close,
|
||||
data: this.options.data,
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<DButton
|
||||
|
@ -58,47 +101,4 @@ export default class DMenu extends Component {
|
|||
</DFloatBody>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service menu;
|
||||
|
||||
@tracked menuInstance = null;
|
||||
|
||||
registerTrigger = modifier((element) => {
|
||||
const options = {
|
||||
...this.args,
|
||||
...{
|
||||
autoUpdate: true,
|
||||
listeners: true,
|
||||
beforeTrigger: () => {
|
||||
this.menu.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
const instance = new DMenuInstance(getOwner(this), element, options);
|
||||
|
||||
this.menuInstance = instance;
|
||||
|
||||
return () => {
|
||||
instance.destroy();
|
||||
|
||||
if (this.isDestroying) {
|
||||
this.menuInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
get menuId() {
|
||||
return `d-menu-${this.menuInstance.id}`;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.menuInstance?.options ?? {};
|
||||
}
|
||||
|
||||
get componentArgs() {
|
||||
return {
|
||||
close: this.menuInstance.close,
|
||||
data: this.options.data,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,6 @@ import { modifier } from "ember-modifier";
|
|||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
export default class DPopover extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div style="display:inline-flex;" {{this.registerDTooltip}}>
|
||||
{{yield}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service tooltip;
|
||||
|
||||
registerDTooltip = modifier((element) => {
|
||||
|
@ -37,4 +30,11 @@ export default class DPopover extends Component {
|
|||
instance.destroy();
|
||||
};
|
||||
});
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div style="display:inline-flex;" {{this.registerDTooltip}}>
|
||||
{{yield}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import concatClass from "discourse/helpers/concat-class";
|
|||
import { on } from "@ember/modifier";
|
||||
|
||||
export default class DToasts extends Component {
|
||||
@service toasts;
|
||||
|
||||
<template>
|
||||
<div class="fk-d-toasts">
|
||||
{{#each this.toasts.activeToasts as |toast|}}
|
||||
|
@ -22,6 +24,4 @@ export default class DToasts extends Component {
|
|||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service toasts;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,44 @@ import { getOwner } from "@ember/application";
|
|||
import and from "truth-helpers/helpers/and";
|
||||
|
||||
export default class DTooltip extends Component {
|
||||
@service tooltip;
|
||||
|
||||
@tracked tooltipInstance = null;
|
||||
|
||||
registerTrigger = modifier((element) => {
|
||||
const options = {
|
||||
...this.args,
|
||||
...{
|
||||
listeners: true,
|
||||
beforeTrigger: () => {
|
||||
this.tooltip.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
const instance = new DTooltipInstance(getOwner(this), element, options);
|
||||
|
||||
this.tooltipInstance = instance;
|
||||
|
||||
return () => {
|
||||
instance.destroy();
|
||||
|
||||
if (this.isDestroying) {
|
||||
this.tooltipInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
get options() {
|
||||
return this.tooltipInstance?.options;
|
||||
}
|
||||
|
||||
get componentArgs() {
|
||||
return {
|
||||
close: this.tooltip.close,
|
||||
data: this.options.data,
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<span
|
||||
|
@ -68,42 +106,4 @@ export default class DTooltip extends Component {
|
|||
</DFloatBody>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service tooltip;
|
||||
|
||||
@tracked tooltipInstance = null;
|
||||
|
||||
registerTrigger = modifier((element) => {
|
||||
const options = {
|
||||
...this.args,
|
||||
...{
|
||||
listeners: true,
|
||||
beforeTrigger: () => {
|
||||
this.tooltip.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
const instance = new DTooltipInstance(getOwner(this), element, options);
|
||||
|
||||
this.tooltipInstance = instance;
|
||||
|
||||
return () => {
|
||||
instance.destroy();
|
||||
|
||||
if (this.isDestroying) {
|
||||
this.tooltipInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
get options() {
|
||||
return this.tooltipInstance?.options;
|
||||
}
|
||||
|
||||
get componentArgs() {
|
||||
return {
|
||||
close: this.tooltip.close,
|
||||
data: this.options.data,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,22 @@ import ChatChannelStatus from "discourse/plugins/chat/discourse/components/chat-
|
|||
import I18n from "I18n";
|
||||
|
||||
export default class ChatChannelMessageEmojiPicker extends Component {
|
||||
@service chatChannelInfoRouteOriginManager;
|
||||
@service site;
|
||||
|
||||
membersLabel = I18n.t("chat.channel_info.tabs.members");
|
||||
settingsLabel = I18n.t("chat.channel_info.tabs.settings");
|
||||
backToChannelLabel = I18n.t("chat.channel_info.back_to_all_channel");
|
||||
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
|
||||
|
||||
get showTabs() {
|
||||
return (
|
||||
this.site.desktopView &&
|
||||
this.args.channel.membershipsCount > 1 &&
|
||||
this.args.channel.isOpen
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-full-page-header">
|
||||
<div class="chat-channel-header-details">
|
||||
|
@ -66,20 +82,4 @@ export default class ChatChannelMessageEmojiPicker extends Component {
|
|||
{{outlet}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service chatChannelInfoRouteOriginManager;
|
||||
@service site;
|
||||
|
||||
membersLabel = I18n.t("chat.channel_info.tabs.members");
|
||||
settingsLabel = I18n.t("chat.channel_info.tabs.settings");
|
||||
backToChannelLabel = I18n.t("chat.channel_info.back_to_all_channel");
|
||||
backToAllChannelsLabel = I18n.t("chat.channel_info.back_to_channel");
|
||||
|
||||
get showTabs() {
|
||||
return (
|
||||
this.site.desktopView &&
|
||||
this.args.channel.membershipsCount > 1 &&
|
||||
this.args.channel.isOpen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,45 +14,6 @@ import { hash } from "@ember/helper";
|
|||
import { schedule } from "@ember/runloop";
|
||||
|
||||
export default class ChatChannelMembers extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div class="chat-channel-members">
|
||||
<DcFilterInput
|
||||
@class="chat-channel-members__filter"
|
||||
@filterAction={{this.mutFilter}}
|
||||
@icons={{hash right="search"}}
|
||||
placeholder={{this.filterPlaceholder}}
|
||||
{{this.focusInput}}
|
||||
/>
|
||||
|
||||
{{#if (gt @channel.membershipsCount 0)}}
|
||||
<ul class="chat-channel-members__list" {{this.fill}}>
|
||||
{{#each this.members as |membership|}}
|
||||
<li class="chat-channel-members__list-item">
|
||||
<ChatUserInfo @user={{membership.user}} @avatarSize="tiny" />
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if this.noResults}}
|
||||
<li
|
||||
class="chat-channel-members__list-item -no-results alert alert-info"
|
||||
>
|
||||
{{this.noMembershipsFoundLabel}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<div {{this.loadMore}}>
|
||||
<br />
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="alert alert-info">
|
||||
{{this.noMembershipsLabel}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service chatApi;
|
||||
@service modal;
|
||||
@service loadingSlider;
|
||||
|
@ -122,4 +83,43 @@ export default class ChatChannelMembers extends Component {
|
|||
await this.members.load({ limit: 20 });
|
||||
this.loadingSlider.transitionEnded();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div class="chat-channel-members">
|
||||
<DcFilterInput
|
||||
@class="chat-channel-members__filter"
|
||||
@filterAction={{this.mutFilter}}
|
||||
@icons={{hash right="search"}}
|
||||
placeholder={{this.filterPlaceholder}}
|
||||
{{this.focusInput}}
|
||||
/>
|
||||
|
||||
{{#if (gt @channel.membershipsCount 0)}}
|
||||
<ul class="chat-channel-members__list" {{this.fill}}>
|
||||
{{#each this.members as |membership|}}
|
||||
<li class="chat-channel-members__list-item">
|
||||
<ChatUserInfo @user={{membership.user}} @avatarSize="tiny" />
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if this.noResults}}
|
||||
<li
|
||||
class="chat-channel-members__list-item -no-results alert alert-info"
|
||||
>
|
||||
{{this.noMembershipsFoundLabel}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<div {{this.loadMore}}>
|
||||
<br />
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="alert alert-info">
|
||||
{{this.noMembershipsLabel}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -7,18 +7,6 @@ import ChatEmojiPicker from "discourse/plugins/chat/discourse/components/chat-em
|
|||
import { modifier } from "ember-modifier";
|
||||
|
||||
export default class ChatChannelMessageEmojiPicker extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<ChatEmojiPicker
|
||||
@context="chat-channel-message"
|
||||
@didInsert={{this.didInsert}}
|
||||
@willDestroy={{this.willDestroy}}
|
||||
@didSelectEmoji={{this.didSelectEmoji}}
|
||||
@class="hidden"
|
||||
{{this.listenToBodyScroll}}
|
||||
/>
|
||||
</template>
|
||||
|
||||
@service site;
|
||||
@service chatEmojiPickerManager;
|
||||
|
||||
|
@ -74,4 +62,16 @@ export default class ChatChannelMessageEmojiPicker extends Component {
|
|||
willDestroy() {
|
||||
this._popper?.destroy();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<ChatEmojiPicker
|
||||
@context="chat-channel-message"
|
||||
@didInsert={{this.didInsert}}
|
||||
@willDestroy={{this.willDestroy}}
|
||||
@didSelectEmoji={{this.didSelectEmoji}}
|
||||
@class="hidden"
|
||||
{{this.listenToBodyScroll}}
|
||||
/>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -22,70 +22,6 @@ import { htmlSafe } from "@ember/template";
|
|||
const FADEOUT_CLASS = "-fade-out";
|
||||
|
||||
export default class ChatChannelRow extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
class={{concatClass
|
||||
"chat-channel-row"
|
||||
(if @channel.focused "focused")
|
||||
(if @channel.currentUserMembership.muted "muted")
|
||||
(if @options.leaveButton "can-leave")
|
||||
(if (eq this.chat.activeChannel.id @channel.id) "active")
|
||||
(if this.channelHasUnread "has-unread")
|
||||
}}
|
||||
tabindex="0"
|
||||
data-chat-channel-id={{@channel.id}}
|
||||
{{didInsert this.startTrackingStatus}}
|
||||
{{willDestroy this.stopTrackingStatus}}
|
||||
{{(if this.shouldRemoveChannel (modifier this.onRemoveChannel))}}
|
||||
>
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-row__content"
|
||||
(if this.shouldReset "-animate-reset")
|
||||
}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.registerSwipableRow))}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.handleSwipe))}}
|
||||
{{(if this.shouldReset (modifier this.onReset))}}
|
||||
style={{this.rowStyle}}
|
||||
>
|
||||
<ChatChannelTitle @channel={{@channel}} />
|
||||
<ChatChannelMetadata @channel={{@channel}} @unreadIndicator={{true}} />
|
||||
|
||||
{{#if
|
||||
(and @options.leaveButton @channel.isFollowing this.site.desktopView)
|
||||
}}
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
leaveClass="btn-flat chat-channel-leave-btn"
|
||||
labelType="none"
|
||||
leaveIcon="times"
|
||||
leaveTitle=(if
|
||||
@channel.isDirectMessageChannel
|
||||
this.leaveDirectMessageLabel
|
||||
this.leaveChannelLabel
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showRemoveButton}}
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-row__action-btn"
|
||||
(if this.isAtThreshold "-at-threshold" "-not-at-threshold")
|
||||
}}
|
||||
>
|
||||
{{icon "times-circle"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
</template>
|
||||
|
||||
@service api;
|
||||
@service capabilities;
|
||||
@service chat;
|
||||
|
@ -219,4 +155,68 @@ export default class ChatChannelRow extends Component {
|
|||
stopTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.stopTrackingStatus();
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
class={{concatClass
|
||||
"chat-channel-row"
|
||||
(if @channel.focused "focused")
|
||||
(if @channel.currentUserMembership.muted "muted")
|
||||
(if @options.leaveButton "can-leave")
|
||||
(if (eq this.chat.activeChannel.id @channel.id) "active")
|
||||
(if this.channelHasUnread "has-unread")
|
||||
}}
|
||||
tabindex="0"
|
||||
data-chat-channel-id={{@channel.id}}
|
||||
{{didInsert this.startTrackingStatus}}
|
||||
{{willDestroy this.stopTrackingStatus}}
|
||||
{{(if this.shouldRemoveChannel (modifier this.onRemoveChannel))}}
|
||||
>
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-row__content"
|
||||
(if this.shouldReset "-animate-reset")
|
||||
}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.registerSwipableRow))}}
|
||||
{{(if this.shouldHandleSwipe (modifier this.handleSwipe))}}
|
||||
{{(if this.shouldReset (modifier this.onReset))}}
|
||||
style={{this.rowStyle}}
|
||||
>
|
||||
<ChatChannelTitle @channel={{@channel}} />
|
||||
<ChatChannelMetadata @channel={{@channel}} @unreadIndicator={{true}} />
|
||||
|
||||
{{#if
|
||||
(and @options.leaveButton @channel.isFollowing this.site.desktopView)
|
||||
}}
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
leaveClass="btn-flat chat-channel-leave-btn"
|
||||
labelType="none"
|
||||
leaveIcon="times"
|
||||
leaveTitle=(if
|
||||
@channel.isDirectMessageChannel
|
||||
this.leaveDirectMessageLabel
|
||||
this.leaveChannelLabel
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showRemoveButton}}
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-channel-row__action-btn"
|
||||
(if this.isAtThreshold "-at-threshold" "-not-at-threshold")
|
||||
}}
|
||||
>
|
||||
{{icon "times-circle"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -27,279 +27,6 @@ const NOTIFICATION_LEVELS = [
|
|||
];
|
||||
|
||||
export default class ChatAboutScreen extends Component {
|
||||
<template>
|
||||
<div class="chat-channel-settings">
|
||||
<ChatForm as |form|>
|
||||
{{#if this.shouldRenderTitleSection}}
|
||||
<form.section @title={{this.titleSectionTitle}} as |section|>
|
||||
<section.row>
|
||||
<:default>
|
||||
<div class="chat-channel-settings__name">
|
||||
{{replaceEmoji @channel.title}}
|
||||
</div>
|
||||
|
||||
{{#if @channel.isCategoryChannel}}
|
||||
<div class="chat-channel-settings__slug">
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
>
|
||||
/chat/c/{{@channel.slug}}/{{@channel.id}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{/if}}
|
||||
</:default>
|
||||
|
||||
<:action>
|
||||
{{#if this.canEditChannel}}
|
||||
<DButton
|
||||
@label="chat.channel_settings.edit"
|
||||
@action={{this.onEditChannelName}}
|
||||
class="edit-name-slug-btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
|
||||
</section.row>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderDescriptionSection}}
|
||||
<form.section @title={{this.descriptionSectionTitle}} as |section|>
|
||||
<section.row>
|
||||
<:default>
|
||||
{{#if @channel.description.length}}
|
||||
{{@channel.description}}
|
||||
{{else}}
|
||||
{{this.descriptionPlaceholder}}
|
||||
{{/if}}
|
||||
</:default>
|
||||
|
||||
<:action>
|
||||
{{#if this.canEditChannel}}
|
||||
<DButton
|
||||
@label={{if
|
||||
@channel.description.length
|
||||
"chat.channel_settings.edit"
|
||||
"chat.channel_settings.add"
|
||||
}}
|
||||
@action={{this.onEditChannelDescription}}
|
||||
class="edit-description-btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
</section.row>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.site.mobileView}}
|
||||
<form.section as |section|>
|
||||
<section.row
|
||||
@label={{this.membersLabel}}
|
||||
@route="chat.channel.info.members"
|
||||
@routeModels={{@channel.routeModels}}
|
||||
/>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if @channel.isOpen}}
|
||||
<form.section @title={{this.settingsSectionTitle}} as |section|>
|
||||
<section.row @label={{this.muteSectionLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.currentUserMembership.muted}}
|
||||
class="chat-channel-settings__mute-switch"
|
||||
{{on "click" this.onToggleMuted}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
|
||||
{{#if this.shouldRenderDesktopNotificationsLevelSection}}
|
||||
<section.row @label={{this.desktopNotificationsLevelLabel}}>
|
||||
<:action>
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{@channel.currentUserMembership.desktopNotificationLevel}}
|
||||
@valueProperty="value"
|
||||
@class="chat-channel-settings__selector chat-channel-settings__desktop-notifications-selector"
|
||||
@onChange={{fn
|
||||
this.saveNotificationSettings
|
||||
"desktopNotificationLevel"
|
||||
"desktop_notification_level"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderMobileNotificationsLevelSection}}
|
||||
<section.row @label={{this.mobileNotificationsLevelLabel}}>
|
||||
<:action>
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
|
||||
@valueProperty="value"
|
||||
@class="chat-channel-settings__selector chat-channel-settings__mobile-notifications-selector"
|
||||
@onChange={{fn
|
||||
this.saveNotificationSettings
|
||||
"mobileNotificationLevel"
|
||||
"mobile_notification_level"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
<form.section @title={{this.channelInfoSectionTitle}} as |section|>
|
||||
{{#if @channel.isCategoryChannel}}
|
||||
<section.row @label={{this.categoryLabel}}>
|
||||
{{categoryBadge
|
||||
@channel.chatable
|
||||
link=true
|
||||
allowUncategorized=true
|
||||
}}
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
<section.row @label={{this.historyLabel}}>
|
||||
<ChatRetentionReminderText @channel={{@channel}} />
|
||||
</section.row>
|
||||
</form.section>
|
||||
|
||||
{{#if this.shouldRenderAdminSection}}
|
||||
<form.section
|
||||
@title={{this.adminSectionTitle}}
|
||||
data-section="admin"
|
||||
as |section|
|
||||
>
|
||||
{{#if this.autoJoinAvailable}}
|
||||
<section.row @label={{this.autoJoinLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.autoJoinUsers}}
|
||||
class="chat-channel-settings__auto-join-switch"
|
||||
{{on
|
||||
"click"
|
||||
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.toggleChannelWideMentionsAvailable}}
|
||||
<section.row @label={{this.channelWideMentionsLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
class="chat-channel-settings__channel-wide-mentions"
|
||||
@state={{@channel.allowChannelWideMentions}}
|
||||
{{on
|
||||
"click"
|
||||
(fn
|
||||
this.onToggleChannelWideMentions
|
||||
@channel.allowChannelWideMentions
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
|
||||
<:description>
|
||||
{{this.channelWideMentionsDescription}}
|
||||
</:description>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.toggleThreadingAvailable}}
|
||||
<section.row @label={{this.toggleThreadingLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.threadingEnabled}}
|
||||
class="chat-channel-settings__threading-switch"
|
||||
{{on
|
||||
"click"
|
||||
(fn
|
||||
this.onToggleThreadingEnabled @channel.threadingEnabled
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
|
||||
<:description>
|
||||
{{this.toggleThreadingDescription}}
|
||||
</:description>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderStatusSection}}
|
||||
{{#if this.shouldRenderArchiveRow}}
|
||||
<section.row>
|
||||
<:action>
|
||||
<DButton
|
||||
@action={{this.onArchiveChannel}}
|
||||
@label="chat.channel_settings.archive_channel"
|
||||
@icon="archive"
|
||||
class="archive-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
<section.row>
|
||||
<:action>
|
||||
{{#if @channel.isOpen}}
|
||||
<DButton
|
||||
@action={{this.onToggleChannelState}}
|
||||
@label="chat.channel_settings.close_channel"
|
||||
@icon="lock"
|
||||
class="close-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@action={{this.onToggleChannelState}}
|
||||
@label="chat.channel_settings.open_channel"
|
||||
@icon="unlock"
|
||||
class="open-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
</section.row>
|
||||
|
||||
<section.row>
|
||||
<:action>
|
||||
<DButton
|
||||
@action={{this.onDeleteChannel}}
|
||||
@label="chat.channel_settings.delete_channel"
|
||||
@icon="trash-alt"
|
||||
class="delete-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
<form.section as |section|>
|
||||
<section.row>
|
||||
<:action>
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
joinClass="btn-primary"
|
||||
leaveClass="btn-flat"
|
||||
joinIcon="sign-in-alt"
|
||||
leaveIcon="sign-out-alt"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
</form.section>
|
||||
</ChatForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service chatApi;
|
||||
@service chatGuardian;
|
||||
@service currentUser;
|
||||
|
@ -578,4 +305,277 @@ export default class ChatAboutScreen extends Component {
|
|||
model: this.args.channel,
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-channel-settings">
|
||||
<ChatForm as |form|>
|
||||
{{#if this.shouldRenderTitleSection}}
|
||||
<form.section @title={{this.titleSectionTitle}} as |section|>
|
||||
<section.row>
|
||||
<:default>
|
||||
<div class="chat-channel-settings__name">
|
||||
{{replaceEmoji @channel.title}}
|
||||
</div>
|
||||
|
||||
{{#if @channel.isCategoryChannel}}
|
||||
<div class="chat-channel-settings__slug">
|
||||
<LinkTo
|
||||
@route="chat.channel"
|
||||
@models={{@channel.routeModels}}
|
||||
>
|
||||
/chat/c/{{@channel.slug}}/{{@channel.id}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{/if}}
|
||||
</:default>
|
||||
|
||||
<:action>
|
||||
{{#if this.canEditChannel}}
|
||||
<DButton
|
||||
@label="chat.channel_settings.edit"
|
||||
@action={{this.onEditChannelName}}
|
||||
class="edit-name-slug-btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
|
||||
</section.row>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderDescriptionSection}}
|
||||
<form.section @title={{this.descriptionSectionTitle}} as |section|>
|
||||
<section.row>
|
||||
<:default>
|
||||
{{#if @channel.description.length}}
|
||||
{{@channel.description}}
|
||||
{{else}}
|
||||
{{this.descriptionPlaceholder}}
|
||||
{{/if}}
|
||||
</:default>
|
||||
|
||||
<:action>
|
||||
{{#if this.canEditChannel}}
|
||||
<DButton
|
||||
@label={{if
|
||||
@channel.description.length
|
||||
"chat.channel_settings.edit"
|
||||
"chat.channel_settings.add"
|
||||
}}
|
||||
@action={{this.onEditChannelDescription}}
|
||||
class="edit-description-btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
</section.row>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.site.mobileView}}
|
||||
<form.section as |section|>
|
||||
<section.row
|
||||
@label={{this.membersLabel}}
|
||||
@route="chat.channel.info.members"
|
||||
@routeModels={{@channel.routeModels}}
|
||||
/>
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
{{#if @channel.isOpen}}
|
||||
<form.section @title={{this.settingsSectionTitle}} as |section|>
|
||||
<section.row @label={{this.muteSectionLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.currentUserMembership.muted}}
|
||||
class="chat-channel-settings__mute-switch"
|
||||
{{on "click" this.onToggleMuted}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
|
||||
{{#if this.shouldRenderDesktopNotificationsLevelSection}}
|
||||
<section.row @label={{this.desktopNotificationsLevelLabel}}>
|
||||
<:action>
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{@channel.currentUserMembership.desktopNotificationLevel}}
|
||||
@valueProperty="value"
|
||||
@class="chat-channel-settings__selector chat-channel-settings__desktop-notifications-selector"
|
||||
@onChange={{fn
|
||||
this.saveNotificationSettings
|
||||
"desktopNotificationLevel"
|
||||
"desktop_notification_level"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderMobileNotificationsLevelSection}}
|
||||
<section.row @label={{this.mobileNotificationsLevelLabel}}>
|
||||
<:action>
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{@channel.currentUserMembership.mobileNotificationLevel}}
|
||||
@valueProperty="value"
|
||||
@class="chat-channel-settings__selector chat-channel-settings__mobile-notifications-selector"
|
||||
@onChange={{fn
|
||||
this.saveNotificationSettings
|
||||
"mobileNotificationLevel"
|
||||
"mobile_notification_level"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
<form.section @title={{this.channelInfoSectionTitle}} as |section|>
|
||||
{{#if @channel.isCategoryChannel}}
|
||||
<section.row @label={{this.categoryLabel}}>
|
||||
{{categoryBadge
|
||||
@channel.chatable
|
||||
link=true
|
||||
allowUncategorized=true
|
||||
}}
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
<section.row @label={{this.historyLabel}}>
|
||||
<ChatRetentionReminderText @channel={{@channel}} />
|
||||
</section.row>
|
||||
</form.section>
|
||||
|
||||
{{#if this.shouldRenderAdminSection}}
|
||||
<form.section
|
||||
@title={{this.adminSectionTitle}}
|
||||
data-section="admin"
|
||||
as |section|
|
||||
>
|
||||
{{#if this.autoJoinAvailable}}
|
||||
<section.row @label={{this.autoJoinLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.autoJoinUsers}}
|
||||
class="chat-channel-settings__auto-join-switch"
|
||||
{{on
|
||||
"click"
|
||||
(fn this.onToggleAutoJoinUsers @channel.autoJoinUsers)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.toggleChannelWideMentionsAvailable}}
|
||||
<section.row @label={{this.channelWideMentionsLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
class="chat-channel-settings__channel-wide-mentions"
|
||||
@state={{@channel.allowChannelWideMentions}}
|
||||
{{on
|
||||
"click"
|
||||
(fn
|
||||
this.onToggleChannelWideMentions
|
||||
@channel.allowChannelWideMentions
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
|
||||
<:description>
|
||||
{{this.channelWideMentionsDescription}}
|
||||
</:description>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.toggleThreadingAvailable}}
|
||||
<section.row @label={{this.toggleThreadingLabel}}>
|
||||
<:action>
|
||||
<DToggleSwitch
|
||||
@state={{@channel.threadingEnabled}}
|
||||
class="chat-channel-settings__threading-switch"
|
||||
{{on
|
||||
"click"
|
||||
(fn
|
||||
this.onToggleThreadingEnabled @channel.threadingEnabled
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
|
||||
<:description>
|
||||
{{this.toggleThreadingDescription}}
|
||||
</:description>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.shouldRenderStatusSection}}
|
||||
{{#if this.shouldRenderArchiveRow}}
|
||||
<section.row>
|
||||
<:action>
|
||||
<DButton
|
||||
@action={{this.onArchiveChannel}}
|
||||
@label="chat.channel_settings.archive_channel"
|
||||
@icon="archive"
|
||||
class="archive-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
<section.row>
|
||||
<:action>
|
||||
{{#if @channel.isOpen}}
|
||||
<DButton
|
||||
@action={{this.onToggleChannelState}}
|
||||
@label="chat.channel_settings.close_channel"
|
||||
@icon="lock"
|
||||
class="close-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@action={{this.onToggleChannelState}}
|
||||
@label="chat.channel_settings.open_channel"
|
||||
@icon="unlock"
|
||||
class="open-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
{{/if}}
|
||||
</:action>
|
||||
</section.row>
|
||||
|
||||
<section.row>
|
||||
<:action>
|
||||
<DButton
|
||||
@action={{this.onDeleteChannel}}
|
||||
@label="chat.channel_settings.delete_channel"
|
||||
@icon="trash-alt"
|
||||
class="delete-btn chat-form__btn btn-flat"
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
{{/if}}
|
||||
|
||||
</form.section>
|
||||
{{/if}}
|
||||
|
||||
<form.section as |section|>
|
||||
<section.row>
|
||||
<:action>
|
||||
<ToggleChannelMembershipButton
|
||||
@channel={{@channel}}
|
||||
@options={{hash
|
||||
joinClass="btn-primary"
|
||||
leaveClass="btn-flat"
|
||||
joinIcon="sign-in-alt"
|
||||
leaveIcon="sign-out-alt"
|
||||
}}
|
||||
/>
|
||||
</:action>
|
||||
</section.row>
|
||||
</form.section>
|
||||
</ChatForm>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -25,6 +25,91 @@ const REDUCED = "reduced";
|
|||
const REDUCED_WIDTH_THRESHOLD = 500;
|
||||
|
||||
export default class ChatMessageActionsDesktop extends Component {
|
||||
@service chat;
|
||||
@service chatEmojiPickerManager;
|
||||
@service site;
|
||||
|
||||
@tracked size = FULL;
|
||||
|
||||
popper = null;
|
||||
|
||||
get message() {
|
||||
return this.chat.activeMessage.model;
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this.chat.activeMessage.context;
|
||||
}
|
||||
|
||||
get messageInteractor() {
|
||||
return new ChatMessageInteractor(
|
||||
getOwner(this),
|
||||
this.message,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
|
||||
get shouldRenderFavoriteReactions() {
|
||||
return this.size === FULL;
|
||||
}
|
||||
|
||||
@action
|
||||
onWheel() {
|
||||
// prevents menu to stop scroll on the list of messages
|
||||
this.chat.activeMessage = null;
|
||||
}
|
||||
|
||||
@action
|
||||
setup(element) {
|
||||
this.popper?.destroy();
|
||||
|
||||
schedule("afterRender", () => {
|
||||
const messageContainer = chatMessageContainer(
|
||||
this.message.id,
|
||||
this.context
|
||||
);
|
||||
|
||||
if (!messageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = messageContainer.closest(".popper-viewport");
|
||||
this.size =
|
||||
viewport.clientWidth < REDUCED_WIDTH_THRESHOLD ? REDUCED : FULL;
|
||||
|
||||
if (!messageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.popper = createPopper(messageContainer, element, {
|
||||
placement: "top-end",
|
||||
strategy: "fixed",
|
||||
modifiers: [
|
||||
{
|
||||
name: "flip",
|
||||
enabled: true,
|
||||
options: {
|
||||
boundary: viewport,
|
||||
fallbackPlacements: ["bottom-end"],
|
||||
},
|
||||
},
|
||||
{ name: "hide", enabled: true },
|
||||
{ name: "eventListeners", options: { scroll: false } },
|
||||
{
|
||||
name: "offset",
|
||||
options: { offset: [-2, MSG_ACTIONS_VERTICAL_PADDING] },
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
teardown() {
|
||||
this.popper?.destroy();
|
||||
this.popper = null;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if (and this.site.desktopView this.chat.activeMessage.model.persisted)}}
|
||||
|
@ -112,89 +197,4 @@ export default class ChatMessageActionsDesktop extends Component {
|
|||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service chat;
|
||||
@service chatEmojiPickerManager;
|
||||
@service site;
|
||||
|
||||
@tracked size = FULL;
|
||||
|
||||
popper = null;
|
||||
|
||||
get message() {
|
||||
return this.chat.activeMessage.model;
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this.chat.activeMessage.context;
|
||||
}
|
||||
|
||||
get messageInteractor() {
|
||||
return new ChatMessageInteractor(
|
||||
getOwner(this),
|
||||
this.message,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
|
||||
get shouldRenderFavoriteReactions() {
|
||||
return this.size === FULL;
|
||||
}
|
||||
|
||||
@action
|
||||
onWheel() {
|
||||
// prevents menu to stop scroll on the list of messages
|
||||
this.chat.activeMessage = null;
|
||||
}
|
||||
|
||||
@action
|
||||
setup(element) {
|
||||
this.popper?.destroy();
|
||||
|
||||
schedule("afterRender", () => {
|
||||
const messageContainer = chatMessageContainer(
|
||||
this.message.id,
|
||||
this.context
|
||||
);
|
||||
|
||||
if (!messageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = messageContainer.closest(".popper-viewport");
|
||||
this.size =
|
||||
viewport.clientWidth < REDUCED_WIDTH_THRESHOLD ? REDUCED : FULL;
|
||||
|
||||
if (!messageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.popper = createPopper(messageContainer, element, {
|
||||
placement: "top-end",
|
||||
strategy: "fixed",
|
||||
modifiers: [
|
||||
{
|
||||
name: "flip",
|
||||
enabled: true,
|
||||
options: {
|
||||
boundary: viewport,
|
||||
fallbackPlacements: ["bottom-end"],
|
||||
},
|
||||
},
|
||||
{ name: "hide", enabled: true },
|
||||
{ name: "eventListeners", options: { scroll: false } },
|
||||
{
|
||||
name: "offset",
|
||||
options: { offset: [-2, MSG_ACTIONS_VERTICAL_PADDING] },
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
teardown() {
|
||||
this.popper?.destroy();
|
||||
this.popper = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,86 @@ import or from "truth-helpers/helpers/or";
|
|||
import BookmarkIcon from "discourse/components/bookmark-icon";
|
||||
|
||||
export default class ChatMessageActionsMobile extends Component {
|
||||
@service chat;
|
||||
@service site;
|
||||
@service capabilities;
|
||||
|
||||
@tracked hasExpandedReply = false;
|
||||
@tracked showFadeIn = false;
|
||||
|
||||
get message() {
|
||||
return this.chat.activeMessage.model;
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this.chat.activeMessage.context;
|
||||
}
|
||||
|
||||
get messageInteractor() {
|
||||
return new ChatMessageInteractor(
|
||||
getOwner(this),
|
||||
this.message,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
fadeAndVibrate() {
|
||||
discourseLater(this.#addFadeIn.bind(this));
|
||||
|
||||
if (this.capabilities.userHasBeenActive && this.capabilities.canVibrate) {
|
||||
navigator.vibrate(5);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
expandReply(event) {
|
||||
event.stopPropagation();
|
||||
this.hasExpandedReply = true;
|
||||
}
|
||||
|
||||
@action
|
||||
collapseMenu(event) {
|
||||
event.preventDefault();
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
@action
|
||||
actAndCloseMenu(fnId) {
|
||||
this.messageInteractor[fnId]();
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
@action
|
||||
openEmojiPicker(_, event) {
|
||||
this.messageInteractor.openEmojiPicker(_, event);
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
#onCloseMenu() {
|
||||
this.#removeFadeIn();
|
||||
|
||||
// we don't want to remove the component right away as it's animating
|
||||
// 200 is equal to the duration of the css animation
|
||||
discourseLater(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// by ensuring we are not hovering any message anymore
|
||||
// we also ensure the menu is fully removed
|
||||
this.chat.activeMessage = null;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
#addFadeIn() {
|
||||
this.showFadeIn = true;
|
||||
}
|
||||
|
||||
#removeFadeIn() {
|
||||
this.showFadeIn = false;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if (and this.site.mobileView this.chat.activeMessage.model.persisted)}}
|
||||
|
@ -113,84 +193,4 @@ export default class ChatMessageActionsMobile extends Component {
|
|||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service chat;
|
||||
@service site;
|
||||
@service capabilities;
|
||||
|
||||
@tracked hasExpandedReply = false;
|
||||
@tracked showFadeIn = false;
|
||||
|
||||
get message() {
|
||||
return this.chat.activeMessage.model;
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this.chat.activeMessage.context;
|
||||
}
|
||||
|
||||
get messageInteractor() {
|
||||
return new ChatMessageInteractor(
|
||||
getOwner(this),
|
||||
this.message,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
fadeAndVibrate() {
|
||||
discourseLater(this.#addFadeIn.bind(this));
|
||||
|
||||
if (this.capabilities.userHasBeenActive && this.capabilities.canVibrate) {
|
||||
navigator.vibrate(5);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
expandReply(event) {
|
||||
event.stopPropagation();
|
||||
this.hasExpandedReply = true;
|
||||
}
|
||||
|
||||
@action
|
||||
collapseMenu(event) {
|
||||
event.preventDefault();
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
@action
|
||||
actAndCloseMenu(fnId) {
|
||||
this.messageInteractor[fnId]();
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
@action
|
||||
openEmojiPicker(_, event) {
|
||||
this.messageInteractor.openEmojiPicker(_, event);
|
||||
this.#onCloseMenu();
|
||||
}
|
||||
|
||||
#onCloseMenu() {
|
||||
this.#removeFadeIn();
|
||||
|
||||
// we don't want to remove the component right away as it's animating
|
||||
// 200 is equal to the duration of the css animation
|
||||
discourseLater(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// by ensuring we are not hovering any message anymore
|
||||
// we also ensure the menu is fully removed
|
||||
this.chat.activeMessage = null;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
#addFadeIn() {
|
||||
this.showFadeIn = true;
|
||||
}
|
||||
|
||||
#removeFadeIn() {
|
||||
this.showFadeIn = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,38 +11,6 @@ import and from "truth-helpers/helpers/and";
|
|||
import concatClass from "discourse/helpers/concat-class";
|
||||
|
||||
export default class ChatMessageReaction extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if (and @reaction this.emojiUrl)}}
|
||||
<button
|
||||
type="button"
|
||||
tabindex="0"
|
||||
class={{concatClass
|
||||
"chat-message-reaction"
|
||||
(if @reaction.reacted "reacted")
|
||||
(if this.isActive "-active")
|
||||
}}
|
||||
data-emoji-name={{@reaction.emoji}}
|
||||
title={{this.emojiString}}
|
||||
{{on "click" this.handleClick passive=true}}
|
||||
{{this.registerTooltip}}
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
class="emoji"
|
||||
width="20"
|
||||
height="20"
|
||||
alt={{this.emojiString}}
|
||||
src={{this.emojiUrl}}
|
||||
/>
|
||||
|
||||
{{#if (and this.showCount @reaction.count)}}
|
||||
<span class="count">{{@reaction.count}}</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service capabilities;
|
||||
@service currentUser;
|
||||
@service tooltip;
|
||||
|
@ -101,4 +69,36 @@ export default class ChatMessageReaction extends Component {
|
|||
|
||||
return emojiUnescape(getReactionText(this.args.reaction, this.currentUser));
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if (and @reaction this.emojiUrl)}}
|
||||
<button
|
||||
type="button"
|
||||
tabindex="0"
|
||||
class={{concatClass
|
||||
"chat-message-reaction"
|
||||
(if @reaction.reacted "reacted")
|
||||
(if this.isActive "-active")
|
||||
}}
|
||||
data-emoji-name={{@reaction.emoji}}
|
||||
title={{this.emojiString}}
|
||||
{{on "click" this.handleClick passive=true}}
|
||||
{{this.registerTooltip}}
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
class="emoji"
|
||||
width="20"
|
||||
height="20"
|
||||
alt={{this.emojiString}}
|
||||
src={{this.emojiUrl}}
|
||||
/>
|
||||
|
||||
{{#if (and this.showCount @reaction.count)}}
|
||||
<span class="count">{{@reaction.count}}</span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -48,140 +48,6 @@ export const MENTION_KEYWORDS = ["here", "all"];
|
|||
export const MESSAGE_CONTEXT_THREAD = "thread";
|
||||
|
||||
export default class ChatMessage extends Component {
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if this.shouldRender}}
|
||||
{{#if (eq @context "channel")}}
|
||||
<ChatMessageSeparatorDate
|
||||
@fetchMessagesByDate={{@fetchMessagesByDate}}
|
||||
@message={{@message}}
|
||||
/>
|
||||
<ChatMessageSeparatorNew @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-message-container"
|
||||
(if this.pane.selectingMessages "-selectable")
|
||||
(if @message.highlighted "-highlighted")
|
||||
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
||||
(if @message.staged "-staged" "-persisted")
|
||||
(if this.hasActiveState "-active")
|
||||
(if @message.bookmark "-bookmarked")
|
||||
(if @message.deletedAt "-deleted")
|
||||
(if @message.selected "-selected")
|
||||
(if @message.error "-errored")
|
||||
(if this.showThreadIndicator "has-thread-indicator")
|
||||
(if this.hideUserInfo "-user-info-hidden")
|
||||
(if this.hasReply "has-reply")
|
||||
}}
|
||||
data-id={{@message.id}}
|
||||
data-thread-id={{@message.thread.id}}
|
||||
{{didInsert this.didInsertMessage}}
|
||||
{{didUpdate this.didUpdateMessageId @message.id}}
|
||||
{{didUpdate this.didUpdateMessageVersion @message.version}}
|
||||
{{willDestroy this.willDestroyMessage}}
|
||||
{{on "mouseenter" this.onMouseEnter passive=true}}
|
||||
{{on "mouseleave" this.onMouseLeave passive=true}}
|
||||
{{on "mousemove" this.onMouseMove passive=true}}
|
||||
{{this.toggleCheckIfPossible}}
|
||||
{{ChatOnLongPress
|
||||
this.onLongPressStart
|
||||
this.onLongPressEnd
|
||||
this.onLongPressCancel
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
{{#if this.show}}
|
||||
{{#if this.pane.selectingMessages}}
|
||||
<Input
|
||||
@type="checkbox"
|
||||
class="chat-message-selector"
|
||||
@checked={{@message.selected}}
|
||||
{{on "click" this.toggleChecked}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.deletedAndCollapsed}}
|
||||
<div class="chat-message-text -deleted">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@translatedLabel={{this.deletedMessageLabel}}
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else if this.hiddenAndCollapsed}}
|
||||
<div class="chat-message-text -hidden">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@label="chat.hidden"
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="chat-message">
|
||||
{{#unless this.hideReplyToInfo}}
|
||||
<ChatMessageInReplyToIndicator @message={{@message}} />
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.hideUserInfo}}
|
||||
<ChatMessageLeftGutter @message={{@message}} />
|
||||
{{else}}
|
||||
<ChatMessageAvatar @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-message-content">
|
||||
<ChatMessageInfo
|
||||
@message={{@message}}
|
||||
@show={{not this.hideUserInfo}}
|
||||
/>
|
||||
|
||||
<ChatMessageText
|
||||
@cooked={{@message.cooked}}
|
||||
@uploads={{@message.uploads}}
|
||||
@edited={{@message.edited}}
|
||||
>
|
||||
{{#if @message.reactions.length}}
|
||||
<div class="chat-message-reaction-list">
|
||||
{{#each @message.reactions as |reaction|}}
|
||||
<ChatMessageReaction
|
||||
@reaction={{reaction}}
|
||||
@onReaction={{this.messageInteractor.react}}
|
||||
@message={{@message}}
|
||||
@showTooltip={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
{{#if this.shouldRenderOpenEmojiPickerButton}}
|
||||
<DButton
|
||||
@action={{this.messageInteractor.openEmojiPicker}}
|
||||
@icon="discourse-emojis"
|
||||
@title="chat.react"
|
||||
@forwardEvent={{true}}
|
||||
class="chat-message-react-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</ChatMessageText>
|
||||
|
||||
<ChatMessageError
|
||||
@message={{@message}}
|
||||
@onRetry={{@resendStagedMessage}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.showThreadIndicator}}
|
||||
<ChatMessageThreadIndicator @message={{@message}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service site;
|
||||
@service dialog;
|
||||
@service currentUser;
|
||||
|
@ -624,4 +490,138 @@ export default class ChatMessage extends Component {
|
|||
user.off("status-changed", this, "refreshStatusOnMentions");
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
{{#if this.shouldRender}}
|
||||
{{#if (eq @context "channel")}}
|
||||
<ChatMessageSeparatorDate
|
||||
@fetchMessagesByDate={{@fetchMessagesByDate}}
|
||||
@message={{@message}}
|
||||
/>
|
||||
<ChatMessageSeparatorNew @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div
|
||||
class={{concatClass
|
||||
"chat-message-container"
|
||||
(if this.pane.selectingMessages "-selectable")
|
||||
(if @message.highlighted "-highlighted")
|
||||
(if (eq @message.user.id this.currentUser.id) "is-by-current-user")
|
||||
(if @message.staged "-staged" "-persisted")
|
||||
(if this.hasActiveState "-active")
|
||||
(if @message.bookmark "-bookmarked")
|
||||
(if @message.deletedAt "-deleted")
|
||||
(if @message.selected "-selected")
|
||||
(if @message.error "-errored")
|
||||
(if this.showThreadIndicator "has-thread-indicator")
|
||||
(if this.hideUserInfo "-user-info-hidden")
|
||||
(if this.hasReply "has-reply")
|
||||
}}
|
||||
data-id={{@message.id}}
|
||||
data-thread-id={{@message.thread.id}}
|
||||
{{didInsert this.didInsertMessage}}
|
||||
{{didUpdate this.didUpdateMessageId @message.id}}
|
||||
{{didUpdate this.didUpdateMessageVersion @message.version}}
|
||||
{{willDestroy this.willDestroyMessage}}
|
||||
{{on "mouseenter" this.onMouseEnter passive=true}}
|
||||
{{on "mouseleave" this.onMouseLeave passive=true}}
|
||||
{{on "mousemove" this.onMouseMove passive=true}}
|
||||
{{this.toggleCheckIfPossible}}
|
||||
{{ChatOnLongPress
|
||||
this.onLongPressStart
|
||||
this.onLongPressEnd
|
||||
this.onLongPressCancel
|
||||
}}
|
||||
...attributes
|
||||
>
|
||||
{{#if this.show}}
|
||||
{{#if this.pane.selectingMessages}}
|
||||
<Input
|
||||
@type="checkbox"
|
||||
class="chat-message-selector"
|
||||
@checked={{@message.selected}}
|
||||
{{on "click" this.toggleChecked}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.deletedAndCollapsed}}
|
||||
<div class="chat-message-text -deleted">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@translatedLabel={{this.deletedMessageLabel}}
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else if this.hiddenAndCollapsed}}
|
||||
<div class="chat-message-text -hidden">
|
||||
<DButton
|
||||
@action={{this.expand}}
|
||||
@label="chat.hidden"
|
||||
class="btn-flat chat-message-expand"
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="chat-message">
|
||||
{{#unless this.hideReplyToInfo}}
|
||||
<ChatMessageInReplyToIndicator @message={{@message}} />
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.hideUserInfo}}
|
||||
<ChatMessageLeftGutter @message={{@message}} />
|
||||
{{else}}
|
||||
<ChatMessageAvatar @message={{@message}} />
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-message-content">
|
||||
<ChatMessageInfo
|
||||
@message={{@message}}
|
||||
@show={{not this.hideUserInfo}}
|
||||
/>
|
||||
|
||||
<ChatMessageText
|
||||
@cooked={{@message.cooked}}
|
||||
@uploads={{@message.uploads}}
|
||||
@edited={{@message.edited}}
|
||||
>
|
||||
{{#if @message.reactions.length}}
|
||||
<div class="chat-message-reaction-list">
|
||||
{{#each @message.reactions as |reaction|}}
|
||||
<ChatMessageReaction
|
||||
@reaction={{reaction}}
|
||||
@onReaction={{this.messageInteractor.react}}
|
||||
@message={{@message}}
|
||||
@showTooltip={{true}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
{{#if this.shouldRenderOpenEmojiPickerButton}}
|
||||
<DButton
|
||||
@action={{this.messageInteractor.openEmojiPicker}}
|
||||
@icon="discourse-emojis"
|
||||
@title="chat.react"
|
||||
@forwardEvent={{true}}
|
||||
class="chat-message-react-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</ChatMessageText>
|
||||
|
||||
<ChatMessageError
|
||||
@message={{@message}}
|
||||
@onRetry={{@resendStagedMessage}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.showThreadIndicator}}
|
||||
<ChatMessageThreadIndicator @message={{@message}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -3,12 +3,6 @@ import I18n from "I18n";
|
|||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class ChatRetentionReminderText extends Component {
|
||||
<template>
|
||||
<span class="chat-retention-reminder-text">
|
||||
{{this.text}}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@service siteSettings;
|
||||
|
||||
get text() {
|
||||
|
@ -36,4 +30,10 @@ export default class ChatRetentionReminderText extends Component {
|
|||
? this.siteSettings.chat_dm_retention_days
|
||||
: this.siteSettings.chat_channel_retention_days;
|
||||
}
|
||||
|
||||
<template>
|
||||
<span class="chat-retention-reminder-text">
|
||||
{{this.text}}
|
||||
</span>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -5,25 +5,6 @@ import { htmlSafe } from "@ember/template";
|
|||
import concatClass from "discourse/helpers/concat-class";
|
||||
|
||||
export default class ChatUserAvatar extends Component {
|
||||
<template>
|
||||
<div
|
||||
class={{concatClass "chat-user-avatar" (if this.isOnline "is-online")}}
|
||||
data-username={{@user.username}}
|
||||
>
|
||||
{{#if this.interactive}}
|
||||
<div
|
||||
role="button"
|
||||
class="chat-user-avatar__container clickable"
|
||||
data-user-card={{@user.username}}
|
||||
>
|
||||
{{this.avatar}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{this.avatar}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service chat;
|
||||
|
||||
get avatar() {
|
||||
|
@ -55,4 +36,23 @@ export default class ChatUserAvatar extends Component {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class={{concatClass "chat-user-avatar" (if this.isOnline "is-online")}}
|
||||
data-username={{@user.username}}
|
||||
>
|
||||
{{#if this.interactive}}
|
||||
<div
|
||||
role="button"
|
||||
class="chat-user-avatar__container clickable"
|
||||
data-user-card={{@user.username}}
|
||||
>
|
||||
{{this.avatar}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{this.avatar}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ import Component from "@glimmer/component";
|
|||
import ChatFormRow from "discourse/plugins/chat/discourse/components/chat/form/row";
|
||||
|
||||
export default class ChatFormSection extends Component {
|
||||
get yieldableArgs() {
|
||||
return { row: ChatFormRow };
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="chat-form__section" ...attributes>
|
||||
{{#if @title}}
|
||||
|
@ -15,8 +19,4 @@ export default class ChatFormSection extends Component {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
get yieldableArgs() {
|
||||
return { row: ChatFormRow };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,24 +8,6 @@ import concatClass from "discourse/helpers/concat-class";
|
|||
import I18n from "I18n";
|
||||
|
||||
export default class ChatHeaderIcon extends Component {
|
||||
<template>
|
||||
<a
|
||||
href={{this.href}}
|
||||
tabindex="0"
|
||||
class={{concatClass "icon" "btn-flat" (if this.isActive "active")}}
|
||||
title={{this.title}}
|
||||
>
|
||||
{{~icon this.icon~}}
|
||||
{{#if this.showUnreadIndicator}}
|
||||
<ChatHeaderIconUnreadIndicator
|
||||
@urgentCount={{@urgentCount}}
|
||||
@unreadCount={{@unreadCount}}
|
||||
@indicatorPreference={{@indicatorPreference}}
|
||||
/>
|
||||
{{/if}}
|
||||
</a>
|
||||
</template>
|
||||
|
||||
@service currentUser;
|
||||
@service site;
|
||||
@service chatStateManager;
|
||||
|
@ -96,4 +78,22 @@ export default class ChatHeaderIcon extends Component {
|
|||
|
||||
return getURL(this.chatStateManager.lastKnownChatURL || "/chat");
|
||||
}
|
||||
|
||||
<template>
|
||||
<a
|
||||
href={{this.href}}
|
||||
tabindex="0"
|
||||
class={{concatClass "icon" "btn-flat" (if this.isActive "active")}}
|
||||
title={{this.title}}
|
||||
>
|
||||
{{~icon this.icon~}}
|
||||
{{#if this.showUnreadIndicator}}
|
||||
<ChatHeaderIconUnreadIndicator
|
||||
@urgentCount={{@urgentCount}}
|
||||
@unreadCount={{@unreadCount}}
|
||||
@indicatorPreference={{@indicatorPreference}}
|
||||
/>
|
||||
{{/if}}
|
||||
</a>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -9,18 +9,6 @@ import {
|
|||
const MAX_UNREAD_COUNT = 99;
|
||||
|
||||
export default class ChatHeaderIconUnreadIndicator extends Component {
|
||||
<template>
|
||||
{{#if this.showUrgentIndicator}}
|
||||
<div class="chat-channel-unread-indicator -urgent">
|
||||
<div class="chat-channel-unread-indicator__number">
|
||||
{{this.unreadCountLabel}}
|
||||
</div>
|
||||
</div>
|
||||
{{else if this.showUnreadIndicator}}
|
||||
<div class="chat-channel-unread-indicator"></div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@service chatTrackingStateManager;
|
||||
@service currentUser;
|
||||
|
||||
|
@ -78,4 +66,16 @@ export default class ChatHeaderIconUnreadIndicator extends Component {
|
|||
|
||||
return preferences.includes(this.indicatorPreference);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showUrgentIndicator}}
|
||||
<div class="chat-channel-unread-indicator -urgent">
|
||||
<div class="chat-channel-unread-indicator__number">
|
||||
{{this.unreadCountLabel}}
|
||||
</div>
|
||||
</div>
|
||||
{{else if this.showUnreadIndicator}}
|
||||
<div class="chat-channel-unread-indicator"></div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -12,49 +12,6 @@ import not from "truth-helpers/helpers/not";
|
|||
import I18n from "I18n";
|
||||
|
||||
export default class ChatSelectionManager extends Component {
|
||||
<template>
|
||||
<div
|
||||
class="chat-selection-management"
|
||||
data-last-copy-successful={{this.lastCopySuccessful}}
|
||||
>
|
||||
<div class="chat-selection-management__buttons">
|
||||
<DButton
|
||||
@icon="quote-left"
|
||||
@label="chat.selection.quote_selection"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.quoteMessages}}
|
||||
id="chat-quote-btn"
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@icon="copy"
|
||||
@label="chat.selection.copy"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.copyMessages}}
|
||||
id="chat-copy-btn"
|
||||
/>
|
||||
|
||||
{{#if this.enableMove}}
|
||||
<DButton
|
||||
@icon="sign-out-alt"
|
||||
@label="chat.selection.move_selection_to_channel"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.openMoveMessageModal}}
|
||||
id="chat-move-to-channel-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<DButton
|
||||
@icon="times"
|
||||
@label="chat.selection.cancel"
|
||||
@action={{@pane.cancelSelecting}}
|
||||
id="chat-cancel-selection-btn"
|
||||
class="btn-secondary cancel-btn"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@service("composer") topicComposer;
|
||||
@service router;
|
||||
@service modal;
|
||||
|
@ -150,4 +107,47 @@ export default class ChatSelectionManager extends Component {
|
|||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="chat-selection-management"
|
||||
data-last-copy-successful={{this.lastCopySuccessful}}
|
||||
>
|
||||
<div class="chat-selection-management__buttons">
|
||||
<DButton
|
||||
@icon="quote-left"
|
||||
@label="chat.selection.quote_selection"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.quoteMessages}}
|
||||
id="chat-quote-btn"
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@icon="copy"
|
||||
@label="chat.selection.copy"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.copyMessages}}
|
||||
id="chat-copy-btn"
|
||||
/>
|
||||
|
||||
{{#if this.enableMove}}
|
||||
<DButton
|
||||
@icon="sign-out-alt"
|
||||
@label="chat.selection.move_selection_to_channel"
|
||||
@disabled={{not this.anyMessagesSelected}}
|
||||
@action={{this.openMoveMessageModal}}
|
||||
id="chat-move-to-channel-btn"
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<DButton
|
||||
@icon="times"
|
||||
@label="chat.selection.cancel"
|
||||
@action={{@pane.cancelSelecting}}
|
||||
id="chat-cancel-selection-btn"
|
||||
class="btn-secondary cancel-btn"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -8,6 +8,25 @@ import { modifier } from "ember-modifier";
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class DcFilterInput extends Component {
|
||||
@tracked isFocused = false;
|
||||
|
||||
focusState = modifier((element) => {
|
||||
const focusInHandler = () => {
|
||||
this.isFocused = true;
|
||||
};
|
||||
const focusOutHandler = () => {
|
||||
this.isFocused = false;
|
||||
};
|
||||
|
||||
element.addEventListener("focusin", focusInHandler);
|
||||
element.addEventListener("focusout", focusOutHandler);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener("focusin", focusInHandler);
|
||||
element.removeEventListener("focusout", focusOutHandler);
|
||||
};
|
||||
});
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable modifier-name-case }}
|
||||
<div
|
||||
|
@ -36,23 +55,4 @@ export default class DcFilterInput extends Component {
|
|||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@tracked isFocused = false;
|
||||
|
||||
focusState = modifier((element) => {
|
||||
const focusInHandler = () => {
|
||||
this.isFocused = true;
|
||||
};
|
||||
const focusOutHandler = () => {
|
||||
this.isFocused = false;
|
||||
};
|
||||
|
||||
element.addEventListener("focusin", focusInHandler);
|
||||
element.addEventListener("focusout", focusOutHandler);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener("focusin", focusInHandler);
|
||||
element.removeEventListener("focusout", focusOutHandler);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue