mirror of https://github.com/usememos/memos
parent
ca5859296a
commit
a07d5d38d6
@ -0,0 +1,60 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEditorStore } from "@/store/module";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import Icon from "../Icon";
|
||||
|
||||
interface FormatedMemoRelation extends MemoRelation {
|
||||
relatedMemo: Memo;
|
||||
}
|
||||
|
||||
const RelationListView = () => {
|
||||
const editorStore = useEditorStore();
|
||||
const memoCacheStore = useMemoCacheStore();
|
||||
const [formatedMemoRelationList, setFormatedMemoRelationList] = useState<FormatedMemoRelation[]>([]);
|
||||
const relationList = editorStore.state.relationList;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRelatedMemoList = async () => {
|
||||
const requests = relationList.map(async (relation) => {
|
||||
const relatedMemo = await memoCacheStore.getOrFetchMemoById(relation.relatedMemoId);
|
||||
return {
|
||||
...relation,
|
||||
relatedMemo,
|
||||
};
|
||||
});
|
||||
const list = await Promise.all(requests);
|
||||
setFormatedMemoRelationList(list);
|
||||
};
|
||||
fetchRelatedMemoList();
|
||||
}, [relationList]);
|
||||
|
||||
const handleDeleteRelation = async (memoRelation: FormatedMemoRelation) => {
|
||||
const newRelationList = relationList.filter((relation) => relation.relatedMemoId !== memoRelation.relatedMemoId);
|
||||
editorStore.setRelationList(newRelationList);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{formatedMemoRelationList.length > 0 && (
|
||||
<div className="w-full flex flex-row gap-2 mt-2 flex-wrap">
|
||||
{formatedMemoRelationList.map((memoRelation) => {
|
||||
return (
|
||||
<div
|
||||
key={memoRelation.relatedMemoId}
|
||||
className="w-auto max-w-[50%] overflow-hidden flex flex-row justify-start items-center bg-gray-100 hover:bg-gray-200 rounded text-sm p-1 px-2 text-gray-500 cursor-pointer"
|
||||
>
|
||||
<Icon.Link className="w-4 h-auto shrink-0" />
|
||||
<span className="mx-1 max-w-full text-ellipsis font-mono whitespace-nowrap overflow-hidden">
|
||||
{memoRelation.relatedMemo.content}
|
||||
</span>
|
||||
<Icon.X className="w-4 h-auto hover:opacity-80 shrink-0" onClick={() => handleDeleteRelation(memoRelation)} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RelationListView;
|
@ -0,0 +1,46 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import Icon from "./Icon";
|
||||
|
||||
interface Props {
|
||||
relationList: MemoRelation[];
|
||||
}
|
||||
|
||||
const MemoRelationListView = (props: Props) => {
|
||||
const memoCacheStore = useMemoCacheStore();
|
||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||
const relationList = props.relationList;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRelatedMemoList = async () => {
|
||||
const requests = relationList.map((relation) => memoCacheStore.getOrFetchMemoById(relation.relatedMemoId));
|
||||
const memoList = await Promise.all(requests);
|
||||
setRelatedMemoList(memoList);
|
||||
};
|
||||
fetchRelatedMemoList();
|
||||
}, [relationList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{relatedMemoList.length > 0 && (
|
||||
<div className="w-full max-w-full overflow-hidden grid grid-cols-1 gap-1 mt-2">
|
||||
{relatedMemoList.map((memo) => {
|
||||
return (
|
||||
<div
|
||||
key={memo.id}
|
||||
className="w-auto flex flex-row justify-start items-center hover:bg-gray-100 dark:hover:bg-zinc-800 rounded text-sm p-1 text-gray-500 dark:text-gray-400 cursor-pointer"
|
||||
>
|
||||
<div className="w-5 h-5 flex justify-center items-center shrink-0 bg-gray-100 dark:bg-zinc-800 rounded-full">
|
||||
<Icon.Link className="w-3 h-auto" />
|
||||
</div>
|
||||
<span className="mx-1 w-auto truncate">{memo.content}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemoRelationListView;
|
@ -1,65 +0,0 @@
|
||||
.page-wrapper.memo-detail {
|
||||
@apply relative top-0 w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800;
|
||||
|
||||
> .page-container {
|
||||
@apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8;
|
||||
|
||||
> .page-header {
|
||||
@apply sticky top-0 z-10 max-w-2xl w-full min-h-full flex flex-row justify-between items-center px-4 pt-6 mb-2 bg-zinc-100 dark:bg-zinc-800;
|
||||
|
||||
> .title-container {
|
||||
@apply flex flex-row justify-start items-center;
|
||||
|
||||
> .logo-img {
|
||||
@apply h-12 sm:h-14 w-auto mr-2;
|
||||
}
|
||||
|
||||
> .logo-text {
|
||||
@apply text-4xl tracking-wide text-black dark:text-white;
|
||||
}
|
||||
|
||||
> .title-text {
|
||||
@apply text-xl sm:text-3xl font-mono text-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
> .action-button-container {
|
||||
> .btn {
|
||||
@apply block text-gray-600 dark:text-gray-300 font-mono text-base py-1 border px-3 leading-8 rounded-xl hover:opacity-80 hover:underline;
|
||||
|
||||
> .icon {
|
||||
@apply text-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .memos-wrapper {
|
||||
@apply relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4;
|
||||
|
||||
> .memo-container {
|
||||
@apply flex flex-col justify-start items-start w-full p-4 mt-2 bg-white dark:bg-zinc-700 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-700;
|
||||
|
||||
> .memo-header {
|
||||
@apply mb-2 w-full flex flex-row justify-between items-center;
|
||||
|
||||
> .status-container {
|
||||
@apply flex flex-row justify-start items-center text-sm text-gray-400;
|
||||
|
||||
> .name-text {
|
||||
@apply ml-2 hover:text-green-600 hover:underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .memo-content {
|
||||
@apply cursor-default;
|
||||
|
||||
> * {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export { useMemoCacheStore } from "./memo";
|
||||
export { useMessageStore } from "./message";
|
@ -0,0 +1,41 @@
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
import * as api from "@/helpers/api";
|
||||
import { convertResponseModelMemo } from "../module";
|
||||
|
||||
export const useMemoCacheStore = create(
|
||||
combine({ memoById: new Map<MemoId, Memo>() }, (set, get) => ({
|
||||
getState: () => get(),
|
||||
getOrFetchMemoById: async (memoId: MemoId) => {
|
||||
const memo = get().memoById.get(memoId);
|
||||
if (memo) {
|
||||
return memo;
|
||||
}
|
||||
|
||||
const { data } = (await api.getMemoById(memoId)).data;
|
||||
const formatedMemo = convertResponseModelMemo(data);
|
||||
|
||||
set((state) => {
|
||||
state.memoById.set(memoId, formatedMemo);
|
||||
return state;
|
||||
});
|
||||
|
||||
return formatedMemo;
|
||||
},
|
||||
getMemoById: (memoId: MemoId) => {
|
||||
return get().memoById.get(memoId);
|
||||
},
|
||||
setMemoCache: (memo: Memo) => {
|
||||
set((state) => {
|
||||
state.memoById.set(memo.id, memo);
|
||||
return state;
|
||||
});
|
||||
},
|
||||
deleteMemoCache: (memoId: MemoId) => {
|
||||
set((state) => {
|
||||
state.memoById.delete(memoId);
|
||||
return state;
|
||||
});
|
||||
},
|
||||
}))
|
||||
);
|
Loading…
Reference in New Issue