1 import { Component, linkEvent } from "inferno";
7 ListCommunitiesResponse,
14 } from "lemmy-js-client";
15 import { Subscription } from "rxjs";
16 import { InitialFetchRequest } from "shared/interfaces";
17 import { i18n } from "../../i18next";
18 import { WebSocketService } from "../../services";
27 routeListingTypeToEnum,
34 import { HtmlTags } from "../common/html-tags";
35 import { Spinner } from "../common/icon";
36 import { ListingTypeSelect } from "../common/listing-type-select";
37 import { Paginator } from "../common/paginator";
38 import { CommunityLink } from "./community-link";
40 const communityLimit = 50;
42 interface CommunitiesState {
43 listCommunitiesResponse?: ListCommunitiesResponse;
45 siteRes: GetSiteResponse;
49 interface CommunitiesProps {
50 listingType: ListingType;
54 function getCommunitiesQueryParams() {
55 return getQueryParams<CommunitiesProps>({
56 listingType: getListingTypeFromQuery,
57 page: getPageFromString,
61 function getListingTypeFromQuery(listingType?: string): ListingType {
62 return routeListingTypeToEnum(listingType ?? "", ListingType.Local);
65 function toggleSubscribe(community_id: number, follow: boolean) {
66 const auth = myAuth();
68 const form: FollowCommunity = {
74 WebSocketService.Instance.send(wsClient.followCommunity(form));
79 const { listingType, page } = getCommunitiesQueryParams();
81 const listCommunitiesForm: ListCommunities = {
83 sort: SortType.TopMonth,
84 limit: communityLimit,
89 WebSocketService.Instance.send(wsClient.listCommunities(listCommunitiesForm));
92 export class Communities extends Component<any, CommunitiesState> {
93 private subscription?: Subscription;
94 private isoData = setIsoData(this.context);
95 state: CommunitiesState = {
97 siteRes: this.isoData.site_res,
101 constructor(props: any, context: any) {
102 super(props, context);
103 this.handlePageChange = this.handlePageChange.bind(this);
104 this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
106 this.parseMessage = this.parseMessage.bind(this);
107 this.subscription = wsSubscribe(this.parseMessage);
109 // Only fetch the data if coming from another route
110 if (this.isoData.path === this.context.router.route.match.url) {
111 const listRes = this.isoData.routeData[0] as ListCommunitiesResponse;
114 listCommunitiesResponse: listRes,
122 componentWillUnmount() {
124 this.subscription?.unsubscribe();
128 get documentTitle(): string {
129 return `${i18n.t("communities")} - ${
130 this.state.siteRes.site_view.site.name
135 const { listingType, page } = getCommunitiesQueryParams();
138 <div className="container-lg">
140 title={this.documentTitle}
141 path={this.context.router.route.match.url}
143 {this.state.loading ? (
149 <div className="row">
150 <div className="col-md-6">
151 <h4>{i18n.t("list_of_communities")}</h4>
152 <span className="mb-2">
155 showLocal={showLocal(this.isoData)}
157 onChange={this.handleListingTypeChange}
161 <div className="col-md-6">
162 <div className="float-md-right">{this.searchForm()}</div>
166 <div className="table-responsive">
169 className="table table-sm table-hover"
171 <thead className="pointer">
173 <th>{i18n.t("name")}</th>
174 <th className="text-right">{i18n.t("subscribers")}</th>
175 <th className="text-right">
176 {i18n.t("users")} / {i18n.t("month")}
178 <th className="text-right d-none d-lg-table-cell">
181 <th className="text-right d-none d-lg-table-cell">
188 {this.state.listCommunitiesResponse?.communities.map(cv => (
189 <tr key={cv.community.id}>
191 <CommunityLink community={cv.community} />
193 <td className="text-right">
194 {numToSI(cv.counts.subscribers)}
196 <td className="text-right">
197 {numToSI(cv.counts.users_active_month)}
199 <td className="text-right d-none d-lg-table-cell">
200 {numToSI(cv.counts.posts)}
202 <td className="text-right d-none d-lg-table-cell">
203 {numToSI(cv.counts.comments)}
205 <td className="text-right">
206 {cv.subscribed == SubscribedType.Subscribed && (
208 className="btn btn-link d-inline-block"
211 this.handleUnsubscribe
214 {i18n.t("unsubscribe")}
217 {cv.subscribed === SubscribedType.NotSubscribed && (
219 className="btn btn-link d-inline-block"
225 {i18n.t("subscribe")}
228 {cv.subscribed === SubscribedType.Pending && (
229 <div className="text-warning d-inline-block">
230 {i18n.t("subscribe_pending")}
239 <Paginator page={page} onChange={this.handlePageChange} />
249 className="form-inline"
250 onSubmit={linkEvent(this, this.handleSearchSubmit)}
254 id="communities-search"
255 className="form-control mr-2 mb-2"
256 value={this.state.searchText}
257 placeholder={`${i18n.t("search")}...`}
258 onInput={linkEvent(this, this.handleSearchChange)}
262 <label className="sr-only" htmlFor="communities-search">
265 <button type="submit" className="btn btn-secondary mr-2 mb-2">
266 <span>{i18n.t("search")}</span>
272 updateUrl({ listingType, page }: Partial<CommunitiesProps>) {
273 const { listingType: urlListingType, page: urlPage } =
274 getCommunitiesQueryParams();
276 const queryParams: QueryParams<CommunitiesProps> = {
277 listingType: listingType ?? urlListingType,
278 page: (page ?? urlPage)?.toString(),
281 this.props.history.push(`/communities${getQueryString(queryParams)}`);
286 handlePageChange(page: number) {
287 this.updateUrl({ page });
290 handleListingTypeChange(val: ListingType) {
297 handleUnsubscribe(communityId: number) {
298 toggleSubscribe(communityId, false);
301 handleSubscribe(communityId: number) {
302 toggleSubscribe(communityId, true);
305 handleSearchChange(i: Communities, event: any) {
306 i.setState({ searchText: event.target.value });
309 handleSearchSubmit(i: Communities) {
310 const searchParamEncoded = encodeURIComponent(i.state.searchText);
311 i.context.router.history.push(`/search?q=${searchParamEncoded}`);
314 static fetchInitialData({
315 query: { listingType, page },
318 }: InitialFetchRequest<QueryParams<CommunitiesProps>>): Promise<any>[] {
319 const listCommunitiesForm: ListCommunities = {
320 type_: getListingTypeFromQuery(listingType),
321 sort: SortType.TopMonth,
322 limit: communityLimit,
323 page: getPageFromString(page),
327 return [client.listCommunities(listCommunitiesForm)];
330 parseMessage(msg: any) {
331 const op = wsUserOp(msg);
334 toast(i18n.t(msg.error), "danger");
335 } else if (op === UserOperation.ListCommunities) {
336 const data = wsJsonToRes<ListCommunitiesResponse>(msg);
337 this.setState({ listCommunitiesResponse: data, loading: false });
338 window.scrollTo(0, 0);
339 } else if (op === UserOperation.FollowCommunity) {
344 counts: { subscribers },
346 } = wsJsonToRes<CommunityResponse>(msg);
347 const res = this.state.listCommunitiesResponse;
348 const found = res?.communities.find(
349 ({ community: { id } }) => id == community.id
353 found.subscribed = subscribed;
354 found.counts.subscribers = subscribers;
355 this.setState(this.state);