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"