]> Untitled Git - lemmy-ui.git/blob - src/shared/components/person/registration-applications.tsx
Fix server-side rendering after first load.
[lemmy-ui.git] / src / shared / components / person / registration-applications.tsx
1 import {
2   editRegistrationApplication,
3   myAuthRequired,
4   setIsoData,
5 } from "@utils/app";
6 import { isBrowser } from "@utils/browser";
7 import { RouteDataResponse } from "@utils/types";
8 import { Component, linkEvent } from "inferno";
9 import {
10   ApproveRegistrationApplication,
11   GetSiteResponse,
12   ListRegistrationApplicationsResponse,
13   RegistrationApplicationView,
14 } from "lemmy-js-client";
15 import { fetchLimit } from "../../config";
16 import { InitialFetchRequest } from "../../interfaces";
17 import { FirstLoadService, I18NextService, UserService } from "../../services";
18 import { HttpService, RequestState } from "../../services/HttpService";
19 import { setupTippy } from "../../tippy";
20 import { HtmlTags } from "../common/html-tags";
21 import { Spinner } from "../common/icon";
22 import { Paginator } from "../common/paginator";
23 import { RegistrationApplication } from "../common/registration-application";
24
25 enum UnreadOrAll {
26   Unread,
27   All,
28 }
29
30 type RegistrationApplicationsData = RouteDataResponse<{
31   listRegistrationApplicationsResponse: ListRegistrationApplicationsResponse;
32 }>;
33
34 interface RegistrationApplicationsState {
35   appsRes: RequestState<ListRegistrationApplicationsResponse>;
36   siteRes: GetSiteResponse;
37   unreadOrAll: UnreadOrAll;
38   page: number;
39   isIsomorphic: boolean;
40 }
41
42 export class RegistrationApplications extends Component<
43   any,
44   RegistrationApplicationsState
45 > {
46   private isoData = setIsoData<RegistrationApplicationsData>(this.context);
47   state: RegistrationApplicationsState = {
48     appsRes: { state: "empty" },
49     siteRes: this.isoData.site_res,
50     unreadOrAll: UnreadOrAll.Unread,
51     page: 1,
52     isIsomorphic: false,
53   };
54
55   constructor(props: any, context: any) {
56     super(props, context);
57
58     this.handlePageChange = this.handlePageChange.bind(this);
59     this.handleApproveApplication = this.handleApproveApplication.bind(this);
60
61     // Only fetch the data if coming from another route
62     if (!isBrowser() || FirstLoadService.isFirstLoad) {
63       this.state = {
64         ...this.state,
65         appsRes: this.isoData.routeData.listRegistrationApplicationsResponse,
66         isIsomorphic: true,
67       };
68     }
69   }
70
71   async componentDidMount() {
72     if (!this.state.isIsomorphic) {
73       await this.refetch();
74     }
75     setupTippy();
76   }
77
78   get documentTitle(): string {
79     const mui = UserService.Instance.myUserInfo;
80     return mui
81       ? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
82           "registration_applications"
83         )} - ${this.state.siteRes.site_view.site.name}`
84       : "";
85   }
86
87   renderApps() {
88     switch (this.state.appsRes.state) {
89       case "loading":
90         return (
91           <h5>
92             <Spinner large />
93           </h5>
94         );
95       case "success": {
96         const apps = this.state.appsRes.data.registration_applications;
97         return (
98           <div className="row">
99             <div className="col-12">
100               <HtmlTags
101                 title={this.documentTitle}
102                 path={this.context.router.route.match.url}
103               />
104               <h5 className="mb-2">
105                 {I18NextService.i18n.t("registration_applications")}
106               </h5>
107               {this.selects()}
108               {this.applicationList(apps)}
109               <Paginator
110                 page={this.state.page}
111                 onChange={this.handlePageChange}
112               />
113             </div>
114           </div>
115         );
116       }
117     }
118   }
119
120   render() {
121     return (
122       <div className="registration-applications container-lg">
123         {this.renderApps()}
124       </div>
125     );
126   }
127
128   unreadOrAllRadios() {
129     return (
130       <div className="btn-group btn-group-toggle flex-wrap mb-2">
131         <label
132           className={`btn btn-outline-secondary pointer
133             ${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
134           `}
135         >
136           <input
137             type="radio"
138             className="btn-check"
139             value={UnreadOrAll.Unread}
140             checked={this.state.unreadOrAll == UnreadOrAll.Unread}
141             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
142           />
143           {I18NextService.i18n.t("unread")}
144         </label>
145         <label
146           className={`btn btn-outline-secondary pointer
147             ${this.state.unreadOrAll == UnreadOrAll.All && "active"}
148           `}
149         >
150           <input
151             type="radio"
152             className="btn-check"
153             value={UnreadOrAll.All}
154             checked={this.state.unreadOrAll == UnreadOrAll.All}
155             onChange={linkEvent(this, this.handleUnreadOrAllChange)}
156           />
157           {I18NextService.i18n.t("all")}
158         </label>
159       </div>
160     );
161   }
162
163   selects() {
164     return (
165       <div className="mb-2">
166         <span className="me-3">{this.unreadOrAllRadios()}</span>
167       </div>
168     );
169   }
170
171   applicationList(apps: RegistrationApplicationView[]) {
172     return (
173       <div>
174         {apps.map(ra => (
175           <>
176             <hr />
177             <RegistrationApplication
178               key={ra.registration_application.id}
179               application={ra}
180               onApproveApplication={this.handleApproveApplication}
181             />
182           </>
183         ))}
184       </div>
185     );
186   }
187
188   handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
189     i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
190     i.refetch();
191   }
192
193   handlePageChange(page: number) {
194     this.setState({ page });
195     this.refetch();
196   }
197
198   static async fetchInitialData({
199     auth,
200     client,
201   }: InitialFetchRequest): Promise<RegistrationApplicationsData> {
202     return {
203       listRegistrationApplicationsResponse: auth
204         ? await client.listRegistrationApplications({
205             unread_only: true,
206             page: 1,
207             limit: fetchLimit,
208             auth: auth as string,
209           })
210         : { state: "empty" },
211     };
212   }
213
214   async refetch() {
215     const unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
216     this.setState({
217       appsRes: { state: "loading" },
218     });
219     this.setState({
220       appsRes: await HttpService.client.listRegistrationApplications({
221         unread_only: unread_only,
222         page: this.state.page,
223         limit: fetchLimit,
224         auth: myAuthRequired(),
225       }),
226     });
227   }
228
229   async handleApproveApplication(form: ApproveRegistrationApplication) {
230     const approveRes = await HttpService.client.approveRegistrationApplication(
231       form
232     );
233     this.setState(s => {
234       if (s.appsRes.state == "success" && approveRes.state == "success") {
235         s.appsRes.data.registration_applications = editRegistrationApplication(
236           approveRes.data.registration_application,
237           s.appsRes.data.registration_applications
238         );
239       }
240       return s;
241     });
242   }
243 }