mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: show pinned count
This commit is contained in:
@@ -52,6 +52,10 @@ func (s *APIV1Service) buildMemoFindWithFilter(ctx context.Context, find *store.
|
|||||||
find.CreatedTsBefore = filterExpr.DisplayTimeBefore
|
find.CreatedTsBefore = filterExpr.DisplayTimeBefore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if filterExpr.Pinned {
|
||||||
|
pinned := true
|
||||||
|
find.Pinned = &pinned
|
||||||
|
}
|
||||||
if filterExpr.HasLink {
|
if filterExpr.HasLink {
|
||||||
find.PayloadFind.HasLink = true
|
find.PayloadFind.HasLink = true
|
||||||
}
|
}
|
||||||
@@ -74,6 +78,7 @@ var MemoFilterCELAttributes = []cel.EnvOption{
|
|||||||
cel.Variable("tag_search", cel.ListType(cel.StringType)),
|
cel.Variable("tag_search", cel.ListType(cel.StringType)),
|
||||||
cel.Variable("display_time_before", cel.IntType),
|
cel.Variable("display_time_before", cel.IntType),
|
||||||
cel.Variable("display_time_after", cel.IntType),
|
cel.Variable("display_time_after", cel.IntType),
|
||||||
|
cel.Variable("pinned", cel.BoolType),
|
||||||
cel.Variable("has_link", cel.BoolType),
|
cel.Variable("has_link", cel.BoolType),
|
||||||
cel.Variable("has_task_list", cel.BoolType),
|
cel.Variable("has_task_list", cel.BoolType),
|
||||||
cel.Variable("has_code", cel.BoolType),
|
cel.Variable("has_code", cel.BoolType),
|
||||||
@@ -85,6 +90,7 @@ type MemoFilter struct {
|
|||||||
TagSearch []string
|
TagSearch []string
|
||||||
DisplayTimeBefore *int64
|
DisplayTimeBefore *int64
|
||||||
DisplayTimeAfter *int64
|
DisplayTimeAfter *int64
|
||||||
|
Pinned bool
|
||||||
HasLink bool
|
HasLink bool
|
||||||
HasTaskList bool
|
HasTaskList bool
|
||||||
HasCode bool
|
HasCode bool
|
||||||
@@ -134,6 +140,9 @@ func findMemoField(callExpr *exprv1.Expr_Call, filter *MemoFilter) {
|
|||||||
} else if idExpr.Name == "display_time_after" {
|
} else if idExpr.Name == "display_time_after" {
|
||||||
displayTimeAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
displayTimeAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||||
filter.DisplayTimeAfter = &displayTimeAfter
|
filter.DisplayTimeAfter = &displayTimeAfter
|
||||||
|
} else if idExpr.Name == "pinned" {
|
||||||
|
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
||||||
|
filter.Pinned = value
|
||||||
} else if idExpr.Name == "has_link" {
|
} else if idExpr.Name == "has_link" {
|
||||||
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
||||||
filter.HasLink = value
|
filter.HasLink = value
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { isEqual } from "lodash-es";
|
import { isEqual } from "lodash-es";
|
||||||
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, HashIcon, LinkIcon, SearchIcon, XIcon } from "lucide-react";
|
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, HashIcon, LinkIcon, PinIcon, SearchIcon, XIcon } from "lucide-react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { FilterFactor, getMemoFilterKey, MemoFilter, stringifyFilters, useMemoFilterStore } from "@/store/v1";
|
import { FilterFactor, getMemoFilterKey, MemoFilter, stringifyFilters, useMemoFilterStore } from "@/store/v1";
|
||||||
@@ -68,6 +68,7 @@ const FactorIcon = ({ factor, className }: { factor: FilterFactor; className?: s
|
|||||||
visibility: <EyeIcon className={className} />,
|
visibility: <EyeIcon className={className} />,
|
||||||
contentSearch: <SearchIcon className={className} />,
|
contentSearch: <SearchIcon className={className} />,
|
||||||
displayTime: <CalendarIcon className={className} />,
|
displayTime: <CalendarIcon className={className} />,
|
||||||
|
pinned: <PinIcon className={className} />,
|
||||||
"property.hasLink": <LinkIcon className={className} />,
|
"property.hasLink": <LinkIcon className={className} />,
|
||||||
"property.hasTaskList": <CheckCircleIcon className={className} />,
|
"property.hasTaskList": <CheckCircleIcon className={className} />,
|
||||||
"property.hasCode": <CodeIcon className={className} />,
|
"property.hasCode": <CodeIcon className={className} />,
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
import { Tooltip } from "@mui/joy";
|
import { Tooltip } from "@mui/joy";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { countBy } from "lodash-es";
|
import { countBy } from "lodash-es";
|
||||||
import { CheckCircleIcon, ChevronRightIcon, ChevronLeftIcon, Code2Icon, LinkIcon, ListTodoIcon } from "lucide-react";
|
import { CheckCircleIcon, ChevronRightIcon, ChevronLeftIcon, Code2Icon, LinkIcon, ListTodoIcon, PinIcon } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
|
import { matchPath, useLocation } from "react-router-dom";
|
||||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||||
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import i18n from "@/i18n";
|
import i18n from "@/i18n";
|
||||||
|
import { Routes } from "@/router";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { useMemoFilterStore } from "@/store/v1";
|
||||||
import { userStore } from "@/store/v2";
|
import { userStore } from "@/store/v2";
|
||||||
import { UserStats_MemoTypeStats } from "@/types/proto/api/v1/user_service";
|
import { UserStats_MemoTypeStats } from "@/types/proto/api/v1/user_service";
|
||||||
@@ -17,7 +20,9 @@ import "react-datepicker/dist/react-datepicker.css";
|
|||||||
|
|
||||||
const StatisticsView = observer(() => {
|
const StatisticsView = observer(() => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
const location = useLocation();
|
||||||
const memoFilterStore = useMemoFilterStore();
|
const memoFilterStore = useMemoFilterStore();
|
||||||
|
const currentUser = useCurrentUser();
|
||||||
const [memoTypeStats, setMemoTypeStats] = useState<UserStats_MemoTypeStats>(UserStats_MemoTypeStats.fromPartial({}));
|
const [memoTypeStats, setMemoTypeStats] = useState<UserStats_MemoTypeStats>(UserStats_MemoTypeStats.fromPartial({}));
|
||||||
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
|
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
|
||||||
const [selectedDate] = useState(new Date());
|
const [selectedDate] = useState(new Date());
|
||||||
@@ -92,7 +97,22 @@ const StatisticsView = observer(() => {
|
|||||||
onClick={onCalendarClick}
|
onClick={onCalendarClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-1 w-full flex flex-row justify-start items-center gap-x-2 gap-y-1 flex-wrap">
|
<div className="pt-1 w-full flex flex-row justify-start items-center gap-1 flex-wrap">
|
||||||
|
{matchPath(Routes.ROOT, location.pathname) &&
|
||||||
|
currentUser &&
|
||||||
|
userStore.state.currentUserStats &&
|
||||||
|
userStore.state.currentUserStats.pinnedMemos.length > 0 && (
|
||||||
|
<div
|
||||||
|
className={cn("w-auto border dark:border-zinc-800 pl-1.5 pr-2 py-0.5 rounded-md flex justify-between items-center")}
|
||||||
|
onClick={() => memoFilterStore.addFilter({ factor: "pinned", value: "" })}
|
||||||
|
>
|
||||||
|
<div className="w-auto flex justify-start items-center mr-1">
|
||||||
|
<PinIcon className="w-4 h-auto mr-1" />
|
||||||
|
<span className="block text-sm">Pinned</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm truncate">{userStore.state.currentUserStats.pinnedMemos.length}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className={cn("w-auto border dark:border-zinc-800 pl-1.5 pr-2 py-0.5 rounded-md flex justify-between items-center")}
|
className={cn("w-auto border dark:border-zinc-800 pl-1.5 pr-2 py-0.5 rounded-md flex justify-between items-center")}
|
||||||
onClick={() => memoFilterStore.addFilter({ factor: "property.hasLink", value: "" })}
|
onClick={() => memoFilterStore.addFilter({ factor: "property.hasLink", value: "" })}
|
||||||
|
@@ -23,6 +23,8 @@ const Home = observer(() => {
|
|||||||
contentSearch.push(`"${filter.value}"`);
|
contentSearch.push(`"${filter.value}"`);
|
||||||
} else if (filter.factor === "tagSearch") {
|
} else if (filter.factor === "tagSearch") {
|
||||||
tagSearch.push(`"${filter.value}"`);
|
tagSearch.push(`"${filter.value}"`);
|
||||||
|
} else if (filter.factor === "pinned") {
|
||||||
|
conditions.push(`pinned == true`);
|
||||||
} else if (filter.factor === "property.hasLink") {
|
} else if (filter.factor === "property.hasLink") {
|
||||||
conditions.push(`has_link == true`);
|
conditions.push(`has_link == true`);
|
||||||
} else if (filter.factor === "property.hasTaskList") {
|
} else if (filter.factor === "property.hasTaskList") {
|
||||||
|
@@ -6,7 +6,6 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import MemoFilters from "@/components/MemoFilters";
|
|
||||||
import MemoView from "@/components/MemoView";
|
import MemoView from "@/components/MemoView";
|
||||||
import PagedMemoList from "@/components/PagedMemoList";
|
import PagedMemoList from "@/components/PagedMemoList";
|
||||||
import UserAvatar from "@/components/UserAvatar";
|
import UserAvatar from "@/components/UserAvatar";
|
||||||
@@ -78,7 +77,7 @@ const UserProfile = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="w-full max-w-3xl mx-auto min-h-full flex flex-col justify-start items-center pb-8">
|
<section className="w-full max-w-3xl mx-auto min-h-full flex flex-col justify-start items-center pb-8">
|
||||||
<div className="w-full px-4 sm:px-6 flex flex-col justify-start items-center">
|
<div className="w-full flex flex-col justify-start items-center max-w-2xl">
|
||||||
{!loadingState.isLoading &&
|
{!loadingState.isLoading &&
|
||||||
(user ? (
|
(user ? (
|
||||||
<>
|
<>
|
||||||
@@ -99,7 +98,6 @@ const UserProfile = observer(() => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MemoFilters />
|
|
||||||
<PagedMemoList
|
<PagedMemoList
|
||||||
renderer={(memo: Memo) => (
|
renderer={(memo: Memo) => (
|
||||||
<MemoView key={`${memo.name}-${memo.displayTime}`} memo={memo} showVisibility showPinned compact />
|
<MemoView key={`${memo.name}-${memo.displayTime}`} memo={memo} showVisibility showPinned compact />
|
||||||
|
@@ -7,6 +7,7 @@ export type FilterFactor =
|
|||||||
| "visibility"
|
| "visibility"
|
||||||
| "contentSearch"
|
| "contentSearch"
|
||||||
| "displayTime"
|
| "displayTime"
|
||||||
|
| "pinned"
|
||||||
| "property.hasLink"
|
| "property.hasLink"
|
||||||
| "property.hasTaskList"
|
| "property.hasTaskList"
|
||||||
| "property.hasCode";
|
| "property.hasCode";
|
||||||
|
Reference in New Issue
Block a user