diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json
index 96e0f04531..a1d2276ba5 100644
--- a/packages/shared-components/src/i18n/strings/en_EN.json
+++ b/packages/shared-components/src/i18n/strings/en_EN.json
@@ -51,7 +51,8 @@
"sort": "Sort",
"sort_type": {
"activity": "Activity",
- "atoz": "A-Z"
+ "atoz": "A-Z",
+ "unread_first": "Unread first"
},
"space_menu": {
"home": "Space home",
diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx
index 4659625555..5da7cf8d37 100644
--- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx
+++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx
@@ -19,7 +19,7 @@ import styles from "./RoomListHeaderView.module.css";
/**
* The available sorting options for the room list.
*/
-export type SortOption = "recent" | "alphabetical";
+export type SortOption = "recent" | "alphabetical" | "unread-first";
export interface RoomListHeaderViewSnapshot {
/**
diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.test.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.test.tsx
index f067c1db3b..83f0703c8a 100644
--- a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.test.tsx
+++ b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.test.tsx
@@ -36,6 +36,7 @@ describe("", () => {
expect(screen.getByRole("menuitemradio", { name: "A-Z" })).toBeChecked();
expect(screen.getByRole("menuitemradio", { name: "Activity" })).not.toBeChecked();
+ expect(screen.getByRole("menuitemradio", { name: "Unread first" })).not.toBeChecked();
});
it("should show Activity selected if activeSortOption is recent", async () => {
@@ -49,9 +50,25 @@ describe("", () => {
await user.click(button);
expect(screen.getByRole("menuitemradio", { name: "A-Z" })).not.toBeChecked();
+ expect(screen.getByRole("menuitemradio", { name: "Unread first" })).not.toBeChecked();
expect(screen.getByRole("menuitemradio", { name: "Activity" })).toBeChecked();
});
+ it("should show `Unread First` selected if activeSortOption is unread-first", async () => {
+ const user = userEvent.setup();
+
+ const vm = new MockedViewModel({ ...defaultSnapshot, activeSortOption: "unread-first" });
+ render();
+
+ // Open the menu
+ const button = screen.getByRole("button", { name: "Room Options" });
+ await user.click(button);
+
+ expect(screen.getByRole("menuitemradio", { name: "A-Z" })).not.toBeChecked();
+ expect(screen.getByRole("menuitemradio", { name: "Activity" })).not.toBeChecked();
+ expect(screen.getByRole("menuitemradio", { name: "Unread first" })).toBeChecked();
+ });
+
it("should sort A to Z", async () => {
const user = userEvent.setup();
@@ -78,6 +95,19 @@ describe("", () => {
expect(vm.sort).toHaveBeenCalledWith("recent");
});
+ it("should sort by unread first", async () => {
+ const user = userEvent.setup();
+
+ const vm = new MockedViewModel({ ...defaultSnapshot, activeSortOption: "recent" });
+ render();
+
+ await user.click(screen.getByRole("button", { name: "Room Options" }));
+
+ await user.click(screen.getByRole("menuitemradio", { name: "Unread first" }));
+
+ expect(vm.sort).toHaveBeenCalledWith("unread-first");
+ });
+
it("should toggle message preview", async () => {
const user = userEvent.setup();
diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.tsx
index bf6a5d80c2..3509d841e4 100644
--- a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.tsx
+++ b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.tsx
@@ -60,6 +60,11 @@ export function OptionMenuView({ vm }: OptionMenuViewProps): JSX.Element {
checked={activeSortOption === "recent"}
onSelect={() => vm.sort("recent")}
/>
+ vm.sort("unread-first")}
+ />
{
- const sortingAlgorithm = option === "recent" ? SortingAlgorithm.Recency : SortingAlgorithm.Alphabetic;
+ let sortingAlgorithm: SortingAlgorithm;
+ switch (option) {
+ case "alphabetical":
+ sortingAlgorithm = SortingAlgorithm.Alphabetic;
+ break;
+ case "recent":
+ sortingAlgorithm = SortingAlgorithm.Recency;
+ break;
+ case "unread-first":
+ sortingAlgorithm = SortingAlgorithm.Unread;
+ break;
+ }
RoomListStoreV3.instance.resort(sortingAlgorithm);
this.snapshot.merge({ activeSortOption: option });
};
@@ -192,8 +203,20 @@ export class RoomListHeaderViewModel
*/
function getInitialSnapshot(spaceStore: SpaceStoreClass, matrixClient: MatrixClient): RoomListHeaderViewSnapshot {
const sortingAlgorithm = SettingsStore.getValue("RoomList.preferredSorting");
- const activeSortOption =
- sortingAlgorithm === SortingAlgorithm.Recency ? ("recent" as const) : ("alphabetical" as const);
+
+ let activeSortOption: SortOption;
+ switch (sortingAlgorithm) {
+ case SortingAlgorithm.Alphabetic:
+ activeSortOption = "alphabetical";
+ break;
+ case SortingAlgorithm.Recency:
+ activeSortOption = "recent";
+ break;
+ case SortingAlgorithm.Unread:
+ activeSortOption = "unread-first";
+ break;
+ }
+
const isMessagePreviewEnabled = SettingsStore.getValue("RoomList.showMessagePreview");
return {
diff --git a/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts b/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts
index 3d6083bc36..ca96762fa8 100644
--- a/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts
+++ b/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts
@@ -271,6 +271,7 @@ describe("RoomListHeaderViewModel", () => {
it.each([
["recent" as const, SortingAlgorithm.Recency],
["alphabetical" as const, SortingAlgorithm.Alphabetic],
+ ["unread-first" as const, SortingAlgorithm.Unread],
])("should resort when sort is called with '%s'", (option, expectedAlgorithm) => {
const resortSpy = jest.spyOn(RoomListStoreV3.instance, "resort").mockImplementation(jest.fn());
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });