out-fox-it / aj-ty-spojka

Web application that connects programmers based on their experience, interests and the type of person they are looking for (mentor, buddy, mentee) through matching and contact sharing.
GNU General Public License v3.0
0 stars 0 forks source link

Contacts #54

Closed agnes97 closed 3 years ago

agnes97 commented 3 years ago

Here's my PR for Contacts, it consists from following tasks:

And now to explain the magic!

PAGE

FUNCTION We have four types of messages - with matched contacts, new match suggestions, matches I suggested and matches I ignored. To display messages in those four blocks, each block with it's title, Jindra helped me created groupMessagesByType function with message: MesageData[] as it's parameter. MessageData[] is an array filled with information about each message (for now it's type and content).

TYPE We set the type of Record<MessageType, MessageData[]>. MessageType are our four message types mentioned above defined in another file. Record<Key, Type> [docs] is a TypeScript device for type transformation. In this case, the object we're creating with groupMessagesByType function now has values of MessageType as keys and MessageData[]array values as values of those keys.

REDUCE Now to the body of the groupMessagesByType function! The functional way to go through all messages a user has is reduce. This is mainly because reduce is immutable (you're never changing original array in the middle of your loop), it iterates over all elements in an array and accumulates them into one "thing" (whatever you define) with a return. This gives it many advantages over forEach and other loops (ways to "filter" or go through all data).

Reduce has two parameters - array.reduce(callback[, initialValue]) (initialValue is optional).

FIRST PARAMETER - CALLBACK In our case, our callback is an anonymous arrow function (previousGroupMessages, currentMessage) => { return ... }. It's two parameters previousGroupMessages and currentMessage define what messages we already went through and what message we're iterating over right now.

Our anonymous array function (callback) uses so called spread operator ..., which clones (copies) object it's used on. ...previousGroupMessages therefore are all messages reduce (loop) already went through with all their types and data. As such, the loop acknowledges what previousGroupMessages it has, what currentMessage it has and then pushes the currentMessage into it's correct type category. The currentMessage becomes a part of previousGroupMessages and reduce goes to next iteration (evaluates next currentMessage) until there are no messages left.

SECOND PARAMETER - INITIAL VALUE Second parameter of reduce after callback (our anonymous arrow function) is an initialValue. Our initialValue is an object {}defining empty arrays []for each MessageType as in here [MessageType.MATCH]: [].

And when there are no messages to iterate over/go through, our reduce returns same structure as initalValue object {} with four MessageType types and each has an array full of corresponding messages. :)

reduce

Since we don't have a back-end/API yet, we set up an object full of fakeMessages. Each one only has a content (it's text) and it's type (correspoding with MessageType enum defined in different file).

DISPLAY FAKEMESSAGES To display those fakeMessages (later our API data) in a Contact page, we create a variable const messageGroups = groupMessagesByType(fakeMessages) using our groupMessagesByType function that uses reduce to loop/iterate through all user's messages and returns four separate categories with their corresponding messages.

OBJECT.ENTRIES() We then want to use .map function to display them, but our types could get all mixed up with a complexity of our value (remember, messageGroups const has the Record<Key, Type> type as Record<MessageType, MessageData[]>). To successfully map this, we use Object.entries()JavaScript method [docs] that can map objects with both keys and types.

MAP Object.entries(messageGroups).map takes const messageGroups as it's parameter and then maps it using an anonymous arrow function ([type, messages]) => ( return ). It's parameters (the message and it's type) are defined in [] array parentheses as a way to deconstruct an array of entries (so we can easily refer to it in the code).

RETURN As a return, we then display our HTML structure using Styled Components and use key and message parameters to define every message that will be displayed in a <Message key={${type}_${index}} message={message} />. The magic in the key happens because Object.entries() wanted to force our key as a string which wouldn't work so we had to be more precise with it's declaration.

Returning messages like this also allows us to say the whole <MessageBoxBorder /> element is hidden when there is 0 messages (messages of lenght 0) and to display the title (to define where one MessageType starts and other begins) for the whole element, not each message.

reduce2

COMPONENT

In the component, all that remains to do is to switch between components (each MessageType has it's own component in the Message folder due to them having different buttons/submit options).

reduce3

Tadá!

Please, feel free to ask for details! I'm barely starting to understand this myself.

DESKTOP / MOBILE VIEW

( I'm not solving the size of the titles yet because I presume this will be set globally when we choose the font and add appropriate rem font sizes! That's why the font is quite big on the mobile!)

desktop phone

tomas90hampl commented 3 years ago

Very good PR :wink: