From e0e59c583180872b12175c27d1d13a9bf994e624 Mon Sep 17 00:00:00 2001 From: boojack Date: Sun, 28 May 2023 01:50:09 +0800 Subject: [PATCH] feat: display memo with updated ts (#1760) --- api/memo.go | 1 + api/system.go | 4 +++- api/system_setting.go | 24 +++++++------------ server/memo.go | 22 ++++++++++++++++- server/system.go | 16 ++++--------- web/src/components/DailyMemo.tsx | 4 ++-- web/src/components/Memo.tsx | 6 ++--- web/src/components/MemoList.tsx | 6 ++--- web/src/components/Settings/SystemSection.tsx | 19 +++++++++++++++ web/src/components/ShareMemoDialog.tsx | 4 ++-- web/src/helpers/filter.ts | 4 ++-- web/src/pages/DailyReview.tsx | 10 ++++---- web/src/pages/EmbedMemo.tsx | 2 +- web/src/pages/Explore.tsx | 2 +- web/src/store/module/global.ts | 1 + web/src/store/module/memo.ts | 1 + web/src/store/reducer/global.ts | 1 + web/src/types/modules/memo.d.ts | 1 + web/src/types/modules/system.d.ts | 1 + 19 files changed, 82 insertions(+), 47 deletions(-) diff --git a/api/memo.go b/api/memo.go index 579d65ee..71bd7258 100644 --- a/api/memo.go +++ b/api/memo.go @@ -34,6 +34,7 @@ type MemoResponse struct { UpdatedTs int64 `json:"updatedTs"` // Domain specific fields + DisplayTs int64 `json:"displayTs"` Content string `json:"content"` Visibility Visibility `json:"visibility"` Pinned bool `json:"pinned"` diff --git a/api/system.go b/api/system.go index 1c5966b7..a916e832 100644 --- a/api/system.go +++ b/api/system.go @@ -24,6 +24,8 @@ type SystemStatus struct { CustomizedProfile CustomizedProfile `json:"customizedProfile"` // Storage service ID. StorageServiceID int `json:"storageServiceId"` - // Local storage path + // Local storage path. LocalStoragePath string `json:"localStoragePath"` + // Memo display with updated timestamp. + MemoDisplayWithUpdatedTs bool `json:"memoDisplayWithUpdatedTs"` } diff --git a/api/system_setting.go b/api/system_setting.go index 522fda8e..f89351aa 100644 --- a/api/system_setting.go +++ b/api/system_setting.go @@ -36,7 +36,8 @@ const ( // SystemSettingOpenAIConfigName is the name of OpenAI config. SystemSettingOpenAIConfigName SystemSettingName = "openai-config" // SystemSettingTelegramRobotToken is the name of Telegram Robot Token. - SystemSettingTelegramRobotTokenName SystemSettingName = "telegram-robot-token" + SystemSettingTelegramRobotTokenName SystemSettingName = "telegram-robot-token" + SystemSettingMemoDisplayWithUpdatedTsName SystemSettingName = "memo-display-with-updated-ts" ) // CustomizedProfile is the struct definition for SystemSettingCustomizedProfileName system setting item. @@ -88,6 +89,8 @@ func (key SystemSettingName) String() string { return "openai-config" case SystemSettingTelegramRobotTokenName: return "telegram-robot-token" + case SystemSettingMemoDisplayWithUpdatedTsName: + return "memo-display-with-updated-ts" } return "" } @@ -111,43 +114,36 @@ func (upsert SystemSettingUpsert) Validate() error { switch settingName := upsert.Name; settingName { case SystemSettingServerIDName: return fmt.Errorf("updating %v is not allowed", settingName) - case SystemSettingAllowSignUpName: var value bool if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingIgnoreUpgradeName: var value bool if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingDisablePublicMemosName: var value bool if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingMaxUploadSizeMiBName: var value int if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingAdditionalStyleName: var value string if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingAdditionalScriptName: var value string if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingCustomizedProfileName: customizedProfile := CustomizedProfile{ Name: "memos", @@ -157,7 +153,6 @@ func (upsert SystemSettingUpsert) Validate() error { Appearance: "system", ExternalURL: "", } - if err := json.Unmarshal([]byte(upsert.Value), &customizedProfile); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } @@ -167,26 +162,22 @@ func (upsert SystemSettingUpsert) Validate() error { if !slices.Contains(UserSettingAppearanceValue, customizedProfile.Appearance) { return fmt.Errorf(`invalid appearance value for system setting "%v"`, settingName) } - case SystemSettingStorageServiceIDName: value := DatabaseStorage if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } return nil - case SystemSettingLocalStoragePathName: value := "" if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingOpenAIConfigName: value := OpenAIConfig{} if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - case SystemSettingTelegramRobotTokenName: if upsert.Value == "" { return nil @@ -195,11 +186,14 @@ func (upsert SystemSettingUpsert) Validate() error { if len(fragments) != 2 { return fmt.Errorf(systemSettingUnmarshalError, settingName) } - + case SystemSettingMemoDisplayWithUpdatedTsName: + var value bool + if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil { + return fmt.Errorf(systemSettingUnmarshalError, settingName) + } default: return fmt.Errorf("invalid system setting name") } - return nil } diff --git a/server/memo.go b/server/memo.go index 41fc763f..4920246d 100644 --- a/server/memo.go +++ b/server/memo.go @@ -609,19 +609,39 @@ func (s *Server) composeMemoMessageToMemoResponse(ctx context.Context, memoMessa Pinned: memoMessage.Pinned, } + // Compose creator name. user, err := s.Store.FindUser(ctx, &api.UserFind{ ID: &memoResponse.CreatorID, }) if err != nil { return nil, err } - if user.Nickname != "" { memoResponse.CreatorName = user.Nickname } else { memoResponse.CreatorName = user.Username } + // Compose display ts. + memoResponse.DisplayTs = memoResponse.CreatedTs + // Find memo display with updated ts setting. + memoDisplayWithUpdatedTsSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{ + Name: api.SystemSettingMemoDisplayWithUpdatedTsName, + }) + if err != nil && common.ErrorCode(err) != common.NotFound { + return nil, errors.Wrap(err, "failed to find system setting") + } + if memoDisplayWithUpdatedTsSetting != nil { + memoDisplayWithUpdatedTs := false + err = json.Unmarshal([]byte(memoDisplayWithUpdatedTsSetting.Value), &memoDisplayWithUpdatedTs) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal system setting value") + } + if memoDisplayWithUpdatedTs { + memoResponse.DisplayTs = memoResponse.UpdatedTs + } + } + relationList := []*api.MemoRelation{} for _, relation := range memoMessage.RelationList { relationList = append(relationList, convertMemoRelationMessageToMemoRelation(relation)) diff --git a/server/system.go b/server/system.go index 76eee0d4..52c0868f 100644 --- a/server/system.go +++ b/server/system.go @@ -55,8 +55,9 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { Appearance: "system", ExternalURL: "", }, - StorageServiceID: api.DatabaseStorage, - LocalStoragePath: "assets/{timestamp}_{filename}", + StorageServiceID: api.DatabaseStorage, + LocalStoragePath: "assets/{timestamp}_{filename}", + MemoDisplayWithUpdatedTs: false, } systemSettingList, err := s.Store.FindSystemSettingList(ctx, &api.SystemSettingFind{}) @@ -78,35 +79,28 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { switch systemSetting.Name { case api.SystemSettingAllowSignUpName: systemStatus.AllowSignUp = baseValue.(bool) - case api.SystemSettingIgnoreUpgradeName: systemStatus.IgnoreUpgrade = baseValue.(bool) - case api.SystemSettingDisablePublicMemosName: systemStatus.DisablePublicMemos = baseValue.(bool) - case api.SystemSettingMaxUploadSizeMiBName: systemStatus.MaxUploadSizeMiB = int(baseValue.(float64)) - case api.SystemSettingAdditionalStyleName: systemStatus.AdditionalStyle = baseValue.(string) - case api.SystemSettingAdditionalScriptName: systemStatus.AdditionalScript = baseValue.(string) - case api.SystemSettingCustomizedProfileName: customizedProfile := api.CustomizedProfile{} if err := json.Unmarshal([]byte(systemSetting.Value), &customizedProfile); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal system setting customized profile value").SetInternal(err) } systemStatus.CustomizedProfile = customizedProfile - case api.SystemSettingStorageServiceIDName: systemStatus.StorageServiceID = int(baseValue.(float64)) - case api.SystemSettingLocalStoragePathName: systemStatus.LocalStoragePath = baseValue.(string) - + case api.SystemSettingMemoDisplayWithUpdatedTsName: + systemStatus.MemoDisplayWithUpdatedTs = baseValue.(bool) default: log.Warn("Unknown system setting name", zap.String("setting name", systemSetting.Name.String())) } diff --git a/web/src/components/DailyMemo.tsx b/web/src/components/DailyMemo.tsx index 450d940d..66e3286c 100644 --- a/web/src/components/DailyMemo.tsx +++ b/web/src/components/DailyMemo.tsx @@ -9,12 +9,12 @@ interface Props { const DailyMemo: React.FC = (props: Props) => { const { memo } = props; - const createdTimeStr = getTimeString(memo.createdTs); + const displayTimeStr = getTimeString(memo.displayTs); return (
- {createdTimeStr} + {displayTimeStr}
diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index d2cb2bc3..b5c509d3 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -33,7 +33,7 @@ const Memo: React.FC = (props: Props) => { const userStore = useUserStore(); const memoStore = useMemoStore(); const memoCacheStore = useMemoCacheStore(); - const [createdTimeStr, setCreatedTimeStr] = useState(getRelativeTimeString(memo.createdTs)); + const [createdTimeStr, setCreatedTimeStr] = useState(getRelativeTimeString(memo.displayTs)); const [relatedMemoList, setRelatedMemoList] = useState([]); const memoContainerRef = useRef(null); const isVisitorMode = userStore.isVisitorMode() || readonly; @@ -54,9 +54,9 @@ const Memo: React.FC = (props: Props) => { useEffect(() => { let intervalFlag: any = -1; - if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) { + if (Date.now() - memo.displayTs < 1000 * 60 * 60 * 24) { intervalFlag = setInterval(() => { - setCreatedTimeStr(getRelativeTimeString(memo.createdTs)); + setCreatedTimeStr(getRelativeTimeString(memo.displayTs)); }, 1000 * 1); } diff --git a/web/src/components/MemoList.tsx b/web/src/components/MemoList.tsx index eed8b1c5..a0a90de9 100644 --- a/web/src/components/MemoList.tsx +++ b/web/src/components/MemoList.tsx @@ -56,7 +56,7 @@ const MemoList = () => { if ( duration && duration.from < duration.to && - (getTimeStampByDate(memo.createdTs) < duration.from || getTimeStampByDate(memo.createdTs) > duration.to) + (getTimeStampByDate(memo.displayTs) < duration.from || getTimeStampByDate(memo.displayTs) > duration.to) ) { shouldShow = false; } @@ -82,7 +82,7 @@ const MemoList = () => { const pinnedMemos = shownMemos.filter((m) => m.pinned); const unpinnedMemos = shownMemos.filter((m) => !m.pinned); const memoSort = (mi: Memo, mj: Memo) => { - return mj.createdTs - mi.createdTs; + return mj.displayTs - mi.displayTs; }; pinnedMemos.sort(memoSort); unpinnedMemos.sort(memoSort); @@ -168,7 +168,7 @@ const MemoList = () => { return (
{sortedMemos.map((memo) => ( - + ))} {isFetching ? (
diff --git a/web/src/components/Settings/SystemSection.tsx b/web/src/components/Settings/SystemSection.tsx index 9b29421b..eb1666d5 100644 --- a/web/src/components/Settings/SystemSection.tsx +++ b/web/src/components/Settings/SystemSection.tsx @@ -17,6 +17,7 @@ interface State { additionalStyle: string; additionalScript: string; maxUploadSizeMiB: number; + memoDisplayWithUpdatedTs: boolean; } const SystemSection = () => { @@ -31,6 +32,7 @@ const SystemSection = () => { additionalScript: systemStatus.additionalScript, disablePublicMemos: systemStatus.disablePublicMemos, maxUploadSizeMiB: systemStatus.maxUploadSizeMiB, + memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs, }); const [telegramRobotToken, setTelegramRobotToken] = useState(""); const [openAIConfig, setOpenAIConfig] = useState({ @@ -65,6 +67,7 @@ const SystemSection = () => { additionalScript: systemStatus.additionalScript, disablePublicMemos: systemStatus.disablePublicMemos, maxUploadSizeMiB: systemStatus.maxUploadSizeMiB, + memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs, }); }, [systemStatus]); @@ -202,6 +205,18 @@ const SystemSection = () => { }); }; + const handleMemoDisplayWithUpdatedTs = async (value: boolean) => { + setState({ + ...state, + memoDisplayWithUpdatedTs: value, + }); + globalStore.setSystemStatus({ disablePublicMemos: value }); + await api.upsertSystemSetting({ + name: "memo-display-with-updated-ts", + value: JSON.stringify(value), + }); + }; + const handleMaxUploadSizeChanged = async (event: React.FocusEvent) => { // fixes cursor skipping position on mobile event.target.selectionEnd = event.target.value.length; @@ -254,6 +269,10 @@ const SystemSection = () => { {t("setting.system-section.disable-public-memos")} handleDisablePublicMemosChanged(event.target.checked)} />
+
+ Display with updated time + handleMemoDisplayWithUpdatedTs(event.target.checked)} /> +
{t("setting.system-section.max-upload-size")} diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index 7bd099d0..0355515c 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -46,7 +46,7 @@ const ShareMemoDialog: React.FC = (props: Props) => { const memoElRef = useRef(null); const memo = { ...propsMemo, - createdAtStr: getDateTimeString(propsMemo.createdTs), + displayTsStr: getDateTimeString(propsMemo.displayTs), }; const createdDays = Math.ceil((Date.now() - getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24); @@ -174,7 +174,7 @@ const ShareMemoDialog: React.FC = (props: Props) => { className="w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800" ref={memoElRef} > - {memo.createdAtStr} + {memo.displayTsStr}
diff --git a/web/src/helpers/filter.ts b/web/src/helpers/filter.ts index 2c97498c..e3b80ba0 100644 --- a/web/src/helpers/filter.ts +++ b/web/src/helpers/filter.ts @@ -203,9 +203,9 @@ export const checkShouldShowMemo = (memo: Memo, filter: Filter) => { } } else if (type === "DISPLAY_TIME") { if (operator === "BEFORE") { - return memo.createdTs < getUnixTimeMillis(value); + return memo.displayTs < getUnixTimeMillis(value); } else { - return memo.createdTs >= getUnixTimeMillis(value); + return memo.displayTs >= getUnixTimeMillis(value); } } else if (type === "VISIBILITY") { let matched = memo.visibility === value; diff --git a/web/src/pages/DailyReview.tsx b/web/src/pages/DailyReview.tsx index 70cabec9..c2616c38 100644 --- a/web/src/pages/DailyReview.tsx +++ b/web/src/pages/DailyReview.tsx @@ -28,15 +28,15 @@ const DailyReview = () => { const currentDate = new Date(currentDateStamp); const dailyMemos = memos .filter((m) => { - const createdTimestamp = getTimeStampByDate(m.createdTs); + const displayTimestamp = getTimeStampByDate(m.displayTs); const currentDateStampWithOffset = currentDateStamp + convertToMillis(localSetting); return ( m.rowStatus === "NORMAL" && - createdTimestamp >= currentDateStampWithOffset && - createdTimestamp < currentDateStampWithOffset + DAILY_TIMESTAMP + displayTimestamp >= currentDateStampWithOffset && + displayTimestamp < currentDateStampWithOffset + DAILY_TIMESTAMP ); }) - .sort((a, b) => getTimeStampByDate(a.createdTs) - getTimeStampByDate(b.createdTs)); + .sort((a, b) => getTimeStampByDate(a.displayTs) - getTimeStampByDate(b.displayTs)); useEffect(() => { let offset = 0; @@ -46,7 +46,7 @@ const DailyReview = () => { offset += fetchedMemos.length; if (fetchedMemos.length === DEFAULT_MEMO_LIMIT) { const lastMemo = last(fetchedMemos); - if (lastMemo && lastMemo.createdTs > currentDateStamp) { + if (lastMemo && lastMemo.displayTs > currentDateStamp) { await fetchMoreMemos(); } } diff --git a/web/src/pages/EmbedMemo.tsx b/web/src/pages/EmbedMemo.tsx index e544e8fb..a6c4d964 100644 --- a/web/src/pages/EmbedMemo.tsx +++ b/web/src/pages/EmbedMemo.tsx @@ -45,7 +45,7 @@ const EmbedMemo = () => {
- {getDateTimeString(state.memo.createdTs)} + {getDateTimeString(state.memo.displayTs)} @{state.memo.creatorName} diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 9bed3067..caedd9d2 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -90,7 +90,7 @@ const Explore = () => {
{sortedMemos.map((memo) => { - return ; + return ; })} {isComplete ? ( state.memos.length === 0 ? ( diff --git a/web/src/store/module/global.ts b/web/src/store/module/global.ts index 38c4866f..86a7f189 100644 --- a/web/src/store/module/global.ts +++ b/web/src/store/module/global.ts @@ -16,6 +16,7 @@ export const initialGlobalState = async () => { maxUploadSizeMiB: 0, additionalStyle: "", additionalScript: "", + memoDisplayWithUpdatedTs: false, customizedProfile: { name: "memos", logoUrl: "/logo.webp", diff --git a/web/src/store/module/memo.ts b/web/src/store/module/memo.ts index 6252217c..c37153d3 100644 --- a/web/src/store/module/memo.ts +++ b/web/src/store/module/memo.ts @@ -11,6 +11,7 @@ export const convertResponseModelMemo = (memo: Memo): Memo => { ...memo, createdTs: memo.createdTs * 1000, updatedTs: memo.updatedTs * 1000, + displayTs: memo.displayTs * 1000, }; }; diff --git a/web/src/store/reducer/global.ts b/web/src/store/reducer/global.ts index bd201f78..e0b6e1a0 100644 --- a/web/src/store/reducer/global.ts +++ b/web/src/store/reducer/global.ts @@ -23,6 +23,7 @@ const globalSlice = createSlice({ disablePublicMemos: false, additionalStyle: "", additionalScript: "", + memoDisplayWithUpdatedTs: false, customizedProfile: { name: "memos", logoUrl: "/logo.webp", diff --git a/web/src/types/modules/memo.d.ts b/web/src/types/modules/memo.d.ts index 6af5fe6d..b3f95c1c 100644 --- a/web/src/types/modules/memo.d.ts +++ b/web/src/types/modules/memo.d.ts @@ -10,6 +10,7 @@ interface Memo { updatedTs: TimeStamp; rowStatus: RowStatus; + displayTs: TimeStamp; content: string; visibility: Visibility; pinned: boolean; diff --git a/web/src/types/modules/system.d.ts b/web/src/types/modules/system.d.ts index 3e620c6b..fe942163 100644 --- a/web/src/types/modules/system.d.ts +++ b/web/src/types/modules/system.d.ts @@ -31,6 +31,7 @@ interface SystemStatus { customizedProfile: CustomizedProfile; storageServiceId: number; localStoragePath: string; + memoDisplayWithUpdatedTs: boolean; } interface SystemSetting {