mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: add dayjs
to parse datetime
This commit is contained in:
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^1.8.1",
|
"@reduxjs/toolkit": "^1.8.1",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"dayjs": "^1.11.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"qs": "^6.11.0",
|
"qs": "^6.11.0",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { memo, useEffect, useRef, useState } from "react";
|
import { memo, useEffect, useRef, useState } from "react";
|
||||||
import { escape, indexOf } from "lodash-es";
|
import { escape, indexOf } from "lodash-es";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts";
|
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||||
import * as utils from "../helpers/utils";
|
|
||||||
import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked";
|
import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked";
|
||||||
import { editorStateService, locationService, memoService, userService } from "../services";
|
import { editorStateService, locationService, memoService, userService } from "../services";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
@ -11,25 +12,33 @@ import showMemoCardDialog from "./MemoCardDialog";
|
|||||||
import showShareMemoImageDialog from "./ShareMemoImageDialog";
|
import showShareMemoImageDialog from "./ShareMemoImageDialog";
|
||||||
import "../less/memo.less";
|
import "../less/memo.less";
|
||||||
|
|
||||||
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
const MAX_MEMO_CONTAINER_HEIGHT = 384;
|
const MAX_MEMO_CONTAINER_HEIGHT = 384;
|
||||||
|
|
||||||
|
type ExpandButtonStatus = -1 | 0 | 1;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
memo: Memo;
|
memo: Memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpandButtonStatus = -1 | 0 | 1;
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
createdAtStr: string;
|
||||||
expandButtonStatus: ExpandButtonStatus;
|
expandButtonStatus: ExpandButtonStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Memo: React.FC<Props> = (props: Props) => {
|
export const getFormatedMemoCreatedAtStr = (createdTs: number): string => {
|
||||||
const { memo: propsMemo } = props;
|
if (Date.now() - createdTs < 1000 * 60 * 60 * 24) {
|
||||||
const memo = {
|
return dayjs(createdTs).fromNow();
|
||||||
...propsMemo,
|
} else {
|
||||||
createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
|
return dayjs(createdTs).format("YYYY/MM/DD HH:mm:ss");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Memo: React.FC<Props> = (props: Props) => {
|
||||||
|
const memo = props.memo;
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
|
createdAtStr: getFormatedMemoCreatedAtStr(memo.createdTs),
|
||||||
expandButtonStatus: -1,
|
expandButtonStatus: -1,
|
||||||
});
|
});
|
||||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@ -46,6 +55,15 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
expandButtonStatus: 0,
|
expandButtonStatus: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) {
|
||||||
|
setInterval(() => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
createdAtStr: dayjs(memo.createdTs).fromNow(),
|
||||||
|
});
|
||||||
|
}, 1000 * 1);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleShowMemoStoryDialog = () => {
|
const handleShowMemoStoryDialog = () => {
|
||||||
@ -146,7 +164,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShowMoreBtnClick = () => {
|
const handleExpandBtnClick = () => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
expandButtonStatus: Number(Boolean(!state.expandButtonStatus)) as ExpandButtonStatus,
|
expandButtonStatus: Number(Boolean(!state.expandButtonStatus)) as ExpandButtonStatus,
|
||||||
@ -156,15 +174,15 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`}>
|
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`}>
|
||||||
<div className="memo-top-wrapper">
|
<div className="memo-top-wrapper">
|
||||||
<span className="time-text" onClick={handleShowMemoStoryDialog}>
|
<div className="status-text-container" onClick={handleShowMemoStoryDialog}>
|
||||||
{memo.createdAtStr}
|
<span className="time-text">{state.createdAtStr}</span>
|
||||||
<Only when={memo.pinned}>
|
<Only when={memo.pinned}>
|
||||||
<span className="ml-2">PINNED</span>
|
<span className="status-text">PINNED</span>
|
||||||
</Only>
|
</Only>
|
||||||
<Only when={memo.visibility === "PUBLIC"}>
|
<Only when={memo.visibility === "PUBLIC"}>
|
||||||
<span className="ml-2">PUBLIC</span>
|
<span className="status-text">PUBLIC</span>
|
||||||
</Only>
|
</Only>
|
||||||
</span>
|
</div>
|
||||||
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
|
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
|
||||||
<span className="btn more-action-btn">
|
<span className="btn more-action-btn">
|
||||||
<img className="icon-img" src="/icons/more.svg" />
|
<img className="icon-img" src="/icons/more.svg" />
|
||||||
@ -173,7 +191,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="more-action-btns-container">
|
<div className="more-action-btns-container">
|
||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<div className="btn" onClick={handleTogglePinMemoBtnClick}>
|
<div className="btn" onClick={handleTogglePinMemoBtnClick}>
|
||||||
<img className="icon-img" src="/icons/pin.svg" alt="" />
|
<img className="icon-img" src={`/icons/${memo.pinned ? "pinned" : "pin"}.svg`} />
|
||||||
<span className="tip-text">{memo.pinned ? "Unpin" : "Pin"}</span>
|
<span className="tip-text">{memo.pinned ? "Unpin" : "Pin"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="btn" onClick={handleEditMemoClick}>
|
<div className="btn" onClick={handleEditMemoClick}>
|
||||||
@ -206,7 +224,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
></div>
|
></div>
|
||||||
{state.expandButtonStatus !== -1 && (
|
{state.expandButtonStatus !== -1 && (
|
||||||
<div className="expand-btn-container">
|
<div className="expand-btn-container">
|
||||||
<span className={`btn ${state.expandButtonStatus === 0 ? "expand-btn" : "fold-btn"}`} onClick={handleShowMoreBtnClick}>
|
<span className={`btn ${state.expandButtonStatus === 0 ? "expand-btn" : "fold-btn"}`} onClick={handleExpandBtnClick}>
|
||||||
{state.expandButtonStatus === 0 ? "Expand" : "Fold"}
|
{state.expandButtonStatus === 0 ? "Expand" : "Fold"}
|
||||||
<img className="icon-img" src="/icons/arrow-right.svg" alt="" />
|
<img className="icon-img" src="/icons/arrow-right.svg" alt="" />
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
|
import { editorStateService, memoService, userService } from "../services";
|
||||||
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { editorStateService, memoService, userService } from "../services";
|
|
||||||
import { parseHtmlToRawText } from "../helpers/marked";
|
import { parseHtmlToRawText } from "../helpers/marked";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
@ -103,6 +103,18 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
editorStateService.setEditMemoWithId(memo.id);
|
editorStateService.setEditMemoWithId(memo.id);
|
||||||
}, [memo.id]);
|
}, [memo.id]);
|
||||||
|
|
||||||
|
const handlePinClick = async () => {
|
||||||
|
if (memo.pinned) {
|
||||||
|
await memoService.unpinMemo(memo.id);
|
||||||
|
} else {
|
||||||
|
await memoService.pinMemo(memo.id);
|
||||||
|
}
|
||||||
|
setMemo({
|
||||||
|
...memo,
|
||||||
|
pinned: !memo.pinned,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleVisibilityClick = async () => {
|
const handleVisibilityClick = async () => {
|
||||||
const visibility = memo.visibility === "PRIVATE" ? "PUBLIC" : "PRIVATE";
|
const visibility = memo.visibility === "PRIVATE" ? "PUBLIC" : "PRIVATE";
|
||||||
await memoService.patchMemo({
|
await memoService.patchMemo({
|
||||||
@ -123,8 +135,11 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="btns-container">
|
<div className="btns-container">
|
||||||
<Only when={!userService.isVisitorMode()}>
|
<Only when={!userService.isVisitorMode()}>
|
||||||
<>
|
<>
|
||||||
|
<button className="btn edit-btn" onClick={handlePinClick}>
|
||||||
|
<img className="icon-img" src={`/icons/${memo.pinned ? "pinned" : "pin"}.svg`} />
|
||||||
|
</button>
|
||||||
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
||||||
<img className={`icon-img ${memo.visibility === "PRIVATE" ? "opacity-30" : ""}`} src="/icons/visibility.svg" />
|
<img className="icon-img" src={`/icons/${memo.visibility === "PRIVATE" ? "invisibility" : "visibility"}.svg`} />
|
||||||
</button>
|
</button>
|
||||||
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
||||||
<img className="icon-img" src="/icons/edit.svg" />
|
<img className="icon-img" src="/icons/edit.svg" />
|
||||||
|
@ -49,7 +49,7 @@ const UserBanner: React.FC<Props> = () => {
|
|||||||
</button>
|
</button>
|
||||||
<MenuBtnsPopup shownStatus={shouldShowPopupBtns} setShownStatus={setShouldShowPopupBtns} />
|
<MenuBtnsPopup shownStatus={shouldShowPopupBtns} setShownStatus={setShouldShowPopupBtns} />
|
||||||
</div>
|
</div>
|
||||||
<div className="status-text-container">
|
<div className="amount-text-container">
|
||||||
<div className="status-text memos-text">
|
<div className="status-text memos-text">
|
||||||
<span className="amount-text">{memos.length}</span>
|
<span className="amount-text">{memos.length}</span>
|
||||||
<span className="type-text">MEMO</span>
|
<span className="type-text">MEMO</span>
|
||||||
|
@ -15,6 +15,19 @@
|
|||||||
> .memo-top-wrapper {
|
> .memo-top-wrapper {
|
||||||
@apply flex flex-row justify-between items-center w-full h-6 mb-1;
|
@apply flex flex-row justify-between items-center w-full h-6 mb-1;
|
||||||
|
|
||||||
|
> .status-text-container {
|
||||||
|
@apply flex flex-row justify-start items-center;
|
||||||
|
|
||||||
|
> .time-text {
|
||||||
|
@apply text-xs text-gray-400 cursor-pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .status-text {
|
||||||
|
@apply text-xs cursor-pointer ml-2 rounded border border-green-600 px-1 text-green-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .time-text {
|
> .time-text {
|
||||||
@apply text-xs text-gray-400 cursor-pointer;
|
@apply text-xs text-gray-400 cursor-pointer;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-text-container {
|
.amount-text-container {
|
||||||
@apply flex flex-row justify-between items-start w-full px-6 select-none shrink-0 pb-4;
|
@apply flex flex-row justify-between items-start w-full px-6 select-none shrink-0 pb-4;
|
||||||
|
|
||||||
> .status-text {
|
> .status-text {
|
||||||
|
@ -786,6 +786,11 @@ csstype@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
||||||
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
||||||
|
|
||||||
|
dayjs@^1.11.3:
|
||||||
|
version "1.11.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
|
||||||
|
integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
|
||||||
|
|
||||||
debug@^3.2.6:
|
debug@^3.2.6:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
|
Reference in New Issue
Block a user