mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
refactor: sync frontend
This commit is contained in:
@ -1,18 +1,18 @@
|
||||
import { memo, useCallback, useEffect, useState } from "react";
|
||||
import { memoService, queryService } from "../services";
|
||||
import { memoService, shortcutService } from "../services";
|
||||
import { checkShouldShowMemoWithFilters, filterConsts, getDefaultFilter, relationConsts } from "../helpers/filter";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import { showDialog } from "./Dialog";
|
||||
import toastHelper from "./Toast";
|
||||
import Selector from "./common/Selector";
|
||||
import "../less/create-query-dialog.less";
|
||||
import "../less/create-shortcut-dialog.less";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
queryId?: string;
|
||||
shortcutId?: string;
|
||||
}
|
||||
|
||||
const CreateQueryDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy, queryId } = props;
|
||||
const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy, shortcutId } = props;
|
||||
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [filters, setFilters] = useState<Filter[]>([]);
|
||||
@ -23,15 +23,15 @@ const CreateQueryDialog: React.FC<Props> = (props: Props) => {
|
||||
}).length;
|
||||
|
||||
useEffect(() => {
|
||||
const queryTemp = queryService.getQueryById(queryId ?? "");
|
||||
if (queryTemp) {
|
||||
setTitle(queryTemp.title);
|
||||
const temp = JSON.parse(queryTemp.querystring);
|
||||
const shortcutTemp = shortcutService.getShortcutById(shortcutId ?? "");
|
||||
if (shortcutTemp) {
|
||||
setTitle(shortcutTemp.title);
|
||||
const temp = JSON.parse(shortcutTemp.payload);
|
||||
if (Array.isArray(temp)) {
|
||||
setFilters(temp);
|
||||
}
|
||||
}
|
||||
}, [queryId]);
|
||||
}, [shortcutId]);
|
||||
|
||||
const handleTitleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const text = e.target.value as string;
|
||||
@ -45,12 +45,12 @@ const CreateQueryDialog: React.FC<Props> = (props: Props) => {
|
||||
}
|
||||
|
||||
try {
|
||||
if (queryId) {
|
||||
const editedQuery = await queryService.updateQuery(queryId, title, JSON.stringify(filters));
|
||||
queryService.editQuery(editedQuery);
|
||||
if (shortcutId) {
|
||||
const editedShortcut = await shortcutService.updateShortcut(shortcutId, title, JSON.stringify(filters));
|
||||
shortcutService.editShortcut(shortcutService.convertResponseModelShortcut(editedShortcut));
|
||||
} else {
|
||||
const query = await queryService.createQuery(title, JSON.stringify(filters));
|
||||
queryService.pushQuery(query);
|
||||
const shortcut = await shortcutService.createShortcut(title, JSON.stringify(filters));
|
||||
shortcutService.pushShortcut(shortcutService.convertResponseModelShortcut(shortcut));
|
||||
}
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error.message);
|
||||
@ -90,7 +90,7 @@ const CreateQueryDialog: React.FC<Props> = (props: Props) => {
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">
|
||||
<span className="icon-text">🔖</span>
|
||||
{queryId ? "编辑检索" : "创建检索"}
|
||||
{shortcutId ? "编辑检索" : "创建检索"}
|
||||
</p>
|
||||
<button className="btn close-btn" onClick={destroy}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
@ -298,12 +298,12 @@ const FilterInputer: React.FC<MemoFilterInputerProps> = (props: MemoFilterInpute
|
||||
|
||||
const MemoFilterInputer: React.FC<MemoFilterInputerProps> = memo(FilterInputer);
|
||||
|
||||
export default function showCreateQueryDialog(queryId?: string): void {
|
||||
export default function showCreateShortcutDialog(shortcutId?: string): void {
|
||||
showDialog(
|
||||
{
|
||||
className: "create-query-dialog",
|
||||
className: "create-shortcut-dialog",
|
||||
},
|
||||
CreateQueryDialog,
|
||||
{ queryId }
|
||||
CreateShortcutDialog,
|
||||
{ shortcutId }
|
||||
);
|
||||
}
|
@ -18,7 +18,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
||||
const memo: FormattedMemo = {
|
||||
...propsMemo,
|
||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
||||
deletedAtStr: utils.getDateTimeString(propsMemo.deletedAt ?? Date.now()),
|
||||
deletedAtStr: utils.getDateTimeString(propsMemo.updatedAt ?? Date.now()),
|
||||
};
|
||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||
|
@ -130,7 +130,7 @@ const MemoEditor: React.FC<Props> = () => {
|
||||
|
||||
try {
|
||||
const image = await resourceService.upload(file);
|
||||
const url = `/r/${image.id}/${image.filename}`;
|
||||
const url = `/h/r/${image.id}/${image.filename}`;
|
||||
|
||||
return url;
|
||||
} catch (error: any) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useContext } from "react";
|
||||
import appContext from "../stores/appContext";
|
||||
import { locationService, queryService } from "../services";
|
||||
import { locationService, shortcutService } from "../services";
|
||||
import utils from "../helpers/utils";
|
||||
import { getTextWithMemoType } from "../helpers/filter";
|
||||
import "../less/memo-filter.less";
|
||||
@ -12,8 +12,8 @@ const MemoFilter: React.FC<FilterProps> = () => {
|
||||
locationState: { query },
|
||||
} = useContext(appContext);
|
||||
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, filter } = query;
|
||||
const queryFilter = queryService.getQueryById(filter);
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId } = query;
|
||||
const queryFilter = shortcutService.getShortcutById(shortcutId);
|
||||
const showFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || queryFilter);
|
||||
|
||||
return (
|
||||
@ -22,7 +22,7 @@ const MemoFilter: React.FC<FilterProps> = () => {
|
||||
<div
|
||||
className={"filter-item-container " + (queryFilter ? "" : "hidden")}
|
||||
onClick={() => {
|
||||
locationService.setMemoFilter("");
|
||||
locationService.setMemoShortcut("");
|
||||
}}
|
||||
>
|
||||
<span className="icon-text">🔖</span> {queryFilter?.title}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import appContext from "../stores/appContext";
|
||||
import { locationService, memoService, queryService } from "../services";
|
||||
import { locationService, memoService, shortcutService } from "../services";
|
||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts";
|
||||
import utils from "../helpers/utils";
|
||||
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
||||
@ -18,8 +18,8 @@ const MemoList: React.FC<Props> = () => {
|
||||
const [isFetching, setFetchStatus] = useState(true);
|
||||
const wrapperElement = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, filter: queryId } = query;
|
||||
const queryFilter = queryService.getQueryById(queryId);
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId } = query;
|
||||
const queryFilter = shortcutService.getShortcutById(shortcutId);
|
||||
const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || queryFilter);
|
||||
|
||||
const shownMemos =
|
||||
@ -28,7 +28,7 @@ const MemoList: React.FC<Props> = () => {
|
||||
let shouldShow = true;
|
||||
|
||||
if (queryFilter) {
|
||||
const filters = JSON.parse(queryFilter.querystring) as Filter[];
|
||||
const filters = JSON.parse(queryFilter.payload) as Filter[];
|
||||
if (Array.isArray(filters)) {
|
||||
shouldShow = checkShouldShowMemoWithFilters(memo, filters);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import appContext from "../stores/appContext";
|
||||
import SearchBar from "./SearchBar";
|
||||
import { globalStateService, memoService, queryService } from "../services";
|
||||
import { globalStateService, memoService, shortcutService } from "../services";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import "../less/memos-header.less";
|
||||
|
||||
@ -12,22 +12,22 @@ interface Props {}
|
||||
const MemosHeader: React.FC<Props> = () => {
|
||||
const {
|
||||
locationState: {
|
||||
query: { filter },
|
||||
query: { shortcutId },
|
||||
},
|
||||
globalState: { isMobileView },
|
||||
queryState: { queries },
|
||||
shortcutState: { shortcuts },
|
||||
} = useContext(appContext);
|
||||
|
||||
const [titleText, setTitleText] = useState("MEMOS");
|
||||
|
||||
useEffect(() => {
|
||||
const query = queryService.getQueryById(filter);
|
||||
const query = shortcutService.getShortcutById(shortcutId);
|
||||
if (query) {
|
||||
setTitleText(query.title);
|
||||
} else {
|
||||
setTitleText("MEMOS");
|
||||
}
|
||||
}, [filter, queries]);
|
||||
}, [shortcutId, shortcuts]);
|
||||
|
||||
const handleMemoTextClick = useCallback(() => {
|
||||
const now = Date.now();
|
||||
|
@ -20,8 +20,8 @@ interface Props {}
|
||||
const MyAccountSection: React.FC<Props> = () => {
|
||||
const { userState } = useContext(appContext);
|
||||
const user = userState.user as Model.User;
|
||||
const [username, setUsername] = useState<string>(user.username);
|
||||
const openAPIRoute = `${window.location.origin}/api/whs/memo/${user.openId}`;
|
||||
const [username, setUsername] = useState<string>(user.name);
|
||||
const openAPIRoute = `${window.location.origin}/h/${user.openId}/memo`;
|
||||
|
||||
const handleUsernameChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const nextUsername = e.target.value as string;
|
||||
@ -29,12 +29,12 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
};
|
||||
|
||||
const handleConfirmEditUsernameBtnClick = async () => {
|
||||
if (user.username === "guest") {
|
||||
if (user.name === "guest") {
|
||||
toastHelper.info("🈲 不要修改我的用户名");
|
||||
return;
|
||||
}
|
||||
|
||||
if (username === user.username) {
|
||||
if (username === user.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
};
|
||||
|
||||
const handleChangePasswordBtnClick = () => {
|
||||
if (user.username === "guest") {
|
||||
if (user.name === "guest") {
|
||||
toastHelper.info("🈲 不要修改我的密码");
|
||||
return;
|
||||
}
|
||||
@ -93,14 +93,14 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
<label className="form-label input-form-label username-label">
|
||||
<span className="normal-text">账号:</span>
|
||||
<input type="text" value={username} onChange={handleUsernameChanged} />
|
||||
<div className={`btns-container ${username === user.username ? "hidden" : ""}`} onClick={handlePreventDefault}>
|
||||
<div className={`btns-container ${username === user.name ? "hidden" : ""}`} onClick={handlePreventDefault}>
|
||||
<span className="btn confirm-btn" onClick={handleConfirmEditUsernameBtnClick}>
|
||||
保存
|
||||
</span>
|
||||
<span
|
||||
className="btn cancel-btn"
|
||||
onClick={() => {
|
||||
setUsername(user.username);
|
||||
setUsername(user.name);
|
||||
}}
|
||||
>
|
||||
撤销
|
||||
|
@ -97,7 +97,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
||||
</Only>
|
||||
<div className="watermark-container">
|
||||
<span className="normal-text">
|
||||
<span className="icon-text">✍️</span> by <span className="name-text">{userinfo?.username}</span>
|
||||
<span className="icon-text">✍️</span> by <span className="name-text">{userinfo?.name}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,27 +5,27 @@ import useLoading from "../hooks/useLoading";
|
||||
import Only from "./common/OnlyWhen";
|
||||
import utils from "../helpers/utils";
|
||||
import toastHelper from "./Toast";
|
||||
import { locationService, queryService } from "../services";
|
||||
import showCreateQueryDialog from "./CreateQueryDialog";
|
||||
import "../less/query-list.less";
|
||||
import { locationService, shortcutService } from "../services";
|
||||
import showCreateQueryDialog from "./CreateShortcutDialog";
|
||||
import "../less/shortcut-list.less";
|
||||
|
||||
interface Props {}
|
||||
|
||||
const QueryList: React.FC<Props> = () => {
|
||||
const ShortcutList: React.FC<Props> = () => {
|
||||
const {
|
||||
queryState: { queries },
|
||||
shortcutState: { shortcuts },
|
||||
locationState: {
|
||||
query: { filter },
|
||||
query: { shortcutId },
|
||||
},
|
||||
} = useContext(appContext);
|
||||
const loadingState = useLoading();
|
||||
const sortedQueries = queries
|
||||
const sortedShortcuts = shortcuts
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.pinnedAt ?? 0) - utils.getTimeStampByDate(a.pinnedAt ?? 0));
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.updatedAt) - utils.getTimeStampByDate(a.updatedAt));
|
||||
|
||||
useEffect(() => {
|
||||
queryService
|
||||
.getMyAllQueries()
|
||||
shortcutService
|
||||
.getMyAllShortcuts()
|
||||
.catch(() => {
|
||||
// do nth
|
||||
})
|
||||
@ -35,47 +35,47 @@ const QueryList: React.FC<Props> = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="queries-wrapper">
|
||||
<div className="shortcuts-wrapper">
|
||||
<p className="title-text">
|
||||
<span className="normal-text">快速检索</span>
|
||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||
+
|
||||
</span>
|
||||
</p>
|
||||
<Only when={loadingState.isSucceed && sortedQueries.length === 0}>
|
||||
<div className="create-query-btn-container">
|
||||
<Only when={loadingState.isSucceed && sortedShortcuts.length === 0}>
|
||||
<div className="create-shortcut-btn-container">
|
||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||
创建检索
|
||||
</span>
|
||||
</div>
|
||||
</Only>
|
||||
<div className="queries-container">
|
||||
{sortedQueries.map((q) => {
|
||||
return <QueryItemContainer key={q.id} query={q} isActive={q.id === filter} />;
|
||||
<div className="shortcuts-container">
|
||||
{sortedShortcuts.map((s) => {
|
||||
return <ShortcutContainer key={s.id} shortcut={s} isActive={s.id === shortcutId} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface QueryItemContainerProps {
|
||||
query: Model.Query;
|
||||
interface ShortcutContainerProps {
|
||||
shortcut: Model.Shortcut;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemContainerProps) => {
|
||||
const { query, isActive } = props;
|
||||
const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutContainerProps) => {
|
||||
const { shortcut, isActive } = props;
|
||||
const [showActionBtns, toggleShowActionBtns] = useToggle(false);
|
||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||
|
||||
const handleQueryClick = () => {
|
||||
if (isActive) {
|
||||
locationService.setMemoFilter("");
|
||||
locationService.setMemoShortcut("");
|
||||
} else {
|
||||
if (!["/", "/recycle"].includes(locationService.getState().pathname)) {
|
||||
locationService.setPathname("/");
|
||||
}
|
||||
locationService.setMemoFilter(query.id);
|
||||
locationService.setMemoShortcut(shortcut.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -93,7 +93,7 @@ const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemC
|
||||
|
||||
if (showConfirmDeleteBtn) {
|
||||
try {
|
||||
await queryService.deleteQuery(query.id);
|
||||
await shortcutService.deleteShortcut(shortcut.id);
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error.message);
|
||||
}
|
||||
@ -104,24 +104,24 @@ const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemC
|
||||
|
||||
const handleEditQueryBtnClick = (event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
showCreateQueryDialog(query.id);
|
||||
showCreateQueryDialog(shortcut.id);
|
||||
};
|
||||
|
||||
const handlePinQueryBtnClick = async (event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
|
||||
try {
|
||||
if (query.pinnedAt) {
|
||||
await queryService.unpinQuery(query.id);
|
||||
queryService.editQuery({
|
||||
...query,
|
||||
pinnedAt: "",
|
||||
if (shortcut.rowStatus === "ARCHIVED") {
|
||||
await shortcutService.unpinShortcut(shortcut.id);
|
||||
shortcutService.editShortcut({
|
||||
...shortcut,
|
||||
rowStatus: "NORMAL",
|
||||
});
|
||||
} else {
|
||||
await queryService.pinQuery(query.id);
|
||||
queryService.editQuery({
|
||||
...query,
|
||||
pinnedAt: utils.getDateTimeString(Date.now()),
|
||||
await shortcutService.pinShortcut(shortcut.id);
|
||||
shortcutService.editShortcut({
|
||||
...shortcut,
|
||||
rowStatus: "NORMAL",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -135,10 +135,10 @@ const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemC
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`query-item-container ${isActive ? "active" : ""}`} onClick={handleQueryClick}>
|
||||
<div className="query-text-container">
|
||||
<div className={`shortcut-container ${isActive ? "active" : ""}`} onClick={handleQueryClick}>
|
||||
<div className="shortcut-text-container">
|
||||
<span className="icon-text">#</span>
|
||||
<span className="query-text">{query.title}</span>
|
||||
<span className="shortcut-text">{shortcut.title}</span>
|
||||
</div>
|
||||
<div className="btns-container">
|
||||
<span className="action-btn toggle-btn" onClick={handleShowActionBtnClick}>
|
||||
@ -147,7 +147,7 @@ const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemC
|
||||
<div className={`action-btns-wrapper ${showActionBtns ? "" : "hidden"}`} onMouseLeave={handleActionBtnContainerMouseLeave}>
|
||||
<div className="action-btns-container">
|
||||
<span className="btn" onClick={handlePinQueryBtnClick}>
|
||||
{query.pinnedAt ? "取消置顶" : "置顶"}
|
||||
{shortcut.rowStatus === "ARCHIVED" ? "取消置顶" : "置顶"}
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditQueryBtnClick}>
|
||||
编辑
|
||||
@ -167,4 +167,4 @@ const QueryItemContainer: React.FC<QueryItemContainerProps> = (props: QueryItemC
|
||||
);
|
||||
};
|
||||
|
||||
export default QueryList;
|
||||
export default ShortcutList;
|
@ -3,7 +3,7 @@ import appContext from "../stores/appContext";
|
||||
import { SHOW_SIDERBAR_MOBILE_CLASSNAME } from "../helpers/consts";
|
||||
import { globalStateService } from "../services";
|
||||
import UserBanner from "./UserBanner";
|
||||
import QueryList from "./QueryList";
|
||||
import ShortcutList from "./ShortcutList";
|
||||
import TagList from "./TagList";
|
||||
import UsageHeatMap from "./UsageHeatMap";
|
||||
import "../less/siderbar.less";
|
||||
@ -66,7 +66,7 @@ const Sidebar: React.FC<Props> = () => {
|
||||
<aside className="sidebar-wrapper" ref={wrapperElRef}>
|
||||
<UserBanner />
|
||||
<UsageHeatMap />
|
||||
<QueryList />
|
||||
<ShortcutList />
|
||||
<TagList />
|
||||
</aside>
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ const UserBanner: React.FC<Props> = () => {
|
||||
memoState: { memos, tags },
|
||||
userState: { user },
|
||||
} = useContext(appContext);
|
||||
const username = user ? user.username : "Memos";
|
||||
const username = user ? user.name : "Memos";
|
||||
const createdDays = user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdAt)) / 1000 / 3600 / 24) : 0;
|
||||
|
||||
const [shouldShowPopupBtns, setShouldShowPopupBtns] = useState(false);
|
||||
|
@ -1,9 +1,7 @@
|
||||
import utils from "./utils";
|
||||
|
||||
type ResponseType<T = unknown> = {
|
||||
succeed: boolean;
|
||||
message: string;
|
||||
type ResponseObject<T> = {
|
||||
data: T;
|
||||
error?: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
type RequestConfig = {
|
||||
@ -13,7 +11,7 @@ type RequestConfig = {
|
||||
dataType?: "json" | "file";
|
||||
};
|
||||
|
||||
async function request<T>(config: RequestConfig): Promise<ResponseType<T>> {
|
||||
async function request<T>(config: RequestConfig): Promise<T> {
|
||||
const { method, url, data, dataType } = config;
|
||||
const requestConfig: RequestInit = {
|
||||
method,
|
||||
@ -31,13 +29,13 @@ async function request<T>(config: RequestConfig): Promise<ResponseType<T>> {
|
||||
}
|
||||
|
||||
const response = await fetch(url, requestConfig);
|
||||
const responseData = (await response.json()) as ResponseType<T>;
|
||||
const responseData = (await response.json()) as ResponseObject<T>;
|
||||
|
||||
if (!responseData.succeed) {
|
||||
throw responseData;
|
||||
if (responseData.error || responseData.message) {
|
||||
throw new Error(responseData.error || responseData.message);
|
||||
}
|
||||
|
||||
return responseData;
|
||||
return responseData.data;
|
||||
}
|
||||
|
||||
namespace api {
|
||||
@ -48,34 +46,42 @@ namespace api {
|
||||
});
|
||||
}
|
||||
|
||||
export function signin(username: string, password: string) {
|
||||
return request({
|
||||
export function login(name: string, password: string) {
|
||||
return request<Model.User>({
|
||||
method: "POST",
|
||||
url: "/api/auth/signin",
|
||||
data: { username, password },
|
||||
url: "/api/auth/login",
|
||||
data: {
|
||||
name,
|
||||
password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function signup(username: string, password: string) {
|
||||
return request({
|
||||
export function signup(name: string, password: string) {
|
||||
return request<Model.User>({
|
||||
method: "POST",
|
||||
url: "/api/auth/signup",
|
||||
data: { username, password },
|
||||
data: {
|
||||
name,
|
||||
password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function signout() {
|
||||
return request({
|
||||
method: "POST",
|
||||
url: "/api/auth/signout",
|
||||
url: "/api/auth/logout",
|
||||
});
|
||||
}
|
||||
|
||||
export function checkUsernameUsable(username: string) {
|
||||
export function checkUsernameUsable(name: string) {
|
||||
return request<boolean>({
|
||||
method: "POST",
|
||||
url: "/api/user/checkusername",
|
||||
data: { username },
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -83,11 +89,13 @@ namespace api {
|
||||
return request<boolean>({
|
||||
method: "POST",
|
||||
url: "/api/user/validpassword",
|
||||
data: { password },
|
||||
data: {
|
||||
password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function updateUserinfo(userinfo: Partial<{ username: string; password: string }>) {
|
||||
export function updateUserinfo(userinfo: Partial<{ name: string; password: string }>) {
|
||||
return request({
|
||||
method: "PATCH",
|
||||
url: "/api/user/me",
|
||||
@ -105,22 +113,24 @@ namespace api {
|
||||
export function getMyMemos() {
|
||||
return request<Model.Memo[]>({
|
||||
method: "GET",
|
||||
url: "/api/memo/all",
|
||||
url: "/api/memo",
|
||||
});
|
||||
}
|
||||
|
||||
export function getMyDeletedMemos() {
|
||||
return request<Model.Memo[]>({
|
||||
method: "GET",
|
||||
url: "/api/memo/all?deleted=true",
|
||||
url: "/api/memo/?hidden=true",
|
||||
});
|
||||
}
|
||||
|
||||
export function createMemo(content: string) {
|
||||
return request<Model.Memo>({
|
||||
method: "PUT",
|
||||
url: "/api/memo/",
|
||||
data: { content },
|
||||
method: "POST",
|
||||
url: "/api/memo",
|
||||
data: {
|
||||
content,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,7 +138,9 @@ namespace api {
|
||||
return request<Model.Memo>({
|
||||
method: "PATCH",
|
||||
url: `/api/memo/${memoId}`,
|
||||
data: { content },
|
||||
data: {
|
||||
content,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -137,7 +149,7 @@ namespace api {
|
||||
method: "PATCH",
|
||||
url: `/api/memo/${memoId}`,
|
||||
data: {
|
||||
deletedAt: utils.getDateTimeString(Date.now()),
|
||||
rowStatus: "HIDDEN",
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -147,7 +159,7 @@ namespace api {
|
||||
method: "PATCH",
|
||||
url: `/api/memo/${memoId}`,
|
||||
data: {
|
||||
deletedAt: "",
|
||||
rowStatus: "NORMAL",
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -159,56 +171,66 @@ namespace api {
|
||||
});
|
||||
}
|
||||
|
||||
export function getMyQueries() {
|
||||
return request<Model.Query[]>({
|
||||
export function getMyShortcuts() {
|
||||
return request<Model.Shortcut[]>({
|
||||
method: "GET",
|
||||
url: "/api/query/all",
|
||||
url: "/api/shortcut",
|
||||
});
|
||||
}
|
||||
|
||||
export function createQuery(title: string, querystring: string) {
|
||||
return request<Model.Query>({
|
||||
method: "PUT",
|
||||
url: "/api/query/",
|
||||
data: { title, querystring },
|
||||
export function createShortcut(title: string, payload: string) {
|
||||
return request<Model.Shortcut>({
|
||||
method: "POST",
|
||||
url: "/api/shortcut",
|
||||
data: {
|
||||
title,
|
||||
payload,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function updateQuery(queryId: string, title: string, querystring: string) {
|
||||
return request<Model.Query>({
|
||||
export function updateShortcut(shortcutId: string, title: string, payload: string) {
|
||||
return request<Model.Shortcut>({
|
||||
method: "PATCH",
|
||||
url: `/api/query/${queryId}`,
|
||||
data: { title, querystring },
|
||||
url: `/api/shortcut/${shortcutId}`,
|
||||
data: {
|
||||
title,
|
||||
payload,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteQueryById(queryId: string) {
|
||||
export function deleteShortcutById(shortcutId: string) {
|
||||
return request({
|
||||
method: "DELETE",
|
||||
url: `/api/query/${queryId}`,
|
||||
url: `/api/shortcut/${shortcutId}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function pinQuery(queryId: string) {
|
||||
export function pinShortcut(shortcutId: string) {
|
||||
return request({
|
||||
method: "PATCH",
|
||||
url: `/api/query/${queryId}`,
|
||||
data: { pinnedAt: utils.getDateTimeString(Date.now()) },
|
||||
url: `/api/shortcut/${shortcutId}`,
|
||||
data: {
|
||||
rowStatus: "ARCHIVED",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function unpinQuery(queryId: string) {
|
||||
export function unpinShortcut(shortcutId: string) {
|
||||
return request({
|
||||
method: "PATCH",
|
||||
url: `/api/query/${queryId}`,
|
||||
data: { pinnedAt: "" },
|
||||
url: `/api/shortcut/${shortcutId}`,
|
||||
data: {
|
||||
rowStatus: "NORMAL",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function uploadFile(formData: FormData) {
|
||||
return request<Model.Resource>({
|
||||
method: "PUT",
|
||||
url: "/api/resource/",
|
||||
method: "POST",
|
||||
url: "/api/resource",
|
||||
data: formData,
|
||||
dataType: "file",
|
||||
});
|
||||
|
@ -43,6 +43,10 @@ namespace utils {
|
||||
return `${year}/${month}/${date}`;
|
||||
}
|
||||
|
||||
export function getDataStringWithTs(ts: number): string {
|
||||
return getDateTimeString(ts * 1000);
|
||||
}
|
||||
|
||||
export function getTimeString(t: Date | number | string): string {
|
||||
const d = new Date(getTimeStampByDate(t));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
.create-query-dialog {
|
||||
.create-shortcut-dialog {
|
||||
> .dialog-container {
|
||||
width: 420px;
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 875px) {
|
||||
.dialog-wrapper.create-query-dialog {
|
||||
.dialog-wrapper.create-shortcut-dialog {
|
||||
padding: 24px 16px;
|
||||
padding-top: 64px;
|
||||
justify-content: unset;
|
@ -1,6 +1,6 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
.queries-wrapper {
|
||||
.shortcuts-wrapper {
|
||||
.flex(column, flex-start, flex-start);
|
||||
width: 100%;
|
||||
padding: 0 8px;
|
||||
@ -35,7 +35,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
> .create-query-btn-container {
|
||||
> .create-shortcut-btn-container {
|
||||
.flex(row, center, center);
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
@ -55,7 +55,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
> .queries-container {
|
||||
> .shortcuts-container {
|
||||
.flex(column, flex-start, flex-start);
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@ -63,7 +63,7 @@
|
||||
flex-wrap: nowrap;
|
||||
margin-bottom: 8px;
|
||||
|
||||
> .query-item-container {
|
||||
> .shortcut-container {
|
||||
.flex(row, space-between, center);
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
@ -86,7 +86,7 @@
|
||||
&.active {
|
||||
background-color: @text-green !important;
|
||||
|
||||
> .query-text-container {
|
||||
> .shortcut-text-container {
|
||||
font-weight: bold;
|
||||
|
||||
> * {
|
||||
@ -95,7 +95,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
> .query-text-container {
|
||||
> .shortcut-text-container {
|
||||
.flex(row, flex-start, center);
|
||||
max-width: calc(100% - 24px);
|
||||
color: @text-black;
|
||||
@ -110,7 +110,7 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
> .query-text {
|
||||
> .shortcut-text {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 875px) {
|
||||
.queries-container {
|
||||
.shortcuts-container {
|
||||
height: auto;
|
||||
|
||||
&:last-child {
|
||||
@ -192,7 +192,7 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
> .query-item-container {
|
||||
> .shortcut-container {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import appContext from "../stores/appContext";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import { globalStateService, locationService, memoService, queryService } from "../services";
|
||||
import { globalStateService, locationService, memoService, shortcutService } from "../services";
|
||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts";
|
||||
import utils from "../helpers/utils";
|
||||
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
||||
@ -21,8 +21,8 @@ const MemoTrash: React.FC<Props> = () => {
|
||||
const loadingState = useLoading();
|
||||
const [deletedMemos, setDeletedMemos] = useState<Model.Memo[]>([]);
|
||||
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, filter: queryId } = query;
|
||||
const queryFilter = queryService.getQueryById(queryId);
|
||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId } = query;
|
||||
const queryFilter = shortcutService.getShortcutById(shortcutId);
|
||||
const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || queryFilter);
|
||||
|
||||
const shownMemos =
|
||||
@ -31,7 +31,7 @@ const MemoTrash: React.FC<Props> = () => {
|
||||
let shouldShow = true;
|
||||
|
||||
if (queryFilter) {
|
||||
const filters = JSON.parse(queryFilter.querystring) as Filter[];
|
||||
const filters = JSON.parse(queryFilter.payload) as Filter[];
|
||||
if (Array.isArray(filters)) {
|
||||
shouldShow = checkShouldShowMemoWithFilters(memo, filters);
|
||||
}
|
||||
|
@ -67,16 +67,11 @@ const Signin: React.FC<Props> = () => {
|
||||
|
||||
try {
|
||||
signinBtnsClickLoadingState.setLoading();
|
||||
let actionFunc = api.signin;
|
||||
let actionFunc = api.login;
|
||||
if (action === "signup") {
|
||||
actionFunc = api.signup;
|
||||
}
|
||||
const { succeed, message } = await actionFunc(username, password);
|
||||
|
||||
if (!succeed && message) {
|
||||
toastHelper.error("😟 " + message);
|
||||
return;
|
||||
}
|
||||
await actionFunc(username, password);
|
||||
|
||||
const user = await userService.doSignIn();
|
||||
if (user) {
|
||||
@ -106,12 +101,7 @@ const Signin: React.FC<Props> = () => {
|
||||
|
||||
try {
|
||||
signinBtnsClickLoadingState.setLoading();
|
||||
const { succeed, message } = await api.signin("guest", "123456");
|
||||
|
||||
if (!succeed && message) {
|
||||
toastHelper.error("😟 " + message);
|
||||
return;
|
||||
}
|
||||
await api.login("guest", "123456");
|
||||
|
||||
const user = await userService.doSignIn();
|
||||
if (user) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import globalStateService from "./globalStateService";
|
||||
import locationService from "./locationService";
|
||||
import memoService from "./memoService";
|
||||
import queryService from "./queryService";
|
||||
import shortcutService from "./shortcutService";
|
||||
import userService from "./userService";
|
||||
import resourceService from "./resourceService";
|
||||
|
||||
export { globalStateService, locationService, memoService, queryService, userService, resourceService };
|
||||
export { globalStateService, locationService, memoService, shortcutService, userService, resourceService };
|
||||
|
@ -36,13 +36,13 @@ class LocationService {
|
||||
duration: null,
|
||||
text: "",
|
||||
type: "",
|
||||
filter: "",
|
||||
shortcutId: "",
|
||||
},
|
||||
};
|
||||
state.query.tag = urlParams.get("tag") ?? "";
|
||||
state.query.type = (urlParams.get("type") ?? "") as MemoSpecType;
|
||||
state.query.text = urlParams.get("text") ?? "";
|
||||
state.query.filter = urlParams.get("filter") ?? "";
|
||||
state.query.shortcutId = urlParams.get("filter") ?? "";
|
||||
const from = parseInt(urlParams.get("from") ?? "0");
|
||||
const to = parseInt(urlParams.get("to") ?? "0");
|
||||
if (to > from && to !== 0) {
|
||||
@ -71,7 +71,7 @@ class LocationService {
|
||||
duration: null,
|
||||
text: "",
|
||||
type: "",
|
||||
filter: "",
|
||||
shortcutId: "",
|
||||
},
|
||||
});
|
||||
|
||||
@ -142,10 +142,10 @@ class LocationService {
|
||||
updateLocationUrl();
|
||||
};
|
||||
|
||||
public setMemoFilter = (filterId: string) => {
|
||||
public setMemoShortcut = (shortcutId: string) => {
|
||||
appStore.dispatch({
|
||||
type: "SET_QUERY_FILTER",
|
||||
payload: filterId,
|
||||
type: "SET_SHORTCUT_ID",
|
||||
payload: shortcutId,
|
||||
});
|
||||
|
||||
updateLocationUrl();
|
||||
|
@ -16,11 +16,10 @@ class MemoService {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { data } = await api.getMyMemos();
|
||||
const memos = [];
|
||||
for (const m of data) {
|
||||
memos.push(m);
|
||||
}
|
||||
const data = await api.getMyMemos();
|
||||
const memos: Model.Memo[] = data.map((m) => {
|
||||
return this.convertResponseModelMemo(m);
|
||||
});
|
||||
appStore.dispatch({
|
||||
type: "SET_MEMOS",
|
||||
payload: {
|
||||
@ -40,9 +39,11 @@ class MemoService {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { data } = await api.getMyDeletedMemos();
|
||||
data.sort((a, b) => utils.getTimeStampByDate(b.deletedAt) - utils.getTimeStampByDate(a.deletedAt));
|
||||
return data;
|
||||
const data = await api.getMyDeletedMemos();
|
||||
const deletedMemos: Model.Memo[] = data.map((m) => {
|
||||
return this.convertResponseModelMemo(m);
|
||||
});
|
||||
return deletedMemos;
|
||||
}
|
||||
|
||||
public pushMemo(memo: Model.Memo) {
|
||||
@ -125,13 +126,21 @@ class MemoService {
|
||||
}
|
||||
|
||||
public async createMemo(text: string): Promise<Model.Memo> {
|
||||
const { data: memo } = await api.createMemo(text);
|
||||
return memo;
|
||||
const memo = await api.createMemo(text);
|
||||
return this.convertResponseModelMemo(memo);
|
||||
}
|
||||
|
||||
public async updateMemo(memoId: string, text: string): Promise<Model.Memo> {
|
||||
const { data: memo } = await api.updateMemo(memoId, text);
|
||||
return memo;
|
||||
const memo = await api.updateMemo(memoId, text);
|
||||
return this.convertResponseModelMemo(memo);
|
||||
}
|
||||
|
||||
private convertResponseModelMemo(memo: Model.Memo): Model.Memo {
|
||||
return {
|
||||
...memo,
|
||||
createdAt: utils.getDataStringWithTs(memo.createdTs),
|
||||
updatedAt: utils.getDataStringWithTs(memo.updatedTs),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
import userService from "./userService";
|
||||
import api from "../helpers/api";
|
||||
import appStore from "../stores/appStore";
|
||||
|
||||
class QueryService {
|
||||
public getState() {
|
||||
return appStore.getState().queryState;
|
||||
}
|
||||
|
||||
public async getMyAllQueries() {
|
||||
if (!userService.getState().user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { data } = await api.getMyQueries();
|
||||
appStore.dispatch({
|
||||
type: "SET_QUERIES",
|
||||
payload: {
|
||||
queries: data,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
public getQueryById(id: string) {
|
||||
for (const q of this.getState().queries) {
|
||||
if (q.id === id) {
|
||||
return q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public pushQuery(query: Model.Query) {
|
||||
appStore.dispatch({
|
||||
type: "INSERT_QUERY",
|
||||
payload: {
|
||||
query: {
|
||||
...query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public editQuery(query: Model.Query) {
|
||||
appStore.dispatch({
|
||||
type: "UPDATE_QUERY",
|
||||
payload: query,
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteQuery(queryId: string) {
|
||||
await api.deleteQueryById(queryId);
|
||||
appStore.dispatch({
|
||||
type: "DELETE_QUERY_BY_ID",
|
||||
payload: {
|
||||
id: queryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async createQuery(title: string, querystring: string) {
|
||||
const { data } = await api.createQuery(title, querystring);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async updateQuery(queryId: string, title: string, querystring: string) {
|
||||
const { data } = await api.updateQuery(queryId, title, querystring);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async pinQuery(queryId: string) {
|
||||
await api.pinQuery(queryId);
|
||||
}
|
||||
|
||||
public async unpinQuery(queryId: string) {
|
||||
await api.unpinQuery(queryId);
|
||||
}
|
||||
}
|
||||
|
||||
const queryService = new QueryService();
|
||||
|
||||
export default queryService;
|
@ -17,7 +17,7 @@ class ResourceService {
|
||||
|
||||
formData.append("file", file, filename);
|
||||
|
||||
const { data } = await api.uploadFile(formData);
|
||||
const data = await api.uploadFile(formData);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
93
web/src/services/shortcutService.ts
Normal file
93
web/src/services/shortcutService.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import userService from "./userService";
|
||||
import api from "../helpers/api";
|
||||
import appStore from "../stores/appStore";
|
||||
import utils from "../helpers/utils";
|
||||
|
||||
class ShortcutService {
|
||||
public getState() {
|
||||
return appStore.getState().shortcutState;
|
||||
}
|
||||
|
||||
public async getMyAllShortcuts() {
|
||||
if (!userService.getState().user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await api.getMyShortcuts();
|
||||
appStore.dispatch({
|
||||
type: "SET_SHORTCUTS",
|
||||
payload: {
|
||||
shortcuts: data.map((s) => this.convertResponseModelShortcut(s)),
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
public getShortcutById(id: string) {
|
||||
for (const q of this.getState().shortcuts) {
|
||||
if (q.id === id) {
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public pushShortcut(shortcut: Model.Shortcut) {
|
||||
appStore.dispatch({
|
||||
type: "INSERT_SHORTCUT",
|
||||
payload: {
|
||||
shortcut: {
|
||||
...shortcut,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public editShortcut(shortcut: Model.Shortcut) {
|
||||
appStore.dispatch({
|
||||
type: "UPDATE_SHORTCUT",
|
||||
payload: shortcut,
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteShortcut(shortcutId: string) {
|
||||
await api.deleteShortcutById(shortcutId);
|
||||
appStore.dispatch({
|
||||
type: "DELETE_SHORTCUT_BY_ID",
|
||||
payload: {
|
||||
id: shortcutId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async createShortcut(title: string, shortcutstring: string) {
|
||||
const data = await api.createShortcut(title, shortcutstring);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async updateShortcut(shortcutId: string, title: string, shortcutstring: string) {
|
||||
const data = await api.updateShortcut(shortcutId, title, shortcutstring);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async pinShortcut(shortcutId: string) {
|
||||
await api.pinShortcut(shortcutId);
|
||||
}
|
||||
|
||||
public async unpinShortcut(shortcutId: string) {
|
||||
await api.unpinShortcut(shortcutId);
|
||||
}
|
||||
|
||||
public convertResponseModelShortcut(shortcut: Model.Shortcut): Model.Shortcut {
|
||||
return {
|
||||
...shortcut,
|
||||
createdAt: utils.getDataStringWithTs(shortcut.createdTs),
|
||||
updatedAt: utils.getDataStringWithTs(shortcut.updatedTs),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const shortcutService = new ShortcutService();
|
||||
|
||||
export default shortcutService;
|
@ -1,4 +1,5 @@
|
||||
import api from "../helpers/api";
|
||||
import utils from "../helpers/utils";
|
||||
import appStore from "../stores/appStore";
|
||||
|
||||
class UserService {
|
||||
@ -7,11 +8,13 @@ class UserService {
|
||||
}
|
||||
|
||||
public async doSignIn() {
|
||||
const { data: user } = await api.getUserInfo();
|
||||
const user = await api.getUserInfo();
|
||||
if (user) {
|
||||
appStore.dispatch({
|
||||
type: "SIGN_IN",
|
||||
payload: { user },
|
||||
type: "LOGIN",
|
||||
payload: {
|
||||
user: this.convertResponseModelUser(user),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
userService.doSignOut();
|
||||
@ -30,18 +33,18 @@ class UserService {
|
||||
}
|
||||
|
||||
public async checkUsernameUsable(username: string): Promise<boolean> {
|
||||
const { data: isUsable } = await api.checkUsernameUsable(username);
|
||||
const isUsable = await api.checkUsernameUsable(username);
|
||||
return isUsable;
|
||||
}
|
||||
|
||||
public async updateUsername(username: string): Promise<void> {
|
||||
public async updateUsername(name: string): Promise<void> {
|
||||
await api.updateUserinfo({
|
||||
username,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
public async checkPasswordValid(password: string): Promise<boolean> {
|
||||
const { data: isValid } = await api.checkPasswordValid(password);
|
||||
const isValid = await api.checkPasswordValid(password);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@ -52,13 +55,21 @@ class UserService {
|
||||
}
|
||||
|
||||
public async resetOpenId(): Promise<string> {
|
||||
const { data: openId } = await api.resetOpenId();
|
||||
const openId = await api.resetOpenId();
|
||||
appStore.dispatch({
|
||||
type: "RESET_OPENID",
|
||||
payload: openId,
|
||||
});
|
||||
return openId;
|
||||
}
|
||||
|
||||
private convertResponseModelUser(user: Model.User): Model.User {
|
||||
return {
|
||||
...user,
|
||||
createdAt: utils.getDataStringWithTs(user.createdTs),
|
||||
updatedAt: utils.getDataStringWithTs(user.updatedTs),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const userService = new UserService();
|
||||
|
@ -4,17 +4,17 @@ import * as globalStore from "./globalStateStore";
|
||||
import * as locationStore from "./locationStore";
|
||||
import * as memoStore from "./memoStore";
|
||||
import * as userStore from "./userStore";
|
||||
import * as queryStore from "./queryStore";
|
||||
import * as shortcutStore from "./shortcutStore";
|
||||
|
||||
interface AppState {
|
||||
globalState: globalStore.State;
|
||||
locationState: locationStore.State;
|
||||
memoState: memoStore.State;
|
||||
userState: userStore.State;
|
||||
queryState: queryStore.State;
|
||||
shortcutState: shortcutStore.State;
|
||||
}
|
||||
|
||||
type AppStateActions = globalStore.Actions | locationStore.Actions | memoStore.Actions | userStore.Actions | queryStore.Actions;
|
||||
type AppStateActions = globalStore.Actions | locationStore.Actions | memoStore.Actions | userStore.Actions | shortcutStore.Actions;
|
||||
|
||||
const appStore = createStore<AppState, AppStateActions>(
|
||||
{
|
||||
@ -22,14 +22,14 @@ const appStore = createStore<AppState, AppStateActions>(
|
||||
locationState: locationStore.defaultState,
|
||||
memoState: memoStore.defaultState,
|
||||
userState: userStore.defaultState,
|
||||
queryState: queryStore.defaultState,
|
||||
shortcutState: shortcutStore.defaultState,
|
||||
},
|
||||
combineReducers<AppState, AppStateActions>({
|
||||
globalState: globalStore.reducer,
|
||||
locationState: locationStore.reducer,
|
||||
memoState: memoStore.reducer,
|
||||
userState: userStore.reducer,
|
||||
queryState: queryStore.reducer,
|
||||
shortcutState: shortcutStore.reducer,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -6,10 +6,10 @@ export interface AppSetting {
|
||||
}
|
||||
|
||||
export interface State extends AppSetting {
|
||||
markMemoId: string;
|
||||
editMemoId: string;
|
||||
isMobileView: boolean;
|
||||
showSiderbarInMobileView: boolean;
|
||||
markMemoId: string;
|
||||
editMemoId: string;
|
||||
}
|
||||
|
||||
interface SetMarkMemoIdAction {
|
||||
|
@ -1,6 +1,6 @@
|
||||
export type State = AppLocation;
|
||||
|
||||
interface SetLocation {
|
||||
interface SetLocationAction {
|
||||
type: "SET_LOCATION";
|
||||
payload: State;
|
||||
}
|
||||
@ -12,13 +12,13 @@ interface SetPathnameAction {
|
||||
};
|
||||
}
|
||||
|
||||
interface SetQuery {
|
||||
interface SetQueryAction {
|
||||
type: "SET_QUERY";
|
||||
payload: Query;
|
||||
}
|
||||
|
||||
interface SetQueryFilterAction {
|
||||
type: "SET_QUERY_FILTER";
|
||||
interface SetShortcutIdAction {
|
||||
type: "SET_SHORTCUT_ID";
|
||||
payload: string;
|
||||
}
|
||||
|
||||
@ -58,14 +58,14 @@ interface SetHashAction {
|
||||
}
|
||||
|
||||
export type Actions =
|
||||
| SetLocation
|
||||
| SetLocationAction
|
||||
| SetPathnameAction
|
||||
| SetQuery
|
||||
| SetQueryAction
|
||||
| SetTagQueryAction
|
||||
| SetFromAndToQueryAction
|
||||
| SetTypeAction
|
||||
| SetTextAction
|
||||
| SetQueryFilterAction
|
||||
| SetShortcutIdAction
|
||||
| SetHashAction;
|
||||
|
||||
export function reducer(state: State, action: Actions) {
|
||||
@ -156,8 +156,8 @@ export function reducer(state: State, action: Actions) {
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_QUERY_FILTER": {
|
||||
if (action.payload === state.query.filter) {
|
||||
case "SET_SHORTCUT_ID": {
|
||||
if (action.payload === state.query.shortcutId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -183,6 +183,6 @@ export const defaultState: State = {
|
||||
duration: null,
|
||||
type: "",
|
||||
text: "",
|
||||
filter: "",
|
||||
shortcutId: "",
|
||||
},
|
||||
};
|
||||
|
@ -1,92 +0,0 @@
|
||||
import utils from "../helpers/utils";
|
||||
|
||||
export interface State {
|
||||
queries: Model.Query[];
|
||||
}
|
||||
|
||||
interface SetQueries {
|
||||
type: "SET_QUERIES";
|
||||
payload: {
|
||||
queries: Model.Query[];
|
||||
};
|
||||
}
|
||||
|
||||
interface InsertQueryAction {
|
||||
type: "INSERT_QUERY";
|
||||
payload: {
|
||||
query: Model.Query;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteQueryByIdAction {
|
||||
type: "DELETE_QUERY_BY_ID";
|
||||
payload: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateQueryAction {
|
||||
type: "UPDATE_QUERY";
|
||||
payload: Model.Query;
|
||||
}
|
||||
|
||||
export type Actions = SetQueries | InsertQueryAction | DeleteQueryByIdAction | UpdateQueryAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "SET_QUERIES": {
|
||||
const queries = utils.dedupeObjectWithId(
|
||||
action.payload.queries
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.pinnedAt ?? 0) - utils.getTimeStampByDate(a.pinnedAt ?? 0))
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
queries,
|
||||
};
|
||||
}
|
||||
case "INSERT_QUERY": {
|
||||
const queries = utils.dedupeObjectWithId(
|
||||
[action.payload.query, ...state.queries].sort(
|
||||
(a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt)
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
queries,
|
||||
};
|
||||
}
|
||||
case "DELETE_QUERY_BY_ID": {
|
||||
return {
|
||||
...state,
|
||||
queries: [...state.queries].filter((query) => query.id !== action.payload.id),
|
||||
};
|
||||
}
|
||||
case "UPDATE_QUERY": {
|
||||
const queries = state.queries.map((m) => {
|
||||
if (m.id === action.payload.id) {
|
||||
return {
|
||||
...m,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
queries,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
queries: [],
|
||||
};
|
92
web/src/stores/shortcutStore.ts
Normal file
92
web/src/stores/shortcutStore.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import utils from "../helpers/utils";
|
||||
|
||||
export interface State {
|
||||
shortcuts: Model.Shortcut[];
|
||||
}
|
||||
|
||||
interface SetShortcutsAction {
|
||||
type: "SET_SHORTCUTS";
|
||||
payload: {
|
||||
shortcuts: Model.Shortcut[];
|
||||
};
|
||||
}
|
||||
|
||||
interface InsertShortcutAction {
|
||||
type: "INSERT_SHORTCUT";
|
||||
payload: {
|
||||
shortcut: Model.Shortcut;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteShortcutByIdAction {
|
||||
type: "DELETE_SHORTCUT_BY_ID";
|
||||
payload: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateShortcutAction {
|
||||
type: "UPDATE_SHORTCUT";
|
||||
payload: Model.Shortcut;
|
||||
}
|
||||
|
||||
export type Actions = SetShortcutsAction | InsertShortcutAction | DeleteShortcutByIdAction | UpdateShortcutAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "SET_SHORTCUTS": {
|
||||
const shortcuts = utils.dedupeObjectWithId(
|
||||
action.payload.shortcuts
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.updatedAt) - utils.getTimeStampByDate(a.updatedAt))
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
case "INSERT_SHORTCUT": {
|
||||
const shortcuts = utils.dedupeObjectWithId(
|
||||
[action.payload.shortcut, ...state.shortcuts].sort(
|
||||
(a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt)
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
case "DELETE_SHORTCUT_BY_ID": {
|
||||
return {
|
||||
...state,
|
||||
shortcuts: [...state.shortcuts].filter((shortcut) => shortcut.id !== action.payload.id),
|
||||
};
|
||||
}
|
||||
case "UPDATE_SHORTCUT": {
|
||||
const shortcuts = state.shortcuts.map((m) => {
|
||||
if (m.id === action.payload.id) {
|
||||
return {
|
||||
...m,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
shortcuts: [],
|
||||
};
|
@ -3,7 +3,7 @@ export interface State {
|
||||
}
|
||||
|
||||
interface SignInAction {
|
||||
type: "SIGN_IN";
|
||||
type: "LOGIN";
|
||||
payload: State;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export type Actions = SignInAction | SignOutAction | ResetOpenIdAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "SIGN_IN": {
|
||||
case "LOGIN": {
|
||||
return {
|
||||
user: action.payload.user,
|
||||
};
|
||||
|
7
web/src/types/api.d.ts
vendored
7
web/src/types/api.d.ts
vendored
@ -1,6 +1 @@
|
||||
declare namespace Api {
|
||||
interface MemosStat {
|
||||
timestamp: string;
|
||||
amount: number;
|
||||
}
|
||||
}
|
||||
declare namespace Api {}
|
||||
|
2
web/src/types/location.d.ts
vendored
2
web/src/types/location.d.ts
vendored
@ -8,7 +8,7 @@ interface Query {
|
||||
duration: Duration | null;
|
||||
type: MemoSpecType | "";
|
||||
text: string;
|
||||
filter: string;
|
||||
shortcutId: string;
|
||||
}
|
||||
|
||||
type AppRouter = "/" | "/signin" | "/recycle" | "/setting";
|
||||
|
16
web/src/types/models.d.ts
vendored
16
web/src/types/models.d.ts
vendored
@ -1,28 +1,30 @@
|
||||
declare namespace Model {
|
||||
interface BaseModel {
|
||||
id: string;
|
||||
createdTs: number;
|
||||
updatedTs: number;
|
||||
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface User extends BaseModel {
|
||||
username: string;
|
||||
name: string;
|
||||
openId: string;
|
||||
}
|
||||
|
||||
interface Memo extends BaseModel {
|
||||
content: string;
|
||||
deletedAt: string;
|
||||
rowStatus: "NORMAL" | "HIDDEN";
|
||||
}
|
||||
|
||||
interface Query extends BaseModel {
|
||||
interface Shortcut extends BaseModel {
|
||||
title: string;
|
||||
querystring: string;
|
||||
pinnedAt: string;
|
||||
payload: string;
|
||||
rowStatus: "NORMAL" | "ARCHIVED";
|
||||
}
|
||||
|
||||
interface Resource {
|
||||
id: string;
|
||||
interface Resource extends BaseModel {
|
||||
filename: string;
|
||||
type: string;
|
||||
size: string;
|
||||
|
@ -8,11 +8,11 @@ export default defineConfig({
|
||||
cors: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
// target: "http://localhost:8080/",
|
||||
target: "https://memos.justsven.top/",
|
||||
target: "http://localhost:8080/",
|
||||
// target: "https://memos.justsven.top/",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/r/": {
|
||||
"/h/": {
|
||||
target: "https://memos.justsven.top/",
|
||||
changeOrigin: true,
|
||||
},
|
||||
|
Reference in New Issue
Block a user