diff --git a/src/components/floating-profile/FloatingProfile.module.scss b/src/components/floating-profile/FloatingProfile.module.scss index dade25fb..5cd17419 100644 --- a/src/components/floating-profile/FloatingProfile.module.scss +++ b/src/components/floating-profile/FloatingProfile.module.scss @@ -211,3 +211,12 @@ body .postItemContainer { font-weight: bold; gap: 5px; } + +.buttonsContainer { + display: flex; + margin-top: 4px; + gap: 4px; + .button { + flex: 1; + } +} diff --git a/src/components/floating-profile/FloatingProfile.tsx b/src/components/floating-profile/FloatingProfile.tsx index 069ab6bd..468473c4 100644 --- a/src/components/floating-profile/FloatingProfile.tsx +++ b/src/components/floating-profile/FloatingProfile.tsx @@ -51,6 +51,9 @@ import { t } from "i18next"; import { PostItem } from "../post-area/PostItem"; import { Skeleton } from "../ui/skeleton/Skeleton"; import average from "@/common/chromaJS"; +import Button from "../ui/Button"; +import { FlexRow } from "../ui/Flexbox"; +import { emitDrawerGoToMain } from "@/common/GlobalEvents"; interface Props { dmPane?: boolean; @@ -292,6 +295,10 @@ const DesktopProfileFlyout = (props: { /> )); }; + const onMessageClicked = () => { + users.openDM(props.userId); + emitDrawerGoToMain(); + }; const StickyArea = () => { return ( @@ -317,6 +324,7 @@ const DesktopProfileFlyout = (props: { size={82} /> +
:{user()!.tag} + +
+
diff --git a/src/components/message-pane/MessagePane.tsx b/src/components/message-pane/MessagePane.tsx index ef9d4c73..5e80c94c 100644 --- a/src/components/message-pane/MessagePane.tsx +++ b/src/components/message-pane/MessagePane.tsx @@ -75,6 +75,7 @@ import Checkbox from "../ui/Checkbox"; import { ChannelIcon } from "../ChannelIcon"; import { MetaTitle } from "@/common/MetaTitle"; import { millisecondsToReadable } from "@/common/date"; +import { useResizeObserver } from "@/common/useResizeObserver"; const DeleteMessageModal = lazy( () => import("./message-delete-modal/MessageDeleteModal") @@ -174,6 +175,8 @@ function MessageArea(props: { const [showEmojiPicker, setShowEmojiPicker] = createSignal(false); const { createPortal } = useCustomPortal(); + const { height } = useResizeObserver(textAreaEl); + const { channels, messages, serverMembers } = useStore(); const setMessage = (content: string) => { @@ -198,7 +201,7 @@ function MessageArea(props: { } }); - createEffect(on(message, () => adjustHeight())); + createEffect(on([message, height], () => adjustHeight())); const cancelEdit = () => { channelProperties.setEditMessage(params.channelId, undefined); diff --git a/src/components/right-drawer/RightDrawer.tsx b/src/components/right-drawer/RightDrawer.tsx index ee16edc1..a50b2f56 100644 --- a/src/components/right-drawer/RightDrawer.tsx +++ b/src/components/right-drawer/RightDrawer.tsx @@ -1,25 +1,36 @@ import styles from "./styles.module.scss"; import Avatar from "@/components/ui/Avatar"; import UserPresence from "@/components/user-presence/UserPresence"; -import {useParams} from "solid-navigator"; +import { useParams } from "solid-navigator"; import useStore from "@/chat-api/store/useStore"; -import {createEffect, createMemo, createSignal, For, JSX, mapArray, on, onCleanup, onMount, Show} from "solid-js"; -import {ServerMember} from "@/chat-api/store/useServerMembers"; +import { + createEffect, + createMemo, + createSignal, + For, + JSX, + mapArray, + on, + onCleanup, + onMount, + Show, +} from "solid-js"; +import { ServerMember } from "@/chat-api/store/useServerMembers"; import MemberContextMenu from "../member-context-menu/MemberContextMenu"; -import {DrawerHeader} from "@/components/drawer-header/DrawerHeader"; -import {useCustomPortal} from "@/components/ui/custom-portal/CustomPortal"; -import {css} from "solid-styled-components"; -import {bannerUrl} from "@/chat-api/store/useUsers"; +import { DrawerHeader } from "@/components/drawer-header/DrawerHeader"; +import { useCustomPortal } from "@/components/ui/custom-portal/CustomPortal"; +import { css } from "solid-styled-components"; +import { bannerUrl } from "@/chat-api/store/useUsers"; import Text from "@/components/ui/Text"; import Icon from "@/components/ui/icon/Icon"; import Button from "@/components/ui/Button"; -import {Banner} from "@/components/ui/Banner"; -import {fetchChannelAttachments} from "@/chat-api/services/MessageService"; -import {RawAttachment, RawMessage} from "@/chat-api/RawData"; +import { Banner } from "@/components/ui/Banner"; +import { fetchChannelAttachments } from "@/chat-api/services/MessageService"; +import { RawAttachment, RawMessage } from "@/chat-api/RawData"; import env from "@/common/env"; -import {classNames, conditionalClass} from "@/common/classNames"; +import { classNames, conditionalClass } from "@/common/classNames"; import socketClient from "@/chat-api/socketClient"; -import {ServerEvents} from "@/chat-api/EventNames"; +import { ServerEvents } from "@/chat-api/EventNames"; import { emitScrollToMessage } from "@/common/GlobalEvents"; import { Skeleton } from "../ui/skeleton/Skeleton"; import { ProfileFlyout } from "../floating-profile/FloatingProfile"; @@ -33,7 +44,9 @@ const MemberItem = (props: { member: ServerMember }) => { const params = useParams<{ serverId: string }>(); const user = () => props.member.user(); let elementRef: undefined | HTMLDivElement; - const [contextPosition, setContextPosition] = createSignal<{ x: number, y: number } | undefined>(undefined); + const [contextPosition, setContextPosition] = createSignal< + { x: number; y: number } | undefined + >(undefined); const [hovering, setHovering] = createSignal(false); const { createPortal, isPortalOpened } = useCustomPortal(); @@ -41,7 +54,6 @@ const MemberItem = (props: { member: ServerMember }) => { return isPortalOpened("profile-pane-flyout-" + user().id); }; - const onContextMenu = (event: MouseEvent) => { event.preventDefault(); setContextPosition({ x: event.clientX, y: event.clientY }); @@ -49,65 +61,114 @@ const MemberItem = (props: { member: ServerMember }) => { const onClick = (e: MouseEvent) => { const rect = elementRef?.getBoundingClientRect()!; - return createPortal(close => , "profile-pane-flyout-" + user().id, true); + return createPortal( + (close) => ( + + ), + "profile-pane-flyout-" + user().id, + true + ); }; return ( -
setHovering(true)} onMouseLeave={() => setHovering(false)} > - setContextPosition(undefined)} /> -
- +
setHovering(true)} + onMouseLeave={() => setHovering(false)} + > + setContextPosition(undefined)} + /> +
+
-
{props.member.nickname || user().username}
- +
+ {props.member.nickname || user().username} +
+
); }; - const Header = () => { - - return (); + return ; }; - const RightDrawer = () => { - const params = useParams<{ serverId?: string; channelId?: string; }>(); + const params = useParams<{ serverId?: string; channelId?: string }>(); const [showAttachments, setShowAttachments] = createSignal(false); - createEffect(on(() => params.channelId, () => { - setShowAttachments(false); - })); + createEffect( + on( + () => params.channelId, + () => { + setShowAttachments(false); + } + ) + ); return (
- setShowAttachments(true)} /> - setShowAttachments(false)} /> + + setShowAttachments(true)} /> + + + setShowAttachments(false)} + /> +
); }; - - const AttachmentDrawer = (props: { onHideAttachmentClick(): void }) => { - const params = useParams<{ serverId?: string; channelId?: string; }>(); + const params = useParams<{ serverId?: string; channelId?: string }>(); const { channels } = useStore(); - const [attachments, setAttachments] = createSignal(null); + const [attachments, setAttachments] = createSignal( + null + ); const incrAttachments = (channelId: string) => { const channel = channels.get(channelId); - const count = (channel?._count?.attachments || 0); + const count = channel?._count?.attachments || 0; channel?.update({ _count: { attachments: count + 1 } }); }; const decrAttachments = (channelId: string) => { const channel = channels.get(channelId); - const count = (channel?._count?.attachments || 1); + const count = channel?._count?.attachments || 1; channel?.update({ _count: { attachments: count - 1 } }); }; @@ -123,16 +184,20 @@ const AttachmentDrawer = (props: { onHideAttachmentClick(): void }) => { if (!attachment) return; setAttachments([ { ...attachment, messageId: payload.message.id }, - ...attachments()! + ...attachments()!, ]); incrAttachments(params.channelId); }; socketClient.useSocketOn(ServerEvents.MESSAGE_CREATED, onMessage); - const onDelete = (payload: { messageId: string, channelId: string }) => { + const onDelete = (payload: { messageId: string; channelId: string }) => { if (!attachments()) return; if (payload.channelId !== params.channelId) return; - setAttachments(attachments()!.filter(attachment => attachment.messageId !== payload.messageId)); + setAttachments( + attachments()!.filter( + (attachment) => attachment.messageId !== payload.messageId + ) + ); decrAttachments(params.channelId); }; socketClient.useSocketOn(ServerEvents.MESSAGE_DELETED, onDelete); @@ -141,28 +206,26 @@ const AttachmentDrawer = (props: { onHideAttachmentClick(): void }) => { <>
- {member => } + {(member) => }
); } - - const ServerChannelNotice = () => { const params = useParams<{ channelId: string }>(); @@ -376,15 +509,15 @@ const ServerChannelNotice = () => {
- + {t("informationDrawer.channelNotice")}
-
+
+ +
); }; export default RightDrawer; - - diff --git a/src/components/right-drawer/styles.module.scss b/src/components/right-drawer/styles.module.scss index b6f88dae..56598246 100644 --- a/src/components/right-drawer/styles.module.scss +++ b/src/components/right-drawer/styles.module.scss @@ -47,6 +47,7 @@ gap: 4px; } .roleExpandButton { + margin: 0; margin-left: 8px; } .roleCount { diff --git a/src/components/ui/Button.module.scss b/src/components/ui/Button.module.scss new file mode 100644 index 00000000..b8775b48 --- /dev/null +++ b/src/components/ui/Button.module.scss @@ -0,0 +1,32 @@ +.container { + all: unset; + display: flex; + align-items: center; + flex-shrink: 0; + justify-content: center; + padding: 5px; + + margin: 5px; + border: solid 1px rgba(255, 255, 255, 0.1); + border-radius: 6px; + color: white; + background-color: rgba(255, 255, 255, 0.08); + cursor: pointer; + user-select: none; + transition: 0.2s background-color, 0.2s opacity; + text-align: center; + &:focus { + background-color: rgba(255, 255, 255, 0.15); + } + &:hover { + background-color: rgba(255, 255, 255, 0.15); + } + &.primary { + &:hover { + opacity: 0.6; + } + } + :nth-child(2) { + margin-left: 5px; + } +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index 80854704..5a9c5100 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -1,131 +1,108 @@ import Icon from "@/components/ui/icon/Icon"; -import { JSX, JSXElement } from "solid-js"; -import { styled } from "solid-styled-components"; +import { JSX, JSXElement, splitProps } from "solid-js"; import Text from "./Text"; +import { cn } from "@/common/classNames"; +import style from "./Button.module.scss"; +import { Dynamic } from "solid-js/web"; +import { A } from "solid-navigator"; -export interface ButtonProps { - onMouseLeave?: (event: MouseEvent) => void; - onMouseEnter?: (event: MouseEvent) => void; - onPointerEnter?: (event: PointerEvent) => void; - onPointerLeave?: (event: PointerEvent) => void; - onPointerMove?: (event: PointerEvent) => void; - onTouchMove?: (event: TouchEvent) => void; - onTouchStart?: (event: TouchEvent) => void; - color?: string; - class?: string; +export type ButtonProps = Omit< + JSX.ButtonHTMLAttributes, + "color" +> & { + color?: string | null; label?: string; margin?: number | number[]; padding?: number | number[]; iconSize?: number; textSize?: number; iconName?: string; - onClick?: (event: MouseEvent) => void; - onPointerDown?: (event: PointerEvent) => void; - onPointerUp?: (event: PointerEvent) => void; - onContextMenu?: (event: MouseEvent) => void; primary?: boolean; customChildren?: JSXElement; customChildrenLeft?: JSXElement; + href?: string; styles?: JSX.CSSProperties; tabIndex?: string; hoverText?: string; iconClass?: string; alert?: boolean; -} - -const ButtonContainer = styled("button")<{ - padding?: number | number[]; - margin?: number | number[]; - primary?: boolean; -}>` - all: unset; - display: flex; - text-align: center; - align-items: center; - justify-content: center; - border-radius: 6px; - flex-shrink: 0; - padding: ${(props) => - props.padding !== undefined - ? typeof props.padding === "object" - ? props.padding.join("px ") - : props.padding - : 5}px; - margin: ${(props) => - props.margin !== undefined - ? typeof props.margin === "object" - ? props.margin.join("px ") - : props.margin - : 5}px; - color: white; - cursor: pointer; - user-select: none; - transition: 0.2s background-color, 0.2s opacity; - background-color: rgba(255, 255, 255, 0.08); - border: solid 1px rgba(255, 255, 255, 0.1); - - &:hover { - background-color: rgba(255, 255, 255, 0.15); - ${(props) => props.primary ? "opacity: 0.6" : undefined}; - } - &:focus { - background-color: rgba(255, 255, 255, 0.15); - } - - :nth-child(2) { - margin-left: 5px; - } -`; +}; export default function Button(props: ButtonProps) { + const [customProps, ogProps] = splitProps(props, [ + "color", + "label", + "margin", + "padding", + "iconSize", + "textSize", + "iconName", + "primary", + "customChildren", + "href", + "customChildrenLeft", + "styles", + "tabIndex", + "hoverText", + "iconClass", + "alert", + ]); const color = () => - props.alert ? "var(--alert-color)" : props.color || "var(--primary-color)"; + customProps.alert + ? "var(--alert-color)" + : customProps.color || "var(--primary-color)"; - const style = () => ({ - ...(props.primary ? { "background-color": color() } : undefined), - ...props.styles, + const btnStyle = () => ({ + ...(customProps.primary ? { "background-color": color() } : {}), + margin: + props.margin !== undefined + ? typeof props.margin === "object" + ? props.margin.join("px ") + "px" + : props.margin + "px" + : undefined, + padding: + props.padding !== undefined + ? typeof props.padding === "object" + ? props.padding.join("px ") + "px" + : props.padding + "px" + : undefined, + ...customProps.styles, }); return ( - - {props.customChildrenLeft && props.customChildrenLeft} - {props.iconName && ( + {customProps.customChildrenLeft && customProps.customChildrenLeft} + {customProps.iconName && ( )} - {props.label && ( + {customProps.label && ( - {props.label} + {customProps.label} )} - {props.customChildren && props.customChildren} - + {customProps.customChildren && customProps.customChildren} + ); }