diff --git a/.eslintrc.json b/.eslintrc.json index 598b7f3f..f82e6b06 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -26,9 +26,7 @@ "jsx-a11y/aria-activedescendant-has-tabindex": 1, "jsx-a11y/aria-role": 1, "jsx-a11y/click-events-have-key-events": 1, - "jsx-a11y/iframe-has-title": 1, "jsx-a11y/interactive-supports-focus": 1, - "jsx-a11y/no-redundant-roles": 1, "jsx-a11y/no-static-element-interactions": 1, "jsx-a11y/role-has-required-aria-props": 1, "curly": 0, diff --git a/src/assets/css/main.css b/src/assets/css/main.css index d61dafda..6c739549 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -124,7 +124,8 @@ .emoji-picker-container { position: absolute; - top: 30px; + top: 0; + left: 50%; z-index: 1000; transform: translateX(-50%); } diff --git a/src/shared/components/app/app.tsx b/src/shared/components/app/app.tsx index 96bf1016..3e334376 100644 --- a/src/shared/components/app/app.tsx +++ b/src/shared/components/app/app.tsx @@ -33,12 +33,13 @@ export class App extends Component { <>
- - ${I18NextService.i18n.t("jump_to_content", "Jump to content")} - + {I18NextService.i18n.t("jump_to_content", "Jump to content")} + {siteView && ( )} diff --git a/src/shared/components/app/navbar.tsx b/src/shared/components/app/navbar.tsx index 2ede00e1..11cfb6c6 100644 --- a/src/shared/components/app/navbar.tsx +++ b/src/shared/components/app/navbar.tsx @@ -347,10 +347,10 @@ export class Navbar extends Component { )} {person && ( - + )} ) : ( diff --git a/src/shared/components/common/emoji-picker.tsx b/src/shared/components/common/emoji-picker.tsx index ba36d755..1385ce8f 100644 --- a/src/shared/components/common/emoji-picker.tsx +++ b/src/shared/components/common/emoji-picker.tsx @@ -12,6 +12,10 @@ interface EmojiPickerState { showPicker: boolean; } +function closeEmojiMartOnEsc(i, event): void { + event.key === "Escape" && i.setState({ showPicker: false }); +} + export class EmojiPicker extends Component { private emptyState: EmojiPickerState = { showPicker: false, @@ -23,6 +27,7 @@ export class EmojiPicker extends Component { this.state = this.emptyState; this.handleEmojiClick = this.handleEmojiClick.bind(this); } + render() { return ( @@ -38,8 +43,8 @@ export class EmojiPicker extends Component { {this.state.showPicker && ( <> -
-
+
+
{ ); } + componentWillUnmount() { + document.removeEventListener("keyup", e => closeEmojiMartOnEsc(this, e)); + } + togglePicker(i: EmojiPicker, e: any) { e.preventDefault(); i.setState({ showPicker: !i.state.showPicker }); + + i.state.showPicker + ? document.addEventListener("keyup", e => closeEmojiMartOnEsc(i, e)) + : document.removeEventListener("keyup", e => closeEmojiMartOnEsc(i, e)); } handleEmojiClick(e: any) { diff --git a/src/shared/components/common/icon.tsx b/src/shared/components/common/icon.tsx index 7aedee71..5b6ddf81 100644 --- a/src/shared/components/common/icon.tsx +++ b/src/shared/components/common/icon.tsx @@ -35,6 +35,7 @@ export class Icon extends Component { interface SpinnerProps { large?: boolean; + className?: string; } export class Spinner extends Component { @@ -46,7 +47,9 @@ export class Spinner extends Component { return ( ); } diff --git a/src/shared/components/common/markdown-textarea.tsx b/src/shared/components/common/markdown-textarea.tsx index 9cfee553..8963d5e9 100644 --- a/src/shared/components/common/markdown-textarea.tsx +++ b/src/shared/components/common/markdown-textarea.tsx @@ -23,15 +23,28 @@ import NavigationPrompt from "./navigation-prompt"; import ProgressBar from "./progress-bar"; interface MarkdownTextAreaProps { + /** + * Initial content inside the textarea + */ initialContent?: string; + /** + * Numerical ID of the language to select in dropdown + */ initialLanguageId?: number; placeholder?: string; buttonTitle?: string; maxLength?: number; + /** + * Whether this form is for a reply to a Private Message. + * If true, a "Cancel" button is shown that will close the reply. + */ replyType?: boolean; focus?: boolean; disabled?: boolean; finished?: boolean; + /** + * Whether to show the language selector + */ showLanguage?: boolean; hideNavigationWarnings?: boolean; onContentChange?(val: string): void; @@ -276,19 +289,6 @@ export class MarkdownTextArea extends Component< {/* A flex expander */}
- {this.props.buttonTitle && ( - - )} {this.props.replyType && ( + {this.props.buttonTitle && ( )}
diff --git a/src/shared/components/home/admin-settings.tsx b/src/shared/components/home/admin-settings.tsx index 7ac69fed..76877d84 100644 --- a/src/shared/components/home/admin-settings.tsx +++ b/src/shared/components/home/admin-settings.tsx @@ -44,7 +44,6 @@ interface AdminSettingsState { instancesRes: RequestState; bannedRes: RequestState; leaveAdminTeamRes: RequestState; - emojiLoading: boolean; loading: boolean; themeList: string[]; isIsomorphic: boolean; @@ -59,7 +58,6 @@ export class AdminSettings extends Component { bannedRes: { state: "empty" }, instancesRes: { state: "empty" }, leaveAdminTeamRes: { state: "empty" }, - emojiLoading: false, loading: false, themeList: [], isIsomorphic: false, @@ -215,7 +213,6 @@ export class AdminSettings extends Component { onCreate={this.handleCreateEmoji} onDelete={this.handleDeleteEmoji} onEdit={this.handleEditEmoji} - loading={this.state.emojiLoading} />
@@ -345,35 +342,23 @@ export class AdminSettings extends Component { } async handleEditEmoji(form: EditCustomEmoji) { - this.setState({ emojiLoading: true }); - const res = await HttpService.client.editCustomEmoji(form); if (res.state === "success") { updateEmojiDataModel(res.data.custom_emoji); } - - this.setState({ emojiLoading: false }); } async handleDeleteEmoji(form: DeleteCustomEmoji) { - this.setState({ emojiLoading: true }); - const res = await HttpService.client.deleteCustomEmoji(form); if (res.state === "success") { removeFromEmojiDataModel(res.data.id); } - - this.setState({ emojiLoading: false }); } async handleCreateEmoji(form: CreateCustomEmoji) { - this.setState({ emojiLoading: true }); - const res = await HttpService.client.createCustomEmoji(form); if (res.state === "success") { updateEmojiDataModel(res.data.custom_emoji); } - - this.setState({ emojiLoading: false }); } } diff --git a/src/shared/components/home/emojis-form.tsx b/src/shared/components/home/emojis-form.tsx index e0fa6807..caf8221c 100644 --- a/src/shared/components/home/emojis-form.tsx +++ b/src/shared/components/home/emojis-form.tsx @@ -1,4 +1,5 @@ import { myAuthRequired, setIsoData } from "@utils/app"; +import { capitalizeFirstLetter } from "@utils/helpers"; import { Component, linkEvent } from "inferno"; import { CreateCustomEmoji, @@ -11,14 +12,13 @@ import { HttpService, I18NextService } from "../../services"; import { pictrsDeleteToast, toast } from "../../toast"; import { EmojiMart } from "../common/emoji-mart"; import { HtmlTags } from "../common/html-tags"; -import { Icon } from "../common/icon"; +import { Icon, Spinner } from "../common/icon"; import { Paginator } from "../common/paginator"; interface EmojiFormProps { onEdit(form: EditCustomEmoji): void; onCreate(form: CreateCustomEmoji): void; onDelete(form: DeleteCustomEmoji): void; - loading: boolean; } interface EmojiFormState { @@ -36,6 +36,7 @@ interface CustomEmojiViewForm { keywords: string; changed: boolean; page: number; + loading: boolean; } export class EmojiForm extends Component { @@ -52,6 +53,7 @@ export class EmojiForm extends Component { keywords: x.keywords.map(x => x.keyword).join(" "), changed: false, page: 1 + Math.floor(index / this.itemsPerPage), + loading: false, })), page: 1, }; @@ -119,33 +121,39 @@ export class EmojiForm extends Component { .map((cv, index) => ( (this.scrollRef[cv.shortcode] = e)}> - - + {cv.image_url.length > 0 && ( + {cv.alt_text} + )} + {cv.image_url.length === 0 && ( +
+ +
+ )} { -
    +
      {!this.myPost ? ( <>
    • {this.reportButton}
    • diff --git a/src/shared/components/private_message/create-private-message.tsx b/src/shared/components/private_message/create-private-message.tsx index 8afd3488..840a4425 100644 --- a/src/shared/components/private_message/create-private-message.tsx +++ b/src/shared/components/private_message/create-private-message.tsx @@ -115,7 +115,9 @@ export class CreatePrivateMessage extends Component< return (
      -
      {I18NextService.i18n.t("create_private_message")}
      +

      + {I18NextService.i18n.t("create_private_message")} +

      +
      {!this.props.privateMessageView && ( -
      +
      -
      +
      )} +
      + + + # + + # + + +
      { + this.handlePrivateMessageSubmit(this, event); + }} initialContent={this.state.content} onContentChange={this.handleContentChange} allLanguages={[]} siteLanguages={[]} hideNavigationWarnings + onReplyCancel={() => this.handleCancel(this)} + replyType={this.props.replyType} + buttonTitle={ + this.props.privateMessageView + ? capitalizeFirstLetter(I18NextService.i18n.t("save")) + : capitalizeFirstLetter(I18NextService.i18n.t("send_message")) + } />
      - - {this.state.showDisclaimer && ( -
      -
      -
      - - # - - # - - -
      -
      -
      - )} -
      -
      - - {this.props.privateMessageView && ( - - )} -
        -
      • -
      -
      -
      ); } @@ -200,8 +161,4 @@ export class PrivateMessageForm extends Component< event.preventDefault(); i.setState({ previewMode: !i.state.previewMode }); } - - handleShowDisclaimer(i: PrivateMessageForm) { - i.setState({ showDisclaimer: !i.state.showDisclaimer }); - } } diff --git a/src/shared/components/private_message/private-message.tsx b/src/shared/components/private_message/private-message.tsx index 74f60901..b31e1cdb 100644 --- a/src/shared/components/private_message/private-message.tsx +++ b/src/shared/components/private_message/private-message.tsx @@ -145,6 +145,7 @@ export class PrivateMessage extends Component< <>