Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 3a226033

Přidáno uživatelem Fantič před téměř 2 roky(ů)

re #10569: NotesView: Create note, update note, triggerRefresh

Zobrazit rozdíly:

src/api/notesservice.ts
1 1
import { SortOptions } from "../types/general"
2
import { Note } from "../types/note"
2 3
import { axiosInstance } from "./api"
3 4

  
4 5

  
......
8 9
    )
9 10
}
10 11

  
12
export const updateNoteRequest = async (note : Note) => {
13
    return await axiosInstance.put(`/note`, note);
14
}
15

  
16
export const createNoteRequest = async (note : Note) => {
17
    return await axiosInstance.post(`/note`, note);
18
}
19

  
20
export const deleteNoteRequest = async (noteId : string) => {
21
    return await axiosInstance.delete(
22
        `/note/${noteId}`
23
    )
24
}
25

  
11 26
export const getAllNotesRequest = async (myComments : boolean, generalComments : boolean, sortOptions: { items: SortOptions, date: SortOptions }) => {
12 27
    let url = `/notes/?my_notes=${myComments}&general_notes=${generalComments}`;
13 28

  
src/components/notes/NoteView.tsx
6 6
import React from "react";
7 7
import { ConfirmDialog } from "../general/Dialogs";
8 8

  
9
const NoteView = (props: { note: Note, handleReply: any, handleDelete: any, handleEdit: any, setConfirmDialog: any }) => {
9
const NoteView = (props: { note: Note, handleReply: any, handleDelete: any, handleEdit: any, setConfirmDialog: any, navigation: any }) => {
10 10
    const note = props.note;
11 11

  
12 12
    const [isEdited, setIsEdited] = useState(false);
......
19 19
        ).replace(/,/g, "").replace(/ /g, "-").replace(/-(?!.*-)/, " ");
20 20
    }
21 21

  
22
    const getUsernameInitials = (username: string): string => {
23
        const words = username.split(" ");
24
        const initials = words.map(word => word.charAt(0).toUpperCase());
25
        return initials.join("");
22
    const getUsernameInitials = (username: string ): string => {
23
        if (username) {
24
            const words = username.split(" ");
25
            const initials = words.map(word => word.charAt(0).toUpperCase());
26
            return initials.join("");
27
        }
28
        return "UK"; // Unknown
26 29
    }
27 30

  
28 31
    const toggleEdited = () => {
......
64 67
            setIsEdited(false);
65 68
        }
66 69
        else {
67
            props.handleEdit(note.uuid, text);
68
            // todo
70
            props.handleEdit({...note, note: text});
69 71
            setIsEdited(false);
70 72
        }
71 73
    }
......
74 76
        <HStack marginTop="5%">
75 77
            <Box width="10" height="10" marginTop="2.5%">
76 78
                <Avatar bg={note.noteColor} size="sm" source={{
77
                    uri: AVATAR_URL + "/" + note.avatarUrl                                     
78
                    // TODO bude zde nebo v atributu ta připona?
79
                    + ".png"                }}>
79
                    uri: AVATAR_URL + "/" + note.avatarUrl
80
                        // TODO bude zde nebo v atributu ta připona?
81
                        + ".png"
82
                }}>
80 83
                    {getUsernameInitials(note.username)}
81 84
                </Avatar>
82 85
            </Box>
