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/package.json b/package.json index 2298d9e1..b7c48c79 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "inferno-server": "^8.1.1", "isomorphic-cookie": "^1.2.4", "jwt-decode": "^3.1.2", - "lemmy-js-client": "0.17.2-rc.24", + "lemmy-js-client": "0.18.0-rc.1", "lodash": "^4.17.21", "markdown-it": "^13.0.1", "markdown-it-container": "^3.0.0", 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/shared/components/common/html-tags.tsx b/src/shared/components/common/html-tags.tsx index 0e6cb2d0..f32b0fc0 100644 --- a/src/shared/components/common/html-tags.tsx +++ b/src/shared/components/common/html-tags.tsx @@ -2,7 +2,8 @@ import { htmlToText } from "html-to-text"; import { Component } from "inferno"; import { Helmet } from "inferno-helmet"; import { httpExternalPath } from "../../env"; -import { getLanguages, md } from "../../utils"; +import { i18n } from "../../i18next"; +import { md } from "../../utils"; interface HtmlTagsProps { title: string; @@ -17,11 +18,10 @@ export class HtmlTags extends Component { 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 c1e85243..4e1bca11 100644 --- a/src/shared/components/common/markdown-textarea.tsx +++ b/src/shared/components/common/markdown-textarea.tsx @@ -183,53 +183,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)} @@ -282,6 +235,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 cb7e9517..58b330f4 100644 --- a/src/shared/components/community/community.tsx +++ b/src/shared/components/community/community.tsx @@ -359,7 +359,6 @@ export class Community extends Component< community_view={res.community_view} moderators={res.moderators} admins={site_res.admins} - online={res.online} enableNsfw={enableNsfw(site_res)} editable allLanguages={site_res.all_languages} @@ -646,6 +645,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 2774747b..592a06ff 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, @@ -38,7 +39,6 @@ interface SidebarProps { allLanguages: Language[]; siteLanguages: number[]; communityLanguages?: number[]; - online: number; enableNsfw?: boolean; showIcon?: boolean; editable?: boolean; @@ -63,7 +63,6 @@ interface SidebarState { removeCommunityLoading: boolean; leaveModTeamLoading: boolean; followCommunityLoading: boolean; - blockCommunityLoading: boolean; purgeCommunityLoading: boolean; } @@ -77,7 +76,6 @@ export class Sidebar extends Component { removeCommunityLoading: false, leaveModTeamLoading: false, followCommunityLoading: false, - blockCommunityLoading: false, purgeCommunityLoading: false, }; @@ -104,7 +102,6 @@ export class Sidebar extends Component { removeCommunityLoading: false, leaveModTeamLoading: false, followCommunityLoading: false, - blockCommunityLoading: false, purgeCommunityLoading: false, }); } @@ -144,10 +141,15 @@ export class Sidebar extends Component { {myUSerInfo && this.blockCommunity()} {!myUSerInfo && (
- {i18n.t("community_not_logged_in_alert", { - community: name, - instance: hostname(actor_id), - })} + + ### +
)} @@ -234,12 +236,6 @@ export class Sidebar extends Component { const counts = community_view.counts; return (
    -
  • - {i18n.t("number_online", { - count: this.props.online, - formattedCount: numToSI(this.props.online), - })} -
  • { } 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 +641,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")} > { siteRes: { site_view: { counts, site }, admins, - online, }, showSubscribedMobile, showTrendingMobile, @@ -393,7 +392,6 @@ export class Home extends Component { site={site} admins={admins} counts={counts} - online={online} showLocal={showLocal(this.isoData)} /> )} @@ -417,7 +415,6 @@ export class Home extends Component { siteRes: { site_view: { counts, site }, admins, - online, }, } = this.state; @@ -443,7 +440,6 @@ export class Home extends Component { site={site} admins={admins} counts={counts} - online={online} showLocal={showLocal(this.isoData)} /> {this.hasFollows && ( 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<