mirror of
https://github.com/Vendicated/Vencord.git
synced 2024-09-20 06:30:35 +00:00
Compare commits
68 commits
601214760e
...
b8fe803c4b
Author | SHA1 | Date | |
---|---|---|---|
|
b8fe803c4b | ||
|
640d99dcda | ||
|
726094aa6f | ||
|
57c8f6915c | ||
|
32476dd1df | ||
|
811397de3c | ||
|
4a95c367e3 | ||
|
a2e089716a | ||
|
5824528add | ||
|
853efa7a90 | ||
|
f80593aef8 | ||
|
917ae898c2 | ||
|
c6560e8446 | ||
|
661686b255 | ||
|
811a47ba66 | ||
|
e1dbd5f753 | ||
|
9475478fbe | ||
|
fb3cdbbf62 | ||
|
c42e490693 | ||
|
b780ae4a2a | ||
|
46facce4b7 | ||
|
265a9755b3 | ||
|
f78c9ce608 | ||
|
3be954f713 | ||
|
8278641c9b | ||
|
dd5dab5efb | ||
|
3ed2edf967 | ||
|
d31d1b74e0 | ||
|
8602141fea | ||
|
d7d64f420c | ||
|
4f0e9312ec | ||
|
58eb7943db | ||
|
4e430c4252 | ||
|
957639be6c | ||
|
7fee537ea1 | ||
|
c0f97446b8 | ||
|
04cf952fe3 | ||
|
17b9e78272 | ||
|
14f1569bc2 | ||
|
b5b43a7900 | ||
|
f6b3df7b5d | ||
|
57f548cb69 | ||
|
e87ced9f9d | ||
|
0753b51104 | ||
|
96175671b1 | ||
|
99420e2cbe | ||
|
3cfefad3f5 | ||
|
77a3b6c96c | ||
|
896421a9fa | ||
|
f2b6e6570d | ||
|
d656aa7119 | ||
|
12ae29c389 | ||
|
ab1ecd2b7f | ||
|
004517ce9b | ||
|
96c1d9cb4b | ||
|
11a1c81916 | ||
|
358fa1d1b4 | ||
|
d7404417be | ||
|
ad1d20a4bc | ||
|
5c4f743b21 | ||
|
682f7e65c8 | ||
|
da764f5144 | ||
|
2254da2474 | ||
|
1867427de4 | ||
|
94c5e6fdb7 | ||
|
c13bb6e47e | ||
|
db6c011396 | ||
|
bf55ea0763 |
12 changed files with 752 additions and 29 deletions
7
src/plugins/betterActivities/README.md
Normal file
7
src/plugins/betterActivities/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# BetterActivities
|
||||
|
||||
Shows activity icons in the member list and allows showing all activities
|
||||
|
||||
![Shows activity icons next to the status line in the member list](https://github.com/Vendicated/Vencord/assets/24937357/4c034963-4448-483a-ba1d-c04f74e4aa51)
|
||||
![Shows all activities in the profile via a carousel](https://github.com/Vendicated/Vencord/assets/24937357/11a2f15e-59b0-4072-847a-33444d964674)
|
||||
![Optionally show all activities as list](https://github.com/Vendicated/Vencord/assets/24937357/277f425f-65e7-4e25-ad98-ff61b4370f08)
|
34
src/plugins/betterActivities/components/ActivityTooltip.tsx
Normal file
34
src/plugins/betterActivities/components/ActivityTooltip.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import { ActivityView } from "../index";
|
||||
import { Activity, Application } from "../types";
|
||||
|
||||
interface ActivityTooltipProps {
|
||||
activity: Activity;
|
||||
application?: Application;
|
||||
user: User;
|
||||
cl: ReturnType<typeof classNameFactory>;
|
||||
}
|
||||
|
||||
export default function ActivityTooltip({ activity, application, user, cl }: Readonly<ActivityTooltipProps>) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div className={cl("activity-tooltip")}>
|
||||
<ActivityView
|
||||
activity={activity}
|
||||
user={user}
|
||||
application={application}
|
||||
type="BiteSizePopout"
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
13
src/plugins/betterActivities/components/Caret.tsx
Normal file
13
src/plugins/betterActivities/components/Caret.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export function Caret({ disabled, direction }: { disabled: boolean; direction: "left" | "right"; }) {
|
||||
return (
|
||||
<svg className={`vc-bactivities-caret-${direction.toLowerCase()} ${disabled && "disabled"}`} width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" fillRule="evenodd" clipRule="evenodd" d="M16.59 8.59004L12 13.17L7.41 8.59004L6 10L12 16L18 10L16.59 8.59004Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
11
src/plugins/betterActivities/components/SpotifyIcon.tsx
Normal file
11
src/plugins/betterActivities/components/SpotifyIcon.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
export function SpotifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" {...props}><path fill="#1ed760" d="M128 0C57.308 0 0 57.309 0 128c0 70.696 57.309 128 128 128c70.697 0 128-57.304 128-128C256 57.314 198.697.007 127.998.007zm58.699 184.614c-2.293 3.76-7.215 4.952-10.975 2.644c-30.053-18.357-67.885-22.515-112.44-12.335a7.981 7.981 0 0 1-9.552-6.007a7.968 7.968 0 0 1 6-9.553c48.76-11.14 90.583-6.344 124.323 14.276c3.76 2.308 4.952 7.215 2.644 10.975m15.667-34.853c-2.89 4.695-9.034 6.178-13.726 3.289c-34.406-21.148-86.853-27.273-127.548-14.92c-5.278 1.594-10.852-1.38-12.454-6.649c-1.59-5.278 1.386-10.842 6.655-12.446c46.485-14.106 104.275-7.273 143.787 17.007c4.692 2.89 6.175 9.034 3.286 13.72zm1.345-36.293C162.457 88.964 94.394 86.71 55.007 98.666c-6.325 1.918-13.014-1.653-14.93-7.978c-1.917-6.328 1.65-13.012 7.98-14.935C93.27 62.027 168.434 64.68 215.929 92.876c5.702 3.376 7.566 10.724 4.188 16.405c-3.362 5.69-10.73 7.565-16.4 4.187z"></path></svg>);
|
||||
}
|
11
src/plugins/betterActivities/components/TwitchIcon.tsx
Normal file
11
src/plugins/betterActivities/components/TwitchIcon.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
export function TwitchIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 268" {...props}><path fill="#5a3e85" d="M17.458 0L0 46.556v186.201h63.983v34.934h34.931l34.898-34.934h52.36L256 162.954V0zm23.259 23.263H232.73v128.029l-40.739 40.741H128L93.113 226.92v-34.886H40.717zm64.008 116.405H128V69.844h-23.275zm63.997 0h23.27V69.844h-23.27z"></path></svg>);
|
||||
}
|
291
src/plugins/betterActivities/index.tsx
Normal file
291
src/plugins/betterActivities/index.tsx
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { PresenceStore, React, Tooltip, useEffect, useMemo, useState, useStateFromStores } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import ActivityTooltip from "./components/ActivityTooltip";
|
||||
import { Caret } from "./components/Caret";
|
||||
import { SpotifyIcon } from "./components/SpotifyIcon";
|
||||
import { TwitchIcon } from "./components/TwitchIcon";
|
||||
import settings from "./settings";
|
||||
import {
|
||||
Activity,
|
||||
ActivityListIcon,
|
||||
ActivityViewProps,
|
||||
ApplicationIcon,
|
||||
IconCSSProperties
|
||||
} from "./types";
|
||||
import {
|
||||
getActivityApplication,
|
||||
getApplicationIcons
|
||||
} from "./utils";
|
||||
|
||||
const cl = classNameFactory("vc-bactivities-");
|
||||
|
||||
export const ActivityView = findComponentByCodeLazy<ActivityViewProps>(",onOpenGameProfileModal:");
|
||||
|
||||
// if discord one day decides to change their icon this needs to be updated
|
||||
const DefaultActivityIcon = findComponentByCodeLazy("M6,7 L2,7 L2,6 L6,6 L6,7 Z M8,5 L2,5 L2,4 L8,4 L8,5 Z M8,3 L2,3 L2,2 L8,2 L8,3 Z M8.88888889,0 L1.11111111,0 C0.494444444,0 0,0.494444444 0,1.11111111 L0,8.88888889 C0,9.50253861 0.497461389,10 1.11111111,10 L8.88888889,10 C9.50253861,10 10,9.50253861 10,8.88888889 L10,1.11111111 C10,0.494444444 9.5,0 8.88888889,0 Z");
|
||||
|
||||
export default definePlugin({
|
||||
name: "BetterActivities",
|
||||
description: "Shows activity icons in the member list and allows showing all activities",
|
||||
authors: [Devs.D3SOX, Devs.Arjix, Devs.AutumnVN],
|
||||
tags: ["activity"],
|
||||
|
||||
settings,
|
||||
|
||||
patchActivityList: ({ activities, user }: { activities: Activity[], user: User; }): JSX.Element | null => {
|
||||
const icons: ActivityListIcon[] = [];
|
||||
|
||||
const applicationIcons = getApplicationIcons(activities);
|
||||
if (applicationIcons.length) {
|
||||
const compareImageSource = (a: ApplicationIcon, b: ApplicationIcon) => {
|
||||
return a.image.src === b.image.src;
|
||||
};
|
||||
const uniqueIcons = applicationIcons.filter((element, index, array) => {
|
||||
return array.findIndex(el => compareImageSource(el, element)) === index;
|
||||
});
|
||||
for (const appIcon of uniqueIcons) {
|
||||
icons.push({
|
||||
iconElement: <img {...appIcon.image} />,
|
||||
tooltip: <ActivityTooltip
|
||||
activity={appIcon.activity}
|
||||
application={appIcon.application}
|
||||
user={user}
|
||||
cl={cl}
|
||||
/>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const addActivityIcon = (activityName: string, IconComponent: React.ComponentType) => {
|
||||
const activityIndex = activities.findIndex(({ name }) => name === activityName);
|
||||
if (activityIndex !== -1) {
|
||||
const activity = activities[activityIndex];
|
||||
const iconObject: ActivityListIcon = {
|
||||
iconElement: <IconComponent />,
|
||||
tooltip: <ActivityTooltip activity={activity} user={user} cl={cl} />
|
||||
};
|
||||
|
||||
if (settings.store.specialFirst) {
|
||||
icons.unshift(iconObject);
|
||||
} else {
|
||||
icons.splice(activityIndex, 0, iconObject);
|
||||
}
|
||||
}
|
||||
};
|
||||
addActivityIcon("Twitch", TwitchIcon);
|
||||
addActivityIcon("Spotify", SpotifyIcon);
|
||||
|
||||
if (icons.length) {
|
||||
const iconStyle: IconCSSProperties = {
|
||||
"--icon-size": `${settings.store.iconSize}px`,
|
||||
};
|
||||
|
||||
return <ErrorBoundary noop>
|
||||
<div className={cl("row")}>
|
||||
{icons.map(({ iconElement, tooltip }, i) => (
|
||||
<div key={i} className={cl("icon")} style={iconStyle}>
|
||||
{tooltip ? <Tooltip text={tooltip}>
|
||||
{({ onMouseEnter, onMouseLeave }) => (
|
||||
<div
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}>
|
||||
{iconElement}
|
||||
</div>
|
||||
)}
|
||||
</Tooltip> : iconElement}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ErrorBoundary>;
|
||||
} else {
|
||||
// Show default icon when there are no custom icons
|
||||
// We need to filter out custom statuses
|
||||
const shouldShow = activities.filter(a => a.type !== 4).length !== icons.length;
|
||||
if (shouldShow) {
|
||||
return <DefaultActivityIcon />;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
showAllActivitiesComponent({ activity, user, ...props }: ActivityViewProps) {
|
||||
const [currentActivity, setCurrentActivity] = useState<Activity | null>(
|
||||
activity?.type !== 4 ? activity! : null
|
||||
);
|
||||
|
||||
const activities = useStateFromStores<Activity[]>(
|
||||
[PresenceStore], () => PresenceStore.getActivities(user.id).filter((activity: Activity) => activity.type !== 4)
|
||||
) ?? [];
|
||||
|
||||
useEffect(() => {
|
||||
if (!activities.length) {
|
||||
setCurrentActivity(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentActivity || !activities.includes(currentActivity))
|
||||
setCurrentActivity(activities[0]);
|
||||
}, [activities]);
|
||||
|
||||
// we use these for other activities, it would be better to somehow get the corresponding activity props
|
||||
const generalProps = useMemo(() => Object.keys(props).reduce((acc, key) => {
|
||||
// exclude activity specific props to prevent copying them to all activities (e.g. buttons)
|
||||
if (key !== "renderActions" && key !== "application") acc[key] = props[key];
|
||||
return acc;
|
||||
}, {} as Omit<typeof props, "renderActions" | "application">), [props]);
|
||||
|
||||
if (!activities.length) return null;
|
||||
|
||||
if (settings.store.allActivitiesStyle === "carousel") {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
{currentActivity?.id === activity?.id ? (
|
||||
<ActivityView
|
||||
activity={currentActivity}
|
||||
user={user}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<ActivityView
|
||||
activity={currentActivity}
|
||||
user={user}
|
||||
// fetch optional application
|
||||
application={getActivityApplication(currentActivity!)}
|
||||
{...generalProps}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={cl("controls")}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Tooltip text="Left" tooltipClassName={cl("controls-tooltip")}>{({
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
}) => {
|
||||
return <span
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={() => {
|
||||
const index = activities.indexOf(currentActivity!);
|
||||
if (index - 1 >= 0)
|
||||
setCurrentActivity(activities[index - 1]);
|
||||
}}
|
||||
>
|
||||
<Caret
|
||||
disabled={activities.indexOf(currentActivity!) < 1}
|
||||
direction="left" />
|
||||
</span>;
|
||||
}}</Tooltip>
|
||||
|
||||
<div className="carousell">
|
||||
{activities.map((activity, index) => (
|
||||
<div
|
||||
key={"dot--" + index}
|
||||
onClick={() => setCurrentActivity(activity)}
|
||||
className={`dot ${currentActivity === activity ? "selected" : ""}`} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Tooltip text="Right" tooltipClassName={cl("controls-tooltip")}>{({
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
}) => {
|
||||
return <span
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={() => {
|
||||
const index = activities.indexOf(currentActivity!);
|
||||
if (index + 1 < activities.length)
|
||||
setCurrentActivity(activities[index + 1]);
|
||||
}}
|
||||
>
|
||||
<Caret
|
||||
disabled={activities.indexOf(currentActivity!) >= activities.length - 1}
|
||||
direction="right" />
|
||||
</span>;
|
||||
}}</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "5px",
|
||||
}}
|
||||
>
|
||||
{activities.map((activity, index) =>
|
||||
index === 0 ? (
|
||||
<ActivityView
|
||||
key={index}
|
||||
activity={activity}
|
||||
user={user}
|
||||
{...props}
|
||||
/>) : (
|
||||
<ActivityView
|
||||
key={index}
|
||||
activity={activity}
|
||||
user={user}
|
||||
application={getActivityApplication(activity)}
|
||||
{...generalProps}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
patches: [
|
||||
{
|
||||
// Patch activity icons
|
||||
find: ".getHangStatusActivity():null!",
|
||||
replacement: {
|
||||
match: /null!=(\i)&&\i.some\(\i=>\(0,\i.\i\)\(\i,\i\)\)\?/,
|
||||
replace: "$self.patchActivityList(e),false?"
|
||||
},
|
||||
predicate: () => settings.store.memberList,
|
||||
},
|
||||
{
|
||||
// Show all activities in the user popout/sidebar
|
||||
find: '"UserActivityContainer"',
|
||||
replacement: {
|
||||
match: /(?<=\(0,\i\.jsx\)\()(\i\.\i)(?=,{...(\i),activity:\i,user:\i,application:\i)/,
|
||||
replace: "$2.type==='BiteSizePopout'?$self.showAllActivitiesComponent:$1"
|
||||
},
|
||||
predicate: () => settings.store.profiles
|
||||
},
|
||||
],
|
||||
});
|
77
src/plugins/betterActivities/settings.tsx
Normal file
77
src/plugins/betterActivities/settings.tsx
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
memberList: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show activity icons in the member list",
|
||||
default: true,
|
||||
restartNeeded: true,
|
||||
},
|
||||
iconSize: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Size of the activity icons",
|
||||
markers: [10, 15, 20],
|
||||
default: 15,
|
||||
stickToMarkers: false,
|
||||
},
|
||||
specialFirst: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show special activities first (Currently Spotify and Twitch)",
|
||||
default: true,
|
||||
},
|
||||
renderGifs: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow rendering GIFs",
|
||||
default: true,
|
||||
},
|
||||
showAppDescriptions: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show application descriptions in the activity tooltip",
|
||||
default: true,
|
||||
restartNeeded: false,
|
||||
},
|
||||
divider: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "",
|
||||
component: () => (
|
||||
<div style={{
|
||||
width: "100%",
|
||||
height: 1,
|
||||
borderTop: "thin solid var(--background-modifier-accent)",
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5
|
||||
}} />
|
||||
),
|
||||
},
|
||||
profiles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show all activities in the profile popout/sidebar",
|
||||
default: true,
|
||||
restartNeeded: true,
|
||||
},
|
||||
allActivitiesStyle: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Style for showing all activities",
|
||||
options: [
|
||||
{
|
||||
default: true,
|
||||
label: "Carousel",
|
||||
value: "carousel",
|
||||
},
|
||||
{
|
||||
label: "List",
|
||||
value: "list",
|
||||
},
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default settings;
|
95
src/plugins/betterActivities/styles.css
Normal file
95
src/plugins/betterActivities/styles.css
Normal file
|
@ -0,0 +1,95 @@
|
|||
.vc-bactivities-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.vc-bactivities-icon {
|
||||
height: var(--icon-size);
|
||||
width: var(--icon-size);
|
||||
}
|
||||
|
||||
.vc-bactivities-icon img {
|
||||
width: var(--icon-size);
|
||||
height: var(--icon-size);
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.vc-bactivities-activity-tooltip {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.vc-bactivities-caret-left,
|
||||
.vc-bactivities-caret-right {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.vc-bactivities-caret-left {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.vc-bactivities-caret-right {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.vc-bactivities-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
background: var(--background-secondary-alt);
|
||||
border-radius: 3px;
|
||||
flex: 1 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls [class^="vc-activities-caret-"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
background-color: #ffffff4d;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls [class^="vc-activities-caret-"].disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls [class^="vc-activities-caret-"]:hover:not(.disabled) {
|
||||
background: var(--background-modifier-accent);
|
||||
}
|
||||
|
||||
.vc-bactivities-controls .carousell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls .carousell .dot {
|
||||
margin: 0 4px;
|
||||
width: 10px;
|
||||
cursor: pointer;
|
||||
height: 10px;
|
||||
border-radius: 100px;
|
||||
background: var(--interactive-muted);
|
||||
transition: background 0.3s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls .carousell .dot:hover:not(.selected) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.vc-bactivities-controls .carousell .dot.selected {
|
||||
opacity: 1;
|
||||
background: var(--dot-color, var(--brand-500));
|
||||
}
|
||||
|
||||
.vc-bactivities-controls-tooltip {
|
||||
--background-floating: var(--background-secondary);
|
||||
}
|
91
src/plugins/betterActivities/types.ts
Normal file
91
src/plugins/betterActivities/types.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { User } from "discord-types/general";
|
||||
import { CSSProperties, ImgHTMLAttributes } from "react";
|
||||
|
||||
export interface Timestamp {
|
||||
start?: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
created_at: number;
|
||||
id: string;
|
||||
name: string;
|
||||
type: number;
|
||||
emoji?: {
|
||||
animated: boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
state?: string;
|
||||
flags?: number;
|
||||
sync_id?: string;
|
||||
details?: string;
|
||||
application_id?: string;
|
||||
assets?: {
|
||||
large_text?: string;
|
||||
large_image?: string;
|
||||
small_text?: string;
|
||||
small_image?: string;
|
||||
};
|
||||
timestamps?: Timestamp;
|
||||
platform?: string;
|
||||
}
|
||||
|
||||
export interface Application {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
description: string;
|
||||
summary: string;
|
||||
type: number;
|
||||
hook: boolean;
|
||||
guild_id: string;
|
||||
executables: Executable[];
|
||||
verify_key: string;
|
||||
publishers: Developer[];
|
||||
developers: Developer[];
|
||||
flags: number;
|
||||
}
|
||||
|
||||
export interface Developer {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Executable {
|
||||
os: string;
|
||||
name: string;
|
||||
is_launcher: boolean;
|
||||
}
|
||||
|
||||
export interface ApplicationIcon {
|
||||
image: ImgHTMLAttributes<HTMLImageElement> & {
|
||||
src: string;
|
||||
alt: string;
|
||||
};
|
||||
activity: Activity;
|
||||
application?: Application;
|
||||
}
|
||||
|
||||
export interface ActivityListIcon {
|
||||
iconElement: JSX.Element;
|
||||
tooltip?: JSX.Element | string;
|
||||
}
|
||||
|
||||
export interface IconCSSProperties extends CSSProperties {
|
||||
"--icon-size": string;
|
||||
}
|
||||
|
||||
export interface ActivityViewProps {
|
||||
activity: Activity | null;
|
||||
user: User;
|
||||
application?: Application;
|
||||
renderActions?: () => JSX.Element;
|
||||
type: string;
|
||||
}
|
122
src/plugins/betterActivities/utils.ts
Normal file
122
src/plugins/betterActivities/utils.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||
|
||||
import settings from "./settings";
|
||||
import { Activity, Application, ApplicationIcon } from "./types";
|
||||
|
||||
const ApplicationStore: {
|
||||
getApplication: (id: string) => Application | null;
|
||||
} = findStoreLazy("ApplicationStore");
|
||||
|
||||
const { fetchApplication }: {
|
||||
fetchApplication: (id: string) => Promise<Application | null>;
|
||||
} = findByPropsLazy("fetchApplication");
|
||||
|
||||
const fetchedApplications = new Map<string, Application | null>();
|
||||
|
||||
export function getActivityApplication({ application_id }: Activity) {
|
||||
if (!application_id) return undefined;
|
||||
let application = ApplicationStore.getApplication(application_id);
|
||||
if (!application && fetchedApplications.has(application_id)) {
|
||||
application = fetchedApplications.get(application_id) ?? null;
|
||||
}
|
||||
return application ?? undefined;
|
||||
}
|
||||
|
||||
// TODO: replace with "renderXboxImage"?
|
||||
const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png";
|
||||
|
||||
export function getApplicationIcons(activities: Activity[], preferSmall = false) {
|
||||
const applicationIcons: ApplicationIcon[] = [];
|
||||
const applications = activities.filter(activity => activity.application_id || activity.platform);
|
||||
|
||||
for (const activity of applications) {
|
||||
const { assets, application_id, platform } = activity;
|
||||
if (!application_id && !platform) {
|
||||
continue;
|
||||
}
|
||||
if (assets) {
|
||||
const addImage = (image: string, alt: string) => {
|
||||
if (image.startsWith("mp:")) {
|
||||
const discordMediaLink = `https://media.discordapp.net/${image.replace(/mp:/, "")}`;
|
||||
if (settings.store.renderGifs || !discordMediaLink.endsWith(".gif")) {
|
||||
applicationIcons.push({
|
||||
image: { src: discordMediaLink, alt },
|
||||
activity
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const src = `https://cdn.discordapp.com/app-assets/${application_id}/${image}.png`;
|
||||
applicationIcons.push({
|
||||
image: { src, alt },
|
||||
activity
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const smallImage = assets.small_image;
|
||||
const smallText = assets.small_text ?? "Small Text";
|
||||
const largeImage = assets.large_image;
|
||||
const largeText = assets.large_text ?? "Large Text";
|
||||
if (preferSmall) {
|
||||
if (smallImage) {
|
||||
addImage(smallImage, smallText);
|
||||
} else if (largeImage) {
|
||||
addImage(largeImage, largeText);
|
||||
}
|
||||
} else {
|
||||
if (largeImage) {
|
||||
addImage(largeImage, largeText);
|
||||
} else if (smallImage) {
|
||||
addImage(smallImage, smallText);
|
||||
}
|
||||
}
|
||||
} else if (application_id) {
|
||||
let application = ApplicationStore.getApplication(application_id);
|
||||
if (!application) {
|
||||
if (fetchedApplications.has(application_id)) {
|
||||
application = fetchedApplications.get(application_id) as Application | null;
|
||||
} else {
|
||||
fetchedApplications.set(application_id, null);
|
||||
fetchApplication(application_id).then(app => {
|
||||
fetchedApplications.set(application_id, app);
|
||||
}).catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
if (application) {
|
||||
if (application.icon) {
|
||||
const src = `https://cdn.discordapp.com/app-icons/${application.id}/${application.icon}.png`;
|
||||
applicationIcons.push({
|
||||
image: { src, alt: application.name },
|
||||
activity,
|
||||
application
|
||||
});
|
||||
} else if (platform === "xbox") {
|
||||
applicationIcons.push({
|
||||
image: { src: xboxUrl, alt: "Xbox" },
|
||||
activity,
|
||||
application
|
||||
});
|
||||
}
|
||||
} else if (platform === "xbox") {
|
||||
applicationIcons.push({
|
||||
image: { src: xboxUrl, alt: "Xbox" },
|
||||
activity
|
||||
});
|
||||
}
|
||||
} else if (platform === "xbox") {
|
||||
applicationIcons.push({
|
||||
image: { src: xboxUrl, alt: "Xbox" },
|
||||
activity
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return applicationIcons;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
# NoDefaultHangStatus
|
||||
|
||||
Disable the default hang status when joining voice channels
|
||||
|
||||
![Visualization](https://github.com/Vendicated/Vencord/assets/24937357/329a9742-236f-48f7-94ff-c3510eca505a)
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "NoDefaultHangStatus",
|
||||
description: "Disable the default hang status when joining voice channels",
|
||||
authors: [Devs.D3SOX],
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ".CHILLING)",
|
||||
replacement: {
|
||||
match: /{enableHangStatus:(\i),/,
|
||||
replace: "{_enableHangStatus:$1=false,"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
Loading…
Reference in a new issue