mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: use username instead of uid (#1977)
* #1916 replace userId to username * resolve --------- Co-authored-by: Александр Тумайкин <AATumaykin@tsum.ru>
This commit is contained in:
committed by
GitHub
parent
336b32004d
commit
f91f09adea
@ -54,6 +54,7 @@ type Memo struct {
|
||||
|
||||
// Related fields
|
||||
CreatorName string `json:"creatorName"`
|
||||
CreatorUsername string `json:"creatorUsername"`
|
||||
ResourceList []*Resource `json:"resourceList"`
|
||||
RelationList []*MemoRelation `json:"relationList"`
|
||||
}
|
||||
@ -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.
|
||||
|
@ -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 = ""
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
@ -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 = () => {
|
||||
</NavLink>
|
||||
</>
|
||||
)}
|
||||
{!isVisitorMode && (
|
||||
{!globalStore.getDisablePublicMemos() && (
|
||||
<>
|
||||
<NavLink
|
||||
to="/explore"
|
||||
@ -179,13 +178,6 @@ const Header = () => {
|
||||
<Icon.LogIn className="mr-3 w-6 h-auto opacity-70" /> {t("common.sign-in")}
|
||||
</>
|
||||
</NavLink>
|
||||
<button
|
||||
id="header-about"
|
||||
className="px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700"
|
||||
onClick={() => showAboutSiteDialog()}
|
||||
>
|
||||
<Icon.CupSoda className="mr-3 w-6 h-auto opacity-70" /> {t("common.about")}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.displayTs));
|
||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||
const memoContainerRef = useRef<HTMLDivElement>(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: Props) => {
|
||||
{createdTimeStr}
|
||||
</Link>
|
||||
{showCreator && (
|
||||
<Link className="name-text" to={`/u/${memo.creatorId}`}>
|
||||
<Link className="name-text" to={`/u/${memo.creatorUsername}`}>
|
||||
@{memo.creatorName}
|
||||
</Link>
|
||||
)}
|
||||
|
@ -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: 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<boolean>(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 (
|
||||
<div className="memo-list-container">
|
||||
{sortedMemos.map((memo) => (
|
||||
<Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} showVisibility />
|
||||
<Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} showVisibility showCreator={showCreator} />
|
||||
))}
|
||||
{isFetching ? (
|
||||
<div className="status-text-container fetching-tip">
|
||||
|
@ -51,7 +51,7 @@ const ShareMemoDialog: React.FC<Props> = (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,
|
||||
|
@ -44,19 +44,19 @@ const UsageHeatMap = () => {
|
||||
const [allStat, setAllStat] = useState<DailyUsageStat[]>(getInitialUsageStat(usedDaysAmount, beginDayTimestamp));
|
||||
const [currentStat, setCurrentStat] = useState<DailyUsageStat | null>(null);
|
||||
const containerElRef = useRef<HTMLDivElement>(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");
|
||||
|
@ -43,7 +43,7 @@ const UserBanner = () => {
|
||||
<div className="px-4 py-2 max-w-full flex flex-row justify-start items-center cursor-pointer rounded-lg hover:shadow hover:bg-white dark:hover:bg-zinc-700">
|
||||
<UserAvatar avatarUrl={user?.avatarUrl} />
|
||||
<span className="px-1 text-lg font-medium text-slate-800 dark:text-gray-200 shrink truncate">
|
||||
{userStore.isVisitorMode() ? systemStatus.customizedProfile.name : username}
|
||||
{user != undefined ? username : systemStatus.customizedProfile.name}
|
||||
</span>
|
||||
{user?.role === "HOST" ? (
|
||||
<span className="text-xs px-1 bg-blue-600 dark:bg-blue-800 rounded text-white dark:text-gray-200 shadow">MOD</span>
|
||||
@ -54,7 +54,7 @@ const UserBanner = () => {
|
||||
positionClassName="top-full mt-2"
|
||||
actions={
|
||||
<>
|
||||
{!userStore.isVisitorMode() && (
|
||||
{user != undefined && (
|
||||
<>
|
||||
<button
|
||||
className="w-full px-3 truncate text-left leading-10 cursor-pointer rounded flex flex-row justify-start items-center dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
|
||||
@ -64,7 +64,7 @@ const UserBanner = () => {
|
||||
</button>
|
||||
<a
|
||||
className="w-full px-3 truncate text-left leading-10 cursor-pointer rounded flex flex-row justify-start items-center dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
|
||||
href={`/u/${user?.id}/rss.xml`}
|
||||
href={`/u/${user?.username}/rss.xml`}
|
||||
target="_blank"
|
||||
>
|
||||
<Icon.Rss className="w-5 h-auto mr-2 opacity-80" /> RSS
|
||||
@ -77,7 +77,7 @@ const UserBanner = () => {
|
||||
>
|
||||
<Icon.Info className="w-5 h-auto mr-2 opacity-80" /> {t("common.about")}
|
||||
</button>
|
||||
{!userStore.isVisitorMode() && (
|
||||
{user != undefined && (
|
||||
<button
|
||||
className="w-full px-3 truncate text-left leading-10 cursor-pointer rounded flex flex-row justify-start items-center dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
|
||||
onClick={handleSignOutBtnClick}
|
||||
|
@ -21,14 +21,14 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
||||
const { className, isFutureDateDisabled, datestamp, handleDateStampChange } = props;
|
||||
const [currentDateStamp, setCurrentDateStamp] = useState<DateStamp>(getMonthFirstDayDateStamp(datestamp));
|
||||
const [countByDate, setCountByDate] = useState(new Map());
|
||||
const currentUserId = useUserStore().getCurrentUserId();
|
||||
const currentUsername = useUserStore().getCurrentUsername();
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentDateStamp(getMonthFirstDayDateStamp(datestamp));
|
||||
}, [datestamp]);
|
||||
|
||||
useEffect(() => {
|
||||
getMemoStats(currentUserId).then(({ data }) => {
|
||||
getMemoStats(currentUsername).then(({ data }) => {
|
||||
const m = new Map();
|
||||
for (const record of data) {
|
||||
const date = getDateStampByDate(record * 1000);
|
||||
@ -36,7 +36,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
||||
}
|
||||
setCountByDate(m);
|
||||
});
|
||||
}, [currentUserId]);
|
||||
}, [currentUsername]);
|
||||
|
||||
const firstDate = new Date(currentDateStamp);
|
||||
const firstDateDay = firstDate.getDay() === 0 ? 7 : firstDate.getDay();
|
||||
|
@ -56,8 +56,8 @@ export function getUserList() {
|
||||
return axios.get<User[]>("/api/v1/user");
|
||||
}
|
||||
|
||||
export function getUserById(id: number) {
|
||||
return axios.get<User>(`/api/v1/user/${id}`);
|
||||
export function getUserByUsername(username: string) {
|
||||
return axios.get<User>(`/api/v1/user/${username}`);
|
||||
}
|
||||
|
||||
export function upsertUserSetting(upsert: UserSettingUpsert) {
|
||||
@ -81,13 +81,17 @@ export function getAllMemos(memoFind?: MemoFind) {
|
||||
queryList.push(`limit=${memoFind.limit}`);
|
||||
}
|
||||
|
||||
if (memoFind?.creatorUsername) {
|
||||
queryList.push(`creatorUsername=${memoFind.creatorUsername}`);
|
||||
}
|
||||
|
||||
return axios.get<Memo[]>(`/api/v1/memo/all?${queryList.join("&")}`);
|
||||
}
|
||||
|
||||
export function getMemoList(memoFind?: MemoFind) {
|
||||
const queryList = [];
|
||||
if (memoFind?.creatorId) {
|
||||
queryList.push(`creatorId=${memoFind.creatorId}`);
|
||||
if (memoFind?.creatorUsername) {
|
||||
queryList.push(`creatorUsername=${memoFind.creatorUsername}`);
|
||||
}
|
||||
if (memoFind?.rowStatus) {
|
||||
queryList.push(`rowStatus=${memoFind.rowStatus}`);
|
||||
@ -104,8 +108,8 @@ export function getMemoList(memoFind?: MemoFind) {
|
||||
return axios.get<Memo[]>(`/api/v1/memo?${queryList.join("&")}`);
|
||||
}
|
||||
|
||||
export function getMemoStats(userId: UserId) {
|
||||
return axios.get<number[]>(`/api/v1/memo/stats?creatorId=${userId}`);
|
||||
export function getMemoStats(username: string) {
|
||||
return axios.get<number[]>(`/api/v1/memo/stats?creatorUsername=${username}`);
|
||||
}
|
||||
|
||||
export function getMemoById(id: MemoId) {
|
||||
@ -163,8 +167,8 @@ export async function chatStreaming(messageList: Array<Message>, onmessage: any,
|
||||
|
||||
export function getShortcutList(shortcutFind?: ShortcutFind) {
|
||||
const queryList = [];
|
||||
if (shortcutFind?.creatorId) {
|
||||
queryList.push(`creatorId=${shortcutFind.creatorId}`);
|
||||
if (shortcutFind?.creatorUsername) {
|
||||
queryList.push(`creatorUsername=${shortcutFind.creatorUsername}`);
|
||||
}
|
||||
return axios.get<Shortcut[]>(`/api/v1/shortcut?${queryList.join("&")}`);
|
||||
}
|
||||
@ -228,8 +232,8 @@ export function deleteMemoResource(memoId: MemoId, resourceId: ResourceId) {
|
||||
|
||||
export function getTagList(tagFind?: TagFind) {
|
||||
const queryList = [];
|
||||
if (tagFind?.creatorId) {
|
||||
queryList.push(`creatorId=${tagFind.creatorId}`);
|
||||
if (tagFind?.creatorUsername) {
|
||||
queryList.push(`creatorUsername=${tagFind.creatorUsername}`);
|
||||
}
|
||||
return axios.get<string[]>(`/api/v1/tag?${queryList.join("&")}`);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// UNKNOWN_ID is the symbol for unknown id
|
||||
export const UNKNOWN_ID = -1;
|
||||
export const UNKNOWN_USERNAME = "";
|
||||
|
||||
// default animation duration
|
||||
export const ANIMATION_DURATION = 200;
|
||||
|
@ -46,7 +46,7 @@ const EmbedMemo = () => {
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
<div className="w-full mb-2 flex flex-row justify-start items-center text-sm text-gray-400 dark:text-gray-300">
|
||||
<span>{getDateTimeString(state.memo.displayTs)}</span>
|
||||
<a className="ml-2 hover:underline hover:text-green-600" href={`/u/${state.memo.creatorId}`}>
|
||||
<a className="ml-2 hover:underline hover:text-green-600" href={`/u/${state.memo.creatorUsername}`}>
|
||||
@{state.memo.creatorName}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useFilterStore, useMemoStore } from "@/store/module";
|
||||
import { useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
||||
import { TAG_REG } from "@/labs/marked/parser";
|
||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
@ -16,24 +16,16 @@ const Explore = () => {
|
||||
const location = useLocation();
|
||||
const filterStore = useFilterStore();
|
||||
const memoStore = useMemoStore();
|
||||
const userStore = useUserStore();
|
||||
const filter = filterStore.state;
|
||||
const memos = memoStore.state.memos;
|
||||
const { memos } = memoStore.state;
|
||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
||||
const loadingState = useLoading();
|
||||
|
||||
useEffect(() => {
|
||||
memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, 0).then((memos) => {
|
||||
if (memos.length < DEFAULT_MEMO_LIMIT) {
|
||||
setIsComplete(true);
|
||||
}
|
||||
loadingState.setFinish();
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
const { tag: tagQuery, text: textQuery } = filter;
|
||||
const showMemoFilter = Boolean(tagQuery || textQuery);
|
||||
|
||||
const shownMemos = showMemoFilter
|
||||
const fetchedMemos = showMemoFilter
|
||||
? memos.filter((memo) => {
|
||||
let shouldShow = true;
|
||||
|
||||
@ -57,13 +49,35 @@ const Explore = () => {
|
||||
})
|
||||
: memos;
|
||||
|
||||
const sortedMemos = shownMemos
|
||||
const username = userStore.getUsernameFromPath();
|
||||
let sortedMemos = fetchedMemos
|
||||
.filter((m) => m.rowStatus === "NORMAL" && m.visibility !== "PRIVATE")
|
||||
.sort((mi, mj) => mj.displayTs - mi.displayTs);
|
||||
|
||||
if (username != undefined) {
|
||||
sortedMemos = sortedMemos.filter((m) => m.creatorUsername === username);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const username = userStore.getUsernameFromPath();
|
||||
memoStore
|
||||
.fetchAllMemos(DEFAULT_MEMO_LIMIT, 0, username)
|
||||
.then((fetchedMemos) => {
|
||||
if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) {
|
||||
setIsComplete(true);
|
||||
}
|
||||
loadingState.setFinish();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
toast.error(error.response.data.message);
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
const handleFetchMoreClick = async () => {
|
||||
try {
|
||||
const fetchedMemos = await memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, memos.length);
|
||||
const username = userStore.getUsernameFromPath();
|
||||
const fetchedMemos = await memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, memos.length, username);
|
||||
if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) {
|
||||
setIsComplete(true);
|
||||
} else {
|
||||
|
@ -15,14 +15,14 @@ function Home() {
|
||||
const user = userStore.state.user;
|
||||
|
||||
useEffect(() => {
|
||||
const currentUserId = userStore.getCurrentUserId();
|
||||
userStore.getUserById(currentUserId).then((user) => {
|
||||
const currentUsername = userStore.getCurrentUsername();
|
||||
userStore.getUserByUsername(currentUsername).then((user) => {
|
||||
if (!user) {
|
||||
toast.error(t("message.user-not-found"));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}, [userStore.getCurrentUserId()]);
|
||||
}, [userStore.getCurrentUsername()]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.setting.locale) {
|
||||
|
@ -63,22 +63,25 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
|
||||
if (isNullorUndefined(host)) {
|
||||
return redirect("/auth");
|
||||
} else if (isNullorUndefined(user) && !systemStatus.disablePublicMemos) {
|
||||
return redirect("/explore");
|
||||
} else if (isNullorUndefined(user) && systemStatus.disablePublicMemos) {
|
||||
// if user is authenticated, then show home
|
||||
if (!isNullorUndefined(user)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if user is anonymous, then redirect to auth if disabled public memos, else redirect to explore
|
||||
if (systemStatus.disablePublicMemos) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
|
||||
return redirect("/explore");
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/u/:userId",
|
||||
element: <Home />,
|
||||
path: "/u/:username",
|
||||
element: <Explore />,
|
||||
loader: async () => {
|
||||
await initialGlobalStateLoader();
|
||||
|
||||
@ -88,12 +91,13 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user) && systemStatus.disablePublicMemos) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
@ -109,10 +113,10 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user) && systemStatus.disablePublicMemos) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -130,10 +134,9 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
const { user } = store.getState().user;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user)) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -151,10 +154,9 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
const { user } = store.getState().user;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user)) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -172,10 +174,9 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
const { user } = store.getState().user;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user)) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -194,10 +195,9 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
const { user } = store.getState().user;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user)) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -215,10 +215,9 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
const { user } = store.getState().user;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user)) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
@ -238,10 +237,10 @@ const router = createBrowserRouter([
|
||||
// do nth
|
||||
}
|
||||
|
||||
const { host, user } = store.getState().user;
|
||||
const { user } = store.getState().user;
|
||||
const { systemStatus } = store.getState().global;
|
||||
|
||||
if (isNullorUndefined(host) || (isNullorUndefined(user) && systemStatus.disablePublicMemos)) {
|
||||
if (isNullorUndefined(user) && systemStatus.disablePublicMemos) {
|
||||
return redirect("/auth");
|
||||
}
|
||||
return null;
|
||||
|
@ -65,6 +65,9 @@ export const useGlobalStore = () => {
|
||||
getState: () => {
|
||||
return store.getState().global;
|
||||
},
|
||||
getDisablePublicMemos: () => {
|
||||
return store.getState().global.systemStatus.disablePublicMemos;
|
||||
},
|
||||
isDev: () => {
|
||||
return state.systemStatus.profile.mode !== "prod";
|
||||
},
|
||||
|
@ -41,7 +41,7 @@ export const useMemoStore = () => {
|
||||
offset,
|
||||
};
|
||||
if (userStore.isVisitorMode()) {
|
||||
memoFind.creatorId = userStore.getUserIdFromPath();
|
||||
memoFind.creatorUsername = userStore.getUsernameFromPath();
|
||||
}
|
||||
const { data } = await api.getMemoList(memoFind);
|
||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||
@ -54,16 +54,22 @@ export const useMemoStore = () => {
|
||||
|
||||
return fetchedMemos;
|
||||
},
|
||||
fetchAllMemos: async (limit = DEFAULT_MEMO_LIMIT, offset?: number) => {
|
||||
fetchAllMemos: async (limit = DEFAULT_MEMO_LIMIT, offset?: number, username?: string) => {
|
||||
store.dispatch(setIsFetching(true));
|
||||
const memoFind: MemoFind = {
|
||||
rowStatus: "NORMAL",
|
||||
limit,
|
||||
offset,
|
||||
};
|
||||
|
||||
if (username != undefined) {
|
||||
memoFind.creatorUsername = username;
|
||||
}
|
||||
|
||||
const { data } = await api.getAllMemos(memoFind);
|
||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||
store.dispatch(upsertMemos(fetchedMemos));
|
||||
store.dispatch(setIsFetching(false));
|
||||
|
||||
for (const m of fetchedMemos) {
|
||||
memoCacheStore.setMemoCache(m);
|
||||
@ -76,7 +82,7 @@ export const useMemoStore = () => {
|
||||
rowStatus: "ARCHIVED",
|
||||
};
|
||||
if (userStore.isVisitorMode()) {
|
||||
memoFind.creatorId = userStore.getUserIdFromPath();
|
||||
memoFind.creatorUsername = userStore.getUsernameFromPath();
|
||||
}
|
||||
const { data } = await api.getMemoList(memoFind);
|
||||
const archivedMemos = data.map((m) => {
|
||||
|
@ -14,7 +14,7 @@ export const useTagStore = () => {
|
||||
fetchTags: async () => {
|
||||
const tagFind: TagFind = {};
|
||||
if (userStore.isVisitorMode()) {
|
||||
tagFind.creatorId = userStore.getUserIdFromPath();
|
||||
tagFind.creatorUsername = userStore.getUsernameFromPath();
|
||||
}
|
||||
const { data } = await api.getTagList(tagFind);
|
||||
store.dispatch(setTags(data));
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { camelCase } from "lodash-es";
|
||||
import * as api from "@/helpers/api";
|
||||
import storage from "@/helpers/storage";
|
||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
||||
import { UNKNOWN_USERNAME } from "@/helpers/consts";
|
||||
import { getSystemColorScheme } from "@/helpers/utils";
|
||||
import store, { useAppSelector } from "..";
|
||||
import { setAppearance, setLocale } from "../reducer/global";
|
||||
@ -82,6 +82,16 @@ const getUserIdFromPath = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getUsernameFromPath = () => {
|
||||
const pathname = window.location.pathname;
|
||||
const usernameRegex = /^\/u\/(\w+).*/;
|
||||
const result = pathname.match(usernameRegex);
|
||||
if (result && result.length === 2) {
|
||||
return String(result[1]);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const doSignIn = async () => {
|
||||
const { data: user } = await api.getMyselfUser();
|
||||
if (user) {
|
||||
@ -100,7 +110,7 @@ export const useUserStore = () => {
|
||||
const state = useAppSelector((state) => state.user);
|
||||
|
||||
const isVisitorMode = () => {
|
||||
return state.user === undefined || (getUserIdFromPath() && state.user.id !== getUserIdFromPath());
|
||||
return state.user === undefined || (getUsernameFromPath() && state.user.username !== getUsernameFromPath());
|
||||
};
|
||||
|
||||
return {
|
||||
@ -110,17 +120,18 @@ export const useUserStore = () => {
|
||||
},
|
||||
isVisitorMode,
|
||||
getUserIdFromPath,
|
||||
getUsernameFromPath,
|
||||
doSignIn,
|
||||
doSignOut,
|
||||
getCurrentUserId: () => {
|
||||
getCurrentUsername: () => {
|
||||
if (isVisitorMode()) {
|
||||
return getUserIdFromPath() || UNKNOWN_ID;
|
||||
return getUsernameFromPath() || UNKNOWN_USERNAME;
|
||||
} else {
|
||||
return state.user?.id || UNKNOWN_ID;
|
||||
return state.user?.username || UNKNOWN_USERNAME;
|
||||
}
|
||||
},
|
||||
getUserById: async (userId: UserId) => {
|
||||
const { data } = await api.getUserById(userId);
|
||||
getUserByUsername: async (username: string) => {
|
||||
const { data } = await api.getUserByUsername(username);
|
||||
if (data) {
|
||||
const user = convertResponseModelUser(data);
|
||||
store.dispatch(setUserById(user));
|
||||
|
4
web/src/types/modules/memo.d.ts
vendored
4
web/src/types/modules/memo.d.ts
vendored
@ -5,7 +5,7 @@ type Visibility = "PUBLIC" | "PROTECTED" | "PRIVATE";
|
||||
interface Memo {
|
||||
id: MemoId;
|
||||
|
||||
creatorId: UserId;
|
||||
creatorUsername: string;
|
||||
createdTs: TimeStamp;
|
||||
updatedTs: TimeStamp;
|
||||
rowStatus: RowStatus;
|
||||
@ -38,7 +38,7 @@ interface MemoPatch {
|
||||
}
|
||||
|
||||
interface MemoFind {
|
||||
creatorId?: UserId;
|
||||
creatorUsername?: string;
|
||||
rowStatus?: RowStatus;
|
||||
pinned?: boolean;
|
||||
visibility?: Visibility;
|
||||
|
4
web/src/types/modules/shortcut.d.ts
vendored
4
web/src/types/modules/shortcut.d.ts
vendored
@ -3,7 +3,7 @@ type ShortcutId = number;
|
||||
interface Shortcut {
|
||||
id: ShortcutId;
|
||||
|
||||
creatorId: UserId;
|
||||
creatorUsername: string;
|
||||
rowStatus: RowStatus;
|
||||
createdTs: TimeStamp;
|
||||
updatedTs: TimeStamp;
|
||||
@ -25,5 +25,5 @@ interface ShortcutPatch {
|
||||
}
|
||||
|
||||
interface ShortcutFind {
|
||||
creatorId?: UserId;
|
||||
creatorUsername?: string;
|
||||
}
|
||||
|
2
web/src/types/modules/tag.d.ts
vendored
2
web/src/types/modules/tag.d.ts
vendored
@ -1,3 +1,3 @@
|
||||
interface TagFind {
|
||||
creatorId?: UserId;
|
||||
creatorUsername?: string;
|
||||
}
|
||||
|
Reference in New Issue
Block a user