diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 76916e60..ee3d7a54 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @dessalines @SleeplessOne1917 @alectrocute +* @dessalines @SleeplessOne1917 @alectrocute @jsit diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index a43a5a55..a0b16005 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -1,32 +1,29 @@ -name: "\U0001F41E Bug Report" -description: Create a report to help us improve lemmy-ui -title: "[Bug]: " +name: "\U0001F41E Bug report" +description: Report a bug to help us improve Lemmy-UI. labels: ["bug", "triage"] body: - type: markdown attributes: value: | - Found a bug? Please fill out the sections below. 👍 - Thanks for taking the time to fill out this bug report! - For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy/issues/new/choose) + Thanks for taking the time to help improve Lemmy-UI by reporting a bug! - type: checkboxes attributes: label: Requirements - description: Before you create a bug report please do the following. + description: Before you create a bug report, please carefully check the following – options: - - label: Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support + - label: This is a bug report, and if not, please post to https://lemmy.ml/c/lemmy_support instead. required: true - - label: Did you check to see if this issue already exists? + - label: Please [check](https://github.com/LemmyNet/lemmy-ui/issues) to see if this issue already exists. required: true - - label: Is this only a single bug? Do not put multiple bugs in one issue. + - label: It's a single bug. Do not report multiple bugs in one issue. + required: true + - label: It's a frontend issue, not a backend issue; Otherwise please create an issue on the [backend repo](https://github.com/LemmyNet/lemmy) instead. required: true - - label: Is this a server side (not related to the UI) issue? Use the [Lemmy back end](https://github.com/LemmyNet/lemmy) repo. - required: false - type: textarea id: summary attributes: label: Summary - description: A summary of the bug. + description: Explain the bug and upload images, screenshots or videos if possible. validations: required: true - type: textarea @@ -34,12 +31,13 @@ body: attributes: label: Steps to Reproduce description: | - Describe the steps to reproduce the bug. - The better your description is _(go 'here', click 'there'...)_ the fastest you'll get an _(accurate)_ resolution. + In a numbered list, walk us through the steps needed to reproduce the bug. + The better your description is _(go 'here', click 'there'...)_, the quicker we can fix it. value: | 1. 2. 3. + 4. validations: required: true - type: textarea @@ -47,20 +45,21 @@ body: attributes: label: Technical Details description: | - - Any browser console errors? + Describe your environment (OS, browser, model of smartphone, etc) + If relevant, also share any console errors and/or screenshots here. validations: required: true - type: input id: lemmy-ui-version attributes: - label: Version - description: Which Lemmy UI version do you use? Displayed in the footer. - placeholder: ex. 0.17.4-rc.4 + label: Lemmy Instance Version + description: What's the version of the Lemmy instance where the bug can be reproduced? + placeholder: ex. 0.18-rc.6 validations: required: true - type: input id: lemmy-instance attributes: label: Lemmy Instance URL - description: Which Lemmy instance do you use? The address - placeholder: lemmy.ml, lemmy.world, etc + description: What's the URL of the Lemmy instance where the bug can be reproduced? + placeholder: https://lemmy.ml diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml index 2d656819..ac7d8dc6 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml @@ -1,54 +1,27 @@ name: "\U0001F680 Feature request" -description: Suggest an idea for improving Lemmy's UI +description: Suggest an idea for Lemmy-UI. labels: ["enhancement"] body: - type: markdown attributes: value: | - Have a suggestion about Lemmy's UI? - For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy/issues/new/choose) + Thanks for taking the time to help improve Lemmy-UI by suggesting a feature! - type: checkboxes attributes: label: Requirements - description: Before you create a feature request please do the following. + description: Before you create a feature request, please carefully check the following – options: - - label: Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support + - label: This is a feature request and not a bug report. Otherwise, please create a new [bug report](https://github.com/LemmyNet/lemmy-ui/issues/new?assignees=&labels=bug%2Ctriage&projects=&template=BUG_REPORT.yml) instead. required: true - - label: Did you check to see if this issue already exists? + - label: Please [check](https://github.com/LemmyNet/lemmy-ui/issues) to see if this request (or a similar one) already exists. required: true - - label: Is this only a feature request? Do not put multiple feature requests in one issue. + - label: It's a single feature. Please don't request multiple features in one issue. required: true - - label: Is this a server side (not related to the UI) issue? Use the [Lemmy back end](https://github.com/LemmyNet/lemmy) repo. - required: false - - type: textarea - id: problem - attributes: - label: Is your proposal related to a problem? - description: | - Provide a clear and concise description of what the problem is. - For example, "I'm always frustrated when..." - validations: - required: true - type: textarea id: solution attributes: - label: Describe the solution you'd like. + label: Describe the feature you'd like description: | - Provide a clear and concise description of what you want to happen. + Provide a clear and concise description of the feature. Explain why it's needed. validations: required: true - - type: textarea - id: alternatives - attributes: - label: Describe alternatives you've considered. - description: | - Let us know about other solutions you've tried or researched. - validations: - required: true - - type: textarea - id: context - attributes: - label: Additional context - description: | - Is there anything else you can add about the proposal? - You might want to link to related issues here, if you haven't already. diff --git a/.github/ISSUE_TEMPLATE/QUESTION.yml b/.github/ISSUE_TEMPLATE/QUESTION.yml deleted file mode 100644 index 734937e9..00000000 --- a/.github/ISSUE_TEMPLATE/QUESTION.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: "? Question" -description: General questions about Lemmy -title: "Question: " -labels: ["question", "triage"] -body: - - type: markdown - attributes: - value: | - Have a question about Lemmy's UI? - Please check the docs first: https://join-lemmy.org/docs/en/index.html - - type: textarea - id: question - attributes: - label: Question - description: What's the question you have about Lemmy's UI? - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..59085700 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://lemmy.ml/c/lemmy_support + about: Please ask and answer general questions here. + - name: Technical Discussion + url: https://github.com/LemmyNet/lemmy-ui/discussions + about: Please discuss technical topics with other contributors here. diff --git a/.github/ISSUE_TEMPLATE/hexbear.yml b/.github/ISSUE_TEMPLATE/hexbear.yml deleted file mode 100644 index 73ef5482..00000000 --- a/.github/ISSUE_TEMPLATE/hexbear.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: "Hexbear" -description: For hexbear issues -labels: ["hexbear", "triage"] -body: - - type: textarea - id: question - attributes: - label: Question - description: What's the question you have about hexbear? - validations: - required: true diff --git a/package.json b/package.json index aab8ecb7..e40307ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lemmy-ui", - "version": "0.18.0-rc.6", + "version": "0.18.0-rc.7", "description": "An isomorphic UI for lemmy", "repository": "https://github.com/LemmyNet/lemmy-ui", "license": "AGPL-3.0", diff --git a/src/shared/components/home/home.tsx b/src/shared/components/home/home.tsx index 4a84664b..bad771fc 100644 --- a/src/shared/components/home/home.tsx +++ b/src/shared/components/home/home.tsx @@ -642,7 +642,7 @@ export class Home extends Component { const siteRes = this.state.siteRes; if (dataType === DataType.Post) { - switch (this.state.postsRes.state) { + switch (this.state.postsRes?.state) { case "loading": return (
diff --git a/src/shared/components/modlog.tsx b/src/shared/components/modlog.tsx index dd4f56fa..0d5afbaa 100644 --- a/src/shared/components/modlog.tsx +++ b/src/shared/components/modlog.tsx @@ -691,6 +691,10 @@ export class Modlog extends Component< } } + async componentDidMount() { + await this.refetch(); + } + get combined() { const res = this.state.res; const combined = res.state == "success" ? buildCombined(res.data) : []; diff --git a/src/shared/components/person/inbox.tsx b/src/shared/components/person/inbox.tsx index 062fc01c..395875be 100644 --- a/src/shared/components/person/inbox.tsx +++ b/src/shared/components/person/inbox.tsx @@ -449,7 +449,6 @@ export class Inbox extends Component { ]} viewType={CommentViewType.Flat} finished={this.state.finished} - noIndent markable showCommunity showContext @@ -489,7 +488,6 @@ export class Inbox extends Component { ]} finished={this.state.finished} viewType={CommentViewType.Flat} - noIndent markable showCommunity showContext @@ -567,7 +565,6 @@ export class Inbox extends Component { nodes={commentsToFlatNodes(replies)} viewType={CommentViewType.Flat} finished={this.state.finished} - noIndent markable showCommunity showContext @@ -617,7 +614,6 @@ export class Inbox extends Component { nodes={[{ comment_view: umv, children: [], depth: 0 }]} viewType={CommentViewType.Flat} finished={this.state.finished} - noIndent markable showCommunity showContext diff --git a/src/shared/components/person/person-details.tsx b/src/shared/components/person/person-details.tsx index 3771b844..b2b74b6e 100644 --- a/src/shared/components/person/person-details.tsx +++ b/src/shared/components/person/person-details.tsx @@ -145,7 +145,6 @@ export class PersonDetails extends Component { finished={this.props.finished} admins={this.props.admins} noBorder - noIndent showCommunity showContext enableDownvotes={this.props.enableDownvotes} diff --git a/src/shared/components/post/post-listing.tsx b/src/shared/components/post/post-listing.tsx index 93b8fff7..d5ddc2f2 100644 --- a/src/shared/components/post/post-listing.tsx +++ b/src/shared/components/post/post-listing.tsx @@ -381,33 +381,30 @@ export class PostListing extends Component { createdLine() { const post_view = this.postView; return ( -
    -
  • - - - {this.creatorIsMod_ && ( - - {I18NextService.i18n.t("mod")} - - )} - {this.creatorIsAdmin_ && ( - - {I18NextService.i18n.t("admin")} - - )} - {post_view.creator.bot_account && ( - - {I18NextService.i18n.t("bot_account").toLowerCase()} - - )} - {this.props.showCommunity && ( - <> - {" "} - {I18NextService.i18n.t("to")}{" "} - - - )} -
  • + + + {this.creatorIsMod_ && ( + + {I18NextService.i18n.t("mod")} + + )} + {this.creatorIsAdmin_ && ( + + {I18NextService.i18n.t("admin")} + + )} + {post_view.creator.bot_account && ( + + {I18NextService.i18n.t("bot_account").toLowerCase()} + + )} + {this.props.showCommunity && ( + <> + {" "} + {I18NextService.i18n.t("to")}{" "} + + + )} {post_view.post.language_id !== 0 && ( { @@ -416,17 +413,13 @@ export class PostListing extends Component { )?.name } - )} -
  • -
  • - - - -
  • -
+ )}{" "} + •{" "} + + ); } @@ -767,10 +760,8 @@ export class PostListing extends Component { to={`/post/${post_view.post.id}?scrollToComments=true`} data-tippy-content={title} > - - - {post_view.counts.comments} - + + {post_view.counts.comments} {this.unreadCount && ( ({this.unreadCount} {I18NextService.i18n.t("new")}) @@ -1104,7 +1095,7 @@ export class PostListing extends Component { const post_view = this.postView; return ( this.state.showAdvanced && ( - <> +
{this.canMod_ && ( <> {!this.creatorIsMod_ && @@ -1265,7 +1256,7 @@ export class PostListing extends Component { )} )} - +
) ); } @@ -1460,11 +1451,11 @@ export class PostListing extends Component { ); } - showMobilePreview() { + showBodyPreview() { const { body, id } = this.postView.post; return !this.showBody && body ? ( - +
{body}
) : ( @@ -1485,7 +1476,7 @@ export class PostListing extends Component { {this.mobileThumbnail()} {/* Show a preview of the post body */} - {this.showMobilePreview()} + {this.showBodyPreview()} {this.commentsLine(true)} {this.userActionsLine()} @@ -1507,6 +1498,7 @@ export class PostListing extends Component {
{this.postTitleLine()} {this.createdLine()} + {this.showBodyPreview()} {this.commentsLine()} {this.duplicatesLine()} {this.userActionsLine()} diff --git a/src/shared/config.ts b/src/shared/config.ts index 28e8ce51..c56c64b0 100644 --- a/src/shared/config.ts +++ b/src/shared/config.ts @@ -25,4 +25,14 @@ export const fetchLimit = 40; export const relTags = "noopener nofollow"; export const emDash = "\u2014"; +/** + * Accepted formats: + * !community@server.com + * /c/community@server.com + * /m/community@server.com + * /u/username@server.com + */ +export const instanceLinkRegex = + /(\/[cmu]\/|!)[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g; + export const testHost = "0.0.0.0:8536"; diff --git a/src/shared/markdown.ts b/src/shared/markdown.ts index 8f4d5c23..9f1ec733 100644 --- a/src/shared/markdown.ts +++ b/src/shared/markdown.ts @@ -14,6 +14,7 @@ import markdown_it_sub from "markdown-it-sub"; import markdown_it_sup from "markdown-it-sup"; import Renderer from "markdown-it/lib/renderer"; import Token from "markdown-it/lib/token"; +import { instanceLinkRegex } from "./config"; export let Tribute: any; @@ -72,6 +73,75 @@ const html5EmbedConfig = { }, }; +function localInstanceLinkParser(md: MarkdownIt) { + md.core.ruler.push("replace-text", state => { + for (let i = 0; i < state.tokens.length; i++) { + if (state.tokens[i].type !== "inline") { + continue; + } + const inlineTokens: Token[] = state.tokens[i].children || []; + for (let j = inlineTokens.length - 1; j >= 0; j--) { + if ( + inlineTokens[j].type === "text" && + new RegExp(instanceLinkRegex).test(inlineTokens[j].content) + ) { + const text = inlineTokens[j].content; + const matches = Array.from(text.matchAll(instanceLinkRegex)); + + let lastIndex = 0; + const newTokens: Token[] = []; + + let linkClass = "community-link"; + + for (const match of matches) { + // If there is plain text before the match, add it as a separate token + if (match.index !== undefined && match.index > lastIndex) { + const textToken = new state.Token("text", "", 0); + textToken.content = text.slice(lastIndex, match.index); + newTokens.push(textToken); + } + + let href; + if (match[0].startsWith("!")) { + href = "/c/" + match[0].substring(1); + } else if (match[0].startsWith("/m/")) { + href = "/c/" + match[0].substring(3); + } else { + href = match[0]; + if (match[0].startsWith("/u/")) { + linkClass = "user-link"; + } + } + + const linkOpenToken = new state.Token("link_open", "a", 1); + linkOpenToken.attrs = [ + ["href", href], + ["class", linkClass], + ]; + const textToken = new state.Token("text", "", 0); + textToken.content = match[0]; + const linkCloseToken = new state.Token("link_close", "a", -1); + + newTokens.push(linkOpenToken, textToken, linkCloseToken); + + lastIndex = + (match.index !== undefined ? match.index : 0) + match[0].length; + } + + // If there is plain text after the last match, add it as a separate token + if (lastIndex < text.length) { + const textToken = new state.Token("text", "", 0); + textToken.content = text.slice(lastIndex); + newTokens.push(textToken); + } + + inlineTokens.splice(j, 1, ...newTokens); + } + } + } + }); +} + export function setupMarkdown() { const markdownItConfig: MarkdownIt.Options = { html: false, @@ -88,7 +158,8 @@ export function setupMarkdown() { .use(markdown_it_sup) .use(markdown_it_footnote) .use(markdown_it_html5_embed, html5EmbedConfig) - .use(markdown_it_container, "spoiler", spoilerConfig); + .use(markdown_it_container, "spoiler", spoilerConfig) + .use(localInstanceLinkParser); // .use(markdown_it_emoji, { // defs: emojiDefs, // }); @@ -99,6 +170,7 @@ export function setupMarkdown() { .use(markdown_it_footnote) .use(markdown_it_html5_embed, html5EmbedConfig) .use(markdown_it_container, "spoiler", spoilerConfig) + .use(localInstanceLinkParser) // .use(markdown_it_emoji, { // defs: emojiDefs, // })