diff --git a/api/v1/memo.go b/api/v1/memo.go index a363333f..393fe609 100644 --- a/api/v1/memo.go +++ b/api/v1/memo.go @@ -53,9 +53,10 @@ type Memo struct { Pinned bool `json:"pinned"` // Related fields - CreatorName string `json:"creatorName"` - ResourceList []*Resource `json:"resourceList"` - RelationList []*MemoRelation `json:"relationList"` + CreatorName string `json:"creatorName"` + CreatorUsername string `json:"creatorUsername"` + ResourceList []*Resource `json:"resourceList"` + RelationList []*MemoRelation `json:"relationList"` } type CreateMemoRequest struct { @@ -354,11 +355,18 @@ func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { findMemoMessage.CreatorID = &userID } + if username := c.QueryParam("creatorUsername"); username != "" { + user, _ := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) + if user != nil { + findMemoMessage.CreatorID = &user.ID + } + } + currentUserID, ok := c.Get(getUserIDContextKey()).(int) if !ok { // Anonymous use should only fetch PUBLIC memos with specified user if findMemoMessage.CreatorID == nil { - return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo") + return echo.NewHTTPError(http.StatusBadRequest, "Missing user to find memo") } findMemoMessage.VisibilityList = []store.Visibility{store.Public} } else { @@ -467,6 +475,14 @@ func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { if creatorID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { findMemoMessage.CreatorID = &creatorID } + + if username := c.QueryParam("creatorUsername"); username != "" { + user, _ := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) + if user != nil { + findMemoMessage.CreatorID = &user.ID + } + } + if findMemoMessage.CreatorID == nil { return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo") } @@ -526,6 +542,13 @@ func (s *APIV1Service) registerMemoRoutes(g *echo.Group) { findMemoMessage.Pinned = &pinned } + if username := c.QueryParam("creatorUsername"); username != "" { + user, _ := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) + if user != nil { + findMemoMessage.CreatorID = &user.ID + } + } + contentSearch := []string{} tag := c.QueryParam("tag") if tag != "" { @@ -650,6 +673,8 @@ func (s *APIV1Service) convertMemoFromStore(ctx context.Context, memo *store.Mem memoResponse.CreatorName = user.Username } + memoResponse.CreatorUsername = user.Username + // Compose display ts. memoResponse.DisplayTs = memoResponse.CreatedTs // Find memo display with updated ts setting. diff --git a/api/v1/system.go b/api/v1/system.go index 38309cb7..def0253f 100644 --- a/api/v1/system.go +++ b/api/v1/system.go @@ -46,6 +46,7 @@ func (s *APIV1Service) registerSystemRoutes(g *echo.Group) { g.GET("/status", func(c echo.Context) error { ctx := c.Request().Context() + systemStatus := SystemStatus{ Profile: *s.Profile, DBSize: 0, @@ -76,7 +77,7 @@ func (s *APIV1Service) registerSystemRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err) } if hostUser != nil { - systemStatus.Host = convertUserFromStore(hostUser) + systemStatus.Host = &User{ID: hostUser.ID} // data desensitize systemStatus.Host.OpenID = "" systemStatus.Host.Email = "" diff --git a/api/v1/user.go b/api/v1/user.go index 50ddc4b0..d6def764 100644 --- a/api/v1/user.go +++ b/api/v1/user.go @@ -258,6 +258,26 @@ func (s *APIV1Service) registerUserRoutes(g *echo.Group) { return c.JSON(http.StatusOK, userMessage) }) + // GET /user/:username - Get user by username. + g.GET("/user/:username", func(c echo.Context) error { + ctx := c.Request().Context() + username := c.Param("username") + + user, err := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err) + } + if user == nil { + return echo.NewHTTPError(http.StatusNotFound, "User not found") + } + + userMessage := convertUserFromStore(user) + // data desensitize + userMessage.OpenID = "" + userMessage.Email = "" + return c.JSON(http.StatusOK, userMessage) + }) + // PUT /user/:id - Update user by id. g.PATCH("/user/:id", func(c echo.Context) error { ctx := c.Request().Context() diff --git a/test/server/system_test.go b/test/server/system_test.go index 649e028e..d743590a 100644 --- a/test/server/system_test.go +++ b/test/server/system_test.go @@ -32,7 +32,6 @@ func TestSystemServer(t *testing.T) { status, err = s.getSystemStatus() require.NoError(t, err) require.Equal(t, user.ID, status.Host.ID) - require.Equal(t, user.Username, status.Host.Username) } func (s *TestingServer) getSystemStatus() (*apiv1.SystemStatus, error) { diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index b8da9338..3584568f 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -6,7 +6,6 @@ import { useTranslate } from "@/utils/i18n"; import { resolution } from "@/utils/layout"; import Icon from "./Icon"; import UserBanner from "./UserBanner"; -import showAboutSiteDialog from "./AboutSiteDialog"; import UpgradeVersionView from "./UpgradeVersionBanner"; const Header = () => { @@ -95,7 +94,7 @@ const Header = () => { )} - {!isVisitorMode && ( + {!globalStore.getDisablePublicMemos() && ( <> { {t("common.sign-in")} - )} diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index c6d0d97b..05f6683a 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -38,7 +38,7 @@ const Memo: React.FC = (props: Props) => { const [createdTimeStr, setCreatedTimeStr] = useState(getRelativeTimeString(memo.displayTs)); const [relatedMemoList, setRelatedMemoList] = useState([]); const memoContainerRef = useRef(null); - const readonly = userStore.isVisitorMode() || userStore.getCurrentUserId() !== memo.creatorId; + const readonly = userStore.isVisitorMode() || userStore.getCurrentUsername() !== memo.creatorUsername; useEffect(() => { Promise.allSettled(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then( @@ -220,7 +220,7 @@ const Memo: React.FC = (props: Props) => { {createdTimeStr} {showCreator && ( - + @{memo.creatorName} )} diff --git a/web/src/components/MemoList.tsx b/web/src/components/MemoList.tsx index 97f5d01e..c8ea8602 100644 --- a/web/src/components/MemoList.tsx +++ b/web/src/components/MemoList.tsx @@ -10,7 +10,12 @@ import Empty from "./Empty"; import Memo from "./Memo"; import "@/less/memo-list.less"; -const MemoList = () => { +interface Props { + showCreator?: boolean; +} + +const MemoList: React.FC = (props: Props) => { + const { showCreator } = props; const t = useTranslate(); const memoStore = useMemoStore(); const userStore = useUserStore(); @@ -20,7 +25,7 @@ const MemoList = () => { const { memos, isFetching } = memoStore.state; const [isComplete, setIsComplete] = useState(false); - const currentUserId = userStore.getCurrentUserId(); + const currentUsername = userStore.getCurrentUsername(); const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = filter; const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null; const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility); @@ -76,7 +81,7 @@ const MemoList = () => { return shouldShow; }) : memos - ).filter((memo) => memo.creatorId === currentUserId && memo.rowStatus === "NORMAL"); + ).filter((memo) => memo.creatorUsername === currentUsername && memo.rowStatus === "NORMAL"); const pinnedMemos = shownMemos.filter((m) => m.pinned); const unpinnedMemos = shownMemos.filter((m) => !m.pinned); @@ -103,7 +108,7 @@ const MemoList = () => { console.error(error); toast.error(error.response.data.message); }); - }, [currentUserId]); + }, [currentUsername]); useEffect(() => { const pageWrapper = document.body.querySelector(".page-wrapper"); @@ -153,7 +158,7 @@ const MemoList = () => { return (
{sortedMemos.map((memo) => ( - + ))} {isFetching ? (
diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index f6c51375..19ad854d 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -51,7 +51,7 @@ const ShareMemoDialog: React.FC = (props: Props) => { const createdDays = Math.ceil((Date.now() - getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24); useEffect(() => { - getMemoStats(user.id) + getMemoStats(user.username) .then(({ data }) => { setPartialState({ memoAmount: data.length, diff --git a/web/src/components/UsageHeatMap.tsx b/web/src/components/UsageHeatMap.tsx index 78436137..19290deb 100644 --- a/web/src/components/UsageHeatMap.tsx +++ b/web/src/components/UsageHeatMap.tsx @@ -44,19 +44,19 @@ const UsageHeatMap = () => { const [allStat, setAllStat] = useState(getInitialUsageStat(usedDaysAmount, beginDayTimestamp)); const [currentStat, setCurrentStat] = useState(null); const containerElRef = useRef(null); - const currentUserId = userStore.getCurrentUserId(); + const currentUsername = userStore.getCurrentUsername(); useEffect(() => { - userStore.getUserById(currentUserId).then((user) => { + userStore.getUserByUsername(currentUsername).then((user) => { if (!user) { return; } setCreatedDays(Math.ceil((Date.now() - getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24)); }); - }, [currentUserId]); + }, [currentUsername]); useEffect(() => { - getMemoStats(currentUserId) + getMemoStats(currentUsername) .then(({ data }) => { setMemoAmount(data.length); const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp); @@ -75,7 +75,7 @@ const UsageHeatMap = () => { .catch((error) => { console.error(error); }); - }, [memos.length, currentUserId]); + }, [memos.length, currentUsername]); const handleUsageStatItemMouseEnter = useCallback((event: React.MouseEvent, item: DailyUsageStat) => { const tempDiv = document.createElement("div"); diff --git a/web/src/components/UserBanner.tsx b/web/src/components/UserBanner.tsx index 720d7e16..909f03e7 100644 --- a/web/src/components/UserBanner.tsx +++ b/web/src/components/UserBanner.tsx @@ -43,7 +43,7 @@ const UserBanner = () => {
- {userStore.isVisitorMode() ? systemStatus.customizedProfile.name : username} + {user != undefined ? username : systemStatus.customizedProfile.name} {user?.role === "HOST" ? ( MOD @@ -54,7 +54,7 @@ const UserBanner = () => { positionClassName="top-full mt-2" actions={ <> - {!userStore.isVisitorMode() && ( + {user != undefined && ( <> RSS @@ -77,7 +77,7 @@ const UserBanner = () => { > {t("common.about")} - {!userStore.isVisitorMode() && ( + {user != undefined && (