[ReviewDB] add emojis, discord markdown & notifications (#1718)
Co-authored-by: V <vendicated@riseup.net>
This commit is contained in:
parent
9550b74b2a
commit
e5c0898dd6
7 changed files with 170 additions and 93 deletions
|
@ -28,8 +28,8 @@ export default function ReviewBadge(badge: Badge) {
|
||||||
{({ onMouseEnter, onMouseLeave }) => (
|
{({ onMouseEnter, onMouseLeave }) => (
|
||||||
<img
|
<img
|
||||||
className={cl("badge")}
|
className={cl("badge")}
|
||||||
width="24px"
|
width="22px"
|
||||||
height="24px"
|
height="22px"
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
src={badge.icon}
|
src={badge.icon}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { openUserProfile } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { filters, findBulk } from "@webpack";
|
import { filters, findBulk } from "@webpack";
|
||||||
import { Alerts, moment, Timestamp, UserStore } from "@webpack/common";
|
import { Alerts, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { Review, ReviewType } from "../entities";
|
import { Review, ReviewType } from "../entities";
|
||||||
import { deleteReview, reportReview } from "../reviewDbApi";
|
import { deleteReview, reportReview } from "../reviewDbApi";
|
||||||
|
@ -30,12 +30,12 @@ import { DeleteButton, ReportButton } from "./MessageButton";
|
||||||
import ReviewBadge from "./ReviewBadge";
|
import ReviewBadge from "./ReviewBadge";
|
||||||
|
|
||||||
export default LazyComponent(() => {
|
export default LazyComponent(() => {
|
||||||
// this is terrible, blame mantika
|
// this is terrible, blame ven
|
||||||
const p = filters.byProps;
|
const p = filters.byProps;
|
||||||
const [
|
const [
|
||||||
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
||||||
{ container, isHeader },
|
{ container, isHeader },
|
||||||
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
{ avatar, clickable, username, wrapper, cozy },
|
||||||
buttonClasses,
|
buttonClasses,
|
||||||
botTag
|
botTag
|
||||||
] = findBulk(
|
] = findBulk(
|
||||||
|
@ -124,12 +124,10 @@ export default LazyComponent(() => {
|
||||||
</Timestamp>)
|
</Timestamp>)
|
||||||
}
|
}
|
||||||
|
|
||||||
<p
|
<div className={cl("review-comment")}>
|
||||||
className={classes(messageContent)}
|
{Parser.parseGuildEventDescription(review.comment)}
|
||||||
style={{ fontSize: 15, marginTop: 4, color: "var(--text-normal)" }}
|
</div>
|
||||||
>
|
|
||||||
{review.comment}
|
|
||||||
</p>
|
|
||||||
{review.id !== 0 && (
|
{review.id !== 0 && (
|
||||||
<div className={classes(container, isHeader, buttons)} style={{
|
<div className={classes(container, isHeader, buttons)} style={{
|
||||||
padding: "0px",
|
padding: "0px",
|
||||||
|
|
|
@ -16,11 +16,9 @@
|
||||||
* 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 { classes } from "@utils/misc";
|
import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react";
|
||||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
import { find, findByPropsLazy } from "@webpack";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
|
||||||
import { Forms, React, RelationshipStore, UserStore } from "@webpack/common";
|
|
||||||
import type { KeyboardEvent } from "react";
|
|
||||||
|
|
||||||
import { Review } from "../entities";
|
import { Review } from "../entities";
|
||||||
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
|
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
|
||||||
|
@ -28,7 +26,12 @@ import { settings } from "../settings";
|
||||||
import { authorize, cl, showToast } from "../utils";
|
import { authorize, cl, showToast } from "../utils";
|
||||||
import ReviewComponent from "./ReviewComponent";
|
import ReviewComponent from "./ReviewComponent";
|
||||||
|
|
||||||
const Classes = findByPropsLazy("inputDefault", "editable");
|
|
||||||
|
const Editor = findByPropsLazy("start", "end", "addMark");
|
||||||
|
const Transform = findByPropsLazy("unwrapNodes");
|
||||||
|
const InputTypes = findByPropsLazy("VOICE_CHANNEL_STATUS", "SIDEBAR");
|
||||||
|
|
||||||
|
const InputComponent = LazyComponent(() => find(m => m?.type?.render?.toString().includes("CHANNEL_TEXT_AREA).AnalyticsLocationProvider")));
|
||||||
|
|
||||||
interface UserProps {
|
interface UserProps {
|
||||||
discordId: string;
|
discordId: string;
|
||||||
|
@ -113,34 +116,38 @@ function ReviewList({ refetch, reviews, hideOwnReview }: { refetch(): void; revi
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
|
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
|
||||||
const { token } = settings.store;
|
const { token } = settings.store;
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
const inputType = InputTypes.FORM;
|
||||||
|
inputType.disableAutoFocus = true;
|
||||||
|
|
||||||
function onKeyPress({ key, target }: KeyboardEvent<HTMLTextAreaElement>) {
|
const channel = {
|
||||||
if (key === "Enter") {
|
flags_: 256,
|
||||||
addReview({
|
guild_id_: null,
|
||||||
userid: discordId,
|
id: "0",
|
||||||
comment: (target as HTMLInputElement).value,
|
getGuildId: () => null,
|
||||||
star: -1
|
isPrivate: () => true,
|
||||||
}).then(res => {
|
isActiveThread: () => false,
|
||||||
if (res?.success) {
|
isArchivedLockedThread: () => false,
|
||||||
(target as HTMLInputElement).value = ""; // clear the input
|
isDM: () => true,
|
||||||
refetch();
|
roles: { "0": { permissions: 0n } },
|
||||||
} else if (res?.message) {
|
getRecipientId: () => "0",
|
||||||
showToast(res.message);
|
hasFlag: () => false,
|
||||||
}
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<textarea
|
<>
|
||||||
className={classes(Classes.inputDefault, "enter-comment", cl("input"))}
|
<div onClick={() => {
|
||||||
onKeyDownCapture={e => {
|
if (!token) {
|
||||||
if (e.key === "Enter") {
|
showToast("Opening authorization window...");
|
||||||
e.preventDefault(); // prevent newlines
|
authorize();
|
||||||
}
|
}
|
||||||
}}
|
}}>
|
||||||
|
<InputComponent
|
||||||
|
className={cl("input")}
|
||||||
|
channel={channel}
|
||||||
placeholder={
|
placeholder={
|
||||||
!token
|
!token
|
||||||
? "You need to authorize to review users!"
|
? "You need to authorize to review users!"
|
||||||
|
@ -148,13 +155,43 @@ export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: {
|
||||||
? `Update review for @${name}`
|
? `Update review for @${name}`
|
||||||
: `Review @${name}`
|
: `Review @${name}`
|
||||||
}
|
}
|
||||||
onKeyDown={onKeyPress}
|
type={inputType}
|
||||||
onClick={() => {
|
disableThemedBackground={true}
|
||||||
if (!token) {
|
setEditorRef={ref => editorRef.current = ref}
|
||||||
showToast("Opening authorization window...");
|
textValue=""
|
||||||
authorize();
|
onSubmit={
|
||||||
|
async res => {
|
||||||
|
const response = await addReview({
|
||||||
|
userid: discordId,
|
||||||
|
comment: res.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response?.success) {
|
||||||
|
refetch();
|
||||||
|
|
||||||
|
const slateEditor = editorRef.current.ref.current.getSlateEditor();
|
||||||
|
|
||||||
|
// clear editor
|
||||||
|
Transform.delete(slateEditor, {
|
||||||
|
at: {
|
||||||
|
anchor: Editor.start(slateEditor, []),
|
||||||
|
focus: Editor.end(slateEditor, []),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (response?.message) {
|
||||||
|
showToast(response.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// even tho we need to return this, it doesnt do anything
|
||||||
|
return {
|
||||||
|
shouldClear: false,
|
||||||
|
shouldRefocus: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,13 @@ export const enum ReviewType {
|
||||||
System = 3
|
System = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum NotificationType {
|
||||||
|
Info = 0,
|
||||||
|
Ban = 1,
|
||||||
|
Unban = 2,
|
||||||
|
Warning = 3
|
||||||
|
}
|
||||||
|
|
||||||
export interface Badge {
|
export interface Badge {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
@ -45,6 +52,13 @@ export interface BanInfo {
|
||||||
banEndDate: number;
|
banEndDate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Notification {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
type: NotificationType;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReviewDBUser {
|
export interface ReviewDBUser {
|
||||||
ID: number;
|
ID: number;
|
||||||
discordID: string;
|
discordID: string;
|
||||||
|
@ -54,6 +68,7 @@ export interface ReviewDBUser {
|
||||||
warningCount: number;
|
warningCount: number;
|
||||||
badges: any[];
|
badges: any[];
|
||||||
banInfo: BanInfo | null;
|
banInfo: BanInfo | null;
|
||||||
|
notification: Notification | null;
|
||||||
lastReviewID: number;
|
lastReviewID: number;
|
||||||
type: UserType;
|
type: UserType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,13 @@ import ExpandableHeader from "@components/ExpandableHeader";
|
||||||
import { OpenExternalIcon } from "@components/Icons";
|
import { OpenExternalIcon } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Alerts, Menu, useState } from "@webpack/common";
|
import { Alerts, Menu, Parser, useState } from "@webpack/common";
|
||||||
import { Guild, User } from "discord-types/general";
|
import { Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
import { openReviewsModal } from "./components/ReviewModal";
|
import { openReviewsModal } from "./components/ReviewModal";
|
||||||
import ReviewsView from "./components/ReviewsView";
|
import ReviewsView from "./components/ReviewsView";
|
||||||
import { UserType } from "./entities";
|
import { NotificationType } from "./entities";
|
||||||
import { getCurrentUserInfo } from "./reviewDbApi";
|
import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { showToast } from "./utils";
|
import { showToast } from "./utils";
|
||||||
|
|
||||||
|
@ -78,26 +78,8 @@ export default definePlugin({
|
||||||
|
|
||||||
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
|
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
|
||||||
|
|
||||||
if (user.banInfo) {
|
if (user.notification) {
|
||||||
const endDate = new Date(user.banInfo.banEndDate);
|
const props = user.notification.type === NotificationType.Ban ? {
|
||||||
if (endDate.getTime() > Date.now() && (s.user?.banInfo?.banEndDate ?? 0) < endDate.getTime()) {
|
|
||||||
Alerts.show({
|
|
||||||
title: "You have been banned from ReviewDB",
|
|
||||||
body: (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
You are banned from ReviewDB {
|
|
||||||
user.type === UserType.Banned
|
|
||||||
? "permanently"
|
|
||||||
: "until " + endDate.toLocaleString()
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
{user.banInfo.reviewContent && (
|
|
||||||
<p>Offending Review: {user.banInfo.reviewContent}</p>
|
|
||||||
)}
|
|
||||||
<p>Continued offenses will result in a permanent ban.</p>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
cancelText: "Appeal",
|
cancelText: "Appeal",
|
||||||
confirmText: "Ok",
|
confirmText: "Ok",
|
||||||
onCancel: () =>
|
onCancel: () =>
|
||||||
|
@ -108,10 +90,21 @@ export default definePlugin({
|
||||||
page: "dashboard/appeal"
|
page: "dashboard/appeal"
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
});
|
} : {};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Alerts.show({
|
||||||
|
title: user.notification.title,
|
||||||
|
body: (
|
||||||
|
Parser.parse(
|
||||||
|
user.notification.content,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
),
|
||||||
|
...props
|
||||||
|
});
|
||||||
|
|
||||||
|
readNotification(user.notification.id);
|
||||||
|
}
|
||||||
s.user = user;
|
s.user = user;
|
||||||
}, 4000);
|
}, 4000);
|
||||||
},
|
},
|
||||||
|
|
|
@ -140,3 +140,12 @@ export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}).then(r => r.json());
|
}).then(r => r.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readNotification(id: number) {
|
||||||
|
return fetch(API_URL + `/api/reviewdb/notifications?id=${id}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"Authorization": settings.store.token || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,16 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid var(--profile-message-input-border-color);
|
border: 1px solid var(--profile-message-input-border-color);
|
||||||
font-size: 14px;
|
}
|
||||||
|
|
||||||
|
.vc-rdb-modal-footer > div {
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When input becomes disabled(while sending review), input adds unneccesary padding to left, this prevents it */
|
||||||
|
.vc-rdb-input > div > div {
|
||||||
|
padding-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-placeholder {
|
.vc-rdb-placeholder {
|
||||||
|
@ -24,13 +33,12 @@
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer {
|
.vc-rdb-input * {
|
||||||
padding: 0;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer > div {
|
.vc-rdb-modal-footer {
|
||||||
width: 100%;
|
padding: 0;
|
||||||
margin: 6px 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer .vc-rdb-input {
|
.vc-rdb-modal-footer .vc-rdb-input {
|
||||||
|
@ -49,3 +57,20 @@
|
||||||
.vc-rdb-modal-reviews {
|
.vc-rdb-modal-reviews {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review-comment img {
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review-comment {
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--text-normal);
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue