diff --git a/web/src/components/ActivityCalendar/ActivityCalendar.tsx b/web/src/components/ActivityCalendar/ActivityCalendar.tsx index bb0fb733..aea59114 100644 --- a/web/src/components/ActivityCalendar/ActivityCalendar.tsx +++ b/web/src/components/ActivityCalendar/ActivityCalendar.tsx @@ -1,5 +1,6 @@ import { Tooltip } from "@mui/joy"; import dayjs from "dayjs"; +import { observer } from "mobx-react-lite"; import { memo, useMemo } from "react"; import { workspaceStore } from "@/store/v2"; import type { ActivityCalendarProps, CalendarDay } from "@/types/statistics"; @@ -67,104 +68,106 @@ const CalendarCell = memo( CalendarCell.displayName = "CalendarCell"; -export const ActivityCalendar = memo((props: ActivityCalendarProps) => { - const t = useTranslate(); - const { month: monthStr, data, onClick } = props; - const weekStartDayOffset = workspaceStore.state.generalSetting.weekStartDayOffset; +export const ActivityCalendar = memo( + observer((props: ActivityCalendarProps) => { + const t = useTranslate(); + const { month: monthStr, data, onClick } = props; + const weekStartDayOffset = workspaceStore.state.generalSetting.weekStartDayOffset; - const { days, weekDays, maxCount } = useMemo(() => { - const yearValue = dayjs(monthStr).toDate().getFullYear(); - const monthValue = dayjs(monthStr).toDate().getMonth(); - const dayInMonth = new Date(yearValue, monthValue + 1, 0).getDate(); - const firstDay = (((new Date(yearValue, monthValue, 1).getDay() - weekStartDayOffset) % 7) + 7) % 7; - const lastDay = new Date(yearValue, monthValue, dayInMonth).getDay() - weekStartDayOffset; - const prevMonthDays = new Date(yearValue, monthValue, 0).getDate(); + const { days, weekDays, maxCount } = useMemo(() => { + const yearValue = dayjs(monthStr).toDate().getFullYear(); + const monthValue = dayjs(monthStr).toDate().getMonth(); + const dayInMonth = new Date(yearValue, monthValue + 1, 0).getDate(); + const firstDay = (((new Date(yearValue, monthValue, 1).getDay() - weekStartDayOffset) % 7) + 7) % 7; + const lastDay = new Date(yearValue, monthValue, dayInMonth).getDay() - weekStartDayOffset; + const prevMonthDays = new Date(yearValue, monthValue, 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 weekDaysOrdered = WEEK_DAYS.slice(weekStartDayOffset).concat(WEEK_DAYS.slice(0, weekStartDayOffset)); + 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 weekDaysOrdered = WEEK_DAYS.slice(weekStartDayOffset).concat(WEEK_DAYS.slice(0, weekStartDayOffset)); - const daysArray: CalendarDay[] = []; + const daysArray: CalendarDay[] = []; - // Previous month's days - for (let i = firstDay - 1; i >= 0; i--) { - daysArray.push({ day: prevMonthDays - i, isCurrentMonth: false }); - } + // Previous month's days + for (let i = firstDay - 1; i >= 0; i--) { + daysArray.push({ day: prevMonthDays - i, isCurrentMonth: false }); + } - // Current month's days - for (let i = 1; i <= dayInMonth; i++) { - const date = dayjs(`${yearValue}-${monthValue + 1}-${i}`).format("YYYY-MM-DD"); - daysArray.push({ day: i, isCurrentMonth: true, date }); - } + // Current month's days + for (let i = 1; i <= dayInMonth; i++) { + const date = dayjs(`${yearValue}-${monthValue + 1}-${i}`).format("YYYY-MM-DD"); + daysArray.push({ day: i, isCurrentMonth: true, date }); + } - // Next month's days - for (let i = 1; i < 7 - lastDay; i++) { - daysArray.push({ day: i, isCurrentMonth: false }); - } + // Next month's days + for (let i = 1; i < 7 - lastDay; i++) { + daysArray.push({ day: i, isCurrentMonth: false }); + } - const maxCountValue = Math.max(...Object.values(data), 1); + const maxCountValue = Math.max(...Object.values(data), 1); - return { - year: yearValue, - month: monthValue, - days: daysArray, - weekDays: weekDaysOrdered, - maxCount: maxCountValue, - }; - }, [monthStr, data, weekStartDayOffset, t]); + return { + year: yearValue, + month: monthValue, + days: daysArray, + weekDays: weekDaysOrdered, + maxCount: maxCountValue, + }; + }, [monthStr, data, weekStartDayOffset, t]); - const today = useMemo(() => dayjs().format("YYYY-MM-DD"), []); - const selectedDateFormatted = useMemo(() => dayjs(props.selectedDate).format("YYYY-MM-DD"), [props.selectedDate]); + const today = useMemo(() => dayjs().format("YYYY-MM-DD"), []); + const selectedDateFormatted = useMemo(() => dayjs(props.selectedDate).format("YYYY-MM-DD"), [props.selectedDate]); + + return ( +
+ {weekDays.map((day, index) => ( +
+ {day} +
+ ))} + {days.map((dayInfo, index) => { + if (!dayInfo.isCurrentMonth) { + return ( + + ); + } + + const date = dayInfo.date!; + const count = data[date] || 0; + const isToday = today === date; + const isSelected = selectedDateFormatted === date; + const tooltipText = + count === 0 + ? date + : t("memo.count-memos-in-date", { + count: count, + memos: count === 1 ? t("common.memo") : t("common.memos"), + date: date, + }).toLowerCase(); - return ( -
- {weekDays.map((day, index) => ( -
- {day} -
- ))} - {days.map((dayInfo, index) => { - if (!dayInfo.isCurrentMonth) { return ( onClick?.(date)} + tooltipText={tooltipText} /> ); - } - - const date = dayInfo.date!; - const count = data[date] || 0; - const isToday = today === date; - const isSelected = selectedDateFormatted === date; - const tooltipText = - count === 0 - ? date - : t("memo.count-memos-in-date", { - count: count, - memos: count === 1 ? t("common.memo") : t("common.memos"), - date: date, - }).toLowerCase(); - - return ( - onClick?.(date)} - tooltipText={tooltipText} - /> - ); - })} -
- ); -}); + })} +
+ ); + }), +); ActivityCalendar.displayName = "ActivityCalendar"; diff --git a/web/src/components/BrandBanner.tsx b/web/src/components/BrandBanner.tsx index 443015ab..97ac17ea 100644 --- a/web/src/components/BrandBanner.tsx +++ b/web/src/components/BrandBanner.tsx @@ -1,3 +1,4 @@ +import { observer } from "mobx-react-lite"; import { workspaceStore } from "@/store/v2"; import { cn } from "@/utils"; import UserAvatar from "./UserAvatar"; @@ -7,7 +8,7 @@ interface Props { collapsed?: boolean; } -const BrandBanner = (props: Props) => { +const BrandBanner = observer((props: Props) => { const { collapsed } = props; const workspaceGeneralSetting = workspaceStore.state.generalSetting; const title = workspaceGeneralSetting.customProfile?.title || "Memos"; @@ -21,6 +22,6 @@ const BrandBanner = (props: Props) => { ); -}; +}); export default BrandBanner; diff --git a/web/src/components/MemoActionMenu.tsx b/web/src/components/MemoActionMenu.tsx index 5d1affb8..a27c1101 100644 --- a/web/src/components/MemoActionMenu.tsx +++ b/web/src/components/MemoActionMenu.tsx @@ -16,8 +16,7 @@ import toast from "react-hot-toast"; import { useLocation } from "react-router-dom"; import { markdownServiceClient } from "@/grpcweb"; import useNavigateTo from "@/hooks/useNavigateTo"; -import { memoStore } from "@/store/v2"; -import { userStore } from "@/store/v2"; +import { memoStore, userStore } from "@/store/v2"; 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"; diff --git a/web/src/components/MemoContent/Tag.tsx b/web/src/components/MemoContent/Tag.tsx index 581fb3e3..560f8add 100644 --- a/web/src/components/MemoContent/Tag.tsx +++ b/web/src/components/MemoContent/Tag.tsx @@ -1,9 +1,10 @@ +import { observer } from "mobx-react-lite"; import { useContext } from "react"; import { useLocation } from "react-router-dom"; import useNavigateTo from "@/hooks/useNavigateTo"; import { Routes } from "@/router"; -import memoFilterStore, { MemoFilter } from "@/store/v2/memoFilter"; -import { stringifyFilters } from "@/store/v2/memoFilter"; +import { memoFilterStore } from "@/store/v2"; +import { stringifyFilters, MemoFilter } from "@/store/v2/memoFilter"; import { cn } from "@/utils"; import { RendererContext } from "./types"; @@ -11,7 +12,7 @@ interface Props { content: string; } -const Tag: React.FC = ({ content }: Props) => { +const Tag = observer(({ content }: Props) => { const context = useContext(RendererContext); const location = useLocation(); const navigateTo = useNavigateTo(); @@ -50,6 +51,6 @@ const Tag: React.FC = ({ content }: Props) => { #{content} ); -}; +}); export default Tag; diff --git a/web/src/components/MemoFilters.tsx b/web/src/components/MemoFilters.tsx index 7f66248e..dc9430a3 100644 --- a/web/src/components/MemoFilters.tsx +++ b/web/src/components/MemoFilters.tsx @@ -1,12 +1,13 @@ import { isEqual } from "lodash-es"; import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, HashIcon, LinkIcon, BookmarkIcon, SearchIcon, XIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { useEffect } from "react"; import { useSearchParams } from "react-router-dom"; -import memoFilterStore from "@/store/v2/memoFilter"; +import { memoFilterStore } from "@/store/v2"; import { FilterFactor, getMemoFilterKey, MemoFilter, stringifyFilters } from "@/store/v2/memoFilter"; import { useTranslate } from "@/utils/i18n"; -const MemoFilters = () => { +const MemoFilters = observer(() => { const t = useTranslate(); const [, setSearchParams] = useSearchParams(); const filters = memoFilterStore.filters; @@ -60,7 +61,7 @@ const MemoFilters = () => { ))} ); -}; +}); const FactorIcon = ({ factor, className }: { factor: FilterFactor; className?: string }) => { const iconMap = { diff --git a/web/src/components/MemoReactionListView.tsx b/web/src/components/MemoReactionListView.tsx index 5ec1189c..f7853527 100644 --- a/web/src/components/MemoReactionListView.tsx +++ b/web/src/components/MemoReactionListView.tsx @@ -1,4 +1,5 @@ import { uniq } from "lodash-es"; +import { observer } from "mobx-react-lite"; import { memo, useEffect, useState } from "react"; import useCurrentUser from "@/hooks/useCurrentUser"; import { userStore } from "@/store/v2"; @@ -14,7 +15,7 @@ interface Props { reactions: Reaction[]; } -const MemoReactionListView = (props: Props) => { +const MemoReactionListView = observer((props: Props) => { const { memo, reactions } = props; const currentUser = useCurrentUser(); const [reactionGroup, setReactionGroup] = useState>(new Map()); @@ -43,6 +44,6 @@ const MemoReactionListView = (props: Props) => { ) ); -}; +}); export default memo(MemoReactionListView); diff --git a/web/src/components/MemoView.tsx b/web/src/components/MemoView.tsx index 5b406c4e..81e49f74 100644 --- a/web/src/components/MemoView.tsx +++ b/web/src/components/MemoView.tsx @@ -6,8 +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 { memoStore } from "@/store/v2"; -import { userStore, workspaceStore } from "@/store/v2"; +import { memoStore, userStore, workspaceStore } from "@/store/v2"; import { State } from "@/types/proto/api/v1/common"; import { Memo, MemoRelation_Type, Visibility } from "@/types/proto/api/v1/memo_service"; import { cn } from "@/utils"; diff --git a/web/src/components/ReactionSelector.tsx b/web/src/components/ReactionSelector.tsx index 5a07fb29..97e5f7e7 100644 --- a/web/src/components/ReactionSelector.tsx +++ b/web/src/components/ReactionSelector.tsx @@ -5,8 +5,7 @@ import { useRef, useState } from "react"; import useClickAway from "react-use/lib/useClickAway"; import { memoServiceClient } from "@/grpcweb"; import useCurrentUser from "@/hooks/useCurrentUser"; -import { memoStore } from "@/store/v2"; -import { workspaceStore } from "@/store/v2"; +import { memoStore, workspaceStore } from "@/store/v2"; import { Memo } from "@/types/proto/api/v1/memo_service"; import { cn } from "@/utils"; diff --git a/web/src/components/SearchBar.tsx b/web/src/components/SearchBar.tsx index d0fbed80..4e541ebf 100644 --- a/web/src/components/SearchBar.tsx +++ b/web/src/components/SearchBar.tsx @@ -1,10 +1,11 @@ import { SearchIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { useState } from "react"; -import memoFilterStore from "@/store/v2/memoFilter"; +import { memoFilterStore } from "@/store/v2"; import { useTranslate } from "@/utils/i18n"; import MemoDisplaySettingMenu from "./MemoDisplaySettingMenu"; -const SearchBar = () => { +const SearchBar = observer(() => { const t = useTranslate(); const [queryText, setQueryText] = useState(""); @@ -41,6 +42,6 @@ const SearchBar = () => { ); -}; +}); export default SearchBar; diff --git a/web/src/components/Settings/MemberSection.tsx b/web/src/components/Settings/MemberSection.tsx index 556775e4..041ff342 100644 --- a/web/src/components/Settings/MemberSection.tsx +++ b/web/src/components/Settings/MemberSection.tsx @@ -2,6 +2,7 @@ import { Dropdown, Menu, MenuButton, MenuItem, Radio, RadioGroup } from "@mui/jo import { Button, Input } from "@usememos/mui"; import { sortBy } from "lodash-es"; import { MoreVerticalIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import React, { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { userServiceClient } from "@/grpcweb"; @@ -16,7 +17,7 @@ interface LocalState { creatingUser: User; } -const MemberSection = () => { +const MemberSection = observer(() => { const t = useTranslate(); const currentUser = useCurrentUser(); const [state, setState] = useState({ @@ -239,6 +240,6 @@ const MemberSection = () => { ); -}; +}); export default MemberSection; diff --git a/web/src/components/Settings/MemoRelatedSettings.tsx b/web/src/components/Settings/MemoRelatedSettings.tsx index cb569db0..38665695 100644 --- a/web/src/components/Settings/MemoRelatedSettings.tsx +++ b/web/src/components/Settings/MemoRelatedSettings.tsx @@ -2,6 +2,7 @@ import { Switch, Chip, ChipDelete } from "@mui/joy"; import { Button, Input } from "@usememos/mui"; import { isEqual, uniq } from "lodash-es"; import { CheckIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { useState } from "react"; import { toast } from "react-hot-toast"; import { workspaceSettingNamePrefix } from "@/store/common"; @@ -10,7 +11,7 @@ import { WorkspaceSettingKey } from "@/store/v2/workspace"; import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_setting_service"; import { useTranslate } from "@/utils/i18n"; -const MemoRelatedSettings = () => { +const MemoRelatedSettings = observer(() => { const t = useTranslate(); const [originalSetting, setOriginalSetting] = useState(workspaceStore.state.memoRelatedSetting); const [memoRelatedSetting, setMemoRelatedSetting] = useState(originalSetting); @@ -197,6 +198,6 @@ const MemoRelatedSettings = () => { ); -}; +}); export default MemoRelatedSettings; diff --git a/web/src/components/Settings/StorageSection.tsx b/web/src/components/Settings/StorageSection.tsx index 4c2081d5..0c6c38d8 100644 --- a/web/src/components/Settings/StorageSection.tsx +++ b/web/src/components/Settings/StorageSection.tsx @@ -2,6 +2,7 @@ import { Divider, List, ListItem, Radio, RadioGroup, Tooltip, Switch } from "@mu import { Button, Input } from "@usememos/mui"; import { isEqual } from "lodash-es"; import { HelpCircleIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import React, { useEffect, useMemo, useState } from "react"; import { toast } from "react-hot-toast"; import { Link } from "react-router-dom"; @@ -15,7 +16,7 @@ import { } from "@/types/proto/api/v1/workspace_setting_service"; import { useTranslate } from "@/utils/i18n"; -const StorageSection = () => { +const StorageSection = observer(() => { const t = useTranslate(); const [workspaceStorageSetting, setWorkspaceStorageSetting] = useState( WorkspaceStorageSetting.fromPartial(workspaceStore.getWorkspaceSettingByKey(WorkspaceSettingKey.STORAGE)?.storageSetting || {}), @@ -223,6 +224,6 @@ const StorageSection = () => { ); -}; +}); export default StorageSection; diff --git a/web/src/components/TagTree.tsx b/web/src/components/TagTree.tsx index c0a37723..f980db33 100644 --- a/web/src/components/TagTree.tsx +++ b/web/src/components/TagTree.tsx @@ -1,4 +1,5 @@ import { ChevronRightIcon, HashIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { useEffect, useState } from "react"; import useToggle from "react-use/lib/useToggle"; import memoFilterStore, { MemoFilter } from "@/store/v2/memoFilter"; @@ -83,7 +84,7 @@ interface TagItemContainerProps { tag: Tag; } -const TagItemContainer: React.FC = (props: TagItemContainerProps) => { +const TagItemContainer = observer((props: TagItemContainerProps) => { const { tag } = props; const tagFilters = memoFilterStore.getFiltersByFactor("tagSearch"); const isActive = tagFilters.some((f: MemoFilter) => f.value === tag.text); @@ -145,6 +146,6 @@ const TagItemContainer: React.FC = (props: TagItemContain ) : null} ); -}; +}); export default TagTree; diff --git a/web/src/pages/AdminSignIn.tsx b/web/src/pages/AdminSignIn.tsx index 25e64643..4dd001e7 100644 --- a/web/src/pages/AdminSignIn.tsx +++ b/web/src/pages/AdminSignIn.tsx @@ -1,8 +1,9 @@ +import { observer } from "mobx-react-lite"; import AuthFooter from "@/components/AuthFooter"; import PasswordSignInForm from "@/components/PasswordSignInForm"; import { workspaceStore } from "@/store/v2"; -const AdminSignIn = () => { +const AdminSignIn = observer(() => { const workspaceGeneralSetting = workspaceStore.state.generalSetting; return ( @@ -20,6 +21,6 @@ const AdminSignIn = () => { ); -}; +}); export default AdminSignIn; diff --git a/web/src/pages/Archived.tsx b/web/src/pages/Archived.tsx index c778dfa2..dd7a64dc 100644 --- a/web/src/pages/Archived.tsx +++ b/web/src/pages/Archived.tsx @@ -1,4 +1,5 @@ import dayjs from "dayjs"; +import { observer } from "mobx-react-lite"; import { useMemo } from "react"; import MemoView from "@/components/MemoView"; import PagedMemoList from "@/components/PagedMemoList"; @@ -8,7 +9,7 @@ import memoFilterStore from "@/store/v2/memoFilter"; import { Direction, State } from "@/types/proto/api/v1/common"; import { Memo } from "@/types/proto/api/v1/memo_service"; -const Archived = () => { +const Archived = observer(() => { const user = useCurrentUser(); const memoListFilter = useMemo(() => { @@ -49,6 +50,6 @@ const Archived = () => { oldFilter={memoListFilter} /> ); -}; +}); export default Archived; diff --git a/web/src/pages/AuthCallback.tsx b/web/src/pages/AuthCallback.tsx index 61ade807..98c348f1 100644 --- a/web/src/pages/AuthCallback.tsx +++ b/web/src/pages/AuthCallback.tsx @@ -1,5 +1,6 @@ import { last } from "lodash-es"; import { LoaderIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { ClientError } from "nice-grpc-web"; import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; @@ -13,7 +14,7 @@ interface State { errorMessage: string; } -const AuthCallback = () => { +const AuthCallback = observer(() => { const navigateTo = useNavigateTo(); const [searchParams] = useSearchParams(); const [state, setState] = useState({ @@ -77,6 +78,6 @@ const AuthCallback = () => { )} ); -}; +}); export default AuthCallback; diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index c85179bc..916cd8a1 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -1,4 +1,5 @@ import dayjs from "dayjs"; +import { observer } from "mobx-react-lite"; import { useMemo } from "react"; import MemoView from "@/components/MemoView"; import PagedMemoList from "@/components/PagedMemoList"; @@ -8,7 +9,7 @@ import memoFilterStore from "@/store/v2/memoFilter"; import { Direction, State } from "@/types/proto/api/v1/common"; import { Memo } from "@/types/proto/api/v1/memo_service"; -const Explore = () => { +const Explore = observer(() => { const user = useCurrentUser(); const memoListFilter = useMemo(() => { @@ -59,6 +60,6 @@ const Explore = () => { oldFilter={memoListFilter} /> ); -}; +}); export default Explore; diff --git a/web/src/pages/SignIn.tsx b/web/src/pages/SignIn.tsx index 6f9002f0..100332f4 100644 --- a/web/src/pages/SignIn.tsx +++ b/web/src/pages/SignIn.tsx @@ -1,5 +1,6 @@ import { Divider } from "@mui/joy"; import { Button } from "@usememos/mui"; +import { observer } from "mobx-react-lite"; import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { Link } from "react-router-dom"; @@ -14,7 +15,7 @@ import { workspaceStore } from "@/store/v2"; import { IdentityProvider, IdentityProvider_Type } from "@/types/proto/api/v1/idp_service"; import { useTranslate } from "@/utils/i18n"; -const SignIn = () => { +const SignIn = observer(() => { const t = useTranslate(); const currentUser = useCurrentUser(); const [identityProviderList, setIdentityProviderList] = useState([]); @@ -98,6 +99,6 @@ const SignIn = () => { ); -}; +}); export default SignIn; diff --git a/web/src/pages/SignUp.tsx b/web/src/pages/SignUp.tsx index 6f697af0..8f5edfd6 100644 --- a/web/src/pages/SignUp.tsx +++ b/web/src/pages/SignUp.tsx @@ -1,5 +1,6 @@ import { Button, Input } from "@usememos/mui"; import { LoaderIcon } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { ClientError } from "nice-grpc-web"; import { useState } from "react"; import { toast } from "react-hot-toast"; @@ -12,7 +13,7 @@ import { workspaceStore } from "@/store/v2"; import { initialUserStore } from "@/store/v2/user"; import { useTranslate } from "@/utils/i18n"; -const SignUp = () => { +const SignUp = observer(() => { const t = useTranslate(); const navigateTo = useNavigateTo(); const actionBtnLoadingState = useLoading(false); @@ -135,6 +136,6 @@ const SignUp = () => { ); -}; +}); export default SignUp;