mirror of
https://github.com/usememos/memos.git
synced 2025-01-02 19:23:30 +03:00
chore: update store types
This commit is contained in:
parent
bc22f69ac5
commit
6fe1db42b5
@ -38,7 +38,7 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<p>
|
<p>
|
||||||
Memos is an open source, quickly self-hosted alternative to <a href="https://flomoapp.com">flomo</a>.
|
Memos is an <i>open source</i>, <i>self-hosted</i> knowledge base that works with local SQLite.
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
|
@ -44,7 +44,7 @@ const ConfirmResetOpenIdDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container">
|
<div className="dialog-content-container">
|
||||||
<p className="warn-text">
|
<p className="warn-text">
|
||||||
⚠️ The existing API will be invalidated and a new one will be generated, are you sure you want to reset?
|
❗️The existing API will be invalidated and a new one will be generated, are you sure you want to reset?
|
||||||
</p>
|
</p>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||||
|
@ -4,20 +4,21 @@ import { formatMemoContent } from "./Memo";
|
|||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import "../less/daily-memo.less";
|
import "../less/daily-memo.less";
|
||||||
|
|
||||||
interface DailyMemo extends FormattedMemo {
|
interface DailyMemo extends Memo {
|
||||||
|
createdAtStr: string;
|
||||||
timeStr: string;
|
timeStr: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DailyMemo: React.FC<Props> = (props: Props) => {
|
const DailyMemo: React.FC<Props> = (props: Props) => {
|
||||||
const { memo: propsMemo } = props;
|
const { memo: propsMemo } = props;
|
||||||
const memo: DailyMemo = {
|
const memo: DailyMemo = {
|
||||||
...propsMemo,
|
...propsMemo,
|
||||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
|
||||||
timeStr: utils.getTimeString(propsMemo.createdAt),
|
timeStr: utils.getTimeString(propsMemo.createdTs),
|
||||||
};
|
};
|
||||||
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ const weekdayChineseStrArray = ["周日", "周一", "周二", "周三", "周四"
|
|||||||
|
|
||||||
const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const [memos, setMemos] = useState<Model.Memo[]>([]);
|
const [memos, setMemos] = useState<Memo[]>([]);
|
||||||
const [currentDateStamp, setCurrentDateStamp] = useState(utils.getDateStampByDate(utils.getDateString(props.currentDateStamp)));
|
const [currentDateStamp, setCurrentDateStamp] = useState(utils.getDateStampByDate(utils.getDateString(props.currentDateStamp)));
|
||||||
const [showDatePicker, toggleShowDatePicker] = useToggle(false);
|
const [showDatePicker, toggleShowDatePicker] = useToggle(false);
|
||||||
const memosElRef = useRef<HTMLDivElement>(null);
|
const memosElRef = useRef<HTMLDivElement>(null);
|
||||||
@ -32,10 +32,10 @@ const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
|||||||
.getState()
|
.getState()
|
||||||
.memos.filter(
|
.memos.filter(
|
||||||
(a) =>
|
(a) =>
|
||||||
utils.getTimeStampByDate(a.createdAt) >= currentDateStamp &&
|
utils.getTimeStampByDate(a.createdTs) >= currentDateStamp &&
|
||||||
utils.getTimeStampByDate(a.createdAt) < currentDateStamp + DAILY_TIMESTAMP
|
utils.getTimeStampByDate(a.createdTs) < currentDateStamp + DAILY_TIMESTAMP
|
||||||
)
|
)
|
||||||
.sort((a, b) => utils.getTimeStampByDate(a.createdAt) - utils.getTimeStampByDate(b.createdAt));
|
.sort((a, b) => utils.getTimeStampByDate(a.createdTs) - utils.getTimeStampByDate(b.createdTs));
|
||||||
setMemos(dailyMemos);
|
setMemos(dailyMemos);
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
};
|
};
|
||||||
@ -115,7 +115,7 @@ const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
|||||||
) : (
|
) : (
|
||||||
<div className="dailymemos-wrapper">
|
<div className="dailymemos-wrapper">
|
||||||
{memos.map((memo) => (
|
{memos.map((memo) => (
|
||||||
<DailyMemo key={`${memo.id}-${memo.updatedAt}`} memo={memo} />
|
<DailyMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -9,16 +9,16 @@ import { formatMemoContent } from "./Memo";
|
|||||||
import "../less/memo.less";
|
import "../less/memo.less";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
handleDeletedMemoAction: (memoId: string) => void;
|
handleDeletedMemoAction: (memoId: MemoId) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DeletedMemo: React.FC<Props> = (props: Props) => {
|
const DeletedMemo: React.FC<Props> = (props: Props) => {
|
||||||
const { memo: propsMemo, handleDeletedMemoAction } = props;
|
const { memo: propsMemo, handleDeletedMemoAction } = props;
|
||||||
const memo: FormattedMemo = {
|
const memo = {
|
||||||
...propsMemo,
|
...propsMemo,
|
||||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
|
||||||
deletedAtStr: utils.getDateTimeString(propsMemo.updatedAt ?? Date.now()),
|
deletedAtStr: utils.getDateTimeString(propsMemo.updatedTs ?? Date.now()),
|
||||||
};
|
};
|
||||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||||
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { escape } from "lodash-es";
|
import { escape } from "lodash-es";
|
||||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts";
|
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||||
import { parseMarkedToHtml, parseRawTextToHtml } from "../helpers/marked";
|
import { parseMarkedToHtml, parseRawTextToHtml } from "../helpers/marked";
|
||||||
import utils from "../helpers/utils";
|
import utils from "../helpers/utils";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
@ -13,14 +13,14 @@ import toastHelper from "./Toast";
|
|||||||
import "../less/memo.less";
|
import "../less/memo.less";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Memo: React.FC<Props> = (props: Props) => {
|
const Memo: React.FC<Props> = (props: Props) => {
|
||||||
const { memo: propsMemo } = props;
|
const { memo: propsMemo } = props;
|
||||||
const memo: FormattedMemo = {
|
const memo = {
|
||||||
...propsMemo,
|
...propsMemo,
|
||||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
|
||||||
};
|
};
|
||||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||||
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||||
@ -31,17 +31,17 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
const handleTogglePinMemoBtnClick = async () => {
|
const handleTogglePinMemoBtnClick = async () => {
|
||||||
try {
|
try {
|
||||||
if (memo.rowStatus === "ARCHIVED") {
|
if (memo.pinned) {
|
||||||
await memoService.unpinMemo(memo.id);
|
await memoService.unpinMemo(memo.id);
|
||||||
memoService.editMemo({
|
memoService.editMemo({
|
||||||
...memo,
|
...memo,
|
||||||
rowStatus: "NORMAL",
|
pinned: false,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await memoService.pinMemo(memo.id);
|
await memoService.pinMemo(memo.id);
|
||||||
memoService.editMemo({
|
memoService.editMemo({
|
||||||
...memo,
|
...memo,
|
||||||
rowStatus: "ARCHIVED",
|
pinned: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -66,7 +66,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (globalStateService.getState().editMemoId === memo.id) {
|
if (globalStateService.getState().editMemoId === memo.id) {
|
||||||
globalStateService.setEditMemoId("");
|
globalStateService.setEditMemoId(UNKNOWN_ID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toggleConfirmDeleteBtn();
|
toggleConfirmDeleteBtn();
|
||||||
@ -88,7 +88,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
if (targetEl.className === "memo-link-text") {
|
if (targetEl.className === "memo-link-text") {
|
||||||
const memoId = targetEl.dataset?.value;
|
const memoId = targetEl.dataset?.value;
|
||||||
const memoTemp = memoService.getMemoById(memoId ?? "");
|
const memoTemp = memoService.getMemoById(Number(memoId) ?? UNKNOWN_ID);
|
||||||
|
|
||||||
if (memoTemp) {
|
if (memoTemp) {
|
||||||
showMemoCardDialog(memoTemp);
|
showMemoCardDialog(memoTemp);
|
||||||
@ -102,11 +102,11 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.rowStatus}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
||||||
<div className="memo-top-wrapper">
|
<div className="memo-top-wrapper">
|
||||||
<span className="time-text" onClick={handleShowMemoStoryDialog}>
|
<span className="time-text" onClick={handleShowMemoStoryDialog}>
|
||||||
{memo.createdAtStr}
|
{memo.createdAtStr}
|
||||||
<Only when={memo.rowStatus === "ARCHIVED"}>
|
<Only when={memo.pinned}>
|
||||||
<span className="ml-2">PINNED</span>
|
<span className="ml-2">PINNED</span>
|
||||||
</Only>
|
</Only>
|
||||||
</span>
|
</span>
|
||||||
@ -120,7 +120,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
View Story
|
View Story
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleTogglePinMemoBtnClick}>
|
<span className="btn" onClick={handleTogglePinMemoBtnClick}>
|
||||||
{memo.rowStatus === "NORMAL" ? "Pin" : "Unpin"}
|
{memo.pinned ? "Unpin" : "Pin"}
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleMarkMemoClick}>
|
<span className="btn" onClick={handleMarkMemoClick}>
|
||||||
Mark
|
Mark
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { IMAGE_URL_REG, MEMO_LINK_REG } from "../helpers/consts";
|
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||||
import utils from "../helpers/utils";
|
import utils from "../helpers/utils";
|
||||||
import { globalStateService, memoService } from "../services";
|
import { globalStateService, memoService } from "../services";
|
||||||
import { parseHtmlToRawText } from "../helpers/marked";
|
import { parseHtmlToRawText } from "../helpers/marked";
|
||||||
@ -11,18 +11,18 @@ import Image from "./Image";
|
|||||||
import "../less/memo-card-dialog.less";
|
import "../less/memo-card-dialog.less";
|
||||||
import "../less/memo-content.less";
|
import "../less/memo-content.less";
|
||||||
|
|
||||||
interface LinkedMemo extends FormattedMemo {
|
interface LinkedMemo extends Memo {
|
||||||
|
createdAtStr: string;
|
||||||
dateStr: string;
|
dateStr: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||||
const [memo, setMemo] = useState<FormattedMemo>({
|
const [memo, setMemo] = useState<Memo>({
|
||||||
...props.memo,
|
...props.memo,
|
||||||
createdAtStr: utils.getDateTimeString(props.memo.createdAt),
|
|
||||||
});
|
});
|
||||||
const [linkMemos, setLinkMemos] = useState<LinkedMemo[]>([]);
|
const [linkMemos, setLinkMemos] = useState<LinkedMemo[]>([]);
|
||||||
const [linkedMemos, setLinkedMemos] = useState<LinkedMemo[]>([]);
|
const [linkedMemos, setLinkedMemos] = useState<LinkedMemo[]>([]);
|
||||||
@ -36,12 +36,12 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
for (const matchRes of matchedArr) {
|
for (const matchRes of matchedArr) {
|
||||||
if (matchRes && matchRes.length === 3) {
|
if (matchRes && matchRes.length === 3) {
|
||||||
const id = matchRes[2];
|
const id = matchRes[2];
|
||||||
const memoTemp = memoService.getMemoById(id);
|
const memoTemp = memoService.getMemoById(Number(id));
|
||||||
if (memoTemp) {
|
if (memoTemp) {
|
||||||
linkMemos.push({
|
linkMemos.push({
|
||||||
...memoTemp,
|
...memoTemp,
|
||||||
createdAtStr: utils.getDateTimeString(memoTemp.createdAt),
|
createdAtStr: utils.getDateTimeString(memoTemp.createdTs),
|
||||||
dateStr: utils.getDateString(memoTemp.createdAt),
|
dateStr: utils.getDateString(memoTemp.createdTs),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,11 +51,11 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
const linkedMemos = await memoService.getLinkedMemos(memo.id);
|
const linkedMemos = await memoService.getLinkedMemos(memo.id);
|
||||||
setLinkedMemos(
|
setLinkedMemos(
|
||||||
linkedMemos
|
linkedMemos
|
||||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
.sort((a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs))
|
||||||
.map((m) => ({
|
.map((m) => ({
|
||||||
...m,
|
...m,
|
||||||
createdAtStr: utils.getDateTimeString(m.createdAt),
|
createdAtStr: utils.getDateTimeString(m.createdTs),
|
||||||
dateStr: utils.getDateString(m.createdAt),
|
dateStr: utils.getDateString(m.createdTs),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -71,12 +71,12 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
if (targetEl.className === "memo-link-text") {
|
if (targetEl.className === "memo-link-text") {
|
||||||
const nextMemoId = targetEl.dataset?.value;
|
const nextMemoId = targetEl.dataset?.value;
|
||||||
const memoTemp = memoService.getMemoById(nextMemoId ?? "");
|
const memoTemp = memoService.getMemoById(Number(nextMemoId) ?? UNKNOWN_ID);
|
||||||
|
|
||||||
if (memoTemp) {
|
if (memoTemp) {
|
||||||
const nextMemo = {
|
const nextMemo = {
|
||||||
...memoTemp,
|
...memoTemp,
|
||||||
createdAtStr: utils.getDateTimeString(memoTemp.createdAt),
|
createdAtStr: utils.getDateTimeString(memoTemp.createdTs),
|
||||||
};
|
};
|
||||||
setLinkMemos([]);
|
setLinkMemos([]);
|
||||||
setLinkedMemos([]);
|
setLinkedMemos([]);
|
||||||
@ -88,7 +88,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLinkedMemoClick = useCallback((memo: FormattedMemo) => {
|
const handleLinkedMemoClick = useCallback((memo: Memo) => {
|
||||||
setLinkMemos([]);
|
setLinkMemos([]);
|
||||||
setLinkedMemos([]);
|
setLinkedMemos([]);
|
||||||
setMemo(memo);
|
setMemo(memo);
|
||||||
@ -103,7 +103,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<>
|
<>
|
||||||
<div className="memo-card-container">
|
<div className="memo-card-container">
|
||||||
<div className="header-container">
|
<div className="header-container">
|
||||||
<p className="time-text">{memo.createdAtStr}</p>
|
<p className="time-text">{utils.getDateTimeString(memo.createdTs)}</p>
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
||||||
<img className="icon-img" src="/icons/edit.svg" />
|
<img className="icon-img" src="/icons/edit.svg" />
|
||||||
@ -179,7 +179,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function showMemoCardDialog(memo: Model.Memo): void {
|
export default function showMemoCardDialog(memo: Memo): void {
|
||||||
showDialog(
|
showDialog(
|
||||||
{
|
{
|
||||||
className: "memo-card-dialog",
|
className: "memo-card-dialog",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
import React, { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
||||||
import appContext from "../stores/appContext";
|
import appContext from "../stores/appContext";
|
||||||
import { globalStateService, locationService, memoService, resourceService } from "../services";
|
import { globalStateService, locationService, memoService, resourceService } from "../services";
|
||||||
import utils from "../helpers/utils";
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
import { storage } from "../helpers/storage";
|
import { storage } from "../helpers/storage";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
@ -53,14 +53,14 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
const tagSeletorRef = useRef<HTMLDivElement>(null);
|
const tagSeletorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (globalState.markMemoId) {
|
if (globalState.markMemoId !== UNKNOWN_ID) {
|
||||||
const editorCurrentValue = editorRef.current?.getContent();
|
const editorCurrentValue = editorRef.current?.getContent();
|
||||||
const memoLinkText = `${editorCurrentValue ? "\n" : ""}Mark: [@MEMO](${globalState.markMemoId})`;
|
const memoLinkText = `${editorCurrentValue ? "\n" : ""}Mark: [@MEMO](${globalState.markMemoId})`;
|
||||||
editorRef.current?.insertText(memoLinkText);
|
editorRef.current?.insertText(memoLinkText);
|
||||||
globalStateService.setMarkMemoId("");
|
globalStateService.setMarkMemoId(UNKNOWN_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalState.editMemoId && globalState.editMemoId !== prevGlobalStateRef.current.editMemoId) {
|
if (globalState.editMemoId !== UNKNOWN_ID && globalState.editMemoId !== prevGlobalStateRef.current.editMemoId) {
|
||||||
const editMemo = memoService.getMemoById(globalState.editMemoId);
|
const editMemo = memoService.getMemoById(globalState.editMemoId);
|
||||||
if (editMemo) {
|
if (editMemo) {
|
||||||
editorRef.current?.setContent(editMemo.content ?? "");
|
editorRef.current?.setContent(editMemo.content ?? "");
|
||||||
@ -147,15 +147,15 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
const { editMemoId } = globalStateService.getState();
|
const { editMemoId } = globalStateService.getState();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (editMemoId) {
|
if (editMemoId !== UNKNOWN_ID) {
|
||||||
const prevMemo = memoService.getMemoById(editMemoId);
|
const prevMemo = memoService.getMemoById(editMemoId);
|
||||||
|
|
||||||
if (prevMemo && prevMemo.content !== content) {
|
if (prevMemo && prevMemo.content !== content) {
|
||||||
const editedMemo = await memoService.updateMemo(prevMemo.id, content);
|
const editedMemo = await memoService.updateMemo(prevMemo.id, content);
|
||||||
editedMemo.updatedAt = utils.getDateTimeString(Date.now());
|
editedMemo.createdTs = Date.now();
|
||||||
memoService.editMemo(editedMemo);
|
memoService.editMemo(editedMemo);
|
||||||
}
|
}
|
||||||
globalStateService.setEditMemoId("");
|
globalStateService.setEditMemoId(UNKNOWN_ID);
|
||||||
} else {
|
} else {
|
||||||
const newMemo = await memoService.createMemo(content);
|
const newMemo = await memoService.createMemo(content);
|
||||||
memoService.pushMemo(newMemo);
|
memoService.pushMemo(newMemo);
|
||||||
@ -169,7 +169,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCancelBtnClick = useCallback(() => {
|
const handleCancelBtnClick = useCallback(() => {
|
||||||
globalStateService.setEditMemoId("");
|
globalStateService.setEditMemoId(UNKNOWN_ID);
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
}, []);
|
}, []);
|
||||||
@ -259,7 +259,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isEditing = Boolean(globalState.editMemoId);
|
const isEditing = globalState.editMemoId !== UNKNOWN_ID;
|
||||||
|
|
||||||
const editorConfig = useMemo(
|
const editorConfig = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -53,7 +53,7 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
if (
|
if (
|
||||||
duration &&
|
duration &&
|
||||||
duration.from < duration.to &&
|
duration.from < duration.to &&
|
||||||
(utils.getTimeStampByDate(memo.createdAt) < duration.from || utils.getTimeStampByDate(memo.createdAt) > duration.to)
|
(utils.getTimeStampByDate(memo.createdTs) < duration.from || utils.getTimeStampByDate(memo.createdTs) > duration.to)
|
||||||
) {
|
) {
|
||||||
shouldShow = false;
|
shouldShow = false;
|
||||||
}
|
}
|
||||||
@ -76,8 +76,8 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
})
|
})
|
||||||
: memos;
|
: memos;
|
||||||
|
|
||||||
const pinnedMemos = shownMemos.filter((m) => m.rowStatus === "ARCHIVED");
|
const pinnedMemos = shownMemos.filter((m) => m.pinned);
|
||||||
const unpinnedMemos = shownMemos.filter((m) => m.rowStatus === "NORMAL");
|
const unpinnedMemos = shownMemos.filter((m) => !m.pinned);
|
||||||
const sortedMemos = pinnedMemos.concat(unpinnedMemos);
|
const sortedMemos = pinnedMemos.concat(unpinnedMemos);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -112,7 +112,7 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={`memo-list-container ${isFetching ? "" : "completed"}`} onClick={handleMemoListClick} ref={wrapperElement}>
|
<div className={`memo-list-container ${isFetching ? "" : "completed"}`} onClick={handleMemoListClick} ref={wrapperElement}>
|
||||||
{sortedMemos.map((memo) => (
|
{sortedMemos.map((memo) => (
|
||||||
<Memo key={`${memo.id}-${memo.updatedAt}`} memo={memo} />
|
<Memo key={`${memo.id}-${memo.updatedTs}`} memo={memo} />
|
||||||
))}
|
))}
|
||||||
<div className="status-text-container">
|
<div className="status-text-container">
|
||||||
<p className="status-text">
|
<p className="status-text">
|
||||||
|
@ -11,7 +11,7 @@ interface Props extends DialogProps {}
|
|||||||
const MemoTrashDialog: React.FC<Props> = (props: Props) => {
|
const MemoTrashDialog: React.FC<Props> = (props: Props) => {
|
||||||
const { destroy } = props;
|
const { destroy } = props;
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const [deletedMemos, setDeletedMemos] = useState<Model.Memo[]>([]);
|
const [deletedMemos, setDeletedMemos] = useState<Memo[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
memoService.fetchAllMemos();
|
memoService.fetchAllMemos();
|
||||||
@ -31,7 +31,7 @@ const MemoTrashDialog: React.FC<Props> = (props: Props) => {
|
|||||||
locationService.clearQuery();
|
locationService.clearQuery();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDeletedMemoAction = useCallback((memoId: string) => {
|
const handleDeletedMemoAction = useCallback((memoId: MemoId) => {
|
||||||
setDeletedMemos((deletedMemos) => deletedMemos.filter((memo) => memo.id !== memoId));
|
setDeletedMemos((deletedMemos) => deletedMemos.filter((memo) => memo.id !== memoId));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ const MemoTrashDialog: React.FC<Props> = (props: Props) => {
|
|||||||
) : (
|
) : (
|
||||||
<div className="deleted-memos-container">
|
<div className="deleted-memos-container">
|
||||||
{deletedMemos.map((memo) => (
|
{deletedMemos.map((memo) => (
|
||||||
<DeletedMemo key={`${memo.id}-${memo.updatedAt}`} memo={memo} handleDeletedMemoAction={handleDeletedMemoAction} />
|
<DeletedMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} handleDeletedMemoAction={handleDeletedMemoAction} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -16,7 +16,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
createUserEmail: "",
|
createUserEmail: "",
|
||||||
createUserPassword: "",
|
createUserPassword: "",
|
||||||
});
|
});
|
||||||
const [userList, setUserList] = useState<Model.User[]>([]);
|
const [userList, setUserList] = useState<User[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUserList();
|
fetchUserList();
|
||||||
@ -47,7 +47,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userCreate: API.UserCreate = {
|
const userCreate: UserCreate = {
|
||||||
email: state.createUserEmail,
|
email: state.createUserEmail,
|
||||||
password: state.createUserPassword,
|
password: state.createUserPassword,
|
||||||
role: "USER",
|
role: "USER",
|
||||||
@ -83,10 +83,18 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="title-text">Member list</p>
|
<p className="title-text">Member list</p>
|
||||||
|
<div className="member-container field-container">
|
||||||
|
<span className="field-text">ID</span>
|
||||||
|
<span className="field-text">EMAIL</span>
|
||||||
|
</div>
|
||||||
{userList.map((user) => (
|
{userList.map((user) => (
|
||||||
<div key={user.id} className="user-container">
|
<div key={user.id} className="member-container">
|
||||||
<span className="field-text id-text">{user.id}</span>
|
<span className="field-text id-text">{user.id}</span>
|
||||||
<span className="field-text">{user.email}</span>
|
<span className="field-text email-text">{user.email}</span>
|
||||||
|
{/* TODO */}
|
||||||
|
{/* <div className="buttons-container">
|
||||||
|
<span>delete</span>
|
||||||
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import appContext from "../../stores/appContext";
|
import appContext from "../../stores/appContext";
|
||||||
import { userService } from "../../services";
|
import { userService } from "../../services";
|
||||||
import utils from "../../helpers/utils";
|
|
||||||
import { validate, ValidatorConfig } from "../../helpers/validator";
|
import { validate, ValidatorConfig } from "../../helpers/validator";
|
||||||
import toastHelper from "../Toast";
|
import toastHelper from "../Toast";
|
||||||
import showChangePasswordDialog from "../ChangePasswordDialog";
|
import showChangePasswordDialog from "../ChangePasswordDialog";
|
||||||
@ -19,7 +18,7 @@ interface Props {}
|
|||||||
|
|
||||||
const MyAccountSection: React.FC<Props> = () => {
|
const MyAccountSection: React.FC<Props> = () => {
|
||||||
const { userState } = useContext(appContext);
|
const { userState } = useContext(appContext);
|
||||||
const user = userState.user as Model.User;
|
const user = userState.user as User;
|
||||||
const [username, setUsername] = useState<string>(user.name);
|
const [username, setUsername] = useState<string>(user.name);
|
||||||
const openAPIRoute = `${window.location.origin}/h/${user.openId}/memo`;
|
const openAPIRoute = `${window.location.origin}/h/${user.openId}/memo`;
|
||||||
|
|
||||||
@ -69,10 +68,6 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||||||
<span className="normal-text">Email:</span>
|
<span className="normal-text">Email:</span>
|
||||||
<span className="normal-text">{user.email}</span>
|
<span className="normal-text">{user.email}</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label">
|
|
||||||
<span className="normal-text">Created at:</span>
|
|
||||||
<span className="normal-text">{utils.getDateString(user.createdAt)}</span>
|
|
||||||
</label>
|
|
||||||
<label className="form-label input-form-label username-label">
|
<label className="form-label input-form-label username-label">
|
||||||
<span className="normal-text">Username:</span>
|
<span className="normal-text">Username:</span>
|
||||||
<input type="text" value={username} onChange={handleUsernameChanged} />
|
<input type="text" value={username} onChange={handleUsernameChanged} />
|
||||||
|
@ -10,7 +10,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
const formatedMemos = memoService.getState().memos.map((m) => {
|
const formatedMemos = memoService.getState().memos.map((m) => {
|
||||||
return {
|
return {
|
||||||
content: m.content,
|
content: m.content,
|
||||||
createdAt: m.createdAt,
|
createdTs: m.createdTs,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsText(fileInputEl.files[0]);
|
reader.readAsText(fileInputEl.files[0]);
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
const memoList = JSON.parse(event.target?.result as string) as Model.Memo[];
|
const memoList = JSON.parse(event.target?.result as string) as Memo[];
|
||||||
if (!Array.isArray(memoList)) {
|
if (!Array.isArray(memoList)) {
|
||||||
toastHelper.error("Unexpected data type.");
|
toastHelper.error("Unexpected data type.");
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
|
|
||||||
for (const memo of memoList) {
|
for (const memo of memoList) {
|
||||||
const content = memo.content || "";
|
const content = memo.content || "";
|
||||||
const createdAt = memo.createdAt || utils.getDateTimeString(Date.now());
|
const createdAt = utils.getDateTimeString(memo.createdTs || Date.now());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await memoService.importMemo(content, createdAt);
|
await memoService.importMemo(content, createdAt);
|
||||||
|
@ -10,15 +10,15 @@ import toastHelper from "./Toast";
|
|||||||
import "../less/share-memo-image-dialog.less";
|
import "../less/share-memo-image-dialog.less";
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
||||||
const { memo: propsMemo, destroy } = props;
|
const { memo: propsMemo, destroy } = props;
|
||||||
const { user: userinfo } = userService.getState();
|
const { user: userinfo } = userService.getState();
|
||||||
const memo: FormattedMemo = {
|
const memo = {
|
||||||
...propsMemo,
|
...propsMemo,
|
||||||
createdAtStr: utils.getDateTimeString(propsMemo.createdAt),
|
createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
|
||||||
};
|
};
|
||||||
const memoImgUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
const memoImgUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []);
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function showShareMemoImageDialog(memo: Model.Memo): void {
|
export default function showShareMemoImageDialog(memo: Memo): void {
|
||||||
showDialog(
|
showDialog(
|
||||||
{
|
{
|
||||||
className: "share-memo-image-dialog",
|
className: "share-memo-image-dialog",
|
||||||
|
@ -20,10 +20,10 @@ const ShortcutList: React.FC<Props> = () => {
|
|||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const pinnedShortcuts = shortcuts
|
const pinnedShortcuts = shortcuts
|
||||||
.filter((s) => s.rowStatus === "ARCHIVED")
|
.filter((s) => s.rowStatus === "ARCHIVED")
|
||||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt));
|
.sort((a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs));
|
||||||
const unpinnedShortcuts = shortcuts
|
const unpinnedShortcuts = shortcuts
|
||||||
.filter((s) => s.rowStatus === "NORMAL")
|
.filter((s) => s.rowStatus === "NORMAL")
|
||||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt));
|
.sort((a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs));
|
||||||
const sortedShortcuts = pinnedShortcuts.concat(unpinnedShortcuts);
|
const sortedShortcuts = pinnedShortcuts.concat(unpinnedShortcuts);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -55,7 +55,7 @@ const ShortcutList: React.FC<Props> = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface ShortcutContainerProps {
|
interface ShortcutContainerProps {
|
||||||
shortcut: Model.Shortcut;
|
shortcut: Shortcut;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const Sidebar: React.FC<Props> = () => {
|
|||||||
memoState: { memos, tags },
|
memoState: { memos, tags },
|
||||||
userState: { user },
|
userState: { user },
|
||||||
} = useContext(appContext);
|
} = useContext(appContext);
|
||||||
const createdDays = user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdAt)) / 1000 / 3600 / 24) : 0;
|
const createdDays = user ? Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24) : 0;
|
||||||
|
|
||||||
const handleMyAccountBtnClick = () => {
|
const handleMyAccountBtnClick = () => {
|
||||||
showSettingDialog();
|
showSettingDialog();
|
||||||
|
@ -77,7 +77,7 @@ const TagList: React.FC<Props> = () => {
|
|||||||
))}
|
))}
|
||||||
<Only when={tags.length < 5 && memoService.initialized}>
|
<Only when={tags.length < 5 && memoService.initialized}>
|
||||||
<p className="tag-tip-container">
|
<p className="tag-tip-container">
|
||||||
Enter <span className="code-text">#Tag </span> to create a tag
|
Enter <span className="code-text">#tag </span> to create a tag
|
||||||
</p>
|
</p>
|
||||||
</Only>
|
</Only>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import utils from "./utils";
|
|
||||||
|
|
||||||
type ResponseObject<T> = {
|
type ResponseObject<T> = {
|
||||||
data: T;
|
data: T;
|
||||||
error?: string;
|
error?: string;
|
||||||
@ -42,29 +40,14 @@ async function request<T>(config: RequestConfig): Promise<T> {
|
|||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
export function getSystemStatus() {
|
export function getSystemStatus() {
|
||||||
return request<API.SystemStatus>({
|
return request<SystemStatus>({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/status",
|
url: "/api/status",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserList() {
|
|
||||||
return request<Model.User[]>({
|
|
||||||
method: "GET",
|
|
||||||
url: "/api/user",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUser(userCreate: API.UserCreate) {
|
|
||||||
return request<Model.User[]>({
|
|
||||||
method: "POST",
|
|
||||||
url: "/api/user",
|
|
||||||
data: userCreate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function login(email: string, password: string) {
|
export function login(email: string, password: string) {
|
||||||
return request<Model.User>({
|
return request<User>({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/api/auth/login",
|
url: "/api/auth/login",
|
||||||
data: {
|
data: {
|
||||||
@ -75,7 +58,7 @@ namespace api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function signup(email: string, password: string, role: UserRole) {
|
export function signup(email: string, password: string, role: UserRole) {
|
||||||
return request<Model.User>({
|
return request<User>({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/api/auth/signup",
|
url: "/api/auth/signup",
|
||||||
data: {
|
data: {
|
||||||
@ -94,70 +77,89 @@ namespace api {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserInfo() {
|
export function createUser(userCreate: UserCreate) {
|
||||||
return request<Model.User>({
|
return request<User[]>({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/user",
|
||||||
|
data: userCreate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUser() {
|
||||||
|
return request<User>({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/user/me",
|
url: "/api/user/me",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateUserinfo(userinfo: Partial<{ name: string; password: string; resetOpenId: boolean }>) {
|
export function getUserList() {
|
||||||
return request<Model.User>({
|
return request<User[]>({
|
||||||
method: "PATCH",
|
method: "GET",
|
||||||
url: "/api/user/me",
|
url: "/api/user",
|
||||||
data: userinfo,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetOpenId() {
|
export function patchUser(userPatch: UserPatch) {
|
||||||
return request<string>({
|
return request<User>({
|
||||||
method: "POST",
|
method: "PATCH",
|
||||||
url: "/api/user/open_id/new",
|
url: "/api/user/me",
|
||||||
|
data: userPatch,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMyMemos() {
|
export function getMyMemos() {
|
||||||
return request<Model.Memo[]>({
|
return request<Memo[]>({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/memo",
|
url: "/api/memo",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMyDeletedMemos() {
|
export function getMyArchivedMemos() {
|
||||||
return request<Model.Memo[]>({
|
return request<Memo[]>({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/memo?rowStatus=HIDDEN",
|
url: "/api/memo?rowStatus=ARCHIVED",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMemo(content: string, createdAt?: string) {
|
export function createMemo(memoCreate: MemoCreate) {
|
||||||
const data: any = {
|
return request<Memo>({
|
||||||
content,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (createdAt) {
|
|
||||||
const createdTms = utils.getTimeStampByDate(createdAt);
|
|
||||||
data.createdTs = Math.floor(createdTms / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request<Model.Memo>({
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/api/memo",
|
url: "/api/memo",
|
||||||
data: data,
|
data: memoCreate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateMemo(memoId: string, content: string) {
|
export function patchMemo(memoPatch: MemoPatch) {
|
||||||
return request<Model.Memo>({
|
return request<Memo>({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: `/api/memo/${memoId}`,
|
url: `/api/memo/${memoPatch.id}`,
|
||||||
data: {
|
data: {
|
||||||
content,
|
memoPatch,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pinMemo(memoId: string) {
|
export function pinMemo(memoId: MemoId) {
|
||||||
|
return request({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/memo/${memoId}/organizer`,
|
||||||
|
data: {
|
||||||
|
pinned: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unpinMemo(memoId: MemoId) {
|
||||||
|
return request({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/memo/${memoId}/organizer`,
|
||||||
|
data: {
|
||||||
|
pinned: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function archiveMemo(memoId: MemoId) {
|
||||||
return request({
|
return request({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: `/api/memo/${memoId}`,
|
url: `/api/memo/${memoId}`,
|
||||||
@ -167,27 +169,7 @@ namespace api {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unpinMemo(shortcutId: string) {
|
export function restoreMemo(memoId: MemoId) {
|
||||||
return request({
|
|
||||||
method: "PATCH",
|
|
||||||
url: `/api/memo/${shortcutId}`,
|
|
||||||
data: {
|
|
||||||
rowStatus: "NORMAL",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hideMemo(memoId: string) {
|
|
||||||
return request({
|
|
||||||
method: "PATCH",
|
|
||||||
url: `/api/memo/${memoId}`,
|
|
||||||
data: {
|
|
||||||
rowStatus: "HIDDEN",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function restoreMemo(memoId: string) {
|
|
||||||
return request({
|
return request({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: `/api/memo/${memoId}`,
|
url: `/api/memo/${memoId}`,
|
||||||
@ -197,7 +179,7 @@ namespace api {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteMemo(memoId: string) {
|
export function deleteMemo(memoId: MemoId) {
|
||||||
return request({
|
return request({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: `/api/memo/${memoId}`,
|
url: `/api/memo/${memoId}`,
|
||||||
@ -205,14 +187,14 @@ namespace api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getMyShortcuts() {
|
export function getMyShortcuts() {
|
||||||
return request<Model.Shortcut[]>({
|
return request<Shortcut[]>({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/shortcut",
|
url: "/api/shortcut",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createShortcut(title: string, payload: string) {
|
export function createShortcut(title: string, payload: string) {
|
||||||
return request<Model.Shortcut>({
|
return request<Shortcut>({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/api/shortcut",
|
url: "/api/shortcut",
|
||||||
data: {
|
data: {
|
||||||
@ -223,7 +205,7 @@ namespace api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function updateShortcut(shortcutId: string, title: string, payload: string) {
|
export function updateShortcut(shortcutId: string, title: string, payload: string) {
|
||||||
return request<Model.Shortcut>({
|
return request<Shortcut>({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: `/api/shortcut/${shortcutId}`,
|
url: `/api/shortcut/${shortcutId}`,
|
||||||
data: {
|
data: {
|
||||||
@ -261,7 +243,7 @@ namespace api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function uploadFile(formData: FormData) {
|
export function uploadFile(formData: FormData) {
|
||||||
return request<Model.Resource>({
|
return request<Resource>({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/api/resource",
|
url: "/api/resource",
|
||||||
data: formData,
|
data: formData,
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// UNKNOWN_ID is the symbol for unknown id
|
||||||
|
export const UNKNOWN_ID = -1;
|
||||||
|
|
||||||
// default animation duration
|
// default animation duration
|
||||||
export const ANIMATION_DURATION = 200;
|
export const ANIMATION_DURATION = 200;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ export const getDefaultFilter = (): BaseFilter => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkShouldShowMemoWithFilters = (memo: Model.Memo, filters: Filter[]) => {
|
export const checkShouldShowMemoWithFilters = (memo: Memo, filters: Filter[]) => {
|
||||||
let shouldShow = true;
|
let shouldShow = true;
|
||||||
|
|
||||||
for (const f of filters) {
|
for (const f of filters) {
|
||||||
@ -106,7 +106,7 @@ export const checkShouldShowMemoWithFilters = (memo: Model.Memo, filters: Filter
|
|||||||
return shouldShow;
|
return shouldShow;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkShouldShowMemo = (memo: Model.Memo, filter: Filter) => {
|
export const checkShouldShowMemo = (memo: Memo, filter: Filter) => {
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
value: { operator, value },
|
value: { operator, value },
|
||||||
|
@ -83,8 +83,8 @@ namespace utils {
|
|||||||
return Array.from(new Set(data));
|
return Array.from(new Set(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dedupeObjectWithId<T extends { id: string }>(data: T[]): T[] {
|
export function dedupeObjectWithId<T extends { id: string | number }>(data: T[]): T[] {
|
||||||
const idSet = new Set<string>();
|
const idSet = new Set<string | number>();
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
@apply border-gray-200;
|
@apply border-gray-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ARCHIVED {
|
&.pinned {
|
||||||
@apply border-gray-200 border-2;
|
@apply border-gray-200 border-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,15 +25,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .user-container {
|
> .field-container {
|
||||||
@apply w-full mb-4 grid grid-cols-5;
|
> .field-text {
|
||||||
|
@apply text-gray-400 text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .member-container {
|
||||||
|
@apply w-full grid grid-cols-5 border-b py-2;
|
||||||
|
|
||||||
> .field-text {
|
> .field-text {
|
||||||
@apply text-base mr-4 w-16;
|
@apply text-base pl-2 mr-4 w-16;
|
||||||
|
|
||||||
&.id-text {
|
&.id-text {
|
||||||
@apply font-mono;
|
@apply font-mono text-gray-600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.email-text {
|
||||||
|
@apply col-span-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .buttons-container {
|
||||||
|
@apply col-span-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
> .shortcut-container {
|
> .shortcut-container {
|
||||||
.flex(row, space-between, center);
|
.flex(row, space-between, center);
|
||||||
@apply w-full h-10 py-0 px-4 mt-1 rounded-lg text-sm cursor-pointer select-none shrink-0;
|
@apply w-full h-10 py-0 px-4 mt-2 rounded-lg text-base cursor-pointer select-none shrink-0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: @bg-gray;
|
background-color: @bg-gray;
|
||||||
|
@ -5,14 +5,21 @@
|
|||||||
@apply w-full h-full bg-white;
|
@apply w-full h-full bg-white;
|
||||||
|
|
||||||
> .page-container {
|
> .page-container {
|
||||||
@apply w-80 max-w-full p-4 -mt-16;
|
@apply w-80 max-w-full py-4 -mt-16;
|
||||||
|
|
||||||
> .page-header-container {
|
> .page-header-container {
|
||||||
.flex(row, space-between, center);
|
@apply flex flex-col justify-start items-start w-full mb-4;
|
||||||
@apply w-full mb-4;
|
|
||||||
|
|
||||||
> .title-text {
|
> .title-text {
|
||||||
@apply text-2xl;
|
@apply text-2xl;
|
||||||
|
|
||||||
|
> .icon-text {
|
||||||
|
@apply text-4xl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .slogan-text {
|
||||||
|
@apply mt-2 text-sm text-gray-600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +29,7 @@
|
|||||||
|
|
||||||
> .form-item-container {
|
> .form-item-container {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
@apply relative w-full text-base my-2;
|
@apply relative w-full text-base mt-2;
|
||||||
|
|
||||||
> .normal-text {
|
> .normal-text {
|
||||||
@apply absolute top-3 left-3 px-1 leading-10 flex-shrink-0 text-base cursor-text text-gray-400 bg-transparent transition-all select-none;
|
@apply absolute top-3 left-3 px-1 leading-10 flex-shrink-0 text-base cursor-text text-gray-400 bg-transparent transition-all select-none;
|
||||||
@ -36,7 +43,7 @@
|
|||||||
@apply py-2;
|
@apply py-2;
|
||||||
|
|
||||||
> input {
|
> input {
|
||||||
@apply w-full py-3 px-3 text-base rounded-lg border border-solid border-gray-400;
|
@apply w-full py-3 px-3 text-base shadow-inner rounded-lg border border-solid border-gray-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -51,24 +58,16 @@
|
|||||||
@apply w-full mt-2;
|
@apply w-full mt-2;
|
||||||
|
|
||||||
> .btn {
|
> .btn {
|
||||||
@apply px-1 py-2 text-sm rounded;
|
@apply px-1 py-2 text-sm rounded hover:opacity-80;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@apply opacity-80;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
@apply text-gray-400 cursor-not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.signin-btn {
|
&.signin-btn {
|
||||||
@apply bg-green-600 text-white px-3;
|
@apply bg-green-600 text-white px-3 shadow;
|
||||||
|
}
|
||||||
|
|
||||||
&.requesting {
|
&.requesting {
|
||||||
@apply cursor-wait opacity-80;
|
@apply cursor-wait opacity-80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
> .btn-text {
|
> .btn-text {
|
||||||
@apply text-sm;
|
@apply text-sm;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
.tag-item-container {
|
.tag-item-container {
|
||||||
.flex(row, space-between, center);
|
.flex(row, space-between, center);
|
||||||
@apply w-full h-10 py-0 px-4 rounded-lg text-sm shrink-0 select-none cursor-pointer;
|
@apply w-full h-10 py-0 px-4 rounded-lg text-base shrink-0 select-none cursor-pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: @bg-gray;
|
background-color: @bg-gray;
|
||||||
|
@ -17,7 +17,7 @@ const validateConfig: ValidatorConfig = {
|
|||||||
|
|
||||||
const Signin: React.FC<Props> = () => {
|
const Signin: React.FC<Props> = () => {
|
||||||
const pageLoadingState = useLoading(true);
|
const pageLoadingState = useLoading(true);
|
||||||
const [siteOwner, setSiteOwner] = useState<Model.User>();
|
const [siteOwner, setSiteOwner] = useState<User>();
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const actionBtnLoadingState = useLoading(false);
|
const actionBtnLoadingState = useLoading(false);
|
||||||
@ -128,12 +128,15 @@ const Signin: React.FC<Props> = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-wrapper signin">
|
<div className={`page-wrapper signin ${pageLoadingState.isLoading ? "hidden" : ""}`}>
|
||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
<div className="page-header-container">
|
<div className="page-header-container">
|
||||||
<p className="title-text">
|
<p className="title-text">
|
||||||
<span className="icon-text">✍️</span> Memos
|
<span className="icon-text">✍️</span> Memos
|
||||||
</p>
|
</p>
|
||||||
|
<p className="slogan-text">
|
||||||
|
An <i>open source</i>, <i>self-hosted</i> knowledge base that works with local SQLite.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="page-content-container">
|
<div className="page-content-container">
|
||||||
<div className="form-item-container input-form-container">
|
<div className="form-item-container input-form-container">
|
||||||
@ -150,7 +153,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
Login as Guest
|
Login as Guest
|
||||||
</button>
|
</button>
|
||||||
<span className="split-text">/</span>
|
<span className="split-text">/</span>
|
||||||
{siteOwner || pageLoadingState.isLoading ? (
|
{siteOwner ? (
|
||||||
<button
|
<button
|
||||||
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
|
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
|
||||||
onClick={() => handleSigninBtnsClick()}
|
onClick={() => handleSigninBtnsClick()}
|
||||||
@ -167,7 +170,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="tip-text">
|
<p className="tip-text">
|
||||||
{siteOwner || pageLoadingState.isLoading
|
{siteOwner
|
||||||
? "If you don't have an account, please contact the site owner or login as guest."
|
? "If you don't have an account, please contact the site owner or login as guest."
|
||||||
: "You are registering as the site owner."}
|
: "You are registering as the site owner."}
|
||||||
</p>
|
</p>
|
||||||
|
@ -18,7 +18,7 @@ class GlobalStateService {
|
|||||||
return appStore.getState().globalState;
|
return appStore.getState().globalState;
|
||||||
};
|
};
|
||||||
|
|
||||||
public setEditMemoId = (editMemoId: string) => {
|
public setEditMemoId = (editMemoId: MemoId) => {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "SET_EDIT_MEMO_ID",
|
type: "SET_EDIT_MEMO_ID",
|
||||||
payload: {
|
payload: {
|
||||||
@ -27,7 +27,7 @@ class GlobalStateService {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public setMarkMemoId = (markMemoId: string) => {
|
public setMarkMemoId = (markMemoId: MemoId) => {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "SET_MARK_MEMO_ID",
|
type: "SET_MARK_MEMO_ID",
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -17,7 +17,7 @@ class MemoService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await api.getMyMemos();
|
const data = await api.getMyMemos();
|
||||||
const memos: Model.Memo[] = data.filter((m) => m.rowStatus !== "HIDDEN").map((m) => this.convertResponseModelMemo(m));
|
const memos: Memo[] = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => this.convertResponseModelMemo(m));
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "SET_MEMOS",
|
type: "SET_MEMOS",
|
||||||
payload: {
|
payload: {
|
||||||
@ -37,14 +37,14 @@ class MemoService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await api.getMyDeletedMemos();
|
const data = await api.getMyArchivedMemos();
|
||||||
const deletedMemos: Model.Memo[] = data.map((m) => {
|
const deletedMemos: Memo[] = data.map((m) => {
|
||||||
return this.convertResponseModelMemo(m);
|
return this.convertResponseModelMemo(m);
|
||||||
});
|
});
|
||||||
return deletedMemos;
|
return deletedMemos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public pushMemo(memo: Model.Memo) {
|
public pushMemo(memo: Memo) {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "INSERT_MEMO",
|
type: "INSERT_MEMO",
|
||||||
payload: {
|
payload: {
|
||||||
@ -55,7 +55,7 @@ class MemoService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMemoById(id: string) {
|
public getMemoById(id: MemoId) {
|
||||||
for (const m of this.getState().memos) {
|
for (const m of this.getState().memos) {
|
||||||
if (m.id === id) {
|
if (m.id === id) {
|
||||||
return m;
|
return m;
|
||||||
@ -65,8 +65,8 @@ class MemoService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async hideMemoById(id: string) {
|
public async hideMemoById(id: MemoId) {
|
||||||
await api.hideMemo(id);
|
await api.archiveMemo(id);
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "DELETE_MEMO_BY_ID",
|
type: "DELETE_MEMO_BY_ID",
|
||||||
payload: {
|
payload: {
|
||||||
@ -75,17 +75,17 @@ class MemoService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async restoreMemoById(id: string) {
|
public async restoreMemoById(id: MemoId) {
|
||||||
await api.restoreMemo(id);
|
await api.restoreMemo(id);
|
||||||
memoService.clearMemos();
|
memoService.clearMemos();
|
||||||
memoService.fetchAllMemos();
|
memoService.fetchAllMemos();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMemoById(id: string) {
|
public async deleteMemoById(id: MemoId) {
|
||||||
await api.deleteMemo(id);
|
await api.deleteMemo(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public editMemo(memo: Model.Memo) {
|
public editMemo(memo: Memo) {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "EDIT_MEMO",
|
type: "EDIT_MEMO",
|
||||||
payload: memo,
|
payload: memo,
|
||||||
@ -118,39 +118,48 @@ class MemoService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getLinkedMemos(memoId: string): Promise<Model.Memo[]> {
|
public async getLinkedMemos(memoId: MemoId): Promise<Memo[]> {
|
||||||
const { memos } = this.getState();
|
const { memos } = this.getState();
|
||||||
return memos.filter((m) => m.content.includes(memoId));
|
return memos.filter((m) => m.content.includes(`${memoId}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createMemo(content: string): Promise<Model.Memo> {
|
public async createMemo(content: string): Promise<Memo> {
|
||||||
const memo = await api.createMemo(content);
|
const memo = await api.createMemo({
|
||||||
|
content,
|
||||||
|
});
|
||||||
return this.convertResponseModelMemo(memo);
|
return this.convertResponseModelMemo(memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateMemo(memoId: string, content: string): Promise<Model.Memo> {
|
public async updateMemo(memoId: MemoId, content: string): Promise<Memo> {
|
||||||
const memo = await api.updateMemo(memoId, content);
|
const memo = await api.patchMemo({
|
||||||
|
id: memoId,
|
||||||
|
content,
|
||||||
|
});
|
||||||
return this.convertResponseModelMemo(memo);
|
return this.convertResponseModelMemo(memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pinMemo(memoId: string) {
|
public async pinMemo(memoId: MemoId) {
|
||||||
await api.pinMemo(memoId);
|
await api.pinMemo(memoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unpinMemo(memoId: string) {
|
public async unpinMemo(memoId: MemoId) {
|
||||||
await api.unpinMemo(memoId);
|
await api.unpinMemo(memoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importMemo(content: string, createdAt: string) {
|
public async importMemo(content: string, createdAt: string) {
|
||||||
await api.createMemo(content, createdAt);
|
const createdTs = Math.floor(utils.getTimeStampByDate(createdAt) / 1000);
|
||||||
|
|
||||||
|
await api.createMemo({
|
||||||
|
content,
|
||||||
|
createdTs,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertResponseModelMemo(memo: Model.Memo): Model.Memo {
|
private convertResponseModelMemo(memo: Memo): Memo {
|
||||||
return {
|
return {
|
||||||
...memo,
|
...memo,
|
||||||
id: String(memo.id),
|
createdTs: memo.createdTs * 1000,
|
||||||
createdAt: utils.getDataStringWithTs(memo.createdTs),
|
updatedTs: memo.updatedTs * 1000,
|
||||||
updatedAt: utils.getDataStringWithTs(memo.updatedTs),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import userService from "./userService";
|
import userService from "./userService";
|
||||||
import api from "../helpers/api";
|
import api from "../helpers/api";
|
||||||
import appStore from "../stores/appStore";
|
import appStore from "../stores/appStore";
|
||||||
import utils from "../helpers/utils";
|
|
||||||
|
|
||||||
class ShortcutService {
|
class ShortcutService {
|
||||||
public getState() {
|
public getState() {
|
||||||
@ -33,7 +32,7 @@ class ShortcutService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public pushShortcut(shortcut: Model.Shortcut) {
|
public pushShortcut(shortcut: Shortcut) {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "INSERT_SHORTCUT",
|
type: "INSERT_SHORTCUT",
|
||||||
payload: {
|
payload: {
|
||||||
@ -44,7 +43,7 @@ class ShortcutService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public editShortcut(shortcut: Model.Shortcut) {
|
public editShortcut(shortcut: Shortcut) {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "UPDATE_SHORTCUT",
|
type: "UPDATE_SHORTCUT",
|
||||||
payload: shortcut,
|
payload: shortcut,
|
||||||
@ -79,12 +78,11 @@ class ShortcutService {
|
|||||||
await api.unpinShortcut(shortcutId);
|
await api.unpinShortcut(shortcutId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public convertResponseModelShortcut(shortcut: Model.Shortcut): Model.Shortcut {
|
public convertResponseModelShortcut(shortcut: Shortcut): Shortcut {
|
||||||
return {
|
return {
|
||||||
...shortcut,
|
...shortcut,
|
||||||
id: String(shortcut.id),
|
createdTs: shortcut.createdTs * 1000,
|
||||||
createdAt: utils.getDataStringWithTs(shortcut.createdTs),
|
updatedTs: shortcut.updatedTs * 1000,
|
||||||
updatedAt: utils.getDataStringWithTs(shortcut.updatedTs),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import api from "../helpers/api";
|
import api from "../helpers/api";
|
||||||
import utils from "../helpers/utils";
|
|
||||||
import appStore from "../stores/appStore";
|
import appStore from "../stores/appStore";
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
@ -8,7 +7,7 @@ class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async doSignIn() {
|
public async doSignIn() {
|
||||||
const user = await api.getUserInfo();
|
const user = await api.getUser();
|
||||||
if (user) {
|
if (user) {
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
type: "LOGIN",
|
type: "LOGIN",
|
||||||
@ -33,19 +32,19 @@ class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async updateUsername(name: string): Promise<void> {
|
public async updateUsername(name: string): Promise<void> {
|
||||||
await api.updateUserinfo({
|
await api.patchUser({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updatePassword(password: string): Promise<void> {
|
public async updatePassword(password: string): Promise<void> {
|
||||||
await api.updateUserinfo({
|
await api.patchUser({
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async resetOpenId(): Promise<string> {
|
public async resetOpenId(): Promise<string> {
|
||||||
const user = await api.updateUserinfo({
|
const user = await api.patchUser({
|
||||||
resetOpenId: true,
|
resetOpenId: true,
|
||||||
});
|
});
|
||||||
appStore.dispatch({
|
appStore.dispatch({
|
||||||
@ -55,11 +54,11 @@ class UserService {
|
|||||||
return user.openId;
|
return user.openId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertResponseModelUser(user: Model.User): Model.User {
|
private convertResponseModelUser(user: User): User {
|
||||||
return {
|
return {
|
||||||
...user,
|
...user,
|
||||||
createdAt: utils.getDataStringWithTs(user.createdTs),
|
createdTs: user.createdTs * 1000,
|
||||||
updatedAt: utils.getDataStringWithTs(user.updatedTs),
|
updatedTs: user.updatedTs * 1000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
|
|
||||||
export interface AppSetting {
|
export interface AppSetting {
|
||||||
shouldSplitMemoWord: boolean;
|
shouldSplitMemoWord: boolean;
|
||||||
shouldHideImageUrl: boolean;
|
shouldHideImageUrl: boolean;
|
||||||
@ -5,21 +7,21 @@ export interface AppSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface State extends AppSetting {
|
export interface State extends AppSetting {
|
||||||
markMemoId: string;
|
markMemoId: MemoId;
|
||||||
editMemoId: string;
|
editMemoId: MemoId;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetMarkMemoIdAction {
|
interface SetMarkMemoIdAction {
|
||||||
type: "SET_MARK_MEMO_ID";
|
type: "SET_MARK_MEMO_ID";
|
||||||
payload: {
|
payload: {
|
||||||
markMemoId: string;
|
markMemoId: MemoId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetEditMemoIdAction {
|
interface SetEditMemoIdAction {
|
||||||
type: "SET_EDIT_MEMO_ID";
|
type: "SET_EDIT_MEMO_ID";
|
||||||
payload: {
|
payload: {
|
||||||
editMemoId: string;
|
editMemoId: MemoId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,8 +67,8 @@ export function reducer(state: State, action: Actions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const defaultState: State = {
|
export const defaultState: State = {
|
||||||
markMemoId: "",
|
markMemoId: UNKNOWN_ID,
|
||||||
editMemoId: "",
|
editMemoId: UNKNOWN_ID,
|
||||||
shouldSplitMemoWord: true,
|
shouldSplitMemoWord: true,
|
||||||
shouldHideImageUrl: true,
|
shouldHideImageUrl: true,
|
||||||
shouldUseMarkdownParser: true,
|
shouldUseMarkdownParser: true,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import utils from "../helpers/utils";
|
import utils from "../helpers/utils";
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
memos: Model.Memo[];
|
memos: Memo[];
|
||||||
tags: string[];
|
tags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetMemosAction {
|
interface SetMemosAction {
|
||||||
type: "SET_MEMOS";
|
type: "SET_MEMOS";
|
||||||
payload: {
|
payload: {
|
||||||
memos: Model.Memo[];
|
memos: Memo[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,20 +22,20 @@ interface SetTagsAction {
|
|||||||
interface InsertMemoAction {
|
interface InsertMemoAction {
|
||||||
type: "INSERT_MEMO";
|
type: "INSERT_MEMO";
|
||||||
payload: {
|
payload: {
|
||||||
memo: Model.Memo;
|
memo: Memo;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteMemoByIdAction {
|
interface DeleteMemoByIdAction {
|
||||||
type: "DELETE_MEMO_BY_ID";
|
type: "DELETE_MEMO_BY_ID";
|
||||||
payload: {
|
payload: {
|
||||||
id: string;
|
id: MemoId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditMemoByIdAction {
|
interface EditMemoByIdAction {
|
||||||
type: "EDIT_MEMO";
|
type: "EDIT_MEMO";
|
||||||
payload: Model.Memo;
|
payload: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actions = SetMemosAction | SetTagsAction | InsertMemoAction | DeleteMemoByIdAction | EditMemoByIdAction;
|
export type Actions = SetMemosAction | SetTagsAction | InsertMemoAction | DeleteMemoByIdAction | EditMemoByIdAction;
|
||||||
@ -43,9 +43,7 @@ export type Actions = SetMemosAction | SetTagsAction | InsertMemoAction | Delete
|
|||||||
export function reducer(state: State, action: Actions): State {
|
export function reducer(state: State, action: Actions): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "SET_MEMOS": {
|
case "SET_MEMOS": {
|
||||||
const memos = utils.dedupeObjectWithId(
|
const memos = utils.dedupeObjectWithId(action.payload.memos.sort((a, b) => b.createdTs - a.createdTs));
|
||||||
action.payload.memos.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -59,10 +57,7 @@ export function reducer(state: State, action: Actions): State {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "INSERT_MEMO": {
|
case "INSERT_MEMO": {
|
||||||
const memos = utils.dedupeObjectWithId(
|
const memos = utils.dedupeObjectWithId([action.payload.memo, ...state.memos].sort((a, b) => b.createdTs - a.createdTs));
|
||||||
[action.payload.memo, ...state.memos].sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
memos,
|
memos,
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import utils from "../helpers/utils";
|
import utils from "../helpers/utils";
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
shortcuts: Model.Shortcut[];
|
shortcuts: Shortcut[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetShortcutsAction {
|
interface SetShortcutsAction {
|
||||||
type: "SET_SHORTCUTS";
|
type: "SET_SHORTCUTS";
|
||||||
payload: {
|
payload: {
|
||||||
shortcuts: Model.Shortcut[];
|
shortcuts: Shortcut[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InsertShortcutAction {
|
interface InsertShortcutAction {
|
||||||
type: "INSERT_SHORTCUT";
|
type: "INSERT_SHORTCUT";
|
||||||
payload: {
|
payload: {
|
||||||
shortcut: Model.Shortcut;
|
shortcut: Shortcut;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ interface DeleteShortcutByIdAction {
|
|||||||
|
|
||||||
interface UpdateShortcutAction {
|
interface UpdateShortcutAction {
|
||||||
type: "UPDATE_SHORTCUT";
|
type: "UPDATE_SHORTCUT";
|
||||||
payload: Model.Shortcut;
|
payload: Shortcut;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actions = SetShortcutsAction | InsertShortcutAction | DeleteShortcutByIdAction | UpdateShortcutAction;
|
export type Actions = SetShortcutsAction | InsertShortcutAction | DeleteShortcutByIdAction | UpdateShortcutAction;
|
||||||
@ -37,8 +37,8 @@ export function reducer(state: State, action: Actions): State {
|
|||||||
case "SET_SHORTCUTS": {
|
case "SET_SHORTCUTS": {
|
||||||
const shortcuts = utils.dedupeObjectWithId(
|
const shortcuts = utils.dedupeObjectWithId(
|
||||||
action.payload.shortcuts
|
action.payload.shortcuts
|
||||||
.sort((a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt))
|
.sort((a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs))
|
||||||
.sort((a, b) => utils.getTimeStampByDate(b.updatedAt) - utils.getTimeStampByDate(a.updatedAt))
|
.sort((a, b) => utils.getTimeStampByDate(b.updatedTs) - utils.getTimeStampByDate(a.updatedTs))
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -49,7 +49,7 @@ export function reducer(state: State, action: Actions): State {
|
|||||||
case "INSERT_SHORTCUT": {
|
case "INSERT_SHORTCUT": {
|
||||||
const shortcuts = utils.dedupeObjectWithId(
|
const shortcuts = utils.dedupeObjectWithId(
|
||||||
[action.payload.shortcut, ...state.shortcuts].sort(
|
[action.payload.shortcut, ...state.shortcuts].sort(
|
||||||
(a, b) => utils.getTimeStampByDate(b.createdAt) - utils.getTimeStampByDate(a.createdAt)
|
(a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export interface State {
|
export interface State {
|
||||||
user: Model.User | null;
|
user: User | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SignInAction {
|
interface SignInAction {
|
||||||
|
13
web/src/types/api.d.ts
vendored
13
web/src/types/api.d.ts
vendored
@ -1,13 +0,0 @@
|
|||||||
declare namespace API {
|
|
||||||
interface SystemStatus {
|
|
||||||
owner: Model.User;
|
|
||||||
profile: Profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserCreate {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
name: string;
|
|
||||||
role: UserRole;
|
|
||||||
}
|
|
||||||
}
|
|
37
web/src/types/models.d.ts
vendored
37
web/src/types/models.d.ts
vendored
@ -1,37 +0,0 @@
|
|||||||
type UserRole = "OWNER" | "USER";
|
|
||||||
|
|
||||||
declare namespace Model {
|
|
||||||
interface BaseModel {
|
|
||||||
id: string;
|
|
||||||
createdTs: number;
|
|
||||||
updatedTs: number;
|
|
||||||
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface User extends BaseModel {
|
|
||||||
role: UserRole;
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
openId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Memo extends BaseModel {
|
|
||||||
content: string;
|
|
||||||
rowStatus: "NORMAL" | "ARCHIVED" | "HIDDEN";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Shortcut extends BaseModel {
|
|
||||||
title: string;
|
|
||||||
payload: string;
|
|
||||||
rowStatus: "NORMAL" | "ARCHIVED";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Resource extends BaseModel {
|
|
||||||
filename: string;
|
|
||||||
type: string;
|
|
||||||
size: string;
|
|
||||||
createdAt: string;
|
|
||||||
}
|
|
||||||
}
|
|
4
web/src/types/module/system.d.ts
vendored
4
web/src/types/module/system.d.ts
vendored
@ -1,4 +0,0 @@
|
|||||||
interface Profile {
|
|
||||||
mode: string;
|
|
||||||
version: string;
|
|
||||||
}
|
|
1
web/src/types/modules/common.d.ts
vendored
Normal file
1
web/src/types/modules/common.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
type RowStatus = "NORMAL" | "ARCHIVED";
|
24
web/src/types/modules/memo.d.ts
vendored
Normal file
24
web/src/types/modules/memo.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
type MemoId = number;
|
||||||
|
|
||||||
|
interface Memo {
|
||||||
|
id: MemoId;
|
||||||
|
|
||||||
|
creatorId: UserId;
|
||||||
|
createdTs: TimeStamp;
|
||||||
|
updatedTs: TimeStamp;
|
||||||
|
rowStatus: RowStatus;
|
||||||
|
|
||||||
|
content: string;
|
||||||
|
pinned: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemoCreate {
|
||||||
|
content: string;
|
||||||
|
createdTs?: TimeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemoPatch {
|
||||||
|
id: MemoId;
|
||||||
|
content?: string;
|
||||||
|
rowStatus?: RowStatus;
|
||||||
|
}
|
12
web/src/types/modules/resource.d.ts
vendored
Normal file
12
web/src/types/modules/resource.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
type ResourceId = number;
|
||||||
|
|
||||||
|
interface Resource {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
createdTs: TimeStamp;
|
||||||
|
updatedTs: TimeStamp;
|
||||||
|
|
||||||
|
filename: string;
|
||||||
|
type: string;
|
||||||
|
size: string;
|
||||||
|
}
|
12
web/src/types/modules/shortcut.d.ts
vendored
Normal file
12
web/src/types/modules/shortcut.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
type ShortcutId = number;
|
||||||
|
|
||||||
|
interface Shortcut {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
rowStatus: RowStatus;
|
||||||
|
createdTs: TimeStamp;
|
||||||
|
updatedTs: TimeStamp;
|
||||||
|
|
||||||
|
title: string;
|
||||||
|
payload: string;
|
||||||
|
}
|
9
web/src/types/modules/system.d.ts
vendored
Normal file
9
web/src/types/modules/system.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
interface Profile {
|
||||||
|
mode: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SystemStatus {
|
||||||
|
owner: User;
|
||||||
|
profile: Profile;
|
||||||
|
}
|
28
web/src/types/modules/user.d.ts
vendored
Normal file
28
web/src/types/modules/user.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
type UserId = number;
|
||||||
|
type UserRole = "OWNER" | "USER";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: UserId;
|
||||||
|
|
||||||
|
createdTs: TimeStamp;
|
||||||
|
updatedTs: TimeStamp;
|
||||||
|
rowStatus: RowStatus;
|
||||||
|
|
||||||
|
role: UserRole;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
openId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserCreate {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
role: UserRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserPatch {
|
||||||
|
name?: string;
|
||||||
|
password?: string;
|
||||||
|
resetOpenId?: boolean;
|
||||||
|
}
|
5
web/src/types/view.d.ts
vendored
5
web/src/types/view.d.ts
vendored
@ -5,8 +5,3 @@ interface DialogProps {
|
|||||||
interface DialogCallback {
|
interface DialogCallback {
|
||||||
destroy: FunctionType;
|
destroy: FunctionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormattedMemo extends Model.Memo {
|
|
||||||
createdAtStr: string;
|
|
||||||
deletedAtStr?: string;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user