mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: tweak user stats in frontend
This commit is contained in:
@@ -29,11 +29,13 @@ func (s *APIV1Service) ListAllUserStats(ctx context.Context, _ *v1pb.ListAllUser
|
||||
return nil, errors.Wrap(err, "failed to get workspace memo related setting")
|
||||
}
|
||||
|
||||
normalStatus := store.Normal
|
||||
memoFind := &store.FindMemo{
|
||||
// Exclude comments by default.
|
||||
ExcludeComments: true,
|
||||
ExcludeContent: true,
|
||||
VisibilityList: visibilities,
|
||||
RowStatus: &normalStatus,
|
||||
}
|
||||
memos, err := s.Store.ListMemos(ctx, memoFind)
|
||||
if err != nil {
|
||||
@@ -92,21 +94,13 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
|
||||
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
|
||||
}
|
||||
|
||||
workspaceMemoRelatedSetting, err := s.Store.GetWorkspaceMemoRelatedSetting(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get workspace memo related setting")
|
||||
}
|
||||
userStats := &v1pb.UserStats{
|
||||
Name: fmt.Sprintf("%s%d", UserNamePrefix, user.ID),
|
||||
MemoDisplayTimestamps: []*timestamppb.Timestamp{},
|
||||
MemoTypeStats: &v1pb.UserStats_MemoTypeStats{},
|
||||
TagCount: map[string]int32{},
|
||||
}
|
||||
normalStatus := store.Normal
|
||||
memoFind := &store.FindMemo{
|
||||
// Exclude comments by default.
|
||||
ExcludeComments: true,
|
||||
ExcludeContent: true,
|
||||
CreatorID: &userID,
|
||||
RowStatus: &normalStatus,
|
||||
}
|
||||
|
||||
currentUser, err := s.GetCurrentUser(ctx)
|
||||
@@ -125,6 +119,17 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to list memos: %v", err)
|
||||
}
|
||||
|
||||
workspaceMemoRelatedSetting, err := s.Store.GetWorkspaceMemoRelatedSetting(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get workspace memo related setting")
|
||||
}
|
||||
userStats := &v1pb.UserStats{
|
||||
Name: fmt.Sprintf("%s%d", UserNamePrefix, user.ID),
|
||||
MemoDisplayTimestamps: []*timestamppb.Timestamp{},
|
||||
MemoTypeStats: &v1pb.UserStats_MemoTypeStats{},
|
||||
TagCount: map[string]int32{},
|
||||
}
|
||||
for _, memo := range memos {
|
||||
displayTs := memo.CreatedTs
|
||||
if workspaceMemoRelatedSetting.DisplayWithUpdateTime {
|
||||
|
@@ -14,10 +14,10 @@ const ExploreSidebar = (props: Props) => {
|
||||
|
||||
useDebounce(
|
||||
async () => {
|
||||
userStatsStore.listUserStats();
|
||||
await userStatsStore.listUserStats();
|
||||
},
|
||||
300,
|
||||
[],
|
||||
[userStatsStore.stateId],
|
||||
);
|
||||
|
||||
return (
|
||||
|
@@ -20,7 +20,7 @@ const HomeSidebar = (props: Props) => {
|
||||
await userStatsStore.listUserStats(currentUser.name);
|
||||
},
|
||||
300,
|
||||
[memoList.size(), currentUser],
|
||||
[memoList.size(), userStatsStore.stateId, currentUser],
|
||||
);
|
||||
|
||||
return (
|
||||
|
@@ -4,7 +4,6 @@ import { Edit3Icon, HashIcon, MoreVerticalIcon, TagsIcon, TrashIcon } from "luci
|
||||
import toast from "react-hot-toast";
|
||||
import useLocalStorage from "react-use/lib/useLocalStorage";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useMemoFilterStore, useUserStatsStore, useUserStatsTags } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import showRenameTagDialog from "../RenameTagDialog";
|
||||
@@ -17,7 +16,6 @@ interface Props {
|
||||
|
||||
const TagsSection = (props: Props) => {
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const memoFilterStore = useMemoFilterStore();
|
||||
const userStatsStore = useUserStatsStore();
|
||||
const [treeMode, setTreeMode] = useLocalStorage<boolean>("tag-view-as-tree", false);
|
||||
@@ -44,7 +42,7 @@ const TagsSection = (props: Props) => {
|
||||
parent: "memos/-",
|
||||
tag: tag,
|
||||
});
|
||||
await userStatsStore.listUserStats(currentUser.name);
|
||||
userStatsStore.setStateId();
|
||||
toast.success(t("message.deleted-successfully"));
|
||||
}
|
||||
};
|
||||
|
@@ -16,7 +16,7 @@ import toast from "react-hot-toast";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { markdownServiceClient } from "@/grpcweb";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useMemoStore } from "@/store/v1";
|
||||
import { useMemoStore, useUserStatsStore } from "@/store/v1";
|
||||
import { State } from "@/types/proto/api/v1/common";
|
||||
import { NodeType } from "@/types/proto/api/v1/markdown_service";
|
||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
@@ -48,10 +48,16 @@ const MemoActionMenu = (props: Props) => {
|
||||
const location = useLocation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const memoStore = useMemoStore();
|
||||
const userStatsStore = useUserStatsStore();
|
||||
const isArchived = memo.state === State.ARCHIVED;
|
||||
const hasCompletedTaskList = checkHasCompletedTaskList(memo);
|
||||
const isInMemoDetailPage = location.pathname.startsWith(`/m/${memo.uid}`);
|
||||
|
||||
const memoUpdatedCallback = () => {
|
||||
// Refresh user stats.
|
||||
userStatsStore.setStateId();
|
||||
};
|
||||
|
||||
const handleTogglePinMemoBtnClick = async () => {
|
||||
try {
|
||||
if (memo.pinned) {
|
||||
@@ -104,6 +110,7 @@ const MemoActionMenu = (props: Props) => {
|
||||
if (isInMemoDetailPage) {
|
||||
memo.state === State.ARCHIVED ? navigateTo("/") : navigateTo("/archived");
|
||||
}
|
||||
memoUpdatedCallback();
|
||||
};
|
||||
|
||||
const handleCopyLink = () => {
|
||||
@@ -119,6 +126,7 @@ const MemoActionMenu = (props: Props) => {
|
||||
if (isInMemoDetailPage) {
|
||||
navigateTo("/");
|
||||
}
|
||||
memoUpdatedCallback();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,6 +158,7 @@ const MemoActionMenu = (props: Props) => {
|
||||
["content"],
|
||||
);
|
||||
toast.success(t("message.remove-completed-task-list-items-successfully"));
|
||||
memoUpdatedCallback();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +189,7 @@ const MemoActionMenu = (props: Props) => {
|
||||
{!readonly && (
|
||||
<>
|
||||
{!isArchived && hasCompletedTaskList && (
|
||||
<MenuItem color="danger" onClick={handleRemoveCompletedTaskListItemsClick}>
|
||||
<MenuItem color="warning" onClick={handleRemoveCompletedTaskListItemsClick}>
|
||||
<SquareCheckIcon className="w-4 h-auto" />
|
||||
{t("memo.remove-completed-task-list-items")}
|
||||
</MenuItem>
|
||||
|
@@ -334,6 +334,10 @@ const MemoEditor = (props: Props) => {
|
||||
updateMask.add("display_time");
|
||||
memoPatch.displayTime = displayTime;
|
||||
}
|
||||
if (updateMask.size === 0) {
|
||||
toast.error("No changes detected");
|
||||
return;
|
||||
}
|
||||
const memo = await memoStore.updateMemo(memoPatch, Array.from(updateMask));
|
||||
if (onConfirm) {
|
||||
onConfirm(memo.name);
|
||||
|
@@ -6,7 +6,7 @@ import { Link, useLocation } from "react-router-dom";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useUserStore, useWorkspaceSettingStore, useMemoStore } from "@/store/v1";
|
||||
import { useUserStore, useWorkspaceSettingStore, useMemoStore, useUserStatsStore } from "@/store/v1";
|
||||
import { State } from "@/types/proto/api/v1/common";
|
||||
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service";
|
||||
import { Memo, Visibility } from "@/types/proto/api/v1/memo_service";
|
||||
@@ -47,6 +47,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
||||
const user = useCurrentUser();
|
||||
const memoStore = useMemoStore();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStatsStore = useUserStatsStore();
|
||||
const [showEditor, setShowEditor] = useState<boolean>(false);
|
||||
const [creator, setCreator] = useState(userStore.getUserByName(memo.creator));
|
||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||
@@ -99,8 +100,12 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onEditorConfirm = () => {
|
||||
setShowEditor(false);
|
||||
userStatsStore.setStateId();
|
||||
};
|
||||
|
||||
const onPinIconClick = async () => {
|
||||
try {
|
||||
if (memo.pinned) {
|
||||
await memoStore.updateMemo(
|
||||
{
|
||||
@@ -110,9 +115,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
||||
["pinned"],
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// do nth
|
||||
}
|
||||
};
|
||||
|
||||
const displayTime = isArchived ? (
|
||||
@@ -136,7 +138,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
||||
className="border-none !p-0 -mb-2"
|
||||
cacheKey={`inline-memo-editor-${memo.name}`}
|
||||
memoName={memo.name}
|
||||
onConfirm={() => setShowEditor(false)}
|
||||
onConfirm={onEditorConfirm}
|
||||
onCancel={() => setShowEditor(false)}
|
||||
/>
|
||||
) : (
|
||||
|
@@ -4,7 +4,6 @@ import { XIcon } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { memoServiceClient } from "@/grpcweb";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useUserStatsStore } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
@@ -20,7 +19,6 @@ const RenameTagDialog: React.FC<Props> = (props: Props) => {
|
||||
const userStatsStore = useUserStatsStore();
|
||||
const [newName, setNewName] = useState(tag);
|
||||
const requestState = useLoading(false);
|
||||
const user = useCurrentUser();
|
||||
|
||||
const handleTagNameInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNewName(e.target.value.trim());
|
||||
@@ -43,7 +41,7 @@ const RenameTagDialog: React.FC<Props> = (props: Props) => {
|
||||
newTag: newName,
|
||||
});
|
||||
toast.success("Rename tag successfully");
|
||||
userStatsStore.listUserStats(user.name);
|
||||
userStatsStore.setStateId();
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toast.error(error.details);
|
||||
|
@@ -34,7 +34,7 @@ const StatisticsView = () => {
|
||||
}
|
||||
setMemoTypeStats(memoTypeStats);
|
||||
setActivityStats(countBy(displayTimeList.map((date) => dayjs(date).format("YYYY-MM-DD"))));
|
||||
}, [userStatsStore.stateId]);
|
||||
}, [userStatsStore.userStatsByName, userStatsStore.stateId]);
|
||||
|
||||
const onCalendarClick = (date: string) => {
|
||||
memoFilterStore.removeFilter((f) => f.factor === "displayTime");
|
||||
|
@@ -31,7 +31,10 @@ export const useUserStatsStore = create(
|
||||
const userStats = await userServiceClient.getUserStats({ name: user });
|
||||
userStatsByName[user] = userStats;
|
||||
}
|
||||
set({ stateId: uniqueId(), userStatsByName });
|
||||
set({ ...get(), userStatsByName });
|
||||
},
|
||||
setStateId: (id = uniqueId()) => {
|
||||
set({ ...get(), stateId: id });
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
Reference in New Issue
Block a user