From 226e60810f5fe2c41bce07898a3469a6fdf6fa9d Mon Sep 17 00:00:00 2001 From: Manoj <77336594+manojmalik20@users.noreply.github.com> Date: Thu, 27 May 2021 22:36:04 +0530 Subject: [PATCH] Custom status expiry (#17570) * Added expiry support in custom status APIs (#11) * Added expiry support in custom status APIs Added validation for the duration and expiration time in request body Made enum for the custom status durations * Fixed the bug in expiry validation with dont clear validation * Fixed review comments Converted the durations enum to map Removed extra if-else * Added expiry support in custom status slash command (#17) * Added expiry support in custom status slash command * Added the check for timezone enabled in expiry time in custom status slash command * Review fixes Changed name of calculateExpriryTime to calculateEndOfDay Made function SetDefaultEmoji for settting default emoji in set custom status API * Added support for empty duration in custom status APIs Made one of emoji and text required and duration optional in set custom status API Made default duration dont clear in both API and slash command * Changed value of ExpiresAt field in custom status slash command * Code refactoring Combined SetDefaults and TrimMessage into 1 function PreSave Refactored isExpirationTimeValid function * Used model variables instead of new variables in custom status slash command * Modified behaviour of set custom status APIs (#19) Removed dont_clear from validCustomStatusDuration map Added logic to set duration custom date/time if only expires_at is specified in the body Made function AreDurationAndExpirationTimeValid in custom status model * Trigger CI build --- api4/status.go | 4 +- app/slashcommands/command_custom_status.go | 7 +--- .../command_custom_status_test.go | 6 +-- model/custom_status.go | 41 +++++++++++++++++-- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/api4/status.go b/api4/status.go index 6f717bb26b..dc57908811 100644 --- a/api4/status.go +++ b/api4/status.go @@ -127,7 +127,7 @@ func updateUserCustomStatus(c *Context, w http.ResponseWriter, r *http.Request) } customStatus := model.CustomStatusFromJson(r.Body) - if customStatus == nil || (customStatus.Text == "" && customStatus.Emoji == "") { + if customStatus == nil || (customStatus.Emoji == "" && customStatus.Text == "") || !customStatus.AreDurationAndExpirationTimeValid() { c.SetInvalidParam("custom_status") return } @@ -137,7 +137,7 @@ func updateUserCustomStatus(c *Context, w http.ResponseWriter, r *http.Request) return } - customStatus.TrimMessage() + customStatus.PreSave() err := c.App.SetCustomStatus(c.Params.UserId, customStatus) if err != nil { c.Err = err diff --git a/app/slashcommands/command_custom_status.go b/app/slashcommands/command_custom_status.go index 926812ad47..4f8bab8be4 100644 --- a/app/slashcommands/command_custom_status.go +++ b/app/slashcommands/command_custom_status.go @@ -21,8 +21,6 @@ type CustomStatusProvider struct { const ( CmdCustomStatus = app.CmdCustomStatusTrigger CmdCustomStatusClear = "clear" - - DefaultCustomStatusEmoji = "speech_balloon" ) func init() { @@ -62,6 +60,7 @@ func (*CustomStatusProvider) DoCommand(a *app.App, c *request.Context, args *mod } customStatus := GetCustomStatus(message) + customStatus.PreSave() if err := a.SetCustomStatus(args.UserId, customStatus); err != nil { mlog.Debug(err.Error()) return &model.CommandResponse{Text: args.T("api.command_custom_status.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} @@ -78,7 +77,7 @@ func (*CustomStatusProvider) DoCommand(a *app.App, c *request.Context, args *mod func GetCustomStatus(message string) *model.CustomStatus { customStatus := &model.CustomStatus{ - Emoji: DefaultCustomStatusEmoji, + Emoji: model.DefaultCustomStatusEmoji, Text: message, } @@ -87,7 +86,6 @@ func GetCustomStatus(message string) *model.CustomStatus { // emoji found at starting index customStatus.Emoji = message[firstEmojiLocations[0]+1 : firstEmojiLocations[1]-1] customStatus.Text = strings.TrimSpace(message[firstEmojiLocations[1]:]) - customStatus.TrimMessage() return customStatus } @@ -117,7 +115,6 @@ func GetCustomStatus(message string) *model.CustomStatus { customStatus.Text = strings.TrimSpace(textString) } - customStatus.TrimMessage() return customStatus } diff --git a/app/slashcommands/command_custom_status_test.go b/app/slashcommands/command_custom_status_test.go index 4e99dad52e..c2b05a0eb7 100644 --- a/app/slashcommands/command_custom_status_test.go +++ b/app/slashcommands/command_custom_status_test.go @@ -11,14 +11,14 @@ import ( func TestGetCustomStatus(t *testing.T) { for msg, expected := range map[string]model.CustomStatus{ - "": {Emoji: DefaultCustomStatusEmoji, Text: ""}, - "Hey": {Emoji: DefaultCustomStatusEmoji, Text: "Hey"}, + "": {Emoji: model.DefaultCustomStatusEmoji, Text: ""}, + "Hey": {Emoji: model.DefaultCustomStatusEmoji, Text: "Hey"}, ":cactus: Hurt": {Emoji: "cactus", Text: "Hurt"}, "👅": {Emoji: "tongue", Text: ""}, "👅 Eating": {Emoji: "tongue", Text: "Eating"}, "💪🏻 Working out": {Emoji: "muscle_light_skin_tone", Text: "Working out"}, "👙 Swimming": {Emoji: "bikini", Text: "Swimming"}, - "👙Swimming": {Emoji: DefaultCustomStatusEmoji, Text: "👙Swimming"}, + "👙Swimming": {Emoji: model.DefaultCustomStatusEmoji, Text: "👙Swimming"}, "👍🏿 Okay": {Emoji: "+1_dark_skin_tone", Text: "Okay"}, "🤴🏾 Dark king": {Emoji: "prince_medium_dark_skin_tone", Text: "Dark king"}, "⛹🏾‍♀️ Playing basketball": {Emoji: "basketball_woman", Text: "Playing basketball"}, diff --git a/model/custom_status.go b/model/custom_status.go index 68e78e4d29..9898ce6c4d 100644 --- a/model/custom_status.go +++ b/model/custom_status.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "time" ) const ( @@ -14,14 +15,34 @@ const ( CustomStatusTextMaxRunes = 100 MaxRecentCustomStatuses = 5 + DefaultCustomStatusEmoji = "speech_balloon" ) -type CustomStatus struct { - Emoji string `json:"emoji"` - Text string `json:"text"` +var validCustomStatusDuration = map[string]bool{ + "thirty_minutes": true, + "one_hour": true, + "four_hours": true, + "today": true, + "this_week": true, + "date_and_time": true, } -func (cs *CustomStatus) TrimMessage() { +type CustomStatus struct { + Emoji string `json:"emoji"` + Text string `json:"text"` + Duration string `json:"duration"` + ExpiresAt time.Time `json:"expires_at"` +} + +func (cs *CustomStatus) PreSave() { + if cs.Emoji == "" { + cs.Emoji = DefaultCustomStatusEmoji + } + + if cs.Duration == "" && !cs.ExpiresAt.Before(time.Now()) { + cs.Duration = "date_and_time" + } + runes := []rune(cs.Text) if len(runes) > CustomStatusTextMaxRunes { cs.Text = string(runes[:CustomStatusTextMaxRunes]) @@ -34,6 +55,18 @@ func (cs *CustomStatus) ToJson() string { return string(b) } +func (cs *CustomStatus) AreDurationAndExpirationTimeValid() bool { + if cs.Duration == "" && (cs.ExpiresAt.IsZero() || !cs.ExpiresAt.Before(time.Now())) { + return true + } + + if validCustomStatusDuration[cs.Duration] && !cs.ExpiresAt.Before(time.Now()) { + return true + } + + return false +} + func CustomStatusFromJson(data io.Reader) *CustomStatus { var cs *CustomStatus _ = json.NewDecoder(data).Decode(&cs)