Merge branch 'main' into update-issue-template

This commit is contained in:
Alec Armbruster 2023-06-22 18:07:09 -04:00 committed by GitHub
commit 11da1152ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 169 additions and 78 deletions

2
.github/CODEOWNERS vendored
View file

@ -1 +1 @@
* @dessalines @SleeplessOne1917 @alectrocute * @dessalines @SleeplessOne1917 @alectrocute @jsit

View file

@ -239,25 +239,40 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
} }
get img() { get img() {
return this.imageSrc ? ( if (this.imageSrc) {
<> return (
<div className="offset-sm-3 my-2 d-none d-sm-block"> <>
<a href={this.imageSrc} className="d-inline-block"> <div className="offset-sm-3 my-2 d-none d-sm-block">
<PictrsImage src={this.imageSrc} /> <a href={this.imageSrc} className="d-inline-block">
</a> <PictrsImage src={this.imageSrc} />
</a>
</div>
<div className="my-2 d-block d-sm-none">
<a
className="d-inline-block"
onClick={linkEvent(this, this.handleImageExpandClick)}
>
<PictrsImage src={this.imageSrc} />
</a>
</div>
</>
);
}
const { post } = this.postView;
const { url } = post;
if (url && isVideo(url)) {
return (
<div className="embed-responsive mt-3">
<video muted controls className="embed-responsive-item col-12">
<source src={url} type="video/mp4" />
</video>
</div> </div>
<div className="my-2 d-block d-sm-none"> );
<a }
className="d-inline-block"
onClick={linkEvent(this, this.handleImageExpandClick)} return <></>;
>
<PictrsImage src={this.imageSrc} />
</a>
</div>
</>
) : (
<></>
);
} }
imgThumb(src: string) { imgThumb(src: string) {
@ -325,17 +340,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
} else if (url) { } else if (url) {
if (!this.props.hideImage && isVideo(url)) { if (!this.props.hideImage && isVideo(url)) {
return ( return (
<div className="embed-responsive embed-responsive-16by9"> <a
<video className="text-body"
playsInline href={url}
muted title={url}
loop rel={relTags}
controls data-tippy-content={I18NextService.i18n.t("expand_here")}
className="embed-responsive-item" onClick={linkEvent(this, this.handleImageExpandClick)}
> aria-label={I18NextService.i18n.t("expand_here")}
<source src={url} type="video/mp4" /> >
</video> <div className="thumbnail rounded bg-light d-flex justify-content-center">
</div> <Icon icon="play" classes="d-flex align-items-center" />
</div>
</a>
); );
} else { } else {
return ( return (
@ -364,33 +381,30 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
createdLine() { createdLine() {
const post_view = this.postView; const post_view = this.postView;
return ( return (
<ul className="list-inline mb-1 text-muted small mt-2"> <span className="small">
<li className="list-inline-item"> <PersonListing person={post_view.creator} />
<PersonListing person={post_view.creator} /> {this.creatorIsMod_ && (
<span className="mx-1 badge text-bg-light">
{this.creatorIsMod_ && ( {I18NextService.i18n.t("mod")}
<span className="mx-1 badge text-bg-light"> </span>
{I18NextService.i18n.t("mod")} )}
</span> {this.creatorIsAdmin_ && (
)} <span className="mx-1 badge text-bg-light">
{this.creatorIsAdmin_ && ( {I18NextService.i18n.t("admin")}
<span className="mx-1 badge text-bg-light"> </span>
{I18NextService.i18n.t("admin")} )}
</span> {post_view.creator.bot_account && (
)} <span className="mx-1 badge text-bg-light">
{post_view.creator.bot_account && ( {I18NextService.i18n.t("bot_account").toLowerCase()}
<span className="mx-1 badge text-bg-light"> </span>
{I18NextService.i18n.t("bot_account").toLowerCase()} )}
</span> {this.props.showCommunity && (
)} <>
{this.props.showCommunity && ( {" "}
<> {I18NextService.i18n.t("to")}{" "}
{" "} <CommunityLink community={post_view.community} />
{I18NextService.i18n.t("to")}{" "} </>
<CommunityLink community={post_view.community} /> )}
</>
)}
</li>
{post_view.post.language_id !== 0 && ( {post_view.post.language_id !== 0 && (
<span className="mx-1 badge text-bg-light"> <span className="mx-1 badge text-bg-light">
{ {
@ -399,17 +413,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
)?.name )?.name
} }
</span> </span>
)} )}{" "}
<li className="list-inline-item"></li> {" "}
<li className="list-inline-item"> <MomentTime
<span> published={post_view.post.published}
<MomentTime updated={post_view.post.updated}
published={post_view.post.published} />
updated={post_view.post.updated} </span>
/>
</span>
</li>
</ul>
); );
} }
@ -750,10 +760,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
to={`/post/${post_view.post.id}?scrollToComments=true`} to={`/post/${post_view.post.id}?scrollToComments=true`}
data-tippy-content={title} data-tippy-content={title}
> >
<span className="me-1"> <Icon icon="message-square" classes="me-1" inline />
<Icon icon="message-square" classes="me-1" inline /> {post_view.counts.comments}
{post_view.counts.comments}
</span>
{this.unreadCount && ( {this.unreadCount && (
<span className="text-muted fst-italic"> <span className="text-muted fst-italic">
({this.unreadCount} {I18NextService.i18n.t("new")}) ({this.unreadCount} {I18NextService.i18n.t("new")})
@ -1087,7 +1095,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
const post_view = this.postView; const post_view = this.postView;
return ( return (
this.state.showAdvanced && ( this.state.showAdvanced && (
<> <div className="mt-3">
{this.canMod_ && ( {this.canMod_ && (
<> <>
{!this.creatorIsMod_ && {!this.creatorIsMod_ &&
@ -1248,7 +1256,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
)} )}
</> </>
)} )}
</> </div>
) )
); );
} }
@ -1443,11 +1451,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
); );
} }
showMobilePreview() { showBodyPreview() {
const { body, id } = this.postView.post; const { body, id } = this.postView.post;
return !this.showBody && body ? ( return !this.showBody && body ? (
<Link className="text-body" to={`/post/${id}`}> <Link className="text-body mt-2 d-block" to={`/post/${id}`}>
<div className="md-div mb-1 preview-lines">{body}</div> <div className="md-div mb-1 preview-lines">{body}</div>
</Link> </Link>
) : ( ) : (
@ -1468,7 +1476,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{this.mobileThumbnail()} {this.mobileThumbnail()}
{/* Show a preview of the post body */} {/* Show a preview of the post body */}
{this.showMobilePreview()} {this.showBodyPreview()}
{this.commentsLine(true)} {this.commentsLine(true)}
{this.userActionsLine()} {this.userActionsLine()}
@ -1490,6 +1498,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div className="col-12"> <div className="col-12">
{this.postTitleLine()} {this.postTitleLine()}
{this.createdLine()} {this.createdLine()}
{this.showBodyPreview()}
{this.commentsLine()} {this.commentsLine()}
{this.duplicatesLine()} {this.duplicatesLine()}
{this.userActionsLine()} {this.userActionsLine()}

View file

@ -25,4 +25,14 @@ export const fetchLimit = 40;
export const relTags = "noopener nofollow"; export const relTags = "noopener nofollow";
export const emDash = "\u2014"; 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"; export const testHost = "0.0.0.0:8536";

View file

@ -14,6 +14,7 @@ import markdown_it_sub from "markdown-it-sub";
import markdown_it_sup from "markdown-it-sup"; import markdown_it_sup from "markdown-it-sup";
import Renderer from "markdown-it/lib/renderer"; import Renderer from "markdown-it/lib/renderer";
import Token from "markdown-it/lib/token"; import Token from "markdown-it/lib/token";
import { instanceLinkRegex } from "./config";
export let Tribute: any; 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() { export function setupMarkdown() {
const markdownItConfig: MarkdownIt.Options = { const markdownItConfig: MarkdownIt.Options = {
html: false, html: false,
@ -88,7 +158,8 @@ export function setupMarkdown() {
.use(markdown_it_sup) .use(markdown_it_sup)
.use(markdown_it_footnote) .use(markdown_it_footnote)
.use(markdown_it_html5_embed, html5EmbedConfig) .use(markdown_it_html5_embed, html5EmbedConfig)
.use(markdown_it_container, "spoiler", spoilerConfig); .use(markdown_it_container, "spoiler", spoilerConfig)
.use(localInstanceLinkParser);
// .use(markdown_it_emoji, { // .use(markdown_it_emoji, {
// defs: emojiDefs, // defs: emojiDefs,
// }); // });
@ -99,6 +170,7 @@ export function setupMarkdown() {
.use(markdown_it_footnote) .use(markdown_it_footnote)
.use(markdown_it_html5_embed, html5EmbedConfig) .use(markdown_it_html5_embed, html5EmbedConfig)
.use(markdown_it_container, "spoiler", spoilerConfig) .use(markdown_it_container, "spoiler", spoilerConfig)
.use(localInstanceLinkParser)
// .use(markdown_it_emoji, { // .use(markdown_it_emoji, {
// defs: emojiDefs, // defs: emojiDefs,
// }) // })