mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: add time filter to timeline
This commit is contained in:
@ -105,11 +105,27 @@ func (s *APIV2Service) ListMemos(ctx context.Context, request *apiv2pb.ListMemos
|
|||||||
if filter.OrderByPinned {
|
if filter.OrderByPinned {
|
||||||
memoFind.OrderByPinned = filter.OrderByPinned
|
memoFind.OrderByPinned = filter.OrderByPinned
|
||||||
}
|
}
|
||||||
if filter.CreatedTsBefore != nil {
|
if filter.DisplayTimeAfter != nil {
|
||||||
memoFind.CreatedTsBefore = filter.CreatedTsBefore
|
displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value")
|
||||||
|
}
|
||||||
|
if displayWithUpdatedTs {
|
||||||
|
memoFind.UpdatedTsAfter = filter.DisplayTimeAfter
|
||||||
|
} else {
|
||||||
|
memoFind.CreatedTsAfter = filter.DisplayTimeAfter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if filter.CreatedTsAfter != nil {
|
if filter.DisplayTimeBefore != nil {
|
||||||
memoFind.CreatedTsAfter = filter.CreatedTsAfter
|
displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value")
|
||||||
|
}
|
||||||
|
if displayWithUpdatedTs {
|
||||||
|
memoFind.UpdatedTsBefore = filter.DisplayTimeBefore
|
||||||
|
} else {
|
||||||
|
memoFind.CreatedTsBefore = filter.DisplayTimeBefore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if filter.Creator != nil {
|
if filter.Creator != nil {
|
||||||
username, err := ExtractUsernameFromName(*filter.Creator)
|
username, err := ExtractUsernameFromName(*filter.Creator)
|
||||||
@ -463,11 +479,27 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G
|
|||||||
if filter.OrderByPinned {
|
if filter.OrderByPinned {
|
||||||
memoFind.OrderByPinned = filter.OrderByPinned
|
memoFind.OrderByPinned = filter.OrderByPinned
|
||||||
}
|
}
|
||||||
if filter.CreatedTsBefore != nil {
|
if filter.DisplayTimeAfter != nil {
|
||||||
memoFind.CreatedTsBefore = filter.CreatedTsBefore
|
displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value")
|
||||||
|
}
|
||||||
|
if displayWithUpdatedTs {
|
||||||
|
memoFind.UpdatedTsAfter = filter.DisplayTimeAfter
|
||||||
|
} else {
|
||||||
|
memoFind.CreatedTsAfter = filter.DisplayTimeAfter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if filter.CreatedTsAfter != nil {
|
if filter.DisplayTimeBefore != nil {
|
||||||
memoFind.CreatedTsAfter = filter.CreatedTsAfter
|
displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value")
|
||||||
|
}
|
||||||
|
if displayWithUpdatedTs {
|
||||||
|
memoFind.UpdatedTsBefore = filter.DisplayTimeBefore
|
||||||
|
} else {
|
||||||
|
memoFind.CreatedTsBefore = filter.DisplayTimeBefore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if filter.RowStatus != nil {
|
if filter.RowStatus != nil {
|
||||||
memoFind.RowStatus = filter.RowStatus
|
memoFind.RowStatus = filter.RowStatus
|
||||||
@ -590,20 +622,20 @@ var ListMemosFilterCELAttributes = []cel.EnvOption{
|
|||||||
cel.Variable("content_search", cel.ListType(cel.StringType)),
|
cel.Variable("content_search", cel.ListType(cel.StringType)),
|
||||||
cel.Variable("visibilities", cel.ListType(cel.StringType)),
|
cel.Variable("visibilities", cel.ListType(cel.StringType)),
|
||||||
cel.Variable("order_by_pinned", cel.BoolType),
|
cel.Variable("order_by_pinned", cel.BoolType),
|
||||||
cel.Variable("created_ts_before", cel.IntType),
|
cel.Variable("display_time_before", cel.IntType),
|
||||||
cel.Variable("created_ts_after", cel.IntType),
|
cel.Variable("display_time_after", cel.IntType),
|
||||||
cel.Variable("creator", cel.StringType),
|
cel.Variable("creator", cel.StringType),
|
||||||
cel.Variable("row_status", cel.StringType),
|
cel.Variable("row_status", cel.StringType),
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListMemosFilter struct {
|
type ListMemosFilter struct {
|
||||||
ContentSearch []string
|
ContentSearch []string
|
||||||
Visibilities []store.Visibility
|
Visibilities []store.Visibility
|
||||||
OrderByPinned bool
|
OrderByPinned bool
|
||||||
CreatedTsBefore *int64
|
DisplayTimeBefore *int64
|
||||||
CreatedTsAfter *int64
|
DisplayTimeAfter *int64
|
||||||
Creator *string
|
Creator *string
|
||||||
RowStatus *store.RowStatus
|
RowStatus *store.RowStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseListMemosFilter(expression string) (*ListMemosFilter, error) {
|
func parseListMemosFilter(expression string) (*ListMemosFilter, error) {
|
||||||
@ -646,12 +678,12 @@ func findField(callExpr *expr.Expr_Call, filter *ListMemosFilter) {
|
|||||||
} else if idExpr.Name == "order_by_pinned" {
|
} else if idExpr.Name == "order_by_pinned" {
|
||||||
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
value := callExpr.Args[1].GetConstExpr().GetBoolValue()
|
||||||
filter.OrderByPinned = value
|
filter.OrderByPinned = value
|
||||||
} else if idExpr.Name == "created_ts_before" {
|
} else if idExpr.Name == "display_time_before" {
|
||||||
createdTsBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
displayTimeBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||||
filter.CreatedTsBefore = &createdTsBefore
|
filter.DisplayTimeBefore = &displayTimeBefore
|
||||||
} else if idExpr.Name == "created_ts_after" {
|
} else if idExpr.Name == "display_time_after" {
|
||||||
createdTsAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
displayTimeAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
|
||||||
filter.CreatedTsAfter = &createdTsAfter
|
filter.DisplayTimeAfter = &displayTimeAfter
|
||||||
} else if idExpr.Name == "creator" {
|
} else if idExpr.Name == "creator" {
|
||||||
creator := callExpr.Args[1].GetConstExpr().GetStringValue()
|
creator := callExpr.Args[1].GetConstExpr().GetStringValue()
|
||||||
filter.Creator = &creator
|
filter.Creator = &creator
|
||||||
|
@ -55,6 +55,12 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
|
|||||||
if v := find.CreatedTsAfter; v != nil {
|
if v := find.CreatedTsAfter; v != nil {
|
||||||
where, args = append(where, "UNIX_TIMESTAMP(`memo`.`created_ts`) > ?"), append(args, *v)
|
where, args = append(where, "UNIX_TIMESTAMP(`memo`.`created_ts`) > ?"), append(args, *v)
|
||||||
}
|
}
|
||||||
|
if v := find.UpdatedTsBefore; v != nil {
|
||||||
|
where, args = append(where, "UNIX_TIMESTAMP(`memo`.`updated_ts`) < ?"), append(args, *v)
|
||||||
|
}
|
||||||
|
if v := find.UpdatedTsAfter; v != nil {
|
||||||
|
where, args = append(where, "UNIX_TIMESTAMP(`memo`.`updated_ts`) > ?"), append(args, *v)
|
||||||
|
}
|
||||||
if v := find.ContentSearch; len(v) != 0 {
|
if v := find.ContentSearch; len(v) != 0 {
|
||||||
for _, s := range v {
|
for _, s := range v {
|
||||||
where, args = append(where, "`memo`.`content` LIKE ?"), append(args, "%"+s+"%")
|
where, args = append(where, "`memo`.`content` LIKE ?"), append(args, "%"+s+"%")
|
||||||
|
@ -46,6 +46,12 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
|
|||||||
if v := find.CreatedTsAfter; v != nil {
|
if v := find.CreatedTsAfter; v != nil {
|
||||||
where, args = append(where, "memo.created_ts > "+placeholder(len(args)+1)), append(args, *v)
|
where, args = append(where, "memo.created_ts > "+placeholder(len(args)+1)), append(args, *v)
|
||||||
}
|
}
|
||||||
|
if v := find.UpdatedTsBefore; v != nil {
|
||||||
|
where, args = append(where, "memo.updated_ts < "+placeholder(len(args)+1)), append(args, *v)
|
||||||
|
}
|
||||||
|
if v := find.UpdatedTsAfter; v != nil {
|
||||||
|
where, args = append(where, "memo.updated_ts > "+placeholder(len(args)+1)), append(args, *v)
|
||||||
|
}
|
||||||
if v := find.ContentSearch; len(v) != 0 {
|
if v := find.ContentSearch; len(v) != 0 {
|
||||||
for _, s := range v {
|
for _, s := range v {
|
||||||
where, args = append(where, "memo.content LIKE "+placeholder(len(args)+1)), append(args, fmt.Sprintf("%%%s%%", s))
|
where, args = append(where, "memo.content LIKE "+placeholder(len(args)+1)), append(args, fmt.Sprintf("%%%s%%", s))
|
||||||
|
@ -45,6 +45,12 @@ func (d *DB) ListMemos(ctx context.Context, find *store.FindMemo) ([]*store.Memo
|
|||||||
if v := find.CreatedTsAfter; v != nil {
|
if v := find.CreatedTsAfter; v != nil {
|
||||||
where, args = append(where, "memo.created_ts > ?"), append(args, *v)
|
where, args = append(where, "memo.created_ts > ?"), append(args, *v)
|
||||||
}
|
}
|
||||||
|
if v := find.UpdatedTsBefore; v != nil {
|
||||||
|
where, args = append(where, "memo.updated_ts < ?"), append(args, *v)
|
||||||
|
}
|
||||||
|
if v := find.UpdatedTsAfter; v != nil {
|
||||||
|
where, args = append(where, "memo.updated_ts > ?"), append(args, *v)
|
||||||
|
}
|
||||||
if v := find.ContentSearch; len(v) != 0 {
|
if v := find.ContentSearch; len(v) != 0 {
|
||||||
for _, s := range v {
|
for _, s := range v {
|
||||||
where, args = append(where, "memo.content LIKE ?"), append(args, fmt.Sprintf("%%%s%%", s))
|
where, args = append(where, "memo.content LIKE ?"), append(args, fmt.Sprintf("%%%s%%", s))
|
||||||
|
@ -54,6 +54,8 @@ type FindMemo struct {
|
|||||||
CreatorID *int32
|
CreatorID *int32
|
||||||
CreatedTsAfter *int64
|
CreatedTsAfter *int64
|
||||||
CreatedTsBefore *int64
|
CreatedTsBefore *int64
|
||||||
|
UpdatedTsAfter *int64
|
||||||
|
UpdatedTsBefore *int64
|
||||||
|
|
||||||
// Domain specific fields
|
// Domain specific fields
|
||||||
ContentSearch []string
|
ContentSearch []string
|
||||||
|
@ -6,6 +6,7 @@ interface Props {
|
|||||||
// Format: 2021-1
|
// Format: 2021-1
|
||||||
month: string;
|
month: string;
|
||||||
data: Record<string, number>;
|
data: Record<string, number>;
|
||||||
|
onClick?: (date: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBgColor = (count: number, maxCount: number) => {
|
const getBgColor = (count: number, maxCount: number) => {
|
||||||
@ -26,7 +27,7 @@ const getBgColor = (count: number, maxCount: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ActivityCalendar = (props: Props) => {
|
const ActivityCalendar = (props: Props) => {
|
||||||
const { month: monthStr, data } = props;
|
const { month: monthStr, data, onClick } = props;
|
||||||
const year = new Date(monthStr).getFullYear();
|
const year = new Date(monthStr).getFullYear();
|
||||||
const month = new Date(monthStr).getMonth() + 1;
|
const month = new Date(monthStr).getMonth() + 1;
|
||||||
const dayInMonth = new Date(year, month, 0).getDate();
|
const dayInMonth = new Date(year, month, 0).getDate();
|
||||||
@ -60,6 +61,7 @@ const ActivityCalendar = (props: Props) => {
|
|||||||
getBgColor(count, maxCount),
|
getBgColor(count, maxCount),
|
||||||
isToday && "border-gray-600 dark:!border-gray-400"
|
isToday && "border-gray-600 dark:!border-gray-400"
|
||||||
)}
|
)}
|
||||||
|
onClick={() => count && onClick && onClick(date)}
|
||||||
></div>
|
></div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
|
@ -10,7 +10,7 @@ import MemoFilter from "@/components/MemoFilter";
|
|||||||
import MemoView from "@/components/MemoView";
|
import MemoView from "@/components/MemoView";
|
||||||
import MobileHeader from "@/components/MobileHeader";
|
import MobileHeader from "@/components/MobileHeader";
|
||||||
import { memoServiceClient } from "@/grpcweb";
|
import { memoServiceClient } from "@/grpcweb";
|
||||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
import { DAILY_TIMESTAMP, DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
||||||
import { getNormalizedTimeString, getTimeStampByDate } from "@/helpers/datetime";
|
import { getNormalizedTimeString, getTimeStampByDate } from "@/helpers/datetime";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||||
@ -52,6 +52,7 @@ const Timeline = () => {
|
|||||||
const memoList = useMemoList();
|
const memoList = useMemoList();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
|
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
|
||||||
|
const [selectedDay, setSelectedDay] = useState<string | undefined>();
|
||||||
const [isRequesting, setIsRequesting] = useState(true);
|
const [isRequesting, setIsRequesting] = useState(true);
|
||||||
const [isComplete, setIsComplete] = useState(false);
|
const [isComplete, setIsComplete] = useState(false);
|
||||||
const { tag: tagQuery, text: textQuery } = filterStore.state;
|
const { tag: tagQuery, text: textQuery } = filterStore.state;
|
||||||
@ -61,7 +62,7 @@ const Timeline = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
memoList.reset();
|
memoList.reset();
|
||||||
fetchMemos();
|
fetchMemos();
|
||||||
}, [tagQuery, textQuery]);
|
}, [selectedDay, tagQuery, textQuery]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@ -97,6 +98,12 @@ const Timeline = () => {
|
|||||||
if (contentSearch.length > 0) {
|
if (contentSearch.length > 0) {
|
||||||
filters.push(`content_search == [${contentSearch.join(", ")}]`);
|
filters.push(`content_search == [${contentSearch.join(", ")}]`);
|
||||||
}
|
}
|
||||||
|
if (selectedDay) {
|
||||||
|
const selectedDateStamp = getTimeStampByDate(selectedDay) + new Date().getTimezoneOffset() * 60 * 1000;
|
||||||
|
filters.push(
|
||||||
|
...[`display_time_after == ${selectedDateStamp / 1000}`, `display_time_before == ${(selectedDateStamp + DAILY_TIMESTAMP) / 1000}`]
|
||||||
|
);
|
||||||
|
}
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
const data = await memoStore.fetchMemos({
|
const data = await memoStore.fetchMemos({
|
||||||
filter: filters.join(" && "),
|
filter: filters.join(" && "),
|
||||||
@ -143,7 +150,7 @@ const Timeline = () => {
|
|||||||
<span className="opacity-60">{new Date(group.month).getFullYear()}</span>
|
<span className="opacity-60">{new Date(group.month).getFullYear()}</span>
|
||||||
<span className="text-xs opacity-40">Total: {sum(Object.values(group.data))}</span>
|
<span className="text-xs opacity-40">Total: {sum(Object.values(group.data))}</span>
|
||||||
</div>
|
</div>
|
||||||
<ActivityCalendar month={group.month} data={group.data} />
|
<ActivityCalendar month={group.month} data={group.data} onClick={(date) => setSelectedDay(date)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={classNames("flex flex-col justify-start items-start", md ? "w-[calc(100%-8rem)]" : "w-full")}>
|
<div className={classNames("flex flex-col justify-start items-start", md ? "w-[calc(100%-8rem)]" : "w-full")}>
|
||||||
|
Reference in New Issue
Block a user