mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
fix: visitor view in frontend
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { editorStateService, memoService } from "../services";
|
import { editorStateService, memoService, userService } from "../services";
|
||||||
import { parseHtmlToRawText } from "../helpers/marked";
|
import { parseHtmlToRawText } from "../helpers/marked";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
@@ -122,12 +122,16 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="header-container">
|
<div className="header-container">
|
||||||
<p className="time-text">{utils.getDateTimeString(memo.createdTs)}</p>
|
<p className="time-text">{utils.getDateTimeString(memo.createdTs)}</p>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
|
<Only when={!userService.isVisitorMode()}>
|
||||||
|
<>
|
||||||
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
||||||
<img className={`icon-img ${memo.visibility === "PRIVATE" ? "opacity-30" : ""}`} src="/icons/visibility.svg" />
|
<img className={`icon-img ${memo.visibility === "PRIVATE" ? "opacity-30" : ""}`} src="/icons/visibility.svg" />
|
||||||
</button>
|
</button>
|
||||||
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
||||||
<img className="icon-img" src="/icons/edit.svg" />
|
<img className="icon-img" src="/icons/edit.svg" />
|
||||||
</button>
|
</button>
|
||||||
|
</>
|
||||||
|
</Only>
|
||||||
<button className="btn close-btn" onClick={props.destroy}>
|
<button className="btn close-btn" onClick={props.destroy}>
|
||||||
<img className="icon-img" src="/icons/close.svg" />
|
<img className="icon-img" src="/icons/close.svg" />
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { locationService, shortcutService, userService } from "../services";
|
import { locationService, shortcutService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
import useLoading from "../hooks/useLoading";
|
import useLoading from "../hooks/useLoading";
|
||||||
import Only from "./common/OnlyWhen";
|
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
import showCreateShortcutDialog from "./CreateShortcutDialog";
|
import showCreateShortcutDialog from "./CreateShortcutDialog";
|
||||||
import "../less/shortcut-list.less";
|
import "../less/shortcut-list.less";
|
||||||
@@ -39,11 +38,9 @@ const ShortcutList: React.FC<Props> = () => {
|
|||||||
<div className="shortcuts-wrapper">
|
<div className="shortcuts-wrapper">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="normal-text">Shortcuts</span>
|
<span className="normal-text">Shortcuts</span>
|
||||||
<Only when={!userService.isVisitorMode()}>
|
|
||||||
<span className="btn" onClick={() => showCreateShortcutDialog()}>
|
<span className="btn" onClick={() => showCreateShortcutDialog()}>
|
||||||
<img src="/icons/add.svg" alt="add shortcut" />
|
<img src="/icons/add.svg" alt="add shortcut" />
|
||||||
</span>
|
</span>
|
||||||
</Only>
|
|
||||||
</p>
|
</p>
|
||||||
<div className="shortcuts-container">
|
<div className="shortcuts-container">
|
||||||
{sortedShortcuts.map((s) => {
|
{sortedShortcuts.map((s) => {
|
||||||
@@ -114,7 +111,7 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
|||||||
<div className="shortcut-text-container">
|
<div className="shortcut-text-container">
|
||||||
<span className="shortcut-text">{shortcut.title}</span>
|
<span className="shortcut-text">{shortcut.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
|
<div className="btns-container">
|
||||||
<span className="action-btn toggle-btn">
|
<span className="action-btn toggle-btn">
|
||||||
<img className="icon-img" src="/icons/more.svg" />
|
<img className="icon-img" src="/icons/more.svg" />
|
||||||
</span>
|
</span>
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import { useAppSelector } from "../store";
|
|
||||||
import * as utils from "../helpers/utils";
|
|
||||||
import { userService } from "../services";
|
import { userService } from "../services";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import showDailyReviewDialog from "./DailyReviewDialog";
|
import showDailyReviewDialog from "./DailyReviewDialog";
|
||||||
@@ -14,11 +12,6 @@ import "../less/siderbar.less";
|
|||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
const Sidebar: React.FC<Props> = () => {
|
const Sidebar: React.FC<Props> = () => {
|
||||||
const { memos, tags } = useAppSelector((state) => state.memo);
|
|
||||||
const user = useAppSelector((state) => state.user.user);
|
|
||||||
|
|
||||||
const createdDays = user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24) : 0;
|
|
||||||
|
|
||||||
const handleMyAccountBtnClick = () => {
|
const handleMyAccountBtnClick = () => {
|
||||||
showSettingDialog();
|
showSettingDialog();
|
||||||
};
|
};
|
||||||
@@ -35,20 +28,6 @@ const Sidebar: React.FC<Props> = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<UserBanner />
|
<UserBanner />
|
||||||
<div className="status-text-container">
|
|
||||||
<div className="status-text memos-text">
|
|
||||||
<span className="amount-text">{memos.length}</span>
|
|
||||||
<span className="type-text">MEMO</span>
|
|
||||||
</div>
|
|
||||||
<div className="status-text tags-text">
|
|
||||||
<span className="amount-text">{tags.length}</span>
|
|
||||||
<span className="type-text">TAG</span>
|
|
||||||
</div>
|
|
||||||
<div className="status-text duration-text">
|
|
||||||
<span className="amount-text">{createdDays}</span>
|
|
||||||
<span className="type-text">DAY</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<UsageHeatMap />
|
<UsageHeatMap />
|
||||||
<Only when={!userService.isVisitorMode()}>
|
<Only when={!userService.isVisitorMode()}>
|
||||||
<div className="action-btns-container">
|
<div className="action-btns-container">
|
||||||
@@ -62,8 +41,8 @@ const Sidebar: React.FC<Props> = () => {
|
|||||||
<span className="icon">🗂</span> Archived
|
<span className="icon">🗂</span> Archived
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Only>
|
|
||||||
<ShortcutList />
|
<ShortcutList />
|
||||||
|
</Only>
|
||||||
<TagList />
|
<TagList />
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import * as api from "../helpers/api";
|
import * as utils from "../helpers/utils";
|
||||||
import userService from "../services/userService";
|
import userService from "../services/userService";
|
||||||
import { locationService } from "../services";
|
import { locationService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
@@ -11,26 +11,31 @@ interface Props {}
|
|||||||
|
|
||||||
const UserBanner: React.FC<Props> = () => {
|
const UserBanner: React.FC<Props> = () => {
|
||||||
const user = useAppSelector((state) => state.user.user);
|
const user = useAppSelector((state) => state.user.user);
|
||||||
|
const { memos, tags } = useAppSelector((state) => state.memo);
|
||||||
const [shouldShowPopupBtns, setShouldShowPopupBtns] = useState(false);
|
const [shouldShowPopupBtns, setShouldShowPopupBtns] = useState(false);
|
||||||
const [username, setUsername] = useState("Memos");
|
const [username, setUsername] = useState("Memos");
|
||||||
|
const [createdDays, setCreatedDays] = useState(0);
|
||||||
const isVisitorMode = userService.isVisitorMode();
|
const isVisitorMode = userService.isVisitorMode();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentUserId = userService.getUserIdFromPath();
|
const currentUserId = userService.getUserIdFromPath();
|
||||||
if (isVisitorMode && currentUserId) {
|
if (isVisitorMode && currentUserId) {
|
||||||
api
|
userService
|
||||||
.getUserNameById(currentUserId)
|
.getUserById(currentUserId)
|
||||||
.then(({ data }) => {
|
.then((user) => {
|
||||||
const { data: username } = data;
|
if (user) {
|
||||||
if (username) {
|
setUsername(user.name);
|
||||||
setUsername(username);
|
setCreatedDays(user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24) : 0);
|
||||||
|
} else {
|
||||||
|
toastHelper.error("User not found");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toastHelper.error("User not found");
|
// do nth
|
||||||
});
|
});
|
||||||
} else if (user) {
|
} else if (user) {
|
||||||
setUsername(user.name);
|
setUsername(user.name);
|
||||||
|
setCreatedDays(user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24) : 0);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -43,6 +48,7 @@ const UserBanner: React.FC<Props> = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="user-banner-container">
|
<div className="user-banner-container">
|
||||||
<div className="username-container" onClick={handleUsernameClick}>
|
<div className="username-container" onClick={handleUsernameClick}>
|
||||||
<span className="username-text">{username}</span>
|
<span className="username-text">{username}</span>
|
||||||
@@ -53,6 +59,21 @@ const UserBanner: React.FC<Props> = () => {
|
|||||||
</span>
|
</span>
|
||||||
<MenuBtnsPopup shownStatus={shouldShowPopupBtns} setShownStatus={setShouldShowPopupBtns} />
|
<MenuBtnsPopup shownStatus={shouldShowPopupBtns} setShownStatus={setShouldShowPopupBtns} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="status-text-container">
|
||||||
|
<div className="status-text memos-text">
|
||||||
|
<span className="amount-text">{memos.length}</span>
|
||||||
|
<span className="type-text">MEMO</span>
|
||||||
|
</div>
|
||||||
|
<div className="status-text tags-text">
|
||||||
|
<span className="amount-text">{tags.length}</span>
|
||||||
|
<span className="type-text">TAG</span>
|
||||||
|
</div>
|
||||||
|
<div className="status-text duration-text">
|
||||||
|
<span className="amount-text">{createdDays}</span>
|
||||||
|
<span className="type-text">DAY</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -44,8 +44,8 @@ export function getUserList() {
|
|||||||
return axios.get<ResponseObject<User[]>>("/api/user");
|
return axios.get<ResponseObject<User[]>>("/api/user");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserNameById(id: number) {
|
export function getUserById(id: number) {
|
||||||
return axios.get<ResponseObject<string>>(`/api/user/${id}/name`);
|
return axios.get<ResponseObject<User>>(`/api/user/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchUser(userPatch: UserPatch) {
|
export function patchUser(userPatch: UserPatch) {
|
||||||
@@ -87,7 +87,7 @@ export function deleteMemo(memoId: MemoId) {
|
|||||||
return axios.delete(`/api/memo/${memoId}`);
|
return axios.delete(`/api/memo/${memoId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getShortcutList(shortcutFind: ShortcutFind) {
|
export function getShortcutList(shortcutFind?: ShortcutFind) {
|
||||||
const queryList = [];
|
const queryList = [];
|
||||||
if (shortcutFind?.creatorId) {
|
if (shortcutFind?.creatorId) {
|
||||||
queryList.push(`creatorId=${shortcutFind.creatorId}`);
|
queryList.push(`creatorId=${shortcutFind.creatorId}`);
|
||||||
|
@@ -27,22 +27,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .status-text-container {
|
|
||||||
.flex(row, space-between, flex-start);
|
|
||||||
@apply w-full px-6 select-none shrink-0 pb-4;
|
|
||||||
|
|
||||||
> .status-text {
|
|
||||||
.flex(column, flex-start, flex-start);
|
|
||||||
|
|
||||||
> .amount-text {
|
|
||||||
@apply font-bold text-2xl opacity-80 leading-10;
|
|
||||||
color: @text-black;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .type-text {
|
|
||||||
@apply text-gray-400 text-xs font-mono;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -27,3 +27,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-text-container {
|
||||||
|
@apply flex flex-row justify-between items-start w-full px-6 select-none shrink-0 pb-4;
|
||||||
|
|
||||||
|
> .status-text {
|
||||||
|
.flex(column, flex-start, flex-start);
|
||||||
|
|
||||||
|
> .amount-text {
|
||||||
|
@apply font-bold text-2xl opacity-80 leading-10;
|
||||||
|
color: @text-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .type-text {
|
||||||
|
@apply text-gray-400 text-xs font-mono;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -22,10 +22,8 @@ function Home() {
|
|||||||
if (!userService.getState().user) {
|
if (!userService.getState().user) {
|
||||||
if (userService.isVisitorMode()) {
|
if (userService.isVisitorMode()) {
|
||||||
const currentUserId = userService.getUserIdFromPath() as number;
|
const currentUserId = userService.getUserIdFromPath() as number;
|
||||||
const {
|
const user = await userService.getUserById(currentUserId);
|
||||||
data: { data: username },
|
if (!user) {
|
||||||
} = await api.getUserNameById(currentUserId);
|
|
||||||
if (!username) {
|
|
||||||
toastHelper.error("User not found");
|
toastHelper.error("User not found");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@@ -18,7 +18,7 @@ const memoService = {
|
|||||||
|
|
||||||
fetchAllMemos: async () => {
|
fetchAllMemos: async () => {
|
||||||
const memoFind: MemoFind = {
|
const memoFind: MemoFind = {
|
||||||
creatorId: userService.getCurrentUserId(),
|
creatorId: userService.getUserIdFromPath(),
|
||||||
};
|
};
|
||||||
const { data } = (await api.getMemoList(memoFind)).data;
|
const { data } = (await api.getMemoList(memoFind)).data;
|
||||||
const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m));
|
const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m));
|
||||||
@@ -29,7 +29,7 @@ const memoService = {
|
|||||||
|
|
||||||
fetchArchivedMemos: async () => {
|
fetchArchivedMemos: async () => {
|
||||||
const memoFind: MemoFind = {
|
const memoFind: MemoFind = {
|
||||||
creatorId: userService.getCurrentUserId(),
|
creatorId: userService.getUserIdFromPath(),
|
||||||
rowStatus: "ARCHIVED",
|
rowStatus: "ARCHIVED",
|
||||||
};
|
};
|
||||||
const { data } = (await api.getMemoList(memoFind)).data;
|
const { data } = (await api.getMemoList(memoFind)).data;
|
||||||
@@ -51,7 +51,7 @@ const memoService = {
|
|||||||
|
|
||||||
updateTagsState: async () => {
|
updateTagsState: async () => {
|
||||||
const tagFind: TagFind = {
|
const tagFind: TagFind = {
|
||||||
creatorId: userService.getCurrentUserId(),
|
creatorId: userService.getUserIdFromPath(),
|
||||||
};
|
};
|
||||||
const { data } = (await api.getTagList(tagFind)).data;
|
const { data } = (await api.getTagList(tagFind)).data;
|
||||||
store.dispatch(setTags(data));
|
store.dispatch(setTags(data));
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import * as api from "../helpers/api";
|
import * as api from "../helpers/api";
|
||||||
import store from "../store/";
|
import store from "../store/";
|
||||||
import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut";
|
import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut";
|
||||||
import userService from "./userService";
|
|
||||||
|
|
||||||
const convertResponseModelShortcut = (shortcut: Shortcut): Shortcut => {
|
const convertResponseModelShortcut = (shortcut: Shortcut): Shortcut => {
|
||||||
return {
|
return {
|
||||||
@@ -17,10 +16,7 @@ const shortcutService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getMyAllShortcuts: async () => {
|
getMyAllShortcuts: async () => {
|
||||||
const shortcutFind: ShortcutFind = {
|
const { data } = (await api.getShortcutList()).data;
|
||||||
creatorId: userService.getCurrentUserId(),
|
|
||||||
};
|
|
||||||
const { data } = (await api.getShortcutList(shortcutFind)).data;
|
|
||||||
const shortcuts = data.map((s) => convertResponseModelShortcut(s));
|
const shortcuts = data.map((s) => convertResponseModelShortcut(s));
|
||||||
store.dispatch(setShortcuts(shortcuts));
|
store.dispatch(setShortcuts(shortcuts));
|
||||||
},
|
},
|
||||||
|
@@ -49,6 +49,15 @@ const userService = {
|
|||||||
await api.signout();
|
await api.signout();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUserById: async (userId: UserId) => {
|
||||||
|
const { data: user } = (await api.getUserById(userId)).data;
|
||||||
|
if (user) {
|
||||||
|
return convertResponseModelUser(user);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
patchUser: async (userPatch: UserPatch): Promise<void> => {
|
patchUser: async (userPatch: UserPatch): Promise<void> => {
|
||||||
const { data } = (await api.patchUser(userPatch)).data;
|
const { data } = (await api.patchUser(userPatch)).data;
|
||||||
const user = convertResponseModelUser(data);
|
const user = convertResponseModelUser(data);
|
||||||
|
Reference in New Issue
Block a user