fix: visitor view in frontend

This commit is contained in:
boojack
2022-07-09 08:32:46 +08:00
parent ac560dfcf9
commit 7418d2965d
11 changed files with 90 additions and 87 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>
); );

View File

@@ -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>
</>
); );
}; };

View File

@@ -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}`);

View File

@@ -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;
}
}
}
} }

View File

@@ -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;
}
}
}

View File

@@ -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 {

View File

@@ -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));

View File

@@ -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));
}, },

View File

@@ -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);