5 files changed,
50 insertions(+),
7 deletions(-)
Author:
Olexandr Smirnov
olexsmir@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2025-08-27 16:12:11 +0300
Parent:
3b5e67f
M
e2e/apiv1_notes_test.go
··· 135 135 }, 136 136 }, 137 137 { 138 + name: "burn before expiration, but without expiration time", 139 + inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 140 + Content: e.uuid(), 141 + BurnBeforeExpiration: true, 142 + }, 143 + assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) { 144 + var body errorResponse 145 + e.readBodyAndUnjsonify(r.Body, &body) 146 + 147 + e.Equal(models.ErrNoteCannotBeBurnt.Error(), body.Message) 148 + }, 149 + }, 150 + { 138 151 name: "set password", 139 152 inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 140 153 Content: e.uuid(),
M
internal/models/note.go
··· 18 18 ErrNoteContentIsEmpty = errors.New("note: content is empty") 19 19 ErrNoteSlugIsAlreadyInUse = errors.New("note: slug is already in use") 20 20 ErrNoteSlugIsInvalid = errors.New("note: slug is invalid") 21 - ErrNoteExpired = errors.New("note: expired") 22 - ErrNoteNotFound = errors.New("note: not found") 21 + ErrNoteCannotBeBurnt = errors.New( 22 + "note: cannot be burnt before expiration if expiration time is not provided", 23 + ) 24 + ErrNoteExpired = errors.New("note: expired") 25 + ErrNoteNotFound = errors.New("note: not found") 23 26 ) 24 27 25 28 type Note struct { ··· 40 43 return ErrNoteContentIsEmpty 41 44 } 42 45 43 - if !slugPattern.MatchString(n.Slug) { 46 + if n.Slug != "" && !slugPattern.MatchString(n.Slug) { 44 47 return ErrNoteSlugIsInvalid 45 48 } 46 49 47 50 if n.IsExpired() { 48 51 return ErrNoteExpired 52 + } 53 + 54 + if n.BurnBeforeExpiration && n.ExpiresAt.IsZero() { 55 + return ErrNoteCannotBeBurnt 49 56 } 50 57 51 58 if _, exists := notAllowedSlugs[n.Slug]; exists {
M
internal/models/note_test.go
··· 43 43 } 44 44 assert.EqualError(t, n.Validate(), ErrNoteExpired.Error()) 45 45 }) 46 + t.Run("should fail if burn before expiration is set, and expiration time is not", 47 + func(t *testing.T) { 48 + n := Note{ 49 + Content: "content", 50 + BurnBeforeExpiration: true, 51 + } 52 + 53 + assert.EqualError(t, n.Validate(), ErrNoteCannotBeBurnt.Error()) 54 + }, 55 + ) 56 + t.Run("should not fail if slug is not provided", func(t *testing.T) { 57 + n := Note{Content: "the content"} 58 + assert.NoError(t, n.Validate()) 59 + }) 46 60 t.Run("should fail if slug is empty", func(t *testing.T) { 47 61 n := Note{Content: "the content", Slug: " "} 48 62 assert.EqualError(t, n.Validate(), ErrNoteSlugIsInvalid.Error())
M
internal/transport/http/apiv1/response.go
··· 32 32 // notes 33 33 errors.Is(err, notesrv.ErrNotePasswordNotProvided) || 34 34 errors.Is(err, models.ErrNoteContentIsEmpty) || 35 + errors.Is(err, models.ErrNoteCannotBeBurnt) || 35 36 errors.Is(err, models.ErrNoteSlugIsAlreadyInUse) || 36 37 errors.Is(err, models.ErrNoteSlugIsInvalid) { 37 38 newError(c, http.StatusBadRequest, err.Error())
M
web/src/Pages/Home_.elm
··· 287 287 ] 288 288 , H.div [ A.class "space-y-6" ] 289 289 [ viewExpirationTimeSelector 290 - , viewBurnBeforeExpirationCheckbox 290 + , viewBurnBeforeExpirationCheckbox (isCheckBoxDisabled model.expirationTime) 291 291 ] 292 292 ] 293 293 , H.div [ A.class "flex justify-end" ] ··· 341 341 ] 342 342 343 343 344 -viewBurnBeforeExpirationCheckbox : Html Msg 345 -viewBurnBeforeExpirationCheckbox = 344 +viewBurnBeforeExpirationCheckbox : Bool -> Html Msg 345 +viewBurnBeforeExpirationCheckbox isDisabled = 346 346 H.div [ A.class "space-y-2" ] 347 347 [ H.div [ A.class "flex items-start space-x-3" ] 348 348 [ H.input ··· 350 350 , A.id "burn" 351 351 , A.type_ "checkbox" 352 352 , A.class "mt-1 h-4 w-4 text-black border-gray-300 rounded focus:ring-black focus:ring-2" 353 + , A.disabled isDisabled 353 354 ] 354 355 [] 355 356 , H.div [ A.class "flex-1" ] 356 357 [ H.label [ A.for "burn", A.class "block text-sm font-medium text-gray-700 cursor-pointer" ] 357 - [ H.text "Don't delete note until expiration time, even if it has been read it" ] 358 + [ H.text "Keep the note until its expiration time, even if it has already been read." ] 359 + , H.span [ A.class "block text-sm font-medium text-gray-500 cursor-pointer" ] 360 + [ H.text "Can only be used if expiration time is set" ] 358 361 ] 359 362 ] 360 363 ] 364 + 365 + 366 +isCheckBoxDisabled : Maybe Int -> Bool 367 +isCheckBoxDisabled expirationTime = 368 + expirationTime == Nothing 361 369 362 370 363 371 isFormDisabled : Model -> Bool