Relations
(Beta)
diff --git a/web/src/components/MemoDetailSidebar/MemoDetailSidebarDrawer.tsx b/web/src/components/MemoDetailSidebar/MemoDetailSidebarDrawer.tsx
index 22fb9cbc..d9712b7e 100644
--- a/web/src/components/MemoDetailSidebar/MemoDetailSidebarDrawer.tsx
+++ b/web/src/components/MemoDetailSidebar/MemoDetailSidebarDrawer.tsx
@@ -8,9 +8,10 @@ import MemoDetailSidebar from "./MemoDetailSidebar";
interface Props {
memo: Memo;
+ parentPage?: string;
}
-const MemoDetailSidebarDrawer = ({ memo }: Props) => {
+const MemoDetailSidebarDrawer = ({ memo, parentPage }: Props) => {
const location = useLocation();
const [open, setOpen] = useState(false);
@@ -32,7 +33,7 @@ const MemoDetailSidebarDrawer = ({ memo }: Props) => {
-
+
>
diff --git a/web/src/components/MemoFilters.tsx b/web/src/components/MemoFilters.tsx
index 7c028705..4527599f 100644
--- a/web/src/components/MemoFilters.tsx
+++ b/web/src/components/MemoFilters.tsx
@@ -1,47 +1,61 @@
import { isEqual } from "lodash-es";
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, FilterIcon, LinkIcon, SearchIcon, TagIcon, XIcon } from "lucide-react";
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
import { useSearchParams } from "react-router-dom";
-import usePrevious from "react-use/lib/usePrevious";
import { FilterFactor, getMemoFilterKey, MemoFilter, parseFilterQuery, stringifyFilters, useMemoFilterStore } from "@/store/v1";
const MemoFilters = () => {
const [searchParams, setSearchParams] = useSearchParams();
const memoFilterStore = useMemoFilterStore();
const filters = memoFilterStore.filters;
- const prevFilters = usePrevious(filters);
const orderByTimeAsc = memoFilterStore.orderByTimeAsc;
- const prevOrderByTimeAsc = usePrevious(orderByTimeAsc);
+ const lastUpdateRef = useRef<"url" | "store">("url");
- // Sync the filters and orderByTimeAsc to the search params.
+ // set lastUpdateRef to store when filters or orderByTimeAsc changes
useEffect(() => {
- const newSearchParams = new URLSearchParams(searchParams);
+ lastUpdateRef.current = "store";
+ }, [filters, orderByTimeAsc]);
- if (prevOrderByTimeAsc !== orderByTimeAsc) {
- if (orderByTimeAsc) {
- newSearchParams.set("orderBy", "asc");
- } else {
- newSearchParams.delete("orderBy");
- }
- }
+ // set lastUpdateRef to url when searchParams changes
+ useEffect(() => {
+ lastUpdateRef.current = "url";
+ }, [searchParams]);
+
+ const checkAndSync = () => {
+ const filtersInURL = searchParams.get("filter") || "";
+ const orderByTimeAscInURL = searchParams.get("orderBy") === "asc";
+ const storeMatchesURL = filtersInURL === stringifyFilters(filters) && orderByTimeAscInURL === orderByTimeAsc;
+
+ if (!storeMatchesURL) {
+ if (lastUpdateRef.current === "url") {
+ // Sync URL -> Store
+ memoFilterStore.setState({
+ filters: parseFilterQuery(filtersInURL),
+ orderByTimeAsc: orderByTimeAscInURL,
+ });
+ } else if (lastUpdateRef.current === "store") {
+ // Sync Store -> URL
+ const newSearchParams = new URLSearchParams(searchParams);
- if (prevFilters && stringifyFilters(prevFilters) !== stringifyFilters(filters)) {
- if (filters.length > 0) {
- newSearchParams.set("filter", stringifyFilters(filters));
- } else {
- newSearchParams.delete("filter");
+ if (orderByTimeAsc) {
+ newSearchParams.set("orderBy", "asc");
+ } else {
+ newSearchParams.delete("orderBy");
+ }
+
+ if (filters.length > 0) {
+ newSearchParams.set("filter", stringifyFilters(filters));
+ } else {
+ newSearchParams.delete("filter");
+ }
+
+ setSearchParams(newSearchParams);
}
}
+ };
- setSearchParams(newSearchParams);
- }, [prevOrderByTimeAsc, orderByTimeAsc, prevFilters, filters, searchParams]);
-
- // Sync the search params to the filters and orderByTimeAsc when the component is mounted.
- useEffect(() => {
- const newFilters = parseFilterQuery(searchParams.get("filter"));
- const newOrderByTimeAsc = searchParams.get("orderBy") === "asc";
- memoFilterStore.setState({ filters: newFilters, orderByTimeAsc: newOrderByTimeAsc });
- }, []);
+ // Watch both URL and store changes
+ useEffect(checkAndSync, [searchParams, filters, orderByTimeAsc]);
const getFilterDisplayText = (filter: MemoFilter) => {
if (filter.value) {
diff --git a/web/src/components/MemoRelationForceGraph/MemoRelationForceGraph.tsx b/web/src/components/MemoRelationForceGraph/MemoRelationForceGraph.tsx
index 695cd622..5a73824a 100644
--- a/web/src/components/MemoRelationForceGraph/MemoRelationForceGraph.tsx
+++ b/web/src/components/MemoRelationForceGraph/MemoRelationForceGraph.tsx
@@ -11,12 +11,13 @@ import { convertMemoRelationsToGraphData } from "./utils";
interface Props {
memo: Memo;
className?: string;
+ parentPage?: string;
}
const MAIN_NODE_COLOR = "#14b8a6";
const DEFAULT_NODE_COLOR = "#a1a1aa";
-const MemoRelationForceGraph = ({ className, memo }: Props) => {
+const MemoRelationForceGraph = ({ className, memo, parentPage }: Props) => {
const navigateTo = useNavigateTo();
const { mode } = useColorScheme();
const containerRef = useRef
(null);
@@ -30,7 +31,11 @@ const MemoRelationForceGraph = ({ className, memo }: Props) => {
const onNodeClick = (node: NodeObject) => {
if (node.memo.uid === memo.uid) return;
- navigateTo(`/m/${node.memo.uid}`);
+ navigateTo(`/m/${node.memo.uid}`, {
+ state: {
+ from: parentPage,
+ },
+ });
};
return (
diff --git a/web/src/components/MemoRelationListView.tsx b/web/src/components/MemoRelationListView.tsx
index 2d1d3ba4..2b56c6c7 100644
--- a/web/src/components/MemoRelationListView.tsx
+++ b/web/src/components/MemoRelationListView.tsx
@@ -8,10 +8,11 @@ import { Memo } from "@/types/proto/api/v1/memo_service";
interface Props {
memo: Memo;
relations: MemoRelation[];
+ parentPage?: string;
}
const MemoRelationListView = (props: Props) => {
- const { memo, relations: relationList } = props;
+ const { memo, relations: relationList, parentPage } = props;
const referencingMemoList = relationList
.filter((relation) => relation.memo?.name === memo.name && relation.relatedMemo?.name !== memo.name)
.map((relation) => relation.relatedMemo!);
@@ -65,6 +66,9 @@ const MemoRelationListView = (props: Props) => {
className="w-auto max-w-full flex flex-row justify-start items-center text-sm leading-5 text-gray-600 dark:text-gray-400 dark:border-zinc-700 dark:bg-zinc-900 hover:underline"
to={`/m/${memo.uid}`}
viewTransition
+ state={{
+ from: parentPage,
+ }}
>
{memo.uid.slice(0, 6)}
@@ -84,6 +88,9 @@ const MemoRelationListView = (props: Props) => {
className="w-auto max-w-full flex flex-row justify-start items-center text-sm leading-5 text-gray-600 dark:text-gray-400 dark:border-zinc-700 dark:bg-zinc-900 hover:underline"
to={`/m/${memo.uid}`}
viewTransition
+ state={{
+ from: parentPage,
+ }}
>
{memo.uid.slice(0, 6)}
diff --git a/web/src/components/MemoView.tsx b/web/src/components/MemoView.tsx
index 840e097b..88d23759 100644
--- a/web/src/components/MemoView.tsx
+++ b/web/src/components/MemoView.tsx
@@ -35,6 +35,7 @@ interface Props {
showVisibility?: boolean;
showPinned?: boolean;
className?: string;
+ parentPage?: string;
}
const MemoView: React.FC = (props: Props) => {
@@ -60,6 +61,7 @@ const MemoView: React.FC = (props: Props) => {
const relativeTimeFormat = Date.now() - memo.displayTime!.getTime() > 1000 * 60 * 60 * 24 ? "datetime" : "auto";
const readonly = memo.creator !== user?.name && !isSuperUser(user);
const isInMemoDetailPage = location.pathname.startsWith(`/m/${memo.uid}`);
+ const parentPage = props.parentPage || location.pathname;
// Initial related data: creator.
useAsyncEffect(async () => {
@@ -68,8 +70,12 @@ const MemoView: React.FC = (props: Props) => {
}, []);
const handleGotoMemoDetailPage = useCallback(() => {
- navigateTo(`/m/${memo.uid}`);
- }, [memo.uid]);
+ navigateTo(`/m/${memo.uid}`, {
+ state: {
+ from: parentPage,
+ },
+ });
+ }, [memo.uid, parentPage]);
const handleMemoContentClick = useCallback(async (e: React.MouseEvent) => {
const targetEl = e.target as HTMLElement;
@@ -217,6 +223,9 @@ const MemoView: React.FC = (props: Props) => {
)}
to={`/m/${memo.uid}#comments`}
viewTransition
+ state={{
+ from: parentPage,
+ }}
>
{commentAmount > 0 && {commentAmount}}
@@ -242,10 +251,11 @@ const MemoView: React.FC = (props: Props) => {
onClick={handleMemoContentClick}
onDoubleClick={handleMemoContentDoubleClick}
compact={props.compact && workspaceMemoRelatedSetting.enableAutoCompact}
+ parentPage={parentPage}
/>
{memo.location && }
-
+
>
)}
diff --git a/web/src/hooks/useNavigateTo.ts b/web/src/hooks/useNavigateTo.ts
index 62e844fd..ad743cd6 100644
--- a/web/src/hooks/useNavigateTo.ts
+++ b/web/src/hooks/useNavigateTo.ts
@@ -1,15 +1,15 @@
-import { useNavigate } from "react-router-dom";
+import { NavigateOptions, useNavigate } from "react-router-dom";
const useNavigateTo = () => {
const navigateTo = useNavigate();
- const navigateToWithViewTransition = (to: string) => {
+ const navigateToWithViewTransition = (to: string, options?: NavigateOptions) => {
const document = window.document as any;
if (!document.startViewTransition) {
- navigateTo(to);
+ navigateTo(to, options);
} else {
document.startViewTransition(() => {
- navigateTo(to);
+ navigateTo(to, options);
});
}
};
diff --git a/web/src/pages/MemoDetail.tsx b/web/src/pages/MemoDetail.tsx
index 6766e2f6..3b006ec9 100644
--- a/web/src/pages/MemoDetail.tsx
+++ b/web/src/pages/MemoDetail.tsx
@@ -4,7 +4,7 @@ import { ArrowUpLeftFromCircleIcon, MessageCircleIcon } from "lucide-react";
import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
-import { Link, useParams } from "react-router-dom";
+import { Link, useLocation, useParams } from "react-router-dom";
import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar";
import MemoEditor from "@/components/MemoEditor";
import MemoView from "@/components/MemoView";
@@ -23,6 +23,7 @@ const MemoDetail = () => {
const { md } = useResponsiveWidth();
const params = useParams();
const navigateTo = useNavigateTo();
+ const { state: locationState } = useLocation();
const workspaceSettingStore = useWorkspaceSettingStore();
const currentUser = useCurrentUser();
const memoStore = useMemoStore();
@@ -86,7 +87,7 @@ const MemoDetail = () => {
{!md && (
-
+
)}
@@ -96,6 +97,7 @@ const MemoDetail = () => {
@@ -108,6 +110,7 @@ const MemoDetail = () => {
className="shadow hover:shadow-md transition-all"
memo={memo}
compact={false}
+ parentPage={locationState?.from}
showCreator
showVisibility
showPinned
@@ -141,7 +144,13 @@ const MemoDetail = () => {
)}
{comments.map((comment) => (
-
+
))}
>
)}
@@ -162,7 +171,7 @@ const MemoDetail = () => {