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