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
|
||||
}
|
||||
}
|
||||
if filterExpr.Pinned {
|
||||
pinned := true
|
||||
find.Pinned = &pinned
|
||||
}
|
||||
if filterExpr.HasLink {
|
||||
find.PayloadFind.HasLink = true
|
||||
}
|
||||
@@ -74,6 +78,7 @@ var MemoFilterCELAttributes = []cel.EnvOption{
|
||||
cel.Variable("tag_search", cel.ListType(cel.StringType)),
|
||||
cel.Variable("display_time_before", cel.IntType),
|
||||
cel.Variable("display_time_after", cel.IntType),
|
||||
cel.Variable("pinned", cel.BoolType),
|
||||
cel.Variable("has_link", cel.BoolType),
|
||||
cel.Variable("has_task_list", cel.BoolType),
|
||||
cel.Variable("has_code", cel.BoolType),
|
||||
@@ -85,6 +90,7 @@ type MemoFilter struct {
|
||||
TagSearch []string
|
||||
DisplayTimeBefore *int64
|
||||
DisplayTimeAfter *int64
|
||||
Pinned bool
|
||||
HasLink bool
|
||||
HasTaskList bool
|
||||
HasCode bool
|
||||
@@ -134,6 +140,9 @@ func findMemoField(callExpr *exprv1.Expr_Call, filter *MemoFilter) {
|
||||
} else if idExpr.Name == "display_time_after" {
|
||||
displayTimeAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||
filter.DisplayTimeAfter = &displayTimeAfter
|
||||
} else if idExpr.Name == "pinned" {
|
||||
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
||||
filter.Pinned = value
|
||||
} else if idExpr.Name == "has_link" {
|
||||
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
||||
filter.HasLink = value
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 { useSearchParams } from "react-router-dom";
|
||||
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} />,
|
||||
contentSearch: <SearchIcon className={className} />,
|
||||
displayTime: <CalendarIcon className={className} />,
|
||||
pinned: <PinIcon className={className} />,
|
||||
"property.hasLink": <LinkIcon className={className} />,
|
||||
"property.hasTaskList": <CheckCircleIcon className={className} />,
|
||||
"property.hasCode": <CodeIcon className={className} />,
|
||||
|
@@ -1,12 +1,15 @@
|
||||
import { Tooltip } from "@mui/joy";
|
||||
import dayjs from "dayjs";
|
||||
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 { useState } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import { matchPath, useLocation } from "react-router-dom";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import i18n from "@/i18n";
|
||||
import { Routes } from "@/router";
|
||||
import { useMemoFilterStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
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 t = useTranslate();
|
||||
const location = useLocation();
|
||||
const memoFilterStore = useMemoFilterStore();
|
||||
const currentUser = useCurrentUser();
|
||||
const [memoTypeStats, setMemoTypeStats] = useState<UserStats_MemoTypeStats>(UserStats_MemoTypeStats.fromPartial({}));
|
||||
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
|
||||
const [selectedDate] = useState(new Date());
|
||||
@@ -92,7 +97,22 @@ const StatisticsView = observer(() => {
|
||||
onClick={onCalendarClick}
|
||||
/>
|
||||
</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
|
||||
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: "" })}
|
||||
|
@@ -23,6 +23,8 @@ const Home = observer(() => {
|
||||
contentSearch.push(`"${filter.value}"`);
|
||||
} else if (filter.factor === "tagSearch") {
|
||||
tagSearch.push(`"${filter.value}"`);
|
||||
} else if (filter.factor === "pinned") {
|
||||
conditions.push(`pinned == true`);
|
||||
} else if (filter.factor === "property.hasLink") {
|
||||
conditions.push(`has_link == true`);
|
||||
} else if (filter.factor === "property.hasTaskList") {
|
||||
|
@@ -6,7 +6,6 @@ import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useParams } from "react-router-dom";
|
||||
import MemoFilters from "@/components/MemoFilters";
|
||||
import MemoView from "@/components/MemoView";
|
||||
import PagedMemoList from "@/components/PagedMemoList";
|
||||
import UserAvatar from "@/components/UserAvatar";
|
||||
@@ -78,7 +77,7 @@ const UserProfile = observer(() => {
|
||||
|
||||
return (
|
||||
<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 &&
|
||||
(user ? (
|
||||
<>
|
||||
@@ -99,7 +98,6 @@ const UserProfile = observer(() => {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<MemoFilters />
|
||||
<PagedMemoList
|
||||
renderer={(memo: Memo) => (
|
||||
<MemoView key={`${memo.name}-${memo.displayTime}`} memo={memo} showVisibility showPinned compact />
|
||||
|
@@ -7,6 +7,7 @@ export type FilterFactor =
|
||||
| "visibility"
|
||||
| "contentSearch"
|
||||
| "displayTime"
|
||||
| "pinned"
|
||||
| "property.hasLink"
|
||||
| "property.hasTaskList"
|
||||
| "property.hasCode";
|
||||
|
Reference in New Issue
Block a user