diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6df17d57..76916e60 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @dessalines @SleeplessOne1917 +* @dessalines @SleeplessOne1917 @alectrocute diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index 2273a138..ae2d4e51 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -21,7 +21,7 @@ body: - label: Is this only a single bug? Do not put multiple bugs 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: true + required: false - type: textarea id: summary attributes: diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml index 2f6f3fc1..3c75050a 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml @@ -19,7 +19,7 @@ body: - label: Is this only a feature request? Do not put multiple feature requests 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: true + required: false - type: textarea id: problem attributes: diff --git a/Dockerfile b/Dockerfile index 3d6d6212..2b36581d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:alpine as builder +FROM node:20.2-alpine as builder RUN apk update && apk add curl yarn python3 build-base gcc wget git --no-cache RUN curl -sf https://gobinaries.com/tj/node-prune | sh diff --git a/dev.dockerfile b/dev.dockerfile index 0e925c0a..3bfc10da 100644 --- a/dev.dockerfile +++ b/dev.dockerfile @@ -1,4 +1,4 @@ -FROM node:alpine as builder +FROM node:20.2-alpine as builder RUN apk update && apk add curl yarn python3 build-base gcc wget git --no-cache WORKDIR /usr/src/app diff --git a/src/assets/css/main.css b/src/assets/css/main.css index e1adfc53..82f8433e 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -80,30 +80,6 @@ overflow-x: auto; } -.md-div table { - border-collapse: collapse; - width: 100%; - margin-bottom: 1rem; - border: 1px solid var(--dark); -} - -.md-div table th, -.md-div table td { - padding: 0.3rem; - vertical-align: top; - border-top: 1px solid var(--dark); - border: 1px solid var(--dark); -} - -.md-div table thead th { - vertical-align: bottom; - border-bottom: 2px solid var(--dark); -} - -.md-div table tbody + tbody { - border-top: 2px solid var(--dark); -} - .vote-bar { margin-top: -6.5px; } diff --git a/src/server/index.tsx b/src/server/index.tsx index 06dc33a4..3a12ad7e 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -421,7 +421,7 @@ async function createSsrHtml(root: string, isoData: IsoDataOptionalSite) { - + { const url = httpExternalPath(this.props.path); const desc = this.props.description; const image = this.props.image; - const lang = getLanguages()[0]; return ( - + {["title", "og:title", "twitter:title"].map(t => ( diff --git a/src/shared/components/common/markdown-textarea.tsx b/src/shared/components/common/markdown-textarea.tsx index 9318d3bb..a4459ac0 100644 --- a/src/shared/components/common/markdown-textarea.tsx +++ b/src/shared/components/common/markdown-textarea.tsx @@ -184,53 +184,6 @@ export class MarkdownTextArea extends Component<
- {this.props.buttonTitle && ( - - )} - {this.props.replyType && ( - - )} - {this.state.content && ( - - )} - {/* A flex expander */} -
- - {this.props.showLanguage && ( - - )} {this.getFormatButton("bold", this.handleInsertBold)} {this.getFormatButton("italic", this.handleInsertItalic)} {this.getFormatButton("link", this.handleInsertLink)} @@ -283,6 +236,57 @@ export class MarkdownTextArea extends Component<
+ +
+ {this.props.showLanguage && ( + + )} + + {/* A flex expander */} +
+ + {this.props.buttonTitle && ( + + )} + {this.props.replyType && ( + + )} + {this.state.content && ( + + )} +
); diff --git a/src/shared/components/common/moment-time.tsx b/src/shared/components/common/moment-time.tsx index 10714f5b..30c1682c 100644 --- a/src/shared/components/common/moment-time.tsx +++ b/src/shared/components/common/moment-time.tsx @@ -1,7 +1,7 @@ import { Component } from "inferno"; import moment from "moment"; import { i18n } from "../../i18next"; -import { capitalizeFirstLetter, getLanguages } from "../../utils"; +import { capitalizeFirstLetter } from "../../utils"; import { Icon } from "./icon"; interface MomentTimeProps { @@ -15,9 +15,7 @@ export class MomentTime extends Component { constructor(props: any, context: any) { super(props, context); - const lang = getLanguages(); - - moment.locale(lang); + moment.locale([...i18n.languages]); } createdAndModifiedTimes() { diff --git a/src/shared/components/community/community.tsx b/src/shared/components/community/community.tsx index 7dc150f3..05fa55e1 100644 --- a/src/shared/components/community/community.tsx +++ b/src/shared/components/community/community.tsx @@ -647,6 +647,12 @@ export class Community extends Component< const blockCommunityRes = await HttpService.client.blockCommunity(form); if (blockCommunityRes.state == "success") { updateCommunityBlock(blockCommunityRes.data); + this.setState(s => { + if (s.communityRes.state == "success") { + s.communityRes.data.community_view.blocked = + blockCommunityRes.data.blocked; + } + }); } } diff --git a/src/shared/components/community/sidebar.tsx b/src/shared/components/community/sidebar.tsx index a5c620f3..57400f48 100644 --- a/src/shared/components/community/sidebar.tsx +++ b/src/shared/components/community/sidebar.tsx @@ -1,4 +1,5 @@ import { Component, InfernoNode, linkEvent } from "inferno"; +import { T } from "inferno-i18next-dess"; import { Link } from "inferno-router"; import { AddModToCommunity, @@ -63,7 +64,6 @@ interface SidebarState { removeCommunityLoading: boolean; leaveModTeamLoading: boolean; followCommunityLoading: boolean; - blockCommunityLoading: boolean; purgeCommunityLoading: boolean; } @@ -77,7 +77,6 @@ export class Sidebar extends Component { removeCommunityLoading: false, leaveModTeamLoading: false, followCommunityLoading: false, - blockCommunityLoading: false, purgeCommunityLoading: false, }; @@ -104,7 +103,6 @@ export class Sidebar extends Component { removeCommunityLoading: false, leaveModTeamLoading: false, followCommunityLoading: false, - blockCommunityLoading: false, purgeCommunityLoading: false, }); } @@ -144,10 +142,15 @@ export class Sidebar extends Component { {myUSerInfo && this.blockCommunity()} {!myUSerInfo && (
- {i18n.t("community_not_logged_in_alert", { - community: name, - instance: hostname(actor_id), - })} + + ### +
)} @@ -370,35 +373,18 @@ export class Sidebar extends Component { } blockCommunity() { - const community_view = this.props.community_view; - const blocked = this.props.community_view.blocked; + const { subscribed, blocked } = this.props.community_view; return (
- {community_view.subscribed == "NotSubscribed" && - (blocked ? ( - - ) : ( - - ))} + {subscribed == "NotSubscribed" && ( + + )}
); } @@ -662,10 +648,11 @@ export class Sidebar extends Component { } handleBlockCommunity(i: Sidebar) { - i.setState({ blockCommunityLoading: true }); + const { community, blocked } = i.props.community_view; + i.props.onBlockCommunity({ - community_id: 0, - block: !i.props.community_view.blocked, + community_id: community.id, + block: !blocked, auth: myAuthRequired(), }); } diff --git a/src/shared/components/home/admin-settings.tsx b/src/shared/components/home/admin-settings.tsx index 9b7256d0..302e96bd 100644 --- a/src/shared/components/home/admin-settings.tsx +++ b/src/shared/components/home/admin-settings.tsx @@ -39,6 +39,8 @@ interface AdminSettingsState { instancesRes: RequestState; bannedRes: RequestState; leaveAdminTeamRes: RequestState; + emojiLoading: boolean; + loading: boolean; themeList: string[]; isIsomorphic: boolean; } @@ -52,6 +54,8 @@ export class AdminSettings extends Component { bannedRes: { state: "empty" }, instancesRes: { state: "empty" }, leaveAdminTeamRes: { state: "empty" }, + emojiLoading: false, + loading: false, themeList: [], isIsomorphic: false, }; @@ -81,6 +85,7 @@ export class AdminSettings extends Component { bannedRes: { state: "loading" }, instancesRes: { state: "loading" }, themeList: [], + loading: true, }); const auth = myAuthRequired(); @@ -95,6 +100,7 @@ export class AdminSettings extends Component { bannedRes, instancesRes, themeList, + loading: false, }); } @@ -156,6 +162,7 @@ export class AdminSettings extends Component { onSaveSite={this.handleEditSite} siteRes={this.state.siteRes} themeList={this.state.themeList} + loading={this.state.loading} />
@@ -174,6 +181,7 @@ export class AdminSettings extends Component { this.state.siteRes.site_view.local_site_rate_limit } onSaveSite={this.handleEditSite} + loading={this.state.loading} /> ), }, @@ -185,6 +193,7 @@ export class AdminSettings extends Component {
), @@ -198,6 +207,7 @@ export class AdminSettings extends Component { onCreate={this.handleCreateEmoji} onDelete={this.handleDeleteEmoji} onEdit={this.handleEditEmoji} + loading={this.state.emojiLoading} /> ), @@ -266,6 +276,8 @@ export class AdminSettings extends Component { } async handleEditSite(form: EditSite) { + this.setState({ loading: true }); + const editRes = await HttpService.client.editSite(form); if (editRes.state === "success") { @@ -278,6 +290,8 @@ export class AdminSettings extends Component { toast(i18n.t("site_saved")); } + this.setState({ loading: false }); + return editRes; } @@ -300,23 +314,35 @@ 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 171b7c99..f77f5125 100644 --- a/src/shared/components/home/emojis-form.tsx +++ b/src/shared/components/home/emojis-form.tsx @@ -23,12 +23,12 @@ interface EmojiFormProps { onEdit(form: EditCustomEmoji): void; onCreate(form: CreateCustomEmoji): void; onDelete(form: DeleteCustomEmoji): void; + loading: boolean; } interface EmojiFormState { siteRes: GetSiteResponse; customEmojis: CustomEmojiViewForm[]; - loading: boolean; page: number; } @@ -47,7 +47,6 @@ export class EmojiForm extends Component { private isoData = setIsoData(this.context); private itemsPerPage = 15; private emptyState: EmojiFormState = { - loading: false, siteRes: this.isoData.site_res, customEmojis: this.isoData.site_res.custom_emojis.map((x, index) => ({ id: x.custom_emoji.id, @@ -223,7 +222,7 @@ export class EmojiForm extends Component { data-tippy-content={i18n.t("save")} aria-label={i18n.t("save")} disabled={ - this.state.loading || + this.props.loading || !this.canEdit(cv) || !cv.changed } @@ -243,7 +242,7 @@ export class EmojiForm extends Component { )} data-tippy-content={i18n.t("delete")} aria-label={i18n.t("delete")} - disabled={this.state.loading} + disabled={this.props.loading} title={i18n.t("delete")} > { } async componentDidMount() { - if (!this.state.isIsomorphic) { + if (!this.state.isIsomorphic || !this.isoData.routeData.length) { await Promise.all([this.fetchTrendingCommunities(), this.fetchData()]); } + setupTippy(); } @@ -456,7 +457,7 @@ export class Home extends Component { } trendingCommunities(isMobile = false) { - switch (this.state.trendingCommunitiesRes.state) { + switch (this.state.trendingCommunitiesRes?.state) { case "loading": return (
@@ -573,7 +574,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/home/login.tsx b/src/shared/components/home/login.tsx index 94915542..381c13bb 100644 --- a/src/shared/components/home/login.tsx +++ b/src/shared/components/home/login.tsx @@ -186,7 +186,9 @@ export class Login extends Component { UserService.Instance.myUserInfo = site.data.my_user; } - i.props.history.replace("/"); + i.props.history.action === "PUSH" + ? i.props.history.back() + : i.props.history.replace("/"); break; } diff --git a/src/shared/components/home/rate-limit-form.tsx b/src/shared/components/home/rate-limit-form.tsx index 74ed18e3..0ce01260 100644 --- a/src/shared/components/home/rate-limit-form.tsx +++ b/src/shared/components/home/rate-limit-form.tsx @@ -24,6 +24,7 @@ interface RateLimitsProps { interface RateLimitFormProps { rateLimits: LocalSiteRateLimit; onSaveSite(form: EditSite): void; + loading: boolean; } interface RateLimitFormState { @@ -41,7 +42,6 @@ interface RateLimitFormState { register?: number; register_per_second?: number; }; - loading: boolean; } function RateLimits({ @@ -117,7 +117,6 @@ function submitRateLimitForm(i: RateLimitsForm, event: any) { } ); - i.setState({ loading: true }); i.props.onSaveSite(form); } @@ -126,7 +125,6 @@ export default class RateLimitsForm extends Component< RateLimitFormState > { state: RateLimitFormState = { - loading: false, form: this.props.rateLimits, }; constructor(props: RateLimitFormProps, context: any) { @@ -164,9 +162,9 @@ export default class RateLimitsForm extends Component<