]> Untitled Git - lemmy-ui.git/blob - src/shared/components/post/create-post.tsx
Merge pull request #1250 from RayBB/issue-template-checkboxes
[lemmy-ui.git] / src / shared / components / post / create-post.tsx
1 import { Component } from "inferno";
2 import { RouteComponentProps } from "inferno-router/dist/Route";
3 import {
4   CreatePost as CreatePostI,
5   GetCommunity,
6   GetSiteResponse,
7   ListCommunitiesResponse,
8 } from "lemmy-js-client";
9 import { i18n } from "../../i18next";
10 import { InitialFetchRequest, PostFormParams } from "../../interfaces";
11 import { FirstLoadService } from "../../services/FirstLoadService";
12 import {
13   HttpService,
14   RequestState,
15   WrappedLemmyHttp,
16 } from "../../services/HttpService";
17 import {
18   Choice,
19   QueryParams,
20   enableDownvotes,
21   enableNsfw,
22   getIdFromString,
23   getQueryParams,
24   myAuth,
25   setIsoData,
26 } from "../../utils";
27 import { HtmlTags } from "../common/html-tags";
28 import { Spinner } from "../common/icon";
29 import { PostForm } from "./post-form";
30
31 export interface CreatePostProps {
32   communityId?: number;
33 }
34
35 function getCreatePostQueryParams() {
36   return getQueryParams<CreatePostProps>({
37     communityId: getIdFromString,
38   });
39 }
40
41 function fetchCommunitiesForOptions(client: WrappedLemmyHttp) {
42   return client.listCommunities({ limit: 30, sort: "TopMonth", type_: "All" });
43 }
44
45 interface CreatePostState {
46   siteRes: GetSiteResponse;
47   loading: boolean;
48   selectedCommunityChoice?: Choice;
49   initialCommunitiesRes: RequestState<ListCommunitiesResponse>;
50   isIsomorphic: boolean;
51 }
52
53 export class CreatePost extends Component<
54   RouteComponentProps<Record<string, never>>,
55   CreatePostState
56 > {
57   private isoData = setIsoData(this.context);
58   state: CreatePostState = {
59     siteRes: this.isoData.site_res,
60     loading: true,
61     initialCommunitiesRes: { state: "empty" },
62     isIsomorphic: false,
63   };
64
65   constructor(props: RouteComponentProps<Record<string, never>>, context: any) {
66     super(props, context);
67
68     this.handlePostCreate = this.handlePostCreate.bind(this);
69     this.handleSelectedCommunityChange =
70       this.handleSelectedCommunityChange.bind(this);
71
72     // Only fetch the data if coming from another route
73     if (FirstLoadService.isFirstLoad) {
74       const [communityRes, listCommunitiesRes] = this.isoData.routeData;
75
76       if (communityRes?.state === "success") {
77         const communityChoice: Choice = {
78           label: communityRes.data.community_view.community.title,
79           value: communityRes.data.community_view.community.id.toString(),
80         };
81
82         this.state = {
83           ...this.state,
84           selectedCommunityChoice: communityChoice,
85         };
86       }
87
88       this.state = {
89         ...this.state,
90         loading: false,
91         initialCommunitiesRes: listCommunitiesRes,
92         isIsomorphic: true,
93       };
94     }
95   }
96
97   async fetchCommunity() {
98     const { communityId } = getCreatePostQueryParams();
99     const auth = myAuth();
100
101     if (communityId) {
102       const res = await HttpService.client.getCommunity({
103         id: communityId,
104         auth,
105       });
106       if (res.state === "success") {
107         this.setState({
108           selectedCommunityChoice: {
109             label: res.data.community_view.community.name,
110             value: res.data.community_view.community.id.toString(),
111           },
112           loading: false,
113         });
114       }
115     }
116   }
117
118   async componentDidMount() {
119     // TODO test this
120     if (!this.state.isIsomorphic) {
121       const { communityId } = getCreatePostQueryParams();
122
123       const initialCommunitiesRes = await fetchCommunitiesForOptions(
124         HttpService.client
125       );
126
127       this.setState({
128         initialCommunitiesRes,
129       });
130
131       if (
132         communityId?.toString() !== this.state.selectedCommunityChoice?.value
133       ) {
134         await this.fetchCommunity();
135       } else if (!communityId) {
136         this.setState({
137           selectedCommunityChoice: undefined,
138           loading: false,
139         });
140       }
141     }
142   }
143
144   get documentTitle(): string {
145     return `${i18n.t("create_post")} - ${
146       this.state.siteRes.site_view.site.name
147     }`;
148   }
149
150   render() {
151     const { selectedCommunityChoice } = this.state;
152
153     const locationState = this.props.history.location.state as
154       | PostFormParams
155       | undefined;
156
157     return (
158       <div className="container-lg">
159         <HtmlTags
160           title={this.documentTitle}
161           path={this.context.router.route.match.url}
162         />
163         {this.state.loading ? (
164           <h5>
165             <Spinner large />
166           </h5>
167         ) : (
168           <div className="row">
169             <div className="col-12 col-lg-6 offset-lg-3 mb-4">
170               <h5>{i18n.t("create_post")}</h5>
171               <PostForm
172                 onCreate={this.handlePostCreate}
173                 params={locationState}
174                 enableDownvotes={enableDownvotes(this.state.siteRes)}
175                 enableNsfw={enableNsfw(this.state.siteRes)}
176                 allLanguages={this.state.siteRes.all_languages}
177                 siteLanguages={this.state.siteRes.discussion_languages}
178                 selectedCommunityChoice={selectedCommunityChoice}
179                 onSelectCommunity={this.handleSelectedCommunityChange}
180                 initialCommunities={
181                   this.state.initialCommunitiesRes.state === "success"
182                     ? this.state.initialCommunitiesRes.data.communities
183                     : []
184                 }
185               />
186             </div>
187           </div>
188         )}
189       </div>
190     );
191   }
192
193   async updateUrl({ communityId }: Partial<CreatePostProps>) {
194     const { communityId: urlCommunityId } = getCreatePostQueryParams();
195
196     const locationState = this.props.history.location.state as
197       | PostFormParams
198       | undefined;
199
200     const url = new URL(location.href);
201
202     const newId = (communityId ?? urlCommunityId)?.toString();
203
204     if (newId !== undefined) {
205       url.searchParams.set("communityId", newId);
206     } else {
207       url.searchParams.delete("communityId");
208     }
209
210     history.replaceState(locationState, "", url);
211
212     await this.fetchCommunity();
213   }
214
215   handleSelectedCommunityChange(choice: Choice) {
216     this.updateUrl({
217       communityId: getIdFromString(choice?.value),
218     });
219   }
220
221   async handlePostCreate(form: CreatePostI) {
222     const res = await HttpService.client.createPost(form);
223
224     if (res.state === "success") {
225       const postId = res.data.post_view.post.id;
226       this.props.history.replace(`/post/${postId}`);
227     }
228   }
229
230   static fetchInitialData({
231     client,
232     query: { communityId },
233     auth,
234   }: InitialFetchRequest<QueryParams<CreatePostProps>>): Promise<
235     RequestState<any>
236   >[] {
237     const promises: Promise<RequestState<any>>[] = [];
238
239     if (communityId) {
240       const form: GetCommunity = {
241         auth,
242         id: getIdFromString(communityId),
243       };
244
245       promises.push(client.getCommunity(form));
246     } else {
247       promises.push(Promise.resolve({ state: "empty" }));
248     }
249
250     promises.push(fetchCommunitiesForOptions(client));
251
252     return promises;
253   }
254 }