diff --git a/apps/web/src/components/group-header/index.js b/apps/web/src/components/group-header/index.js
index 76b176118..19b237f18 100644
--- a/apps/web/src/components/group-header/index.js
+++ b/apps/web/src/components/group-header/index.js
@@ -1,72 +1,130 @@
import * as Icon from "../icons";
-import React, { useState } from "react";
-import { Flex, Text } from "rebass";
+import React, { useMemo } from "react";
+import { Button, Flex, Text } from "rebass";
import Animated from "../animated";
-import { usePersistentState } from "../../utils/hooks";
-import { useStore as useNoteStore } from "../../stores/note-store";
-import { useTheme } from "emotion-theming";
+import { db } from "../../common/db";
+import { useOpenContextMenu } from "../../utils/useContextMenu";
+import { useStore as useSettingsStore } from "../../stores/setting-store";
+import useMobile from "../../utils/use-mobile";
-const groups = [
- { type: undefined, title: "Default" },
- { type: "abc", title: "Alphabetic" },
- { type: "year", title: "Year" },
- { type: "week", title: "Week" },
- { type: "month", title: "Month" },
+const groupByToTitleMap = {
+ [undefined]: "Default",
+ default: "Default",
+ abc: "A - Z",
+ year: "Year",
+ week: "Week",
+ month: "Month",
+};
+
+const menuItems = [
+ {
+ key: "sortDirection:asc",
+ title: () => "Order by ascending",
+ },
+ { key: "sortDirection:desc", title: () => "Order by descending" },
+ { key: "orderSeperator", type: "seperator" },
+ {
+ key: "sortBy:dateCreated",
+ title: () => "Sort by date created",
+ },
+ {
+ key: "sortBy:dateEdited",
+ title: () => "Sort by date edited",
+ },
+ {
+ key: "sortBy:title",
+ title: () => "Sort by title",
+ },
+ { key: "sortSeperator", type: "seperator" },
+ { key: "groupBy:default", title: () => "Group by default" },
+ { key: "groupBy:year", title: () => "Group by year" },
+ { key: "groupBy:month", title: () => "Group by month" },
+ { key: "groupBy:week", title: () => "Group by week" },
+ { key: "groupBy:abc", title: () => "Group by A - Z" },
];
-function getGroupTitleByType(type) {
- return groups.find((v) => v.type === type).title;
+function changeGroupOptions({ groupOptions, type, refresh }, { key: itemKey }) {
+ let [key, value] = itemKey.split(":");
+ groupOptions[key] = value;
+ if (key === "groupBy") {
+ if (value === "abc") groupOptions.sortBy = "title";
+ else groupOptions.sortBy = "dateEdited";
+}
+ db.settings.setGroupOptions(type, groupOptions);
+ refresh();
}
+const getMenuItems = (groupOptions) => {
+ return menuItems.map((item) => {
+ if (item.type === "seperator") return item;
+ let [key, value] = item.key.split(":");
+
+ item.checked = groupOptions[key] === value;
+
+ if (key === "sortBy") {
+ if (value === "title")
+ item.disabled = () => groupOptions.groupBy !== "abc";
+ else item.disabled = () => groupOptions.groupBy === "abc";
+ }
+
+ item.onClick = changeGroupOptions;
+ return item;
+ });
+};
+
function GroupHeader(props) {
- const { title, groups, onJump, wasJumpedTo, index } = props;
- const [selectedGroup] = usePersistentState("selectedGroup", undefined);
- const [isExpanded, setIsExpanded] = useState(false);
- const [menuType, setMenuType] = useState();
- const theme = useTheme();
+ const { title, groups, onJump, index, type, refresh } = props;
+ const groupOptions = useMemo(() => db.settings.getGroupOptions(type), [type]);
+ const openContextMenu = useOpenContextMenu();
+
if (!title) return null;
return (
{
+ onClick={(e) => {
if (groups.length <= 0) return;
- setMenuType("jumpto");
- setIsExpanded((s) => !s);
+ e.stopPropagation();
+ const items = groups.map((group) => ({
+ key: group.title,
+ title: () => group.title,
+ onClick: () => onJump(group.title),
+ checked: group.title === title,
+ }));
+ openContextMenu(e, items, {
+ title: "Jump to group",
+ });
}}
- >
- 0 ? [2, "8px"] : 1}
alignItems="center"
justifyContent="space-between"
- sx={{ borderBottom: "1px solid", borderBottomColor: "border" }}
+ bg="bgSecondary"
+ sx={{ borderRadius: "default" }}
>
{title}
+
{index === 0 && (
+
- ) : (
-
- )
+ groupOptions.sortDirection === "asc"
+ ? Icon.SortAsc
+ : Icon.SortDesc
}
- onClick={() => {
- setMenuType("groupby");
- setIsExpanded((s) => !s);
+ title={`Grouped by ${groupByToTitleMap[groupOptions.groupBy]}`}
+ onClick={(e) => {
+ const items = getMenuItems(groupOptions);
+ openContextMenu(e, items, {
+ title: "Group & sort",
+ groupOptions,
+ refresh,
+ type,
+ });
}}
/>
)}
@@ -99,121 +157,35 @@ function GroupHeader(props) {
isVisible={menuType === "jumpto"}
groups={groups}
/>
-
+ )}
);
}
export default GroupHeader;
function IconButton(props) {
- const { text, icon, onClick, textStyle, sx } = props;
+ const { text, title, onClick } = props;
+ const isMobile = useMobile();
return (
- {
e.stopPropagation();
onClick(e);
}}
- px={1}
- sx={{
- borderRadius: "default",
- cursor: "pointer",
- ":hover": { bg: "hover" },
- ...sx,
- }}
- alignItems="center"
>
-
- {text}
-
- {icon}
-
- );
-}
-
-function JumpToGroupMenu(props) {
- const { groups, isVisible, onJump } = props;
- if (!isVisible) return null;
- return (
- <>
-
- Jump to
-
-
- {groups.map((group) => (
- onJump(group.title)}
- sx={{ bg: "shade", mr: 1, mt: 1 }}
- >
- ))}
-
- >
- );
-}
-
-function GroupByMenu(props) {
- const { isVisible } = props;
-
- const [sortDirection, setSortDirection] = usePersistentState(
- "sortDirection",
- "desc"
- );
- const [selectedGroup, setSelectedGroup] = usePersistentState(
- "selectedGroup",
- undefined
- );
- const refresh = useNoteStore((store) => store.refresh);
-
- if (!isVisible) return null;
- return (
- <>
-
-
- Group by
-
-
- ) : (
-
- )
- }
- textStyle={{ mr: 1 }}
- onClick={() => {
- setSortDirection(sortDirection === "desc" ? "asc" : "desc");
- refresh();
- }}
- />
-
- {groups.map((item) => (
- {
- setSelectedGroup(item.type);
- refresh();
- }}
- >
- {item.title}
- {selectedGroup === item.type && (
-
+ {text && {text}}
+ {props.icon && (
+
)}
-
- ))}
- >
+
);
}
diff --git a/apps/web/src/components/list-container/index.js b/apps/web/src/components/list-container/index.js
index cb03d341e..ecf8b67bc 100644
--- a/apps/web/src/components/list-container/index.js
+++ b/apps/web/src/components/list-container/index.js
@@ -24,7 +24,6 @@ function ListContainer(props) {
const profile = useMemo(() => ListProfiles[type], [type]);
const shouldSelectAll = useSelectionStore((store) => store.shouldSelectAll);
const setSelectedItems = useSelectionStore((store) => store.setSelectedItems);
- const [jumpToIndex, setJumpToIndex] = useState(-1);
const listRef = useRef();
useEffect(() => {
@@ -86,27 +85,21 @@ function ListContainer(props) {
case "header":
return (
- v.type === "header" &&
- v.title !== item.title
- )}
- wasJumpedTo={index === jumpToIndex}
+ groups={props.items.filter((v) => v.type === "header")}
onJump={(title) => {
const index = props.items.findIndex(
(v) => v.title === title
);
if (index < 0) return;
- setJumpToIndex(index);
- listRef.current.scrollToItem(
- index,
- "center"
- );
- setTimeout(() => {
- setJumpToIndex(-1);
- }, 1900);
+ listRef.current.scrollToIndex({
+ index,
+ align: "center",
+ behavior: "smooth",
+ });
}}
/>
);
diff --git a/apps/web/src/components/menu/index.js b/apps/web/src/components/menu/index.js
index 5e37d8a2b..006f0a1db 100644
--- a/apps/web/src/components/menu/index.js
+++ b/apps/web/src/components/menu/index.js
@@ -1,4 +1,5 @@
import { useAnimation } from "framer-motion";
+import { Check } from "../icons";
import React, { useEffect, useMemo } from "react";
import { Flex, Box, Text, Button } from "rebass";
import { useIsUserPremium } from "../../hooks/use-is-user-premium";
@@ -15,91 +16,116 @@ function Menu(props) {
);
return (
-
+
{menuItems.map(
- ({
- title,
- key,
- onClick,
- component: Component,
- color,
- isPro,
- isNew,
- disabled,
- disableReason,
- icon: Icon,
- }) => (
-
+ )
)}
);
@@ -108,7 +134,7 @@ export default React.memo(Menu, (prev, next) => {
return prev.state === next.state;
});
-function MenuContainer({ id, style, sx, children }) {
+function MenuContainer({ id, style, sx, title, children }) {
return (
@@ -133,7 +160,7 @@ function MenuContainer({ id, style, sx, children }) {
px={3}
sx={{ borderBottom: "1px solid", borderBottomColor: "border" }}
>
- Properties
+ {title || "Properties"}
{children}
@@ -141,7 +168,7 @@ function MenuContainer({ id, style, sx, children }) {
);
}
-function MobileMenuContainer({ style, id, state, children }) {
+function MobileMenuContainer({ style, id, state, title, children }) {
const animation = useAnimation();
useEffect(() => {
@@ -190,7 +217,7 @@ function MobileMenuContainer({ style, id, state, children }) {
/>
- Properties
+ {title || "Properties"}
{children}
diff --git a/apps/web/src/stores/note-store.js b/apps/web/src/stores/note-store.js
index 6dfaa20b3..d0fd442b5 100644
--- a/apps/web/src/stores/note-store.js
+++ b/apps/web/src/stores/note-store.js
@@ -10,6 +10,7 @@ import Config from "../utils/config";
import { showToast } from "../utils/toast";
import { qclone } from "qclone";
import { hashNavigate } from "../navigation";
+import { groupArray } from "notes-core/utils/grouping";
class NoteStore extends BaseStore {
notes = [];
@@ -30,12 +31,11 @@ class NoteStore extends BaseStore {
};
refresh = () => {
- const notes = db.notes.group(
- Config.get("selectedGroup"),
- Config.get("sortDirection", "desc")
- );
this.set((state) => {
- state.notes = qclone(notes);
+ state.notes = groupArray(
+ db.notes.all,
+ db.settings.getGroupOptions("home")
+ );
});
this.refreshContext();
};
diff --git a/apps/web/src/stores/notebook-store.js b/apps/web/src/stores/notebook-store.js
index 82d7ef196..7f2026691 100644
--- a/apps/web/src/stores/notebook-store.js
+++ b/apps/web/src/stores/notebook-store.js
@@ -4,6 +4,7 @@ import { store as appStore } from "./app-store";
import { store as noteStore } from "./note-store";
import BaseStore from "./index";
import { showToast } from "../utils/toast";
+import { groupArray } from "notes-core/utils/grouping";
class NotebookStore extends BaseStore {
notebooks = [];
@@ -12,7 +13,10 @@ class NotebookStore extends BaseStore {
refresh = () => {
this.set((state) => {
- state.notebooks = db.notebooks.all;
+ state.notebooks = groupArray(
+ db.notebooks.all,
+ db.settings.getGroupOptions("notebooks")
+ );
});
this.setSelectedNotebook(this.get().selectedNotebookId);
};
@@ -34,10 +38,13 @@ class NotebookStore extends BaseStore {
};
setSelectedNotebook = (id) => {
- const topics = db.notebooks.notebook(id)?.topics.all;
+ const topics = db.notebooks.notebook(id)?.topics?.all;
if (!topics) return;
this.set((state) => {
- state.selectedNotebookTopics = topics;
+ state.selectedNotebookTopics = groupArray(
+ topics,
+ db.settings.getGroupOptions("topics")
+ );
state.selectedNotebookId = id;
});
};
diff --git a/apps/web/src/stores/tag-store.js b/apps/web/src/stores/tag-store.js
index 731bef221..591ff6487 100644
--- a/apps/web/src/stores/tag-store.js
+++ b/apps/web/src/stores/tag-store.js
@@ -1,12 +1,19 @@
import createStore from "../common/store";
import { db } from "../common/db";
import BaseStore from "./index";
+import { groupArray } from "notes-core/utils/grouping";
class TagStore extends BaseStore {
tags = [];
refresh = () => {
- this.set((state) => (state.tags = db.tags.all));
+ this.set(
+ (state) =>
+ (state.tags = groupArray(
+ db.tags.all,
+ db.settings.getGroupOptions("tags")
+ ))
+ );
};
}
diff --git a/apps/web/src/stores/trash-store.js b/apps/web/src/stores/trash-store.js
index c8489a77d..8ee0e0470 100644
--- a/apps/web/src/stores/trash-store.js
+++ b/apps/web/src/stores/trash-store.js
@@ -3,12 +3,19 @@ import createStore from "../common/store";
import BaseStore from "./index";
import { store as appStore } from "./app-store";
import { store as notestore } from "./note-store";
+import { groupArray } from "notes-core/utils/grouping";
class TrashStore extends BaseStore {
trash = [];
refresh = () => {
- this.set((state) => (state.trash = db.trash.all));
+ this.set(
+ (state) =>
+ (state.trash = groupArray(
+ db.trash.all,
+ db.settings.getGroupOptions("trash")
+ ))
+ );
};
delete = (id, commit = false) => {
@@ -23,7 +30,7 @@ class TrashStore extends BaseStore {
restore = (id) => {
return db.trash.restore(id).then(() => {
- this.set((state) => (state.trash = db.trash.all));
+ this.refresh();
appStore.refreshColors();
notestore.refresh();
});
diff --git a/apps/web/src/views/home.js b/apps/web/src/views/home.js
index 5fce48509..a90b1e6f0 100644
--- a/apps/web/src/views/home.js
+++ b/apps/web/src/views/home.js
@@ -8,6 +8,7 @@ import useNavigate from "../utils/use-navigate";
function Home() {
const notes = useStore((store) => store.notes);
+ const refresh = useStore((store) => store.refresh);
const clearContext = useStore((store) => store.clearContext);
const [isLoading, setIsLoading] = useState(true);
useNavigate("home", () => {
@@ -39,6 +40,8 @@ function Home() {
return (
diff --git a/apps/web/src/views/notebooks.js b/apps/web/src/views/notebooks.js
index 6e5ca3e69..710ac71ec 100644
--- a/apps/web/src/views/notebooks.js
+++ b/apps/web/src/views/notebooks.js
@@ -8,11 +8,14 @@ import useNavigate from "../utils/use-navigate";
function Notebooks() {
useNavigate("notebooks", () => store.refresh());
const notebooks = useStore((state) => state.notebooks);
+ const refresh = useStore((state) => state.refresh);
return (
<>
store.context);
+ const refreshContext = useNotesStore((store) => store.refreshContext);
+ const type = context?.type === "favorite" ? "favorites" : "notes";
if (!context) return null;
return (
store.refresh());
const tags = useStore((store) => store.tags);
+ const refresh = useStore((store) => store.refresh);
return (
-
+
);
}
diff --git a/apps/web/src/views/topics.js b/apps/web/src/views/topics.js
index 12bbad9cd..b0ddf78bd 100644
--- a/apps/web/src/views/topics.js
+++ b/apps/web/src/views/topics.js
@@ -9,11 +9,14 @@ function Topics() {
(store) => store.selectedNotebookTopics
);
const selectedNotebookId = useNbStore((store) => store.selectedNotebookId);
+ const refresh = useNbStore((store) => store.setSelectedNotebook);
return (
<>
refresh(selectedNotebookId)}
items={selectedNotebookTopics}
context={{ notebookId: selectedNotebookId }}
placeholder={TopicsPlaceholder}
diff --git a/apps/web/src/views/trash.js b/apps/web/src/views/trash.js
index 6058cfb86..0e4165c76 100644
--- a/apps/web/src/views/trash.js
+++ b/apps/web/src/views/trash.js
@@ -15,11 +15,14 @@ function Trash() {
store.refresh();
});
const items = useStore((store) => store.trash);
+ const refresh = useStore((store) => store.refresh);
const clearTrash = useStore((store) => store.clear);
return (