chore: tweak user stats in frontend

pull/4304/head
Steven 8 months ago
parent 6904dc16af
commit 147458347b

@ -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,19 +100,20 @@ const MemoView: React.FC<Props> = (props: Props) => {
}
}, []);
const onEditorConfirm = () => {
setShowEditor(false);
userStatsStore.setStateId();
};
const onPinIconClick = async () => {
try {
if (memo.pinned) {
await memoStore.updateMemo(
{
name: memo.name,
pinned: false,
},
["pinned"],
);
}
} catch (error) {
// do nth
if (memo.pinned) {
await memoStore.updateMemo(
{
name: memo.name,
pinned: false,
},
["pinned"],
);
}
};
@ -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 });
},
})),
);

Loading…
Cancel
Save