mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
Merge branch 'main' of https://github.com/usememos/memos
This commit is contained in:
13
.github/semantic.yml
vendored
Normal file
13
.github/semantic.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Only check PR title
|
||||||
|
titleOnly: true
|
||||||
|
|
||||||
|
types:
|
||||||
|
- feat
|
||||||
|
- fix
|
||||||
|
- refactor
|
||||||
|
- chore
|
||||||
|
- docs
|
||||||
|
- style
|
||||||
|
- test
|
@@ -140,6 +140,7 @@ func (s *Store) GetWorkspaceGeneralSetting(ctx context.Context) (*storepb.Worksp
|
|||||||
const (
|
const (
|
||||||
// DefaultContentLengthLimit is the default limit of content length in bytes. 8KB.
|
// DefaultContentLengthLimit is the default limit of content length in bytes. 8KB.
|
||||||
DefaultContentLengthLimit = 8 * 1024
|
DefaultContentLengthLimit = 8 * 1024
|
||||||
|
DefaultReaction = "👍"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.WorkspaceMemoRelatedSetting, error) {
|
func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.WorkspaceMemoRelatedSetting, error) {
|
||||||
@@ -157,6 +158,9 @@ func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.Wo
|
|||||||
if workspaceMemoRelatedSetting.ContentLengthLimit < DefaultContentLengthLimit {
|
if workspaceMemoRelatedSetting.ContentLengthLimit < DefaultContentLengthLimit {
|
||||||
workspaceMemoRelatedSetting.ContentLengthLimit = DefaultContentLengthLimit
|
workspaceMemoRelatedSetting.ContentLengthLimit = DefaultContentLengthLimit
|
||||||
}
|
}
|
||||||
|
if len(workspaceMemoRelatedSetting.Reactions) == 0 {
|
||||||
|
workspaceMemoRelatedSetting.Reactions = append(workspaceMemoRelatedSetting.Reactions, DefaultReaction)
|
||||||
|
}
|
||||||
s.workspaceSettingCache.Store(storepb.WorkspaceSettingKey_MEMO_RELATED.String(), &storepb.WorkspaceSetting{
|
s.workspaceSettingCache.Store(storepb.WorkspaceSettingKey_MEMO_RELATED.String(), &storepb.WorkspaceSetting{
|
||||||
Key: storepb.WorkspaceSettingKey_MEMO_RELATED,
|
Key: storepb.WorkspaceSettingKey_MEMO_RELATED,
|
||||||
Value: &storepb.WorkspaceSetting_MemoRelatedSetting{MemoRelatedSetting: workspaceMemoRelatedSetting},
|
Value: &storepb.WorkspaceSetting_MemoRelatedSetting{MemoRelatedSetting: workspaceMemoRelatedSetting},
|
||||||
|
@@ -5,10 +5,10 @@ import { useWorkspaceSettingStore } from "@/store/v1";
|
|||||||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
|
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
|
||||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
import { cn } from "@/utils/utils";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
// Format: 2021-1
|
month: string; // Format: 2021-1
|
||||||
month: string;
|
|
||||||
selectedDate: string;
|
selectedDate: string;
|
||||||
data: Record<string, number>;
|
data: Record<string, number>;
|
||||||
onClick?: (date: string) => void;
|
onClick?: (date: string) => void;
|
||||||
@@ -16,9 +16,8 @@ interface Props {
|
|||||||
|
|
||||||
const getCellAdditionalStyles = (count: number, maxCount: number) => {
|
const getCellAdditionalStyles = (count: number, maxCount: number) => {
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
return "bg-gray-100 text-gray-400 dark:bg-gray-700 dark:text-gray-500";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const ratio = count / maxCount;
|
const ratio = count / maxCount;
|
||||||
if (ratio > 0.7) {
|
if (ratio > 0.7) {
|
||||||
return "bg-primary-darker text-gray-100 dark:opacity-80";
|
return "bg-primary-darker text-gray-100 dark:opacity-80";
|
||||||
@@ -36,72 +35,64 @@ const ActivityCalendar = (props: Props) => {
|
|||||||
const weekStartDayOffset = (
|
const weekStartDayOffset = (
|
||||||
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL).generalSetting || WorkspaceGeneralSetting.fromPartial({})
|
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL).generalSetting || WorkspaceGeneralSetting.fromPartial({})
|
||||||
).weekStartDayOffset;
|
).weekStartDayOffset;
|
||||||
|
|
||||||
const year = dayjs(monthStr).toDate().getFullYear();
|
const year = dayjs(monthStr).toDate().getFullYear();
|
||||||
const month = dayjs(monthStr).toDate().getMonth() + 1;
|
const month = dayjs(monthStr).toDate().getMonth();
|
||||||
const dayInMonth = new Date(year, month, 0).getDate();
|
const dayInMonth = new Date(year, month + 1, 0).getDate();
|
||||||
const firstDay = (((new Date(year, month - 1, 1).getDay() - weekStartDayOffset) % 7) + 7) % 7;
|
const firstDay = (((new Date(year, month, 1).getDay() - weekStartDayOffset) % 7) + 7) % 7;
|
||||||
const lastDay = new Date(year, month - 1, dayInMonth).getDay() - weekStartDayOffset;
|
const lastDay = new Date(year, month, dayInMonth).getDay() - weekStartDayOffset;
|
||||||
|
const prevMonthDays = new Date(year, month, 0).getDate();
|
||||||
|
|
||||||
const WEEK_DAYS = [t("days.sun"), t("days.mon"), t("days.tue"), t("days.wed"), t("days.thu"), t("days.fri"), t("days.sat")];
|
const WEEK_DAYS = [t("days.sun"), t("days.mon"), t("days.tue"), t("days.wed"), t("days.thu"), t("days.fri"), t("days.sat")];
|
||||||
const weekDays = WEEK_DAYS.slice(weekStartDayOffset).concat(WEEK_DAYS.slice(0, weekStartDayOffset));
|
const weekDays = WEEK_DAYS.slice(weekStartDayOffset).concat(WEEK_DAYS.slice(0, weekStartDayOffset));
|
||||||
const maxCount = Math.max(...Object.values(data));
|
const maxCount = Math.max(...Object.values(data));
|
||||||
const days = [];
|
const days = [];
|
||||||
|
|
||||||
for (let i = 0; i < firstDay; i++) {
|
// Fill in previous month's days.
|
||||||
days.push(0);
|
for (let i = firstDay - 1; i >= 0; i--) {
|
||||||
|
days.push({ day: prevMonthDays - i, isCurrentMonth: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill in current month's days.
|
||||||
for (let i = 1; i <= dayInMonth; i++) {
|
for (let i = 1; i <= dayInMonth; i++) {
|
||||||
days.push(i);
|
days.push({ day: i, isCurrentMonth: true });
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 6 - lastDay; i++) {
|
|
||||||
days.push(0);
|
// Fill in next month's days.
|
||||||
|
for (let i = 1; i < 7 - lastDay; i++) {
|
||||||
|
days.push({ day: i, isCurrentMonth: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx("w-full h-auto shrink-0 grid grid-cols-7 grid-flow-row gap-1")}>
|
<div className={clsx("w-full h-auto shrink-0 grid grid-cols-7 grid-flow-row gap-1")}>
|
||||||
{weekDays.map((day, index) => {
|
{weekDays.map((day, index) => (
|
||||||
return (
|
|
||||||
<div key={index} className={clsx("w-6 h-5 text-xs flex justify-center items-center cursor-default opacity-60")}>
|
<div key={index} className={clsx("w-6 h-5 text-xs flex justify-center items-center cursor-default opacity-60")}>
|
||||||
{day}
|
{day}
|
||||||
</div>
|
</div>
|
||||||
);
|
))}
|
||||||
})}
|
{days.map((item, index) => {
|
||||||
{days.map((day, index) => {
|
const date = dayjs(`${year}-${month + 1}-${item.day}`).format("YYYY-MM-DD");
|
||||||
const date = dayjs(`${year}-${month}-${day}`).format("YYYY-MM-DD");
|
const count = item.isCurrentMonth ? data[date] || 0 : 0;
|
||||||
const count = data[date] || 0;
|
|
||||||
const isToday = dayjs().format("YYYY-MM-DD") === date;
|
const isToday = dayjs().format("YYYY-MM-DD") === date;
|
||||||
const tooltipText = count ? t("memo.count-memos-in-date", { count: count, date: date }) : date;
|
const tooltipText = count ? t("memo.count-memos-in-date", { count: count, date: date }) : date;
|
||||||
const isSelected = dayjs(props.selectedDate).format("YYYY-MM-DD") === date;
|
const isSelected = dayjs(props.selectedDate).format("YYYY-MM-DD") === date;
|
||||||
return day ? (
|
|
||||||
count > 0 ? (
|
return (
|
||||||
<Tooltip className="shrink-0" key={`${date}-${index}`} title={tooltipText} placement="top" arrow>
|
<Tooltip className="shrink-0" key={`${date}-${index}`} title={tooltipText} placement="top" arrow>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={cn(
|
||||||
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default",
|
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default",
|
||||||
getCellAdditionalStyles(count, maxCount),
|
"text-gray-400",
|
||||||
isToday && "border-zinc-400 dark:border-zinc-300",
|
item.isCurrentMonth ? getCellAdditionalStyles(count, maxCount) : "opacity-60",
|
||||||
isSelected && "font-bold border-zinc-400 dark:border-zinc-300",
|
isToday && "border-zinc-400",
|
||||||
|
isSelected && "font-bold border-zinc-400",
|
||||||
!isToday && !isSelected && "border-transparent",
|
!isToday && !isSelected && "border-transparent",
|
||||||
)}
|
)}
|
||||||
onClick={() => count && onClick && onClick(date)}
|
onClick={() => count && onClick && onClick(date)}
|
||||||
>
|
>
|
||||||
{day}
|
{item.day}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
key={`${date}-${index}`}
|
|
||||||
className={clsx(
|
|
||||||
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default",
|
|
||||||
"bg-gray-100 text-gray-400 dark:bg-zinc-800 dark:text-gray-500",
|
|
||||||
isToday && "border-zinc-400 dark:border-zinc-500",
|
|
||||||
!isToday && !isSelected && "border-transparent",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{day}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<div key={`${date}-${index}`} className="shrink-0 w-6 h-6 opacity-0"></div>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,18 +1,8 @@
|
|||||||
import { Divider, Tooltip } from "@mui/joy";
|
import { Divider, Tooltip } from "@mui/joy";
|
||||||
import { Button } from "@usememos/mui";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { countBy } from "lodash-es";
|
import { countBy } from "lodash-es";
|
||||||
import {
|
import { CalendarDaysIcon, CheckCircleIcon, ChevronLeftIcon, ChevronRightIcon, Code2Icon, LinkIcon, ListTodoIcon } from "lucide-react";
|
||||||
CalendarDaysIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
ChevronLeftIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
Code2Icon,
|
|
||||||
LinkIcon,
|
|
||||||
ListTodoIcon,
|
|
||||||
MoreVerticalIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
@@ -20,7 +10,6 @@ import i18n from "@/i18n";
|
|||||||
import { useMemoFilterStore, useMemoMetadataStore } from "@/store/v1";
|
import { useMemoFilterStore, useMemoMetadataStore } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import ActivityCalendar from "./ActivityCalendar";
|
import ActivityCalendar from "./ActivityCalendar";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
|
|
||||||
|
|
||||||
interface UserMemoStats {
|
interface UserMemoStats {
|
||||||
link: number;
|
link: number;
|
||||||
@@ -71,33 +60,26 @@ const UserStatisticsView = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800">
|
<div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800">
|
||||||
<div className="w-full mb-1 flex flex-row justify-between items-center">
|
<div className="w-full mb-1 flex flex-row justify-between items-center gap-1">
|
||||||
<div className="relative text-base font-medium leading-6 flex flex-row items-center dark:text-gray-400">
|
<div className="relative text-sm font-medium inline-flex flex-row items-center w-auto dark:text-gray-400 truncate">
|
||||||
<CalendarDaysIcon className="w-5 h-auto mr-1 opacity-60" strokeWidth={1.5} />
|
<CalendarDaysIcon className="w-4 h-auto mr-1 opacity-60 shrink-0" strokeWidth={1.5} />
|
||||||
<span>{dayjs(visibleMonthString).toDate().toLocaleString(i18n.language, { year: "numeric", month: "long" })}</span>
|
<span className="truncate">
|
||||||
|
{dayjs(visibleMonthString).toDate().toLocaleString(i18n.language, { year: "numeric", month: "long" })}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="invisible group-hover:visible flex justify-end items-center">
|
<div className="flex justify-end items-center shrink-0">
|
||||||
<Popover>
|
<span
|
||||||
<PopoverTrigger>
|
className="cursor-pointer hover:opacity-80"
|
||||||
<MoreVerticalIcon className="w-4 h-auto shrink-0 opacity-60" />
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="flex flex-row justify-end items-center" align="end" alignOffset={-12}>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).subtract(1, "month").format("YYYY-MM"))}
|
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).subtract(1, "month").format("YYYY-MM"))}
|
||||||
>
|
>
|
||||||
<ChevronLeftIcon className="w-5 h-auto shrink-0 opacity-60" />
|
<ChevronLeftIcon className="w-4 h-auto shrink-0 opacity-60" />
|
||||||
</Button>
|
</span>
|
||||||
<Button
|
<span
|
||||||
size="sm"
|
className="cursor-pointer hover:opacity-80"
|
||||||
variant="plain"
|
|
||||||
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).add(1, "month").format("YYYY-MM"))}
|
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).add(1, "month").format("YYYY-MM"))}
|
||||||
>
|
>
|
||||||
<ChevronRightIcon className="w-5 h-auto shrink-0 opacity-60" />
|
<ChevronRightIcon className="w-4 h-auto shrink-0 opacity-60" />
|
||||||
</Button>
|
</span>
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
Reference in New Issue
Block a user