mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-13 08:33:01 -04:00
Merge remote-tracking branch 'upstream/dev' into dev
Some checks are pending
Test / Test (push) Waiting to run
Some checks are pending
Test / Test (push) Waiting to run
This commit is contained in:
commit
a1c19e6990
6 changed files with 109 additions and 84 deletions
|
@ -16,8 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { app, protocol } from "electron";
|
||||
import { app, net, protocol } from "electron";
|
||||
import { join } from "path";
|
||||
import { pathToFileURL } from "url";
|
||||
|
||||
import { initCsp } from "./csp";
|
||||
import { ensureSafePath } from "./ipcMain";
|
||||
|
@ -27,55 +28,71 @@ import { installExt } from "./utils/extensions";
|
|||
|
||||
if (!IS_VANILLA && !IS_EXTENSION) {
|
||||
app.whenReady().then(() => {
|
||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||
// from a string I don't think any other form of sourcemaps would work
|
||||
protocol.registerFileProtocol("vencord", ({ url: unsafeUrl }, cb) => {
|
||||
let url = unsafeUrl.slice("vencord://".length);
|
||||
protocol.handle("vencord", ({ url: unsafeUrl }) => {
|
||||
let url = decodeURI(unsafeUrl).slice("vencord://".length).replace(/\?v=\d+$/, "");
|
||||
|
||||
if (url.endsWith("/")) url = url.slice(0, -1);
|
||||
|
||||
if (url.startsWith("/themes/")) {
|
||||
const theme = url.slice("/themes/".length);
|
||||
|
||||
const safeUrl = ensureSafePath(THEMES_DIR, theme);
|
||||
if (!safeUrl) {
|
||||
cb({ statusCode: 403 });
|
||||
return;
|
||||
return new Response(null, {
|
||||
status: 404
|
||||
});
|
||||
}
|
||||
cb(safeUrl.replace(/\?v=\d+$/, ""));
|
||||
return;
|
||||
|
||||
return net.fetch(pathToFileURL(safeUrl).toString());
|
||||
}
|
||||
|
||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||
// from a string I don't think any other form of sourcemaps would work
|
||||
|
||||
switch (url) {
|
||||
case "renderer.js.map":
|
||||
case "preload.js.map":
|
||||
case "patcher.js.map":
|
||||
case "main.js.map":
|
||||
cb(join(__dirname, url));
|
||||
break;
|
||||
return net.fetch(pathToFileURL(join(__dirname, url)).toString());
|
||||
default:
|
||||
cb({ statusCode: 403 });
|
||||
return new Response(null, {
|
||||
status: 404
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerFileProtocol("equicord", ({ url: unsafeUrl }, cb) => {
|
||||
let url = unsafeUrl.slice("equicord://".length);
|
||||
protocol.handle("equicord", ({ url: unsafeUrl }) => {
|
||||
let url = decodeURI(unsafeUrl).slice("equicord://".length).replace(/\?v=\d+$/, "");
|
||||
|
||||
if (url.endsWith("/")) url = url.slice(0, -1);
|
||||
|
||||
if (url.startsWith("/themes/")) {
|
||||
const theme = url.slice("/themes/".length);
|
||||
|
||||
const safeUrl = ensureSafePath(THEMES_DIR, theme);
|
||||
if (!safeUrl) {
|
||||
cb({ statusCode: 403 });
|
||||
return;
|
||||
return new Response(null, {
|
||||
status: 404
|
||||
});
|
||||
}
|
||||
cb(safeUrl.replace(/\?v=\d+$/, ""));
|
||||
return;
|
||||
|
||||
return net.fetch(pathToFileURL(safeUrl).toString());
|
||||
}
|
||||
|
||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||
// from a string I don't think any other form of sourcemaps would work
|
||||
|
||||
switch (url) {
|
||||
case "renderer.js.map":
|
||||
case "preload.js.map":
|
||||
case "patcher.js.map":
|
||||
case "main.js.map":
|
||||
cb(join(__dirname, url));
|
||||
break;
|
||||
return net.fetch(pathToFileURL(join(__dirname, url)).toString());
|
||||
default:
|
||||
cb({ statusCode: 403 });
|
||||
return new Response(null, {
|
||||
status: 404
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ import { makeLinksOpenExternally } from "./utils/externalLinks";
|
|||
mkdirSync(THEMES_DIR, { recursive: true });
|
||||
|
||||
export function ensureSafePath(basePath: string, path: string) {
|
||||
const normalizedBasePath = normalize(basePath);
|
||||
const normalizedBasePath = normalize(basePath + "/");
|
||||
const newPath = join(basePath, path);
|
||||
const normalizedPath = normalize(newPath);
|
||||
return normalizedPath.startsWith(normalizedBasePath) ? normalizedPath : null;
|
||||
|
|
|
@ -20,7 +20,8 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common";
|
||||
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common";
|
||||
import NoReplyMentionPlugin from "plugins/noReplyMention";
|
||||
|
||||
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
||||
const EditStore = findByPropsLazy("isEditing", "isEditingAny");
|
||||
|
@ -28,6 +29,7 @@ const EditStore = findByPropsLazy("isEditing", "isEditingAny");
|
|||
let isDeletePressed = false;
|
||||
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
|
||||
const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);
|
||||
const focusChanged = () => !WindowStore.isFocused() && (isDeletePressed = false);
|
||||
|
||||
const settings = definePluginSettings({
|
||||
enableDeleteOnClick: {
|
||||
|
@ -62,11 +64,13 @@ export default definePlugin({
|
|||
start() {
|
||||
document.addEventListener("keydown", keydown);
|
||||
document.addEventListener("keyup", keyup);
|
||||
WindowStore.addChangeListener(focusChanged);
|
||||
},
|
||||
|
||||
stop() {
|
||||
document.removeEventListener("keydown", keydown);
|
||||
document.removeEventListener("keyup", keyup);
|
||||
WindowStore.removeChangeListener(focusChanged);
|
||||
},
|
||||
|
||||
onMessageClick(msg: any, channel, event) {
|
||||
|
@ -89,9 +93,8 @@ export default definePlugin({
|
|||
if (msg.hasFlag(EPHEMERAL)) return;
|
||||
|
||||
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
|
||||
const NoReplyMention = Vencord.Plugins.plugins.NoReplyMention as any as typeof import("../noReplyMention").default;
|
||||
const shouldMention = Vencord.Plugins.isPluginEnabled("NoReplyMention")
|
||||
? NoReplyMention.shouldMention(msg, isShiftPress)
|
||||
const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)
|
||||
? NoReplyMentionPlugin.shouldMention(msg, isShiftPress)
|
||||
: !isShiftPress;
|
||||
|
||||
FluxDispatcher.dispatch({
|
||||
|
|
|
@ -16,19 +16,20 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageActions, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import NoBlockedMessagesPlugin from "plugins/noBlockedMessages";
|
||||
import NoReplyMentionPlugin from "plugins/noReplyMention";
|
||||
|
||||
const Kangaroo = findByPropsLazy("jumpToMessage");
|
||||
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
|
||||
|
||||
const isMac = navigator.platform.includes("Mac"); // bruh
|
||||
let replyIdx = -1;
|
||||
let editIdx = -1;
|
||||
let currentlyReplyingId: string | null = null;
|
||||
let currentlyEditingId: string | null = null;
|
||||
|
||||
|
||||
const enum MentionOptions {
|
||||
|
@ -69,36 +70,29 @@ export default definePlugin({
|
|||
|
||||
flux: {
|
||||
DELETE_PENDING_REPLY() {
|
||||
replyIdx = -1;
|
||||
currentlyReplyingId = null;
|
||||
},
|
||||
MESSAGE_END_EDIT() {
|
||||
editIdx = -1;
|
||||
currentlyEditingId = null;
|
||||
},
|
||||
CHANNEL_SELECT() {
|
||||
currentlyReplyingId = null;
|
||||
currentlyEditingId = null;
|
||||
},
|
||||
MESSAGE_START_EDIT: onStartEdit,
|
||||
CREATE_PENDING_REPLY: onCreatePendingReply
|
||||
}
|
||||
});
|
||||
|
||||
function calculateIdx(messages: Message[], id: string) {
|
||||
const idx = messages.findIndex(m => m.id === id);
|
||||
return idx === -1
|
||||
? idx
|
||||
: messages.length - idx - 1;
|
||||
}
|
||||
|
||||
function onStartEdit({ channelId, messageId, _isQuickEdit }: any) {
|
||||
function onStartEdit({ messageId, _isQuickEdit }: any) {
|
||||
if (_isQuickEdit) return;
|
||||
|
||||
const meId = UserStore.getCurrentUser().id;
|
||||
|
||||
const messages = MessageStore.getMessages(channelId)._array.filter(m => m.author.id === meId);
|
||||
editIdx = calculateIdx(messages, messageId);
|
||||
currentlyEditingId = messageId;
|
||||
}
|
||||
|
||||
function onCreatePendingReply({ message, _isQuickReply }: { message: Message; _isQuickReply: boolean; }) {
|
||||
if (_isQuickReply) return;
|
||||
|
||||
replyIdx = calculateIdx(MessageStore.getMessages(message.channel_id)._array, message.id);
|
||||
currentlyReplyingId = message.id;
|
||||
}
|
||||
|
||||
const isCtrl = (e: KeyboardEvent) => isMac ? e.metaKey : e.ctrlKey;
|
||||
|
@ -123,10 +117,10 @@ function jumpIfOffScreen(channelId: string, messageId: string) {
|
|||
|
||||
const vh = Math.max(document.documentElement.clientHeight, window.innerHeight);
|
||||
const rect = element.getBoundingClientRect();
|
||||
const isOffscreen = rect.bottom < 200 || rect.top - vh >= -200;
|
||||
const isOffscreen = rect.bottom < 150 || rect.top - vh >= -150;
|
||||
|
||||
if (isOffscreen) {
|
||||
Kangaroo.jumpToMessage({
|
||||
MessageActions.jumpToMessage({
|
||||
channelId,
|
||||
messageId,
|
||||
flash: false,
|
||||
|
@ -137,44 +131,48 @@ function jumpIfOffScreen(channelId: string, messageId: string) {
|
|||
|
||||
function getNextMessage(isUp: boolean, isReply: boolean) {
|
||||
let messages: Array<Message & { deleted?: boolean; }> = MessageStore.getMessages(SelectedChannelStore.getChannelId())._array;
|
||||
if (!isReply) {
|
||||
// we are editing so only include own
|
||||
const meId = UserStore.getCurrentUser().id;
|
||||
messages = messages.filter(m => m.author.id === meId);
|
||||
}
|
||||
|
||||
if (Vencord.Plugins.isPluginEnabled("NoBlockedMessages")) {
|
||||
messages = messages.filter(m => !RelationshipStore.isBlocked(m.author.id));
|
||||
}
|
||||
const meId = UserStore.getCurrentUser().id;
|
||||
const hasNoBlockedMessages = Vencord.Plugins.isPluginEnabled(NoBlockedMessagesPlugin.name);
|
||||
|
||||
const mutate = (i: number) => isUp
|
||||
? Math.min(messages.length - 1, i + 1)
|
||||
: Math.max(-1, i - 1);
|
||||
messages = messages.filter(m => {
|
||||
if (m.deleted) return false;
|
||||
if (!isReply && m.author.id !== meId) return false; // editing only own messages
|
||||
if (hasNoBlockedMessages && NoBlockedMessagesPlugin.shouldIgnoreMessage(m)) return false;
|
||||
|
||||
const findNextNonDeleted = (i: number) => {
|
||||
do {
|
||||
i = mutate(i);
|
||||
} while (i !== -1 && messages[messages.length - i - 1]?.deleted === true);
|
||||
return i;
|
||||
return true;
|
||||
});
|
||||
|
||||
const findNextNonDeleted = (id: string | null) => {
|
||||
if (id === null) return messages[messages.length - 1];
|
||||
|
||||
const idx = messages.findIndex(m => m.id === id);
|
||||
if (idx === -1) return messages[messages.length - 1];
|
||||
|
||||
const i = isUp ? idx - 1 : idx + 1;
|
||||
return messages[i] ?? null;
|
||||
};
|
||||
|
||||
let i: number;
|
||||
if (isReply)
|
||||
replyIdx = i = findNextNonDeleted(replyIdx);
|
||||
else
|
||||
editIdx = i = findNextNonDeleted(editIdx);
|
||||
|
||||
return i === - 1 ? undefined : messages[messages.length - i - 1];
|
||||
if (isReply) {
|
||||
const msg = findNextNonDeleted(currentlyReplyingId);
|
||||
currentlyReplyingId = msg?.id ?? null;
|
||||
return msg;
|
||||
} else {
|
||||
const msg = findNextNonDeleted(currentlyEditingId);
|
||||
currentlyEditingId = msg?.id ?? null;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldMention(message) {
|
||||
const { enabled, userList, shouldPingListed } = Settings.plugins.NoReplyMention;
|
||||
const shouldPing = !enabled || (shouldPingListed === userList.includes(message.author.id));
|
||||
|
||||
function shouldMention(message: Message) {
|
||||
switch (settings.store.shouldMention) {
|
||||
case MentionOptions.NO_REPLY_MENTION_PLUGIN: return shouldPing;
|
||||
case MentionOptions.DISABLED: return false;
|
||||
default: return true;
|
||||
case MentionOptions.NO_REPLY_MENTION_PLUGIN:
|
||||
if (!Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)) return true;
|
||||
return NoReplyMentionPlugin.shouldMention(message, false);
|
||||
case MentionOptions.DISABLED:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,13 +180,16 @@ function shouldMention(message) {
|
|||
function nextReply(isUp: boolean) {
|
||||
const currChannel = ChannelStore.getChannel(SelectedChannelStore.getChannelId());
|
||||
if (currChannel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, currChannel)) return;
|
||||
|
||||
const message = getNextMessage(isUp, true);
|
||||
|
||||
if (!message)
|
||||
if (!message) {
|
||||
return void Dispatcher.dispatch({
|
||||
type: "DELETE_PENDING_REPLY",
|
||||
channelId: SelectedChannelStore.getChannelId(),
|
||||
});
|
||||
}
|
||||
|
||||
const channel = ChannelStore.getChannel(message.channel_id);
|
||||
const meId = UserStore.getCurrentUser().id;
|
||||
|
||||
|
@ -200,6 +201,7 @@ function nextReply(isUp: boolean) {
|
|||
showMentionToggle: !channel.isPrivate() && message.author.id !== meId,
|
||||
_isQuickReply: true
|
||||
});
|
||||
|
||||
ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS");
|
||||
jumpIfOffScreen(channel.id, message.id);
|
||||
}
|
||||
|
@ -210,11 +212,13 @@ function nextEdit(isUp: boolean) {
|
|||
if (currChannel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, currChannel)) return;
|
||||
const message = getNextMessage(isUp, false);
|
||||
|
||||
if (!message)
|
||||
if (!message) {
|
||||
return Dispatcher.dispatch({
|
||||
type: "MESSAGE_END_EDIT",
|
||||
channelId: SelectedChannelStore.getChannelId()
|
||||
});
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: "MESSAGE_START_EDIT",
|
||||
channelId: message.channel_id,
|
||||
|
@ -222,5 +226,6 @@ function nextEdit(isUp: boolean) {
|
|||
content: message.content,
|
||||
_isQuickEdit: true
|
||||
});
|
||||
|
||||
jumpIfOffScreen(message.channel_id, message.id);
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ export default definePlugin({
|
|||
replace: ";b=AS:800000;level-asymmetry-allowed=1"
|
||||
},
|
||||
{
|
||||
match: "useinbandfec=1",
|
||||
replace: "useinbandfec=1;stereo=1;sprop-stereo=1"
|
||||
match: /;usedtx=".concat\((\i)\?"0":"1"\)/,
|
||||
replace: '$&.concat($1?";stereo=1;sprop-stereo=1":"")'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue