Merge branch 'dev' into emoteCloner

This commit is contained in:
sadan4 2024-09-04 20:20:42 -04:00 committed by GitHub
commit b3bf32abd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 132 additions and 93 deletions

View file

@ -138,6 +138,8 @@ export function registerCommand<C extends Command>(command: C, plugin: string) {
throw new Error(`Command '${command.name}' already exists.`);
command.isVencordCommand = true;
command.untranslatedName ??= command.name;
command.untranslatedDescription ??= command.description;
command.id ??= `-${BUILT_IN.length + 1}`;
command.applicationId ??= "-1"; // BUILT_IN;
command.type ??= ApplicationCommandType.CHAT_INPUT;

View file

@ -93,8 +93,10 @@ export interface Command {
isVencordCommand?: boolean;
name: string;
untranslatedName?: string;
displayName?: string;
description: string;
untranslatedDescription?: string;
displayDescription?: string;
options?: Option[];

View file

@ -34,7 +34,7 @@ export default definePlugin({
{
find: "Messages.SERVERS,children",
replacement: {
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/,
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
}
}

View file

@ -132,8 +132,8 @@ export default definePlugin({
},
// Export the isBetterFolders variable to the folders component
{
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/,
replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
}
]
},

View file

@ -46,7 +46,7 @@ const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
async function embedDidMount(this: Component<Props>) {
try {
const { embed } = this.props;
const { replaceElements } = settings.store;
const { replaceElements, dearrowByDefault } = settings.store;
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
@ -63,18 +63,22 @@ async function embedDidMount(this: Component<Props>) {
if (!hasTitle && !hasThumb) return;
embed.dearrow = {
enabled: true
enabled: dearrowByDefault
};
if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) {
embed.dearrow.oldTitle = embed.rawTitle;
embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
}
const replacementTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
embed.dearrow.oldTitle = dearrowByDefault ? embed.rawTitle : replacementTitle;
if (dearrowByDefault) embed.rawTitle = replacementTitle;
}
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL;
if (dearrowByDefault) embed.thumbnail.proxyURL = replacementProxyURL;
}
this.forceUpdate();
@ -96,6 +100,7 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")}
onClick={() => {
const { enabled, oldThumb, oldTitle } = embed.dearrow;
settings.store.dearrowByDefault = !enabled;
embed.dearrow.enabled = !enabled;
if (oldTitle) {
embed.dearrow.oldTitle = embed.rawTitle;
@ -153,6 +158,12 @@ const settings = definePluginSettings({
{ label: "Titles", value: ReplaceElements.ReplaceTitlesOnly },
{ label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly },
],
},
dearrowByDefault: {
description: "Dearrow videos automatically",
type: OptionType.BOOLEAN,
default: true,
restartNeeded: false
}
});

View file

@ -7,16 +7,15 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord";
import { Logger } from "@utils/Logger";
import definePlugin from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
import { Heading, RelationshipStore, Text } from "@webpack/common";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { RelationshipStore, Text } from "@webpack/common";
const containerWrapper = findByPropsLazy("memberSinceWrapper");
const container = findByPropsLazy("memberSince");
const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"');
const locale = findByPropsLazy("getLocale");
const section = findLazy((m: any) => m.section !== void 0 && m.heading !== void 0 && Object.values(m).length === 2);
const Section = findComponentByCodeLazy('"auto":"smooth"', ".section");
export default definePlugin({
name: "FriendsSince",
@ -28,7 +27,7 @@ export default definePlugin({
find: ".PANEL}),nicknameIcons",
replacement: {
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id)}\)}\)/,
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:true})"
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})"
}
},
// User Profile Modal
@ -36,34 +35,19 @@ export default definePlugin({
find: "action:\"PRESS_APP_CONNECTION\"",
replacement: {
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:false}),"
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false}),"
}
}
],
getFriendSince(userId: string) {
try {
if (!RelationshipStore.isFriend(userId)) return null;
return RelationshipStore.getSince(userId);
} catch (err) {
new Logger("FriendsSince").error(err);
return null;
}
},
friendsSinceNew: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
FriendsSinceComponent: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
if (!RelationshipStore.isFriend(userId)) return null;
const friendsSince = RelationshipStore.getSince(userId);
if (!friendsSince) return null;
return (
<section className={section.section}>
<Heading variant="text-xs/semibold" style={isSidebar ? {} : { color: "var(--header-secondary)" }}>
Friends Since
</Heading>
<Section heading="Friends Since">
{
isSidebar ? (
<Text variant="text-sm/normal">
@ -91,8 +75,7 @@ export default definePlugin({
</div>
)
}
</section>
</Section>
);
}, { noop: true }),
});

