ReviewDB: make warning review disableable; add timestamps (#948)

This commit is contained in:
Manti 2023-04-30 01:53:37 +03:00 committed by GitHub
parent 5b485806ea
commit 043381963b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 23 deletions

View File

@ -19,6 +19,7 @@
import { Settings } from "@api/settings"; import { Settings } from "@api/settings";
import { Review } from "../entities/Review"; import { Review } from "../entities/Review";
import { ReviewDBUser } from "../entities/User";
import { authorize, showToast } from "./Utils"; import { authorize, showToast } from "./Utils";
const API_URL = "https://manti.vendicated.dev"; const API_URL = "https://manti.vendicated.dev";
@ -32,8 +33,12 @@ interface Response {
updated: boolean; updated: boolean;
} }
const WarningFlag = 0b00000010;
export async function getReviews(id: string): Promise<Review[]> { export async function getReviews(id: string): Promise<Review[]> {
const req = await fetch(API_URL + `/api/reviewdb/users/${id}/reviews`); var flags = 0;
if (!Settings.plugins.ReviewDB.showWarning) flags |= WarningFlag;
const req = await fetch(API_URL + `/api/reviewdb/users/${id}/reviews?flags=${flags}`);
const res = (req.status === 200) ? await req.json() as Response : { success: false, message: "An Error occured while fetching reviews. Please try again later.", reviews: [], updated: false }; const res = (req.status === 200) ? await req.json() as Response : { success: false, message: "An Error occured while fetching reviews. Please try again later.", reviews: [], updated: false };
if (!res.success) { if (!res.success) {
@ -43,6 +48,7 @@ export async function getReviews(id: string): Promise<Review[]> {
id: 0, id: 0,
comment: "An Error occured while fetching reviews. Please try again later.", comment: "An Error occured while fetching reviews. Please try again later.",
star: 0, star: 0,
timestamp: 0,
sender: { sender: {
id: 0, id: 0,
username: "Error", username: "Error",
@ -108,8 +114,10 @@ export async function reportReview(id: number) {
showToast(await res.message); showToast(await res.message);
} }
export function getLastReviewID(id: string): Promise<number> { export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> {
return fetch(API_URL + "/getLastReviewID?discordid=" + id) return fetch(API_URL + "/api/reviewdb/users", {
.then(r => r.text()) body: JSON.stringify({ token }),
.then(Number); method: "POST",
})
.then(r => r.json());
} }

View File

@ -17,13 +17,13 @@
*/ */
import { Settings } from "@api/settings"; import { Settings } from "@api/settings";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger"; import Logger from "@utils/Logger";
import { openModal } from "@utils/modal"; import { openModal } from "@utils/modal";
import { findByProps } from "@webpack"; import { findByProps } from "@webpack";
import { FluxDispatcher, React, SelectedChannelStore, Toasts, UserUtils } from "@webpack/common"; import { FluxDispatcher, React, SelectedChannelStore, Toasts, UserUtils } from "@webpack/common";
import { Review } from "../entities/Review"; import { Review } from "../entities/Review";
import { UserType } from "../entities/User";
export async function openUserProfileModal(userId: string) { export async function openUserProfileModal(userId: string) {
await UserUtils.fetchUser(userId); await UserUtils.fetchUser(userId);
@ -86,10 +86,5 @@ export function showToast(text: string) {
export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
export function canDeleteReview(review: Review, userId: string) { export function canDeleteReview(review: Review, userId: string) {
if (review.sender.discordID === userId) return true; if (review.sender.discordID === userId || Settings.plugins.ReviewDB.userType === UserType.Admin) return true;
const myId = BigInt(userId);
return myId === Devs.mantikafasi.id ||
myId === Devs.Ven.id ||
myId === Devs.rushii.id;
} }

View File

@ -16,9 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/settings";
import { classes, LazyComponent } from "@utils/misc"; import { classes, LazyComponent } from "@utils/misc";
import { filters, findBulk } from "@webpack"; import { filters, findBulk } from "@webpack";
import { Alerts, UserStore } from "@webpack/common"; import { Alerts, moment, Timestamp, UserStore } from "@webpack/common";
import { Review } from "../entities/Review"; import { Review } from "../entities/Review";
import { deleteReview, reportReview } from "../Utils/ReviewDBAPI"; import { deleteReview, reportReview } from "../Utils/ReviewDBAPI";
@ -32,7 +33,7 @@ export default LazyComponent(() => {
const [ const [
{ cozyMessage, buttons, message, groupStart }, { cozyMessage, buttons, message, groupStart },
{ container, isHeader }, { container, isHeader },
{ avatar, clickable, username, messageContent, wrapper, cozy }, { avatar, clickable, username, messageContent, wrapper, cozy, timestampInline, timestamp },
{ contents }, { contents },
buttonClasses, buttonClasses,
{ defaultColor } { defaultColor }
@ -102,6 +103,16 @@ export default LazyComponent(() => {
{review.sender.username} {review.sender.username}
</span> </span>
{review.sender.badges.map(badge => <ReviewBadge {...badge} />)} {review.sender.badges.map(badge => <ReviewBadge {...badge} />)}
{
!Settings.plugins.ReviewDB.hideTimestamps && (
<Timestamp
timestamp={moment(review.timestamp * 1000)}
compact={true}
/>
)
}
<p <p
className={classes(messageContent, defaultColor)} className={classes(messageContent, defaultColor)}
style={{ fontSize: 15, marginTop: 4 }} style={{ fontSize: 15, marginTop: 4 }}

View File

@ -16,18 +16,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/settings";
import { classes, useAwaiter } from "@utils/misc"; import { classes, useAwaiter } from "@utils/misc";
import { findLazy } from "@webpack"; import { findLazy } from "@webpack";
import { Forms, React, Text, UserStore } from "@webpack/common"; import { Forms, React, Text, UserStore } from "@webpack/common";
import type { KeyboardEvent } from "react"; import type { KeyboardEvent } from "react";
import { addReview, getReviews } from "../Utils/ReviewDBAPI"; import { addReview, getReviews } from "../Utils/ReviewDBAPI";
import { showToast } from "../Utils/Utils"; import { authorize, showToast } from "../Utils/Utils";
import ReviewComponent from "./ReviewComponent"; import ReviewComponent from "./ReviewComponent";
const Classes = findLazy(m => typeof m.textarea === "string"); const Classes = findLazy(m => typeof m.textarea === "string");
export default function ReviewsView({ userId }: { userId: string; }) { export default function ReviewsView({ userId }: { userId: string; }) {
const { token } = Settings.plugins.ReviewDB;
const [refetchCount, setRefetchCount] = React.useState(0); const [refetchCount, setRefetchCount] = React.useState(0);
const [reviews, _, isLoading] = useAwaiter(() => getReviews(userId), { const [reviews, _, isLoading] = useAwaiter(() => getReviews(userId), {
fallbackValue: [], fallbackValue: [],
@ -83,8 +85,21 @@ export default function ReviewsView({ userId }: { userId: string; }) {
<textarea <textarea
className={classes(Classes.textarea.replace("textarea", ""), "enter-comment")} className={classes(Classes.textarea.replace("textarea", ""), "enter-comment")}
// this produces something like '-_59yqs ...' but since no class exists with that name its fine // this produces something like '-_59yqs ...' but since no class exists with that name its fine
placeholder={reviews?.some(r => r.sender.discordID === UserStore.getCurrentUser().id) ? `Update review for @${username}` : `Review @${username}`} placeholder={
token
? (reviews?.some(r => r.sender.discordID === UserStore.getCurrentUser().id)
? `Update review for @${username}`
: `Review @${username}`)
: "You need to authorize to review users!"
}
onKeyDown={onKeyPress} onKeyDown={onKeyPress}
onClick={() => {
if (!token) {
showToast("Opening authorization window...");
authorize();
}
}}
style={{ style={{
marginTop: "6px", marginTop: "6px",
resize: "none", resize: "none",

View File

@ -31,4 +31,5 @@ export interface Review {
id: number, id: number,
star: number, star: number,
sender: Sender, sender: Sender,
timestamp: number
} }

View File

@ -0,0 +1,28 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 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 const enum UserType {
Banned = -1,
Normal = 0,
Admin = 1
}
export interface ReviewDBUser {
lastReviewID: number,
type: UserType;
}

View File

@ -22,11 +22,11 @@ import { Settings } from "@api/settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Button, UserStore } from "@webpack/common"; import { Button } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import ReviewsView from "./components/ReviewsView"; import ReviewsView from "./components/ReviewsView";
import { getLastReviewID } from "./Utils/ReviewDBAPI"; import { getCurrentUserInfo } from "./Utils/ReviewDBAPI";
import { authorize, showToast } from "./Utils/Utils"; import { authorize, showToast } from "./Utils/Utils";
export default definePlugin({ export default definePlugin({
@ -58,19 +58,31 @@ export default definePlugin({
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
description: "Notify about new reviews on startup", description: "Notify about new reviews on startup",
default: true, default: true,
},
showWarning: {
type: OptionType.BOOLEAN,
description: "Display warning to be respectful at the top of the reviews list",
default: true,
},
hideTimestamps: {
type: OptionType.BOOLEAN,
description: "Hide timestamps on reviews",
default: false,
} }
}, },
async start() { async start() {
const settings = Settings.plugins.ReviewDB; const settings = Settings.plugins.ReviewDB;
if (!settings.lastReviewId || !settings.notifyReviews) return; if (!settings.notifyReviews || !settings.token) return;
setTimeout(async () => { setTimeout(async () => {
const id = await getLastReviewID(UserStore.getCurrentUser().id); const user = await getCurrentUserInfo(settings.token);
if (settings.lastReviewId < id) { if (settings.lastReviewId < user.lastReviewID) {
showToast("You have new reviews on your profile!"); settings.lastReviewId = user.lastReviewID;
settings.lastReviewId = id; if (user.lastReviewID !== 0)
showToast("You have new reviews on your profile!");
} }
settings.userType = user.type;
}, 4000); }, 4000);
}, },