parent
							
								
									b30508aef8
								
							
						
					
					
						commit
						84ec839b04
					
				
					 12 changed files with 553 additions and 14 deletions
				
			
		|  | @ -55,9 +55,10 @@ export const traceFunction = !IS_DEV | |||
|             const traceName = mapper?.(...args) ?? name; | ||||
| 
 | ||||
|             beginTrace(traceName, ...arguments); | ||||
|             const result = f.apply(this, args); | ||||
|             finishTrace(traceName); | ||||
| 
 | ||||
|             return result; | ||||
|             try { | ||||
|                 return f.apply(this, args); | ||||
|             } finally { | ||||
|                 finishTrace(traceName); | ||||
|             } | ||||
|         } as F; | ||||
|     }; | ||||
|  |  | |||
							
								
								
									
										93
									
								
								src/plugins/reviewDB/Utils/ReviewDBAPI.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/plugins/reviewDB/Utils/ReviewDBAPI.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import { Settings } from "../../../Vencord"; | ||||
| import { Review } from "../entities/Review"; | ||||
| import { authorize, showToast } from "./Utils"; | ||||
| 
 | ||||
| const settings = Settings.plugins.ReviewDB; | ||||
| const API_URL = "https://manti.vendicated.dev"; | ||||
| 
 | ||||
| enum Response { | ||||
|     "Added your review" = 0, | ||||
|     "Updated your review" = 1, | ||||
|     "Error" = 2, | ||||
| } | ||||
| 
 | ||||
| export async function getReviews(id: string): Promise<Review[]> { | ||||
|     const res = await fetch(API_URL + "/getUserReviews?snowflakeFormat=string&discordid=" + id); | ||||
|     return await res.json() as Review[]; | ||||
| } | ||||
| 
 | ||||
| export async function addReview(review: any): Promise<Response> { | ||||
|     review.token = settings.token; | ||||
| 
 | ||||
|     if (!review.token) { | ||||
|         showToast("Please authorize to add a review."); | ||||
|         authorize(); | ||||
|         return Response.Error; | ||||
|     } | ||||
| 
 | ||||
|     return fetch(API_URL + "/addUserReview", { | ||||
|         method: "POST", | ||||
|         body: JSON.stringify(review), | ||||
|         headers: { | ||||
|             "Content-Type": "application/json", | ||||
|         } | ||||
|     }) | ||||
|         .then(r => r.text()) | ||||
|         .then(res => { | ||||
|             showToast(res); | ||||
|             return Response[res] ?? Response.Error; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| export function deleteReview(id: number): Promise<any> { | ||||
|     return fetch(API_URL + "/deleteReview", { | ||||
|         method: "POST", | ||||
|         headers: new Headers({ | ||||
|             "Content-Type": "application/json", | ||||
|             Accept: "application/json", | ||||
|         }), | ||||
|         body: JSON.stringify({ | ||||
|             token: settings.token, | ||||
|             reviewid: id | ||||
|         }) | ||||
|     }).then(r => r.json()); | ||||
| } | ||||
| 
 | ||||
| export async function reportReview(id: number) { | ||||
|     const res = await fetch(API_URL + "/reportReview", { | ||||
|         method: "POST", | ||||
|         headers: new Headers({ | ||||
|             "Content-Type": "application/json", | ||||
|             Accept: "application/json", | ||||
|         }), | ||||
|         body: JSON.stringify({ | ||||
|             reviewid: id, | ||||
|             token: settings.token | ||||
|         }) | ||||
|     }); | ||||
|     showToast(await res.text()); | ||||
| } | ||||
| 
 | ||||
| export function getLastReviewID(id: string): Promise<number> { | ||||
|     return fetch(API_URL + "/getLastReviewID?discordid=" + id) | ||||
|         .then(r => r.text()) | ||||
|         .then(Number); | ||||
| } | ||||
							
								
								
									
										94
									
								
								src/plugins/reviewDB/Utils/Utils.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/plugins/reviewDB/Utils/Utils.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import { Devs } from "../../../utils/constants"; | ||||
| import Logger from "../../../utils/Logger"; | ||||
| import { openModal } from "../../../utils/modal"; | ||||
| import { Settings } from "../../../Vencord"; | ||||
| import { findByProps } from "../../../webpack"; | ||||
| import { FluxDispatcher, React, SelectedChannelStore, Toasts, UserUtils } from "../../../webpack/common"; | ||||
| import { Review } from "../entities/Review"; | ||||
| 
 | ||||
| export async function openUserProfileModal(userId: string) { | ||||
|     await UserUtils.fetchUser(userId); | ||||
| 
 | ||||
|     await FluxDispatcher.dispatch({ | ||||
|         type: "USER_PROFILE_MODAL_OPEN", | ||||
|         userId, | ||||
|         channelId: SelectedChannelStore.getChannelId(), | ||||
|         analyticsLocation: "Explosive Hotel" | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| export function authorize(callback?: any) { | ||||
|     const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal"); | ||||
| 
 | ||||
|     openModal((props: any) => | ||||
|         <OAuth2AuthorizeModal | ||||
|             {...props} | ||||
|             scopes={["identify"]} | ||||
|             responseType="code" | ||||
|             redirectUri="https://manti.vendicated.dev/URauth" | ||||
|             permissions={0n} | ||||
|             clientId="915703782174752809" | ||||
|             cancelCompletesFlow={false} | ||||
|             callback={async (u: string) => { | ||||
|                 try { | ||||
|                     const url = new URL(u); | ||||
|                     url.searchParams.append("returnType", "json"); | ||||
|                     url.searchParams.append("clientMod", "vencord"); | ||||
|                     const res = await fetch(url, { | ||||
|                         headers: new Headers({ Accept: "application/json" }) | ||||
|                     }); | ||||
|                     const { token, status } = await res.json(); | ||||
|                     if (status === 0) { | ||||
|                         Settings.plugins.ReviewDB.token = token; | ||||
|                         showToast("Successfully logged in!"); | ||||
|                         callback?.(); | ||||
|                     } else if (res.status === 1) { | ||||
|                         showToast("An Error occurred while logging in."); | ||||
|                     } | ||||
|                 } catch (e) { | ||||
|                     new Logger("ReviewDB").error("Failed to authorise", e); | ||||
|                 } | ||||
|             }} | ||||
|         /> | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| export function showToast(text: string) { | ||||
|     Toasts.show({ | ||||
|         type: Toasts.Type.MESSAGE, | ||||
|         message: text, | ||||
|         id: Toasts.genId(), | ||||
|         options: { | ||||
|             position: Toasts.Position.BOTTOM | ||||
|         }, | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); | ||||
| 
 | ||||
| export function canDeleteReview(review: Review, userId: string) { | ||||
|     if (review.senderdiscordid === userId) return true; | ||||
| 
 | ||||
|     const myId = BigInt(userId); | ||||
|     return myId === Devs.mantikafasi.id || | ||||
|         myId === Devs.Ven.id || | ||||
|         myId === Devs.rushii.id; | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/plugins/reviewDB/components/MessageButton.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/plugins/reviewDB/components/MessageButton.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import { classes, LazyComponent } from "../../../utils/misc"; | ||||
| import { findByProps } from "../../../webpack"; | ||||
| 
 | ||||
| export default LazyComponent(() => { | ||||
|     const { button, dangerous } = findByProps("button", "wrapper", "disabled"); | ||||
| 
 | ||||
|     return function MessageButton(props) { | ||||
|         return props.type === "delete" | ||||
|             ? ( | ||||
|                 <div className={classes(button, dangerous)} aria-label="Delete Review" onClick={props.callback}> | ||||
|                     <svg aria-hidden="false" width="16" height="16" viewBox="0 0 20 20"> | ||||
|                         <path fill="currentColor" d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"></path> | ||||
|                         <path fill="currentColor" d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"></path> | ||||
|                     </svg> | ||||
|                 </div> | ||||
|             ) | ||||
|             : ( | ||||
|                 <div className={button} aria-label="Report Review" onClick={() => props.callback()}> | ||||
|                     <svg aria-hidden="false" width="16" height="16" viewBox="0 0 20 20"> | ||||
|                         <path fill="currentColor" d="M20,6.002H14V3.002C14,2.45 13.553,2.002 13,2.002H4C3.447,2.002 3,2.45 3,3.002V22.002H5V14.002H10.586L8.293,16.295C8.007,16.581 7.922,17.011 8.076,17.385C8.23,17.759 8.596,18.002 9,18.002H20C20.553,18.002 21,17.554 21,17.002V7.002C21,6.45 20.553,6.002 20,6.002Z"></path> | ||||
|                     </svg> | ||||
|                 </div> | ||||
|             ); | ||||
|     }; | ||||
| }); | ||||
							
								
								
									
										112
									
								
								src/plugins/reviewDB/components/ReviewComponent.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/plugins/reviewDB/components/ReviewComponent.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import { classes, LazyComponent } from "../../../utils/misc"; | ||||
| import { filters, findBulk } from "../../../webpack"; | ||||
| import { Alerts, UserStore } from "../../../webpack/common"; | ||||
| import { Review } from "../entities/Review"; | ||||
| import { deleteReview, reportReview } from "../Utils/ReviewDBAPI"; | ||||
| import { canDeleteReview, openUserProfileModal, showToast } from "../Utils/Utils"; | ||||
| import MessageButton from "./MessageButton"; | ||||
| 
 | ||||
| export default LazyComponent(() => { | ||||
|     // this is terrible, blame mantika
 | ||||
|     const p = filters.byProps; | ||||
|     const [ | ||||
|         { cozyMessage, buttons, message, groupStart }, | ||||
|         { container, isHeader }, | ||||
|         { avatar, clickable, username, messageContent, wrapper, cozy }, | ||||
|         { contents }, | ||||
|         buttonClasses, | ||||
|         { defaultColor } | ||||
|     ] = findBulk( | ||||
|         p("cozyMessage"), | ||||
|         p("container", "isHeader"), | ||||
|         p("avatar", "zalgo"), | ||||
|         p("contents"), | ||||
|         p("button", "wrapper", "disabled"), | ||||
|         p("defaultColor") | ||||
|     ); | ||||
| 
 | ||||
|     return function ReviewComponent({ review, refetch }: { review: Review; refetch(): void; }) { | ||||
|         function openModal() { | ||||
|             openUserProfileModal(review.senderdiscordid); | ||||
|         } | ||||
| 
 | ||||
|         function delReview() { | ||||
|             Alerts.show({ | ||||
|                 title: "Are you sure?", | ||||
|                 body: "Do you really want to delete this review?", | ||||
|                 confirmText: "Delete", | ||||
|                 cancelText: "Nevermind", | ||||
|                 onConfirm: () => { | ||||
|                     deleteReview(review.id).then(res => { | ||||
|                         if (res.successful) { | ||||
|                             refetch(); | ||||
|                         } | ||||
|                         showToast(res.message); | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         function reportRev() { | ||||
|             Alerts.show({ | ||||
|                 title: "Are you sure?", | ||||
|                 body: "Do you really you want to report this review?", | ||||
|                 confirmText: "Report", | ||||
|                 cancelText: "Nevermind", | ||||
|                 confirmColor: "red", | ||||
|                 onConfirm: () => reportReview(review.id) | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className={classes(cozyMessage, message, groupStart, wrapper, cozy)}> | ||||
|                 <div className={contents}> | ||||
|                     <img | ||||
|                         className={classes(avatar, clickable)} | ||||
|                         onClick={openModal} | ||||
|                         src={review.profile_photo || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} | ||||
|                     /> | ||||
|                     <span | ||||
|                         className={classes(username, clickable)} | ||||
|                         style={{ color: "var(--text-muted)" }} | ||||
|                         onClick={() => openModal()} | ||||
|                     > | ||||
|                         {review.username} | ||||
|                     </span> | ||||
|                     <p | ||||
|                         className={classes(messageContent, defaultColor)} | ||||
|                         style={{ fontSize: 15, marginTop: 4 }} | ||||
|                     > | ||||
|                         {review.comment} | ||||
|                     </p> | ||||
|                     <div className={classes(container, isHeader, buttons)}> | ||||
|                         <div className={buttonClasses.wrapper}> | ||||
|                             <MessageButton type="report" callback={reportRev} /> | ||||
|                             {canDeleteReview(review, UserStore.getCurrentUser().id) && ( | ||||
|                                 <MessageButton type="delete" callback={delReview} /> | ||||
|                             )} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     }; | ||||
| }); | ||||
							
								
								
									
										83
									
								
								src/plugins/reviewDB/components/ReviewsView.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/plugins/reviewDB/components/ReviewsView.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import type { KeyboardEvent } from "react"; | ||||
| 
 | ||||
| import { lazyWebpack, useAwaiter } from "../../../utils/misc"; | ||||
| import { Forms, Text } from "../../../webpack/common"; | ||||
| import { addReview, getReviews } from "../Utils/ReviewDBAPI"; | ||||
| import ReviewComponent from "./ReviewComponent"; | ||||
| 
 | ||||
| const Classes = lazyWebpack(m => typeof m.textarea === "string"); | ||||
| 
 | ||||
| export default function ReviewsView({ userId }: { userId: string; }) { | ||||
|     const [reviews, _, isLoading, refetch] = useAwaiter(() => getReviews(userId), []); | ||||
| 
 | ||||
|     if (isLoading) return null; | ||||
| 
 | ||||
|     function onKeyPress({ key, target }: KeyboardEvent<HTMLTextAreaElement>) { | ||||
|         if (key === "Enter") { | ||||
|             addReview({ | ||||
|                 userid: userId, | ||||
|                 comment: (target as HTMLInputElement).value, | ||||
|                 star: -1 | ||||
|             }).then(res => { | ||||
|                 if (res === 0 || res === 1) { | ||||
|                     (target as HTMLInputElement).value = ""; // clear the input
 | ||||
|                     refetch(); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <Text | ||||
|                 tag="h2" | ||||
|                 variant="eyebrow" | ||||
|                 style={{ | ||||
|                     paddingLeft: "12px", | ||||
|                     marginBottom: "12px", | ||||
|                     color: "var(--header-primary)" | ||||
|                 }} | ||||
|             > | ||||
|                 User Reviews | ||||
|             </Text> | ||||
|             {reviews?.map(review => | ||||
|                 <ReviewComponent | ||||
|                     key={review.id} | ||||
|                     review={review} | ||||
|                     refetch={refetch} | ||||
|                 /> | ||||
|             )} | ||||
|             {reviews?.length === 0 && ( | ||||
|                 <Forms.FormText style={{ paddingLeft: "12px", paddingRight: "12px" }}> | ||||
|                     Looks like nobody reviewed this user yet. You could be the first! | ||||
|                 </Forms.FormText> | ||||
|             )} | ||||
|             <textarea | ||||
|                 className={Classes.textarea} | ||||
|                 placeholder="Enter a comment" | ||||
|                 onKeyDown={onKeyPress} | ||||
|                 style={{ | ||||
|                     padding: "12px", | ||||
|                 }} | ||||
|             /> | ||||
|         </> | ||||
|     ); | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/plugins/reviewDB/entities/Review.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/plugins/reviewDB/entities/Review.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| export interface Review { | ||||
|     comment: string, | ||||
|     id: number, | ||||
|     senderdiscordid: string, | ||||
|     senderuserid: number, | ||||
|     star: number, | ||||
|     username: string, | ||||
|     profile_photo: string; | ||||
| } | ||||
							
								
								
									
										80
									
								
								src/plugins/reviewDB/index.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/plugins/reviewDB/index.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | |||
| /* | ||||
|  * Vencord, a modification for Discord's desktop app | ||||
|  * Copyright (c) 2022 Vendicated and contributors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| import { User } from "discord-types/general"; | ||||
| 
 | ||||
| import ErrorBoundary from "../../components/ErrorBoundary"; | ||||
| import { Devs } from "../../utils/constants"; | ||||
| import definePlugin, { OptionType } from "../../utils/types"; | ||||
| import { Settings } from "../../Vencord"; | ||||
| import { Button, UserStore } from "../../webpack/common"; | ||||
| import ReviewsView from "./components/ReviewsView"; | ||||
| import { getLastReviewID } from "./Utils/ReviewDBAPI"; | ||||
| import { authorize, showToast } from "./Utils/Utils"; | ||||
| 
 | ||||
| export default definePlugin({ | ||||
|     name: "ReviewDB", | ||||
|     description: "Review other users (Adds a new settings to profiles)", | ||||
|     authors: [Devs.mantikafasi, Devs.Ven], | ||||
| 
 | ||||
|     patches: [ | ||||
|         { | ||||
|             find: "disableBorderColor:!0", | ||||
|             replacement: { | ||||
|                 match: /\(.{0,10}\{user:(.),setNote:.,canDM:.,.+?\}\)/, | ||||
|                 replace: "$&,Vencord.Plugins.plugins.ReviewDB.getReviewsComponent($1)" | ||||
|             }, | ||||
|         } | ||||
|     ], | ||||
| 
 | ||||
|     options: { | ||||
|         authorize: { | ||||
|             type: OptionType.COMPONENT, | ||||
|             description: "Authorise with ReviewDB", | ||||
|             component: () => ( | ||||
|                 <Button onClick={authorize}> | ||||
|                     Authorise with ReviewDB | ||||
|                 </Button> | ||||
|             ) | ||||
|         }, | ||||
|         notifyReviews: { | ||||
|             type: OptionType.BOOLEAN, | ||||
|             description: "Notify about new reviews on startup", | ||||
|             default: true, | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     async start() { | ||||
|         const settings = Settings.plugins.ReviewDB; | ||||
|         if (!settings.lastReviewId || !settings.notifyReviews) return; | ||||
| 
 | ||||
|         setTimeout(async () => { | ||||
|             const id = await getLastReviewID(UserStore.getCurrentUser().id); | ||||
|             if (settings.lastReviewId < id) { | ||||
|                 showToast("You have new reviews on your profile!"); | ||||
|                 settings.lastReviewId = id; | ||||
|             } | ||||
|         }, 4000); | ||||
|     }, | ||||
| 
 | ||||
|     getReviewsComponent: (user: User) => ( | ||||
|         <ErrorBoundary message="Failed to render Reviews"> | ||||
|             <ReviewsView userId={user.id} /> | ||||
|         </ErrorBoundary> | ||||
|     ) | ||||
| }); | ||||
|  | @ -93,6 +93,10 @@ export const Devs = Object.freeze({ | |||
|         name: "Nickyux", | ||||
|         id: 427146305651998721n | ||||
|     }, | ||||
|     mantikafasi: { | ||||
|         name: "mantikafasi", | ||||
|         id: 287555395151593473n | ||||
|     }, | ||||
|     Xinto: { | ||||
|         name: "Xinto", | ||||
|         id: 423915768191647755n | ||||
|  |  | |||
|  | @ -40,21 +40,23 @@ export function lazyWebpack<T = any>(filter: FilterFn): T { | |||
|     return proxyLazy(() => find(filter)); | ||||
| } | ||||
| 
 | ||||
| type AwaiterRes<T> = [T, any, boolean, () => void]; | ||||
| /** | ||||
|  * Await a promise | ||||
|  * @param factory Factory | ||||
|  * @param fallbackValue The fallback value that will be used until the promise resolved | ||||
|  * @returns [value, error, isPending] | ||||
|  */ | ||||
| export function useAwaiter<T>(factory: () => Promise<T>): [T | null, any, boolean]; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T): [T, any, boolean]; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: null, onError: (e: unknown) => unknown): [T, any, boolean]; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null = null, onError?: (e: unknown) => unknown): [T | null, any, boolean] { | ||||
| export function useAwaiter<T>(factory: () => Promise<T>): AwaiterRes<T | null>; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T): AwaiterRes<T>; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: null, onError: (e: unknown) => unknown): AwaiterRes<T>; | ||||
| export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null = null, onError?: (e: unknown) => unknown): AwaiterRes<T | null> { | ||||
|     const [state, setState] = React.useState({ | ||||
|         value: fallbackValue, | ||||
|         error: null, | ||||
|         pending: true | ||||
|     }); | ||||
|     const [signal, setSignal] = React.useState(0); | ||||
| 
 | ||||
|     React.useEffect(() => { | ||||
|         let isAlive = true; | ||||
|  | @ -63,9 +65,9 @@ export function useAwaiter<T>(factory: () => Promise<T>, fallbackValue: T | null | |||
|             .catch(error => isAlive && (setState({ value: null, error, pending: false }), onError?.(error))); | ||||
| 
 | ||||
|         return () => void (isAlive = false); | ||||
|     }, []); | ||||
|     }, [signal]); | ||||
| 
 | ||||
|     return [state.value, state.error, state.pending]; | ||||
|     return [state.value, state.error, state.pending, () => setSignal(signal + 1)]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -172,7 +172,7 @@ export type TextProps = React.PropsWithChildren & { | |||
|     variant: TextVariant; | ||||
|     style?: React.CSSProperties; | ||||
|     color?: string; | ||||
|     tag?: "div" | "span" | "p" | "strong"; | ||||
|     tag?: "div" | "span" | "p" | "strong" | `h${1 | 2 | 3 | 4 | 5 | 6}`; | ||||
|     selectable?: boolean; | ||||
|     lineClamp?: number; | ||||
|     id?: string; | ||||
|  |  | |||
|  | @ -139,11 +139,11 @@ export function findAll(filter: FilterFn, getDefault = true) { | |||
| 
 | ||||
| /** | ||||
|  * Same as {@link find} but in bulk | ||||
|  * @param filterFns Arry of filters. Please note that this array will be modified in place, so if you still | ||||
|  * @param filterFns Array of filters. Please note that this array will be modified in place, so if you still | ||||
|  *                need it afterwards, pass a copy. | ||||
|  * @returns Array of results in the same order as the passed filters | ||||
|  */ | ||||
| export function bulk(...filterFns: FilterFn[]) { | ||||
| export const findBulk = traceFunction("findBulk", function findBulk(...filterFns: FilterFn[]) { | ||||
|     if (!Array.isArray(filterFns)) | ||||
|         throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); | ||||
| 
 | ||||
|  | @ -216,7 +216,7 @@ export function bulk(...filterFns: FilterFn[]) { | |||
|     } | ||||
| 
 | ||||
|     return results; | ||||
| } | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * Find the id of a module by its code | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue