# User App Optimizations ## Intro First of all how fast is our apps depend on what device we are using (I mean performance). A lot of optimizations depend on how the redux store is organized and how we are using/combining received data from our services. Good redux store structure is good but we also need to effectively get data from this store and render in components without unnecessary updates. **Typescript** helps a lot to keep all data more structured (typed) and organized. #### Core Principles - Use as much "one source of truth" as possible - Use as few API calls as possible - If you can show something to the user - show it (some data about just selected conversation or workspace) and just update it in the background ## Sessions #### Background In redux store I'm using one generic [`Conversation`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/types.ts#L179) type which consist of [`ConversationBase`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/types.ts#L104) type (`sessionId`, `name`, `memberEntityIds`, `history`, etc.) and is actually one of more specific types ([`ExternalConversation`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/types.ts#L117), [`ChannelConversation`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/types.ts#L145) or [`DirectConversation`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/types.ts#L164)). We have different ways to get info about a conversation (from responses for list, one workspace, history, from different stomp events, etc.). I collecting and mapping (converting) all this data into one `Conversation` type and merging into existing or putting new `Conversation` objects, again - in one place. #### What it gives us? So when I get list of conversations (for example `GET /rake-user-app/sessions/internal/workspaces/4`) I already have some info about conversations (`sessionId`, `name`, `memberEntityIds`, etc.) and when user selects a conversation why not show this info in chat header? So we don't need to wait for response for one session (`/rake-user-app/sessions/155016/internal`) to show this information. And it is only one example. But with this small hack it looks faster. #### Storing sessions in state As I said, I'm storing info about sessions in one place and use it as one source of truth. I can access any conversation by [`sessionId`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/list/state.ts#L13). So I don't need to copy duplicate parts of conversation data in another place of the store to render it in components. I just getting this data by `sessionId`. The history is also storing in that place. So when user selects another conversation, I can show him the history that was downloaded earlier and then just update it in background. The same thing with conversation members. I storing them only in one place, updating them in one place and getting info about them (`name`, etc.) from one place by [`entityId`](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/members/state.ts#L3) #### Own direct Honestly, I really don't like "creating" a session each time when we are opening our own direct (`POST /rake-user-app/sessions/direct?workspaceId=4`) so I just [doing it one time](https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/entities/conversations/directs/sagas.ts#L65) when loading list of conversations and then user can just quickly open his own conversation with got before `sessionId`. ## Workspaces #### One source of truth, again :) Just like with sessions I'm storing workspace data in one place. So when I'm getting workspace info using `GET /rake-user-app-backend/workspaces` I already have some useful data (`name`, `logoImagePath`) and I can show it when user select another workspace. Then just loaded other needed data and store it in background. Again, it looks faster. #### Workspace depended data I have a list of items that depend on the selected workspace and need to be present globally in state. And I trying to load as few data as possible. Why load KB categories and last articles if you even didn't open a blade with KB? Why load `GET /platforms` again if you did it once in `GET /common-data` which is not depended on a workspace? Why load `GET /workspaces/7?withData=false` and then `GET /workspaces/7?withData=false` if you already got all required info in first response? ## Other Stuff #### Opening items in blades A lot of info about task, customers, etc. we already have in the received list. Why not use this when opening a blade? It looks really fast, and to be sure that you have most actual data we are still loading this in background and updating something if we need it. #### Notification Counters All counters (total workspace, conversations, tasks, etc.) are storing in one place and updating in one place, no duplicates, confusing while developing, all numbers match. ## API Calls #### How it organized on the mobile app? Here is one API call for getting common data, that I'm doing once on app launch: https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/workspace/sagas.ts#L304 Also one time on app startup I'm getting localizations (not on each workspace data loading as on the web) Here is data that I'm getting on each workspace change: https://gitlab.com/rake-developers/mobile-user-app/-/blob/master/src/store/workspace/sagas.ts#L59 First, I'm doing a parallel call of two methods - to get one workspace and to get permissions for the user (then I'll know to which screens the user will have access in the workspace). Then I'm doing parallel calls of few rest API routes: - Get a new list of visible workspace tiles (we need this if for example workspace order changed and to trigger notifications re-calculating, actually probably we can omit this on each workspace switching) - Get data about current user (not sure that I actually need to get this on each workspace load) - Get the list of workspace teams (using this in task blade or in the email invitation blade to get default teams) - Get the list of workspace active system users (actually we are getting this in response for workspace, so we can also omit this) - Get the list of workspace task types (probably we shouldn't load them on each workspace load, just when opening task screen or task blade, but from another side, we loaded them just once after workspace switch and that's it) - Get workspace user statuses #### What can be optimized on the web app? On the web, we can optimize some redux logic for getting workspace-dependent data. - For some reason on each workspace change we are making the first API call for getting system user data that is not running in parallel with other calls and blocking loading flow (we are waiting for a response, and then doing the rest of API calls). This call sometimes is very slow. We are doing this call a second time later(?) - We have a few other "blocking" API calls where we are waiting for each request's response sequentially. We can collect part of them and make requests in parallel - For some reason, we are making calls for getting conversation lists (externals, channels, directs) which is not making sense when the user is on another screen (tasks, customers, etc.). And it still blocking the whole workspace switch process ![](https://i.imgur.com/3WFBXzT.png)