diff --git a/web/src/components/OrderSetting.tsx b/web/src/components/OrderSetting.tsx new file mode 100644 index 0000000..62e8a48 --- /dev/null +++ b/web/src/components/OrderSetting.tsx @@ -0,0 +1,48 @@ +import { Select, Option, Button } from "@mui/joy"; +import useViewStore from "../stores/v1/view"; +import Dropdown from "./common/Dropdown"; +import Icon from "./Icon"; + +const OrderSetting = () => { + const viewStore = useViewStore(); + const order = viewStore.getOrder(); + const { field, direction } = order; + + return ( + + + + } + actions={ +
e.stopPropagation()}> +
+ View order + +
+
+ Order by + +
+
+ Direction + +
+
+ } + >
+ ); +}; + +export default OrderSetting; diff --git a/web/src/components/common/Dropdown.tsx b/web/src/components/common/Dropdown.tsx index 07114c6..11b5997 100644 --- a/web/src/components/common/Dropdown.tsx +++ b/web/src/components/common/Dropdown.tsx @@ -21,10 +21,15 @@ const Dropdown: React.FC = (props: Props) => { toggleDropdownStatus(false); } }; + window.addEventListener("click", handleClickOutside, { capture: true, - once: true, }); + return () => { + window.removeEventListener("click", handleClickOutside, { + capture: true, + }); + }; } }, [dropdownStatus]); diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index 4ec779f..9cc869a 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -2,41 +2,19 @@ import { Button, Tab, TabList, Tabs } from "@mui/joy"; import { useEffect, useState } from "react"; import { shortcutService } from "../services"; import { useAppSelector } from "../stores"; -import useViewStore, { Filter } from "../stores/v1/view"; +import useViewStore, { getFilteredShortcutList, getOrderedShortcutList } from "../stores/v1/view"; import useUserStore from "../stores/v1/user"; import useLoading from "../hooks/useLoading"; import Icon from "../components/Icon"; import ShortcutListView from "../components/ShortcutListView"; import CreateShortcutDialog from "../components/CreateShortcutDialog"; import FilterView from "../components/FilterView"; +import OrderSetting from "../components/OrderSetting"; interface State { showCreateShortcutDialog: boolean; } -const getFilteredShortcutList = (shortcutList: Shortcut[], filter: Filter, currentUser: User) => { - const { tag, mineOnly, visibility } = filter; - const filteredShortcutList = shortcutList.filter((shortcut) => { - if (tag) { - if (!shortcut.tags.includes(tag)) { - return false; - } - } - if (mineOnly) { - if (shortcut.creatorId !== currentUser.id) { - return false; - } - } - if (visibility) { - if (shortcut.visibility !== visibility) { - return false; - } - } - return true; - }); - return filteredShortcutList; -}; - const Home: React.FC = () => { const loadingState = useLoading(); const currentUser = useUserStore().getCurrentUser(); @@ -47,6 +25,7 @@ const Home: React.FC = () => { }); const filter = viewStore.filter; const filteredShortcutList = getFilteredShortcutList(shortcutList, filter, currentUser); + const orderedShortcutList = getOrderedShortcutList(filteredShortcutList, viewStore.order); useEffect(() => { Promise.all([shortcutService.getMyAllShortcuts()]).finally(() => { @@ -69,6 +48,12 @@ const Home: React.FC = () => {
+ +
+
+ {
-
- -
@@ -94,13 +74,13 @@ const Home: React.FC = () => { loading - ) : filteredShortcutList.length === 0 ? ( + ) : orderedShortcutList.length === 0 ? (

No shortcuts found.

) : ( - + )} diff --git a/web/src/stores/v1/view.ts b/web/src/stores/v1/view.ts index 291be0c..e48a2db 100644 --- a/web/src/stores/v1/view.ts +++ b/web/src/stores/v1/view.ts @@ -7,18 +7,39 @@ export interface Filter { visibility?: Visibility; } +export interface Order { + field: "name" | "createdTs" | "updatedTs" | "view"; + direction: "asc" | "desc"; +} + interface ViewState { filter: Filter; + order: Order; setFilter: (filter: Partial) => void; + getOrder: () => Order; + setOrder: (order: Partial) => void; } const useViewStore = create()( persist( (set, get) => ({ filter: {}, + order: { + field: "name", + direction: "asc", + }, setFilter: (filter: Partial) => { set({ filter: { ...get().filter, ...filter } }); }, + getOrder: () => { + return { + field: get().order.field || "name", + direction: get().order.direction || "asc", + }; + }, + setOrder: (order: Partial) => { + set({ order: { ...get().order, ...order } }); + }, }), { name: "view", @@ -26,4 +47,48 @@ const useViewStore = create()( ) ); +export const getFilteredShortcutList = (shortcutList: Shortcut[], filter: Filter, currentUser: User) => { + const { tag, mineOnly, visibility } = filter; + const filteredShortcutList = shortcutList.filter((shortcut) => { + if (tag) { + if (!shortcut.tags.includes(tag)) { + return false; + } + } + if (mineOnly) { + if (shortcut.creatorId !== currentUser.id) { + return false; + } + } + if (visibility) { + if (shortcut.visibility !== visibility) { + return false; + } + } + return true; + }); + return filteredShortcutList; +}; + +export const getOrderedShortcutList = (shortcutList: Shortcut[], order: Order) => { + const { field, direction } = { + field: order.field || "name", + direction: order.direction || "asc", + }; + const orderedShortcutList = shortcutList.sort((a, b) => { + if (field === "name") { + return direction === "asc" ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name); + } else if (field === "createdTs") { + return direction === "asc" ? a.createdTs - b.createdTs : b.createdTs - a.createdTs; + } else if (field === "updatedTs") { + return direction === "asc" ? a.updatedTs - b.updatedTs : b.updatedTs - a.updatedTs; + } else if (field === "view") { + return direction === "asc" ? a.view - b.view : b.view - a.view; + } else { + return 0; + } + }); + return orderedShortcutList; +}; + export default useViewStore;