diff --git a/external/lang b/external/lang index 46a2dcdd..8755aa48 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 46a2dcdd88c42d4e6e504ca5c60a4beebc8d0280 +Subproject commit 8755aa48b2173f1f509e8fe6edaccc5ac32601f5 diff --git a/package.json b/package.json index c36abdc1..e264e2a4 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,6 @@ "@types/react-router-dom": "^5.1.7", "@types/react-scroll": "^1.8.2", "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/react-window": "^1.8.5", "@types/styled-components": "^5.1.10", "@types/twemoji": "^12.1.1", "@typescript-eslint/eslint-plugin": "^4.27.0", @@ -114,7 +113,7 @@ "react-router-dom": "^5.2.0", "react-scroll": "^1.8.2", "react-virtualized-auto-sizer": "^1.0.5", - "react-window": "^1.8.6", + "react-virtuoso": "^1.10.4", "redux": "^4.1.0", "revolt-api": "0.5.1-alpha.10-patch.0", "revolt.js": "5.0.0-alpha.20", diff --git a/src/components/navigation/right/MemberList.tsx b/src/components/navigation/right/MemberList.tsx index 9bf2f83e..3e15341a 100644 --- a/src/components/navigation/right/MemberList.tsx +++ b/src/components/navigation/right/MemberList.tsx @@ -1,135 +1,92 @@ -import AutoSizer from "react-virtualized-auto-sizer"; -import { VariableSizeList as List } from "react-window"; +import { GroupedVirtuoso } from "react-virtuoso"; import { Channel } from "revolt.js/dist/maps/Channels"; import { User } from "revolt.js/dist/maps/Users"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { Text } from "preact-i18n"; -import { forwardRef } from "preact/compat"; -import { - Screen, - useIntermediate, -} from "../../../context/intermediate/Intermediate"; +import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { UserButton } from "../items/ButtonItem"; -export type MemberListEntry = string | User; -interface ItemData { - entries: MemberListEntry[]; - context: Channel; - openScreen: (screen: Screen) => void; -} +export type MemberListGroup = { + type: "online" | "offline" | "role"; + name?: string; + users: User[]; +}; -const PADDING_SIZE = 6; - -const ListCategory = styled.div` - height: 100%; - display: flex; - padding: 0 14px; +const ListCategory = styled.div<{ first?: boolean }>` + opacity: 0.8; font-size: 0.8em; font-weight: 600; user-select: none; - flex-direction: column; - justify-content: flex-end; + + padding: 4px 14px; + padding-top: 12px; + color: var(--secondary-foreground); + background: var(--secondary-background); + + ${(props) => + !props.first && + css` + padding-top: 16px; + `} `; -const Row = ({ - data, - style: styleIn, - index, -}: { - data: ItemData; - index: number; - style: JSX.CSSProperties; -}) => { - const item = data.entries[index]; - const style = { - ...styleIn, - top: `${parseFloat(styleIn.top as string) + PADDING_SIZE}px`, - }; - - if (typeof item === "string") { - const [cat, count] = item.split(":"); - return ( -
- - {cat === "online" ? ( - - ) : ( - - )} - {" - "} - {count} - -
- ); - // eslint-disable-next-line - } else { - return ( -
- - data.openScreen({ - id: "profile", - user_id: item._id, - }) - } - /> -
- ); - } -}; - -// @ts-expect-error Copied directly from example code. -const innerElementType = forwardRef(({ style, ...rest }, ref) => ( -
-)); - export default function MemberList({ entries, context, }: { - entries: MemberListEntry[]; + entries: MemberListGroup[]; context: Channel; }) { const { openScreen } = useIntermediate(); + return ( - - {({ width, height }) => ( - - typeof entries[index] === "string" ? 24 : 42 - } - estimatedItemSize={42}> - { - // eslint-disable-next-line - Row as any - } - - )} - + x.users.length)} + groupContent={(index) => { + const type = entries[index].type; + return ( + + {type === "online" ? ( + + ) : ( + + )} + {" - "} + {entries[index].users.length} + + ); + }} + itemContent={(absoluteIndex, groupIndex) => { + const relativeIndex = + absoluteIndex - + entries + .slice(0, groupIndex) + .reduce((a, b) => a + b.users.length, 0); + + const item = entries[groupIndex].users[relativeIndex]; + if (!item) return null; + + return ( +
+ + openScreen({ + id: "profile", + user_id: item._id, + }) + } + /> +
+ ); + }} + /> ); } diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index da082dfc..0bc77a1d 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -14,7 +14,7 @@ import { } from "../../../context/revoltjs/RevoltClient"; import { GenericSidebarBase } from "../SidebarBase"; -import MemberList from "./MemberList"; +import MemberList, { MemberListGroup } from "./MemberList"; export default function MemberSidebar({ channel: obj }: { channel?: Channel }) { const { channel: channel_id } = useParams<{ channel: string }>(); @@ -73,20 +73,20 @@ function useEntries(channel: Channel, keys: string[], isServer?: boolean) { categories[key].sort((a, b) => a[1].localeCompare(b[1])), ); - const entries = []; + const entries: MemberListGroup[] = []; if (categories.online.length > 0) { - entries.push( - `online:${categories.online.length}`, - ...categories.online.map((x) => x[0]), - ); + entries.push({ + type: "online", + users: categories.online.map((x) => x[0]), + }); } if (categories.offline.length > 0) { - entries.push( - `offline:${categories.offline.length}`, - ...categories.offline.map((x) => x[0]), - ); + entries.push({ + type: "offline", + users: categories.offline.map((x) => x[0]), + }); } return entries; diff --git a/yarn.lock b/yarn.lock index 59823df0..6334c65b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -872,13 +872,6 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.14.8": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" - integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.14.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" @@ -886,6 +879,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" + integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.13", "@babel/template@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -1473,13 +1473,6 @@ dependencies: "@types/react" "*" -"@types/react-window@^1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1" - integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw== - dependencies: - "@types/react" "*" - "@types/react@*": version "17.0.13" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.13.tgz#6b7c9a8f2868586ad87d941c02337c6888fb874f" @@ -1607,6 +1600,18 @@ "@typescript-eslint/types" "4.28.2" eslint-visitor-keys "^2.0.0" +"@virtuoso.dev/react-urx@^0.2.5": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.6.tgz#e1d8bc717723b2fc23d80ea4e07703dbc276448b" + integrity sha512-+PLQ2iWmSH/rW7WGPEf+Kkql+xygHFL43Jij5aREde/O9mE0OFFGqeetA2a6lry3LDVWzupPntvvWhdaYw0TyA== + dependencies: + "@virtuoso.dev/urx" "^0.2.6" + +"@virtuoso.dev/urx@^0.2.5", "@virtuoso.dev/urx@^0.2.6": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.6.tgz#0028c49e52037e673993900d32abea83262fbd53" + integrity sha512-EKJ0WvJgWaXIz6zKbh9Q63Bcq//p8OHXHbdz4Fy+ruhjJCyI8ADE8E5gwSqBoUchaiYlgwKrT+sX4L2h/H+hMg== + acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" @@ -3047,11 +3052,6 @@ mdurl@^1.0.1: sdp-transform "^2.14.1" supports-color "^8.1.1" -"memoize-one@>=3.1.1 <6": - version "5.2.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" - integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -3475,13 +3475,14 @@ react-virtualized-auto-sizer@^1.0.5: resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz#9eeeb8302022de56fbd7a860b08513120ce36509" integrity sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA== -react-window@^1.8.6: - version "1.8.6" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112" - integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg== +react-virtuoso@^1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-1.10.4.tgz#c7ec91f98c3e65f3d98a41df1e01d305b523ba0a" + integrity sha512-rWXr61gASl+KDEwakwgxoSL+uwuevSsu1wQyDzeCdIN+PgbGfLOcvEsFoTt96E4+VWQ5BawZYkh8iNUgQpucXw== dependencies: - "@babel/runtime" "^7.0.0" - memoize-one ">=3.1.1 <6" + "@virtuoso.dev/react-urx" "^0.2.5" + "@virtuoso.dev/urx" "^0.2.5" + resize-observer-polyfill "^1.5.1" readdirp@~3.6.0: version "3.6.0"