View file

@ -14,7 +14,7 @@ import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) {
const currentChannel = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
const guildId = isTooltip ? tooltipGuildId! : currentChannel.guild_id;
const guildId = isTooltip ? tooltipGuildId! : currentChannel?.guild_id;
const totalCount = useStateFromStores(
[GuildMemberCountStore],
@ -33,7 +33,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
const threadGroups = useStateFromStores(
[ThreadMemberListStore],
() => ThreadMemberListStore.getMemberListSections(currentChannel.id)
() => ThreadMemberListStore.getMemberListSections(currentChannel?.id)
);
if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) {

View file

@ -15,8 +15,8 @@ export const OnlineMemberCountStore = proxyLazy(() => {
const onlineMemberMap = new Map<string, number>();
class OnlineMemberCountStore extends Flux.Store {
getCount(guildId: string) {
return onlineMemberMap.get(guildId);
getCount(guildId?: string) {
return onlineMemberMap.get(guildId!);
}
async _ensureCount(guildId: string) {
@ -25,8 +25,8 @@ export const OnlineMemberCountStore = proxyLazy(() => {
await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
}
ensureCount(guildId: string) {
if (onlineMemberMap.has(guildId)) return;
ensureCount(guildId?: string) {
if (!guildId || onlineMemberMap.has(guildId)) return;
preloadQueue.push(() =>
this._ensureCount(guildId)

View file

@ -28,12 +28,12 @@ import { FluxStore } from "@webpack/types";
import { MemberCount } from "./MemberCount";
export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; };
export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; };
export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; };
};
export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as FluxStore & {
getMemberListSections(channelId: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
};

View file

@ -36,7 +36,7 @@ export default definePlugin({
}
],
shouldSkip(guildId: string, emoji: any) {
if (emoji.type !== "GUILD_EMOJI") {
if (emoji.type !== 1) {
return false;
}
if (settings.store.shownEmojis === "onlyUnicode") {

View file

@ -57,7 +57,7 @@ export default definePlugin({
},
{
match: /text:\i\.\i.Messages.USER_PROFILE_PRONOUNS/,
replace: '$&+vcHasPendingPronouns?"":` (${vcPronounSource})`'
replace: '$&+(vcHasPendingPronouns?"":` (${vcPronounSource})`)'
},
{
match: /(\.pronounsText.+?children:)(\i)/,

View file

@ -22,12 +22,13 @@ import { useForceUpdater } from "@utils/react";
import { Paginator, Text, useRef, useState } from "@webpack/common";
import { Auth } from "../auth";
import { ReviewType } from "../entities";
import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
import { cl } from "../utils";
import ReviewComponent from "./ReviewComponent";
import ReviewsView, { ReviewsInputComponent } from "./ReviewsView";
function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; modalKey: string, discordId: string; name: string; }) {
function Modal({ modalProps, modalKey, discordId, name, type }: { modalProps: any; modalKey: string, discordId: string; name: string; type: ReviewType; }) {
const [data, setData] = useState<Response>();
const [signal, refetch] = useForceUpdater(true);
const [page, setPage] = useState(1);
@ -58,6 +59,7 @@ function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; mod
onFetchReviews={setData}
scrollToTop={() => ref.current?.scrollTo({ top: 0, behavior: "smooth" })}
hideOwnReview
type={type}
/>
</div>
</ModalContent>
@ -95,7 +97,7 @@ function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; mod
);
}
export function openReviewsModal(discordId: string, name: string) {
export function openReviewsModal(discordId: string, name: string, type: ReviewType) {
const modalKey = "vc-rdb-modal-" + Date.now();
openModal(props => (
@ -104,6 +106,7 @@ export function openReviewsModal(discordId: string, name: string) {
modalProps={props}
discordId={discordId}
name={name}
type={type}
/>
), { modalKey });
}

View file

@ -21,7 +21,7 @@ import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpa
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
import { Auth, authorize } from "../auth";
import { Review } from "../entities";
import { Review, ReviewType } from "../entities";
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
import { settings } from "../settings";
import { cl, showToast } from "../utils";
@ -45,6 +45,7 @@ interface Props extends UserProps {
page?: number;
scrollToTop?(): void;
hideOwnReview?: boolean;
type: ReviewType;
}
export default function ReviewsView({
@ -56,6 +57,7 @@ export default function ReviewsView({
page = 1,
showInput = false,
hideOwnReview = false,
type,
}: Props) {
const [signal, refetch] = useForceUpdater(true);
@ -80,6 +82,7 @@ export default function ReviewsView({
reviews={reviewData!.reviews}
hideOwnReview={hideOwnReview}
profileId={discordId}
type={type}
/>
{showInput && (
@ -94,7 +97,7 @@ export default function ReviewsView({
);
}
function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; }) {
function ReviewList({ refetch, reviews, hideOwnReview, profileId, type }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; type: ReviewType; }) {
const myId = UserStore.getCurrentUser().id;
return (
@ -111,7 +114,7 @@ function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch():
{reviews?.length === 0 && (
<Forms.FormText className={cl("placeholder")}>
Looks like nobody reviewed this user yet. You could be the first!
Looks like nobody reviewed this {type === ReviewType.User ? "user" : "server"} yet. You could be the first!
</Forms.FormText>
)}
</div>

View file

@ -30,7 +30,7 @@ import { Guild, User } from "discord-types/general";
import { Auth, initAuth, updateAuth } from "./auth";
import { openReviewsModal } from "./components/ReviewModal";
import { NotificationType } from "./entities";
import { NotificationType, ReviewType } from "./entities";
import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
import { settings } from "./settings";
import { showToast } from "./utils";
@ -44,7 +44,7 @@ const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { gu
label="View Reviews"
id="vc-rdb-server-reviews"
icon={OpenExternalIcon}
action={() => openReviewsModal(guild.id, guild.name)}
action={() => openReviewsModal(guild.id, guild.name, ReviewType.Server)}
/>
);
};
@ -56,7 +56,7 @@ const userContextPatch: NavContextMenuPatchCallback = (children, { user }: { use
label="View Reviews"
id="vc-rdb-user-reviews"
icon={OpenExternalIcon}
action={() => openReviewsModal(user.id, user.username)}
action={() => openReviewsModal(user.id, user.username, ReviewType.User)}
/>
);
};
@ -157,7 +157,7 @@ export default definePlugin({
return (
<TooltipContainer text="View Reviews">
<Button
onClick={() => openReviewsModal(user.id, user.username)}
onClick={() => openReviewsModal(user.id, user.username, ReviewType.User)}
look={Button.Looks.FILLED}
size={Button.Sizes.NONE}
color={RoleButtonClasses.bannerColor}

View file

@ -4,21 +4,38 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import definePlugin, { OptionType } from "@utils/types";
const settings = definePluginSettings({
onlySnow: {
type: OptionType.BOOLEAN,
description: "Only play the Snow Halation Theme",
default: false,
restartNeeded: true
}
});
// NOTE - Ultimately should probably be turned into a ringtone picker plugin
export default definePlugin({
name: "SecretRingToneEnabler",
description: "Always play the secret version of the discord ringtone (except during special ringtone events)",
authors: [Devs.AndrewDLO, Devs.FieryFlames],
authors: [Devs.AndrewDLO, Devs.FieryFlames, Devs.RamziAH],
settings,
patches: [
{
find: '"call_ringing_beat"',
replacement: {
match: /500!==\i\(\)\.random\(1,1e3\)/,
replace: "false",
}
},
],
replacement: [
{
match: /500!==\i\(\)\.random\(1,1e3\)/,
replace: "false"
},
{
predicate: () => settings.store.onlySnow,
match: /"call_ringing_beat",/,
replace: ""
}
]
}
]
});

View file

@ -39,7 +39,7 @@ interface StreamData {
gainNode?: GainNode,
id: string,
levelNode: AudioWorkletNode,
sinkId: string,
sinkId: string | "default",
stream: MediaStream,
streamSourceNode?: MediaStreamAudioSourceNode,
videoStreamId: string,
@ -128,6 +128,12 @@ export default definePlugin({
gain.connect(data.audioContext.destination);
}
// @ts-expect-error
if (data.sinkId != null && data.sinkId !== data.audioContext.sinkId && "setSinkId" in AudioContext.prototype) {
// @ts-expect-error https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/setSinkId
data.audioContext.setSinkId(data.sinkId);
}
data.gainNode.gain.value = data._mute
? 0
: data._volume / 100;

View file

@ -86,7 +86,7 @@ interface NotificationObject {
title: string;
content: string;
useBase64Icon: boolean;
icon: ArrayBuffer | string;
icon: string;
sourceApp: string;
}
@ -320,23 +320,29 @@ function shouldIgnoreForChannelType(channel: Channel) {
}
function sendMsgNotif(titleString: string, content: string, message: Message) {
fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => {
const msgData: NotificationObject = {
type: 1,
timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout,
height: calculateHeight(content),
opacity: settings.store.opacity,
volume: settings.store.volume,
audioPath: settings.store.soundPath,
title: titleString,
content: content,
useBase64Icon: true,
icon: new TextDecoder().decode(result),
sourceApp: "Vencord"
};
fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`)
.then(response => response.blob())
.then(blob => new Promise<string>(resolve => {
const r = new FileReader();
r.onload = () => resolve((r.result as string).split(",")[1]);
r.readAsDataURL(blob);
})).then(result => {
const msgData: NotificationObject = {
type: 1,
timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout,
height: calculateHeight(content),
opacity: settings.store.opacity,
volume: settings.store.volume,
audioPath: settings.store.soundPath,
title: titleString,
content: content,
useBase64Icon: true,
icon: result,
sourceApp: "Vencord"
};
sendToOverlay(msgData);
});
sendToOverlay(msgData);
});
}
function sendOtherNotif(content: string, titleString: string) {

View file

@ -566,6 +566,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "niko",
id: 341377368075796483n,
},
RamziAH: {
name: "RamziAH",
id: 1279957227612147747n,
},
} satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly

View file

@ -18,7 +18,7 @@
import { MessageObject } from "@api/MessageEvents";
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import { Guild, Message, User } from "discord-types/general";
import { Channel, Guild, Message, User } from "discord-types/general";
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
@ -54,12 +54,12 @@ export async function openInviteModal(code: string) {
});
}
export function getCurrentChannel() {
export function getCurrentChannel(): Channel | undefined {
return ChannelStore.getChannel(SelectedChannelStore.getChannelId());
}
export function getCurrentGuild(): Guild | undefined {
return GuildStore.getGuild(getCurrentChannel()?.guild_id);
return GuildStore.getGuild(getCurrentChannel()?.guild_id!);
}
export function openPrivateChannel(userId: string) {

View file

@ -91,7 +91,7 @@ export type Tooltip = ComponentType<{
/** Tooltip.Colors.BLACK */
color?: string;
/** TooltipPositions.TOP */
position?: string;
position?: PopoutPosition;
tooltipClassName?: string;
tooltipContentClassName?: string;
@ -110,7 +110,7 @@ export type TooltipContainer = ComponentType<PropsWithChildren<{
/** Tooltip.Colors.BLACK */
color?: string;
/** TooltipPositions.TOP */
position?: string;
position?: PopoutPosition;
spacing?: number;
className?: string;
@ -252,7 +252,7 @@ export type Select = ComponentType<PropsWithChildren<{
look?: 0 | 1;
className?: string;
popoutClassName?: string;
popoutPosition?: "top" | "left" | "right" | "bottom" | "center" | "window_center";
popoutPosition?: PopoutPosition;
optionClassName?: string;
autoFocus?: boolean;
@ -293,7 +293,7 @@ export type SearchableSelect = ComponentType<PropsWithChildren<{
className?: string;
popoutClassName?: string;
wrapperClassName?: string;
popoutPosition?: "top" | "left" | "right" | "bottom" | "center" | "window_center";
popoutPosition?: PopoutPosition;
optionClassName?: string;
autoFocus?: boolean;
@ -376,6 +376,8 @@ declare enum PopoutAnimation {
FADE = "4"
}
type PopoutPosition = "top" | "bottom" | "left" | "right" | "center" | "window_center";
export type Popout = ComponentType<{
children(
thing: {
@ -387,7 +389,7 @@ export type Popout = ComponentType<{
},
data: {
isShown: boolean;
position: string;
position: PopoutPosition;
}
): ReactNode;
shouldShow?: boolean;
@ -395,7 +397,7 @@ export type Popout = ComponentType<{
closePopout(): void;
isPositioned: boolean;
nudge: number;
position: string;
position: PopoutPosition;
setPopoutRef(ref: any): void;
updatePosition(): void;
}): ReactNode;
@ -404,13 +406,13 @@ export type Popout = ComponentType<{
onRequestClose?(): void;
/** "center" and others */
align?: string;
align?: "left" | "right" | "center";
/** Popout.Animation */
animation?: PopoutAnimation;
autoInvert?: boolean;
nudgeAlignIntoViewport?: boolean;
/** "bottom" and others */
position?: string;
position?: PopoutPosition;
positionKey?: string;
spacing?: number;
}> & {