From c0f2c974587d75a38e3e753368ef0e2e2be139fd Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 6 Oct 2023 19:40:53 +0200 Subject: ReviewDB: proper multi account support --- src/plugins/reviewDB/auth.tsx | 78 ++++++++++++++++++++++ .../reviewDB/components/ReviewComponent.tsx | 4 +- src/plugins/reviewDB/components/ReviewModal.tsx | 5 +- src/plugins/reviewDB/components/ReviewsView.tsx | 7 +- src/plugins/reviewDB/entities.ts | 5 ++ src/plugins/reviewDB/index.tsx | 45 +++++++++---- src/plugins/reviewDB/reviewDbApi.ts | 18 ++--- src/plugins/reviewDB/settings.tsx | 12 ++-- src/plugins/reviewDB/utils.tsx | 55 +-------------- 9 files changed, 141 insertions(+), 88 deletions(-) create mode 100644 src/plugins/reviewDB/auth.tsx diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx new file mode 100644 index 0000000..1d95e47 --- /dev/null +++ b/src/plugins/reviewDB/auth.tsx @@ -0,0 +1,78 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "@api/index"; +import { Logger } from "@utils/Logger"; +import { openModal } from "@utils/modal"; +import { findByPropsLazy } from "@webpack"; +import { showToast, Toasts, UserStore } from "@webpack/common"; + +import { ReviewDBAuth } from "./entities"; + +const DATA_STORE_KEY = "rdb-auth"; + +const OAuth = findByPropsLazy("OAuth2AuthorizeModal"); + +export let Auth: ReviewDBAuth = {}; + +export async function initAuth() { + Auth = await getAuth() ?? {}; +} + +export async function getAuth(): Promise { + const auth = await DataStore.get(DATA_STORE_KEY); + return auth?.[UserStore.getCurrentUser()?.id]; +} + +export async function getToken() { + const auth = await getAuth(); + return auth?.token; +} + +export async function updateAuth(newAuth: ReviewDBAuth) { + return DataStore.update(DATA_STORE_KEY, auth => { + auth ??= {}; + Auth = auth[UserStore.getCurrentUser().id] ??= {}; + + if (newAuth.token) Auth.token = newAuth.token; + if (newAuth.user) Auth.user = newAuth.user; + + return auth; + }); +} + +export function authorize(callback?: any) { + openModal(props => + { + try { + const url = new URL(response.location); + url.searchParams.append("clientMod", "vencord"); + const res = await fetch(url, { + headers: new Headers({ Accept: "application/json" }) + }); + const { token, success } = await res.json(); + if (success) { + updateAuth({ token }); + showToast("Successfully logged in!"); + callback?.(); + } else if (res.status === 1) { + showToast("An Error occurred while logging in.", Toasts.Type.FAILURE); + } + } catch (e) { + new Logger("ReviewDB").error("Failed to authorize", e); + } + }} + /> + ); +} diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 5792400..1865917 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -20,12 +20,12 @@ import { openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { LazyComponent } from "@utils/react"; import { filters, findBulk } from "@webpack"; -import { Alerts, moment, Parser, Timestamp } from "@webpack/common"; +import { Alerts, moment, Parser, showToast, Timestamp } from "@webpack/common"; import { Review, ReviewType } from "../entities"; import { deleteReview, reportReview } from "../reviewDbApi"; import { settings } from "../settings"; -import { canDeleteReview, cl, showToast } from "../utils"; +import { canDeleteReview, cl } from "../utils"; import { DeleteButton, ReportButton } from "./MessageButton"; import ReviewBadge from "./ReviewBadge"; diff --git a/src/plugins/reviewDB/components/ReviewModal.tsx b/src/plugins/reviewDB/components/ReviewModal.tsx index 6e85dc2..9669a2b 100644 --- a/src/plugins/reviewDB/components/ReviewModal.tsx +++ b/src/plugins/reviewDB/components/ReviewModal.tsx @@ -21,8 +21,8 @@ import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, Mo import { useForceUpdater } from "@utils/react"; import { Paginator, Text, useRef, useState } from "@webpack/common"; +import { Auth } from "../auth"; import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; -import { settings } from "../settings"; import { cl } from "../utils"; import ReviewComponent from "./ReviewComponent"; import ReviewsView, { ReviewsInputComponent } from "./ReviewsView"; @@ -35,7 +35,7 @@ function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: st const ref = useRef(null); const reviewCount = data?.reviewCount; - const ownReview = data?.reviews.find(r => r.sender.discordID === settings.store.user?.discordID); + const ownReview = data?.reviews.find(r => r.sender.discordID === Auth.user?.discordID); return ( @@ -68,6 +68,7 @@ function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: st )} (null); const inputType = InputTypes.FORM; inputType.disableAutoFocus = true; diff --git a/src/plugins/reviewDB/entities.ts b/src/plugins/reviewDB/entities.ts index fefb1d3..0a77fef 100644 --- a/src/plugins/reviewDB/entities.ts +++ b/src/plugins/reviewDB/entities.ts @@ -36,6 +36,11 @@ export const enum NotificationType { Warning = 3 } +export interface ReviewDBAuth { + token?: string; + user?: ReviewDBUser; +} + export interface Badge { name: string; description: string; diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index a519713..b9350ba 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -23,16 +23,17 @@ import ErrorBoundary from "@components/ErrorBoundary"; import ExpandableHeader from "@components/ExpandableHeader"; import { OpenExternalIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; -import { Alerts, Menu, Parser, useState } from "@webpack/common"; +import { Alerts, Menu, Parser, showToast, useState } from "@webpack/common"; import { Guild, User } from "discord-types/general"; +import { Auth, initAuth, updateAuth } from "./auth"; import { openReviewsModal } from "./components/ReviewModal"; import ReviewsView from "./components/ReviewsView"; import { NotificationType } from "./entities"; import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; import { settings } from "./settings"; -import { showToast } from "./utils"; const guildPopoutPatch: NavContextMenuPatchCallback = (children, props: { guild: Guild, onClose(): void; }) => () => { children.push( @@ -62,31 +63,48 @@ export default definePlugin({ } ], + flux: { + CONNECTION_OPEN: initAuth, + }, + async start() { + addContextMenuPatch("guild-header-popout", guildPopoutPatch); + const s = settings.store; - const { token, lastReviewId, notifyReviews } = s; + const { lastReviewId, notifyReviews } = s; + + const legacy = s as any as { token?: string; }; + if (legacy.token) { + await updateAuth({ token: legacy.token }); + legacy.token = undefined; + new Logger("ReviewDB").info("Migrated legacy settings"); + } - if (!notifyReviews || !token) return; + await initAuth(); setTimeout(async () => { - const user = await getCurrentUserInfo(token); - if (lastReviewId && lastReviewId < user.lastReviewID) { - s.lastReviewId = user.lastReviewID; - if (user.lastReviewID !== 0) - showToast("You have new reviews on your profile!"); - } + if (!Auth.token) return; + + const user = await getCurrentUserInfo(Auth.token); + updateAuth({ user }); - addContextMenuPatch("guild-header-popout", guildPopoutPatch); + if (notifyReviews) { + if (lastReviewId && lastReviewId < user.lastReviewID) { + s.lastReviewId = user.lastReviewID; + if (user.lastReviewID !== 0) + showToast("You have new reviews on your profile!"); + } + } if (user.notification) { const props = user.notification.type === NotificationType.Ban ? { cancelText: "Appeal", confirmText: "Ok", - onCancel: () => + onCancel: async () => VencordNative.native.openExternal( "https://reviewdb.mantikafasi.dev/api/redirect?" + new URLSearchParams({ - token: settings.store.token!, + token: Auth.token!, page: "dashboard/appeal" }) ) @@ -105,7 +123,6 @@ export default definePlugin({ readNotification(user.notification.id); } - s.user = user; }, 4000); }, diff --git a/src/plugins/reviewDB/reviewDbApi.ts b/src/plugins/reviewDB/reviewDbApi.ts index 5370a9b..add16dd 100644 --- a/src/plugins/reviewDB/reviewDbApi.ts +++ b/src/plugins/reviewDB/reviewDbApi.ts @@ -16,9 +16,11 @@ * along with this program. If not, see . */ +import { showToast, Toasts } from "@webpack/common"; + +import { authorize, getToken } from "./auth"; import { Review, ReviewDBUser } from "./entities"; import { settings } from "./settings"; -import { authorize, showToast } from "./utils"; const API_URL = "https://manti.vendicated.dev"; @@ -57,7 +59,7 @@ export async function getReviews(id: string, offset = 0): Promise { }; if (!res.success) { - showToast(res.message); + showToast(res.message, Toasts.Type.FAILURE); return { ...res, reviews: [ @@ -82,7 +84,7 @@ export async function getReviews(id: string, offset = 0): Promise { } export async function addReview(review: any): Promise { - review.token = settings.store.token; + review.token = await getToken(); if (!review.token) { showToast("Please authorize to add a review."); @@ -104,7 +106,7 @@ export async function addReview(review: any): Promise { }); } -export function deleteReview(id: number): Promise { +export async function deleteReview(id: number): Promise { return fetch(API_URL + `/api/reviewdb/users/${id}/reviews`, { method: "DELETE", headers: new Headers({ @@ -112,7 +114,7 @@ export function deleteReview(id: number): Promise { Accept: "application/json", }), body: JSON.stringify({ - token: settings.store.token, + token: await getToken(), reviewid: id }) }).then(r => r.json()); @@ -127,7 +129,7 @@ export async function reportReview(id: number) { }), body: JSON.stringify({ reviewid: id, - token: settings.store.token + token: await getToken() }) }).then(r => r.json()) as Response; @@ -141,11 +143,11 @@ export function getCurrentUserInfo(token: string): Promise { }).then(r => r.json()); } -export function readNotification(id: number) { +export async function readNotification(id: number) { return fetch(API_URL + `/api/reviewdb/notifications?id=${id}`, { method: "PATCH", headers: { - "Authorization": settings.store.token || "", + "Authorization": await getToken() || "", }, }); } diff --git a/src/plugins/reviewDB/settings.tsx b/src/plugins/reviewDB/settings.tsx index e318bc7..cf61a38 100644 --- a/src/plugins/reviewDB/settings.tsx +++ b/src/plugins/reviewDB/settings.tsx @@ -20,8 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { OptionType } from "@utils/types"; import { Button } from "@webpack/common"; -import { ReviewDBUser } from "./entities"; -import { authorize } from "./utils"; +import { authorize, getToken } from "./auth"; export const settings = definePluginSettings({ authorize: { @@ -57,10 +56,11 @@ export const settings = definePluginSettings({ type: OptionType.COMPONENT, description: "ReviewDB website", component: () => ( -