refactor: visitor view (#107)

* refactor: update api

* refactor: visitor view

* chore: update seed data
This commit is contained in:
boojack 2022-07-07 23:11:20 +08:00 committed by GitHub
parent 346d219cd5
commit 6f32643d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 252 additions and 257 deletions

View File

@ -1,6 +1,6 @@
package api
type Login struct {
type Signin struct {
Email string `json:"email"`
Password string `json:"password"`
}

View File

@ -13,33 +13,33 @@ import (
)
func (s *Server) registerAuthRoutes(g *echo.Group) {
g.POST("/auth/login", func(c echo.Context) error {
login := &api.Login{}
if err := json.NewDecoder(c.Request().Body).Decode(login); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted login request").SetInternal(err)
g.POST("/auth/signin", func(c echo.Context) error {
signin := &api.Signin{}
if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
}
userFind := &api.UserFind{
Email: &login.Email,
Email: &signin.Email,
}
user, err := s.Store.FindUser(userFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by email %s", login.Email)).SetInternal(err)
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by email %s", signin.Email)).SetInternal(err)
}
if user == nil {
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found with email %s", login.Email))
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found with email %s", signin.Email))
} else if user.RowStatus == api.Archived {
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", login.Email))
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with email %s", signin.Email))
}
// Compare the stored hashed password, with the hashed version of the password that was received.
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(login.Password)); err != nil {
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(signin.Password)); err != nil {
// If the two passwords don't match, return a 401 status.
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect password").SetInternal(err)
}
if err = setUserSession(c, user); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to set login session").SetInternal(err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to set signin session").SetInternal(err)
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)

View File

@ -60,31 +60,17 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
})
g.GET("/memo", func(c echo.Context) error {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
if c.QueryParam("userID") != "" {
var err error
userID, err = strconv.Atoi(c.QueryParam("userID"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.QueryParam("userID")))
}
} else {
ownerUserType := api.Owner
ownerUser, err := s.Store.FindUser(&api.UserFind{
Role: &ownerUserType,
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find owner user").SetInternal(err)
}
if ownerUser == nil {
return echo.NewHTTPError(http.StatusNotFound, "Owner user do not exist")
}
userID = ownerUser.ID
}
}
memoFind := &api.MemoFind{}
memoFind := &api.MemoFind{
CreatorID: &userID,
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
memoFind.CreatorID = &userID
} else {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusBadRequest, "Missing creatorId to find memo")
}
memoFind.CreatorID = &userID
}
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))

View File

