mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-09 22:53:02 -04:00
Merge branch 'dev'
This commit is contained in:
commit
df3fa6589f
9 changed files with 271 additions and 17 deletions
2
.github/workflows/uploadBuilds.yml
vendored
2
.github/workflows/uploadBuilds.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
name: Upload Builds
|
name: Upload Builds
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: [Release]
|
workflows: [Release, Test]
|
||||||
types: [completed]
|
types: [completed]
|
||||||
branches: [main, dev]
|
branches: [main, dev]
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/uploadPlugins.yml
vendored
2
.github/workflows/uploadPlugins.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
name: Upload Plugins JSONs
|
name: Upload Plugins JSONs
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: [Release]
|
workflows: [Release, Test]
|
||||||
types: [completed]
|
types: [completed]
|
||||||
branches: [main]
|
branches: [main]
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -263,8 +263,10 @@ function loadImagePreview(url: string, sticker: boolean) {
|
||||||
fileSize.appendChild(showingSize);
|
fileSize.appendChild(showingSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.appendChild(fileName);
|
if (settings.store.fileInformation) {
|
||||||
preview.appendChild(fileInfo);
|
preview.appendChild(fileName);
|
||||||
|
preview.appendChild(fileInfo);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loadingSpinner) loadingSpinner.remove();
|
if (loadingSpinner) loadingSpinner.remove();
|
||||||
|
@ -287,7 +289,7 @@ function loadImagePreview(url: string, sticker: boolean) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
currentPreview.addEventListener("mouseout", () => {
|
currentPreviewFile.addEventListener("mouseout", () => {
|
||||||
if (currentPreview && !isCtrlHeld && shouldKeepPreviewOpen) {
|
if (currentPreview && !isCtrlHeld && shouldKeepPreviewOpen) {
|
||||||
deleteCurrentPreview();
|
deleteCurrentPreview();
|
||||||
shouldKeepPreviewOpen = false;
|
shouldKeepPreviewOpen = false;
|
||||||
|
|
|
@ -43,6 +43,11 @@ const settings = definePluginSettings({
|
||||||
description: "Fixes the image preview to the initial point of hover",
|
description: "Fixes the image preview to the initial point of hover",
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
fileInformation: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show file information on hover",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
hoverDelay: {
|
hoverDelay: {
|
||||||
type: OptionType.SLIDER,
|
type: OptionType.SLIDER,
|
||||||
description: "Delay in seconds before the image preview appears",
|
description: "Delay in seconds before the image preview appears",
|
||||||
|
|
|
@ -15,6 +15,8 @@ import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, GuildStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, GuildStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
||||||
import { Guild, User } from "discord-types/general";
|
import { Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
|
import { settings } from ".";
|
||||||
|
|
||||||
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
||||||
const FriendRow = findComponentByCodeLazy(".listName,discriminatorClass");
|
const FriendRow = findComponentByCodeLazy(".listName,discriminatorClass");
|
||||||
|
|
||||||
|
@ -31,7 +33,8 @@ export function openGuildInfoModal(guild: Guild) {
|
||||||
const enum Tabs {
|
const enum Tabs {
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
Friends,
|
Friends,
|
||||||
BlockedUsers
|
BlockedUsers,
|
||||||
|
MutualMembers
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GuildProps {
|
interface GuildProps {
|
||||||
|
@ -56,6 +59,7 @@ function renderTimestamp(timestamp: number) {
|
||||||
function GuildInfoModal({ guild }: GuildProps) {
|
function GuildInfoModal({ guild }: GuildProps) {
|
||||||
const [friendCount, setFriendCount] = useState<number>();
|
const [friendCount, setFriendCount] = useState<number>();
|
||||||
const [blockedCount, setBlockedCount] = useState<number>();
|
const [blockedCount, setBlockedCount] = useState<number>();
|
||||||
|
const [mutualMembersCount, setMutualMembersCount] = useState<number>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetched.friends = false;
|
fetched.friends = false;
|
||||||
|
@ -126,6 +130,12 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
>
|
>
|
||||||
Friends{friendCount !== undefined ? ` (${friendCount})` : ""}
|
Friends{friendCount !== undefined ? ` (${friendCount})` : ""}
|
||||||
</TabBar.Item>
|
</TabBar.Item>
|
||||||
|
<TabBar.Item
|
||||||
|
className={cl("tab", { selected: currentTab === Tabs.MutualMembers })}
|
||||||
|
id={Tabs.MutualMembers}
|
||||||
|
>
|
||||||
|
Mutual Server Members{mutualMembersCount !== undefined ? ` (${mutualMembersCount})` : ""}
|
||||||
|
</TabBar.Item>
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className={cl("tab", { selected: currentTab === Tabs.BlockedUsers })}
|
className={cl("tab", { selected: currentTab === Tabs.BlockedUsers })}
|
||||||
id={Tabs.BlockedUsers}
|
id={Tabs.BlockedUsers}
|
||||||
|
@ -137,6 +147,7 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
<div className={cl("tab-content")}>
|
<div className={cl("tab-content")}>
|
||||||
{currentTab === Tabs.ServerInfo && <ServerInfoTab guild={guild} />}
|
{currentTab === Tabs.ServerInfo && <ServerInfoTab guild={guild} />}
|
||||||
{currentTab === Tabs.Friends && <FriendsTab guild={guild} setCount={setFriendCount} />}
|
{currentTab === Tabs.Friends && <FriendsTab guild={guild} setCount={setFriendCount} />}
|
||||||
|
{currentTab === Tabs.MutualMembers && <MutualMembersTab guild={guild} setCount={setMutualMembersCount} />}
|
||||||
{currentTab === Tabs.BlockedUsers && <BlockedUsersTab guild={guild} setCount={setBlockedCount} />}
|
{currentTab === Tabs.BlockedUsers && <BlockedUsersTab guild={guild} setCount={setBlockedCount} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -243,16 +254,152 @@ function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setC
|
||||||
|
|
||||||
useEffect(() => setCount(members.length), [members.length]);
|
useEffect(() => setCount(members.length), [members.length]);
|
||||||
|
|
||||||
|
const sortedMembers = members
|
||||||
|
.map(id => UserStore.getUser(id))
|
||||||
|
.sort(
|
||||||
|
(a, b) => {
|
||||||
|
switch (settings.store.sorting) {
|
||||||
|
case "username":
|
||||||
|
return a.username.localeCompare(b.username);
|
||||||
|
case "displayname":
|
||||||
|
return a?.globalName?.localeCompare(b?.globalName || b.username)
|
||||||
|
|| a.username.localeCompare(b?.globalName || b.username);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollerThin fade className={cl("scroller")}>
|
<ScrollerThin fade className={cl("scroller")}>
|
||||||
{members.map(id =>
|
{sortedMembers.map(user => (
|
||||||
<FriendRow
|
<FriendRow
|
||||||
user={UserStore.getUser(id)}
|
user={user}
|
||||||
status={PresenceStore.getStatus(id) || "offline"}
|
status={PresenceStore.getStatus(user.id) || "offline"}
|
||||||
onSelect={() => openUserProfile(id)}
|
onSelect={() => openUserProfile(user.id)}
|
||||||
onContextMenu={() => { }}
|
onContextMenu={() => { }}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
|
</ScrollerThin>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemberWithMutuals {
|
||||||
|
id: string;
|
||||||
|
mutualCount: number;
|
||||||
|
mutualGuilds: Array<{
|
||||||
|
guild: Guild;
|
||||||
|
iconUrl: string | null;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMutualGuilds(id: string): MemberWithMutuals {
|
||||||
|
const mutualGuilds: Array<{ guild: Guild; iconUrl: string | null; }> = [];
|
||||||
|
|
||||||
|
for (const guild of Object.values(GuildStore.getGuilds())) {
|
||||||
|
if (GuildMemberStore.isMember(guild.id, id)) {
|
||||||
|
const iconUrl = guild.icon
|
||||||
|
? IconUtils.getGuildIconURL({
|
||||||
|
id: guild.id,
|
||||||
|
icon: guild.icon,
|
||||||
|
canAnimate: true,
|
||||||
|
size: 20
|
||||||
|
}) ?? null
|
||||||
|
: null;
|
||||||
|
|
||||||
|
mutualGuilds.push({ guild, iconUrl });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
mutualCount: mutualGuilds.length,
|
||||||
|
mutualGuilds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function MutualServerIcons({ member }: { member: MemberWithMutuals; }) {
|
||||||
|
const MAX_ICONS = 3;
|
||||||
|
const { mutualGuilds, mutualCount } = member;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cl("mutual-guilds")}>
|
||||||
|
{mutualGuilds.slice(0, MAX_ICONS).map(({ guild, iconUrl }) => (
|
||||||
|
<div key={guild.id} className={cl("guild-icon")} role="img" aria-label={guild.name}>
|
||||||
|
{iconUrl ? (
|
||||||
|
<img src={iconUrl} alt="" />
|
||||||
|
) : (
|
||||||
|
<div className={cl("guild-acronym")}>{guild.acronym}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{mutualCount > MAX_ICONS && (
|
||||||
|
<div className={cl("guild-count")}>
|
||||||
|
+{mutualCount - MAX_ICONS}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MutualMembersTab({ guild, setCount }: RelationshipProps) {
|
||||||
|
const [members, setMembers] = useState<MemberWithMutuals[]>([]);
|
||||||
|
const currentUserId = UserStore.getCurrentUser().id;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const guildMembers = GuildMemberStore.getMemberIds(guild.id);
|
||||||
|
const membersWithMutuals = guildMembers
|
||||||
|
.map(id => getMutualGuilds(id))
|
||||||
|
// dont show yourself and members that are only in this server
|
||||||
|
.filter(member => member.mutualCount > 1 && member.id !== currentUserId);
|
||||||
|
|
||||||
|
// sort by mutual server count (descending)
|
||||||
|
membersWithMutuals.sort((a, b) => b.mutualCount - a.mutualCount);
|
||||||
|
|
||||||
|
setMembers(membersWithMutuals);
|
||||||
|
setCount(membersWithMutuals.length);
|
||||||
|
}, [guild.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollerThin fade className={cl("scroller")}>
|
||||||
|
{members
|
||||||
|
.map(member => {
|
||||||
|
const user = UserStore.getUser(member.id);
|
||||||
|
return { ...member, user };
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((a, b) => {
|
||||||
|
switch (settings.store.sorting) {
|
||||||
|
case "username":
|
||||||
|
return a.user.username.localeCompare(b.user.username);
|
||||||
|
case "displayname":
|
||||||
|
return a.user?.globalName?.localeCompare(b.user?.globalName || b.user.username)
|
||||||
|
|| a.user.username.localeCompare(b.user?.globalName || b.user.username);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(member => (
|
||||||
|
<div
|
||||||
|
className={cl("member-row")}
|
||||||
|
key={member.id}
|
||||||
|
onClick={() => openUserProfile(member.id)}
|
||||||
|
>
|
||||||
|
<div className={cl("member-content")}>
|
||||||
|
<FriendRow
|
||||||
|
user={member.user}
|
||||||
|
status={PresenceStore.getStatus(member.id) || "offline"}
|
||||||
|
onSelect={() => { }}
|
||||||
|
onContextMenu={() => { }}
|
||||||
|
mutualGuilds={member.mutualCount}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cl("member-icons")} onClick={e => e.stopPropagation()}>
|
||||||
|
<MutualServerIcons member={member} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</ScrollerThin>
|
</ScrollerThin>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||||
import { migratePluginSettings } from "@api/Settings";
|
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs, EquicordDevs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Menu } from "@webpack/common";
|
import { Menu } from "@webpack/common";
|
||||||
import { Guild } from "discord-types/general";
|
import { Guild } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -25,15 +25,38 @@ const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild;
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const settings = definePluginSettings({
|
||||||
|
sorting: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: "Username or if applicable Display Name",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Username",
|
||||||
|
value: "username"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Display Name",
|
||||||
|
value: "displayname",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Dont Sort",
|
||||||
|
value: "none",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao
|
migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ServerInfo",
|
name: "ServerInfo",
|
||||||
description: "Allows you to view info about a server",
|
description: "Allows you to view info about a server",
|
||||||
authors: [Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.Ven, Devs.Nuckyz, EquicordDevs.Z1xus],
|
||||||
dependencies: ["DynamicImageModalAPI"],
|
dependencies: ["DynamicImageModalAPI"],
|
||||||
tags: ["guild", "info", "ServerProfile"],
|
tags: ["guild", "info", "ServerProfile"],
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"guild-context": Patch,
|
"guild-context": Patch,
|
||||||
"guild-header-popout": Patch
|
"guild-header-popout": Patch
|
||||||
}
|
},
|
||||||
|
settings
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
color: var(--interactive-normal);
|
color: var(--interactive-normal);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 39px;
|
height: 39px;
|
||||||
|
margin-right: -10px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +94,74 @@
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-gp-member-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-right: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
margin: 1px 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-member-row:hover {
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-member-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-member-icons {
|
||||||
|
user-select: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-mutual-guilds {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-guild-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--background-tertiary);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-guild-icon img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-guild-acronym {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-normal);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-gp-guild-count {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.vc-gp-scroller [class^="listRow"] {
|
.vc-gp-scroller [class^="listRow"] {
|
||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -580,6 +580,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "RamziAH",
|
name: "RamziAH",
|
||||||
id: 1279957227612147747n
|
id: 1279957227612147747n
|
||||||
},
|
},
|
||||||
|
SomeAspy: {
|
||||||
|
name: "SomeAspy",
|
||||||
|
id: 516750892372852754n
|
||||||
|
},
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
export const EquicordDevs = Object.freeze({
|
export const EquicordDevs = Object.freeze({
|
||||||
|
@ -936,6 +940,10 @@ export const EquicordDevs = Object.freeze({
|
||||||
name: "nvhhr",
|
name: "nvhhr",
|
||||||
id: 165098921071345666n
|
id: 165098921071345666n
|
||||||
},
|
},
|
||||||
|
Z1xus: {
|
||||||
|
name: "Z1xus",
|
||||||
|
id: 377450600797044746n,
|
||||||
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue