]> Untitled Git - lemmy-ui.git/blob - src/shared/components/post/create-post.tsx
Merge remote-tracking branch 'lemmy/main' into fix/fix-long-words-in-titles-overflow
[lemmy-ui.git] / src / shared / components / post / create-post.tsx
1 import { enableDownvotes, enableNsfw, myAuth, setIsoData } from "@utils/app";
2 import { getIdFromString, getQueryParams } from "@utils/helpers";
3 import type { QueryParams } from "@utils/types";
4 import { Choice, RouteDataResponse } from "@utils/types";
5 import { Component } from "inferno";
6 import { RouteComponentProps } from "inferno-router/dist/Route";
7 import {
8   CreatePost as CreatePostI,
9   GetCommunity,
10   GetCommunityResponse,
11   GetSiteResponse,
12   ListCommunitiesResponse,
13 } from "lemmy-js-client";
14 import { InitialFetchRequest, PostFormParams } from "../../interfaces";
15 import { FirstLoadService, I18NextService } from "../../services";
16 import {
17   HttpService,
18   RequestState,
19   WrappedLemmyHttp,
20 } from "../../services/HttpService";
21 import { HtmlTags } from "../common/html-tags";
22 import { Spinner } from "../common/icon";
23 import { PostForm } from "./post-form";
24
25 export interface CreatePostProps {
26   communityId?: number;
27 }
28
29 type CreatePostData = RouteDataResponse<{
30   communityResponse: GetCommunityResponse;
31   initialCommunitiesRes: ListCommunitiesResponse;
32 }>;
33
34 function getCreatePostQueryParams() {
35   return getQueryParams<CreatePostProps>({
36     communityId: getIdFromString,
37   });
38 }
39
40 function fetchCommunitiesForOptions(client: WrappedLemmyHttp) {
41   return client.listCommunities({ limit: 30, sort: "TopMonth", type_: "All" });
42 }
43
44 interface CreatePostState {
45   siteRes: GetSiteResponse;
46   loading: boolean;
47   selectedCommunityChoice?: Choice;
48   initialCommunitiesRes: RequestState<ListCommunitiesResponse>;
49   isIsomorphic: boolean;
50 }
51
52 export class CreatePost extends Component<
53   RouteComponentProps<Record<string, never>>,
54   CreatePostState
55 > {
56   private isoData = setIsoData<CreatePostData>(this.context);
57   state: CreatePostState = {
58     siteRes: this.isoData.site_res,
59     loading: true,
60     initialCommunitiesRes: { state: "empty" },
61     isIsomorphic: false,
62   };
63
64   constructor(props: RouteComponentProps<Record<string, never>>, context: any) {
65     super(props, context);
66
67     this.handlePostCreate = this.handlePostCreate.bind(this);
68     this.handleSelectedCommunityChange =
69       this.handleSelectedCommunityChange.bind(this);
70
71     // Only fetch the data if coming from another route
72     if (FirstLoadService.isFirstLoad) {
73       const { communityResponse: communityRes, initialCommunitiesRes } =
74         this.isoData.routeData;
75
76       this.state = {
77         ...this.state,
78         loading: false,
79         initialCommunitiesRes,
80         isIsomorphic: true,
81       };
82
83       if (communityRes?.state === "success") {
84         const communityChoice: Choice = {
85           label: communityRes.data.community_view.community.title,
86           value: communityRes.data.community_view.community.id.toString(),
87         };
88
89         this.state = {
90           ...this.state,
91           selectedCommunityChoice: communityChoice,
92         };
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.title,
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 `${I18NextService.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="create-post 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
170               id="createPostForm"
171               className="col-12 col-lg-6 offset-lg-3 mb-4"
172             >
173               <h1 className="h4 mb-4">
174                 {I18NextService.i18n.t("create_post")}
175               </h1>
176               <PostForm
177                 onCreate={this.handlePostCreate}
178                 params={locationState}
179                 enableDownvotes={enableDownvotes(this.state.siteRes)}
180                 enableNsfw={enableNsfw(this.state.siteRes)}
181                 allLanguages={this.state.siteRes.all_languages}
182                 siteLanguages={this.state.siteRes.discussion_languages}
183                 selectedCommunityChoice={selectedCommunityChoice}
184                 onSelectCommunity={this.handleSelectedCommunityChange}
185                 initialCommunities={
186                   this.state.initialCommunitiesRes.state === "success"
187                     ? this.state.initialCommunitiesRes.data.communities
188                     : []
189                 }
190               />
191             </div>
192           </div>
193         )}
194       </div>
195     );
196   }
197
198   async updateUrl({ communityId }: Partial<CreatePostProps>) {
199     const { communityId: urlCommunityId } = getCreatePostQueryParams();
200
201     const locationState = this.props.history.location.state as
202       | PostFormParams
203       | undefined;
204
205     const url = new URL(location.href);
206
207     const newId = (communityId ?? urlCommunityId)?.toString();
208
209     if (newId !== undefined) {
210       url.searchParams.set("communityId", newId);
211     } else {
212       url.searchParams.delete("communityId");
213     }
214
215     history.replaceState(locationState, "", url);
216
217     await this.fetchCommunity();
218   }
219
220   handleSelectedCommunityChange(choice: Choice) {
221     this.updateUrl({
222       communityId: getIdFromString(choice?.value),
223     });
224   }
225
226   async handlePostCreate(form: CreatePostI) {
227     const res = await HttpService.client.createPost(form);
228
229     if (res.state === "success") {
230       const postId = res.data.post_view.post.id;
231       this.props.history.replace(`/post/${postId}`);
232     } else {
233       this.setState({
234         loading: false,
235       });
236     }
237   }
238
239   static async fetchInitialData({
240     client,
241     query: { communityId },
242     auth,
243   }: InitialFetchRequest<
244     QueryParams<CreatePostProps>
245   >): Promise<CreatePostData> {
246     const data: CreatePostData = {
247       initialCommunitiesRes: await fetchCommunitiesForOptions(client),
248       communityResponse: { state: "empty" },
249     };
250
251     if (communityId) {
252       const form: GetCommunity = {
253         auth,
254         id: getIdFromString(communityId),
255       };
256
257       data.communityResponse = await client.getCommunity(form);
258     }
259
260     return data;
261   }
262 }