@ -45,7 +45,7 @@ func NewServer(profile *profile.Profile) *Server {
HTML5: true,
}))
// In dev mode, set the const secret key to make login session persistence.
// In dev mode, set the const secret key to make signin session persistence.
secret := []byte("usememos")
if profile.Mode == "prod" {
secret = securecookie.GenerateRandomKey(16)

View File

@ -59,32 +59,19 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) {
})
g.GET("/shortcut", func(c echo.Context) error {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
if c.QueryParam("userID") != "" {
var err error
userID, err = strconv.Atoi(c.QueryParam("userID"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.QueryParam("userID")))
}
} else {
ownerUserType := api.Owner
ownerUser, err := s.Store.FindUser(&api.UserFind{
Role: &ownerUserType,
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find owner user").SetInternal(err)
}
if ownerUser == nil {
return echo.NewHTTPError(http.StatusNotFound, "Owner user do not exist")
}
userID = ownerUser.ID
shortcutFind := &api.ShortcutFind{}
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
shortcutFind.CreatorID = &userID
} else {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusBadRequest, "Missing creatorId to find shortcut")
}
shortcutFind.CreatorID = &userID
}
shortcutFind := &api.ShortcutFind{
CreatorID: &userID,
}
list, err := s.Store.FindShortcutList(shortcutFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch shortcut list").SetInternal(err)

View File

@ -2,7 +2,6 @@ package server
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"sort"
@ -15,37 +14,24 @@ import (
func (s *Server) registerTagRoutes(g *echo.Group) {
g.GET("/tag", func(c echo.Context) error {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
if c.QueryParam("userID") != "" {
var err error
userID, err = strconv.Atoi(c.QueryParam("userID"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.QueryParam("userID")))
}
} else {
ownerUserType := api.Owner
ownerUser, err := s.Store.FindUser(&api.UserFind{
Role: &ownerUserType,
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find owner user").SetInternal(err)
}
if ownerUser == nil {
return echo.NewHTTPError(http.StatusNotFound, "Owner user do not exist")
}
userID = ownerUser.ID
}
}
contentSearch := "#"
normalRowStatus := api.Normal
memoFind := api.MemoFind{
CreatorID: &userID,
ContentSearch: &contentSearch,
RowStatus: &normalRowStatus,
}
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil {
memoFind.CreatorID = &userID
} else {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusBadRequest, "Missing creatorId to find shortcut")
}
memoFind.CreatorID = &userID
}
memoList, err := s.Store.FindMemoList(&memoFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err)

View File

@ -52,6 +52,24 @@ INSERT INTO
VALUES
(
104,
'好好学习,天天向上。🤜🤛',
'#TODO
- [x] Take more photos about **🌄 sunset**;
- [ ] Clean the classroom;
- [ ] Watch *👦 The Boys*;
(👆 click to toggle status)
',
102
);
INSERT INTO
memo (
`id`,
`content`,
`creator_id`
)
VALUES
(
105,
'三人行,必有我师焉!👨‍🏫',
102
);

View File

@ -11,12 +11,14 @@
"@reduxjs/toolkit": "^1.8.1",
"axios": "^0.27.2",
"lodash-es": "^4.17.21",
"qs": "^6.11.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-redux": "^8.0.1"
},
"devDependencies": {
"@types/lodash-es": "^4.17.5",
"@types/qs": "^6.9.7",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.4",
"@typescript-eslint/eslint-plugin": "^5.6.0",

View File

@ -112,7 +112,11 @@ const Memo: React.FC<Props> = (props: Props) => {
} else {
locationService.setTagQuery(tagName);
}
} else if (targetEl.classList.contains("todo-block") && userService.isNotVisitor()) {
} else if (targetEl.classList.contains("todo-block")) {
if (userService.isVisitorMode()) {
return;
}
const status = targetEl.dataset?.value;
const todoElementList = [...(memoContainerRef.current?.querySelectorAll(`span.todo-block[data-value=${status}]`) ?? [])];
for (const element of todoElementList) {
@ -158,40 +162,38 @@ const Memo: React.FC<Props> = (props: Props) => {
<span className="ml-2">PINNED</span>
</Only>
</span>
{userService.isNotVisitor() && (
<div className="btns-container">
<span className="btn more-action-btn">
<img className="icon-img" src="/icons/more.svg" />
</span>
<div className="more-action-btns-wrapper">
<div className="more-action-btns-container">
<div className="btns-container">
<div className="btn" onClick={handleTogglePinMemoBtnClick}>
<img className="icon-img" src="/icons/pin.svg" alt="" />
<span className="tip-text">{memo.pinned ? "Unpin" : "Pin"}</span>
</div>
<div className="btn" onClick={handleEditMemoClick}>
<img className="icon-img" src="/icons/edit.svg" alt="" />
<span className="tip-text">Edit</span>
</div>
<div className="btn" onClick={handleGenMemoImageBtnClick}>
<img className="icon-img" src="/icons/share.svg" alt="" />
<span className="tip-text">Share</span>
</div>
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
<span className="btn more-action-btn">
<img className="icon-img" src="/icons/more.svg" />
</span>
<div className="more-action-btns-wrapper">
<div className="more-action-btns-container">
<div className="btns-container">
<div className="btn" onClick={handleTogglePinMemoBtnClick}>
<img className="icon-img" src="/icons/pin.svg" alt="" />
<span className="tip-text">{memo.pinned ? "Unpin" : "Pin"}</span>
</div>
<div className="btn" onClick={handleEditMemoClick}>
<img className="icon-img" src="/icons/edit.svg" alt="" />
<span className="tip-text">Edit</span>
</div>
<div className="btn" onClick={handleGenMemoImageBtnClick}>
<img className="icon-img" src="/icons/share.svg" alt="" />
<span className="tip-text">Share</span>
</div>
<span className="btn" onClick={handleMarkMemoClick}>
Mark
</span>
<span className="btn" onClick={handleShowMemoStoryDialog}>
View Story
</span>
<span className="btn archive-btn" onClick={handleArchiveMemoClick}>
Archive
</span>
</div>
<span className="btn" onClick={handleMarkMemoClick}>
Mark
</span>
<span className="btn" onClick={handleShowMemoStoryDialog}>
View Story
</span>
<span className="btn archive-btn" onClick={handleArchiveMemoClick}>
Archive
</span>
</div>
</div>
)}
</div>
</div>
<div
ref={memoContainerRef}

View File

@ -67,8 +67,8 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
<button className="btn action-btn" onClick={handlePingBtnClick}>
<span className="icon">🎯</span> Ping
</button>
<button className="btn action-btn" onClick={userService.isNotVisitor() ? handleSignOutBtnClick : handleSignInBtnClick}>
<span className="icon">👋</span> {userService.isNotVisitor() ? "Sign out" : "Sign in"}
<button className="btn action-btn" onClick={!userService.isVisitorMode() ? handleSignOutBtnClick : handleSignInBtnClick}>
<span className="icon">👋</span> {!userService.isVisitorMode() ? "Sign out" : "Sign in"}
</button>
</div>
);

View File

@ -4,6 +4,7 @@ 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";
@ -38,11 +39,11 @@ const ShortcutList: React.FC<Props> = () => {
<div className="shortcuts-wrapper">
<p className="title-text">
<span className="normal-text">Shortcuts</span>
{userService.isNotVisitor() && (
<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) => {
@ -66,9 +67,6 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
if (isActive) {
locationService.setMemoShortcut(undefined);
} else {
if (!["/"].includes(locationService.getState().pathname)) {
locationService.setPathname("/");
}
locationService.setMemoShortcut(shortcut.id);
}
};
@ -116,30 +114,28 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
<div className="shortcut-text-container">
<span className="shortcut-text">{shortcut.title}</span>
</div>
{userService.isNotVisitor() && (
<div className="btns-container">
<span className="action-btn toggle-btn">
<img className="icon-img" src="/icons/more.svg" />
</span>
<div className="action-btns-wrapper">
<div className="action-btns-container">
<span className="btn" onClick={handlePinShortcutBtnClick}>
{shortcut.rowStatus === "ARCHIVED" ? "Unpin" : "Pin"}
</span>
<span className="btn" onClick={handleEditShortcutBtnClick}>
Edit
</span>
<span
className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`}
onClick={handleDeleteMemoClick}
onMouseLeave={handleDeleteBtnMouseLeave}
>
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
</span>
</div>
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
<span className="action-btn toggle-btn">
<img className="icon-img" src="/icons/more.svg" />
</span>
<div className="action-btns-wrapper">
<div className="action-btns-container">
<span className="btn" onClick={handlePinShortcutBtnClick}>
{shortcut.rowStatus === "ARCHIVED" ? "Unpin" : "Pin"}
</span>
<span className="btn" onClick={handleEditShortcutBtnClick}>
Edit
</span>
<span
className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`}
onClick={handleDeleteMemoClick}
onMouseLeave={handleDeleteBtnMouseLeave}
>
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
</span>
</div>
</div>
)}
</div>
</div>
</>
);

View File

@ -1,6 +1,7 @@
import { useAppSelector } from "../store";
import * as utils from "../helpers/utils";
import { userService } from "../services";
import Only from "./common/OnlyWhen";
import showDailyReviewDialog from "./DailyReviewDialog";
import showSettingDialog from "./SettingDialog";
import showArchivedMemoDialog from "./ArchivedMemoDialog";
@ -49,21 +50,19 @@ const Sidebar: React.FC<Props> = () => {
</div>
</div>
<UsageHeatMap />
{userService.isNotVisitor() && (
<>
<div className="action-btns-container">
<button className="btn action-btn" onClick={() => showDailyReviewDialog()}>
<span className="icon">📅</span> Daily Review
</button>
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
<span className="icon"></span> Setting
</button>
<button className="btn action-btn" onClick={handleArchivedBtnClick}>
<span className="icon">🗂</span> Archived
</button>
</div>
</>
)}
<Only when={!userService.isVisitorMode()}>
<div className="action-btns-container">
<button className="btn action-btn" onClick={() => showDailyReviewDialog()}>
<span className="icon">📅</span> Daily Review
</button>
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
<span className="icon"></span> Setting
</button>
<button className="btn action-btn" onClick={handleArchivedBtnClick}>
<span className="icon">🗂</span> Archived
</button>
</div>
</Only>
<ShortcutList />
<TagList />
</aside>

View File

@ -71,7 +71,7 @@ const TagList: React.FC<Props> = () => {
{tags.map((t, idx) => (
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={query?.tag} />
))}
<Only when={userService.isNotVisitor() && tags.length < 5}>
<Only when={!userService.isVisitorMode() && tags.length < 5}>
<p className="tag-tip-container">
Enter <span className="code-text">#tag </span> to create a tag
</p>
@ -97,9 +97,6 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
locationService.setTagQuery(undefined);
} else {
utils.copyTextToClipboard(`#${tag.text} `);
if (!["/"].includes(locationService.getState().pathname)) {
locationService.setPathname("/");
}
locationService.setTagQuery(tag.text);
}
};

View File

@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import * as api from "../helpers/api";
import { getUserIdFromPath } from "../services/userService";
import userService from "../services/userService";
import { locationService } from "../services";
import { useAppSelector } from "../store";
import toastHelper from "./Toast";
@ -31,15 +31,18 @@ const UserBanner: React.FC<Props> = () => {
setUsername(status.owner.name);
});
} else {
api
.getUserNameById(Number(getUserIdFromPath()))
.then(({ data }) => {
const { data: username } = data;
setUsername(username);
})
.catch(() => {
toastHelper.error("User not found");
});
const currentUserId = userService.getCurrentUserId();
if (currentUserId) {
api
.getUserNameById(currentUserId)
.then(({ data }) => {
const { data: username } = data;
setUsername(username);
})
.catch(() => {
toastHelper.error("User not found");
});
}
}
}
}, []);

View File

@ -12,8 +12,8 @@ export function getSystemStatus() {
return axios.get<ResponseObject<SystemStatus>>("/api/status");
}
export function login(email: string, password: string) {
return axios.post<ResponseObject<User>>("/api/auth/login", {
export function signin(email: string, password: string) {
return axios.post<ResponseObject<User>>("/api/auth/signin", {
email,
password,
});
@ -52,12 +52,15 @@ export function patchUser(userPatch: UserPatch) {
return axios.patch<ResponseObject<User>>("/api/user/me", userPatch);
}
export function getMemoList(userId?: number) {
return axios.get<ResponseObject<Memo[]>>(`/api/memo${userId ? "?userID=" + userId : ""}`);
}
export function getArchivedMemoList(userId?: number) {
return axios.get<ResponseObject<Memo[]>>(`/api/memo?rowStatus=ARCHIVED${userId ? "&userID=" + userId : ""}`);
export function getMemoList(memoFind?: MemoFind) {
const queryList = [];
if (memoFind?.creatorId) {
queryList.push(`creatorId=${memoFind.creatorId}`);
}
if (memoFind?.rowStatus) {
queryList.push(`rowStatus=${memoFind.rowStatus}`);
}
return axios.get<ResponseObject<Memo[]>>(`/api/memo?${queryList.join("&")}`);
}
export function createMemo(memoCreate: MemoCreate) {
@ -84,8 +87,12 @@ export function deleteMemo(memoId: MemoId) {
return axios.delete(`/api/memo/${memoId}`);
}
export function getShortcutList(userId?: number) {
return axios.get<ResponseObject<Shortcut[]>>(`/api/shortcut${userId ? "?userID=" + userId : ""}`);
export function getShortcutList(shortcutFind: ShortcutFind) {
const queryList = [];
if (shortcutFind?.creatorId) {
queryList.push(`creatorId=${shortcutFind.creatorId}`);
}
return axios.get<ResponseObject<Shortcut[]>>(`/api/shortcut?${queryList.join("&")}`);
}
export function createShortcut(shortcutCreate: ShortcutCreate) {
@ -104,6 +111,10 @@ export function uploadFile(formData: FormData) {
return axios.post<ResponseObject<Resource>>("/api/resource", formData);
}
export function getTagList(userId?: number) {
return axios.get<ResponseObject<string[]>>(`/api/tag${userId ? "?userID=" + userId : ""}`);
export function getTagList(tagFind?: TagFind) {
const queryList = [];
if (tagFind?.creatorId) {
queryList.push(`creatorId=${tagFind.creatorId}`);
}
return axios.get<ResponseObject<string[]>>(`/api/tag?${queryList.join("&")}`);
}

View File

@ -126,38 +126,6 @@ export function throttle(fn: FunctionType, delay: number) {
};
}
export function transformObjectToParamsString(object: KVObject): string {
const params = [];
const keys = Object.keys(object).sort();
for (const key of keys) {
const val = object[key];
if (val) {
if (typeof val === "object") {
params.push(...transformObjectToParamsString(val).split("&"));
} else {
params.push(`${key}=${val}`);
}
}
}
return params.join("&");
}
export function transformParamsStringToObject(paramsString: string): KVObject {
const object: KVObject = {};
const params = paramsString.split("&");
for (const p of params) {
const [key, val] = p.split("=");
if (key && val) {
object[key] = val;
}
}
return object;
}
export function filterObjectNullKeys(object: KVObject): KVObject {
if (!object) {
return {};

View File

@ -1,7 +1,8 @@
import { useEffect } from "react";
import { locationService, userService } from "../services";
import Sidebar from "../components/Sidebar";
import { userService } from "../services";
import useLoading from "../hooks/useLoading";
import Only from "../components/common/OnlyWhen";
import Sidebar from "../components/Sidebar";
import MemosHeader from "../components/MemosHeader";
import MemoEditor from "../components/MemoEditor";
import MemoFilter from "../components/MemoFilter";
@ -12,25 +13,14 @@ function Home() {
const loadingState = useLoading();
useEffect(() => {
if (window.location.pathname !== locationService.getState().pathname) {
locationService.replaceHistory("/");
}
const { user } = userService.getState();
if (!user) {
userService
.doSignIn()
.catch(() => {
// do nth
})
.finally(() => {
if (userService.getState().user && locationService.getState().pathname !== "/") {
locationService.replaceHistory("/");
}
loadingState.setFinish();
});
} else {
loadingState.setFinish();
}
userService
.doSignIn()
.catch(() => {
// do nth
})
.finally(() => {
loadingState.setFinish();
});
}, []);
return (
@ -41,7 +31,9 @@ function Home() {
<main className="memos-wrapper">
<div className="memos-editor-wrapper">
<MemosHeader />
{userService.isNotVisitor() && <MemoEditor />}
<Only when={!userService.isVisitorMode()}>
<MemoEditor />
</Only>
<MemoFilter />
</div>
<MemoList />

View File

@ -63,7 +63,7 @@ const Signin: React.FC<Props> = () => {
try {
actionBtnLoadingState.setLoading();
await api.login(email, password);
await api.signin(email, password);
const user = await userService.doSignIn();
if (user) {
locationService.replaceHistory("/");

View File

@ -1,10 +1,10 @@
import * as utils from "../helpers/utils";
import { stringify } from "qs";
import store from "../store";
import { setQuery, setPathname, Query } from "../store/modules/location";
const updateLocationUrl = (method: "replace" | "push" = "replace") => {
const { query, pathname, hash } = store.getState().location;
let queryString = utils.transformObjectToParamsString(query ?? {});
let queryString = stringify(query);
if (queryString) {
queryString = "?" + queryString;
} else {

View File

@ -1,7 +1,7 @@
import * as api from "../helpers/api";
import { createMemo, patchMemo, setMemos, setTags } from "../store/modules/memo";
import store from "../store";
import { getUserIdFromPath } from "./userService";
import userService from "./userService";
const convertResponseModelMemo = (memo: Memo): Memo => {
return {
@ -17,7 +17,10 @@ const memoService = {
},
fetchAllMemos: async () => {
const { data } = (await api.getMemoList(getUserIdFromPath())).data;
const memoFind: MemoFind = {
creatorId: userService.getCurrentUserId(),
};
const { data } = (await api.getMemoList(memoFind)).data;
const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m));
store.dispatch(setMemos(memos));
@ -25,7 +28,11 @@ const memoService = {
},
fetchArchivedMemos: async () => {
const { data } = (await api.getArchivedMemoList(getUserIdFromPath())).data;
const memoFind: MemoFind = {
creatorId: userService.getCurrentUserId(),
rowStatus: "ARCHIVED",
};
const { data } = (await api.getMemoList(memoFind)).data;
const archivedMemos = data.map((m) => {
return convertResponseModelMemo(m);
});
@ -43,7 +50,10 @@ const memoService = {
},
updateTagsState: async () => {
const { data } = (await api.getTagList(getUserIdFromPath())).data;
const tagFind: TagFind = {
creatorId: userService.getCurrentUserId(),
};
const { data } = (await api.getTagList(tagFind)).data;
store.dispatch(setTags(data));
},

View File

@ -1,7 +1,7 @@
import * as api from "../helpers/api";
import store from "../store/";
import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut";
import { getUserIdFromPath } from "./userService";
import userService from "./userService";
const convertResponseModelShortcut = (shortcut: Shortcut): Shortcut => {
return {
@ -17,7 +17,10 @@ const shortcutService = {
},
getMyAllShortcuts: async () => {
const { data } = (await api.getShortcutList(getUserIdFromPath())).data;
const shortcutFind: ShortcutFind = {
creatorId: userService.getCurrentUserId(),
};
const { data } = (await api.getShortcutList(shortcutFind)).data;
const shortcuts = data.map((s) => convertResponseModelShortcut(s));
store.dispatch(setShortcuts(shortcuts));
},

View File

@ -1,3 +1,4 @@
import { isUndefined } from "lodash-es";
import { locationService } from ".";
import * as api from "../helpers/api";
import store from "../store";
@ -11,18 +12,26 @@ const convertResponseModelUser = (user: User): User => {
};
};
export const getUserIdFromPath = () => {
const path = locationService.getState().pathname.slice(3);
return !isNaN(Number(path)) ? Number(path) : undefined;
};
const userService = {
getState: () => {
return store.getState().user;
},
isNotVisitor: () => {
return store.getState().user.user !== undefined;
isVisitorMode: () => {
return !isUndefined(userService.getUserIdFromPath());
},
getCurrentUserId: () => {
return userService.getUserIdFromPath() ?? store.getState().user.user?.id;
},
getUserIdFromPath: () => {
const userIdRegex = /^\/u\/(\d+).*/;
const result = locationService.getState().pathname.match(userIdRegex);
if (result && result.length === 2) {
return Number(result[1]);
}
return undefined;
},
doSignIn: async () => {

View File

@ -20,7 +20,8 @@ interface State {
}
const getValidPathname = (pathname: string): string => {
if (["/", "/signin"].includes(pathname) || pathname.match(/^\/u\/(\d+)/)) {
const userPageUrlRegex = /^\/u\/\d+.*/;
if (["/", "/signin"].includes(pathname) || userPageUrlRegex.test(pathname)) {
return pathname;
} else {
return "/";

View File

@ -22,3 +22,8 @@ interface MemoPatch {
content?: string;
rowStatus?: RowStatus;
}
interface MemoFind {
creatorId?: UserId;
rowStatus?: RowStatus;
}

View File

@ -3,6 +3,7 @@ type ShortcutId = number;
interface Shortcut {
id: ShortcutId;
creatorId: UserId;
rowStatus: RowStatus;
createdTs: TimeStamp;
updatedTs: TimeStamp;
@ -22,3 +23,7 @@ interface ShortcutPatch {
payload?: string;
rowStatus?: RowStatus;
}
interface ShortcutFind {
creatorId?: UserId;
}

3
web/src/types/modules/tag.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
interface TagFind {
creatorId?: UserId;
}

View File

@ -358,6 +358,11 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
"@types/qs@^6.9.7":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
"@types/react-dom@^18.0.4":
version "18.0.4"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.4.tgz#dcbcadb277bcf6c411ceff70069424c57797d375"
@ -1988,6 +1993,13 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@^6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"