diff --git a/web/package.json b/web/package.json index 08459b36..2df30275 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,7 @@ "dependencies": { "@reduxjs/toolkit": "^1.8.1", "axios": "^0.27.2", + "dayjs": "^1.11.3", "lodash-es": "^4.17.21", "qs": "^6.11.0", "react": "^18.1.0", diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index f5d1d602..f475cfc0 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -1,7 +1,8 @@ import { memo, useEffect, useRef, useState } from "react"; 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 * as utils from "../helpers/utils"; import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked"; import { editorStateService, locationService, memoService, userService } from "../services"; import Only from "./common/OnlyWhen"; @@ -11,25 +12,33 @@ import showMemoCardDialog from "./MemoCardDialog"; import showShareMemoImageDialog from "./ShareMemoImageDialog"; import "../less/memo.less"; +dayjs.extend(relativeTime); + const MAX_MEMO_CONTAINER_HEIGHT = 384; +type ExpandButtonStatus = -1 | 0 | 1; + interface Props { memo: Memo; } -type ExpandButtonStatus = -1 | 0 | 1; - interface State { + createdAtStr: string; expandButtonStatus: ExpandButtonStatus; } +export const getFormatedMemoCreatedAtStr = (createdTs: number): string => { + if (Date.now() - createdTs < 1000 * 60 * 60 * 24) { + return dayjs(createdTs).fromNow(); + } else { + return dayjs(createdTs).format("YYYY/MM/DD HH:mm:ss"); + } +}; + const Memo: React.FC = (props: Props) => { - const { memo: propsMemo } = props; - const memo = { - ...propsMemo, - createdAtStr: utils.getDateTimeString(propsMemo.createdTs), - }; + const memo = props.memo; const [state, setState] = useState({ + createdAtStr: getFormatedMemoCreatedAtStr(memo.createdTs), expandButtonStatus: -1, }); const memoContainerRef = useRef(null); @@ -46,6 +55,15 @@ const Memo: React.FC = (props: Props) => { expandButtonStatus: 0, }); } + + if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) { + setInterval(() => { + setState({ + ...state, + createdAtStr: dayjs(memo.createdTs).fromNow(), + }); + }, 1000 * 1); + } }, []); const handleShowMemoStoryDialog = () => { @@ -146,7 +164,7 @@ const Memo: React.FC = (props: Props) => { } }; - const handleShowMoreBtnClick = () => { + const handleExpandBtnClick = () => { setState({ ...state, expandButtonStatus: Number(Boolean(!state.expandButtonStatus)) as ExpandButtonStatus, @@ -156,15 +174,15 @@ const Memo: React.FC = (props: Props) => { return (
- - {memo.createdAtStr} +
+ {state.createdAtStr} - PINNED + PINNED - PUBLIC + PUBLIC - +
@@ -173,7 +191,7 @@ const Memo: React.FC = (props: Props) => {
- + {memo.pinned ? "Unpin" : "Pin"}
@@ -206,7 +224,7 @@ const Memo: React.FC = (props: Props) => { >
{state.expandButtonStatus !== -1 && (
- + {state.expandButtonStatus === 0 ? "Expand" : "Fold"} diff --git a/web/src/components/MemoCardDialog.tsx b/web/src/components/MemoCardDialog.tsx index 211c847b..4314fa28 100644 --- a/web/src/components/MemoCardDialog.tsx +++ b/web/src/components/MemoCardDialog.tsx @@ -1,7 +1,7 @@ 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 * as utils from "../helpers/utils"; -import { editorStateService, memoService, userService } from "../services"; import { parseHtmlToRawText } from "../helpers/marked"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; @@ -103,6 +103,18 @@ const MemoCardDialog: React.FC = (props: Props) => { editorStateService.setEditMemoWithId(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 visibility = memo.visibility === "PRIVATE" ? "PUBLIC" : "PRIVATE"; await memoService.patchMemo({ @@ -123,8 +135,11 @@ const MemoCardDialog: React.FC = (props: Props) => {
<> +
-
+
{memos.length} MEMO diff --git a/web/src/less/memo.less b/web/src/less/memo.less index 238d6a2a..ccb87879 100644 --- a/web/src/less/memo.less +++ b/web/src/less/memo.less @@ -15,6 +15,19 @@ > .memo-top-wrapper { @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 { @apply text-xs text-gray-400 cursor-pointer; } diff --git a/web/src/less/user-banner.less b/web/src/less/user-banner.less index 7be2b111..8b052a29 100644 --- a/web/src/less/user-banner.less +++ b/web/src/less/user-banner.less @@ -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; > .status-text { diff --git a/web/yarn.lock b/web/yarn.lock index 9fbcc214..b576930e 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -786,6 +786,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" 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: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"