diff --git a/plugins/pindms/components/GuildPins.tsx b/plugins/pindms/components/GuildPins.tsx index f263213..3553e52 100644 --- a/plugins/pindms/components/GuildPins.tsx +++ b/plugins/pindms/components/GuildPins.tsx @@ -10,9 +10,13 @@ import { Store } from "replugged/dist/renderer/modules/common/flux"; import { ChannelStore, GuildChannelStore, + SelectedChannelStore, ReadStateStore, StatusStore, TypingStore, + ApplicationStreamingStore, + ChannelRTCStore, + RTCConnectionStore, } from "../stores"; const classes = webpack.getByProps<{ listItem: string; listItemWrapper: string; pill: string }>( @@ -36,6 +40,44 @@ const transitionTo = (transtionRaw && '"transitionTo - Transitioning to "', )!) as ((to: string) => void) | undefined; +function DropEndWrapper({ id }: { id: string }) { + if (!useDrop) return null; + + const [{ isOver }, drop] = useDrop(() => ({ + accept: "GUILDPIN", + drop: (item, monitor) => { + if (item.id == id) return; + const pins = pluginSettings.get("guildPins", []); + const draggedIndex = pins.findIndex((pin) => pin === item.id); + const droppedIndex = pins.findIndex((pin) => pin === id); + + if (draggedIndex == -1 || droppedIndex == -1) return; + + pluginSettings.set( + "guildPins", + pins.reduce((acc: string[], item, index) => { + if (index === draggedIndex) return acc; + if (index === droppedIndex) return [...acc, item, pins[draggedIndex]]; + return [...acc, item]; + }, []), + ); + + common.fluxDispatcher.dispatch({ type: GUILDLIST_UPDATE }); + }, + collect: (e) => ({ + isOver: e.isOver(), + }), + })); + + return ( + drop(node)} + style={{ padding: "10% 0% 10% 100%" }} + /> + ); +} + function GuildPin({ id }: { id: string }) { if ( // !useStateFromStore || @@ -43,6 +85,10 @@ function GuildPin({ id }: { id: string }) { !ReadStateStore || !StatusStore || !TypingStore || + !SelectedChannelStore || + !ApplicationStreamingStore || + !ChannelRTCStore || + !RTCConnectionStore || !useDrop || !useDrag || !Pill || @@ -56,7 +102,7 @@ function GuildPin({ id }: { id: string }) { if (!channel || !user) return null; - const [, drop] = useDrop(() => ({ + const [{ isOver }, drop] = useDrop(() => ({ accept: "GUILDPIN", drop: (item, monitor) => { if (item.id == id) return; @@ -68,22 +114,27 @@ function GuildPin({ id }: { id: string }) { pluginSettings.set( "guildPins", - pins.map((pin, index) => { - if (index === draggedIndex) return id; - else if (index === droppedIndex) return item.id; - - return pin; - }), + pins.reduce((acc: string[], item, index) => { + if (index === draggedIndex) return acc; + if (index === droppedIndex) return [...acc, pins[draggedIndex], item]; + return [...acc, item]; + }, []), ); common.fluxDispatcher.dispatch({ type: GUILDLIST_UPDATE }); }, + collect: (e) => ({ + isOver: e.isOver(), + }), })); - const [, drag] = useDrag(() => ({ + const [{ dragging }, drag] = useDrag(() => ({ type: "GUILDPIN", item: channel, options: { dropEffect: "copy" }, + collect: (e) => ({ + dragging: e.isDragging(), + }), })); const [hovered, setHovered] = useState(false); const isMobileOnline = common.flux.useStateFromStores([StatusStore], () => @@ -98,13 +149,37 @@ function GuildPin({ id }: { id: string }) { const isTyping = common.flux.useStateFromStores([TypingStore], () => TypingStore!.isTyping(channel.id, user.id), ); + const selected = common.flux.useStateFromStores( + [SelectedChannelStore], + () => SelectedChannelStore!.getCurrentlySelectedChannelId() === channel.id, + ); + const mediaInfo = common.flux.useStateFromStores<{ + video: boolean; + audio: boolean; + screenshare: boolean; + isCurrentUserConnected: boolean; + }>([ApplicationStreamingStore, ChannelRTCStore, RTCConnectionStore], () => { + const connectedVoiceChannelId = RTCConnectionStore!.getChannelId(); + const streams = ApplicationStreamingStore?.getAllApplicationStreamsForChannel(id); + const voiceChannelMode = connectedVoiceChannelId === id ? ChannelRTCStore?.getMode(id) : ""; + return { + video: voiceChannelMode === "video", + audio: voiceChannelMode === "voice", + screenshare: Boolean(streams?.length), + isCurrentUserConnected: connectedVoiceChannelId === id, + }; + }); const showStatus = pluginSettings.get("showStatus", true); return ( - +
drop(drag(node))} className={[ classes?.listItem, "pindms-guildlist-pin", @@ -113,27 +188,33 @@ function GuildPin({ id }: { id: string }) { {Pill ? ( ) : undefined} -
- setHovered(true)} - onMouseLeave={() => setHovered(false)} - onClick={() => transitionTo?.(`/channels/@me/${id}`)} - onContextMenu={(e) => - common.contextMenu.open(e, () => ) - } - /> - {Badge && (unreadCount ?? 0) > 0 ? : undefined} -
+ drop(drag(node))}> + {!dragging && ( + <> + setHovered(true)} + onMouseLeave={() => setHovered(false)} + onClick={() => transitionTo?.(`/channels/@me/${id}`)} + onContextMenu={(e) => + common.contextMenu.open(e, () => ) + } + /> + {Badge && (unreadCount ?? 0) > 0 + ? Badge.renderMentionBadge(unreadCount) + : Badge?.renderMediaBadge(mediaInfo)} + + )} +
); @@ -141,22 +222,24 @@ function GuildPin({ id }: { id: string }) { export default () => { const [guildPins, setGuildPins] = useState(pluginSettings.get("guildPins", [] as string[])); - + const [key, setKey] = useState(Date.now()); useEffect(() => { const update = () => { - setGuildPins([]); - setTimeout(() => setGuildPins([...pluginSettings.get("guildPins", [])])); + setTimeout(() => { + setGuildPins([...pluginSettings.get("guildPins", [])]); + setKey(Date.now()); + }, 500); }; common.fluxDispatcher.subscribe(GUILDLIST_UPDATE, update); return () => common.fluxDispatcher.unsubscribe(GUILDLIST_UPDATE, update); - }, []); - + }, [pluginSettings.get("guildPins", [] as string[])]); return ( - <> - {guildPins.map((id) => ( - + + {guildPins.map((id, index) => ( + ))} - + + ); }; diff --git a/plugins/pindms/components/index.ts b/plugins/pindms/components/index.ts index 9e6f1fc..1196276 100644 --- a/plugins/pindms/components/index.ts +++ b/plugins/pindms/components/index.ts @@ -94,9 +94,19 @@ export const StatusBlob = AvatarRaw?.X as FC | undefined; const BadgeRaw = webpack.getBySource('"count","color","disableColor","shape","className","style"'); -export const Badge = webpack.getByProps<{ NumberBadge: FC<{ count: number }> }>( - "NumberBadge", -)?.NumberBadge; +export const Badge = webpack.getByProps<{ + renderMediaBadge: (props: { + activeEvent?: boolean; + activity?: boolean; + audio?: boolean; + gaming?: boolean; + isCurrentUserConnected?: boolean; + liveStage?: boolean; + screenshare?: boolean; + video?: boolean; + }) => JSX.Element; + renderMentionBadge: (count: number, color?: string) => JSX.Element; +}>("renderMediaBadge", "renderMentionBadge"); export const SearchBar = webpack.getBySource< FC<{ className: string; query: string; onChange: (value: string) => void; onClear: () => void }> diff --git a/plugins/pindms/index.tsx b/plugins/pindms/index.tsx index 8fe35a7..5b8abbd 100644 --- a/plugins/pindms/index.tsx +++ b/plugins/pindms/index.tsx @@ -161,7 +161,7 @@ function patchUnreadDMs(component: JSX.Element) { }, []); res.props.children = res.props.children.filter( - (c: ReactElement) => !pins.includes(c.key as string), + (c: ReactElement) => !pins.includes(c?.key as string), ); return res; diff --git a/plugins/pindms/stores.tsx b/plugins/pindms/stores.tsx index a5e348e..8ee72c5 100644 --- a/plugins/pindms/stores.tsx +++ b/plugins/pindms/stores.tsx @@ -21,3 +21,19 @@ export const ChannelStore = webpack.getByStoreName< export const GuildChannelStore = webpack.getByStoreName< Store & { getChannels: (guildId: string) => { SELECTABLE: Array<{ channel: Channel }> } } >("GuildChannelStore"); + +export const SelectedChannelStore = webpack.getByStoreName< + Store & { getCurrentlySelectedChannelId: () => string } +>("SelectedChannelStore"); + +export const ApplicationStreamingStore = webpack.getByStoreName< + Store & { getAllApplicationStreamsForChannel: (channelId: string) => unknown[] } +>("ApplicationStreamingStore"); + +export const ChannelRTCStore = webpack.getByStoreName< + Store & { getMode: (channelId: string) => string } +>("ChannelRTCStore"); + +export const RTCConnectionStore = webpack.getByStoreName string }>( + "RTCConnectionStore", +); diff --git a/plugins/pindms/style.css b/plugins/pindms/style.css index 0a2f6e4..c60c677 100644 --- a/plugins/pindms/style.css +++ b/plugins/pindms/style.css @@ -102,3 +102,21 @@ position: absolute; top: 0; } + +.pindms-guildlist-pin [class*="iconBadge"] { + right: 8px; + position: absolute; + top: 0; +} + +.pindms-guildList-dragTarget:before { + content: ""; + position: absolute; + z-index: 1; + left: 4px; + right: 4px; + height: 4px; + background-color: var(--green-360); + border-radius: 2px; + box-shadow: 0 0 3px hsl(var(--black-500-hsl) / 0.3); +}