13 } from "@utils/helpers";
14 import type { QueryParams } from "@utils/types";
15 import { RouteDataResponse } from "@utils/types";
16 import { Component, linkEvent } from "inferno";
21 ListCommunitiesResponse,
23 } from "lemmy-js-client";
24 import { InitialFetchRequest } from "../../interfaces";
25 import { FirstLoadService, I18NextService } from "../../services";
26 import { HttpService, RequestState } from "../../services/HttpService";
27 import { HtmlTags } from "../common/html-tags";
28 import { Spinner } from "../common/icon";
29 import { ListingTypeSelect } from "../common/listing-type-select";
30 import { Paginator } from "../common/paginator";
31 import { CommunityLink } from "./community-link";
33 const communityLimit = 50;
35 type CommunitiesData = RouteDataResponse<{
36 listCommunitiesResponse: ListCommunitiesResponse;
39 interface CommunitiesState {
40 listCommunitiesResponse: RequestState<ListCommunitiesResponse>;
41 siteRes: GetSiteResponse;
43 isIsomorphic: boolean;
46 interface CommunitiesProps {
47 listingType: ListingType;
51 function getListingTypeFromQuery(listingType?: string): ListingType {
52 return listingType ? (listingType as ListingType) : "Local";
55 export class Communities extends Component<any, CommunitiesState> {
56 private isoData = setIsoData<CommunitiesData>(this.context);
57 state: CommunitiesState = {
58 listCommunitiesResponse: { state: "empty" },
59 siteRes: this.isoData.site_res,
64 constructor(props: any, context: any) {
65 super(props, context);
66 this.handlePageChange = this.handlePageChange.bind(this);
67 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
69 // Only fetch the data if coming from another route
70 if (FirstLoadService.isFirstLoad) {
71 const { listCommunitiesResponse } = this.isoData.routeData;
75 listCommunitiesResponse,
81 async componentDidMount() {
82 if (!this.state.isIsomorphic) {
87 get documentTitle(): string {
88 return `${I18NextService.i18n.t("communities")} - ${
89 this.state.siteRes.site_view.site.name
94 switch (this.state.listCommunitiesResponse.state) {
102 const { listingType, page } = this.getCommunitiesQueryParams();
105 <h1 className="h4 mb-4">
106 {I18NextService.i18n.t("list_of_communities")}
108 <div className="row g-2 justify-content-between">
109 <div className="col-auto">
112 showLocal={showLocal(this.isoData)}
114 onChange={this.handleListingTypeChange}
117 <div className="col-auto">{this.searchForm()}</div>
120 <div className="table-responsive">
123 className="table table-sm table-hover"
125 <thead className="pointer">
127 <th>{I18NextService.i18n.t("name")}</th>
128 <th className="text-right">
129 {I18NextService.i18n.t("subscribers")}
131 <th className="text-right">
132 {I18NextService.i18n.t("users")} /{" "}
133 {I18NextService.i18n.t("month")}
135 <th className="text-right d-none d-lg-table-cell">
136 {I18NextService.i18n.t("posts")}
138 <th className="text-right d-none d-lg-table-cell">
139 {I18NextService.i18n.t("comments")}
145 {this.state.listCommunitiesResponse.data.communities.map(
147 <tr key={cv.community.id}>
149 <CommunityLink community={cv.community} />
151 <td className="text-right">
152 {numToSI(cv.counts.subscribers)}
154 <td className="text-right">
155 {numToSI(cv.counts.users_active_month)}
157 <td className="text-right d-none d-lg-table-cell">
158 {numToSI(cv.counts.posts)}
160 <td className="text-right d-none d-lg-table-cell">
161 {numToSI(cv.counts.comments)}
163 <td className="text-right">
164 {cv.subscribed == "Subscribed" && (
166 className="btn btn-link d-inline-block"
170 communityId: cv.community.id,
176 {I18NextService.i18n.t("unsubscribe")}
179 {cv.subscribed === "NotSubscribed" && (
181 className="btn btn-link d-inline-block"
185 communityId: cv.community.id,
191 {I18NextService.i18n.t("subscribe")}
194 {cv.subscribed === "Pending" && (
195 <div className="text-warning d-inline-block">
196 {I18NextService.i18n.t("subscribe_pending")}
206 <Paginator page={page} onChange={this.handlePageChange} />
215 <div className="communities container-lg">
217 title={this.documentTitle}
218 path={this.context.router.route.match.url}
220 {this.renderListings()}
229 onSubmit={linkEvent(this, this.handleSearchSubmit)}
231 <div className="col-auto">
234 id="communities-search"
235 className="form-control"
236 value={this.state.searchText}
237 placeholder={`${I18NextService.i18n.t("search")}...`}
238 onInput={linkEvent(this, this.handleSearchChange)}
243 <div className="col-auto">
244 <label className="visually-hidden" htmlFor="communities-search">
245 {I18NextService.i18n.t("search")}
247 <button type="submit" className="btn btn-secondary">
248 <span>{I18NextService.i18n.t("search")}</span>
255 async updateUrl({ listingType, page }: Partial<CommunitiesProps>) {
256 const { listingType: urlListingType, page: urlPage } =
257 this.getCommunitiesQueryParams();
259 const queryParams: QueryParams<CommunitiesProps> = {
260 listingType: listingType ?? urlListingType,
261 page: (page ?? urlPage)?.toString(),
264 this.props.history.push(`/communities${getQueryString(queryParams)}`);
266 await this.refetch();
269 handlePageChange(page: number) {
270 this.updateUrl({ page });
273 handleListingTypeChange(val: ListingType) {
280 handleSearchChange(i: Communities, event: any) {
281 i.setState({ searchText: event.target.value });
284 handleSearchSubmit(i: Communities, event: any) {
285 event.preventDefault();
286 const searchParamEncoded = encodeURIComponent(i.state.searchText);
287 i.context.router.history.push(
288 `/search?q=${searchParamEncoded}&type=Communities`
292 static async fetchInitialData({
293 query: { listingType, page },
296 }: InitialFetchRequest<
297 QueryParams<CommunitiesProps>
298 >): Promise<CommunitiesData> {
299 const listCommunitiesForm: ListCommunities = {
300 type_: getListingTypeFromQuery(listingType),
302 limit: communityLimit,
303 page: getPageFromString(page),
308 listCommunitiesResponse: await client.listCommunities(
314 getCommunitiesQueryParams() {
315 return getQueryParams<CommunitiesProps>({
316 listingType: getListingTypeFromQuery,
317 page: getPageFromString,
321 async handleFollow(data: {
326 const res = await HttpService.client.followCommunity({
327 community_id: data.communityId,
329 auth: myAuthRequired(),
331 data.i.findAndUpdateCommunity(res);
335 this.setState({ listCommunitiesResponse: { state: "loading" } });
337 const { listingType, page } = this.getCommunitiesQueryParams();
340 listCommunitiesResponse: await HttpService.client.listCommunities({
343 limit: communityLimit,
349 window.scrollTo(0, 0);
352 findAndUpdateCommunity(res: RequestState<CommunityResponse>) {
355 s.listCommunitiesResponse.state == "success" &&
356 res.state == "success"
358 s.listCommunitiesResponse.data.communities = editCommunity(
359 res.data.community_view,
360 s.listCommunitiesResponse.data.communities