parent
55901ba35e
commit
2ce3487477
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { DEFAULT_COLOR, SWATCHES } from "../constants";
|
||||
import { categories, Category, createCategory, getCategory, updateCategory } from "../data";
|
||||
import { forceUpdate } from "../index";
|
||||
|
||||
interface ColorPickerProps {
|
||||
color: number | null;
|
||||
showEyeDropper?: boolean;
|
||||
suggestedColors?: string[];
|
||||
onChange(value: number | null): void;
|
||||
}
|
||||
|
||||
interface ColorPickerWithSwatchesProps {
|
||||
defaultColor: number;
|
||||
colors: number[];
|
||||
value: number;
|
||||
disabled?: boolean;
|
||||
onChange(value: number | null): void;
|
||||
renderDefaultButton?: () => React.ReactNode;
|
||||
renderCustomButton?: () => React.ReactNode;
|
||||
}
|
||||
|
||||
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ColorPickerWithSwatches = findComponentByCodeLazy<ColorPickerWithSwatchesProps>(".presets,", "customColor:");
|
||||
|
||||
export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}el\("(.+?)"\).{0,50}"UserSettings"/);
|
||||
|
||||
const cl = classNameFactory("vc-pindms-modal-");
|
||||
|
||||
interface Props {
|
||||
categoryId: string | null;
|
||||
initalChannelId: string | null;
|
||||
modalProps: ModalProps;
|
||||
}
|
||||
|
||||
function useCategory(categoryId: string | null, initalChannelId: string | null) {
|
||||
const [category, setCategory] = useState<Category | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (categoryId)
|
||||
setCategory(getCategory(categoryId)!);
|
||||
else if (initalChannelId)
|
||||
setCategory({
|
||||
id: Toasts.genId(),
|
||||
name: `Pin Category ${categories.length + 1}`,
|
||||
color: 10070709,
|
||||
collapsed: false,
|
||||
channels: [initalChannelId]
|
||||
});
|
||||
}, [categoryId, initalChannelId]);
|
||||
|
||||
return {
|
||||
category,
|
||||
setCategory
|
||||
};
|
||||
}
|
||||
|
||||
export function NewCategoryModal({ categoryId, modalProps, initalChannelId }: Props) {
|
||||
const { category, setCategory } = useCategory(categoryId, initalChannelId);
|
||||
|
||||
if (!category) return null;
|
||||
|
||||
const onSave = async (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
if (!categoryId)
|
||||
await createCategory(category);
|
||||
else
|
||||
await updateCategory(category);
|
||||
|
||||
forceUpdate();
|
||||
modalProps.onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{categoryId ? "Edit" : "New"} Category</Text>
|
||||
</ModalHeader>
|
||||
|
||||
{/* form is here so when you press enter while in the text input it submits */}
|
||||
<form onSubmit={onSave}>
|
||||
<ModalContent className={cl("content")}>
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>Name</Forms.FormTitle>
|
||||
<TextInput
|
||||
value={category.name}
|
||||
onChange={e => setCategory({ ...category, name: e })}
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
<Forms.FormDivider />
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>Color</Forms.FormTitle>
|
||||
<ColorPickerWithSwatches
|
||||
key={category.name}
|
||||
defaultColor={DEFAULT_COLOR}
|
||||
colors={SWATCHES}
|
||||
onChange={c => setCategory({ ...category, color: c! })}
|
||||
value={category.color}
|
||||
renderDefaultButton={() => null}
|
||||
renderCustomButton={() => (
|
||||
<ColorPicker
|
||||
color={category.color}
|
||||
onChange={c => setCategory({ ...category, color: c! })}
|
||||
key={category.name}
|
||||
showEyeDropper={false}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button type="submit" onClick={onSave} disabled={!category.name}>{categoryId ? "Save" : "Create"}</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const openCategoryModal = (categoryId: string | null, channelId: string | null) =>
|
||||
openModalLazy(async () => {
|
||||
await requireSettingsMenu();
|
||||
return modalProps => <NewCategoryModal categoryId={categoryId} modalProps={modalProps} initalChannelId={channelId} />;
|
||||
});
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
import { addChannelToCategory, canMoveChannelInDirection, categories, isPinned, moveChannel, removeChannelFromCategory } from "../data";
|
||||
import { forceUpdate, settings } from "../index";
|
||||
import { openCategoryModal } from "./CreateCategoryModal";
|
||||
|
||||
function createPinMenuItem(channelId: string) {
|
||||
const pinned = isPinned(channelId);
|
||||
|
||||
return (
|
||||
<Menu.MenuItem
|
||||
id="pin-dm"
|
||||
label="Pin DMs"
|
||||
>
|
||||
|
||||
{!pinned && (
|
||||
<>
|
||||
<Menu.MenuItem
|
||||
id="vc-add-category"
|
||||
label="Add Category"
|
||||
color="brand"
|
||||
action={() => openCategoryModal(null, channelId)}
|
||||
/>
|
||||
<Menu.MenuSeparator />
|
||||
|
||||
{
|
||||
categories.map(category => (
|
||||
<Menu.MenuItem
|
||||
id={`pin-category-${category.name}`}
|
||||
label={category.name}
|
||||
action={() => addChannelToCategory(channelId, category.id).then(forceUpdate)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
{pinned && (
|
||||
<>
|
||||
<Menu.MenuItem
|
||||
id="unpin-dm"
|
||||
label="Unpin DM"
|
||||
color="danger"
|
||||
action={() => removeChannelFromCategory(channelId).then(forceUpdate)}
|
||||
/>
|
||||
|
||||
{
|
||||
!settings.store.sortDmsByNewestMessage && canMoveChannelInDirection(channelId, -1) && (
|
||||
<Menu.MenuItem
|
||||
id="move-up"
|
||||
label="Move Up"
|
||||
action={() => moveChannel(channelId, -1).then(forceUpdate)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!settings.store.sortDmsByNewestMessage && canMoveChannelInDirection(channelId, 1) && (
|
||||
<Menu.MenuItem
|
||||
id="move-down"
|
||||
label="Move Down"
|
||||
action={() => moveChannel(channelId, 1).then(forceUpdate)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
</Menu.MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
const GroupDMContext: NavContextMenuPatchCallback = (children, props) => {
|
||||
const container = findGroupChildrenByChildId("leave-channel", children);
|
||||
container?.unshift(createPinMenuItem(props.channel.id));
|
||||
};
|
||||
|
||||
const UserContext: NavContextMenuPatchCallback = (children, props) => {
|
||||
const container = findGroupChildrenByChildId("close-dm", children);
|
||||
if (container) {
|
||||
const idx = container.findIndex(c => c?.props?.id === "close-dm");
|
||||
container.splice(idx, 0, createPinMenuItem(props.channel.id));
|
||||
}
|
||||
};
|
||||
|
||||
export const contextMenus = {
|
||||
"gdm-context": GroupDMContext,
|
||||
"user-context": UserContext
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export const DEFAULT_CHUNK_SIZE = 256;
|
||||
export const DEFAULT_COLOR = 10070709;
|
||||
|
||||
export const SWATCHES = [
|
||||
1752220,
|
||||
3066993,
|
||||
3447003,
|
||||
10181046,
|
||||
15277667,
|
||||
15844367,
|
||||
15105570,
|
||||
15158332,
|
||||
9807270,
|
||||
6323595,
|
||||
|
||||
1146986,
|
||||
2067276,
|
||||
2123412,
|
||||
7419530,
|
||||
11342935,
|
||||
12745742,
|
||||
11027200,
|
||||
10038562,
|
||||
9936031,
|
||||
5533306
|
||||
];
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
import { isPinned, movePin, PinOrder, settings, snapshotArray, togglePin } from "./settings";
|
||||
|
||||
function PinMenuItem(channelId: string) {
|
||||
const pinned = isPinned(channelId);
|
||||
const canMove = pinned && settings.store.pinOrder === PinOrder.Custom;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.MenuItem
|
||||
id="pin-dm"
|
||||
label={pinned ? "Unpin DM" : "Pin DM"}
|
||||
action={() => togglePin(channelId)}
|
||||
/>
|
||||
{canMove && snapshotArray[0] !== channelId && (
|
||||
<Menu.MenuItem
|
||||
id="move-pin-up"
|
||||
label="Move Pin Up"
|
||||
action={() => movePin(channelId, -1)}
|
||||
/>
|
||||
)}
|
||||
{canMove && snapshotArray[snapshotArray.length - 1] !== channelId && (
|
||||
<Menu.MenuItem
|
||||
id="move-pin-down"
|
||||
label="Move Pin Down"
|
||||
action={() => movePin(channelId, +1)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const GroupDMContext: NavContextMenuPatchCallback = (children, props) => {
|
||||
const container = findGroupChildrenByChildId("leave-channel", children);
|
||||
if (container)
|
||||
container.unshift(PinMenuItem(props.channel.id));
|
||||
};
|
||||
|
||||
const UserContext: NavContextMenuPatchCallback = (children, props) => {
|
||||
const container = findGroupChildrenByChildId("close-dm", children);
|
||||
if (container) {
|
||||
const idx = container.findIndex(c => c?.props?.id === "close-dm");
|
||||
container.splice(idx, 0, PinMenuItem(props.channel.id));
|
||||
}
|
||||
};
|
||||
|
||||
export const contextMenus = {
|
||||
"gdm-context": GroupDMContext,
|
||||
"user-context": UserContext
|
||||
};
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
||||
import { DEFAULT_COLOR } from "./constants";
|
||||
import { forceUpdate } from "./index";
|
||||
|
||||
export interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
color: number;
|
||||
channels: string[];
|
||||
collapsed?: boolean;
|
||||
}
|
||||
|
||||
const CATEGORY_BASE_KEY = "PinDMsCategories-";
|
||||
const CATEGORY_MIGRATED_PINDMS_KEY = "PinDMsMigratedPinDMs";
|
||||
const CATEGORY_MIGRATED_KEY = "PinDMsMigratedOldCategories";
|
||||
const OLD_CATEGORY_KEY = "BetterPinDMsCategories-";
|
||||
|
||||
|
||||
export let categories: Category[] = [];
|
||||
|
||||
export async function saveCats(cats: Category[]) {
|
||||
const { id } = UserStore.getCurrentUser();
|
||||
await DataStore.set(CATEGORY_BASE_KEY + id, cats);
|
||||
}
|
||||
|
||||
export async function init() {
|
||||
const id = UserStore.getCurrentUser()?.id;
|
||||
await initCategories(id);
|
||||
await migrateData(id);
|
||||
forceUpdate();
|
||||
}
|
||||
|
||||
export async function initCategories(userId: string) {
|
||||
categories = await DataStore.get<Category[]>(CATEGORY_BASE_KEY + userId) ?? [];
|
||||
}
|
||||
|
||||
export function getCategory(id: string) {
|
||||
return categories.find(c => c.id === id);
|
||||
}
|
||||
|
||||
export async function createCategory(category: Category) {
|
||||
categories.push(category);
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
export async function updateCategory(category: Category) {
|
||||
const index = categories.findIndex(c => c.id === category.id);
|
||||
if (index === -1) return;
|
||||
|
||||
categories[index] = category;
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
export async function addChannelToCategory(channelId: string, categoryId: string) {
|
||||
const category = categories.find(c => c.id === categoryId);
|
||||
if (!category) return;
|
||||
|
||||
if (category.channels.includes(channelId)) return;
|
||||
|
||||
category.channels.push(channelId);
|
||||
await saveCats(categories);
|
||||
|
||||
}
|
||||
|
||||
export async function removeChannelFromCategory(channelId: string) {
|
||||
const category = categories.find(c => c.channels.includes(channelId));
|
||||
if (!category) return;
|
||||
|
||||
category.channels = category.channels.filter(c => c !== channelId);
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
export async function removeCategory(categoryId: string) {
|
||||
const catagory = categories.find(c => c.id === categoryId);
|
||||
if (!catagory) return;
|
||||
|
||||
// catagory?.channels.forEach(c => removeChannelFromCategory(c));
|
||||
categories = categories.filter(c => c.id !== categoryId);
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
export async function collapseCategory(id: string, value = true) {
|
||||
const category = categories.find(c => c.id === id);
|
||||
if (!category) return;
|
||||
|
||||
category.collapsed = value;
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
// utils
|
||||
export function isPinned(id: string) {
|
||||
return categories.some(c => c.channels.includes(id));
|
||||
}
|
||||
|
||||
export function categoryLen() {
|
||||
return categories.length;
|
||||
}
|
||||
|
||||
export function getAllUncollapsedChannels() {
|
||||
return categories.filter(c => !c.collapsed).map(c => c.channels).flat();
|
||||
}
|
||||
|
||||
export function getSections() {
|
||||
return categories.reduce((acc, category) => {
|
||||
acc.push(category.channels.length === 0 ? 1 : category.channels.length);
|
||||
return acc;
|
||||
}, [] as number[]);
|
||||
}
|
||||
|
||||
// move categories
|
||||
export const canMoveArrayInDirection = (array: any[], index: number, direction: -1 | 1) => {
|
||||
const a = array[index];
|
||||
const b = array[index + direction];
|
||||
|
||||
return a && b;
|
||||
};
|
||||
|
||||
export const canMoveCategoryInDirection = (id: string, direction: -1 | 1) => {
|
||||
const index = categories.findIndex(m => m.id === id);
|
||||
return canMoveArrayInDirection(categories, index, direction);
|
||||
};
|
||||
|
||||
export const canMoveCategory = (id: string) => canMoveCategoryInDirection(id, -1) || canMoveCategoryInDirection(id, 1);
|
||||
|
||||
export const canMoveChannelInDirection = (channelId: string, direction: -1 | 1) => {
|
||||
const category = categories.find(c => c.channels.includes(channelId));
|
||||
if (!category) return false;
|
||||
|
||||
const index = category.channels.indexOf(channelId);
|
||||
return canMoveArrayInDirection(category.channels, index, direction);
|
||||
};
|
||||
|
||||
|
||||
function swapElementsInArray(array: any[], index1: number, index2: number) {
|
||||
if (!array[index1] || !array[index2]) return;
|
||||
[array[index1], array[index2]] = [array[index2], array[index1]];
|
||||
}
|
||||
|
||||
// stolen from PinDMs
|
||||
export async function moveCategory(id: string, direction: -1 | 1) {
|
||||
const a = categories.findIndex(m => m.id === id);
|
||||
const b = a + direction;
|
||||
|
||||
swapElementsInArray(categories, a, b);
|
||||
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
export async function moveChannel(channelId: string, direction: -1 | 1) {
|
||||
const category = categories.find(c => c.channels.includes(channelId));
|
||||
if (!category) return;
|
||||
|
||||
const a = category.channels.indexOf(channelId);
|
||||
const b = a + direction;
|
||||
|
||||
swapElementsInArray(category.channels, a, b);
|
||||
|
||||
await saveCats(categories);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// migrate data
|
||||
const getPinDMsPins = () => (Settings.plugins.PinDMs.pinnedDMs || void 0)?.split(",") as string[] | undefined;
|
||||
|
||||
async function migratePinDMs() {
|
||||
if (categories.some(m => m.id === "oldPins")) {
|
||||
return await DataStore.set(CATEGORY_MIGRATED_PINDMS_KEY, true);
|
||||
}
|
||||
|
||||
const pindmspins = getPinDMsPins();
|
||||
|
||||
// we dont want duplicate pins
|
||||
const difference = [...new Set(pindmspins)]?.filter(m => !categories.some(c => c.channels.includes(m)));
|
||||
if (difference?.length) {
|
||||
categories.push({
|
||||
id: "oldPins",
|
||||
name: "Pins",
|
||||
color: DEFAULT_COLOR,
|
||||
channels: difference
|
||||
});
|
||||
}
|
||||
|
||||
await DataStore.set(CATEGORY_MIGRATED_PINDMS_KEY, true);
|
||||
}
|
||||
|
||||
async function migrateOldCategories(userId: string) {
|
||||
const oldCats = await DataStore.get<Category[]>(OLD_CATEGORY_KEY + userId);
|
||||
// dont want to migrate if the user has already has categories.
|
||||
if (categories.length === 0 && oldCats?.length) {
|
||||
categories.push(...(oldCats.filter(m => m.id !== "oldPins")));
|
||||
}
|
||||
await DataStore.set(CATEGORY_MIGRATED_KEY, true);
|
||||
}
|
||||
|
||||
export async function migrateData(userId: string) {
|
||||
const m1 = await DataStore.get(CATEGORY_MIGRATED_KEY), m2 = await DataStore.get(CATEGORY_MIGRATED_PINDMS_KEY);
|
||||
if (m1 && m2) return;
|
||||
|
||||
// want to migrate the old categories first and then slove any conflicts with the PinDMs pins
|
||||
if (!m1) await migrateOldCategories(userId);
|
||||
if (!m2) await migratePinDMs();
|
||||
|
||||
await saveCats(categories);
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings, useSettings } from "@api/Settings";
|
||||
import { OptionType } from "@utils/types";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
|
||||
export const enum PinOrder {
|
||||
LastMessage,
|
||||
Custom
|
||||
}
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
pinOrder: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Which order should pinned DMs be displayed in?",
|
||||
options: [
|
||||
{ label: "Most recent message", value: PinOrder.LastMessage, default: true },
|
||||
{ label: "Custom (right click channels to reorder)", value: PinOrder.Custom }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore");
|
||||
|
||||
export let snapshotArray: string[];
|
||||
let snapshot: Set<string> | undefined;
|
||||
|
||||
const getArray = () => (Settings.plugins.PinDMs.pinnedDMs || void 0)?.split(",") as string[] | undefined;
|
||||
const save = (pins: string[]) => {
|
||||
snapshot = void 0;
|
||||
Settings.plugins.PinDMs.pinnedDMs = pins.join(",");
|
||||
};
|
||||
const takeSnapshot = () => {
|
||||
snapshotArray = getArray() ?? [];
|
||||
return snapshot = new Set<string>(snapshotArray);
|
||||
};
|
||||
const requireSnapshot = () => snapshot ?? takeSnapshot();
|
||||
|
||||
export function usePinnedDms() {
|
||||
useSettings(["plugins.PinDMs.pinnedDMs"]);
|
||||
|
||||
return requireSnapshot();
|
||||
}
|
||||
|
||||
export function isPinned(id: string) {
|
||||
return requireSnapshot().has(id);
|
||||
}
|
||||
|
||||
export function togglePin(id: string) {
|
||||
const snapshot = requireSnapshot();
|
||||
if (!snapshot.delete(id)) {
|
||||
snapshot.add(id);
|
||||
}
|
||||
|
||||
save([...snapshot]);
|
||||
}
|
||||
|
||||
export function sortedSnapshot() {
|
||||
requireSnapshot();
|
||||
if (settings.store.pinOrder === PinOrder.LastMessage)
|
||||
return PrivateChannelSortStore.getPrivateChannelIds().filter(isPinned);
|
||||
|
||||
return snapshotArray;
|
||||
}
|
||||
|
||||
export function getPinAt(idx: number) {
|
||||
return sortedSnapshot()[idx];
|
||||
}
|
||||
|
||||
export function movePin(id: string, direction: -1 | 1) {
|
||||
const pins = getArray()!;
|
||||
const a = pins.indexOf(id);
|
||||
const b = a + direction;
|
||||
|
||||
[pins[a], pins[b]] = [pins[b], pins[a]];
|
||||
|
||||
save(pins);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
.vc-pindms-section-container {
|
||||
box-sizing: border-box;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
letter-spacing: .02em;
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
flex: 1 1 auto;
|
||||
color: var(--channels-default);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vc-pindms-modal-content {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.vc-pindms-modal-content [class^="defaultContainer"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vc-pindms-collapse-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--interactive-normal);
|
||||
transform: rotate(90deg)
|
||||
}
|
||||
|
||||
.vc-pindms-collapsed .vc-pindms-collapse-icon {
|
||||
transform: rotate(0deg);
|
||||
}
|
Loading…
Reference in new issue