......
102 105

  
103 106
                    {note.items && note.items.length > 0 &&
104 107
                        <Flex marginLeft="1.5%" direction="column" alignItems="flex-start" justify="flex-start" >
105
                            <Button width={"16"} variant="outline" size="sm">
108
                            <Button width={"16"} variant="outline" size="sm" onPress={() => props.navigation.navigate("Item", { itemId: note.items[0] })}>
106 109
                                {note.items[0]}
107 110
                            </Button>
108 111
                        </Flex>
src/components/notes/NotesListView.tsx
2 2
import { Note } from "../../types/note";
3 3
import NoteView from "./NoteView";
4 4

  
5
const NotesListView = (props: { notes: Note[], handleReply : any, handleDelete : any, handleEdit : any, setConfirmDialog: any}) => {
5
const NotesListView = (props: { notes: Note[], handleReply : any, handleDelete : any, handleEdit : any, setConfirmDialog: any, navigation: any}) => {
6 6
    const notes = props.notes;
7 7
    return (
8 8
        <ScrollView>
......
10 10
                notes && notes.length > 0 ? (
11 11
                    <VStack>
12 12
                        {notes.map((note, index) => (
13
                            <NoteView key={index} note={note} handleReply={props.handleReply} handleDelete={props.handleDelete} handleEdit={props.handleEdit} setConfirmDialog={props.setConfirmDialog}/>
13
                            <NoteView key={index} note={note} handleReply={props.handleReply} handleDelete={props.handleDelete} handleEdit={props.handleEdit} setConfirmDialog={props.setConfirmDialog} navigation={props.navigation}/>
14 14
                        ))}
15 15
                    </VStack>
16 16
                ) : (
src/pages/NotesViewPage.tsx
12 12
import { useDispatch, useSelector } from "react-redux"
13 13
import { AppDispatch, RootState } from "../stores/store"
14 14
import LoadingBox from "../components/loading/LoadingBox"
15
import { getAllNotes } from "../stores/actions/notesThunks"
15
import { createNote, deleteNote, getAllNotes, updateNote } from "../stores/actions/notesThunks"
16 16
import { login } from "../stores/actions/userThunks"
17 17
const NotesViewPage = ({ route, navigation }: DrawerScreenProps<RootDrawerParamList, 'Notes'>) => {
18 18

  
19
  const { notes, notesLoading, requestPending } = useSelector((state: RootState) => state.noteViewState)
19
  const { notes, notesLoading, requestPending, triggerRefresh } = useSelector((state: RootState) => state.noteViewState)
20
  
20 21
  const dispatch = useDispatch<AppDispatch>();
21 22

  
22 23
  const [isSortOpen, setIsSortOpen] = useState(false);
24
  
25
  const [newComment, setNewComment] = useState<string>("");
23 26

  
24 27
  const [myCommentsCheck, setMyCommentsCheck] = useState(false);
25 28
  const [generalCommentsCheck, setGeneralCommentsCheck] = useState(true);
......
76 79
    }
77 80
  };
78 81

  
79
  // initial: load notes
82
  // trigger refresh on triggerRefresh increment
80 83
  useEffect(() => {
81 84
    log.debug("NotesViewPage", "useEffect", "getNotes");
82 85
    getNotes();
83
  }, [])
86
    
87
    setNewComment("");
88
    setReplyingTo(null);
89
  }, [triggerRefresh])
84 90

  
85 91
  // general comments check changed
86 92
  useEffect(() => {
......
100 106
  }
101 107

  
102 108
  const handleReply = (messageId: string, userName: string) => {
103
    console.log("Handling reply: " + messageId);
104 109
    setReplyingTo({ messageId, userName })
105 110
  }
106 111

  
107
  const handleEdit = (messageId: string, newMessage: string) => {
108
    console.log("Handling edit:" + messageId + ", " + newMessage);
112
  const handleEdit = (note : Note) => {
113
    if(!requestPending)
114
      dispatch(updateNote(note));
109 115
  }
110 116

  
111 117
  const handleDelete = (messageId: string) => {
112
    console.log("Handling delete:" + messageId);
118
    if(!requestPending)
119
      dispatch(deleteNote({noteId: messageId}))
113 120
  }
114 121

  
115
  console.log("notes loading: " + notesLoading);
122
  const handleCreateComment = () => {
123
    if(!requestPending){
124
      // creating new comment
125
      if(replyingTo != null){
126
        dispatch(createNote( {
127
          newNote: {note: newComment, reply_to: replyingTo.messageId} as Note
128
        }))
129
      }
130
      else{
131
        dispatch(createNote({
132
          newNote: {note: newComment} as Note
133
        }))
134
      }
135
    } 
136
  }
116 137

  
117 138
  return (
118
    <Box width="90%" marginLeft="5%">
139
    <Box width="92%" marginLeft="5%">
119 140
      <VStack>
120 141
        <HStack marginTop="5%">
121 142
          <Box width="30%" justifyContent={"center"}>
......
168 189
          {
169 190
            notesLoading ? (<LoadingBox text="Loading notes"/>)
170 191
              :
171
              (<NotesListView notes={notes} handleReply={handleReply} handleDelete={handleDelete} handleEdit={handleEdit} setConfirmDialog={setConfirmDialog} />)
192
              (<NotesListView notes={notes} handleReply={handleReply} handleDelete={handleDelete} handleEdit={handleEdit} setConfirmDialog={setConfirmDialog} navigation={navigation} />)
172 193
          }
173 194
        </Box>
174 195
        {replyingTo != null &&
......
179 200
          </Flex>
180 201
        }
181 202
        <HStack h={replyingTo != null ? "20%" : "15%"} marginTop={replyingTo != null ? "0%" : "5%"}>
182
          <TextArea placeholder="Add comment" width="90%" autoCompleteType={undefined} ></TextArea>
203
          <TextArea placeholder="Add comment" width="90%" fontSize={14} value={newComment} onChangeText={setNewComment} autoCompleteType={undefined} ></TextArea>
183 204
          <VStack>
184 205
            <Box h={replyingTo != null ? "30%" : "45%"}></Box>
185 206
            <Box marginLeft="2">
186
              <Button startIcon={<MessageIcon color="#FFF" />}></Button>
207
              <Button onPress={handleCreateComment} startIcon={<MessageIcon color="#FFF" /> }></Button>
187 208
            </Box>
188 209
          </VStack>
189 210
        </HStack>
src/stores/actions/itemThunks.ts
120 120
                            createdTime: (note as any).created, 
121 121
                            updatedTime: (note as any).updated,
122 122
                            noteColor: (note as any).note_color,
123
                            reply_to: (note as any).reply_to
123 124
                        })
124 125
                    }
125 126

  
src/stores/actions/notesThunks.ts
1 1
import { createAsyncThunk } from "@reduxjs/toolkit"
2
import { getAllNotesRequest } from "../../api/notesservice"
2
import { createNoteRequest, deleteNoteRequest, getAllNotesRequest, updateNoteRequest } from "../../api/notesservice"
3 3
import { SortOptions } from "../../types/general";
4
import { Note } from "../../types/note";
5
import { useDispatch } from "react-redux";
6
import { AppDispatch } from "../store";
7

  
8
export const deleteNote = createAsyncThunk(
9
    "notes/deleteNote",
10
    async ({ noteId }: { noteId: string }) => {
11
        try {
12

  
13
            const response = await deleteNoteRequest(noteId);
14

  
15
            console.log(response);
16

  
17
            if (response.status === 200) {
18
                return { deletedNoteId: noteId };
19
            }
20
            else {
21
                console.log("Error");
22
                return Promise.reject(response.data ? response.data : "Error")
23
            }
24
        } catch (err: any) {
25
            console.log(err);
26
            return Promise.reject(err.response.data)
27
        }
28
    }
29
)
30

  
31
export const createNote = createAsyncThunk(
32
    "notes/createNote",
33
    async ({ newNote }: { newNote: Note}) => {
34
        try {
35

  
36
            const response = await createNoteRequest(newNote);
37
            console.log(response);
38
            if (response.status === 201) {
39
                return { createdNote: newNote };
40
            }
41
            else {
42
                console.log("Error");
43
                return Promise.reject(response.data ? response.data : "Error")
44
            }
45
        } catch (err: any) {
46
            console.log(err);
47
            return Promise.reject(err.response.data)
48
        }
49
    }
50
)
51

  
52
export const updateNote = createAsyncThunk(
53
    "notes/updateNote",
54
    async (note: Note) => {
55
        try {
56
            const response = await updateNoteRequest(note);
57
            if (response.status === 201) {
58
                return { updateNote: note };
59
            }
60
            else {
61
                console.log("Error");
62
                return Promise.reject(response.data ? response.data : "Error")
63
            }
64
        } catch (err: any) {
65
            console.log(err);
66
            return Promise.reject(err.response.data)
67
        }
68
    }
69
)
70

  
4 71

  
5 72

  
6 73
export const getAllNotes = createAsyncThunk(
7 74
    "notes/getAllNotes",
8
    async ({ myComments, generalComments, sortOptions }: { myComments : boolean, generalComments : boolean, sortOptions: { items: SortOptions, date: SortOptions }}) => {
75
    async ({ myComments, generalComments, sortOptions }: { myComments: boolean, generalComments: boolean, sortOptions: { items: SortOptions, date: SortOptions } }) => {
9 76
        try {
10 77
            console.log("GET notes/getNotes/");
11 78

  
......
14 81
            console.log(response);
15 82

  
16 83
            if (response.status === 200) {
17
                if(response.data.length > 0){
84
                if (response.data.length > 0) {
18 85

  
19 86
                    let notes = [];
20
                    for(let i = 0; i < response.data.length; i++){
87
                    for (let i = 0; i < response.data.length; i++) {
21 88
                        let note = response.data[i];
22 89
                        notes.push({
23 90
                            uuid: (note as any).uuid,
......
26 93
                            note: (note as any).note,
27 94
                            avatarUrl: (note as any).avatar,
28 95
                            items: (note as any).items,
29
                            createdTime: (note as any).created, 
96
                            createdTime: (note as any).created,
30 97
                            updatedTime: (note as any).updated,
31 98
                            noteColor: (note as any).note_color,
99
                            reply_to: (note as any).reply_to
32 100
                        })
33 101
                    }
34 102

  
......
36 104
                        notes,
37 105
                    }
38 106
                }
39
                else{
107
                else {
40 108
                    // no notes
41 109
                    return {
42 110
                        notes: [],
src/stores/reducers/notesSlice.ts
1
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
2
import { getAllNotes } from "../actions/notesThunks"
1
import { AsyncThunkAction, PayloadAction, createSlice } from "@reduxjs/toolkit"
2
import { deleteNote, getAllNotes, createNote, updateNote } from "../actions/notesThunks"
3 3
import { Certainty, ItemViewState, Item } from "../../types/item";
4 4
import { Note, NotesViewState } from "../../types/note";
5 5

  
......
9 9
    notes: [],
10 10
    notesLoading: true, // for reading notes
11 11
    requestPending: false, // for updating or deleting note
12
    triggerRefresh: 0,
12 13
    lastError: ""
13 14
}
14 15

  
......
18 19
    reducers: {
19 20
    },
20 21
    extraReducers: (builder) => {
21
            // getAllNotes
22
            builder.addCase(getAllNotes.fulfilled, (state, action) => {
23
                state.notesLoading = false;
24
                state.notes = action.payload.notes;
25
            })
26
            builder.addCase(getAllNotes.pending, (state, action) => {
27
                state.notesLoading = true;
28
            })
29
            builder.addCase(getAllNotes.rejected, (state, action) => {
30
                state.notesLoading = false;
31
                state.lastError = action.error.message
32
            })
22
        // getAllNotes
23
        builder.addCase(getAllNotes.fulfilled, (state, action) => {
24
            state.notesLoading = false;
25
            state.lastError = "";
26
            state.notes = action.payload.notes;
27
        })
28
        builder.addCase(getAllNotes.pending, (state, action) => {
29
            state.notesLoading = true;
30
        })
31
        builder.addCase(getAllNotes.rejected, (state, action) => {
32
            state.notesLoading = false;
33
            state.lastError = action.error.message
34
        })
35

  
36
        // deleteNote
37
        builder.addCase(deleteNote.pending, (state, action) => {
38
            state.requestPending = true;
39
        })
40
        builder.addCase(deleteNote.fulfilled, (state, action) => {
41
            state.lastError = "";
42
            state.triggerRefresh++;
43
            state.requestPending = false;
44
        })
45
        builder.addCase(deleteNote.rejected, (state, action) => {
46
            state.requestPending = false;
47
            state.lastError = action.error.message
48
        })
49

  
50
        // createNote
51
        builder.addCase(createNote.pending, (state, action) => {
52
            state.requestPending = true;
53
        })
54
        builder.addCase(createNote.fulfilled, (state, action) => {
55
            state.notes = [action.payload.createdNote ,...state.notes]
56
            state.lastError = "";
57
            state.triggerRefresh++;
58
            state.requestPending = false;
59
        })
60
        builder.addCase(createNote.rejected, (state, action) => {
61
            state.requestPending = false;
62
            state.lastError = action.error.message
63
        })
64

  
65
        // createNote
66
        builder.addCase(updateNote.pending, (state, action) => {
67
            state.requestPending = true;
68
        })
69
        builder.addCase(updateNote.fulfilled, (state, action) => {
70
            state.requestPending = false;
71
            state.notes = state.notes.map((note) => {
72
                if (note.uuid === action.payload.updateNote.uuid) {
73
                    return action.payload.updateNote;
74
                } else {
75
                    return note;
76
                }
77
            });
78
            state.lastError = "";
79
        });
80
        builder.addCase(updateNote.rejected, (state, action) => {
81
            state.requestPending = false;
82
            state.lastError = action.error.message
83
        })
33 84
    }
34 85
})
35 86

  
36
export default noteSlice.reducer
87
export default noteSlice.reducer
src/types/note.ts
8 8
    createdTime: Date 
9 9
    updatedTime: Date
10 10
    noteColor: string
11
    reply_to: string // parent id
11 12
}
12 13

  
13 14

  
......
15 16
    notes: Note[] 
16 17
    notesLoading: boolean
17 18
    requestPending: boolean
19
    triggerRefresh: number
18 20
    lastError?: string
19 21
}
20 22

  

Také k dispozici: Unified diff