ReviewDB: proper multi account support

This commit is contained in:
Vendicated 2023-10-06 19:40:53 +02:00
parent 664dd0a992
commit c0f2c97458
No known key found for this signature in database
GPG Key ID: D66986BAF75ECF18
9 changed files with 143 additions and 90 deletions

View File

@ -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<ReviewDBAuth | undefined> {
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 =>
<OAuth.OAuth2AuthorizeModal
{...props}
scopes={["identify"]}
responseType="code"
redirectUri="https://manti.vendicated.dev/api/reviewdb/auth"
permissions={0n}
clientId="915703782174752809"
cancelCompletesFlow={false}
callback={async (response: any) => {
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);
}
}}
/>
);
}

View File

@ -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";

View File

@ -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<HTMLDivElement>(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 (
<ErrorBoundary>
@ -68,6 +68,7 @@ function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: st
<ReviewComponent
refetch={refetch}
review={ownReview}
profileId={discordId}
/>
)}
<ReviewsInputComponent

View File

@ -18,12 +18,13 @@
import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react";
import { find, findByPropsLazy } from "@webpack";
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
import { Forms, React, RelationshipStore, showToast, useRef, UserStore } from "@webpack/common";
import { Auth, authorize } from "../auth";
import { Review } from "../entities";
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
import { settings } from "../settings";
import { authorize, cl, showToast } from "../utils";
import { cl } from "../utils";
import ReviewComponent from "./ReviewComponent";
@ -120,7 +121,7 @@ function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch():
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
const { token } = settings.store;
const { token } = Auth;
const editorRef = useRef<any>(null);
const inputType = InputTypes.FORM;
inputType.disableAutoFocus = true;

View File

@ -36,6 +36,11 @@ export const enum NotificationType {
Warning = 3
}
export interface ReviewDBAuth {
token?: string;
user?: ReviewDBUser;
}
export interface Badge {
name: string;
description: string;

View File

@ -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({
}
],
async start() {
const s = settings.store;
const { token, lastReviewId, notifyReviews } = s;
flux: {
CONNECTION_OPEN: initAuth,
},
if (!notifyReviews || !token) return;
async start() {
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
const s = settings.store;
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");
}
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;
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
const user = await getCurrentUserInfo(Auth.token);
updateAuth({ user });
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);
},

View File

@ -16,9 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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<Response> {
};
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<Response> {
}
export async function addReview(review: any): Promise<Response | null> {
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<Response | null> {
});
}
export function deleteReview(id: number): Promise<Response> {
export async function deleteReview(id: number): Promise<Response> {
return fetch(API_URL + `/api/reviewdb/users/${id}/reviews`, {
method: "DELETE",
headers: new Headers({
@ -112,7 +114,7 @@ export function deleteReview(id: number): Promise<Response> {
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<ReviewDBUser> {
}).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() || "",
},
});
}

View File

@ -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: () => (
<Button onClick={() => {
<Button onClick={async () => {
let url = "https://reviewdb.mantikafasi.dev/";
if (settings.store.token)
url += "/api/redirect?token=" + encodeURIComponent(settings.store.token);
const token = await getToken();
if (token)
url += "/api/redirect?token=" + encodeURIComponent(token);
VencordNative.native.openExternal(url);
}}>
@ -80,8 +80,6 @@ export const settings = definePluginSettings({
)
}
}).withPrivateSettings<{
token?: string;
user?: ReviewDBUser;
lastReviewId?: number;
reviewsDropdownState?: boolean;
}>();

View File

@ -17,67 +17,18 @@
*/
import { classNameFactory } from "@api/Styles";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { findByProps } from "@webpack";
import { React, Toasts, UserStore } from "@webpack/common";
import { UserStore } from "@webpack/common";
import { Auth } from "./auth";
import { Review, UserType } from "./entities";
import { settings } from "./settings";
export const cl = classNameFactory("vc-rdb-");
export function authorize(callback?: any) {
const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal");
openModal((props: any) =>
<OAuth2AuthorizeModal
{...props}
scopes={["identify"]}
responseType="code"
redirectUri="https://manti.vendicated.dev/api/reviewdb/auth"
permissions={0n}
clientId="915703782174752809"
cancelCompletesFlow={false}
callback={async (response: any) => {
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) {
settings.store.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 authorize", e);
}
}}
/>
);
}
export function showToast(text: string) {
Toasts.show({
type: Toasts.Type.MESSAGE,
message: text,
id: Toasts.genId(),
options: {
position: Toasts.Position.BOTTOM
},
});
}
export function canDeleteReview(profileId: string, review: Review) {
const myId = UserStore.getCurrentUser().id;
return (
myId === profileId
|| review.sender.discordID === profileId
|| settings.store.user?.type === UserType.Admin
|| Auth.user?.type === UserType.Admin
);
}