Skip to content

Commit

Permalink
feat: marker for edited notes (#4277)
Browse files Browse the repository at this point in the history
  • Loading branch information
mateo-ivc authored Jul 9, 2024
1 parent f30f1dc commit e6e73d2
Show file tree
Hide file tree
Showing 22 changed files with 110 additions and 45 deletions.
15 changes: 7 additions & 8 deletions server/src/api/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package api

import (
"fmt"
"net/http"
"scrumlr.io/server/identifiers"

"github.com/go-chi/render"
"github.com/google/uuid"
"net/http"
"scrumlr.io/server/common"
"scrumlr.io/server/common/dto"
"scrumlr.io/server/identifiers"
)

// createNote creates a new note
Expand Down Expand Up @@ -69,17 +68,17 @@ func (s *Server) getNotes(w http.ResponseWriter, r *http.Request) {

// updateNote updates a note
func (s *Server) updateNote(w http.ResponseWriter, r *http.Request) {
board := r.Context().Value(identifiers.BoardIdentifier).(uuid.UUID)
noteId := r.Context().Value(identifiers.NoteIdentifier).(uuid.UUID)
boardID := r.Context().Value(identifiers.BoardIdentifier).(uuid.UUID)
noteID := r.Context().Value(identifiers.NoteIdentifier).(uuid.UUID)

var body dto.NoteUpdateRequest
if err := render.Decode(r, &body); err != nil {
common.Throw(w, r, common.BadRequestError(err))
return
}

body.ID = noteId
body.Board = board

body.ID = noteID
body.Board = boardID
note, err := s.notes.Update(r.Context(), body)
if err != nil {
common.Throw(w, r, err)
Expand Down
8 changes: 6 additions & 2 deletions server/src/common/dto/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Note struct {
// The text of the note.
Text string `json:"text"`

Edited bool `json:"edited"`

// The position of the note.
Position NotePosition `json:"position"`
}
Expand All @@ -43,6 +45,7 @@ func (n *Note) From(note database.Note) *Note {
Stack: note.Stack,
Rank: note.Rank,
}
n.Edited = note.Edited
return n
}

Expand Down Expand Up @@ -83,8 +86,9 @@ type NoteUpdateRequest struct {
// The position of the note
Position *NotePosition `json:"position"`

ID uuid.UUID `json:"-"`
Board uuid.UUID `json:"-"`
Edited bool `json:"-"`
ID uuid.UUID `json:"-"`
Board uuid.UUID `json:"-"`
}

// NoteDeleteRequest represents the request to delete a note.
Expand Down
2 changes: 1 addition & 1 deletion server/src/common/dto/votings.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type VotingCreateRequest struct {
ShowVotesOfOthers bool `json:"showVotesOfOthers"`
}

// VotingUpdateRequest represents the request to update a voting session.
// VotingUpdateRequest represents the request to u pdate a voting session.
type VotingUpdateRequest struct {
ID uuid.UUID `json:"-"`
Board uuid.UUID `json:"-"`
Expand Down
18 changes: 9 additions & 9 deletions server/src/database/boards.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package database

import (
"context"
"errors"
"scrumlr.io/server/identifiers"
"time"

"github.com/google/uuid"
"github.com/uptrace/bun"
"scrumlr.io/server/common"
"scrumlr.io/server/database/types"
"context"
"errors"
"scrumlr.io/server/identifiers"
"time"

"github.com/google/uuid"
"github.com/uptrace/bun"
"scrumlr.io/server/common"
"scrumlr.io/server/database/types"
)

type Board struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE IF EXISTS notes DROP COLUMN IF EXISTS edited ;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE IF EXISTS notes ADD COLUMN edited BOOL DEFAULT FALSE;
4 changes: 3 additions & 1 deletion server/src/database/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Note struct {
Text string
Stack uuid.NullUUID
Rank int
Edited bool
}

type NoteInsert struct {
Expand All @@ -44,6 +45,7 @@ type NoteUpdate struct {
Board uuid.UUID
Text *string
Position *NoteUpdatePosition `bun:"embed"`
Edited bool
}

func (d *Database) CreateNote(insert NoteInsert) (Note, error) {
Expand Down Expand Up @@ -123,7 +125,7 @@ func (d *Database) UpdateNote(caller uuid.UUID, update NoteUpdate) (Note, error)

func (d *Database) updateNoteText(update NoteUpdate) (Note, error) {
var note Note
_, err := d.db.NewUpdate().Model(&update).Column("text").Where("id = ?", update.ID).Where("board = ?", update.Board).Where("id = ?", update.ID).Returning("*").Exec(common.ContextWithValues(context.Background(), "Database", d, identifiers.BoardIdentifier, update.Board), &note)
_, err := d.db.NewUpdate().Model(&update).Column("text", "edited").Where("id = ?", update.ID).Where("board = ?", update.Board).Where("id = ?", update.ID).Returning("*").Exec(common.ContextWithValues(context.Background(), "Database", d, identifiers.BoardIdentifier, update.Board), &note)
if err != nil {
return note, err
}
Expand Down
1 change: 1 addition & 0 deletions server/src/services/boards/boards.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (s *BoardService) Create(ctx context.Context, body dto.CreateBoardRequest)
log.Errorw("unable to create board", "owner", body.Owner, "policy", body.AccessPolicy, "error", err)
return nil, err
}

return new(dto.Board).From(b), nil
}

Expand Down
3 changes: 3 additions & 0 deletions server/src/services/notes/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,21 @@ func (s *NoteService) List(ctx context.Context, boardID uuid.UUID) ([]*dto.Note,
func (s *NoteService) Update(ctx context.Context, body dto.NoteUpdateRequest) (*dto.Note, error) {
log := logger.FromContext(ctx)
var positionUpdate *database.NoteUpdatePosition
edited := body.Text != nil
if body.Position != nil {
positionUpdate = &database.NoteUpdatePosition{
Column: body.Position.Column,
Rank: body.Position.Rank,
Stack: body.Position.Stack,
}
}

note, err := s.database.UpdateNote(ctx.Value(identifiers.UserIdentifier).(uuid.UUID), database.NoteUpdate{
ID: body.ID,
Board: body.Board,
Text: body.Text,
Position: positionUpdate,
Edited: edited,
})
if err != nil {
log.Errorw("unable to update note", "error", err, "note", body.ID)
Expand Down
2 changes: 2 additions & 0 deletions server/src/services/notes/notes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,15 @@ func (suite *NoteServiceTestSuite) TestUpdateNote() {
Board: boardID,
Text: &txt,
Position: &posUpdate,
Edited: true,
}).Return(database.Note{}, nil)

_, err := s.Update(ctx, dto.NoteUpdateRequest{
Text: &txt,
ID: noteID,
Board: boardID,
Position: &pos,
Edited: true,
})
assert.NoError(suite.T(), err)
mock.AssertExpectations(suite.T())
Expand Down
23 changes: 19 additions & 4 deletions src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,20 @@ exports[`Column should have correct style show column with correct style 1`] = `
</div>
</header>
<main
class="note__text"
class="note__container"
>
Lorem Ipsum
<div
class="note__text"
>
Lorem Ipsum
</div>
<div
class="note__marker-edited"
>
(
edited
)
</div>
</main>
<footer
class="note__footer"
Expand Down Expand Up @@ -1140,9 +1151,13 @@ exports[`Column should have correct style show column with correct style 1`] = `
</div>
</header>
<main
class="note__text"
class="note__container"
>
Lorem Ipsum
<div
class="note__text"
>
Lorem Ipsum
</div>
</main>
<footer
class="note__footer"
Expand Down
26 changes: 17 additions & 9 deletions src/components/Note/Note.scss
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,14 @@ $note__footer-gap: 42px;
border-radius: 0 $note__indicator-width $note__indicator-width 0;
background: var(--accent-color);
}

.note__text {
.note__container {
margin: $note__text-margin-top 0 $spacing--xs;
min-height: 3 * $line-height--medium;
width: 100%;
color: $color-black;
font-size: $text-size--medium;
letter-spacing: $letter-spacing--medium;
line-height: $line-height--medium;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
word-break: keep-all;
word-wrap: break-word;
white-space: pre-line;
Expand All @@ -140,6 +134,20 @@ $note__footer-gap: 42px;
}
}

.note__text {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;

display: -webkit-box;
}

.note__marker-edited {
color: $color-middle-gray;
font-size: $text--sm;
}

.note__image-wrapper {
height: 72px;
}
Expand All @@ -157,8 +165,8 @@ $note__footer-gap: 42px;
height: $icon--huge;
width: $icon--huge;
position: absolute;
top: calc(50% - ($icon--huge/2));
left: calc(50% - ($icon--huge/2));
top: calc(50% - ($icon--huge / 2));
left: calc(50% - ($icon--huge / 2));
fill: var(--accent-color);
}

Expand Down
9 changes: 6 additions & 3 deletions src/components/Note/Note.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export const Note = (props: NoteProps) => {
// TODO: replace with stack setting from state when implemented. thanks, love u <3
// de-activated in css for now
const stackSetting: "stackOntop" | "stackBetween" | "stackBelow" = "stackBetween";

if (!note) return null;

return (
Expand Down Expand Up @@ -120,10 +119,14 @@ export const Note = (props: NoteProps) => {
/>
</div>
) : (
<main className={classNames("note__text", {"note__text--extended": !showNoteReactions})}>
<NoteTextContent text={note.text} truncate />
<main className={classNames("note__container")}>
<div className={classNames("note__text", {"note__text--extended": !showNoteReactions})}>
<NoteTextContent text={note.text} truncate />
</div>
{note.edited && <div className="note__marker-edited">({t("Note.edited")})</div>}
</main>
)}

<footer className={classNames("note__footer", {"note__footer--collapsed": !showNoteReactions})}>
<NoteReactionList noteId={props.noteId} dimensions={dimensions} colorClassName={props.colorClassName} show={showNoteReactions} />
</footer>
Expand Down
8 changes: 8 additions & 0 deletions src/components/Note/__tests__/Note.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,12 @@ describe("Note", () => {
expect(navigateSpy).toHaveBeenCalledWith(`note/${NOTE_ID}/stack`);
});
});

describe("edited marker", () => {
it("should contain edited marker for edited note", () => {
const board = createBoardData({showAuthors: true});
const {container} = render(createNote(false, {board: board}));
expect(container.getElementsByClassName("note__marker-edited").length).toBeGreaterThan(0);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
line-height: $line-height--medium;
flex: 1;
overflow-y: visible;
justify-content: center;
justify-content: flex-start;
flex-direction: column;

&:has(&--image) {
padding: $spacing--xs;
Expand Down Expand Up @@ -38,10 +39,9 @@
outline: none;
resize: none;
word-wrap: break-word;
padding: 0 4px;
padding: 0 $spacing--xxs;
font-size: $text--md;
line-height: 1.5rem;

&:not(:disabled) {
&:hover,
&:focus-visible {
Expand All @@ -50,6 +50,12 @@
}
}

.note-dialog__marker-edited {
color: $color-middle-gray;
font-size: $text--base;
padding: 0 $spacing--xxs;
}

.note-dialog__note-content--image {
object-fit: contain;
cursor: zoom-in;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const NoteDialogNoteContent: FC<NoteDialogNoteContentProps> = ({noteId, a
const {t} = useTranslation();
const editable = viewer.user.id === authorId || viewer.role === "OWNER" || viewer.role === "MODERATOR";

const note = useAppSelector((state) => state.notes.find((n) => n.id === noteId));

const author = useAppSelector((state) => {
const noteAuthor = state.participants?.others.find((p) => p.user.id === authorId) ?? state.participants?.self;
const isSelf = noteAuthor?.user.id === state.participants?.self.user.id;
Expand Down Expand Up @@ -58,7 +60,10 @@ export const NoteDialogNoteContent: FC<NoteDialogNoteContentProps> = ({noteId, a

const isImage = useImageChecker(text);

const {value, ...emoji} = useEmojiAutocomplete<HTMLDivElement>({initialValue: text, suggestionsHidden: isStackedNote});
const {value, ...emoji} = useEmojiAutocomplete<HTMLDivElement>({
initialValue: text,
suggestionsHidden: isStackedNote,
});

return (
<div className={classNames("note-dialog__note-content", {"note-dialog__note-content--extended": !showNoteReactions})} ref={emoji.containerRef}>
Expand Down Expand Up @@ -108,7 +113,7 @@ export const NoteDialogNoteContent: FC<NoteDialogNoteContentProps> = ({noteId, a
}
}}
/>

{note?.edited && <div className="note-dialog__marker-edited">({t("Note.edited")})</div>}
{!isStackedNote && (
<div className="note-dialog__note-content--emoji-suggestions">
<EmojiSuggestions {...emoji.suggestionsProps} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/NoteInput/__tests__/NoteInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jest.mock("utils/hooks/useImageChecker.ts", () => ({
const createNoteInput = (columnId: string, maxNoteLength: number) => (
<I18nextProvider i18n={i18nTest}>
<Provider store={getTestStore()}>
<NoteInput columnId={columnId} maxNoteLength={maxNoteLength} columnIndex={1} columnIsVisible toggleColumnVisibility={() => undefined} />
<NoteInput columnId={columnId} columnIndex={1} columnIsVisible toggleColumnVisibility={() => undefined} />
</Provider>
</I18nextProvider>
);
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/de/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@
"Note": {
"me": "Ich",
"you": "dir",
"userImageAlt": "Von {{user}} hochgeladenes Bild"
"userImageAlt": "Von {{user}} hochgeladenes Bild",
"edited": "bearbeitet"
},
"StackView": {
"prevNote": "Vorheriges Kärtchen",
Expand Down
Loading

0 comments on commit e6e73d2

Please sign in to comment.