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 { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||
import * as utils from "../helpers/utils";
|
||||
import { editorStateService, memoService } from "../services";
|
||||
import { editorStateService, memoService, userService } from "../services";
|
||||
import { parseHtmlToRawText } from "../helpers/marked";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import toastHelper from "./Toast";
|
||||
@@ -122,12 +122,16 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
<div className="header-container">
|
||||
<p className="time-text">{utils.getDateTimeString(memo.createdTs)}</p>
|
||||
<div className="btns-container">
|
||||
<Only when={!userService.isVisitorMode()}>
|
||||
<>
|
||||
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
||||
<img className={`icon-img ${memo.visibility === "PRIVATE" ? "opacity-30" : ""}`} src="/icons/visibility.svg" />
|
||||
</button>
|
||||
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
||||
<img className="icon-img" src="/icons/edit.svg" />
|
||||
</button>
|
||||
</>
|
||||
</Only>
|
||||
<button className="btn close-btn" onClick={props.destroy}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
</button>
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import { useEffect } from "react";
|
||||
import { locationService, shortcutService, userService } from "../services";
|
||||
import { locationService, shortcutService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import * as utils from "../helpers/utils";
|
||||
import useToggle from "../hooks/useToggle";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import toastHelper from "./Toast";
|
||||
import showCreateShortcutDialog from "./CreateShortcutDialog";
|
||||
import "../less/shortcut-list.less";
|
||||
@@ -39,11 +38,9 @@ const ShortcutList: React.FC<Props> = () => {
|
||||
<div className="shortcuts-wrapper">
|
||||
<p className="title-text">
|
||||
<span className="normal-text">Shortcuts</span>
|
||||
<Only when={!userService.isVisitorMode()}>
|
||||
<span className="btn" onClick={() => showCreateShortcutDialog()}>
|
||||
<img src="/icons/add.svg" alt="add shortcut" />
|
||||
</span>
|
||||
</Only>
|
||||
</p>
|
||||
<div className="shortcuts-container">
|
||||
{sortedShortcuts.map((s) => {
|
||||
@@ -114,7 +111,7 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
||||
<div className="shortcut-text-container">
|
||||
<span className="shortcut-text">{shortcut.title}</span>
|
||||
</div>
|
||||
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
|
||||
<div className="btns-container">
|
||||
<span className="action-btn toggle-btn">
|
||||
<img className="icon-img" src="/icons/more.svg" />
|
||||
</span>
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import { useAppSelector } from "../store";
|
||||
import * as utils from "../helpers/utils";
|
||||
import { userService } from "../services";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import showDailyReviewDialog from "./DailyReviewDialog";
|
||||
@@ -14,11 +12,6 @@ import "../less/siderbar.less";
|
||||
interface 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 = () => {
|
||||
showSettingDialog();
|
||||
};
|
||||
@@ -35,20 +28,6 @@ const Sidebar: React.FC<Props> = () => {
|
||||
</span>
|
||||
</div>
|
||||
<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 />
|
||||
<Only when={!userService.isVisitorMode()}>
|
||||
<div className="action-btns-container">
|
||||
@@ -62,8 +41,8 @@ const Sidebar: React.FC<Props> = () => {
|
||||
<span className="icon">🗂</span> Archived
|
||||
</button>
|
||||
</div>
|
||||
</Only>
|
||||
<ShortcutList />
|
||||
</Only>
|
||||
<TagList />
|
||||
</aside>
|
||||
);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import * as api from "../helpers/api";
|
||||
import * as utils from "../helpers/utils";
|
||||
import userService from "../services/userService";
|
||||
import { locationService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
@@ -11,26 +11,31 @@ interface Props {}
|
||||
|
||||
const UserBanner: React.FC<Props> = () => {
|
||||
const user = useAppSelector((state) => state.user.user);
|
||||
const { memos, tags } = useAppSelector((state) => state.memo);
|
||||
const [shouldShowPopupBtns, setShouldShowPopupBtns] = useState(false);
|
||||
const [username, setUsername] = useState("Memos");
|
||||
const [createdDays, setCreatedDays] = useState(0);
|
||||
const isVisitorMode = userService.isVisitorMode();
|
||||
|
||||
useEffect(() => {
|
||||
const currentUserId = userService.getUserIdFromPath();
|
||||
if (isVisitorMode && currentUserId) {
|
||||
api
|
||||
.getUserNameById(currentUserId)
|
||||
.then(({ data }) => {
|
||||
const { data: username } = data;
|
||||
if (username) {
|
||||
setUsername(username);
|
||||
userService
|
||||
.getUserById(currentUserId)
|
||||
.then((user) => {
|
||||
if (user) {
|
||||
setUsername(user.name);
|
||||
setCreatedDays(user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24) : 0);
|
||||
} else {
|
||||
toastHelper.error("User not found");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toastHelper.error("User not found");
|
||||
// do nth
|
||||
});
|
||||
} else if (user) {
|
||||
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 (
|
||||
<>
|
||||
<div className="user-banner-container">
|
||||
<div className="username-container" onClick={handleUsernameClick}>
|
||||
<span className="username-text">{username}</span>
|
||||
@@ -53,6 +59,21 @@ const UserBanner: React.FC<Props> = () => {
|
||||
</span>
|
||||
<MenuBtnsPopup shownStatus={shouldShowPopupBtns} setShownStatus={setShouldShowPopupBtns} />
|
||||
</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");
|
||||
}
|
||||
|
||||
export function getUserNameById(id: number) {
|
||||
return axios.get<ResponseObject<string>>(`/api/user/${id}/name`);
|
||||
export function getUserById(id: number) {
|
||||
return axios.get<ResponseObject<User>>(`/api/user/${id}`);
|
||||
}
|
||||
|
||||
export function patchUser(userPatch: UserPatch) {
|
||||
@@ -87,7 +87,7 @@ export function deleteMemo(memoId: MemoId) {
|
||||
return axios.delete(`/api/memo/${memoId}`);
|
||||
}
|
||||
|
||||
export function getShortcutList(shortcutFind: ShortcutFind) {
|
||||
export function getShortcutList(shortcutFind?: ShortcutFind) {
|
||||
const queryList = [];
|
||||
if (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.isVisitorMode()) {
|
||||
const currentUserId = userService.getUserIdFromPath() as number;
|
||||
const {
|
||||
data: { data: username },
|
||||
} = await api.getUserNameById(currentUserId);
|
||||
if (!username) {
|
||||
const user = await userService.getUserById(currentUserId);
|
||||
if (!user) {
|
||||
toastHelper.error("User not found");
|
||||
}
|
||||
} else {
|
||||
|
@@ -18,7 +18,7 @@ const memoService = {
|
||||
|
||||
fetchAllMemos: async () => {
|
||||
const memoFind: MemoFind = {
|
||||
creatorId: userService.getCurrentUserId(),
|
||||
creatorId: userService.getUserIdFromPath(),
|
||||
};
|
||||
const { data } = (await api.getMemoList(memoFind)).data;
|
||||
const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m));
|
||||
@@ -29,7 +29,7 @@ const memoService = {
|
||||
|
||||
fetchArchivedMemos: async () => {
|
||||
const memoFind: MemoFind = {
|
||||
creatorId: userService.getCurrentUserId(),
|
||||
creatorId: userService.getUserIdFromPath(),
|
||||
rowStatus: "ARCHIVED",
|
||||
};
|
||||
const { data } = (await api.getMemoList(memoFind)).data;
|
||||
@@ -51,7 +51,7 @@ const memoService = {
|
||||
|
||||
updateTagsState: async () => {
|
||||
const tagFind: TagFind = {
|
||||
creatorId: userService.getCurrentUserId(),
|
||||
creatorId: userService.getUserIdFromPath(),
|
||||
};
|
||||
const { data } = (await api.getTagList(tagFind)).data;
|
||||
store.dispatch(setTags(data));
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import * as api from "../helpers/api";
|
||||
import store from "../store/";
|
||||
import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut";
|
||||
import userService from "./userService";
|
||||
|
||||
const convertResponseModelShortcut = (shortcut: Shortcut): Shortcut => {
|
||||
return {
|
||||
@@ -17,10 +16,7 @@ const shortcutService = {
|
||||
},
|
||||
|
||||
getMyAllShortcuts: async () => {
|
||||
const shortcutFind: ShortcutFind = {
|
||||
creatorId: userService.getCurrentUserId(),
|
||||
};
|
||||
const { data } = (await api.getShortcutList(shortcutFind)).data;
|
||||
const { data } = (await api.getShortcutList()).data;
|
||||
const shortcuts = data.map((s) => convertResponseModelShortcut(s));
|
||||
store.dispatch(setShortcuts(shortcuts));
|
||||
},
|
||||
|
@@ -49,6 +49,15 @@ const userService = {
|
||||
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> => {
|
||||
const { data } = (await api.patchUser(userPatch)).data;
|
||||
const user = convertResponseModelUser(data);
|
||||
|
Reference in New Issue
Block a user