migrate and fix a bunch of test issues because of deprecations
This commit is contained in:
parent
b7ed0728c8
commit
1924684544
84 changed files with 2106 additions and 2336 deletions
|
|
@ -37,7 +37,6 @@ import { classNameFactory } from "@utils/css";
|
|||
import { copyWithToast, openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { relaunch, showItemInFolder } from "@utils/native";
|
||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
||||
import type { ThemeHeader } from "@utils/themes";
|
||||
|
|
@ -45,7 +44,7 @@ import { getThemeInfo, stripBOM, type UserThemeHeader } from "@utils/themes/bd";
|
|||
import { usercssParse } from "@utils/themes/usercss";
|
||||
import { getStylusWebStoreUrl } from "@utils/web";
|
||||
import { findComponentByCodeLazy, findLazy } from "@webpack";
|
||||
import { Alerts, Menu, React, Select, showToast, TextInput, Toasts, Tooltip, useEffect, useMemo, useState } from "@webpack/common";
|
||||
import { Alerts, Menu, openModal,React, Select, showToast, TextInput, Toasts, Tooltip, useEffect, useMemo, useState } from "@webpack/common";
|
||||
import { ContextMenuApi } from "@webpack/common/menu";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
import type { UserstyleHeader } from "usercss-meta";
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import { BaseText } from "@components/BaseText";
|
|||
import { Flex } from "@components/Flex";
|
||||
import { CopyIcon, PasteIcon, ResetIcon } from "@components/Icons";
|
||||
import { copyWithToast } from "@utils/discord";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot } from "@utils/modal";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { showToast, Toasts, Tooltip } from "@webpack/common";
|
||||
import { Modal, showToast, Toasts, Tooltip } from "@webpack/common";
|
||||
import { type ReactNode } from "react";
|
||||
import { UserstyleHeader } from "usercss-meta";
|
||||
|
||||
|
|
@ -187,21 +186,22 @@ export function UserCSSSettingsModal({ modalProps, theme, onSettingsReset }: Use
|
|||
}
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>Settings for {theme.name}</BaseText>
|
||||
<Flex style={{ gap: 4, marginRight: 4 }} className="vc-settings-usercss-ie-buttons">
|
||||
<ExportButton themeSettings={themeVars} />
|
||||
<ImportButton themeSettings={themeVars} />
|
||||
<ResetButton themeSettings={userCssVars} themeId={theme.id} close={modalProps.onClose} onReset={onSettingsReset} />
|
||||
<Modal
|
||||
{...modalProps}
|
||||
title={
|
||||
<Flex style={{ alignItems: "center", gap: 4 }}>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>Settings for {theme.name}</BaseText>
|
||||
<Flex style={{ gap: 4, marginRight: 4 }} className="vc-settings-usercss-ie-buttons">
|
||||
<ExportButton themeSettings={themeVars} />
|
||||
<ImportButton themeSettings={themeVars} />
|
||||
<ResetButton themeSettings={userCssVars} themeId={theme.id} close={modalProps.onClose} onReset={onSettingsReset} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>
|
||||
{controls}
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
}
|
||||
>
|
||||
<Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>
|
||||
{controls}
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
.vc-author-modal-root {
|
||||
padding: 1em;
|
||||
padding: 0 0 16px;
|
||||
}
|
||||
|
||||
.vc-author-modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.vc-author-modal-name {
|
||||
|
|
@ -50,18 +50,21 @@
|
|||
0 2px 6px rgb(0 0 0 / 35%);
|
||||
}
|
||||
|
||||
.vc-author-modal-root + [class*="footer"] {
|
||||
padding: 16px;
|
||||
div:has(.vc-author-modal-root) > [class*="actionBar"] {
|
||||
padding: 8px 16px;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.vc-author-modal-links {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.vc-author-modal-plugins {
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { Paragraph } from "@components/Paragraph";
|
|||
import { EquicordDevsById, VencordDevsById } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { fetchUserProfile } from "@utils/discord";
|
||||
import { classes, pluralise } from "@utils/misc";
|
||||
import { pluralise } from "@utils/misc";
|
||||
import { RenderModalProps, User } from "@vencord/discord-types";
|
||||
import { Modal, openModal, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ function ContributorModal({ user, modalProps }: { user: User; modalProps: Render
|
|||
<Modal
|
||||
{...modalProps}
|
||||
title={
|
||||
<div className="vc-plugin-modal-header">
|
||||
<div className={cl("header")}>
|
||||
<img
|
||||
className={cl("avatar")}
|
||||
src={user.getAvatarURL(void 0, 512, true)}
|
||||
|
|
@ -85,36 +85,42 @@ function ContributorModal({ user, modalProps }: { user: User; modalProps: Render
|
|||
</Paragraph>
|
||||
)
|
||||
}
|
||||
actionBarInput={
|
||||
hasLinks && (
|
||||
<div
|
||||
className={cl("links")}
|
||||
style={{ width: "100%", justifyContent: "flex-end" }}
|
||||
>
|
||||
{website && (
|
||||
<WebsiteButton
|
||||
text={website}
|
||||
href={`https://${website}`}
|
||||
/>
|
||||
)}
|
||||
{githubName && (
|
||||
<GithubButton
|
||||
text={githubName}
|
||||
href={`https://github.com/${githubName}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
{!!plugins.length && (
|
||||
<div className={cl("plugins")}>
|
||||
{plugins.map(p =>
|
||||
<PluginCard
|
||||
key={p.name}
|
||||
plugin={p}
|
||||
disabled={p.required ?? false}
|
||||
onRestartNeeded={() => showToast("Restart to apply changes!")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasLinks && (
|
||||
<div className={classes("vc-settings-modal-links", cl("links"))}>
|
||||
{website && (
|
||||
<WebsiteButton
|
||||
text={website}
|
||||
href={`https://${website}`}
|
||||
/>
|
||||
)}
|
||||
{githubName && (
|
||||
<GithubButton
|
||||
text={githubName}
|
||||
href={`https://github.com/${githubName}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={cl("root")}>
|
||||
{!!plugins.length && (
|
||||
<div className={cl("plugins")}>
|
||||
{plugins.map(p =>
|
||||
<PluginCard
|
||||
key={p.name}
|
||||
plugin={p}
|
||||
disabled={p.required ?? false}
|
||||
onRestartNeeded={() => showToast("Restart to apply changes!")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,13 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/discord";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Button, ChannelStore } from "@webpack/common";
|
||||
import { ChannelStore, Modal, openModal } from "@webpack/common";
|
||||
|
||||
function DecodeIcon() {
|
||||
return (
|
||||
|
|
@ -64,32 +61,26 @@ function decodeBase64Strings(base64Strings) {
|
|||
}
|
||||
|
||||
function openDecodedBase64Modal(decodedContent) {
|
||||
const key = openModal(props => (
|
||||
openModal(props => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...props} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>Decoded Base64 Content</BaseText>
|
||||
<ModalCloseButton onClick={() => closeModal(key)} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
<Heading>Decoded Content</Heading>
|
||||
{decodedContent.map((content, index) => (
|
||||
<CodeBlock key={index} content={content} lang="" />
|
||||
))}
|
||||
</div>
|
||||
</ModalContent >
|
||||
<ModalFooter>
|
||||
<Flex gap={10}>
|
||||
{decodedContent.map((content, index) => (
|
||||
<Button key={index} onClick={() => copyWithToast(content, "Decoded content copied to clipboard!")}>
|
||||
Copy Decoded Content {index + 1}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot >
|
||||
</ErrorBoundary >
|
||||
<Modal
|
||||
{...props}
|
||||
size="lg"
|
||||
title="Decoded Base64 Content"
|
||||
actions={decodedContent.map((content, index) => ({
|
||||
text: `Copy Decoded Content ${index + 1}`,
|
||||
variant: "primary",
|
||||
onClick: () => copyWithToast(content, "Decoded content copied to clipboard!")
|
||||
}))}
|
||||
>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
<Heading>Decoded Content</Heading>
|
||||
{decodedContent.map((content, index) => (
|
||||
<CodeBlock key={index} content={content} lang="" />
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ import { CircleQuestionIcon, DiscoveryIcon, EnvelopeIcon, FriendsIcon, NitroIcon
|
|||
import { classNameFactory } from "@utils/css";
|
||||
import { getGuildAcronym, getIntlMessage } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Avatar, ChannelStore, ContextMenuApi, FluxDispatcher, GuildStore, Menu, React, ReadStateStore, ReadStateUtils, SelectedChannelStore, SelectedGuildStore, TextInput, Tooltip, useDrag, useDrop, useEffect, useRef, UserStore, useState } from "@webpack/common";
|
||||
import { Avatar, ChannelStore, closeModal, ContextMenuApi, FluxDispatcher, GuildStore, Menu, openModal,React, ReadStateStore, ReadStateUtils, SelectedChannelStore, SelectedGuildStore, TextInput, Tooltip, useDrag, useDrop, useEffect, useRef, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { NotificationDot } from "./ChannelTab";
|
||||
import { BookmarkContextMenu, EditModal } from "./ContextMenus";
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import { bookmarkFolderColors, bookmarkPlaceholderName, closeOtherTabs, closeTab
|
|||
import { Bookmark, BookmarkFolder, Bookmarks, ChannelTabsProps, UseBookmarkMethods } from "@equicordplugins/channelTabs/util/types";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { Button, ChannelStore, ColorPicker, FluxDispatcher, Menu, ReadStateStore, ReadStateUtils, Select, TextInput, useMemo, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Button, ChannelStore, closeModal, ColorPicker, FluxDispatcher, Menu, Modal, openModal, ReadStateStore, ReadStateUtils, Select, TextInput, useMemo, useState } from "@webpack/common";
|
||||
|
||||
const legacyFolderColors: Record<string, string> = {
|
||||
"var(--channeltabs-red)": bookmarkFolderColors.Red,
|
||||
|
|
@ -115,7 +115,7 @@ function FolderChipPreview({ name, color, iconName }: { name: string; color: str
|
|||
}
|
||||
|
||||
function FolderIconPickerModal({ modalProps, modalKey, name, color, iconName, onSelect, onColorChange }: {
|
||||
modalProps: ModalProps,
|
||||
modalProps: RenderModalProps,
|
||||
modalKey: string,
|
||||
name: string,
|
||||
color: string,
|
||||
|
|
@ -133,73 +133,72 @@ function FolderIconPickerModal({ modalProps, modalKey, name, color, iconName, on
|
|||
}, [iconNames, search]);
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold">Choose Folder Icon</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Heading className={Margins.top16}>Preview</Heading>
|
||||
<FolderChipPreview name={name} color={localColor} iconName={iconName} />
|
||||
<Heading className={Margins.top16}>Icon Color</Heading>
|
||||
<div className={Margins.top8}>
|
||||
<ColorPicker
|
||||
color={colorToInt(localColor)}
|
||||
onChange={value => {
|
||||
const nextColor = intToColor(value);
|
||||
setLocalColor(nextColor);
|
||||
onColorChange(nextColor);
|
||||
}}
|
||||
showEyeDropper={false}
|
||||
/>
|
||||
</div>
|
||||
<Heading className={Margins.top16}>Search</Heading>
|
||||
<TextInput
|
||||
value={search}
|
||||
placeholder={`Search ${iconNames.length} icons...`}
|
||||
onChange={setSearch}
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title={<BaseText size="lg" weight="semibold">Choose Folder Icon</BaseText>}
|
||||
actions={[
|
||||
{
|
||||
text: "Close",
|
||||
variant: "secondary",
|
||||
onClick: () => closeModal(modalKey)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading className={Margins.top16}>Preview</Heading>
|
||||
<FolderChipPreview name={name} color={localColor} iconName={iconName} />
|
||||
<Heading className={Margins.top16}>Icon Color</Heading>
|
||||
<div className={Margins.top8}>
|
||||
<ColorPicker
|
||||
color={colorToInt(localColor)}
|
||||
onChange={value => {
|
||||
const nextColor = intToColor(value);
|
||||
setLocalColor(nextColor);
|
||||
onColorChange(nextColor);
|
||||
}}
|
||||
showEyeDropper={false}
|
||||
/>
|
||||
<div style={{
|
||||
display: "grid",
|
||||
gap: 8,
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(52px, 1fr))",
|
||||
marginTop: 16,
|
||||
maxHeight: 320,
|
||||
overflowY: "auto"
|
||||
}}>
|
||||
{filteredIconNames.map(name => (
|
||||
<button
|
||||
key={name}
|
||||
onClick={() => {
|
||||
onSelect(name);
|
||||
closeModal(modalKey);
|
||||
}}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
background: name === iconName ? "var(--background-modifier-hover)" : "var(--background-secondary)",
|
||||
border: "1px solid var(--border-subtle)",
|
||||
borderRadius: 8,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
height: 52,
|
||||
justifyContent: "center",
|
||||
padding: 0
|
||||
}}
|
||||
title={name}
|
||||
type="button"
|
||||
>
|
||||
<FolderGlyph color={localColor} iconName={name} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => closeModal(modalKey)}
|
||||
>Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
<Heading className={Margins.top16}>Search</Heading>
|
||||
<TextInput
|
||||
value={search}
|
||||
placeholder={`Search ${iconNames.length} icons...`}
|
||||
onChange={setSearch}
|
||||
/>
|
||||
<div style={{
|
||||
display: "grid",
|
||||
gap: 8,
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(52px, 1fr))",
|
||||
marginTop: 16,
|
||||
maxHeight: 320,
|
||||
overflowY: "auto"
|
||||
}}>
|
||||
{filteredIconNames.map(name => (
|
||||
<button
|
||||
key={name}
|
||||
onClick={() => {
|
||||
onSelect(name);
|
||||
closeModal(modalKey);
|
||||
}}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
background: name === iconName ? "var(--background-modifier-hover)" : "var(--background-secondary)",
|
||||
border: "1px solid var(--border-subtle)",
|
||||
borderRadius: 8,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
height: 52,
|
||||
justifyContent: "center",
|
||||
padding: 0
|
||||
}}
|
||||
title={name}
|
||||
type="button"
|
||||
>
|
||||
<FolderGlyph color={localColor} iconName={name} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +262,7 @@ function FolderAppearanceFields({
|
|||
}
|
||||
|
||||
export function EditModal({ modalProps, modalKey, bookmark, onSave }: {
|
||||
modalProps: ModalProps,
|
||||
modalProps: RenderModalProps,
|
||||
modalKey: string,
|
||||
bookmark: Bookmark | BookmarkFolder,
|
||||
onSave: (name: string, color: string, iconName?: string) => void;
|
||||
|
|
@ -274,46 +273,47 @@ export function EditModal({ modalProps, modalKey, bookmark, onSave }: {
|
|||
const placeholder = bookmarkPlaceholderName(bookmark);
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold">Edit Bookmark</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
{isBookmarkFolder(bookmark)
|
||||
? <FolderAppearanceFields
|
||||
name={name}
|
||||
setName={setName}
|
||||
color={color}
|
||||
setColor={setColor}
|
||||
iconName={iconName}
|
||||
setIconName={setIconName}
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title={<BaseText size="lg" weight="semibold">Edit Bookmark</BaseText>}
|
||||
actions={[
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: () => onSave(name || placeholder, color, iconName)
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: () => closeModal(modalKey)
|
||||
}
|
||||
]}
|
||||
>
|
||||
{isBookmarkFolder(bookmark)
|
||||
? <FolderAppearanceFields
|
||||
name={name}
|
||||
setName={setName}
|
||||
color={color}
|
||||
setColor={setColor}
|
||||
iconName={iconName}
|
||||
setIconName={setIconName}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
: <>
|
||||
<Heading className={Margins.top16}>Bookmark Name</Heading>
|
||||
<TextInput
|
||||
value={name === placeholder ? undefined : name}
|
||||
placeholder={placeholder}
|
||||
onChange={setName}
|
||||
/>
|
||||
: <>
|
||||
<Heading className={Margins.top16}>Bookmark Name</Heading>
|
||||
<TextInput
|
||||
value={name === placeholder ? undefined : name}
|
||||
placeholder={placeholder}
|
||||
onChange={setName}
|
||||
/>
|
||||
</>}
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => onSave(name || placeholder, color, iconName)}
|
||||
>Save</Button>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => closeModal(modalKey)}
|
||||
>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</>}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function AddToFolderModal({ modalProps, modalKey, bookmarks, onSave }: {
|
||||
modalProps: any,
|
||||
modalProps: RenderModalProps,
|
||||
modalKey: string,
|
||||
bookmarks: Bookmarks,
|
||||
onSave: (folderIndex: number, folderName: string, folderColor: string, iconName?: string) => void;
|
||||
|
|
@ -324,78 +324,79 @@ function AddToFolderModal({ modalProps, modalKey, bookmarks, onSave }: {
|
|||
const [iconName, setIconName] = useState<string | undefined>();
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold">Add Bookmark to Folder</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Heading className={Margins.top16}>Select a folder</Heading>
|
||||
<Select
|
||||
options={[...Object.entries(bookmarks)
|
||||
.filter(([, bookmark]) => isBookmarkFolder(bookmark))
|
||||
.map(([index, bookmark]) => ({
|
||||
label: bookmark.name,
|
||||
value: parseInt(index, 10)
|
||||
})),
|
||||
{
|
||||
label: "Create one",
|
||||
value: -1,
|
||||
default: true
|
||||
}]}
|
||||
isSelected={v => v === folderIndex}
|
||||
select={setIndex}
|
||||
serialize={String}
|
||||
/>
|
||||
{folderIndex === -1 && <FolderAppearanceFields
|
||||
name={folderName}
|
||||
setName={setFolderName}
|
||||
color={folderColor}
|
||||
setColor={setFolderColor}
|
||||
iconName={iconName}
|
||||
setIconName={setIconName}
|
||||
/>}
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => onSave(folderIndex, folderName, folderColor, iconName)}
|
||||
>Save</Button>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => closeModal(modalKey)}
|
||||
>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title={<BaseText size="lg" weight="semibold">Add Bookmark to Folder</BaseText>}
|
||||
actions={[
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: () => onSave(folderIndex, folderName, folderColor, iconName)
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: () => closeModal(modalKey)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading className={Margins.top16}>Select a folder</Heading>
|
||||
<Select
|
||||
options={[...Object.entries(bookmarks)
|
||||
.filter(([, bookmark]) => isBookmarkFolder(bookmark))
|
||||
.map(([index, bookmark]) => ({
|
||||
label: bookmark.name,
|
||||
value: parseInt(index, 10)
|
||||
})),
|
||||
{
|
||||
label: "Create one",
|
||||
value: -1,
|
||||
default: true
|
||||
}]}
|
||||
isSelected={v => v === folderIndex}
|
||||
select={setIndex}
|
||||
serialize={String}
|
||||
/>
|
||||
{folderIndex === -1 && <FolderAppearanceFields
|
||||
name={folderName}
|
||||
setName={setFolderName}
|
||||
color={folderColor}
|
||||
setColor={setFolderColor}
|
||||
iconName={iconName}
|
||||
setIconName={setIconName}
|
||||
/>}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function DeleteFolderConfirmationModal({ modalProps, modalKey, onConfirm }) {
|
||||
function DeleteFolderConfirmationModal({ modalProps, modalKey, onConfirm }: {
|
||||
modalProps: RenderModalProps,
|
||||
modalKey: string,
|
||||
onConfirm: () => void;
|
||||
}) {
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold">Are you sure?</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph className={Margins.top16}>
|
||||
Deleting a bookmark folder will also delete all bookmarks within it.
|
||||
</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => closeModal(modalKey)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title={<BaseText size="lg" weight="semibold">Are you sure?</BaseText>}
|
||||
actions={[
|
||||
{
|
||||
text: "Delete",
|
||||
variant: "critical-primary",
|
||||
onClick: onConfirm
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: () => closeModal(modalKey)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph className={Margins.top16}>
|
||||
Deleting a bookmark folder will also delete all bookmarks within it.
|
||||
</Paragraph>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
import { CogWheel } from "@components/Icons";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { classes } from "@utils/misc";
|
||||
import { type ModalProps,ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,openModal, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
|
||||
import {
|
||||
type CommandEntry, getCategoryGroupLabel, getCategoryWeight, getCommandSearchText, getRecentRank, getRegistryVersion, listCommands, subscribeRegistry
|
||||
|
|
@ -56,7 +56,7 @@ export function openCommandPicker(props: CommandPickerProps) {
|
|||
));
|
||||
}
|
||||
|
||||
function CommandPickerModal({ modalProps, commands: providedCommands, allowMultiple = false, initialQuery = "", initialSelectedIds = [], includeHiddenInSearch = false, emptyStateText = "No commands found", filter: filterPredicate, onSelect, onComplete, onClose }: CommandPickerProps & { modalProps: ModalProps; }) {
|
||||
function CommandPickerModal({ modalProps, commands: providedCommands, allowMultiple = false, initialQuery = "", initialSelectedIds = [], includeHiddenInSearch = false, emptyStateText = "No commands found", filter: filterPredicate, onSelect, onComplete, onClose }: CommandPickerProps & { modalProps: RenderModalProps; }) {
|
||||
const [query, setQuery] = useState(initialQuery);
|
||||
const [registryVersion, setRegistryVersion] = useState(() => getRegistryVersion());
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
|
|
@ -321,8 +321,8 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.SMALL} className={classes(cn("vc-command-palette"), cl("picker"))}>
|
||||
<div className={classes(cl("shell"), cl("picker-shell"))} onKeyDown={handleKeyDown}>
|
||||
<Modal {...modalProps} size="sm" title="Command Picker">
|
||||
<div className={classes(cl("shell"), cl("picker-shell"), cn("vc-command-palette"), cl("picker"))} onKeyDown={handleKeyDown}>
|
||||
<div className={cl("input")}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
|
|
@ -373,6 +373,6 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti
|
|||
})}
|
||||
</div>
|
||||
</div>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import { closeModal, openModal } from "@webpack/common";
|
||||
|
||||
import { CommandPaletteModal } from "./ui/CommandPaletteModal";
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import { classNameFactory, classNameToSelector } from "@utils/css";
|
|||
import { copyWithToast } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { classes } from "@utils/misc";
|
||||
import { type ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { TextInput, Toasts, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,TextInput, Toasts, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
import type { KeyboardEvent as ReactKeyboardEvent } from "react";
|
||||
|
||||
import { buildQueryResolution } from "../actions/executors";
|
||||
|
|
@ -282,7 +282,7 @@ function createInitialPageState(ref: PalettePageRef): PalettePageValuesState {
|
|||
};
|
||||
}
|
||||
|
||||
export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: ModalProps; instanceKey: number; }) {
|
||||
export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: RenderModalProps; instanceKey: number; }) {
|
||||
const {
|
||||
compactStartEnabled = true,
|
||||
closeAfterExecute = true
|
||||
|
|
@ -1469,12 +1469,12 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: M
|
|||
}, [activePage, activePageFieldKey, activePageSpec, activePageState, activePageSuggestions]);
|
||||
|
||||
return (
|
||||
<ModalRoot
|
||||
<Modal
|
||||
{...modalProps}
|
||||
className={cn("vc-command-palette", compact && cl("compact"))}
|
||||
size={compact ? ModalSize.SMALL : ModalSize.LARGE}
|
||||
size={compact ? "sm" : "lg"}
|
||||
title="Command Palette"
|
||||
>
|
||||
<div className={cl("shell")} onKeyDown={onKeyDown}>
|
||||
<div className={cn("vc-command-palette", cl("shell"), compact && cl("compact"))} onKeyDown={onKeyDown}>
|
||||
{!isPageOpen && (
|
||||
<CommandPaletteInput
|
||||
inputRef={mainInputRef}
|
||||
|
|
@ -1656,6 +1656,6 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: M
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { closeModal, ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import { Button, Menu, Slider, TextInput, useState } from "@webpack/common";
|
||||
import { Button, closeModal, Menu, Modal,openModalLazy, Slider, TextInput, useState } from "@webpack/common";
|
||||
|
||||
import { folderIconsData, settings } from "./settings";
|
||||
import { folderProp, int2rgba, setFolderData } from "./util";
|
||||
|
|
@ -95,17 +94,12 @@ export function makeContextItem(a: folderProp) {
|
|||
action={() => {
|
||||
openModalLazy(async () => {
|
||||
return props => (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader >
|
||||
<div style={{
|
||||
color: "white"
|
||||
}}>
|
||||
Set a New Icon.
|
||||
</div>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<ImageModal folderId={a.folderId} folderColor={a.folderColor} />
|
||||
</ModalContent>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Set a New Icon."
|
||||
>
|
||||
<ImageModal folderId={a.folderId} folderColor={a.folderColor} />
|
||||
<div style={{
|
||||
color: "white",
|
||||
margin: "2.5%",
|
||||
|
|
@ -113,7 +107,7 @@ export function makeContextItem(a: folderProp) {
|
|||
}}>
|
||||
You might have to hover the folder after setting in order for it to refresh.
|
||||
</div>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
*/
|
||||
|
||||
import { set } from "@api/DataStore";
|
||||
import { HeadingPrimary, HeadingSecondary } from "@components/Heading";
|
||||
import { HeadingSecondary } from "@components/Heading";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { Button, ColorPicker, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { ColorPicker, Modal, React, useState } from "@webpack/common";
|
||||
|
||||
import { colors, DATASTORE_KEY } from "./index";
|
||||
|
||||
const cl = classNameFactory("vc-customColors-");
|
||||
|
||||
export function SetColorModal({ id, modalProps }: { id: string, modalProps: ModalProps; }) {
|
||||
export function SetColorModal({ id, modalProps }: { id: string, modalProps: RenderModalProps; }) {
|
||||
const initialColor = parseInt(colors[id], 16) || 372735;
|
||||
// color picker default to current color set for user (if null it's 0x05afff :3 )
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export function SetColorModal({ id, modalProps }: { id: string, modalProps: Moda
|
|||
setColorPickerColor(color);
|
||||
}
|
||||
|
||||
function handleKey(e: KeyboardEvent) {
|
||||
function handleKey(e: React.KeyboardEvent) {
|
||||
if (e.key === "Enter")
|
||||
saveUserColor();
|
||||
}
|
||||
|
|
@ -44,14 +44,24 @@ export function SetColorModal({ id, modalProps }: { id: string, modalProps: Moda
|
|||
}
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<HeadingPrimary>
|
||||
Custom Color
|
||||
</HeadingPrimary>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")} onKeyDown={handleKey}>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Custom Color"
|
||||
actions={[
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: saveUserColor
|
||||
},
|
||||
{
|
||||
text: "Delete Entry",
|
||||
variant: "dangerPrimary",
|
||||
onClick: deleteUserColor
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div onKeyDown={handleKey} className={cl("modal-content")}>
|
||||
<section className={Margins.bottom16}>
|
||||
<HeadingSecondary>
|
||||
Pick a Color
|
||||
|
|
@ -62,22 +72,7 @@ export function SetColorModal({ id, modalProps }: { id: string, modalProps: Moda
|
|||
showEyeDropper={false}
|
||||
/>
|
||||
</section>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
onClick={deleteUserColor}
|
||||
>
|
||||
Delete Entry
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.BRAND}
|
||||
onClick={saveUserColor}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
|||
import { get } from "@api/DataStore";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Channel, User } from "@vencord/discord-types";
|
||||
import { extractAndLoadChunksLazy } from "@webpack";
|
||||
import { ChannelStore, Menu, SelectedChannelStore } from "@webpack/common";
|
||||
import { ChannelStore, Menu, openModal,SelectedChannelStore } from "@webpack/common";
|
||||
|
||||
import { SetColorModal } from "./SetColorModal";
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ import "./styles.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Paginator, requirePaginator } from "@plugins/reviewDB/components/ReviewModal";
|
||||
import { ModalCloseButton } from "@utils/modal";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { findCssClassesLazy } from "@webpack";
|
||||
import { findComponentByCodeLazy, findCssClassesLazy } from "@webpack";
|
||||
import { React, useRef, useState } from "@webpack/common";
|
||||
|
||||
const CloseButton = findComponentByCodeLazy("CLOSE_BUTTON_LABEL");
|
||||
import { MutableRefObject } from "react";
|
||||
|
||||
import { jumper } from "./index";
|
||||
|
|
@ -53,7 +54,7 @@ export default function ReplyNavigator({ replies }: { replies: Message[]; }) {
|
|||
totalCount={replies.length}
|
||||
onPageChange={processPageChange}
|
||||
/>
|
||||
<ModalCloseButton className={"vc-findreply-close"} onClick={() => setVisible(false)} />
|
||||
<CloseButton className={"vc-findreply-close"} onClick={() => setVisible(false)} />
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import { Flex } from "@components/Flex";
|
|||
import { Paragraph } from "@components/Paragraph";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Forms, RelationshipStore } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Forms, Modal,openModal, RelationshipStore } from "@webpack/common";
|
||||
|
||||
interface rankInfo {
|
||||
title: string;
|
||||
|
|
@ -84,10 +84,12 @@ const ranks: rankInfo[] =
|
|||
];
|
||||
|
||||
function openRankModal(rank: rankInfo) {
|
||||
openModal(props => (
|
||||
openModal((props: RenderModalProps) => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...props} size={ModalSize.DYNAMIC}>
|
||||
<ModalHeader>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title={
|
||||
<Flex className={cl("flex")}>
|
||||
<Forms.FormTitle
|
||||
className={cl("img")}
|
||||
|
|
@ -97,15 +99,14 @@ function openRankModal(rank: rankInfo) {
|
|||
{rank.title}
|
||||
</Forms.FormTitle>
|
||||
</Flex>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div className={cl("text")}>
|
||||
<Paragraph>
|
||||
{rank.description}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
}
|
||||
>
|
||||
<div className={cl("text")}>
|
||||
<Paragraph>
|
||||
{rank.description}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary >
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@
|
|||
*/
|
||||
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { Channel, RenderModalProps } from "@vencord/discord-types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Avatar, Button, ChannelStore, MessageStore, React, Text, UserStore } from "@webpack/common";
|
||||
import { Avatar, Button, ChannelStore, MessageStore, Modal,React, Text, UserStore } from "@webpack/common";
|
||||
|
||||
const cl = classNameFactory("vc-boo-");
|
||||
|
||||
|
|
@ -37,9 +36,8 @@ function GroupDmsIcon({ channel }: { channel: Channel; }) {
|
|||
}
|
||||
|
||||
interface GhostedUsersModalProps {
|
||||
modalProps: any;
|
||||
modalProps: RenderModalProps;
|
||||
ghostedChannels: string[];
|
||||
onClose: () => void;
|
||||
onClearGhost: (channelId: string) => void;
|
||||
}
|
||||
|
||||
|
|
@ -57,14 +55,14 @@ export function getChannelDisplayName(channelId: string): string {
|
|||
return user?.username || "Unknown User";
|
||||
}
|
||||
|
||||
export function GhostedUsersModal({ modalProps, ghostedChannels: initialChannels, onClose, onClearGhost }: GhostedUsersModalProps) {
|
||||
export function GhostedUsersModal({ modalProps, ghostedChannels: initialChannels, onClearGhost }: GhostedUsersModalProps) {
|
||||
const [ghostedChannels, setGhostedChannels] = React.useState(initialChannels);
|
||||
|
||||
const handleChannelClick = (channelId: string) => {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (channel) {
|
||||
SelectedChannelActionCreators.selectPrivateChannel(channelId);
|
||||
onClose();
|
||||
modalProps.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -83,77 +81,67 @@ export function GhostedUsersModal({ modalProps, ghostedChannels: initialChannels
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader>
|
||||
<Text
|
||||
variant="heading-lg/semibold"
|
||||
className={cl("modal-header")}
|
||||
>
|
||||
Ghosted Users ({ghostedChannels.length})
|
||||
</Text>
|
||||
{ghostedChannels.length > 0 && (
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
onClick={handleClearAll}
|
||||
className={cl("clear")}
|
||||
>
|
||||
Clear All
|
||||
</Button>
|
||||
)}
|
||||
<ModalCloseButton onClick={onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div className={cl("modal-content")}>
|
||||
{ghostedChannels.length === 0 ? (
|
||||
<Text variant="text-md/normal">No ghosts here!</Text>
|
||||
) : (
|
||||
ghostedChannels.map(channelId => {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (!channel) return null;
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title={`Ghosted Users (${ghostedChannels.length})`}
|
||||
actions={ghostedChannels.length > 0 ? [
|
||||
{
|
||||
text: "Clear All",
|
||||
variant: "primary",
|
||||
onClick: handleClearAll
|
||||
}
|
||||
] : []}
|
||||
>
|
||||
<div className={cl("modal-content")}>
|
||||
{ghostedChannels.length === 0 ? (
|
||||
<Text variant="text-md/normal">No ghosts here!</Text>
|
||||
) : (
|
||||
ghostedChannels.map(channelId => {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (!channel) return null;
|
||||
|
||||
const lastMessage = MessageStore.getMessages(channelId)?.last();
|
||||
const lastMessageDate = lastMessage?.timestamp ? formatMessageDate(lastMessage.timestamp) : "";
|
||||
const lastMessage = MessageStore.getMessages(channelId)?.last();
|
||||
const lastMessageDate = lastMessage?.timestamp ? formatMessageDate(lastMessage.timestamp) : "";
|
||||
|
||||
const displayName = getChannelDisplayName(channel.id);
|
||||
const userId = channel?.recipients?.[0] ?? "";
|
||||
return (
|
||||
<div
|
||||
key={channelId}
|
||||
onClick={() => handleChannelClick(channelId)}
|
||||
className={cl("ghosted-entry")}
|
||||
>
|
||||
{channel.isGroupDM() ?
|
||||
<GroupDmsIcon
|
||||
channel={channel}
|
||||
/> : <Avatar
|
||||
src={UserStore.getUser(userId)?.getAvatarURL(undefined, 128, true)}
|
||||
size="SIZE_40"
|
||||
aria-label={displayName}
|
||||
/>}
|
||||
<div className={cl("user-info")}>
|
||||
<Text variant="text-md/normal">
|
||||
{displayName}
|
||||
const displayName = getChannelDisplayName(channel.id);
|
||||
const userId = channel?.recipients?.[0] ?? "";
|
||||
return (
|
||||
<div
|
||||
key={channelId}
|
||||
onClick={() => handleChannelClick(channelId)}
|
||||
className={cl("ghosted-entry")}
|
||||
>
|
||||
{channel.isGroupDM() ?
|
||||
<GroupDmsIcon
|
||||
channel={channel}
|
||||
/> : <Avatar
|
||||
src={UserStore.getUser(userId)?.getAvatarURL(undefined, 128, true)}
|
||||
size="SIZE_40"
|
||||
aria-label={displayName}
|
||||
/>}
|
||||
<div className={cl("user-info")}>
|
||||
<Text variant="text-md/normal">
|
||||
{displayName}
|
||||
</Text>
|
||||
{lastMessageDate && (
|
||||
<Text variant="text-xs/normal" className={cl("modal-text")}>
|
||||
{lastMessageDate}
|
||||
</Text>
|
||||
{lastMessageDate && (
|
||||
<Text variant="text-xs/normal" className={cl("modal-text")}>
|
||||
{lastMessageDate}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
onClick={e => handleClearClick(e, channelId)}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
onClick={e => handleClearClick(e, channelId)}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,9 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { Menu, Tooltip, useEffect, useState } from "@webpack/common";
|
||||
import { Menu, openModal,Tooltip, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { Boo, clearChannelFromGhost, getBooCount, getGhostedChannels, onBooCountChange } from "./Boo";
|
||||
import { getChannelDisplayName, GhostedUsersModal } from "./GhostedUsersModal";
|
||||
|
|
@ -85,12 +84,11 @@ function BooIndicator() {
|
|||
|
||||
const handleClick = () => {
|
||||
const ghostedChannels = getGhostedChannels();
|
||||
const modalKey = openModal(modalProps => (
|
||||
openModal(modalProps => (
|
||||
<ErrorBoundary>
|
||||
<GhostedUsersModal
|
||||
modalProps={modalProps}
|
||||
ghostedChannels={ghostedChannels}
|
||||
onClose={() => closeModal(modalKey)}
|
||||
onClearGhost={clearChannelFromGhost}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { Flex } from "@components/Flex";
|
|||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Button, FluxDispatcher, TextInput, useCallback, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Button, FluxDispatcher, Modal, openModal, TextInput, useCallback, useState } from "@webpack/common";
|
||||
|
||||
import { settings } from "../settings";
|
||||
import { Collection, Gif } from "../types";
|
||||
|
|
@ -49,124 +49,116 @@ export function openRenameCollectionModal(name: string) {
|
|||
openModal(props => <RenameCollectionModal props={props} name={name} />);
|
||||
}
|
||||
|
||||
function InfoModal({ props, title, rows }: { props: ModalProps; title: string; rows: { label: string; value: string; }[]; }) {
|
||||
function InfoModal({ props, title, rows }: { props: RenderModalProps; title: string; rows: { label: string; value: string; }[]; }) {
|
||||
return (
|
||||
<ModalRoot {...props} size={ModalSize.SMALL} className={cl("modal")}>
|
||||
<ModalHeader separator={false} className={cl("modal-header")}>
|
||||
<Paragraph className={cl("modal-title")}>{title}</Paragraph>
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<section>
|
||||
{rows.map(row => (
|
||||
<Flex key={row.label} className={cl("info-row")}>
|
||||
<Heading className={cl("info-title")}>{row.label}</Heading>
|
||||
<Paragraph className={cl("info-text")}>{row.value}</Paragraph>
|
||||
</Flex>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title={title}
|
||||
actions={[
|
||||
{ text: "Close", variant: "secondary", onClick: props.onClose }
|
||||
]}
|
||||
>
|
||||
<section>
|
||||
{rows.map(row => (
|
||||
<Flex key={row.label} className={cl("info-row")}>
|
||||
<Heading className={cl("info-title")}>{row.label}</Heading>
|
||||
<Paragraph className={cl("info-text")}>{row.value}</Paragraph>
|
||||
</Flex>
|
||||
))}
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function MoveToCollectionModal({ props, gifId }: { props: RenderModalProps; gifId: string; }) {
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Move To Collection"
|
||||
actions={[
|
||||
{ text: "Close", variant: "secondary", onClick: props.onClose }
|
||||
]}
|
||||
>
|
||||
<Heading style={{ marginBottom: "10px" }}>Select a collection to move the item to</Heading>
|
||||
<div className={cl("buttons")}>
|
||||
{cache_collections
|
||||
.filter(col => col.name !== getItemCollectionNameFromId(gifId))
|
||||
.map(col => (
|
||||
<Button
|
||||
key={col.name}
|
||||
className={cl("button")}
|
||||
onClick={async () => {
|
||||
const fromCollection = getItemCollectionNameFromId(gifId);
|
||||
if (!fromCollection) return;
|
||||
await moveGifToCollection(gifId, fromCollection, col.name);
|
||||
FluxDispatcher.dispatch({ type: "GIF_PICKER_QUERY", query: "" });
|
||||
FluxDispatcher.dispatch({ type: "GIF_PICKER_QUERY", query: fromCollection });
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
{stripPrefix(col.name)}
|
||||
</Button>
|
||||
))}
|
||||
</section>
|
||||
</ModalContent>
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button onClick={props.onClose}>Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function MoveToCollectionModal({ props, gifId }: { props: ModalProps; gifId: string; }) {
|
||||
return (
|
||||
<ModalRoot {...props} size={ModalSize.SMALL} className={cl("modal")}>
|
||||
<ModalHeader separator={false} className={cl("modal-header")}>
|
||||
<Paragraph className={cl("modal-title")}>Move To Collection</Paragraph>
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<Heading>Select a collection to move the item to</Heading>
|
||||
<div className={cl("buttons")}>
|
||||
{cache_collections
|
||||
.filter(col => col.name !== getItemCollectionNameFromId(gifId))
|
||||
.map(col => (
|
||||
<Button
|
||||
key={col.name}
|
||||
className={cl("button")}
|
||||
onClick={async () => {
|
||||
const fromCollection = getItemCollectionNameFromId(gifId);
|
||||
if (!fromCollection) return;
|
||||
await moveGifToCollection(gifId, fromCollection, col.name);
|
||||
FluxDispatcher.dispatch({ type: "GIF_PICKER_QUERY", query: "" });
|
||||
FluxDispatcher.dispatch({ type: "GIF_PICKER_QUERY", query: fromCollection });
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
{stripPrefix(col.name)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button onClick={props.onClose}>Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
|
||||
function CreateCollectionModal({ props, gif }: { props: ModalProps; gif: Gif; }) {
|
||||
function CreateCollectionModal({ props, gif }: { props: RenderModalProps; gif: Gif; }) {
|
||||
const [name, setName] = useState("");
|
||||
const onSubmit = useCallback((e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const onSubmit = useCallback((e?: React.FormEvent) => {
|
||||
e?.preventDefault();
|
||||
if (!name.length) return;
|
||||
createCollection(name, [gif]);
|
||||
props.onClose();
|
||||
}, [name, gif, props]);
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Create Collection"
|
||||
actions={[
|
||||
{ text: "Create", onClick: onSubmit, disabled: !name.length, variant: "primary" }
|
||||
]}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<ModalHeader>
|
||||
<Paragraph>Create Collection</Paragraph>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Heading className={cl("rename-text")}>Collection Name</Heading>
|
||||
<TextInput onChange={setName} />
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button type="submit" color={Button.Colors.GREEN} disabled={!name.length} onClick={onSubmit}>
|
||||
Create
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
<Heading className={cl("rename-text")}>Collection Name</Heading>
|
||||
<TextInput onChange={setName} />
|
||||
</form>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function RenameCollectionModal({ props, name }: { props: ModalProps; name: string; }) {
|
||||
function RenameCollectionModal({ props, name }: { props: RenderModalProps; name: string; }) {
|
||||
const prefix = settings.store.collectionPrefix;
|
||||
const strippedName = name.startsWith(prefix) ? name.slice(prefix.length) : name;
|
||||
const [newName, setNewName] = useState(strippedName);
|
||||
const tooLong = newName.length >= 25;
|
||||
|
||||
const onSubmit = useCallback(async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const onSubmit = useCallback(async (e?: React.FormEvent) => {
|
||||
e?.preventDefault();
|
||||
if (!newName.length || tooLong) return;
|
||||
await renameCollection(name, newName);
|
||||
props.onClose();
|
||||
}, [newName, name, tooLong, props]);
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Rename Collection"
|
||||
actions={[
|
||||
{ text: "Rename", onClick: onSubmit, disabled: !newName.length || tooLong, variant: "primary" }
|
||||
]}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<ModalHeader>
|
||||
<Paragraph>Rename Collection</Paragraph>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph className={cl("rename-text")}>New Collection Name</Paragraph>
|
||||
<TextInput value={newName} className={classes(cl("rename-input"), tooLong ? cl("input-warning") : "")} onChange={setNewName} />
|
||||
{tooLong && <Paragraph className={cl("warning-text")}>Name can't be longer than 24 characters</Paragraph>}
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button type="submit" color={Button.Colors.GREEN} disabled={!newName.length || tooLong} onClick={onSubmit}>
|
||||
Rename
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
<Paragraph className={cl("rename-text")}>New Collection Name</Paragraph>
|
||||
<TextInput value={newName} className={classes(cl("rename-input"), tooLong ? cl("input-warning") : "")} onChange={setNewName} />
|
||||
{tooLong && <Paragraph className={cl("warning-text")}>Name can't be longer than 24 characters</Paragraph>}
|
||||
</form>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import { Span } from "@components/Span";
|
|||
import { fetchReposByUserId, fetchReposByUsername, fetchUserInfo, GitHubUserInfo } from "@equicordplugins/githubRepos/githubApi";
|
||||
import { GitHubRepo } from "@equicordplugins/githubRepos/types";
|
||||
import { classes } from "@utils/misc";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { findCssClassesLazy } from "@webpack";
|
||||
import { Clickable, React, useEffect, UserProfileStore, useState } from "@webpack/common";
|
||||
import { Clickable, openModal,React, useEffect, UserProfileStore, useState } from "@webpack/common";
|
||||
|
||||
import { ReposModal } from "./ReposModal";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
import { BaseText } from "@components/BaseText";
|
||||
import { fetchReposByUserId, fetchReposByUsername, fetchUserInfo, GitHubUserInfo } from "@equicordplugins/githubRepos/githubApi";
|
||||
import { GitHubRepo } from "@equicordplugins/githubRepos/types";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { React, useEffect, UserProfileStore, useState } from "@webpack/common";
|
||||
import { openModal,React, useEffect, UserProfileStore, useState } from "@webpack/common";
|
||||
|
||||
import { cl, settings } from "..";
|
||||
import { RepoCard } from "./RepoCard";
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
import { HeadingPrimary } from "@components/Heading";
|
||||
import { getLanguageColor } from "@equicordplugins/githubRepos/colors";
|
||||
import { GitHubRepo } from "@equicordplugins/githubRepos/types";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalRoot } from "@utils/modal";
|
||||
import { React } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, React } from "@webpack/common";
|
||||
|
||||
import { cl } from "..";
|
||||
import { Star } from "./Star";
|
||||
|
|
@ -20,7 +18,7 @@ interface ReposModalProps {
|
|||
rootProps: any;
|
||||
}
|
||||
|
||||
export function ReposModal({ repos, username, rootProps }: ReposModalProps) {
|
||||
export function ReposModal({ repos, username, rootProps }: ReposModalProps & { rootProps: RenderModalProps; }) {
|
||||
const renderTableHeader = () => (
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -63,44 +61,38 @@ export function ReposModal({ repos, username, rootProps }: ReposModalProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<ModalRoot className={cl("modal")} size="large" {...rootProps}>
|
||||
<ModalHeader>
|
||||
<HeadingPrimary className={cl("modal-title")}>
|
||||
{username}'s GitHub Repositories
|
||||
</HeadingPrimary>
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<div className={cl("table-container")}>
|
||||
<table className={cl("table")}>
|
||||
<colgroup>
|
||||
<col className={cl("header-repo")} />
|
||||
<col className={cl("header-description")} />
|
||||
<col className={cl("header-language")} />
|
||||
<col className={cl("header-stars")} />
|
||||
</colgroup>
|
||||
{renderTableHeader()}
|
||||
<tbody>
|
||||
{repos.map(renderTableRow)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button
|
||||
className={cl("modal-footer-github")}
|
||||
variant="link"
|
||||
onClick={() => window.open(`https://github.com/${username}?tab=repositories`, "_blank")}
|
||||
>
|
||||
View on GitHub
|
||||
</Button>
|
||||
<Button
|
||||
className={cl("modal-footer-close")}
|
||||
variant="secondary"
|
||||
onClick={rootProps.onClose}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...rootProps}
|
||||
className={cl("modal")}
|
||||
size="lg"
|
||||
title={`${username}'s GitHub Repositories`}
|
||||
actions={[
|
||||
{
|
||||
text: "View on GitHub",
|
||||
variant: "link",
|
||||
onClick: () => window.open(`https://github.com/${username}?tab=repositories`, "_blank")
|
||||
},
|
||||
{
|
||||
text: "Close",
|
||||
variant: "secondary",
|
||||
onClick: rootProps.onClose
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div className={cl("table-container")}>
|
||||
<table className={cl("table")}>
|
||||
<colgroup>
|
||||
<col className={cl("header-repo")} />
|
||||
<col className={cl("header-description")} />
|
||||
<col className={cl("header-language")} />
|
||||
<col className={cl("header-stars")} />
|
||||
</colgroup>
|
||||
{renderTableHeader()}
|
||||
<tbody>
|
||||
{repos.map(renderTableRow)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { HeadingTertiary } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { React, Tooltip } from "@webpack/common";
|
||||
import { RenderModalProps, User } from "@vencord/discord-types";
|
||||
import { Modal, openModal, React, Tooltip } from "@webpack/common";
|
||||
|
||||
import { cl, GlobalBadges, INVITE_LINK } from "./utils";
|
||||
|
||||
|
|
@ -37,20 +35,32 @@ export function openBadgeModal(user: User) {
|
|||
<BadgeModalComponent key={badge.key} tooltip={badge.tooltip} badge={badge.badge} />
|
||||
));
|
||||
|
||||
openModal(modalprops => (
|
||||
<ModalRoot {...modalprops}>
|
||||
<ErrorBoundary>
|
||||
<ModalHeader>
|
||||
openModal((modalprops: RenderModalProps) => (
|
||||
<Modal
|
||||
{...modalprops}
|
||||
size="sm"
|
||||
title={
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||
<img
|
||||
className={cl("modal-avatar")}
|
||||
src={user.getAvatarURL(undefined, 512, true)}
|
||||
alt=""
|
||||
/>
|
||||
<HeadingTertiary className={cl("modal-name")}>
|
||||
<HeadingTertiary className={cl("modal-name")} style={{ color: "white" }}>
|
||||
{user.username}
|
||||
</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-description")}>
|
||||
</div>
|
||||
}
|
||||
actions={[
|
||||
{
|
||||
text: "Join GlobalBadges Server",
|
||||
variant: "link",
|
||||
onClick: () => openInviteModal(INVITE_LINK)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<div className={cl("modal-description")}>
|
||||
{badgeDataElements.length ? (
|
||||
<>
|
||||
<Paragraph className={cl("modal-text")}>
|
||||
|
|
@ -66,16 +76,8 @@ export function openBadgeModal(user: User) {
|
|||
No global badges.
|
||||
</Paragraph>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => openInviteModal(INVITE_LINK)}
|
||||
>
|
||||
Join GlobalBadges Server
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,40 +10,21 @@ import { HiddenServersStore } from "@equicordplugins/hideServers/HiddenServersSt
|
|||
import { classNameFactory } from "@utils/css";
|
||||
import { getGuildAcronym } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import {
|
||||
closeModal,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
ModalSize,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { Guild } from "@vencord/discord-types";
|
||||
import { Guild, RenderModalProps } from "@vencord/discord-types";
|
||||
import { findCssClassesLazy } from "@webpack";
|
||||
import { Button, IconUtils, useStateFromStores } from "@webpack/common";
|
||||
import { Button, closeModal, IconUtils, Modal,openModal, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { SortedGuildStore } from "..";
|
||||
|
||||
const cl = classNameFactory("vc-hideservers-");
|
||||
const IconClasses = findCssClassesLazy("icon", "acronym", "childWrapper");
|
||||
|
||||
function HiddenServersModal({ modalProps, close }: { modalProps: ModalProps; close(): void; }) {
|
||||
function HiddenServersModal({ modalProps, close }: { modalProps: RenderModalProps; close(): void; }) {
|
||||
const guilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail());
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>
|
||||
Hidden Servers
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<HiddenServersMenu guilds={guilds} />
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Modal {...modalProps} size="lg" title="Hidden Servers">
|
||||
<HiddenServersMenu guilds={guilds} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { HeadingSecondary } from "@components/Heading";
|
||||
|
|
@ -13,18 +12,8 @@ import { Paragraph } from "@components/Paragraph";
|
|||
import { TooltipContainer } from "@components/TooltipContainer";
|
||||
import { copyWithToast, getIntlMessage } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import {
|
||||
CloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
ModalSize,
|
||||
openModal
|
||||
} from "@utils/modal";
|
||||
import { saveFile } from "@utils/web";
|
||||
import { Icon } from "@vencord/discord-types";
|
||||
import { Icon, RenderModalProps } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import {
|
||||
Clickable,
|
||||
|
|
@ -32,6 +21,8 @@ import {
|
|||
createRoot,
|
||||
FluxDispatcher,
|
||||
Menu,
|
||||
Modal,
|
||||
openModal,
|
||||
ReactDOM,
|
||||
useCallback,
|
||||
useEffect,
|
||||
|
|
@ -226,7 +217,7 @@ function OtherContextMenu({ iconName, Icon, color }: { iconName: string; Icon: I
|
|||
);
|
||||
}
|
||||
|
||||
function IconModal({ iconName, Icon, onClose, transitionState }: { iconName: string; Icon: Icon; } & ModalProps) {
|
||||
function IconModal({ iconName, Icon, onClose, transitionState }: { iconName: string; Icon: Icon; } & RenderModalProps) {
|
||||
const [color, setColor] = useColorNavigation(209);
|
||||
const colorData = cssColors[color];
|
||||
const colorKeys = useMemo(() => getCssColorKeys(), []);
|
||||
|
|
@ -247,62 +238,59 @@ function IconModal({ iconName, Icon, onClose, transitionState }: { iconName: str
|
|||
});
|
||||
}, [colorKeys.length, setColor]);
|
||||
|
||||
const openOtherMenu = (e: React.MouseEvent) => {
|
||||
ContextMenuApi.openContextMenu(e, () => (
|
||||
const openOtherMenu = (e?: React.MouseEvent) => {
|
||||
if (e) ContextMenuApi.openContextMenu(e, () => (
|
||||
<OtherContextMenu iconName={iconName} Icon={Icon} color={color} />
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false} className="vc-ic-modal-header">
|
||||
<div className="vc-ic-modal-header-content">
|
||||
<BaseText size="lg" weight="semibold" className="vc-ic-modal-title">{iconName}</BaseText>
|
||||
<Modal
|
||||
transitionState={transitionState}
|
||||
onClose={onClose}
|
||||
size="md"
|
||||
title={iconName}
|
||||
actions={[
|
||||
{
|
||||
text: "Actions",
|
||||
variant: "primary",
|
||||
onClick: openOtherMenu
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Flex className="vc-ic-modal-main">
|
||||
<div
|
||||
className="vc-ic-icon-preview"
|
||||
aria-label={colorData?.name}
|
||||
onContextMenu={openColorMenu}
|
||||
onWheel={onWheel}
|
||||
>
|
||||
<Icon className="vc-ic-icon-large" color={colorData?.css} fill={fill} />
|
||||
</div>
|
||||
<div className="vc-ic-modal-header-trailing">
|
||||
<CloseButton onClick={onClose} />
|
||||
</div>
|
||||
</ModalHeader>
|
||||
<ModalContent className="vc-ic-modal-content">
|
||||
<Flex className="vc-ic-modal-main">
|
||||
<div
|
||||
className="vc-ic-icon-preview"
|
||||
aria-label={colorData?.name}
|
||||
onContextMenu={openColorMenu}
|
||||
onWheel={onWheel}
|
||||
>
|
||||
<Icon className="vc-ic-icon-large" color={colorData?.css} fill={fill} />
|
||||
</div>
|
||||
<Flex flexDirection="column" className="vc-ic-icon-info">
|
||||
<Flex className="vc-ic-icon-sizes">
|
||||
{iconSizes.map(size => (
|
||||
<TooltipContainer text={size} key={size}>
|
||||
<Icon size={size} color={colorData?.css} fill={fill} />
|
||||
</TooltipContainer>
|
||||
))}
|
||||
</Flex>
|
||||
<TooltipContainer text="Right-click icon to change">
|
||||
<BaseText size="sm" color="text-muted" className="vc-ic-color-label">
|
||||
{colorData?.name}
|
||||
</BaseText>
|
||||
</TooltipContainer>
|
||||
<Flex flexDirection="column" className="vc-ic-icon-info">
|
||||
<Flex className="vc-ic-icon-sizes">
|
||||
{iconSizes.map(size => (
|
||||
<TooltipContainer text={size} key={size}>
|
||||
<Icon size={size} color={colorData?.css} fill={fill} />
|
||||
</TooltipContainer>
|
||||
))}
|
||||
</Flex>
|
||||
<TooltipContainer text="Right-click icon to change">
|
||||
<BaseText size="sm" color="text-muted" className="vc-ic-color-label">
|
||||
{colorData?.name}
|
||||
</BaseText>
|
||||
</TooltipContainer>
|
||||
</Flex>
|
||||
<div className="vc-ic-use-as">
|
||||
<BaseText size="md" weight="semibold">Usage</BaseText>
|
||||
<BaseText size="sm" color="text-muted">Click to copy</BaseText>
|
||||
</div>
|
||||
{/* for some reason i cant make this shit codeblock full width, FF 15 */}
|
||||
<Clickable className="vc-ic-codeblock-wrapper" onClick={() => copyWithToast(findCode, "Copied!")}>
|
||||
<CodeBlock content={findCode} lang="ts" />
|
||||
</Clickable>
|
||||
</ModalContent>
|
||||
<ModalFooter className="vc-ic-modal-footer">
|
||||
<Button variant="primary" onClick={openOtherMenu}>
|
||||
Actions
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</Flex>
|
||||
<div className="vc-ic-use-as">
|
||||
<BaseText size="md" weight="semibold">Usage</BaseText>
|
||||
<BaseText size="sm" color="text-muted">Click to copy</BaseText>
|
||||
</div>
|
||||
{/* for some reason i cant make this shit codeblock full width, FF 15 */}
|
||||
<Clickable className="vc-ic-codeblock-wrapper" onClick={() => copyWithToast(findCode, "Copied!")}>
|
||||
<CodeBlock content={findCode} lang="ts" />
|
||||
</Clickable>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,38 +7,35 @@
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { GuildMember } from "@vencord/discord-types";
|
||||
import { Parser } from "@webpack/common";
|
||||
import { GuildMember, RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,openModal, Parser, React } from "@webpack/common";
|
||||
|
||||
const cl = classNameFactory("vc-inrole-");
|
||||
|
||||
export function showInRoleModal(members: GuildMember[], roleId: string, channelId: string) {
|
||||
openModal(props =>
|
||||
<>
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...props} size={ModalSize.DYNAMIC} fullscreenOnMobile={true} >
|
||||
<ModalHeader className={cl("header")}>
|
||||
<Paragraph style={{ fontSize: "1.2rem", fontWeight: "bold", marginRight: "7px" }}>Members of role {
|
||||
Parser.parse(`<@&${roleId}>`, true, { channelId, viewingChannelId: channelId })
|
||||
} ({members.length})</Paragraph>
|
||||
<ModalCloseButton onClick={props.onClose} className={cl("close")} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div style={{ padding: "13px 20px" }} className={cl("member-list")}>
|
||||
{
|
||||
members.length !== 0 ? members.map(member =>
|
||||
<>
|
||||
<Paragraph className={cl("modal-member")}>
|
||||
{Parser.parse(`<@${member.userId}>`, true, { channelId, viewingChannelId: channelId })}
|
||||
</Paragraph>
|
||||
</>
|
||||
) : <Paragraph>Looks like no online cached members with that role were found. Try scrolling down on your member list to cache more users!</Paragraph>
|
||||
}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
</ErrorBoundary>
|
||||
</>
|
||||
openModal((props: RenderModalProps) =>
|
||||
<ErrorBoundary>
|
||||
<Modal
|
||||
{...props}
|
||||
size="md"
|
||||
title={
|
||||
<Paragraph style={{ fontSize: "1.2rem", fontWeight: "bold", marginRight: "7px" }}>
|
||||
Members of role {Parser.parse(`<@&${roleId}>`, true, { channelId, viewingChannelId: channelId })} ({members.length})
|
||||
</Paragraph>
|
||||
}
|
||||
>
|
||||
<div style={{ padding: "13px 20px" }} className={cl("member-list")}>
|
||||
{
|
||||
members.length !== 0 ? members.map(member =>
|
||||
<React.Fragment key={member.userId}>
|
||||
<Paragraph className={cl("modal-member")}>
|
||||
{Parser.parse(`<@${member.userId}>`, true, { channelId, viewingChannelId: channelId })}
|
||||
</Paragraph>
|
||||
</React.Fragment>
|
||||
) : <Paragraph>Looks like no online cached members with that role were found. Try scrolling down on your member list to cache more users!</Paragraph>
|
||||
}
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,59 +16,49 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Heading, HeadingTertiary } from "@components/Heading";
|
||||
import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalRoot,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { Button, React, TextInput } from "@webpack/common";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, openModal, React, TextInput } from "@webpack/common";
|
||||
|
||||
import { buildEmbed, decrypt } from "../index";
|
||||
|
||||
export function DecModal(props: any) {
|
||||
export function DecModal(props: RenderModalProps & { message: any; }) {
|
||||
const encryptedMessage: string = props?.message?.content;
|
||||
const [password, setPassword] = React.useState("password");
|
||||
|
||||
const onDecrypt = () => {
|
||||
const toSend = decrypt(encryptedMessage, password, true);
|
||||
if (!toSend || !props?.message) return;
|
||||
buildEmbed(props?.message, toSend);
|
||||
props.onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>Decrypt Message</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<Heading style={{ marginTop: "10px" }}>Message with Encryption</Heading>
|
||||
<TextInput defaultValue={encryptedMessage} disabled={true}></TextInput>
|
||||
<Heading style={{ marginTop: "10px" }}>Password</Heading>
|
||||
<TextInput
|
||||
style={{ marginBottom: "20px" }}
|
||||
onChange={setPassword}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.GREEN}
|
||||
onClick={() => {
|
||||
const toSend = decrypt(encryptedMessage, password, true);
|
||||
if (!toSend || !props?.message) return;
|
||||
buildEmbed(props?.message, toSend);
|
||||
props.onClose();
|
||||
}}>
|
||||
Decrypt
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.LINK}
|
||||
style={{ left: 15, position: "absolute" }}
|
||||
onClick={props.onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Decrypt Message"
|
||||
actions={[
|
||||
{
|
||||
text: "Decrypt",
|
||||
variant: "primary",
|
||||
onClick: onDecrypt
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: props.onClose
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading style={{ marginTop: "10px" }}>Message with Encryption</Heading>
|
||||
<TextInput defaultValue={encryptedMessage} disabled={true}></TextInput>
|
||||
<Heading style={{ marginTop: "10px" }}>Password</Heading>
|
||||
<TextInput
|
||||
style={{ marginBottom: "20px" }}
|
||||
onChange={setPassword}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,21 +17,14 @@
|
|||
*/
|
||||
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Heading, HeadingTertiary } from "@components/Heading";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { insertTextIntoChatInputBox } from "@utils/discord";
|
||||
import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { Button, React, TextInput } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, openModal, React, TextInput } from "@webpack/common";
|
||||
|
||||
import { encrypt } from "../index";
|
||||
|
||||
function EncModal(props: ModalProps) {
|
||||
function EncModal(props: RenderModalProps) {
|
||||
const [secret, setSecret] = React.useState("");
|
||||
const [cover, setCover] = React.useState("");
|
||||
const [password, setPassword] = React.useState("password");
|
||||
|
|
@ -39,72 +32,65 @@ function EncModal(props: ModalProps) {
|
|||
|
||||
const isValid = secret && (noCover || (cover && cover.trim().split(" ").length > 1));
|
||||
|
||||
const onSend = () => {
|
||||
if (!isValid) return;
|
||||
const encrypted = encrypt(secret, password, noCover ? "d d" : cover);
|
||||
const toSend = noCover ? encrypted.replaceAll("d", "") : encrypted;
|
||||
if (!toSend) return;
|
||||
|
||||
insertTextIntoChatInputBox(toSend);
|
||||
|
||||
props.onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>Encrypt Message</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<Heading style={{ marginTop: "10px" }}>Secret</Heading>
|
||||
<TextInput
|
||||
onChange={(e: string) => {
|
||||
setSecret(e);
|
||||
}}
|
||||
/>
|
||||
<Heading style={{ marginTop: "10px" }}>Cover (2 or more Words!!)</Heading>
|
||||
<TextInput
|
||||
disabled={noCover}
|
||||
onChange={(e: string) => {
|
||||
setCover(e);
|
||||
}}
|
||||
/>
|
||||
<Heading style={{ marginTop: "10px" }}>Password</Heading>
|
||||
<TextInput
|
||||
style={{ marginBottom: "20px" }}
|
||||
defaultValue={"password"}
|
||||
onChange={(e: string) => {
|
||||
setPassword(e);
|
||||
}}
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Don't use a Cover"
|
||||
value={noCover}
|
||||
onChange={(e: boolean) => {
|
||||
setNoCover(e);
|
||||
}}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.GREEN}
|
||||
disabled={!isValid}
|
||||
onClick={() => {
|
||||
if (!isValid) return;
|
||||
const encrypted = encrypt(secret, password, noCover ? "d d" : cover);
|
||||
const toSend = noCover ? encrypted.replaceAll("d", "") : encrypted;
|
||||
if (!toSend) return;
|
||||
|
||||
insertTextIntoChatInputBox(toSend);
|
||||
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
look={Button.Looks.LINK}
|
||||
style={{ left: 15, position: "absolute" }}
|
||||
onClick={() => {
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title="Encrypt Message"
|
||||
actions={[
|
||||
{
|
||||
text: "Send",
|
||||
variant: "primary",
|
||||
disabled: !isValid,
|
||||
onClick: onSend
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: props.onClose
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading style={{ marginTop: "10px" }}>Secret</Heading>
|
||||
<TextInput
|
||||
onChange={(e: string) => {
|
||||
setSecret(e);
|
||||
}}
|
||||
/>
|
||||
<Heading style={{ marginTop: "10px" }}>Cover (2 or more Words!!)</Heading>
|
||||
<TextInput
|
||||
disabled={noCover}
|
||||
onChange={(e: string) => {
|
||||
setCover(e);
|
||||
}}
|
||||
/>
|
||||
<Heading style={{ marginTop: "10px" }}>Password</Heading>
|
||||
<TextInput
|
||||
style={{ marginBottom: "20px" }}
|
||||
defaultValue={"password"}
|
||||
onChange={(e: string) => {
|
||||
setPassword(e);
|
||||
}}
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Don't use a Cover"
|
||||
value={noCover}
|
||||
onChange={(e: boolean) => {
|
||||
setNoCover(e);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import "./style.css";
|
|||
import { actions } from "@equicordplugins/keyboardNavigation/commands";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { closeAllModals, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { closeAllModals, Modal,openModal, React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { settings } from "..";
|
||||
|
||||
const logger = new Logger("CommandPalette", "#e5c890");
|
||||
|
||||
export function CommandPalette({ modalProps }) {
|
||||
export function CommandPalette({ modalProps }: { modalProps: RenderModalProps; }) {
|
||||
const cl = classNameFactory("vc-command-palette-");
|
||||
const [focusedIndex, setFocusedIndex] = useState<number | null>(null);
|
||||
const [startIndex, setStartIndex] = useState(0);
|
||||
|
|
@ -100,8 +100,8 @@ export function CommandPalette({ modalProps }) {
|
|||
}, [queryEh]);
|
||||
|
||||
return (
|
||||
<ModalRoot className={cl("root")} {...modalProps} size={ModalSize.MEDIUM} onKeyDown={handleKeyDown} onWheel={handleWheel}>
|
||||
<div>
|
||||
<Modal {...modalProps} size="md" title="Command Palette">
|
||||
<div className={cl("root")} onKeyDown={handleKeyDown} onWheel={handleWheel}>
|
||||
<TextInput
|
||||
value={queryEh}
|
||||
onChange={e => setQuery(e)}
|
||||
|
|
@ -123,7 +123,7 @@ export function CommandPalette({ modalProps }) {
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import "./style.css";
|
|||
|
||||
import { ButtonAction } from "@equicordplugins/keyboardNavigation/commands";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeAllModals, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { closeAllModals, Modal,openModal, React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { settings } from "..";
|
||||
|
||||
interface MultipleChoiceProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
onSelect: (selectedValue: any) => void;
|
||||
choices: ButtonAction[];
|
||||
}
|
||||
|
|
@ -99,9 +99,8 @@ export function MultipleChoice({ modalProps, onSelect, choices }: MultipleChoice
|
|||
}, [queryEh]);
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<ModalRoot className={cl("root")} {...modalProps} size={ModalSize.MEDIUM} onKeyDown={handleKeyDown} onWheel={handleWheel}>
|
||||
<div>
|
||||
<Modal {...modalProps} size="md" title="Multiple Choice">
|
||||
<div className={cl("root")} onKeyDown={handleKeyDown} onWheel={handleWheel}>
|
||||
<TextInput
|
||||
value={queryEh}
|
||||
onChange={e => setQuery(e)}
|
||||
|
|
@ -123,7 +122,7 @@ export function MultipleChoice({ modalProps, onSelect, choices }: MultipleChoice
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
import "./style.css";
|
||||
|
||||
import { closeAllModals, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { closeAllModals, Modal,openModal, React, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
interface SimpleTextInputProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
onSelect: (inputValue: string) => void;
|
||||
placeholder?: string;
|
||||
info?: string;
|
||||
|
|
@ -35,16 +35,17 @@ export function SimpleTextInput({ modalProps, onSelect, placeholder, info }: Sim
|
|||
}, []);
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<ModalRoot className="vc-command-palette-simple-text" {...modalProps} size={ModalSize.DYNAMIC} onKeyDown={handleKeyDown}>
|
||||
<TextInput
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e as unknown as string)}
|
||||
style={{ width: "30vw", borderRadius: "5px" }}
|
||||
placeholder={placeholder ?? "Type and press Enter"}
|
||||
/>
|
||||
{info && <div className="vc-command-palette-textinfo">{info}</div>}
|
||||
</ModalRoot>
|
||||
<Modal {...modalProps} size="sm" title="Text Input">
|
||||
<div className="vc-command-palette-simple-text" onKeyDown={handleKeyDown}>
|
||||
<TextInput
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e as unknown as string)}
|
||||
style={{ width: "30vw", borderRadius: "5px" }}
|
||||
placeholder={placeholder ?? "Type and press Enter"}
|
||||
/>
|
||||
{info && <div className="vc-command-palette-textinfo">{info}</div>}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { SettingsRouter, useState } from "@webpack/common";
|
||||
import { closeAllModals,SettingsRouter, useState } from "@webpack/common";
|
||||
|
||||
import { registerAction } from "./commands";
|
||||
import { openCommandPalette } from "./components/CommandPalette";
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ import { QrCodeIcon } from "@components/Icons";
|
|||
import { wrapTab } from "@components/settings";
|
||||
import loginWithQR from "@equicordplugins/loginWithQR";
|
||||
import { images } from "@equicordplugins/loginWithQR/images";
|
||||
import {
|
||||
ModalProps,
|
||||
} from "@utils/modal";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import {
|
||||
RestAPI,
|
||||
|
|
@ -135,7 +132,7 @@ const handleProcessImage = (file: File, modalPropsRef: QrModalPropsRef) => {
|
|||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
function QrModal(props: ModalProps) {
|
||||
function QrModal() {
|
||||
const [state, setState] = useState(LoginStateType.Idle);
|
||||
const [preview, setPreview] = useState<Preview | null>(null);
|
||||
const error = useRef<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -8,21 +8,15 @@ import { BaseText } from "@components/BaseText";
|
|||
import { Button, TextButton } from "@components/Button";
|
||||
import { images } from "@equicordplugins/loginWithQR/images";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
ModalSize,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import {
|
||||
Modal,
|
||||
openModal,
|
||||
RestAPI,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "@webpack/common";
|
||||
useState } from "@webpack/common";
|
||||
|
||||
import { cl } from "..";
|
||||
|
||||
|
|
@ -41,7 +35,7 @@ function VerifyModal({
|
|||
}: {
|
||||
token: string | null;
|
||||
onAbort: () => void;
|
||||
} & ModalProps) {
|
||||
} & RenderModalProps) {
|
||||
const [state, setState] = useState(
|
||||
!token ? VerifyState.NotFound : VerifyState.Verifying
|
||||
);
|
||||
|
|
@ -112,8 +106,8 @@ function VerifyModal({
|
|||
}, [state]);
|
||||
|
||||
return (
|
||||
<ModalRoot size={ModalSize.DYNAMIC} {...props}>
|
||||
<ModalContent scrollbarType="none" className={cl("device-content")}>
|
||||
<Modal size="sm" {...props} title="Verify Login">
|
||||
<div className={cl("device-content")}>
|
||||
{state === VerifyState.LoggedIn ? (
|
||||
<>
|
||||
<img
|
||||
|
|
@ -191,7 +185,7 @@ function VerifyModal({
|
|||
variant="dangerPrimary"
|
||||
className={cl("device-confirm")}
|
||||
style={{
|
||||
["--duration" as any]: `${holdDuration}ms`,
|
||||
["--progress" as any]: `${holdDuration}ms`,
|
||||
}}
|
||||
onPointerDown={startInput}
|
||||
onPointerUp={endInput}
|
||||
|
|
@ -202,8 +196,8 @@ function VerifyModal({
|
|||
</Button>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ModalFooter className={cl("device-footer")}>
|
||||
</div>
|
||||
<div className={cl("device-footer")} style={{ marginTop: "20px", display: "flex", justifyContent: "flex-end", gap: "10px" }}>
|
||||
{state === VerifyState.LoggedIn ? (
|
||||
<Button onClick={props.onClose}>
|
||||
{getIntlMessage("QR_CODE_LOGIN_FINISH_BUTTON")}
|
||||
|
|
@ -218,8 +212,8 @@ function VerifyModal({
|
|||
: getIntlMessage("CANCEL")}
|
||||
</TextButton>
|
||||
)}
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,11 @@ import { BaseText } from "@components/BaseText";
|
|||
import { Button } from "@components/Button";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { InfoIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { copyWithToast, openUserProfile } from "@utils/discord";
|
||||
import { closeAllModals, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
import { type User } from "@vencord/discord-types";
|
||||
import { RenderModalProps, type User } from "@vencord/discord-types";
|
||||
import { find, findByCode, findByCodeLazy } from "@webpack";
|
||||
import { Alerts, ChannelStore, ContextMenuApi, FluxDispatcher, GuildStore, Menu, NavigationRouter, React, TabBar, TextInput, Tooltip, useMemo, useRef, useState } from "@webpack/common";
|
||||
import { Alerts, ChannelStore, closeAllModals, ContextMenuApi, FluxDispatcher, GuildStore, Menu, Modal, NavigationRouter, openModal, React, TabBar, TextInput, Tooltip, useMemo, useRef, useState } from "@webpack/common";
|
||||
|
||||
import { DBMessageRecord, deleteMessageIDB, deleteMessagesBulkIDB } from "../db";
|
||||
import { cl, clearLogs, settings } from "../index";
|
||||
|
|
@ -62,7 +60,7 @@ export enum LogTabs {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
initalQuery?: string;
|
||||
}
|
||||
|
||||
|
|
@ -76,46 +74,96 @@ export function LogsModal({ modalProps, initalQuery }: Props) {
|
|||
const { messages, total, statusTotal, pending, reset } = useMessages(queryEh, currentTab, sortNewest, numDisplayedMessages);
|
||||
|
||||
return (
|
||||
<ModalRoot className={cl("modal-root")} {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<TextInput value={queryEh} onChange={e => setQuery(e)} style={{ width: "100%" }} placeholder="Filter Messages" />
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className={cl("modal-tab-bar")}
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={e => {
|
||||
setCurrentTab(e);
|
||||
setNumDisplayedMessages(settings.store.messagesToDisplayAtOnceInLogs);
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title={
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "10px", width: "100%", paddingRight: "20px" }}>
|
||||
<TextInput value={queryEh} onChange={e => setQuery(e)} style={{ width: "100%" }} placeholder="Filter Messages" />
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className={cl("modal-tab-bar")}
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={e => {
|
||||
setCurrentTab(e);
|
||||
setNumDisplayedMessages(settings.store.messagesToDisplayAtOnceInLogs);
|
||||
contentRef.current?.firstElementChild?.scrollTo(0, 0);
|
||||
}}
|
||||
>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.DELETED}
|
||||
>
|
||||
Deleted
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.EDITED}
|
||||
>
|
||||
Edited
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.GHOST_PING}
|
||||
>
|
||||
Ghost Pinged
|
||||
</TabBar.Item>
|
||||
</TabBar>
|
||||
</div>
|
||||
}
|
||||
actions={[
|
||||
{
|
||||
text: "Clear All Logs",
|
||||
variant: "dangerPrimary",
|
||||
onClick: () => Alerts.show({
|
||||
title: "Clear Logs",
|
||||
body: "Are you sure you want to clear all the logs",
|
||||
confirmText: "Clear",
|
||||
// @ts-expect-error not typed
|
||||
confirmVariant: "critical-primary",
|
||||
cancelText: "Cancel",
|
||||
onConfirm: async () => {
|
||||
await clearLogs();
|
||||
reset();
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
text: "Clear Visible Logs",
|
||||
variant: "dangerSecondary",
|
||||
disabled: messages?.length === 0,
|
||||
onClick: () => Alerts.show({
|
||||
title: "Clear Logs",
|
||||
body: `Are you sure you want to clear ${messages.length} logs`,
|
||||
confirmText: "Clear",
|
||||
// @ts-expect-error not typed
|
||||
confirmVariant: "critical-primary",
|
||||
cancelText: "Cancel",
|
||||
onConfirm: async () => {
|
||||
await deleteMessagesBulkIDB(messages.map(e => e.message_id));
|
||||
reset();
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
text: `Sort ${sortNewest ? "Oldest First" : "Newest First"}`,
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
setSortNewest(e => {
|
||||
const val = !e;
|
||||
settings.store.sortNewest = val;
|
||||
return val;
|
||||
});
|
||||
contentRef.current?.firstElementChild?.scrollTo(0, 0);
|
||||
}}
|
||||
>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.DELETED}
|
||||
>
|
||||
Deleted
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.EDITED}
|
||||
>
|
||||
Edited
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className={cl("modal-tab-bar-item")}
|
||||
id={LogTabs.GHOST_PING}
|
||||
>
|
||||
Ghost Pinged
|
||||
</TabBar.Item>
|
||||
</TabBar>
|
||||
</ModalHeader>
|
||||
<div style={{ opacity: modalProps.transitionState === 1 ? "1" : "0" }} className={cl("modal-content-container")} ref={contentRef}>
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div style={{ opacity: modalProps.transitionState === 1 ? "1" : "0" }} className={`${cl("modal-content-container")} ${cl("modal-root")}`} ref={contentRef}>
|
||||
{
|
||||
modalProps.transitionState === 1 &&
|
||||
<ModalContent
|
||||
className={cl("modal-content")}
|
||||
>
|
||||
<div>
|
||||
{messages != null && total === 0 && (
|
||||
<EmptyLogs
|
||||
hasQuery={queryEh.length !== 0}
|
||||
|
|
@ -133,62 +181,10 @@ export function LogsModal({ modalProps, initalQuery }: Props) {
|
|||
handleLoadMore={() => setNumDisplayedMessages(e => e + settings.store.messagesToDisplayAtOnceInLogs)}
|
||||
/>
|
||||
)}
|
||||
</ModalContent>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<ModalFooter className={cl("footer")}>
|
||||
<Button
|
||||
variant="dangerPrimary"
|
||||
onClick={() => Alerts.show({
|
||||
title: "Clear Logs",
|
||||
body: "Are you sure you want to clear all the logs",
|
||||
confirmText: "Clear",
|
||||
// @ts-expect-error not typed
|
||||
confirmVariant: "critical-primary",
|
||||
cancelText: "Cancel",
|
||||
onConfirm: async () => {
|
||||
await clearLogs();
|
||||
reset();
|
||||
}
|
||||
|
||||
})}
|
||||
>
|
||||
Clear All Logs
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginRight: "16px" }}
|
||||
variant="dangerSecondary"
|
||||
disabled={messages?.length === 0}
|
||||
onClick={() => Alerts.show({
|
||||
title: "Clear Logs",
|
||||
body: `Are you sure you want to clear ${messages.length} logs`,
|
||||
confirmText: "Clear",
|
||||
// @ts-expect-error not typed
|
||||
confirmVariant: "critical-primary",
|
||||
cancelText: "Cancel",
|
||||
onConfirm: async () => {
|
||||
await deleteMessagesBulkIDB(messages.map(e => e.message_id));
|
||||
reset();
|
||||
}
|
||||
})}
|
||||
>
|
||||
Clear Visible Logs
|
||||
</Button>
|
||||
<Link
|
||||
style={{ marginRight: "1rem" }}
|
||||
onClick={() => {
|
||||
setSortNewest(e => {
|
||||
const val = !e;
|
||||
settings.store.sortNewest = val;
|
||||
return val;
|
||||
});
|
||||
contentRef.current?.firstElementChild?.scrollTo(0, 0);
|
||||
}}
|
||||
>
|
||||
Sort {sortNewest ? "Oldest First" : "Newest First"}
|
||||
</Link>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { HeadingPrimary } from "@components/Heading";
|
||||
import { PickerContent, PickerContentHeader, PickerContentRow, PickerContentRowGrid, PickerHeaderProps, SidebarProps, Sticker, StickerCategoryType, StickerPack } from "@equicordplugins/moreStickers/types";
|
||||
import { sendSticker } from "@equicordplugins/moreStickers/upload";
|
||||
import { clPicker, FFmpegStateContext } from "@equicordplugins/moreStickers/utils";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { React, TextInput } from "@webpack/common";
|
||||
import { Modal,openModal, React, TextInput } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { CategoryImage, CategoryScroller, CategoryWrapper, StickerCategory } from "./categories";
|
||||
|
|
@ -72,14 +70,9 @@ export const PickerSidebar = ({ packMetas, onPackSelect }: SidebarProps) => {
|
|||
onClick={() => {
|
||||
openModal(modalProps => {
|
||||
return (
|
||||
<ModalRoot size={ModalSize.LARGE} {...modalProps}>
|
||||
<ModalHeader>
|
||||
<HeadingPrimary>Stickers+</HeadingPrimary>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Packs />
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Modal size="lg" title="Stickers+" {...modalProps}>
|
||||
<Packs />
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import { TooltipContainer } from "@components/TooltipContainer";
|
|||
import { settings } from "@equicordplugins/musicControls/settings";
|
||||
import { SpotifyLrcStore } from "@equicordplugins/musicControls/spotify/lyrics/providers/store";
|
||||
import { SpotifyStore } from "@equicordplugins/musicControls/spotify/SpotifyStore";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { ContextMenuApi, React, useEffect, useState, useStateFromStores } from "@webpack/common";
|
||||
import { ContextMenuApi, openModal,React, useEffect, useState, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { LyricsContextMenu } from "./ctxMenu";
|
||||
import { LyricsModal } from "./modal";
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
import { BaseText } from "@components/BaseText";
|
||||
import { SpotifyStore, Track } from "@equicordplugins/musicControls/spotify/SpotifyStore";
|
||||
import { openImageModal } from "@utils/discord";
|
||||
import { ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { React } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,React } from "@webpack/common";
|
||||
|
||||
import { cl, NoteSvg, scrollClasses, useLyrics } from "./util";
|
||||
|
||||
|
|
@ -18,72 +18,63 @@ const formatTime = (time: number) => {
|
|||
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
function ModalHeaderContent({ track }: { track: Track | null; }) {
|
||||
function getTitleNode(track: Track | null) {
|
||||
if (!track) {
|
||||
return (
|
||||
<ModalHeader>
|
||||
<BaseText size="sm" weight="semibold">No track playing</BaseText>
|
||||
</ModalHeader>
|
||||
);
|
||||
return <BaseText size="sm" weight="semibold">No track playing</BaseText>;
|
||||
}
|
||||
return (
|
||||
<ModalHeader>
|
||||
<div className={cl("header-content")}>
|
||||
{track?.album?.image?.url && (
|
||||
<img
|
||||
src={track.album.image.url}
|
||||
alt={track.album.name}
|
||||
className={cl("album-image")}
|
||||
onClick={() => openImageModal({
|
||||
url: track.album.image.url,
|
||||
width: track.album.image.width,
|
||||
height: track.album.image.height,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold">{track.name}</BaseText>
|
||||
<BaseText size="sm">by {track.artists.map(a => a.name).join(", ")}</BaseText>
|
||||
<BaseText size="sm">on {track.album.name}</BaseText>
|
||||
</div>
|
||||
<div className={cl("header-content")}>
|
||||
{track?.album?.image?.url && (
|
||||
<img
|
||||
src={track.album.image.url}
|
||||
alt={track.album.name}
|
||||
className={cl("album-image")}
|
||||
onClick={() => openImageModal({
|
||||
url: track.album.image.url,
|
||||
width: track.album.image.width,
|
||||
height: track.album.image.height,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold">{track.name}</BaseText>
|
||||
<BaseText size="sm">by {track.artists.map(a => a.name).join(", ")}</BaseText>
|
||||
<BaseText size="sm">on {track.album.name}</BaseText>
|
||||
</div>
|
||||
</ModalHeader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const modalCurrentLine = cl("modal-line-current");
|
||||
const modalLine = cl("modal-line");
|
||||
|
||||
export function LyricsModal({ props }: { props: ModalProps; }) {
|
||||
export function LyricsModal({ props }: { props: RenderModalProps; }) {
|
||||
const { track, lyricsInfo, currLrcIndex } = useLyrics({ scroll: false });
|
||||
const currentLyrics = lyricsInfo?.lyricsVersions[lyricsInfo.useLyric];
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeaderContent track={track} />
|
||||
<ModalContent>
|
||||
<div className={`${cl("lyrics-modal-container")} ${scrollClasses.auto}`}>
|
||||
{currentLyrics ? (
|
||||
currentLyrics.map((line, i) => (
|
||||
<BaseText
|
||||
key={i}
|
||||
size={currLrcIndex === i ? "md" : "sm"}
|
||||
weight={currLrcIndex === i ? "semibold" : "normal"}
|
||||
className={currLrcIndex === i ? modalCurrentLine : modalLine}
|
||||
>
|
||||
<span className={cl("modal-timestamp")} onClick={() => SpotifyStore.seek(line.time * 1000)}>
|
||||
{formatTime(line.time)}
|
||||
</span>
|
||||
{line.text || NoteSvg()}
|
||||
</BaseText>
|
||||
))
|
||||
) : (
|
||||
<BaseText size="sm" className={cl("modal-no-lyrics")}>
|
||||
No lyrics available :(
|
||||
<Modal {...props} size="md" title={getTitleNode(track)}>
|
||||
<div className={`${cl("lyrics-modal-container")} ${scrollClasses.auto}`}>
|
||||
{currentLyrics ? (
|
||||
currentLyrics.map((line, i) => (
|
||||
<BaseText
|
||||
key={i}
|
||||
size={currLrcIndex === i ? "md" : "sm"}
|
||||
weight={currLrcIndex === i ? "semibold" : "normal"}
|
||||
className={currLrcIndex === i ? modalCurrentLine : modalLine}
|
||||
>
|
||||
<span className={cl("modal-timestamp")} onClick={() => SpotifyStore.seek(line.time * 1000)}>
|
||||
{formatTime(line.time)}
|
||||
</span>
|
||||
{line.text || NoteSvg()}
|
||||
</BaseText>
|
||||
)}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
))
|
||||
) : (
|
||||
<BaseText size="sm" className={cl("modal-no-lyrics")}>
|
||||
No lyrics available :(
|
||||
</BaseText>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import { TooltipContainer } from "@components/TooltipContainer";
|
|||
import { settings } from "@equicordplugins/musicControls/settings";
|
||||
import { TidalLrcStore } from "@equicordplugins/musicControls/tidal/lyrics/providers/store";
|
||||
import { TidalStore } from "@equicordplugins/musicControls/tidal/TidalStore";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { ContextMenuApi, useEffect, useState, useStateFromStores } from "@webpack/common";
|
||||
import { ContextMenuApi, openModal,useEffect, useState, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { LyricsContextMenu } from "./ctxMenu";
|
||||
import { LyricsModal } from "./modal";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { TidalStore, Track } from "@equicordplugins/musicControls/tidal/TidalStore";
|
||||
import { ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,React } from "@webpack/common";
|
||||
|
||||
import { cl, NoteSvg, scrollClasses, useLyrics } from "./util";
|
||||
|
||||
|
|
@ -16,58 +17,54 @@ const formatTime = (time: number) => {
|
|||
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
function ModalHeaderContent({ track }: { track: Track; }) {
|
||||
function getTitleNode(track: Track | null) {
|
||||
if (!track) return null;
|
||||
return (
|
||||
<ModalHeader>
|
||||
<div className={cl("header-content")}>
|
||||
{track?.imageSrc && (
|
||||
<img
|
||||
src={track.imageSrc}
|
||||
alt={track.album || track.name}
|
||||
className={cl("album-image")}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold">{track.name}</BaseText>
|
||||
<BaseText size="sm">by {track.artist}</BaseText>
|
||||
{track.album && <BaseText size="sm">on {track.album}</BaseText>}
|
||||
</div>
|
||||
<div className={cl("header-content")}>
|
||||
{track?.imageSrc && (
|
||||
<img
|
||||
src={track.imageSrc}
|
||||
alt={track.album || track.name}
|
||||
className={cl("album-image")}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold">{track.name}</BaseText>
|
||||
<BaseText size="sm">by {track.artist}</BaseText>
|
||||
{track.album && <BaseText size="sm">on {track.album}</BaseText>}
|
||||
</div>
|
||||
</ModalHeader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LyricsModal({ rootProps }: { rootProps: ModalProps; }) {
|
||||
export function LyricsModal({ rootProps }: { rootProps: RenderModalProps; }) {
|
||||
const { track, lyrics, currLrcIndex } = useLyrics({ scroll: false });
|
||||
const currentLyrics = lyrics || null;
|
||||
const position = track ? TidalStore.mPosition : 0;
|
||||
|
||||
return (
|
||||
<ModalRoot {...rootProps}>
|
||||
{track && <ModalHeaderContent track={track} />}
|
||||
<ModalContent>
|
||||
<div className={cl("lyrics-modal-container") + ` ${scrollClasses.auto}`}>
|
||||
{currentLyrics ? (
|
||||
currentLyrics.map((line, i) => (
|
||||
<BaseText
|
||||
key={i}
|
||||
size={currLrcIndex === i ? "md" : "sm"}
|
||||
weight={currLrcIndex === i ? "semibold" : "normal"}
|
||||
className={currLrcIndex === i ? cl("modal-line-current") : cl("modal-line")}
|
||||
<Modal {...rootProps} size="md" title={getTitleNode(track)}>
|
||||
<div className={cl("lyrics-modal-container") + ` ${scrollClasses.auto}`}>
|
||||
{currentLyrics ? (
|
||||
currentLyrics.map((line, i) => (
|
||||
<BaseText
|
||||
key={i}
|
||||
size={currLrcIndex === i ? "md" : "sm"}
|
||||
weight={currLrcIndex === i ? "semibold" : "normal"}
|
||||
className={currLrcIndex === i ? cl("modal-line-current") : cl("modal-line")}
|
||||
>
|
||||
<span className={cl("modal-timestamp")}
|
||||
onClick={() => TidalStore.seek(line.time * 1000)}
|
||||
>
|
||||
<span className={cl("modal-timestamp")}
|
||||
onClick={() => TidalStore.seek(line.time * 1000)}
|
||||
>
|
||||
{formatTime(line.time)}
|
||||
</span>
|
||||
{line.text || NoteSvg(cl("modal-note"))}
|
||||
</BaseText>
|
||||
))
|
||||
) : (
|
||||
<BaseText size="sm" className={cl("modal-no-lyrics")}>No lyrics available :(</BaseText>
|
||||
)}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
{formatTime(line.time)}
|
||||
</span>
|
||||
{line.text || NoteSvg(cl("modal-note"))}
|
||||
</BaseText>
|
||||
))
|
||||
) : (
|
||||
<BaseText size="sm" className={cl("modal-no-lyrics")}>No lyrics available :(</BaseText>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,14 @@ import { Settings, useSettings } from "@api/Settings";
|
|||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { PluginDependencyList } from "@components/settings/tabs/plugins";
|
||||
import { PluginCard } from "@components/settings/tabs/plugins/PluginCard";
|
||||
import { ChangeList } from "@utils/ChangeList";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { CloseButton, closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Tooltip, useMemo } from "@webpack/common";
|
||||
import { closeModal, Modal,openModal, Tooltip, useMemo } from "@webpack/common";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
|
@ -32,7 +31,7 @@ const Checkbox = findComponentByCodeLazy('"data-toggleable-component":"checkbox'
|
|||
let hasSeen = false;
|
||||
|
||||
interface ModalComponentProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
newPlugins: Set<string>;
|
||||
newSettings: KnownPluginSettingsMap;
|
||||
}
|
||||
|
|
@ -121,8 +120,10 @@ function NewPluginsModal({ modalProps, newPlugins, newSettings }: ModalComponent
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false} className={cl("header")}>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title={
|
||||
<div className={cl("header-content")}>
|
||||
<BaseText size="lg" weight="semibold" className={cl("title")}>
|
||||
New Plugins and Settings ({totalCount})
|
||||
|
|
@ -131,53 +132,46 @@ function NewPluginsModal({ modalProps, newPlugins, newSettings }: ModalComponent
|
|||
New plugins have been added since your last visit. Enable any you'd like or continue to dismiss.
|
||||
</BaseText>
|
||||
</div>
|
||||
<div className={cl("header-trailing")}>
|
||||
<CloseButton onClick={modalProps.onClose} />
|
||||
</div>
|
||||
</ModalHeader>
|
||||
}
|
||||
>
|
||||
<div className={cl("grid")}>
|
||||
{pluginCards}
|
||||
{requiredPluginCards}
|
||||
</div>
|
||||
|
||||
<ModalContent>
|
||||
<div className={cl("grid")}>
|
||||
{pluginCards}
|
||||
{requiredPluginCards}
|
||||
</div>
|
||||
</ModalContent>
|
||||
<div className={cl("footer")} style={{ marginTop: "20px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Tooltip
|
||||
text={
|
||||
<>
|
||||
The following plugins require a restart:
|
||||
<ul className={cl("restart-list")}>
|
||||
{changes.map(p => <li key={p}>{p}</li>)}
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
shouldShow={changes.hasChanges}
|
||||
>
|
||||
{tooltipProps => (
|
||||
<Button
|
||||
{...tooltipProps}
|
||||
onClick={handleContinue}
|
||||
>
|
||||
{changes.hasChanges ? "Restart" : "Continue"}
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
<ModalFooter>
|
||||
<Flex className={cl("footer")}>
|
||||
<Tooltip
|
||||
text={
|
||||
<>
|
||||
The following plugins require a restart:
|
||||
<ul className={cl("restart-list")}>
|
||||
{changes.map(p => <li key={p}>{p}</li>)}
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
shouldShow={changes.hasChanges}
|
||||
>
|
||||
{tooltipProps => (
|
||||
<Button
|
||||
{...tooltipProps}
|
||||
onClick={handleContinue}
|
||||
>
|
||||
{changes.hasChanges ? "Restart" : "Continue"}
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
<Checkbox
|
||||
type="inverted"
|
||||
value={!settings?.plugins?.NewPluginsManager?.enabled}
|
||||
onChange={() => {
|
||||
Settings.plugins.NewPluginsManager.enabled = !settings?.plugins?.NewPluginsManager?.enabled;
|
||||
}}
|
||||
>
|
||||
Don't show this again
|
||||
</Checkbox>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Checkbox
|
||||
type="inverted"
|
||||
value={!settings?.plugins?.NewPluginsManager?.enabled}
|
||||
onChange={() => {
|
||||
Settings.plugins.NewPluginsManager.enabled = !settings?.plugins?.NewPluginsManager?.enabled;
|
||||
}}
|
||||
>
|
||||
Don't show this again
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
interface ConfirmModalProps extends ModalProps {
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, React } from "@webpack/common";
|
||||
|
||||
interface ConfirmModalProps extends RenderModalProps {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmText: string;
|
||||
|
|
@ -17,7 +17,7 @@ interface ConfirmModalProps extends ModalProps {
|
|||
onCancel: () => void;
|
||||
}
|
||||
|
||||
interface ImportProfilesModalProps extends ModalProps {
|
||||
interface ImportProfilesModalProps extends RenderModalProps {
|
||||
title: string;
|
||||
message: string;
|
||||
onOverride: () => void;
|
||||
|
|
@ -25,57 +25,65 @@ interface ImportProfilesModalProps extends ModalProps {
|
|||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export function ConfirmModal({ title, message, confirmText, cancelText, onConfirm, onCancel, onClose, transitionState }: ConfirmModalProps) {
|
||||
export function ConfirmModal({ title, message, confirmText, cancelText, onConfirm, onCancel, ...props }: ConfirmModalProps) {
|
||||
const closeAfter = (action: () => void) => () => {
|
||||
action();
|
||||
onClose();
|
||||
props.onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot transitionState={transitionState}>
|
||||
<ModalHeader>
|
||||
<Heading tag="h2">{title}</Heading>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph>{message}</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button onClick={closeAfter(onConfirm)}>
|
||||
{confirmText}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={closeAfter(onCancel)}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title={title}
|
||||
actions={[
|
||||
{
|
||||
text: confirmText,
|
||||
variant: "primary",
|
||||
onClick: closeAfter(onConfirm)
|
||||
},
|
||||
{
|
||||
text: cancelText,
|
||||
variant: "secondary",
|
||||
onClick: closeAfter(onCancel)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph>{message}</Paragraph>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export function ImportProfilesModal({ title, message, onOverride, onMerge, onCancel, onClose, transitionState }: ImportProfilesModalProps) {
|
||||
export function ImportProfilesModal({ title, message, onOverride, onMerge, onCancel, ...props }: ImportProfilesModalProps) {
|
||||
const closeAfter = (action: () => void) => () => {
|
||||
action();
|
||||
onClose();
|
||||
props.onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot transitionState={transitionState}>
|
||||
<ModalHeader>
|
||||
<Heading tag="h2">{title}</Heading>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph>{message}</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button onClick={closeAfter(onOverride)}>
|
||||
Override
|
||||
</Button>
|
||||
<Button onClick={closeAfter(onMerge)}>
|
||||
Merge
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={closeAfter(onCancel)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...props}
|
||||
size="sm"
|
||||
title={title}
|
||||
actions={[
|
||||
{
|
||||
text: "Override",
|
||||
variant: "primary",
|
||||
onClick: closeAfter(onOverride)
|
||||
},
|
||||
{
|
||||
text: "Merge",
|
||||
variant: "primary",
|
||||
onClick: closeAfter(onMerge)
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: closeAfter(onCancel)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph>{message}</Paragraph>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
import { Button } from "@components/Button";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { classes } from "@utils/misc";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { React, SelectedGuildStore, TextInput, useStateFromStores } from "@webpack/common";
|
||||
import { openModal,React, SelectedGuildStore, TextInput, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { cl, settings } from "../index";
|
||||
import { exportPresets, ImportDecision, importPresets, savePreset } from "../utils/actions";
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
import PluginModal from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { openModal } from "@utils/modal";
|
||||
import type { Plugin } from "@utils/types";
|
||||
import { openModal } from "@webpack/common";
|
||||
|
||||
import { promptToRestartIfDirty } from "../settings/restartTracking";
|
||||
import { setSettingsModalOpen } from "../state";
|
||||
|
|
|
|||
|
|
@ -5,14 +5,12 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { Button, IconUtils, Menu, TextInput, UploadHandler, useEffect, useState } from "@webpack/common";
|
||||
import { Message, RenderModalProps } from "@vencord/discord-types";
|
||||
import { IconUtils, Menu, Modal, openModal, TextInput, UploadHandler, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { QuoteIcon } from "./components/QuoteIcon";
|
||||
import { QuoteFont } from "./types";
|
||||
|
|
@ -89,7 +87,7 @@ export default definePlugin({
|
|||
}
|
||||
});
|
||||
|
||||
function QuoteModal({ message, ...props }: ModalProps & { message: Message; }) {
|
||||
function QuoteModal({ message, ...props }: RenderModalProps & { message: Message; }) {
|
||||
const [gray, setGray] = useState(settings.store.grayscale);
|
||||
const [showWatermark, setShowWatermark] = useState(settings.store.showWatermark);
|
||||
const [saveAsGif, setSaveAsGif] = useState(settings.store.saveAsGif);
|
||||
|
|
@ -166,49 +164,48 @@ function QuoteModal({ message, ...props }: ModalProps & { message: Message; }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...props} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false}>
|
||||
<BaseText color="text-strong" size="lg" weight="semibold" tag="h1" style={{ flexGrow: 1 }}>
|
||||
Catch Them In 4K.
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={props.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent scrollbarType="none">
|
||||
<img alt="Quote preview" src="" id="quoterPreview" style={{ borderRadius: "20px", width: "100%", marginBottom: "20px" }} />
|
||||
<Modal
|
||||
{...props}
|
||||
size="md"
|
||||
title="Catch Them In 4K."
|
||||
actions={[
|
||||
{
|
||||
text: "Export",
|
||||
variant: "primary",
|
||||
onClick: handleExport
|
||||
},
|
||||
{
|
||||
text: "Send",
|
||||
variant: "primary",
|
||||
onClick: handleSendInChat
|
||||
}
|
||||
]}
|
||||
>
|
||||
<img alt="Quote preview" src="" id="quoterPreview" style={{ borderRadius: "20px", width: "100%", marginBottom: "20px" }} />
|
||||
|
||||
<FormSwitch title="Grayscale" value={gray} onChange={setGray} />
|
||||
<FormSwitch
|
||||
title="Save as GIF"
|
||||
value={saveAsGif}
|
||||
onChange={setSaveAsGif}
|
||||
description="Saves/Sends the image as a GIF instead of a PNG"
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Show Watermark"
|
||||
value={showWatermark}
|
||||
onChange={setShowWatermark}
|
||||
hideBorder={showWatermark}
|
||||
/>
|
||||
{showWatermark && (
|
||||
<div style={{ marginTop: "8px", marginBottom: "20px" }}>
|
||||
<TextInput
|
||||
value={watermarkText}
|
||||
onChange={setWatermarkText}
|
||||
placeholder="Watermark text (max 32 characters)"
|
||||
maxLength={32}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ display: "flex", gap: "8px", marginTop: "16px", marginBottom: "16px" }}>
|
||||
<Button color={Button.Colors.BRAND} size={Button.Sizes.MEDIUM} onClick={handleExport}>
|
||||
Export
|
||||
</Button>
|
||||
<Button color={Button.Colors.BRAND} size={Button.Sizes.MEDIUM} onClick={handleSendInChat}>
|
||||
Send
|
||||
</Button>
|
||||
<FormSwitch title="Grayscale" value={gray} onChange={setGray} />
|
||||
<FormSwitch
|
||||
title="Save as GIF"
|
||||
value={saveAsGif}
|
||||
onChange={setSaveAsGif}
|
||||
description="Saves/Sends the image as a GIF instead of a PNG"
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Show Watermark"
|
||||
value={showWatermark}
|
||||
onChange={setShowWatermark}
|
||||
hideBorder={showWatermark}
|
||||
/>
|
||||
{showWatermark && (
|
||||
<div style={{ marginTop: "8px", marginBottom: "20px" }}>
|
||||
<TextInput
|
||||
value={watermarkText}
|
||||
onChange={setWatermarkText}
|
||||
placeholder="Watermark text (max 32 characters)"
|
||||
maxLength={32}
|
||||
/>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ import * as DataStore from "@api/DataStore";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs, IS_MAC } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { Button, ChannelRouter, ChannelStore, IconUtils, React, RelationshipStore, SelectedChannelStore, Toasts, UserStore } from "@webpack/common";
|
||||
import { Button, ChannelRouter, ChannelStore, closeModal, IconUtils, openModal,React, RelationshipStore, SelectedChannelStore, Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
const STORAGE_KEY = "RDMSwitch_history";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { Button } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, React } from "@webpack/common";
|
||||
|
||||
import { sendRemix } from ".";
|
||||
import { brushCanvas, canvas, cropCanvas, ctx, exportImg, shapeCanvas } from "./editor/components/Canvas";
|
||||
|
|
@ -15,7 +14,7 @@ import { resetBounds } from "./editor/tools/crop";
|
|||
import { SendIcon } from "./icons/SendIcon";
|
||||
|
||||
type Props = {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
close: () => void;
|
||||
url?: string;
|
||||
};
|
||||
|
|
@ -38,18 +37,28 @@ async function closeModal(closeFunc: () => void, save?: boolean) {
|
|||
|
||||
export default function RemixModal({ modalProps, close, url }: Props) {
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>Remix</BaseText>
|
||||
<ModalCloseButton onClick={() => closeModal(close)} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Editor url={url} />
|
||||
</ModalContent>
|
||||
<ModalFooter className="vc-remix-modal-footer">
|
||||
<Button onClick={() => closeModal(close, true)} className="vc-remix-send"><SendIcon /> Send</Button>
|
||||
<Button onClick={() => closeModal(close)} color={Button.Colors.RED}>Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title="Remix"
|
||||
actions={[
|
||||
{
|
||||
text: (
|
||||
<span style={{ display: "flex", alignItems: "center", gap: "6px" }}>
|
||||
<SendIcon /> Send
|
||||
</span>
|
||||
) as any,
|
||||
variant: "primary",
|
||||
onClick: () => closeModal(close, true)
|
||||
},
|
||||
{
|
||||
text: "Close",
|
||||
variant: "dangerPrimary",
|
||||
onClick: () => closeModal(close)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Editor url={url} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co
|
|||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import { PaintbrushIcon } from "@components/Icons";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { extractAndLoadChunksLazy } from "@webpack";
|
||||
import { ChannelStore, DraftType, FluxDispatcher, Menu, PendingReplyStore, SelectedChannelStore, UploadHandler } from "@webpack/common";
|
||||
import { ChannelStore, closeModal, DraftType, FluxDispatcher, Menu, openModal,PendingReplyStore, SelectedChannelStore, UploadHandler } from "@webpack/common";
|
||||
|
||||
import RemixModal from "./RemixModal";
|
||||
import css from "./styles.css?managed";
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { Button } from "@components/Button";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, DraftType, showToast, TextInput, Toasts, UploadManager, useState } from "@webpack/common";
|
||||
import { ChannelStore, closeModal, DraftType, Modal, openModal, showToast, TextInput, Toasts, UploadManager, useState } from "@webpack/common";
|
||||
|
||||
import { ScheduledAttachment } from "../types";
|
||||
import { addScheduledMessage, getChannelDisplayInfo } from "../utils";
|
||||
|
|
@ -23,7 +23,7 @@ function ScheduleTimeModalInner({ channelId, content, attachments, rootProps, cl
|
|||
channelId: string;
|
||||
content: string;
|
||||
attachments?: ScheduledAttachment[];
|
||||
rootProps: ModalProps;
|
||||
rootProps: RenderModalProps;
|
||||
close: () => void;
|
||||
}) {
|
||||
const [scheduleType, setScheduleType] = useState<"delay" | "time">("delay");
|
||||
|
|
@ -69,74 +69,78 @@ function ScheduleTimeModalInner({ channelId, content, attachments, rootProps, cl
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...rootProps}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<Heading tag="h2" className={cl("modal-title")}>Schedule Message</Heading>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
<Modal
|
||||
{...rootProps}
|
||||
size="sm"
|
||||
title="Schedule Message"
|
||||
actions={[
|
||||
{
|
||||
text: "Schedule",
|
||||
variant: "primary",
|
||||
onClick: handleSchedule
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: close
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div className={cl("channel-info")}>
|
||||
{avatar && <img src={avatar} className={cl("channel-avatar")} alt="" />}
|
||||
<span className={cl("channel-text")}>
|
||||
Scheduling for: <strong>{isDM ? name : `#${name}`}</strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<div className={cl("channel-info")}>
|
||||
{avatar && <img src={avatar} className={cl("channel-avatar")} alt="" />}
|
||||
<span className={cl("channel-text")}>
|
||||
Scheduling for: <strong>{isDM ? name : `#${name}`}</strong>
|
||||
</span>
|
||||
<Heading tag="h5" className={cl("field-label")}>Schedule Type</Heading>
|
||||
<div className={cl("schedule-type-buttons")}>
|
||||
<Button
|
||||
size="small"
|
||||
variant={scheduleType === "delay" ? "primary" : "secondary"}
|
||||
onClick={() => setScheduleType("delay")}
|
||||
>
|
||||
Delay
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
variant={scheduleType === "time" ? "primary" : "secondary"}
|
||||
onClick={() => setScheduleType("time")}
|
||||
>
|
||||
Specific Time
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{scheduleType === "delay" ? (
|
||||
<>
|
||||
<Heading tag="h5" className={cl("field-label")}>Delay (minutes)</Heading>
|
||||
<TextInput
|
||||
value={delayMinutes}
|
||||
onChange={setDelayMinutes}
|
||||
placeholder="5"
|
||||
type="number"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Heading tag="h5" className={cl("field-label")}>Date & Time</Heading>
|
||||
<input
|
||||
type="datetime-local"
|
||||
className={cl("datetime-input")}
|
||||
value={scheduledDateTime}
|
||||
onChange={e => setScheduledDateTime(e.target.value)}
|
||||
min={new Date().toISOString().slice(0, 16)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className={cl("error")}>
|
||||
<ErrorIcon />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
|
||||
<Heading tag="h5" className={cl("field-label")}>Schedule Type</Heading>
|
||||
<div className={cl("schedule-type-buttons")}>
|
||||
<Button
|
||||
size="small"
|
||||
variant={scheduleType === "delay" ? "primary" : "secondary"}
|
||||
onClick={() => setScheduleType("delay")}
|
||||
>
|
||||
Delay
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
variant={scheduleType === "time" ? "primary" : "secondary"}
|
||||
onClick={() => setScheduleType("time")}
|
||||
>
|
||||
Specific Time
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{scheduleType === "delay" ? (
|
||||
<>
|
||||
<Heading tag="h5" className={cl("field-label")}>Delay (minutes)</Heading>
|
||||
<TextInput
|
||||
value={delayMinutes}
|
||||
onChange={setDelayMinutes}
|
||||
placeholder="5"
|
||||
type="number"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Heading tag="h5" className={cl("field-label")}>Date & Time</Heading>
|
||||
<input
|
||||
type="datetime-local"
|
||||
className={cl("datetime-input")}
|
||||
value={scheduledDateTime}
|
||||
onChange={e => setScheduledDateTime(e.target.value)}
|
||||
min={new Date().toISOString().slice(0, 16)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className={cl("error")}>
|
||||
<ErrorIcon />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button onClick={handleSchedule} variant="positive">Schedule</Button>
|
||||
<Button variant="secondary" onClick={close}>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
import { Button } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { ChannelStore, showToast, Toasts, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { ChannelStore, closeModal, Modal, openModal, showToast, Toasts, useState } from "@webpack/common";
|
||||
|
||||
import { clearAllScheduledMessages, getChannelDisplayInfo, getScheduledMessages, removeScheduledMessage } from "../utils";
|
||||
import { CalendarIcon, TimerIcon } from "./Icons";
|
||||
|
|
@ -17,7 +16,7 @@ import { CalendarIcon, TimerIcon } from "./Icons";
|
|||
const cl = classNameFactory("vc-scheduled-msg-");
|
||||
|
||||
interface ViewScheduledModalProps {
|
||||
rootProps: ModalProps;
|
||||
rootProps: RenderModalProps;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -36,69 +35,74 @@ function ViewScheduledModalInner({ rootProps, close }: ViewScheduledModalProps)
|
|||
showToast("All scheduled messages cleared", Toasts.Type.SUCCESS);
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
text: "Close",
|
||||
variant: "secondary",
|
||||
onClick: close
|
||||
}
|
||||
];
|
||||
|
||||
if (messages.length > 0) {
|
||||
actions.unshift({
|
||||
text: "Clear All",
|
||||
variant: "dangerPrimary",
|
||||
onClick: handleClearAll
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalRoot {...rootProps}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<Heading tag="h2" className={cl("modal-title")}>Scheduled Messages</Heading>
|
||||
{messages.length > 0 && (
|
||||
<Button size="small" variant="dangerPrimary" onClick={handleClearAll}>
|
||||
Clear All
|
||||
</Button>
|
||||
)}
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
<Modal
|
||||
{...rootProps}
|
||||
size="md"
|
||||
title="Scheduled Messages"
|
||||
actions={actions}
|
||||
>
|
||||
{!messages.length ? (
|
||||
<div className={cl("empty-state")}>
|
||||
<CalendarIcon width={48} height={48} />
|
||||
<span>No scheduled messages</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className={cl("message-list")}>
|
||||
{messages.map(msg => {
|
||||
const { name, avatar } = getChannelDisplayInfo(msg.channelId);
|
||||
const channel = ChannelStore.getChannel(msg.channelId);
|
||||
if (!channel) return null;
|
||||
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
{!messages.length ? (
|
||||
<div className={cl("empty-state")}>
|
||||
<CalendarIcon width={48} height={48} />
|
||||
<span>No scheduled messages</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className={cl("message-list")}>
|
||||
{messages.map(msg => {
|
||||
const { name, avatar } = getChannelDisplayInfo(msg.channelId);
|
||||
const channel = ChannelStore.getChannel(msg.channelId);
|
||||
if (!channel) return null;
|
||||
const isDM = channel.isPrivate();
|
||||
const displayContent = msg.content.length > 200
|
||||
? msg.content.slice(0, 200) + "..."
|
||||
: msg.content;
|
||||
|
||||
const isDM = channel.isPrivate();
|
||||
const displayContent = msg.content.length > 200
|
||||
? msg.content.slice(0, 200) + "..."
|
||||
: msg.content;
|
||||
|
||||
return (
|
||||
<div key={msg.id} className={cl("message-item")}>
|
||||
<div className={cl("message-info")}>
|
||||
<div className={cl("message-header")}>
|
||||
{avatar && <img src={avatar} className={cl("message-avatar")} alt="" />}
|
||||
<span className={cl("message-channel")}>
|
||||
{isDM ? name : `#${name}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className={cl("message-time")}>
|
||||
<TimerIcon width={14} height={14} />
|
||||
<span>{new Date(msg.scheduledTime).toLocaleString()}</span>
|
||||
</div>
|
||||
<div className={cl("message-content")}>{displayContent}</div>
|
||||
return (
|
||||
<div key={msg.id} className={cl("message-item")}>
|
||||
<div className={cl("message-info")}>
|
||||
<div className={cl("message-header")}>
|
||||
{avatar && <img src={avatar} className={cl("message-avatar")} alt="" />}
|
||||
<span className={cl("message-channel")}>
|
||||
{isDM ? name : `#${name}`}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
variant="dangerPrimary"
|
||||
onClick={() => handleDelete(msg.id)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<div className={cl("message-time")}>
|
||||
<TimerIcon width={14} height={14} />
|
||||
<span>{new Date(msg.scheduledTime).toLocaleString()}</span>
|
||||
</div>
|
||||
<div className={cl("message-content")}>{displayContent}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Button onClick={close} variant="secondary">Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Button
|
||||
size="small"
|
||||
variant="dangerPrimary"
|
||||
onClick={() => handleDelete(msg.id)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { characters } from "@equicordplugins/sekaiStickers/characters.json";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { React, ScrollerThin, TextInput } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,React, ScrollerThin, TextInput } from "@webpack/common";
|
||||
|
||||
export default function CharSelectModal({ modalProps, setCharacter }: { modalProps: ModalProps; setCharacter?: any; }) {
|
||||
export default function CharSelectModal({ modalProps, setCharacter }: { modalProps: RenderModalProps; setCharacter?: any; }) {
|
||||
const [search, setSearch] = React.useState<string>("");
|
||||
|
||||
const memoedSearchChar = React.useMemo(() => {
|
||||
|
|
@ -30,22 +29,15 @@ export default function CharSelectModal({ modalProps, setCharacter }: { modalPro
|
|||
});
|
||||
}, [search, characters]);
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.DYNAMIC}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="bold" style={{ flexGrow: 1 }}>Select character menu</BaseText>
|
||||
<ModalCloseButton onClick={modalProps.onClose} ></ModalCloseButton>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Flex flexDirection="column" style={{ paddingTop: 12 }}>
|
||||
<TextInput content="mafuyu" placeholder="Mafuyu" onChange={(e: string) => setSearch(e)} />
|
||||
<ScrollerThin style={{ height: 520 }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 330px)", rowGap: 6, columnGap: 5, gridTemplateRows: "repeat(3, 256px)" }}>
|
||||
{memoedSearchChar}
|
||||
</div>
|
||||
</ScrollerThin>
|
||||
</Flex>
|
||||
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Modal {...modalProps} size="lg" title="Select character menu">
|
||||
<Flex flexDirection="column" style={{ paddingTop: 12 }}>
|
||||
<TextInput content="mafuyu" placeholder="Mafuyu" onChange={(e: string) => setSearch(e)} />
|
||||
<ScrollerThin style={{ height: 520 }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 330px)", rowGap: 6, columnGap: 5, gridTemplateRows: "repeat(3, 256px)" }}>
|
||||
{memoedSearchChar}
|
||||
</div>
|
||||
</ScrollerThin>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,18 +4,17 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { characters } from "@equicordplugins/sekaiStickers/characters.json";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Button, ChannelStore, React, SelectedChannelStore, Slider, TextArea, UploadHandler } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { ChannelStore, Modal, openModal, React, SelectedChannelStore, Slider, TextArea, UploadHandler } from "@webpack/common";
|
||||
|
||||
import Canvas from "./Canvas";
|
||||
import CharSelectModal from "./Picker";
|
||||
|
||||
export default function SekaiStickersModal({ modalProps, settings }: { modalProps: ModalProps; settings: any; }) {
|
||||
export default function SekaiStickersModal({ modalProps, settings }: { modalProps: RenderModalProps; settings: any; }) {
|
||||
const [text, setText] = React.useState<string>("奏でーかわいい");
|
||||
const [character, setChracter] = React.useState<number>(49);
|
||||
const [fontSize, setFontSize] = React.useState<number>(characters[character].defaultText.s);
|
||||
|
|
@ -97,45 +96,51 @@ export default function SekaiStickersModal({ modalProps, settings }: { modalProp
|
|||
}
|
||||
};
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.DYNAMIC}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="bold" style={{ flexGrow: 1 }}>Sekai Stickers</BaseText>
|
||||
<ModalCloseButton onClick={modalProps.onClose} ></ModalCloseButton>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Flex flexDirection="row" style={{ paddingTop: 12 }}>
|
||||
<div style={{ marginRight: 30 }}>
|
||||
<Canvas draw={draw} id="SekaiCard_Canvas" />
|
||||
<Heading>Text Y Pos</Heading>
|
||||
<Slider minValue={0} maxValue={256} asValueChanges={va => { va = Math.round(va); setPosition({ x: position.x, y: curve ? 256 + fontSize * 3 - va : 256 - va }); }} initialValue={curve ? 256 - position.y + fontSize * 3 : 256 - position.y} orientation={"vertical"} onValueRender={va => String(Math.round(va))} />
|
||||
<Heading>Text XZ Pos</Heading>
|
||||
<Slider minValue={0} maxValue={296} asValueChanges={va => { va = Math.round(va); setPosition({ y: position.y, x: va }); }} initialValue={position.x} orientation={"horizontal"} onValueRender={(v: number) => String(Math.round(v))} />
|
||||
</div>
|
||||
<div style={{ marginRight: 10, width: "30vw" }}>
|
||||
<Heading>Text</Heading>
|
||||
<TextArea onChange={setText} placeholder={text} rows={4} />
|
||||
<Heading>Rotation</Heading>
|
||||
<Slider markers={[-10, -5, 0, 5, 10]} stickToMarkers={false} minValue={-10} maxValue={10} asValueChanges={val => setRotate(val)} initialValue={rotate} keyboardStep={0.2} orientation={"horizontal"} onValueRender={(v: number) => String(v.toFixed(2))} />
|
||||
<Heading>Font Size</Heading>
|
||||
<Slider minValue={10} asValueChanges={val => setFontSize(Math.round(val))} maxValue={100} initialValue={fontSize} keyboardStep={1} orientation={"horizontal"} onValueRender={(v: number) => String(Math.round(v))} />
|
||||
<Heading>Spacing</Heading>
|
||||
<Slider markers={[18, 36, 72, 100]} stickToMarkers={false} minValue={18} maxValue={100} initialValue={spaceSize} asValueChanges={e => setSpaceSize(e)} onValueRender={e => String(Math.round(e))} />
|
||||
<FormSwitch title="Enable curve" value={curve} onChange={val => setCurve(val)} />
|
||||
<Button onClick={() => openModal(props => <CharSelectModal modalProps={props} setCharacter={setChracter} />)}>Switch Character</Button>
|
||||
</div>
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex flexDirection="row" style={{ gap: 12 }}>
|
||||
<Button onClick={() => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title="Sekai Stickers"
|
||||
actions={[
|
||||
{
|
||||
text: "Switch Character",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
openModal(props => <CharSelectModal modalProps={props} setCharacter={setChracter} />);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Upload as Attachment",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
if (settings.store.AutoCloseModal) modalProps.onClose();
|
||||
canvast.toBlob(blob => {
|
||||
const file = new File([blob as Blob], `${characters[character].character}-sekai_cards.png`, { type: "image/png" });
|
||||
UploadHandler.promptToUpload([file], ChannelStore.getChannel(SelectedChannelStore.getChannelId()), 0);
|
||||
});
|
||||
}}>Upload as Attachment</Button>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Flex flexDirection="row" style={{ paddingTop: 12 }}>
|
||||
<div style={{ marginRight: 30 }}>
|
||||
<Canvas draw={draw} id="SekaiCard_Canvas" />
|
||||
<Heading>Text Y Pos</Heading>
|
||||
<Slider minValue={0} maxValue={256} asValueChanges={va => { va = Math.round(va); setPosition({ x: position.x, y: curve ? 256 + fontSize * 3 - va : 256 - va }); }} initialValue={curve ? 256 - position.y + fontSize * 3 : 256 - position.y} orientation={"vertical"} onValueRender={va => String(Math.round(va))} />
|
||||
<Heading>Text XZ Pos</Heading>
|
||||
<Slider minValue={0} maxValue={296} asValueChanges={va => { va = Math.round(va); setPosition({ y: position.y, x: va }); }} initialValue={position.x} orientation={"horizontal"} onValueRender={(v: number) => String(Math.round(v))} />
|
||||
</div>
|
||||
<div style={{ marginRight: 10, width: "30vw" }}>
|
||||
<Heading>Text</Heading>
|
||||
<TextArea onChange={setText} placeholder={text} rows={4} />
|
||||
<Heading>Rotation</Heading>
|
||||
<Slider markers={[-10, -5, 0, 5, 10]} stickToMarkers={false} minValue={-10} maxValue={10} asValueChanges={val => setRotate(val)} initialValue={rotate} keyboardStep={0.2} orientation={"horizontal"} onValueRender={(v: number) => String(v.toFixed(2))} />
|
||||
<Heading>Font Size</Heading>
|
||||
<Slider minValue={10} asValueChanges={val => setFontSize(Math.round(val))} maxValue={100} initialValue={fontSize} keyboardStep={1} orientation={"horizontal"} onValueRender={(v: number) => String(Math.round(v))} />
|
||||
<Heading>Spacing</Heading>
|
||||
<Slider markers={[18, 36, 72, 100]} stickToMarkers={false} minValue={18} maxValue={100} initialValue={spaceSize} asValueChanges={e => setSpaceSize(e)} onValueRender={e => String(Math.round(e))} />
|
||||
<FormSwitch title="Enable curve" value={curve} onChange={val => setCurve(val)} />
|
||||
</div>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { openModal } from "@webpack/common";
|
||||
|
||||
import SekaiStickersModal from "./Components/SekaiStickersModal";
|
||||
import { kanadeSvg } from "./kanade.svg";
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { AudioPlayerInterface, createAudioPlayer } from "@api/AudioPlayer";
|
|||
import { HeaderBarButton } from "@api/HeaderBar";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal,openModal, React } from "@webpack/common";
|
||||
let boopSound: AudioPlayerInterface;
|
||||
let song: AudioPlayerInterface;
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ function assignBoop(url: string, volume: number) {
|
|||
boopSound.load();
|
||||
}
|
||||
|
||||
function SoggyModal(props: ModalProps) {
|
||||
function SoggyModal(props: RenderModalProps) {
|
||||
if (settings.store.songVolume !== 0) {
|
||||
React.useEffect(() => {
|
||||
song?.loop();
|
||||
|
|
@ -54,14 +54,13 @@ function SoggyModal(props: ModalProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<Modal {...props} size="dynamic" title="Soggy Cat">
|
||||
<img
|
||||
src={settings.store.imageLink}
|
||||
onClick={boop}
|
||||
style={{ display: "block" }}
|
||||
|
||||
/>
|
||||
</ModalRoot >
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { openModal } from "@utils/modal";
|
||||
import { ApplicationIntegrationType } from "@vencord/discord-types/enums";
|
||||
import { OAuth2AuthorizeModal, showToast, Toasts } from "@webpack/common";
|
||||
import { OAuth2AuthorizeModal, openModal,showToast, Toasts } from "@webpack/common";
|
||||
|
||||
import { apiConstants, authFetch, getData } from "./api";
|
||||
import { useAuthorizationStore } from "./stores/AuthorizationStore";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { PlusIcon } from "@components/Icons";
|
||||
|
|
@ -13,11 +12,11 @@ import { cl, logger } from "@equicordplugins/songSpotlight.desktop/lib/utils";
|
|||
import { Native } from "@equicordplugins/songSpotlight.desktop/service";
|
||||
import { parsers } from "@song-spotlight/api/handlers";
|
||||
import { Song } from "@song-spotlight/api/structs";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { Clickable, TextInput, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Clickable, closeModal, Modal, openModal, TextInput, useState } from "@webpack/common";
|
||||
|
||||
interface AddSongModalProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
close(): void;
|
||||
onAdd(song: Song): string | undefined;
|
||||
}
|
||||
|
|
@ -29,64 +28,60 @@ function AddSongModal({ modalProps, close, onAdd }: AddSongModalProps) {
|
|||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<BaseText size="xl" weight="bold">Add a new song</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Flex flexDirection="column" gap="10px">
|
||||
<BaseText size="md" weight="normal" className={cl("sub")}>
|
||||
Song Spotlight supports these services: <b>{parsers.map(x => x.label).join(", ")}</b>
|
||||
</BaseText>
|
||||
<TextInput
|
||||
placeholder="https://open.spotify.com/..."
|
||||
error={error}
|
||||
onChange={value => {
|
||||
setURL(value);
|
||||
try {
|
||||
if (value) new URL(value);
|
||||
setError(undefined);
|
||||
} catch {
|
||||
setError("Invalid URL");
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title="Add a new song"
|
||||
actions={[
|
||||
{
|
||||
text: "Add song",
|
||||
variant: "primary",
|
||||
disabled: !url || !!error || pending,
|
||||
onClick: async () => {
|
||||
setPending(true);
|
||||
try {
|
||||
const parsed = await Native.parseLink(url);
|
||||
if (!parsed) {
|
||||
setError("Invalid link");
|
||||
return setPending(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex justifyContent="flex-end" gap={0}>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={async () => {
|
||||
setPending(true);
|
||||
try {
|
||||
const parsed = await Native.parseLink(url);
|
||||
if (!parsed) {
|
||||
setError("Invalid link");
|
||||
return setPending(false);
|
||||
}
|
||||
|
||||
const result = onAdd(parsed);
|
||||
if (result) {
|
||||
setError(result);
|
||||
return setPending(false);
|
||||
}
|
||||
|
||||
close();
|
||||
} catch (error) {
|
||||
logger.error("parseLink error", error);
|
||||
|
||||
setError("Failed to parse link");
|
||||
setPending(false);
|
||||
const result = onAdd(parsed);
|
||||
if (result) {
|
||||
setError(result);
|
||||
return setPending(false);
|
||||
}
|
||||
}}
|
||||
disabled={!url || !!error || pending}
|
||||
>
|
||||
Add song
|
||||
</Button>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
|
||||
close();
|
||||
} catch (error) {
|
||||
logger.error("parseLink error", error);
|
||||
|
||||
setError("Failed to parse link");
|
||||
setPending(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Flex flexDirection="column" gap="10px">
|
||||
<BaseText size="md" weight="normal" className={cl("sub")}>
|
||||
Song Spotlight supports these services: <b>{parsers.map(x => x.label).join(", ")}</b>
|
||||
</BaseText>
|
||||
<TextInput
|
||||
placeholder="https://open.spotify.com/..."
|
||||
error={error}
|
||||
onChange={value => {
|
||||
setURL(value);
|
||||
try {
|
||||
if (value) new URL(value);
|
||||
setError(undefined);
|
||||
} catch {
|
||||
setError("Invalid URL");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import { UserData, UserDataSchema } from "@song-spotlight/api/structs";
|
|||
import { sid } from "@song-spotlight/api/util";
|
||||
import { readClipboard } from "@utils/clipboard";
|
||||
import { copyWithToast } from "@utils/discord";
|
||||
import { ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Alerts, Parser, showToast, Toasts, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Alerts, Modal,openModal, Parser, showToast, Toasts, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
|
||||
interface ImportButtonProps {
|
||||
overwrite: boolean;
|
||||
|
|
@ -210,19 +210,14 @@ export default function Settings({ templateData }: SettingsProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export function SettingsModal({ modalProps, ...props }: SettingsProps & { modalProps: ModalProps; }) {
|
||||
export function SettingsModal({ modalProps, ...props }: SettingsProps & { modalProps: RenderModalProps; }) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="xl" weight="bold">Song Spotlight</BaseText>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<Settings {...props} />
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Modal {...modalProps} size="lg" title="Song Spotlight">
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<Settings {...props} />
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,10 @@ import { getUserSettingLazy } from "@api/UserSettings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { openModalLazy } from "@utils/modal";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { extractAndLoadChunksLazy, findComponentByCodeLazy, findModuleId, wreq } from "@webpack";
|
||||
import { Menu, OverridePremiumTypeStore, Toasts } from "@webpack/common";
|
||||
import { Menu, openModalLazy,OverridePremiumTypeStore, Toasts } from "@webpack/common";
|
||||
|
||||
interface Emoji {
|
||||
animated: boolean;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
import * as DataStore from "@api/DataStore";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { OAuth2AuthorizeModal, showToast, Toasts, UserStore, zustandCreate, zustandPersist } from "@webpack/common";
|
||||
import { OAuth2AuthorizeModal, openModal,showToast, Toasts, UserStore, zustandCreate, zustandPersist } from "@webpack/common";
|
||||
|
||||
import { AUTHORIZE_URL, CLIENT_ID } from "../constants";
|
||||
import { useStreaksStore } from "./StreaksStore";
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@ import { generateId } from "@api/Commands";
|
|||
import { Settings } from "@api/Settings";
|
||||
import { Button } from "@components/Button";
|
||||
import { Card } from "@components/Card";
|
||||
import { HeadingPrimary, HeadingTertiary } from "@components/Heading";
|
||||
import { HeadingPrimary } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import type { Theme, ThemeLikeProps } from "@equicordplugins/themeLibrary/types";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { FluxDispatcher, Parser, React, UserStore, UserUtils } from "@webpack/common";
|
||||
import { FluxDispatcher, Modal, openModal, Parser, React, UserStore, UserUtils } from "@webpack/common";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
import { LikesComponent } from "./LikesComponent";
|
||||
|
|
@ -65,36 +64,32 @@ export const ThemeCard: React.FC<ThemeCardProps> = ({ theme, themeLinks, likedTh
|
|||
|
||||
if (requiresThemeAttributes && !Settings.plugins.ThemeAttributes.enabled) {
|
||||
openModal(modalProps => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.SMALL}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>Hold on!</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph style={{ padding: "8px" }}>
|
||||
<p>This theme requires the <b>ThemeAttributes</b> plugin to work properly!</p>
|
||||
<p>Do you want to enable it?</p>
|
||||
</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="positive"
|
||||
onClick={() => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Hold on!"
|
||||
actions={[
|
||||
{
|
||||
text: "Enable Plugin",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
Settings.plugins.ThemeAttributes.enabled = true;
|
||||
modalProps.onClose();
|
||||
handleAddRemoveTheme();
|
||||
}}
|
||||
>
|
||||
Enable Plugin
|
||||
</Button>
|
||||
<Button
|
||||
variant="dangerPrimary"
|
||||
className={Margins.right8}
|
||||
onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: () => modalProps.onClose()
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph style={{ padding: "8px" }}>
|
||||
<p>This theme requires the <b>ThemeAttributes</b> plugin to work properly!</p>
|
||||
<p>Do you want to enable it?</p>
|
||||
</Paragraph>
|
||||
</Modal>
|
||||
));
|
||||
} else {
|
||||
handleAddRemoveTheme();
|
||||
|
|
|
|||
|
|
@ -5,19 +5,17 @@
|
|||
*/
|
||||
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import { Heading, HeadingTertiary } from "@components/Heading";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Heart } from "@components/Heart";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { Theme, ThemeInfoModalProps } from "@equicordplugins/themeLibrary/types";
|
||||
import { ClockIcon, DownloadIcon, WarningIcon } from "@equicordplugins/themeLibrary/utils/Icons";
|
||||
import { ClockIcon, WarningIcon } from "@equicordplugins/themeLibrary/utils/Icons";
|
||||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import type { PluginNative } from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Parser, React, showToast, Toasts } from "@webpack/common";
|
||||
import { Button, Modal, openModal, Parser, React, showToast, Toasts } from "@webpack/common";
|
||||
|
||||
import { logger } from "./ThemeTab";
|
||||
|
||||
|
|
@ -48,212 +46,192 @@ export const ThemeInfoModal: React.FC<ThemeInfoModalProps> = ({ author, theme, .
|
|||
const lastUpdated = Math.floor(new Date(last_updated ?? 0).getTime() / 1000);
|
||||
|
||||
return (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>{type} Details</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Heading style={{ marginTop: "10px" }}>{authors.length > 1 ? "Authors" : "Author"}</Heading>
|
||||
<div style={{ display: "flex", alignItems: "center", marginBottom: "10px" }}>
|
||||
<div>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<UserSummaryItem
|
||||
users={authors}
|
||||
count={authors.length}
|
||||
guildId={undefined}
|
||||
renderIcon={false}
|
||||
max={4}
|
||||
size={32}
|
||||
showDefaultAvatarsForNullUsers
|
||||
showUserPopout
|
||||
className={Margins.right8}
|
||||
/>
|
||||
<Paragraph style={{ maxWidth: "200px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{authors.map(author => author.username).join(", ")}
|
||||
</Paragraph>
|
||||
</div>
|
||||
{version && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Version</Heading>
|
||||
<Paragraph>
|
||||
{version}
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
<Heading style={{ marginTop: "10px" }}>Likes</Heading>
|
||||
<Paragraph>
|
||||
{likes === 0 ? `Nobody liked this ${type} yet.` : `${likes} users liked this ${type}!`}
|
||||
</Paragraph>
|
||||
{donate && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Donate</Heading>
|
||||
<Paragraph>
|
||||
You can support the author by donating below!
|
||||
</Paragraph>
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<Button onClick={() => VencordNative.native.openExternal(donate)}>
|
||||
<Heart />
|
||||
Donate
|
||||
</Button>
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
{(guild || invite) && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Support Server</Heading>
|
||||
{guild && (
|
||||
<Paragraph>
|
||||
{guild.name}
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph>
|
||||
<Button
|
||||
color={Button.Colors.BRAND}
|
||||
look={Button.Looks.FILLED}
|
||||
className={Margins.top8}
|
||||
onClick={async e => {
|
||||
e.preventDefault();
|
||||
const useInvite = guild ? guild.invite_link?.split("discord.gg/")[1] : invite;
|
||||
useInvite != null && openInviteModal(useInvite).catch(() => showToast("Invalid or expired invite!", Toasts.Type.FAILURE));
|
||||
}}
|
||||
>
|
||||
Join Discord Server
|
||||
</Button>
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
<Heading style={{ marginTop: "10px" }}>Source</Heading>
|
||||
<Paragraph>
|
||||
<Button
|
||||
disabled={!theme.content || theme.id === "preview"}
|
||||
onClick={() => openModal(modalProps => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>Theme Source</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph style={{
|
||||
padding: "8px",
|
||||
}}>
|
||||
<CodeBlock lang="css" content={themeContent} />
|
||||
</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button className={Margins.right8}
|
||||
onClick={() => {
|
||||
copyToClipboard(themeContent);
|
||||
showToast("Copied to Clipboard", Toasts.Type.SUCCESS);
|
||||
}}>Copy to Clipboard</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
))}
|
||||
>
|
||||
View Theme Source
|
||||
</Button>
|
||||
</Paragraph>
|
||||
{tags && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Tags</Heading>
|
||||
<Paragraph>
|
||||
{tags.map(tag => (
|
||||
<span className="vce-theme-info-tag" key={"vce-theme-info-tag"}>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
{requiresThemeAttributes && (
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<WarningIcon /> This theme requires the <b>ThemeAttributes</b> plugin!
|
||||
</Paragraph>
|
||||
)}
|
||||
{last_updated && (
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<ClockIcon /> This theme was last updated {Parser.parse("<t:" + lastUpdated + ":F>")} ({Parser.parse("<t:" + lastUpdated + ":R>")})
|
||||
</Paragraph>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => props.onClose()}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.GREEN}
|
||||
look={Button.Looks.FILLED}
|
||||
className={classes("vce-button", Margins.right8)}
|
||||
disabled={!theme.content || theme.id === "preview"}
|
||||
onClick={async () => {
|
||||
<Modal
|
||||
{...props}
|
||||
size="md"
|
||||
title={`${type} Details`}
|
||||
actions={[
|
||||
{
|
||||
text: "Close",
|
||||
variant: "dangerPrimary",
|
||||
onClick: () => props.onClose()
|
||||
},
|
||||
{
|
||||
text: "Download",
|
||||
variant: "positive",
|
||||
disabled: !theme.content || theme.id === "preview",
|
||||
onClick: async () => {
|
||||
const themesDir = await VencordNative.themes.getThemesDir();
|
||||
const exists = await Native.themeExists(themesDir, theme);
|
||||
// using another function so we get the proper file path instead of just guessing
|
||||
// which slash to use (im looking at you windows)
|
||||
const validThemesDir = await Native.getThemesDir(themesDir, theme);
|
||||
// check if theme exists, and ask if they want to overwrite
|
||||
if (exists) {
|
||||
openModal(modalProps => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.SMALL}>
|
||||
<ModalHeader>
|
||||
<HeadingTertiary>Conflict!</HeadingTertiary>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph style={{
|
||||
padding: "8px",
|
||||
}}>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<p>A theme with the same name <b>already exist</b> in your themes directory! Do you want to overwrite it?</p>
|
||||
<div className="vce-overwrite-modal">
|
||||
<code style={{ wordWrap: "break-word" }}>
|
||||
{validThemesDir}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
look={Button.Looks.FILLED}
|
||||
color={Button.Colors.RED}
|
||||
onClick={async () => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Conflict!"
|
||||
actions={[
|
||||
{
|
||||
text: "Overwrite",
|
||||
variant: "dangerPrimary",
|
||||
onClick: async () => {
|
||||
await downloadTheme(themesDir, theme);
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Overwrite
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.GREEN}
|
||||
look={Button.Looks.FILLED}
|
||||
className={Margins.right8} onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Keep my file
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Keep my file",
|
||||
variant: "positive",
|
||||
onClick: () => modalProps.onClose()
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph style={{ padding: "8px" }}>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<p>A theme with the same name <b>already exists</b> in your themes directory! Do you want to overwrite it?</p>
|
||||
<div className="vce-overwrite-modal">
|
||||
<code style={{ wordWrap: "break-word" }}>
|
||||
{validThemesDir}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</Paragraph>
|
||||
</Modal>
|
||||
));
|
||||
} else {
|
||||
await downloadTheme(themesDir, theme);
|
||||
}
|
||||
}}
|
||||
>
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading style={{ marginTop: "10px" }}>{authors.length > 1 ? "Authors" : "Author"}</Heading>
|
||||
<div style={{ display: "flex", alignItems: "center", marginBottom: "10px" }}>
|
||||
<div>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
Download <DownloadIcon style={{ marginLeft: "5px" }} />
|
||||
<UserSummaryItem
|
||||
users={authors}
|
||||
count={authors.length}
|
||||
guildId={undefined}
|
||||
renderIcon={false}
|
||||
max={4}
|
||||
size={32}
|
||||
showDefaultAvatarsForNullUsers
|
||||
showUserPopout
|
||||
className={Margins.right8}
|
||||
/>
|
||||
<Paragraph style={{ maxWidth: "200px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{authors.map(author => author.username).join(", ")}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
{version && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Version</Heading>
|
||||
<Paragraph>
|
||||
{version}
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
<Heading style={{ marginTop: "10px" }}>Likes</Heading>
|
||||
<Paragraph>
|
||||
{likes === 0 ? `Nobody liked this ${type} yet.` : `${likes} users liked this ${type}!`}
|
||||
</Paragraph>
|
||||
{donate && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Donate</Heading>
|
||||
<Paragraph>
|
||||
You can support the author by donating below!
|
||||
</Paragraph>
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<Button onClick={() => VencordNative.native.openExternal(donate)}>
|
||||
<Heart />
|
||||
Donate
|
||||
</Button>
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
{(guild || invite) && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Support Server</Heading>
|
||||
{guild && (
|
||||
<Paragraph>
|
||||
{guild.name}
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph>
|
||||
<Button
|
||||
color={Button.Colors.BRAND}
|
||||
look={Button.Looks.FILLED}
|
||||
className={Margins.top8}
|
||||
onClick={async e => {
|
||||
e.preventDefault();
|
||||
const useInvite = guild ? guild.invite_link?.split("discord.gg/")[1] : invite;
|
||||
useInvite != null && openInviteModal(useInvite).catch(() => showToast("Invalid or expired invite!", Toasts.Type.FAILURE));
|
||||
}}
|
||||
>
|
||||
Join Discord Server
|
||||
</Button>
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
<Heading style={{ marginTop: "10px" }}>Source</Heading>
|
||||
<Paragraph>
|
||||
<Button
|
||||
disabled={!theme.content || theme.id === "preview"}
|
||||
onClick={() => openModal(modalProps => (
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title="Theme Source"
|
||||
actions={[
|
||||
{
|
||||
text: "Close",
|
||||
variant: "dangerPrimary",
|
||||
onClick: () => modalProps.onClose()
|
||||
},
|
||||
{
|
||||
text: "Copy to Clipboard",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
copyToClipboard(themeContent);
|
||||
showToast("Copied to Clipboard", Toasts.Type.SUCCESS);
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph style={{ padding: "8px" }}>
|
||||
<CodeBlock lang="css" content={themeContent} />
|
||||
</Paragraph>
|
||||
</Modal>
|
||||
))}
|
||||
>
|
||||
View Theme Source
|
||||
</Button>
|
||||
</Paragraph>
|
||||
{tags && (
|
||||
<>
|
||||
<Heading style={{ marginTop: "10px" }}>Tags</Heading>
|
||||
<Paragraph>
|
||||
{tags.map(tag => (
|
||||
<span className="vce-theme-info-tag" key={"vce-theme-info-tag"}>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
{requiresThemeAttributes && (
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<WarningIcon /> This theme requires the <b>ThemeAttributes</b> plugin!
|
||||
</Paragraph>
|
||||
)}
|
||||
{last_updated && (
|
||||
<Paragraph style={{ marginTop: "10px" }}>
|
||||
<ClockIcon /> This theme was last updated {Parser.parse("<t:" + lastUpdated + ":F>")} ({Parser.parse("<t:" + lastUpdated + ":R>")})
|
||||
</Paragraph>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ModalProps } from "@utils/modal";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { RenderModalProps, User } from "@vencord/discord-types";
|
||||
|
||||
type Author = {
|
||||
github_name?: string;
|
||||
|
|
@ -35,7 +34,7 @@ export interface Theme {
|
|||
requiresThemeAttributes?: boolean;
|
||||
}
|
||||
|
||||
export interface ThemeInfoModalProps extends ModalProps {
|
||||
export interface ThemeInfoModalProps extends RenderModalProps {
|
||||
author: User | User[];
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
import * as DataStore from "@api/DataStore";
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { logger, themeRequest } from "@equicordplugins/themeLibrary/components/ThemeTab";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { OAuth2AuthorizeModal, Toasts, UserStore } from "@webpack/common";
|
||||
import { OAuth2AuthorizeModal, openModal,Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
export async function authorizeUser(triggerModal: boolean = true) {
|
||||
const isAuthorized = await getAuthorization();
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { HeadingPrimary, HeadingSecondary } from "@components/Heading";
|
||||
import { HeadingSecondary } from "@components/Heading";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { Button, SearchableSelect, useEffect, useMemo, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Modal, SearchableSelect, useEffect, useMemo, useState } from "@webpack/common";
|
||||
|
||||
import { DATASTORE_KEY, getSystemTimezone, resolveUserTimezone, settings, timezones } from ".";
|
||||
import { setTimezone, setUserDatabaseTimezone } from "./database";
|
||||
|
|
@ -21,7 +21,7 @@ export async function setUserTimezone(userId: string, timezone: string | null) {
|
|||
|
||||
const cl = classNameFactory("vc-timezone-");
|
||||
|
||||
export function SetTimezoneModal({ userId, modalProps, database }: { userId: string, modalProps: ModalProps; database?: boolean; }) {
|
||||
export function SetTimezoneModal({ userId, modalProps, database }: { userId: string, modalProps: RenderModalProps; database?: boolean; }) {
|
||||
const [currentValue, setCurrentValue] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -39,63 +39,59 @@ export function SetTimezoneModal({ userId, modalProps, database }: { userId: str
|
|||
});
|
||||
}, []);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
disabled: currentValue === null,
|
||||
onClick: async () => {
|
||||
if (database) {
|
||||
const success = await setTimezone(currentValue!);
|
||||
if (success) {
|
||||
await setUserDatabaseTimezone(userId, currentValue);
|
||||
}
|
||||
} else {
|
||||
await setUserTimezone(userId, currentValue);
|
||||
}
|
||||
|
||||
modalProps.onClose();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (!database) {
|
||||
actions.unshift({
|
||||
text: "Delete Timezone",
|
||||
variant: "dangerPrimary",
|
||||
disabled: false,
|
||||
onClick: async () => {
|
||||
await setUserTimezone(userId, null);
|
||||
modalProps.onClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<HeadingPrimary>
|
||||
Timezones
|
||||
</HeadingPrimary>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Timezones"
|
||||
actions={actions}
|
||||
>
|
||||
<section className={Margins.bottom16}>
|
||||
<HeadingSecondary>
|
||||
Select Timezone
|
||||
</HeadingSecondary>
|
||||
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<section className={Margins.bottom16}>
|
||||
<HeadingSecondary>
|
||||
Select Timezone
|
||||
</HeadingSecondary>
|
||||
|
||||
<SearchableSelect
|
||||
options={options}
|
||||
value={options.find(o => o.value === currentValue)?.value}
|
||||
placeholder={"Select a Timezone"}
|
||||
maxVisibleItems={5}
|
||||
closeOnSelect={true}
|
||||
onChange={v => setCurrentValue(v)}
|
||||
/>
|
||||
</section>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
{!database && (
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
onClick={async () => {
|
||||
await setUserTimezone(userId, null);
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Delete Timezone
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
color={Button.Colors.BRAND}
|
||||
disabled={currentValue === null}
|
||||
onClick={async () => {
|
||||
if (database) {
|
||||
const success = await setTimezone(currentValue!);
|
||||
if (success) {
|
||||
await setUserDatabaseTimezone(userId, currentValue);
|
||||
}
|
||||
} else {
|
||||
await setUserTimezone(userId, currentValue);
|
||||
}
|
||||
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<SearchableSelect
|
||||
options={options}
|
||||
value={options.find(o => o.value === currentValue)?.value}
|
||||
placeholder={"Select a Timezone"}
|
||||
maxVisibleItems={5}
|
||||
closeOnSelect={true}
|
||||
onChange={v => setCurrentValue(v)}
|
||||
/>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,10 @@ import * as DataStore from "@api/DataStore";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message, User } from "@vencord/discord-types";
|
||||
import { findByPropsLazy, findCssClassesLazy } from "@webpack";
|
||||
import { Button, Menu, showToast, Toasts, Tooltip, useEffect, UserStore, useState } from "@webpack/common";
|
||||
import { Button, Menu, openModal,showToast, Toasts, Tooltip, useEffect, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { deleteTimezone, getTimezone, loadDatabaseTimezones, setUserDatabaseTimezone } from "./database";
|
||||
import { SetTimezoneModal } from "./TimezoneModal";
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
*/
|
||||
|
||||
import { set } from "@api/DataStore";
|
||||
import { Button } from "@components/Button";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Margins } from "@components/margins";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { IconUtils, React, TextInput, Toasts, UserStore, useState } from "@webpack/common";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { IconUtils, Modal, React, TextInput, Toasts, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { data, KEY_DATASTORE } from ".";
|
||||
|
||||
|
|
@ -26,7 +24,7 @@ function fileToDataUrl(file: File): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
export function SetAvatarModal({ userId, modalProps }: { userId: string; modalProps: ModalProps; }) {
|
||||
export function SetAvatarModal({ userId, modalProps }: { userId: string; modalProps: RenderModalProps; }) {
|
||||
const { avatars } = data;
|
||||
const user = UserStore.getUser(userId);
|
||||
const originalAvatar = IconUtils.getUserAvatarURL(user, true, 128) || "";
|
||||
|
|
@ -36,7 +34,7 @@ export function SetAvatarModal({ userId, modalProps }: { userId: string; modalPr
|
|||
const [isDragging, setIsDragging] = useState(false);
|
||||
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
function handleKey(e: KeyboardEvent) {
|
||||
function handleKey(e: React.KeyboardEvent) {
|
||||
if (e.key === "Enter") saveUserAvatar();
|
||||
}
|
||||
|
||||
|
|
@ -78,15 +76,30 @@ export function SetAvatarModal({ userId, modalProps }: { userId: string; modalPr
|
|||
modalProps.onClose();
|
||||
}
|
||||
|
||||
const actions = [
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: saveUserAvatar
|
||||
}
|
||||
];
|
||||
|
||||
if (avatars[userId]) {
|
||||
actions.unshift({
|
||||
text: "Delete",
|
||||
variant: "dangerPrimary",
|
||||
onClick: deleteUserAvatar
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader className={cl("modal-header")}>
|
||||
<Heading tag="h3">Custom Avatar</Heading>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent className={cl("modal-content")} onKeyDown={handleKey}>
|
||||
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Custom Avatar"
|
||||
actions={actions}
|
||||
>
|
||||
<div onKeyDown={handleKey}>
|
||||
{/* Preview */}
|
||||
<div className={cl("preview-row")}>
|
||||
<div className={cl("preview-box")}>
|
||||
|
|
@ -141,17 +154,7 @@ export function SetAvatarModal({ userId, modalProps }: { userId: string; modalPr
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
<Flex gap="8px">
|
||||
{avatars[userId] && (
|
||||
<Button variant="dangerPrimary" onClick={deleteUserAvatar}>Delete</Button>
|
||||
)}
|
||||
<Button onClick={saveUserAvatar}>Save</Button>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,10 @@ import { Notice } from "@components/Notice";
|
|||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { extractAndLoadChunksLazy } from "@webpack";
|
||||
import { IconUtils, Menu, UserStore } from "@webpack/common";
|
||||
import { IconUtils, Menu, openModal,UserStore } from "@webpack/common";
|
||||
|
||||
import { SetAvatarModal } from "./AvatarModal";
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@ import {
|
|||
wrapTab,
|
||||
} from "@components/settings/tabs/BaseTab";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { Alerts, NavigationRouter, Toasts, useEffect, useState } from "@webpack/common";
|
||||
import { Alerts, closeAllModals,NavigationRouter, Toasts, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import userpluginInstaller, { Native } from "..";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -11,31 +11,22 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Button } from "@components/Button";
|
||||
import { Divider } from "@components/Divider";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { HeadingPrimary, HeadingSecondary } from "@components/Heading";
|
||||
import { HeadingSecondary } from "@components/Heading";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Margins } from "@utils/margins";
|
||||
import {
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
ModalSize,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { wordsToTitle } from "@utils/text";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import type { User } from "@vencord/discord-types";
|
||||
import { RenderModalProps, type User } from "@vencord/discord-types";
|
||||
import { ChannelType } from "@vencord/discord-types/enums";
|
||||
import {
|
||||
Button as DiscordButton,
|
||||
ChannelStore,
|
||||
Forms,
|
||||
GuildMemberStore,
|
||||
IconUtils,
|
||||
Menu,
|
||||
Modal,
|
||||
openModal,
|
||||
React,
|
||||
SearchableSelect,
|
||||
SelectedChannelStore,
|
||||
|
|
@ -686,7 +677,7 @@ interface UserContextProps {
|
|||
user: User;
|
||||
}
|
||||
|
||||
function VoiceSelectModal({ modalProps, user }: { modalProps: ModalProps; user: User; }) {
|
||||
function VoiceSelectModal({ modalProps, user }: { modalProps: RenderModalProps; user: User; }) {
|
||||
const DEFAULT_VALUE = "__default__";
|
||||
|
||||
const options = useMemo(() => {
|
||||
|
|
@ -741,80 +732,72 @@ function VoiceSelectModal({ modalProps, user }: { modalProps: ModalProps; user:
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader>
|
||||
<HeadingPrimary className={cl("modal-title")}>VC Narrator Voice</HeadingPrimary>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title="VC Narrator Voice"
|
||||
actions={[
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: modalProps.onClose
|
||||
},
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
if (currentValue === DEFAULT_VALUE) {
|
||||
settings.store.userVoiceMap = removeUserVoiceFromMap(settings.store.userVoiceMap, user.id);
|
||||
} else {
|
||||
settings.store.userVoiceMap = upsertUserVoiceMap(settings.store.userVoiceMap, user.id, currentValue);
|
||||
}
|
||||
modalProps.onClose();
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<section className={Margins.bottom16}>
|
||||
<HeadingSecondary>Select voice for {user.username}</HeadingSecondary>
|
||||
<SearchableSelect
|
||||
options={options}
|
||||
value={options.find(o => o.value === currentValue)?.value}
|
||||
placeholder="Select a voice"
|
||||
maxVisibleItems={6}
|
||||
closeOnSelect={true}
|
||||
onChange={v => setCurrentValue(v as any)}
|
||||
/>
|
||||
|
||||
<ModalContent>
|
||||
<section className={Margins.bottom16}>
|
||||
<HeadingSecondary>Select voice for {user.username}</HeadingSecondary>
|
||||
<SearchableSelect
|
||||
options={options}
|
||||
value={options.find(o => o.value === currentValue)?.value}
|
||||
placeholder="Select a voice"
|
||||
maxVisibleItems={6}
|
||||
closeOnSelect={true}
|
||||
onChange={v => setCurrentValue(v as any)}
|
||||
/>
|
||||
|
||||
<Forms.FormText className={cl("preview-hint")}>
|
||||
Preview how this voice sounds with their name:
|
||||
</Forms.FormText>
|
||||
<div className={cl("preview-buttons")}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(clean(displayName, settings.store.latinOnly) || "Someone")}
|
||||
>
|
||||
{busy ? "Playing..." : `"${clean(displayName, settings.store.latinOnly) || "Someone"}"`}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(`${clean(displayName, settings.store.latinOnly) || "Someone"} joined`)}
|
||||
>
|
||||
Joined
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(`${clean(displayName, settings.store.latinOnly) || "Someone"} left`)}
|
||||
>
|
||||
Left
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<div className={cl("modal-footer")}>
|
||||
<DiscordButton
|
||||
color={DiscordButton.Colors.PRIMARY}
|
||||
onClick={modalProps.onClose}
|
||||
<Forms.FormText className={cl("preview-hint")}>
|
||||
Preview how this voice sounds with their name:
|
||||
</Forms.FormText>
|
||||
<div className={cl("preview-buttons")}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(clean(displayName, settings.store.latinOnly) || "Someone")}
|
||||
>
|
||||
Cancel
|
||||
</DiscordButton>
|
||||
<DiscordButton
|
||||
color={DiscordButton.Colors.BRAND}
|
||||
onClick={() => {
|
||||
if (currentValue === DEFAULT_VALUE) {
|
||||
settings.store.userVoiceMap = removeUserVoiceFromMap(settings.store.userVoiceMap, user.id);
|
||||
} else {
|
||||
settings.store.userVoiceMap = upsertUserVoiceMap(settings.store.userVoiceMap, user.id, currentValue);
|
||||
}
|
||||
modalProps.onClose();
|
||||
}}
|
||||
{busy ? "Playing..." : `"${clean(displayName, settings.store.latinOnly) || "Someone"}"`}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(`${clean(displayName, settings.store.latinOnly) || "Someone"} joined`)}
|
||||
>
|
||||
Save
|
||||
</DiscordButton>
|
||||
Joined
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy}
|
||||
onClick={() => previewVoice(`${clean(displayName, settings.store.latinOnly) || "Someone"} left`)}
|
||||
>
|
||||
Left
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import { Divider } from "@components/Divider";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
|
@ -13,18 +12,9 @@ import { Heading } from "@components/Heading";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import {
|
||||
closeModal,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalRoot,
|
||||
ModalSize,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { Menu } from "@webpack/common";
|
||||
import { Menu, Modal,openModal } from "@webpack/common";
|
||||
|
||||
type CustomMessage = Message & {
|
||||
editHistory?: any;
|
||||
|
|
@ -60,38 +50,34 @@ function cleanMessage(msg: CustomMessage) {
|
|||
}
|
||||
|
||||
function openViewRawModal(obj: any, type: string, isMessage?: boolean) {
|
||||
const key = openModal(props => (
|
||||
openModal(props => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...props} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>
|
||||
View Raw {type}
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={() => closeModal(key)} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
{isMessage && (
|
||||
<>
|
||||
<Heading>
|
||||
Content
|
||||
</Heading>
|
||||
<CodeBlock
|
||||
content={obj.content}
|
||||
lang="markdown"
|
||||
/>
|
||||
<Divider className={Margins.bottom20} />
|
||||
</>
|
||||
)}
|
||||
<Modal
|
||||
{...props}
|
||||
size="lg"
|
||||
title={`View Raw ${type}`}
|
||||
>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
{isMessage && (
|
||||
<>
|
||||
<Heading>
|
||||
Content
|
||||
</Heading>
|
||||
<CodeBlock
|
||||
content={obj.content}
|
||||
lang="markdown"
|
||||
/>
|
||||
<Divider className={Margins.bottom20} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Heading>{type} Data</Heading>
|
||||
<CodeBlock
|
||||
content={JSON.stringify(obj, null, 4)}
|
||||
lang="json"
|
||||
/>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<Heading>{type} Data</Heading>
|
||||
<CodeBlock
|
||||
content={JSON.stringify(obj, null, 4)}
|
||||
lang="json"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
import "./VoiceChannelLogEntryComponent.css";
|
||||
|
||||
import { openUserProfile } from "@utils/discord";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { ApplicationStore, Clickable, IconUtils, NavigationRouter, Timestamp, Tooltip, UserStore } from "@webpack/common";
|
||||
import { ApplicationStore, Clickable, closeAllModals,IconUtils, NavigationRouter, Timestamp, Tooltip, UserStore } from "@webpack/common";
|
||||
|
||||
import { getCallStartTime } from "../logs";
|
||||
import { VoiceChannelLogEntry } from "../types";
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { Channel, RenderModalProps } from "@vencord/discord-types";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { Button, React, ScrollerThin } from "@webpack/common";
|
||||
import { Modal, openModal, React, ScrollerThin } from "@webpack/common";
|
||||
|
||||
import { clearLogs, getVcLogs, vcLogSubscribe } from "../logs";
|
||||
import { cl } from "../utils";
|
||||
|
|
@ -23,47 +21,43 @@ export function openVoiceChannelLog(channel: Channel) {
|
|||
));
|
||||
}
|
||||
|
||||
export function VoiceChannelLogModal({ channel, props }: { channel: Channel; props: ModalProps; }) {
|
||||
export function VoiceChannelLogModal({ channel, props }: { channel: Channel; props: RenderModalProps; }) {
|
||||
const logs = React.useSyncExternalStore(vcLogSubscribe, () => getVcLogs(channel.id));
|
||||
|
||||
return (
|
||||
<ModalRoot {...props} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" className={cl("header")} style={{ flexGrow: 1 }}>
|
||||
{channel.name} logs
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={props.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<ScrollerThin fade className={classes(cl("scroller"), `group-spacing-${AccessibilityStore.messageGroupSpacing}`)}>
|
||||
{logs.length > 0 ? logs.map((entry, i) => {
|
||||
const elements: React.ReactNode[] = [];
|
||||
|
||||
if (i === 0 || entry.timestamp.toDateString() !== logs[i - 1].timestamp.toDateString()) {
|
||||
elements.push(
|
||||
<div key={`sep-${i}`} className={cl("date-separator")} role="separator" aria-label={entry.timestamp.toDateString()}>
|
||||
<span>{entry.timestamp.toDateString()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Modal
|
||||
{...props}
|
||||
size="lg"
|
||||
title={`${channel.name} logs`}
|
||||
actions={[
|
||||
{
|
||||
text: "Clear logs",
|
||||
variant: "dangerPrimary",
|
||||
onClick: () => clearLogs(channel.id)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<ScrollerThin fade className={classes(cl("scroller"), `group-spacing-${AccessibilityStore.messageGroupSpacing}`)}>
|
||||
{logs.length > 0 ? logs.map((entry, i) => {
|
||||
const elements: React.ReactNode[] = [];
|
||||
|
||||
if (i === 0 || entry.timestamp.toDateString() !== logs[i - 1].timestamp.toDateString()) {
|
||||
elements.push(
|
||||
<VoiceChannelLogEntryComponent key={`entry-${i}`} logEntry={entry} channel={channel} />
|
||||
<div key={`sep-${i}`} className={cl("date-separator")} role="separator" aria-label={entry.timestamp.toDateString()}>
|
||||
<span>{entry.timestamp.toDateString()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}) : (
|
||||
<div className={cl("empty")}>No logs to display.</div>
|
||||
)}
|
||||
</ScrollerThin>
|
||||
</ModalContent>
|
||||
elements.push(
|
||||
<VoiceChannelLogEntryComponent key={`entry-${i}`} logEntry={entry} channel={channel} />
|
||||
);
|
||||
|
||||
<ModalFooter>
|
||||
<Button color={Button.Colors.RED} onClick={() => clearLogs(channel.id)}>
|
||||
Clear logs
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
return elements;
|
||||
}) : (
|
||||
<div className={cl("empty")}>No logs to display.</div>
|
||||
)}
|
||||
</ScrollerThin>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import { Heading } from "@components/Heading";
|
|||
import { Span } from "@components/Span";
|
||||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType, PluginNative } from "@utils/types";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { lodash, ScrollerAuto, SearchableSelect, useEffect, useRef, useState } from "@webpack/common";
|
||||
import { lodash, Modal, openModal, ScrollerAuto, SearchableSelect, useEffect, useRef, useState } from "@webpack/common";
|
||||
|
||||
import { cl, decodeAudio, LANGUAGES, TranscriptionWorker } from "./utils";
|
||||
const Native = VencordNative.pluginHelpers.VoiceMessageTranscriber as PluginNative<typeof import("./native")>;
|
||||
|
|
@ -93,7 +93,7 @@ const settings = definePluginSettings({
|
|||
}
|
||||
});
|
||||
|
||||
function LanguageSelectionModal(props: { modalProps: ModalProps, src: string; }) {
|
||||
function LanguageSelectionModal(props: { modalProps: RenderModalProps, src: string; }) {
|
||||
const { modalProps, src } = props;
|
||||
const [language, setLanguage] = useState<string>("auto");
|
||||
const [task, setTask] = useState<string>("transcribe");
|
||||
|
|
@ -118,51 +118,52 @@ function LanguageSelectionModal(props: { modalProps: ModalProps, src: string; })
|
|||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="bold" style={{ flexGrow: 1 }}>Transcription Options</BaseText>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent style={{ padding: "16px" }}>
|
||||
<Flex flexDirection="column" gap={20}>
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold" style={{ marginBottom: "8px" }}>
|
||||
Audio Language
|
||||
</BaseText>
|
||||
<SearchableSelect
|
||||
options={languageOptions}
|
||||
value={languageOptions.find(o => o.value === language)?.value}
|
||||
onChange={setLanguage}
|
||||
/>
|
||||
</div>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title="Transcription Options"
|
||||
actions={[
|
||||
{
|
||||
text: "Start",
|
||||
variant: "primary",
|
||||
onClick: start
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Flex flexDirection="column" gap={20} style={{ padding: "16px" }}>
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold" style={{ marginBottom: "8px" }}>
|
||||
Audio Language
|
||||
</BaseText>
|
||||
<SearchableSelect
|
||||
options={languageOptions}
|
||||
value={languageOptions.find(o => o.value === language)?.value}
|
||||
onChange={setLanguage}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold" style={{ marginBottom: "8px" }}>
|
||||
Action
|
||||
</BaseText>
|
||||
<ManaBaseRadioGroup
|
||||
options={[{
|
||||
name: "Transcribe",
|
||||
value: "transcribe"
|
||||
}, {
|
||||
name: "Translate to English",
|
||||
value: "translate"
|
||||
}]}
|
||||
value={task}
|
||||
onChange={v => setTask(v as string)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button onClick={start} variant="primary">
|
||||
Start
|
||||
</Button>
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
<div>
|
||||
<BaseText size="sm" weight="semibold" style={{ marginBottom: "8px" }}>
|
||||
Action
|
||||
</BaseText>
|
||||
<ManaBaseRadioGroup
|
||||
options={[{
|
||||
name: "Transcribe",
|
||||
value: "transcribe"
|
||||
}, {
|
||||
name: "Translate to English",
|
||||
value: "translate"
|
||||
}]}
|
||||
value={task}
|
||||
onChange={v => setTask(v as string)}
|
||||
/>
|
||||
</div>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function TranscriptionModal(props: { modalProps: ModalProps, src: string, options: { language: string, task: string; }; }) {
|
||||
function TranscriptionModal(props: { modalProps: RenderModalProps, src: string, options: { language: string, task: string; }; }) {
|
||||
const { modalProps, src, options } = props;
|
||||
const [status, setStatus] = useState<string>("initializing");
|
||||
const [result, setResult] = useState<{ text: string, chunks: { timestamp: [number, number], text: string; }[]; } | null>(null);
|
||||
|
|
@ -252,61 +253,71 @@ function TranscriptionModal(props: { modalProps: ModalProps, src: string, option
|
|||
: result.text
|
||||
) : "";
|
||||
|
||||
return <ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="bold" style={{ flexGrow: 1 }}>Transcription</BaseText>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("content")}>
|
||||
{error ? (
|
||||
<Flex flexDirection="column" gap={16} style={{ padding: "20px 0" }}>
|
||||
<Heading tag="h3" style={{ color: "var(--red-360)" }}>Error</Heading>
|
||||
<Span style={{ whiteSpace: "pre-wrap" }}>{error}</Span>
|
||||
<Button onClick={retry}>
|
||||
Retry
|
||||
</Button>
|
||||
</Flex>
|
||||
) : (status === "complete" || (status === "transcribing" && result)) ? (
|
||||
<Flex flexDirection="column" gap={16} style={{ paddingBottom: "20px" }}>
|
||||
{status === "transcribing" && (
|
||||
<Span size="sm" color="text-muted">Transcribing in progress...</Span>
|
||||
)}
|
||||
<ScrollerAuto className={cl("result")}>
|
||||
<Span>{displayText}</Span>
|
||||
</ScrollerAuto>
|
||||
<Flex flexDirection="row" gap={12} alignItems="center">
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<FormSwitch
|
||||
title="Show Timestamps"
|
||||
value={showTimestamps}
|
||||
onChange={setShowTimestamps}
|
||||
/>
|
||||
</div>
|
||||
const actions: any[] = [];
|
||||
if (error) {
|
||||
actions.push({
|
||||
text: "Retry",
|
||||
variant: "primary",
|
||||
onClick: retry
|
||||
});
|
||||
} else if (status === "complete" || (status === "transcribing" && result)) {
|
||||
actions.push({
|
||||
text: copied ? "Copied!" : "Copy to Clipboard",
|
||||
variant: "primary",
|
||||
disabled: status === "transcribing" || copied,
|
||||
onClick: () => {
|
||||
copyToClipboard(displayText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title="Transcription"
|
||||
actions={actions}
|
||||
>
|
||||
<div className={cl("content")}>
|
||||
{error ? (
|
||||
<Flex flexDirection="column" gap={16} style={{ padding: "20px 0" }}>
|
||||
<Heading tag="h3" style={{ color: "var(--red-360)" }}>Error</Heading>
|
||||
<Span style={{ whiteSpace: "pre-wrap" }}>{error}</Span>
|
||||
</Flex>
|
||||
<Button
|
||||
disabled={status === "transcribing" || copied}
|
||||
onClick={() => {
|
||||
copyToClipboard(displayText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}}
|
||||
>
|
||||
{copied ? "Copied!" : "Copy to Clipboard"}
|
||||
</Button>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex flexDirection="column" gap={16} style={{ padding: "20px 0", alignItems: "center" }}>
|
||||
<Heading tag="h3">
|
||||
{status === "initializing" && "Initializing..."}
|
||||
{status === "downloading_audio" && "Downloading Audio..."}
|
||||
{status === "processing_audio" && "Processing Audio..."}
|
||||
{status === "loading" && "Loading Model..."}
|
||||
{status === "transcribing" && "Transcribing..."}
|
||||
</Heading>
|
||||
</Flex>
|
||||
)}
|
||||
</ModalContent>
|
||||
</ModalRoot>;
|
||||
) : (status === "complete" || (status === "transcribing" && result)) ? (
|
||||
<Flex flexDirection="column" gap={16} style={{ paddingBottom: "20px" }}>
|
||||
{status === "transcribing" && (
|
||||
<Span size="sm" color="text-muted">Transcribing in progress...</Span>
|
||||
)}
|
||||
<ScrollerAuto className={cl("result")}>
|
||||
<Span>{displayText}</Span>
|
||||
</ScrollerAuto>
|
||||
<Flex flexDirection="row" gap={12} alignItems="center">
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<FormSwitch
|
||||
title="Show Timestamps"
|
||||
value={showTimestamps}
|
||||
onChange={setShowTimestamps}
|
||||
/>
|
||||
</div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex flexDirection="column" gap={16} style={{ padding: "20px 0", alignItems: "center" }}>
|
||||
<Heading tag="h3">
|
||||
{status === "initializing" && "Initializing..."}
|
||||
{status === "downloading_audio" && "Downloading Audio..."}
|
||||
{status === "processing_audio" && "Processing Audio..."}
|
||||
{status === "loading" && "Loading Model..."}
|
||||
{status === "transcribing" && "Transcribing..."}
|
||||
</Heading>
|
||||
</Flex>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { BaseText } from "@components/BaseText";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Heading, HeadingPrimary } from "@components/Heading";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { makeLazy } from "@utils/lazy";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { findByProps, wreq } from "@webpack";
|
||||
import { Button, Timestamp, useState } from "@webpack/common";
|
||||
import { Button, Modal,openModal, Timestamp, useState } from "@webpack/common";
|
||||
|
||||
import TarFile from "./tar";
|
||||
import * as Webpack from "./webpack";
|
||||
|
|
@ -37,10 +37,9 @@ export default definePlugin({
|
|||
|
||||
toolboxActions: {
|
||||
"Webpack Tarball"() {
|
||||
const key = openModal(props => (
|
||||
openModal(props => (
|
||||
<TarModal
|
||||
modalProps={props}
|
||||
close={() => closeModal(key)}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
|
@ -83,7 +82,7 @@ async function saveTar(patched: boolean) {
|
|||
tar.save(`${root}.tar`);
|
||||
}
|
||||
|
||||
function TarModal({ modalProps, close }: { modalProps: ModalProps; close(): void; }) {
|
||||
function TarModal({ modalProps }: { modalProps: RenderModalProps; }) {
|
||||
const webpackRequire = wreq as any;
|
||||
const { buildNumber, builtAt } = getBuildNumber();
|
||||
const [, rerender] = useState({});
|
||||
|
|
@ -99,66 +98,63 @@ function TarModal({ modalProps, close }: { modalProps: ModalProps; close(): void
|
|||
const { patched } = settings.use(["patched"]);
|
||||
const WEBPACK_CHUNK = "webpackChunkdiscord_app";
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<HeadingPrimary>
|
||||
Webpack Tarball
|
||||
</HeadingPrimary>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="md"
|
||||
title="Webpack Tarball"
|
||||
actions={[
|
||||
{
|
||||
text: "Create",
|
||||
variant: "primary",
|
||||
onClick: () => {
|
||||
saveTar(patched);
|
||||
modalProps.onClose();
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div style={{ marginBottom: "16px" }}>
|
||||
<BaseText size="md">
|
||||
<Timestamp timestamp={new Date(builtAt)} isInline={false}>
|
||||
{"Build number "}
|
||||
{buildNumber}
|
||||
</Timestamp>
|
||||
</BaseText>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
</div>
|
||||
|
||||
<ModalContent>
|
||||
<div style={{ marginTop: "8px", marginBottom: "24px" }}>
|
||||
<Heading>
|
||||
Lazy chunks
|
||||
</Heading>
|
||||
<Flex alignItems="center">
|
||||
<BaseText
|
||||
size="md"
|
||||
style={{ flexGrow: 1 }}
|
||||
>
|
||||
{loaded}/{all}
|
||||
{errored ? ` (${errored} errors)` : null}
|
||||
</BaseText>
|
||||
<Button
|
||||
disabled={loading === all || isLoading}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
// @ts-ignore
|
||||
await Webpack.protectWebpack(window[WEBPACK_CHUNK], async () => {
|
||||
await Webpack.forceLoadAll(webpackRequire, rerender);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loaded === all ? "Loaded" : loading === all ? "Loading" : "Load all"}
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
<div style={{ marginTop: "8px", marginBottom: "24px" }}>
|
||||
<Heading>
|
||||
Lazy chunks
|
||||
</Heading>
|
||||
<Flex alignItems="center">
|
||||
<BaseText
|
||||
size="md"
|
||||
style={{ flexGrow: 1 }}
|
||||
>
|
||||
{loaded}/{all}
|
||||
{errored ? ` (${errored} errors)` : null}
|
||||
</BaseText>
|
||||
<Button
|
||||
disabled={loading === all || isLoading}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
// @ts-ignore
|
||||
await Webpack.protectWebpack(window[WEBPACK_CHUNK], async () => {
|
||||
await Webpack.forceLoadAll(webpackRequire, rerender);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loaded === all ? "Loaded" : loading === all ? "Loading" : "Load all"}
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
|
||||
<FormSwitch
|
||||
value={patched}
|
||||
onChange={v => settings.store.patched = v}
|
||||
title={settings.def.patched.description}
|
||||
hideBorder
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
saveTar(patched);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<FormSwitch
|
||||
value={patched}
|
||||
onChange={v => settings.store.patched = v}
|
||||
title={settings.def.patched.description}
|
||||
hideBorder
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { ChevronSmallDownIcon, ChevronSmallUpIcon, FolderIcon } from "@components/Icons";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { copyWithToast } from "@utils/discord";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
import { Modal,openModal, useEffect, useMemo, useRef, useState } from "@webpack/common";
|
||||
|
||||
import {
|
||||
createImageObjectUrl,
|
||||
|
|
@ -312,52 +308,50 @@ function openZipEntryModal(entry: ZipEntry) {
|
|||
|
||||
function openTextEntryModal(entry: ZipEntry) {
|
||||
const content = readTextEntry(entry);
|
||||
const key = openModal(modalProps => (
|
||||
openModal(modalProps => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<Heading tag="h2" className={cl("modal-title")}>{entry.name}</Heading>
|
||||
<ModalCloseButton onClick={() => closeModal(key)} />
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<div className={cl("code-wrap")}>
|
||||
<CodeBlock content={content} lang={getCodeLanguage(entry)} />
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex gap={8} justifyContent="flex-end">
|
||||
<Button variant="secondary" onClick={() => copyWithToast(content, "File contents copied to clipboard!")}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button onClick={() => makeDownload(entry)}>
|
||||
Download
|
||||
</Button>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title={entry.name}
|
||||
actions={[
|
||||
{
|
||||
text: "Copy",
|
||||
variant: "secondary",
|
||||
onClick: () => copyWithToast(content, "File contents copied to clipboard!")
|
||||
},
|
||||
{
|
||||
text: "Download",
|
||||
variant: "primary",
|
||||
onClick: () => makeDownload(entry)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<div className={cl("code-wrap")}>
|
||||
<CodeBlock content={content} lang={getCodeLanguage(entry)} />
|
||||
</div>
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
}
|
||||
|
||||
function openImageEntryModal(entry: ZipEntry) {
|
||||
const key = openModal(modalProps => (
|
||||
openModal(modalProps => (
|
||||
<ErrorBoundary>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<Heading tag="h2" className={cl("modal-title")}>{entry.name}</Heading>
|
||||
<ModalCloseButton onClick={() => closeModal(key)} />
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<ZipImagePreview entry={entry} />
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex gap={8} justifyContent="flex-end">
|
||||
<Button onClick={() => makeDownload(entry)}>
|
||||
Download
|
||||
</Button>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="lg"
|
||||
title={entry.name}
|
||||
actions={[
|
||||
{
|
||||
text: "Download",
|
||||
variant: "primary",
|
||||
onClick: () => makeDownload(entry)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<ZipImagePreview entry={entry} />
|
||||
</Modal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,35 +4,29 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { Modal } from "@webpack/common";
|
||||
|
||||
import { ClearAliasesConfirmModalProps } from "./types";
|
||||
|
||||
export function ClearAliasesConfirmModal({ modalProps, onConfirm }: ClearAliasesConfirmModalProps) {
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.SMALL}>
|
||||
<ModalHeader>
|
||||
<Heading style={{ flexGrow: 1 }}>Delete all aliases</Heading>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Paragraph>This will remove every emoji alias you saved.</Paragraph>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="dangerPrimary"
|
||||
style={{ marginLeft: "auto" }}
|
||||
onClick={async () => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Delete all aliases"
|
||||
actions={[
|
||||
{
|
||||
text: "Delete all aliases",
|
||||
variant: "danger-primary",
|
||||
onClick: async () => {
|
||||
await onConfirm();
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Delete all aliases
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph>This will remove every emoji alias you saved.</Paragraph>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@
|
|||
*/
|
||||
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize } from "@utils/modal";
|
||||
import { TextInput, useState } from "@webpack/common";
|
||||
import { Modal, TextInput, useState } from "@webpack/common";
|
||||
|
||||
import { SetAliasModalProps } from "./types";
|
||||
|
||||
|
|
@ -30,45 +27,40 @@ export function SetAliasModal({
|
|||
const canSave = !validationError && !duplicateAlias;
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.SMALL}>
|
||||
<ModalHeader>
|
||||
<Heading style={{ flexGrow: 1 }}>Set alias</Heading>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent style={{ overflowY: "hidden" }}>
|
||||
<Paragraph style={{ margin: 0, marginBottom: 8 }}>Set an alias for {emojiDisplayName}</Paragraph>
|
||||
<TextInput
|
||||
value={input}
|
||||
onChange={value => {
|
||||
setInput(value);
|
||||
setError(null);
|
||||
}}
|
||||
placeholder='Alias, e.g. "sob"'
|
||||
/>
|
||||
{finalError && (
|
||||
<BaseText style={{ color: "var(--text-feedback-critical)", marginTop: 8 }}>
|
||||
{finalError}
|
||||
</BaseText>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={!canSave}
|
||||
onClick={async () => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title="Set alias"
|
||||
actions={[
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
disabled: !canSave,
|
||||
onClick: async () => {
|
||||
const result = await onSave(input);
|
||||
if (!result.ok) {
|
||||
setError(result.error);
|
||||
return;
|
||||
}
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Paragraph style={{ margin: 0, marginBottom: 8 }}>Set an alias for {emojiDisplayName}</Paragraph>
|
||||
<TextInput
|
||||
value={input}
|
||||
onChange={value => {
|
||||
setInput(value);
|
||||
setError(null);
|
||||
}}
|
||||
placeholder='Alias, e.g. "sob"'
|
||||
/>
|
||||
{finalError && (
|
||||
<BaseText style={{ color: "var(--text-feedback-critical)", marginTop: 8 }}>
|
||||
{finalError}
|
||||
</BaseText>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ModalProps } from "@utils/modal";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
|
||||
export type SetAliasSaveResult = { ok: true; } | { ok: false; error: string; };
|
||||
|
||||
export interface SetAliasModalProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
emojiDisplayName: string;
|
||||
initialAlias: string;
|
||||
getValidationError: (input: string) => string | null;
|
||||
|
|
@ -18,6 +18,6 @@ export interface SetAliasModalProps {
|
|||
}
|
||||
|
||||
export interface ClearAliasesConfirmModalProps {
|
||||
modalProps: ModalProps;
|
||||
modalProps: RenderModalProps;
|
||||
onConfirm: () => Promise<void>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@ import { Paragraph } from "@components/Paragraph";
|
|||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Emoji, Message } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { EmojiStore, Menu, TextInput, Toasts, useEffect, useState } from "@webpack/common";
|
||||
import { EmojiStore, Menu, openModal,TextInput, Toasts, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { ClearAliasesConfirmModal } from "./components/modals/ClearAliasesConfirmModal";
|
||||
import { SetAliasModal } from "./components/modals/SetAliasModal";
|
||||
|
|
|
|||
|
|
@ -51,22 +51,26 @@ function getEmojiValue(emoji: EmojiSelectPayload | null | undefined) {
|
|||
return emoji.name?.trim() ?? "";
|
||||
}
|
||||
|
||||
function toRenderedEmoji(value: string) {
|
||||
type RenderedEmoji =
|
||||
| { kind: "custom"; id: string; name: string; animated: boolean; }
|
||||
| { kind: "unicode"; name: string; animated: boolean; };
|
||||
|
||||
function toRenderedEmoji(value: string): RenderedEmoji | null {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const customEmoji = parseCustomEmoji(trimmed);
|
||||
if (customEmoji) {
|
||||
return {
|
||||
kind: "custom" as const,
|
||||
id: customEmoji[3],
|
||||
name: customEmoji[2],
|
||||
kind: "custom",
|
||||
id: customEmoji[3]!,
|
||||
name: customEmoji[2]!,
|
||||
animated: customEmoji[1] === "a"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "unicode" as const,
|
||||
kind: "unicode",
|
||||
name: trimmed,
|
||||
animated: false
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
import "./styles.css";
|
||||
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { getGuildAcronym, openImageModal, openUserProfile } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co
|
|||
import { DataStore } from "@api/index";
|
||||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Button, TextButton } from "@components/Button";
|
||||
import { TextButton } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Heading } from "@components/Heading";
|
||||
import ircColors from "@plugins/ircColors";
|
||||
import mentionAvatars from "@plugins/mentionAvatars";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/index";
|
||||
import { classNameFactory } from "@utils/index";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { GuildMember, Message, User } from "@vencord/discord-types";
|
||||
import { GuildMember, Message, RenderModalProps, User } from "@vencord/discord-types";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, RelationshipStore, StreamerModeStore, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, Modal, openModal, RelationshipStore, StreamerModeStore, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
const SMYNC = classNameFactory();
|
||||
|
|
@ -842,49 +842,19 @@ function removeHoveringReactionPopout(id: string) {
|
|||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
|
||||
function CustomNicknameModal({ modalProps, user }: { modalProps: ModalProps; user: User; }) {
|
||||
function CustomNicknameModal({ modalProps, user }: { modalProps: RenderModalProps; user: User; }) {
|
||||
const [value, setValue] = useState(customNicknames[user.id] ?? "");
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<Heading tag="h1" style={{ flexGrow: 1, margin: 0 }}>
|
||||
{customNicknames[user.id] ? "Change SMYN Nickname" : "Add SMYN Nickname"}
|
||||
</Heading>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Heading tag="h3" style={{ marginBottom: 8, fontSize: "16px", fontWeight: "400", lineHeight: "1.25", color: "var(--text-subtle)" }}>
|
||||
{"Set a custom SMYN nickname for this user. Make use of it by specifying {custom} in the SMYN template settings."}
|
||||
</Heading>
|
||||
<div style={{ paddingTop: "10px", flexGrow: 0 }}></div>
|
||||
<Heading tag="h3" style={{ marginBottom: 8, fontSize: "14px", fontWeight: 600 }}>
|
||||
SMYN Nickname
|
||||
</Heading>
|
||||
<TextInput
|
||||
value={value}
|
||||
maxLength={32}
|
||||
onChange={setValue}
|
||||
placeholder={user.globalName ?? user.username}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
<TextButton
|
||||
className="smyn-reset-button"
|
||||
onClick={async () => {
|
||||
setValue("");
|
||||
delete customNicknames[user.id];
|
||||
await DataStore.set("SMYNCustomNicknames", customNicknames);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}}
|
||||
>
|
||||
Reset SMYN Nickname
|
||||
</TextButton>
|
||||
<div style={{ paddingTop: "10px", flexGrow: 0 }}></div>
|
||||
</ModalContent>
|
||||
<ModalFooter className="smyn-modal-footer-container">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={async () => {
|
||||
<Modal
|
||||
{...modalProps}
|
||||
size="sm"
|
||||
title={customNicknames[user.id] ? "Change SMYN Nickname" : "Add SMYN Nickname"}
|
||||
actions={[
|
||||
{
|
||||
text: "Save",
|
||||
variant: "primary",
|
||||
onClick: async () => {
|
||||
const trimmed = value.trim().slice(0, 32).trim();
|
||||
|
||||
if (trimmed) {
|
||||
|
|
@ -896,19 +866,42 @@ function CustomNicknameModal({ modalProps, user }: { modalProps: ModalProps; use
|
|||
await DataStore.set("SMYNCustomNicknames", customNicknames);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
style={{ marginRight: "8px" }}
|
||||
onClick={modalProps.onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
variant: "secondary",
|
||||
onClick: modalProps.onClose
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Heading tag="h3" style={{ marginBottom: 8, fontSize: "16px", fontWeight: "400", lineHeight: "1.25", color: "var(--text-subtle)" }}>
|
||||
{"Set a custom SMYN nickname for this user. Make use of it by specifying {custom} in the SMYN template settings."}
|
||||
</Heading>
|
||||
<div style={{ paddingTop: "10px", flexGrow: 0 }}></div>
|
||||
<Heading tag="h3" style={{ marginBottom: 8, fontSize: "14px", fontWeight: 600 }}>
|
||||
SMYN Nickname
|
||||
</Heading>
|
||||
<TextInput
|
||||
value={value}
|
||||
maxLength={32}
|
||||
onChange={setValue}
|
||||
placeholder={user.globalName ?? user.username}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
<TextButton
|
||||
className="smyn-reset-button"
|
||||
onClick={async () => {
|
||||
setValue("");
|
||||
delete customNicknames[user.id];
|
||||
await DataStore.set("SMYNCustomNicknames", customNicknames);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}}
|
||||
>
|
||||
Reset SMYN Nickname
|
||||
</TextButton>
|
||||
<div style={{ paddingTop: "10px", flexGrow: 0 }}></div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { TooltipContainer } from "@components/TooltipContainer";
|
||||
import { classes } from "@utils/misc";
|
||||
import { IconComponent } from "@utils/types";
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { FormSwitch } from "@components/FormSwitch";
|
|||
import { HeadingSecondary } from "@components/Heading";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { RenderModalProps } from "@vencord/discord-types";
|
||||
import { Forms, Modal, openModal, SearchableSelect, useMemo } from "@webpack/common";
|
||||
import { Modal, openModal, SearchableSelect, useMemo } from "@webpack/common";
|
||||
|
||||
import { settings } from "./settings";
|
||||
import { getLanguages } from "./utils";
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import { Divider } from "@components/Divider";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
|
@ -30,7 +29,6 @@ import definePlugin, { IconComponent, OptionType } from "@utils/types";
|
|||
import { Message } from "@vencord/discord-types";
|
||||
import { ChannelStore, Forms, GuildRoleStore, Menu, Modal, openModal } from "@webpack/common";
|
||||
|
||||
|
||||
const CopyIcon: IconComponent = ({ height = 20, width = 20, className }) => {
|
||||
return (
|
||||
<svg
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue