mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: display memo with updated ts (#1760)
This commit is contained in:
@ -34,6 +34,7 @@ type MemoResponse struct {
|
|||||||
UpdatedTs int64 `json:"updatedTs"`
|
UpdatedTs int64 `json:"updatedTs"`
|
||||||
|
|
||||||
// Domain specific fields
|
// Domain specific fields
|
||||||
|
DisplayTs int64 `json:"displayTs"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Visibility Visibility `json:"visibility"`
|
Visibility Visibility `json:"visibility"`
|
||||||
Pinned bool `json:"pinned"`
|
Pinned bool `json:"pinned"`
|
||||||
|
@ -24,6 +24,8 @@ type SystemStatus struct {
|
|||||||
CustomizedProfile CustomizedProfile `json:"customizedProfile"`
|
CustomizedProfile CustomizedProfile `json:"customizedProfile"`
|
||||||
// Storage service ID.
|
// Storage service ID.
|
||||||
StorageServiceID int `json:"storageServiceId"`
|
StorageServiceID int `json:"storageServiceId"`
|
||||||
// Local storage path
|
// Local storage path.
|
||||||
LocalStoragePath string `json:"localStoragePath"`
|
LocalStoragePath string `json:"localStoragePath"`
|
||||||
|
// Memo display with updated timestamp.
|
||||||
|
MemoDisplayWithUpdatedTs bool `json:"memoDisplayWithUpdatedTs"`
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,8 @@ const (
|
|||||||
// SystemSettingOpenAIConfigName is the name of OpenAI config.
|
// SystemSettingOpenAIConfigName is the name of OpenAI config.
|
||||||
SystemSettingOpenAIConfigName SystemSettingName = "openai-config"
|
SystemSettingOpenAIConfigName SystemSettingName = "openai-config"
|
||||||
// SystemSettingTelegramRobotToken is the name of Telegram Robot Token.
|
// 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.
|
// CustomizedProfile is the struct definition for SystemSettingCustomizedProfileName system setting item.
|
||||||
@ -88,6 +89,8 @@ func (key SystemSettingName) String() string {
|
|||||||
return "openai-config"
|
return "openai-config"
|
||||||
case SystemSettingTelegramRobotTokenName:
|
case SystemSettingTelegramRobotTokenName:
|
||||||
return "telegram-robot-token"
|
return "telegram-robot-token"
|
||||||
|
case SystemSettingMemoDisplayWithUpdatedTsName:
|
||||||
|
return "memo-display-with-updated-ts"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -111,43 +114,36 @@ func (upsert SystemSettingUpsert) Validate() error {
|
|||||||
switch settingName := upsert.Name; settingName {
|
switch settingName := upsert.Name; settingName {
|
||||||
case SystemSettingServerIDName:
|
case SystemSettingServerIDName:
|
||||||
return fmt.Errorf("updating %v is not allowed", settingName)
|
return fmt.Errorf("updating %v is not allowed", settingName)
|
||||||
|
|
||||||
case SystemSettingAllowSignUpName:
|
case SystemSettingAllowSignUpName:
|
||||||
var value bool
|
var value bool
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingIgnoreUpgradeName:
|
case SystemSettingIgnoreUpgradeName:
|
||||||
var value bool
|
var value bool
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingDisablePublicMemosName:
|
case SystemSettingDisablePublicMemosName:
|
||||||
var value bool
|
var value bool
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingMaxUploadSizeMiBName:
|
case SystemSettingMaxUploadSizeMiBName:
|
||||||
var value int
|
var value int
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingAdditionalStyleName:
|
case SystemSettingAdditionalStyleName:
|
||||||
var value string
|
var value string
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingAdditionalScriptName:
|
case SystemSettingAdditionalScriptName:
|
||||||
var value string
|
var value string
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingCustomizedProfileName:
|
case SystemSettingCustomizedProfileName:
|
||||||
customizedProfile := CustomizedProfile{
|
customizedProfile := CustomizedProfile{
|
||||||
Name: "memos",
|
Name: "memos",
|
||||||
@ -157,7 +153,6 @@ func (upsert SystemSettingUpsert) Validate() error {
|
|||||||
Appearance: "system",
|
Appearance: "system",
|
||||||
ExternalURL: "",
|
ExternalURL: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &customizedProfile); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &customizedProfile); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
@ -167,26 +162,22 @@ func (upsert SystemSettingUpsert) Validate() error {
|
|||||||
if !slices.Contains(UserSettingAppearanceValue, customizedProfile.Appearance) {
|
if !slices.Contains(UserSettingAppearanceValue, customizedProfile.Appearance) {
|
||||||
return fmt.Errorf(`invalid appearance value for system setting "%v"`, settingName)
|
return fmt.Errorf(`invalid appearance value for system setting "%v"`, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingStorageServiceIDName:
|
case SystemSettingStorageServiceIDName:
|
||||||
value := DatabaseStorage
|
value := DatabaseStorage
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case SystemSettingLocalStoragePathName:
|
case SystemSettingLocalStoragePathName:
|
||||||
value := ""
|
value := ""
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingOpenAIConfigName:
|
case SystemSettingOpenAIConfigName:
|
||||||
value := OpenAIConfig{}
|
value := OpenAIConfig{}
|
||||||
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SystemSettingTelegramRobotTokenName:
|
case SystemSettingTelegramRobotTokenName:
|
||||||
if upsert.Value == "" {
|
if upsert.Value == "" {
|
||||||
return nil
|
return nil
|
||||||
@ -195,11 +186,14 @@ func (upsert SystemSettingUpsert) Validate() error {
|
|||||||
if len(fragments) != 2 {
|
if len(fragments) != 2 {
|
||||||
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
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:
|
default:
|
||||||
return fmt.Errorf("invalid system setting name")
|
return fmt.Errorf("invalid system setting name")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,19 +609,39 @@ func (s *Server) composeMemoMessageToMemoResponse(ctx context.Context, memoMessa
|
|||||||
Pinned: memoMessage.Pinned,
|
Pinned: memoMessage.Pinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compose creator name.
|
||||||
user, err := s.Store.FindUser(ctx, &api.UserFind{
|
user, err := s.Store.FindUser(ctx, &api.UserFind{
|
||||||
ID: &memoResponse.CreatorID,
|
ID: &memoResponse.CreatorID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Nickname != "" {
|
if user.Nickname != "" {
|
||||||
memoResponse.CreatorName = user.Nickname
|
memoResponse.CreatorName = user.Nickname
|
||||||
} else {
|
} else {
|
||||||
memoResponse.CreatorName = user.Username
|
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{}
|
relationList := []*api.MemoRelation{}
|
||||||
for _, relation := range memoMessage.RelationList {
|
for _, relation := range memoMessage.RelationList {
|
||||||
relationList = append(relationList, convertMemoRelationMessageToMemoRelation(relation))
|
relationList = append(relationList, convertMemoRelationMessageToMemoRelation(relation))
|
||||||
|
@ -55,8 +55,9 @@ func (s *Server) registerSystemRoutes(g *echo.Group) {
|
|||||||
Appearance: "system",
|
Appearance: "system",
|
||||||
ExternalURL: "",
|
ExternalURL: "",
|
||||||
},
|
},
|
||||||
StorageServiceID: api.DatabaseStorage,
|
StorageServiceID: api.DatabaseStorage,
|
||||||
LocalStoragePath: "assets/{timestamp}_{filename}",
|
LocalStoragePath: "assets/{timestamp}_{filename}",
|
||||||
|
MemoDisplayWithUpdatedTs: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
systemSettingList, err := s.Store.FindSystemSettingList(ctx, &api.SystemSettingFind{})
|
systemSettingList, err := s.Store.FindSystemSettingList(ctx, &api.SystemSettingFind{})
|
||||||
@ -78,35 +79,28 @@ func (s *Server) registerSystemRoutes(g *echo.Group) {
|
|||||||
switch systemSetting.Name {
|
switch systemSetting.Name {
|
||||||
case api.SystemSettingAllowSignUpName:
|
case api.SystemSettingAllowSignUpName:
|
||||||
systemStatus.AllowSignUp = baseValue.(bool)
|
systemStatus.AllowSignUp = baseValue.(bool)
|
||||||
|
|
||||||
case api.SystemSettingIgnoreUpgradeName:
|
case api.SystemSettingIgnoreUpgradeName:
|
||||||
systemStatus.IgnoreUpgrade = baseValue.(bool)
|
systemStatus.IgnoreUpgrade = baseValue.(bool)
|
||||||
|
|
||||||
case api.SystemSettingDisablePublicMemosName:
|
case api.SystemSettingDisablePublicMemosName:
|
||||||
systemStatus.DisablePublicMemos = baseValue.(bool)
|
systemStatus.DisablePublicMemos = baseValue.(bool)
|
||||||
|
|
||||||
case api.SystemSettingMaxUploadSizeMiBName:
|
case api.SystemSettingMaxUploadSizeMiBName:
|
||||||
systemStatus.MaxUploadSizeMiB = int(baseValue.(float64))
|
systemStatus.MaxUploadSizeMiB = int(baseValue.(float64))
|
||||||
|
|
||||||
case api.SystemSettingAdditionalStyleName:
|
case api.SystemSettingAdditionalStyleName:
|
||||||
systemStatus.AdditionalStyle = baseValue.(string)
|
systemStatus.AdditionalStyle = baseValue.(string)
|
||||||
|
|
||||||
case api.SystemSettingAdditionalScriptName:
|
case api.SystemSettingAdditionalScriptName:
|
||||||
systemStatus.AdditionalScript = baseValue.(string)
|
systemStatus.AdditionalScript = baseValue.(string)
|
||||||
|
|
||||||
case api.SystemSettingCustomizedProfileName:
|
case api.SystemSettingCustomizedProfileName:
|
||||||
customizedProfile := api.CustomizedProfile{}
|
customizedProfile := api.CustomizedProfile{}
|
||||||
if err := json.Unmarshal([]byte(systemSetting.Value), &customizedProfile); err != nil {
|
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)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal system setting customized profile value").SetInternal(err)
|
||||||
}
|
}
|
||||||
systemStatus.CustomizedProfile = customizedProfile
|
systemStatus.CustomizedProfile = customizedProfile
|
||||||
|
|
||||||
case api.SystemSettingStorageServiceIDName:
|
case api.SystemSettingStorageServiceIDName:
|
||||||
systemStatus.StorageServiceID = int(baseValue.(float64))
|
systemStatus.StorageServiceID = int(baseValue.(float64))
|
||||||
|
|
||||||
case api.SystemSettingLocalStoragePathName:
|
case api.SystemSettingLocalStoragePathName:
|
||||||
systemStatus.LocalStoragePath = baseValue.(string)
|
systemStatus.LocalStoragePath = baseValue.(string)
|
||||||
|
case api.SystemSettingMemoDisplayWithUpdatedTsName:
|
||||||
|
systemStatus.MemoDisplayWithUpdatedTs = baseValue.(bool)
|
||||||
default:
|
default:
|
||||||
log.Warn("Unknown system setting name", zap.String("setting name", systemSetting.Name.String()))
|
log.Warn("Unknown system setting name", zap.String("setting name", systemSetting.Name.String()))
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,12 @@ interface Props {
|
|||||||
|
|
||||||
const DailyMemo: React.FC<Props> = (props: Props) => {
|
const DailyMemo: React.FC<Props> = (props: Props) => {
|
||||||
const { memo } = props;
|
const { memo } = props;
|
||||||
const createdTimeStr = getTimeString(memo.createdTs);
|
const displayTimeStr = getTimeString(memo.displayTs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="daily-memo-wrapper">
|
<div className="daily-memo-wrapper">
|
||||||
<div className="time-wrapper">
|
<div className="time-wrapper">
|
||||||
<span className="normal-text">{createdTimeStr}</span>
|
<span className="normal-text">{displayTimeStr}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="memo-container">
|
<div className="memo-container">
|
||||||
<MemoContent content={memo.content} showFull={true} />
|
<MemoContent content={memo.content} showFull={true} />
|
||||||
|
@ -33,7 +33,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const memoCacheStore = useMemoCacheStore();
|
const memoCacheStore = useMemoCacheStore();
|
||||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.createdTs));
|
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.displayTs));
|
||||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const isVisitorMode = userStore.isVisitorMode() || readonly;
|
const isVisitorMode = userStore.isVisitorMode() || readonly;
|
||||||
@ -54,9 +54,9 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let intervalFlag: any = -1;
|
let intervalFlag: any = -1;
|
||||||
if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) {
|
if (Date.now() - memo.displayTs < 1000 * 60 * 60 * 24) {
|
||||||
intervalFlag = setInterval(() => {
|
intervalFlag = setInterval(() => {
|
||||||
setCreatedTimeStr(getRelativeTimeString(memo.createdTs));
|
setCreatedTimeStr(getRelativeTimeString(memo.displayTs));
|
||||||
}, 1000 * 1);
|
}, 1000 * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ const MemoList = () => {
|
|||||||
if (
|
if (
|
||||||
duration &&
|
duration &&
|
||||||
duration.from < duration.to &&
|
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;
|
shouldShow = false;
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ const MemoList = () => {
|
|||||||
const pinnedMemos = shownMemos.filter((m) => m.pinned);
|
const pinnedMemos = shownMemos.filter((m) => m.pinned);
|
||||||
const unpinnedMemos = shownMemos.filter((m) => !m.pinned);
|
const unpinnedMemos = shownMemos.filter((m) => !m.pinned);
|
||||||
const memoSort = (mi: Memo, mj: Memo) => {
|
const memoSort = (mi: Memo, mj: Memo) => {
|
||||||
return mj.createdTs - mi.createdTs;
|
return mj.displayTs - mi.displayTs;
|
||||||
};
|
};
|
||||||
pinnedMemos.sort(memoSort);
|
pinnedMemos.sort(memoSort);
|
||||||
unpinnedMemos.sort(memoSort);
|
unpinnedMemos.sort(memoSort);
|
||||||
@ -168,7 +168,7 @@ const MemoList = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="memo-list-container">
|
<div className="memo-list-container">
|
||||||
{sortedMemos.map((memo) => (
|
{sortedMemos.map((memo) => (
|
||||||
<Memo key={`${memo.id}-${memo.createdTs}`} memo={memo} />
|
<Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} />
|
||||||
))}
|
))}
|
||||||
{isFetching ? (
|
{isFetching ? (
|
||||||
<div className="status-text-container fetching-tip">
|
<div className="status-text-container fetching-tip">
|
||||||
|
@ -17,6 +17,7 @@ interface State {
|
|||||||
additionalStyle: string;
|
additionalStyle: string;
|
||||||
additionalScript: string;
|
additionalScript: string;
|
||||||
maxUploadSizeMiB: number;
|
maxUploadSizeMiB: number;
|
||||||
|
memoDisplayWithUpdatedTs: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SystemSection = () => {
|
const SystemSection = () => {
|
||||||
@ -31,6 +32,7 @@ const SystemSection = () => {
|
|||||||
additionalScript: systemStatus.additionalScript,
|
additionalScript: systemStatus.additionalScript,
|
||||||
disablePublicMemos: systemStatus.disablePublicMemos,
|
disablePublicMemos: systemStatus.disablePublicMemos,
|
||||||
maxUploadSizeMiB: systemStatus.maxUploadSizeMiB,
|
maxUploadSizeMiB: systemStatus.maxUploadSizeMiB,
|
||||||
|
memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs,
|
||||||
});
|
});
|
||||||
const [telegramRobotToken, setTelegramRobotToken] = useState<string>("");
|
const [telegramRobotToken, setTelegramRobotToken] = useState<string>("");
|
||||||
const [openAIConfig, setOpenAIConfig] = useState<OpenAIConfig>({
|
const [openAIConfig, setOpenAIConfig] = useState<OpenAIConfig>({
|
||||||
@ -65,6 +67,7 @@ const SystemSection = () => {
|
|||||||
additionalScript: systemStatus.additionalScript,
|
additionalScript: systemStatus.additionalScript,
|
||||||
disablePublicMemos: systemStatus.disablePublicMemos,
|
disablePublicMemos: systemStatus.disablePublicMemos,
|
||||||
maxUploadSizeMiB: systemStatus.maxUploadSizeMiB,
|
maxUploadSizeMiB: systemStatus.maxUploadSizeMiB,
|
||||||
|
memoDisplayWithUpdatedTs: systemStatus.memoDisplayWithUpdatedTs,
|
||||||
});
|
});
|
||||||
}, [systemStatus]);
|
}, [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<HTMLInputElement>) => {
|
const handleMaxUploadSizeChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||||
// fixes cursor skipping position on mobile
|
// fixes cursor skipping position on mobile
|
||||||
event.target.selectionEnd = event.target.value.length;
|
event.target.selectionEnd = event.target.value.length;
|
||||||
@ -254,6 +269,10 @@ const SystemSection = () => {
|
|||||||
<span className="normal-text">{t("setting.system-section.disable-public-memos")}</span>
|
<span className="normal-text">{t("setting.system-section.disable-public-memos")}</span>
|
||||||
<Switch checked={state.disablePublicMemos} onChange={(event) => handleDisablePublicMemosChanged(event.target.checked)} />
|
<Switch checked={state.disablePublicMemos} onChange={(event) => handleDisablePublicMemosChanged(event.target.checked)} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-label">
|
||||||
|
<span className="normal-text">Display with updated time</span>
|
||||||
|
<Switch checked={state.memoDisplayWithUpdatedTs} onChange={(event) => handleMemoDisplayWithUpdatedTs(event.target.checked)} />
|
||||||
|
</div>
|
||||||
<div className="form-label">
|
<div className="form-label">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<span className="text-sm mr-1">{t("setting.system-section.max-upload-size")}</span>
|
<span className="text-sm mr-1">{t("setting.system-section.max-upload-size")}</span>
|
||||||
|
@ -46,7 +46,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
|||||||
const memoElRef = useRef<HTMLDivElement>(null);
|
const memoElRef = useRef<HTMLDivElement>(null);
|
||||||
const memo = {
|
const memo = {
|
||||||
...propsMemo,
|
...propsMemo,
|
||||||
createdAtStr: getDateTimeString(propsMemo.createdTs),
|
displayTsStr: getDateTimeString(propsMemo.displayTs),
|
||||||
};
|
};
|
||||||
const createdDays = Math.ceil((Date.now() - getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24);
|
const createdDays = Math.ceil((Date.now() - getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24);
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
|||||||
className="w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800"
|
className="w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800"
|
||||||
ref={memoElRef}
|
ref={memoElRef}
|
||||||
>
|
>
|
||||||
<span className="w-full px-6 pt-5 pb-2 text-sm text-gray-500">{memo.createdAtStr}</span>
|
<span className="w-full px-6 pt-5 pb-2 text-sm text-gray-500">{memo.displayTsStr}</span>
|
||||||
<div className="w-full px-6 text-base pb-4">
|
<div className="w-full px-6 text-base pb-4">
|
||||||
<MemoContent content={memo.content} showFull={true} />
|
<MemoContent content={memo.content} showFull={true} />
|
||||||
<MemoResourceListView className="!grid-cols-2" resourceList={memo.resourceList} />
|
<MemoResourceListView className="!grid-cols-2" resourceList={memo.resourceList} />
|
||||||
|
@ -203,9 +203,9 @@ export const checkShouldShowMemo = (memo: Memo, filter: Filter) => {
|
|||||||
}
|
}
|
||||||
} else if (type === "DISPLAY_TIME") {
|
} else if (type === "DISPLAY_TIME") {
|
||||||
if (operator === "BEFORE") {
|
if (operator === "BEFORE") {
|
||||||
return memo.createdTs < getUnixTimeMillis(value);
|
return memo.displayTs < getUnixTimeMillis(value);
|
||||||
} else {
|
} else {
|
||||||
return memo.createdTs >= getUnixTimeMillis(value);
|
return memo.displayTs >= getUnixTimeMillis(value);
|
||||||
}
|
}
|
||||||
} else if (type === "VISIBILITY") {
|
} else if (type === "VISIBILITY") {
|
||||||
let matched = memo.visibility === value;
|
let matched = memo.visibility === value;
|
||||||
|
@ -28,15 +28,15 @@ const DailyReview = () => {
|
|||||||
const currentDate = new Date(currentDateStamp);
|
const currentDate = new Date(currentDateStamp);
|
||||||
const dailyMemos = memos
|
const dailyMemos = memos
|
||||||
.filter((m) => {
|
.filter((m) => {
|
||||||
const createdTimestamp = getTimeStampByDate(m.createdTs);
|
const displayTimestamp = getTimeStampByDate(m.displayTs);
|
||||||
const currentDateStampWithOffset = currentDateStamp + convertToMillis(localSetting);
|
const currentDateStampWithOffset = currentDateStamp + convertToMillis(localSetting);
|
||||||
return (
|
return (
|
||||||
m.rowStatus === "NORMAL" &&
|
m.rowStatus === "NORMAL" &&
|
||||||
createdTimestamp >= currentDateStampWithOffset &&
|
displayTimestamp >= currentDateStampWithOffset &&
|
||||||
createdTimestamp < currentDateStampWithOffset + DAILY_TIMESTAMP
|
displayTimestamp < currentDateStampWithOffset + DAILY_TIMESTAMP
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.sort((a, b) => getTimeStampByDate(a.createdTs) - getTimeStampByDate(b.createdTs));
|
.sort((a, b) => getTimeStampByDate(a.displayTs) - getTimeStampByDate(b.displayTs));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
@ -46,7 +46,7 @@ const DailyReview = () => {
|
|||||||
offset += fetchedMemos.length;
|
offset += fetchedMemos.length;
|
||||||
if (fetchedMemos.length === DEFAULT_MEMO_LIMIT) {
|
if (fetchedMemos.length === DEFAULT_MEMO_LIMIT) {
|
||||||
const lastMemo = last(fetchedMemos);
|
const lastMemo = last(fetchedMemos);
|
||||||
if (lastMemo && lastMemo.createdTs > currentDateStamp) {
|
if (lastMemo && lastMemo.displayTs > currentDateStamp) {
|
||||||
await fetchMoreMemos();
|
await fetchMoreMemos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ const EmbedMemo = () => {
|
|||||||
<main className="w-full max-w-lg mx-auto my-auto shadow px-4 py-4 rounded-lg">
|
<main className="w-full max-w-lg mx-auto my-auto shadow px-4 py-4 rounded-lg">
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
<div className="w-full mb-2 flex flex-row justify-start items-center text-sm text-gray-400 dark:text-gray-300">
|
<div className="w-full mb-2 flex flex-row justify-start items-center text-sm text-gray-400 dark:text-gray-300">
|
||||||
<span>{getDateTimeString(state.memo.createdTs)}</span>
|
<span>{getDateTimeString(state.memo.displayTs)}</span>
|
||||||
<a className="ml-2 hover:underline hover:text-green-600" href={`/u/${state.memo.creatorId}`}>
|
<a className="ml-2 hover:underline hover:text-green-600" href={`/u/${state.memo.creatorId}`}>
|
||||||
@{state.memo.creatorName}
|
@{state.memo.creatorName}
|
||||||
</a>
|
</a>
|
||||||
|
@ -90,7 +90,7 @@ const Explore = () => {
|
|||||||
<main className="relative w-full h-auto flex flex-col justify-start items-start -mt-2">
|
<main className="relative w-full h-auto flex flex-col justify-start items-start -mt-2">
|
||||||
<MemoFilter />
|
<MemoFilter />
|
||||||
{sortedMemos.map((memo) => {
|
{sortedMemos.map((memo) => {
|
||||||
return <Memo key={`${memo.id}-${memo.createdTs}`} memo={memo} readonly={true} />;
|
return <Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} readonly={true} />;
|
||||||
})}
|
})}
|
||||||
{isComplete ? (
|
{isComplete ? (
|
||||||
state.memos.length === 0 ? (
|
state.memos.length === 0 ? (
|
||||||
|
@ -16,6 +16,7 @@ export const initialGlobalState = async () => {
|
|||||||
maxUploadSizeMiB: 0,
|
maxUploadSizeMiB: 0,
|
||||||
additionalStyle: "",
|
additionalStyle: "",
|
||||||
additionalScript: "",
|
additionalScript: "",
|
||||||
|
memoDisplayWithUpdatedTs: false,
|
||||||
customizedProfile: {
|
customizedProfile: {
|
||||||
name: "memos",
|
name: "memos",
|
||||||
logoUrl: "/logo.webp",
|
logoUrl: "/logo.webp",
|
||||||
|
@ -11,6 +11,7 @@ export const convertResponseModelMemo = (memo: Memo): Memo => {
|
|||||||
...memo,
|
...memo,
|
||||||
createdTs: memo.createdTs * 1000,
|
createdTs: memo.createdTs * 1000,
|
||||||
updatedTs: memo.updatedTs * 1000,
|
updatedTs: memo.updatedTs * 1000,
|
||||||
|
displayTs: memo.displayTs * 1000,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ const globalSlice = createSlice({
|
|||||||
disablePublicMemos: false,
|
disablePublicMemos: false,
|
||||||
additionalStyle: "",
|
additionalStyle: "",
|
||||||
additionalScript: "",
|
additionalScript: "",
|
||||||
|
memoDisplayWithUpdatedTs: false,
|
||||||
customizedProfile: {
|
customizedProfile: {
|
||||||
name: "memos",
|
name: "memos",
|
||||||
logoUrl: "/logo.webp",
|
logoUrl: "/logo.webp",
|
||||||
|
1
web/src/types/modules/memo.d.ts
vendored
1
web/src/types/modules/memo.d.ts
vendored
@ -10,6 +10,7 @@ interface Memo {
|
|||||||
updatedTs: TimeStamp;
|
updatedTs: TimeStamp;
|
||||||
rowStatus: RowStatus;
|
rowStatus: RowStatus;
|
||||||
|
|
||||||
|
displayTs: TimeStamp;
|
||||||
content: string;
|
content: string;
|
||||||
visibility: Visibility;
|
visibility: Visibility;
|
||||||
pinned: boolean;
|
pinned: boolean;
|
||||||
|
1
web/src/types/modules/system.d.ts
vendored
1
web/src/types/modules/system.d.ts
vendored
@ -31,6 +31,7 @@ interface SystemStatus {
|
|||||||
customizedProfile: CustomizedProfile;
|
customizedProfile: CustomizedProfile;
|
||||||
storageServiceId: number;
|
storageServiceId: number;
|
||||||
localStoragePath: string;
|
localStoragePath: string;
|
||||||
|
memoDisplayWithUpdatedTs: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SystemSetting {
|
interface SystemSetting {
|
||||||
|
Reference in New Issue
Block a user