How I’ve implemented multiple selection into Tresorit

György Szövérdffy
Tresorit Engineering
6 min readJul 24, 2020

Tresorit has a web application up and running for years now. It contains several views where specific resources are listed (e.g. files, folders, and users). It is possible to perform operations on these resources, however, for power users, it was missing something significant; the ability to select multiple items at the same time and perform batch actions on the selected resources. Luckily, I was given the opportunity to implement this feature while working with a handful of people from UX and Product development teams. I’m going to highlight my biggest learnings from this brief month-long project.

How it all began

Our web-based client is an Angular 7 application which is built in a pretty standard way. Listing pages have wrapper components that contain the list item components. With multiple selection, each list item will have a checkbox. Also, the list header will make it possible to select all displayed items as depicted below.

Multiple selection in all its glory

At the start of the project, I thought the best way would be to create an abstract selection handling pattern that will be reusable for every page. It turned out these expectations weren’t so far fetched, however they were far from reality.

To avoid the unpleasant case of implementing unnecessary abstraction before any working code, I started to work on a Proof of Concept implementation for one of the most complex pages where two different types of resources are accessible.

I started with implementing a basic Angular service with the following responsibilities:

  • Store selected list items. (I chose to store the Angular component instances so I can access the already implemented functions if needed.)
  • Emit RxJS events for single selection as well as all selection toggles.
  • Clear selection when navigation occurs.
  • Perform batch operations on the selected resources.

List items use the service as follows:

  • On selection toggle, they add (or remove) themselves from the selected items array of the service.
  • Handle the all selection toggle event accordingly.

Components which contain the select all toggle have slightly more responsibilities than their list-item counterparts:

  • Handle selection change event.
  • If every item is selected individually then toggle all selection, same with deselection.
  • Determine available batch operations for selected items.
  • Make these operations accessible for the user in the form of a context menu and/or several action buttons.

Besides this several UI changes were needed. However, from a technological perspective, these were more straightforward. Half of these additions were CSS changes, but the other half consisted of an insane amount of logic to determine if every visible item got selected and what exactly which batch actions are available for these items. More on that soon.

Finishing the Proof of Concept

It came clear after encountering the second listing page that a perfect abstraction would be impossible over the project’s time frame for the following reasons:

  1. The component hierarchy isn’t the same throughout the listings
  2. The implementation follows different concepts
  3. Inability to use polymorphism due to the lack of OOP in our current implementation
  4. Special cases of list items can occur. (e.g. deleted files, suspended users)
  5. There is no unified data representation
  6. Every list item behaves differently
  7. Most of our flows were implemented for a single item

Component hierarchy

In some cases, the wrapper list component doesn’t contain the all selection toggle. Instead, special list items function as group headers. This introduces the need for more RxJS observables since otherwise it wouldn’t be possible to distinguish which group was selected. This leads to more event handling, Subscriptions, and so on.

Different implementation

The problem here is that it’s much more difficult to apply any sort of abstraction when the affected components execute the same logic differently. To spice things up, one particular case is where the user can select all items in subcategories. This isn’t a big deal all by itself, the annoying part for me was the realization that almost none of our listing pages are consistent in the application, which could lead to usability issues as well.

Lack of OOP

This introduces a simple problem. I had to implement some functions several times with the same responsibilities. I could get into a mad refactoring spree but that could’ve resulted in running out of time and lead to future maintainability questions as well. As we all know, deadlines exist for a reason. However, I took note of these so our team can consider all future improvement possibilities.

Special list items

Some listing pages contain different toggles that control what kind of list items are displayed. A fine example is “Show deleted” — which makes deleted items appear, thus all selection has to be reevaluated not to mention updating the list has to take this into account as well.

Only single item actions

Most of our data manipulating flows contain confirmation modals or some other more complex modals, not to mention event tracking to see a user’s progress in the flow. Several questions arose, some including

  • Do we want tracking events for every single resource or only one per batch?
  • How do we change tracking parameters to be comprehensible and meaningful?
  • How should we show potential errors and warnings to the user during batch actions?
  • Do we want server endpoints to manipulate resources in a batch instead of an API call for every single item? (The answer was a straight no so far, this was a front-end development project.)

The first obvious thing, in this case, was that modals have to be able to handle one or more items as input parameters at once.

The solution was to compromise

Due to the detailed list of interfering factors above I had to compromise.

The selection handler service only handles singular item selections and emits events accordingly. Different selection events (believe me, there are quite a few) are emitted by the respective list wrapper components or group header list items. These additional events are handled accordingly where needed.

I added no abstract Angular components for list items, and list wrappers because I didn’t want to risk adding the possibility of new bugs into the whole application.

I didn’t unify the logic for the availability of batch operations. According to the agreed specification:

  • If any batch operation is available always show one primary action
  • If only two actions are available show them as action buttons
  • If more than two actions are available, show one primary action and another button opening a context menu with the remaining actions

This resulted in an insane amount of states and backtracking so that every use-case works as intended, or that one additional case doesn’t mess with the already implemented ones. For example, the code below shows the conditions that have to be met for one button to be visible on our pages.

Note that in some cases almost any option can appear in the context menu or as an action button. However, should something appear as an action button, that option is not accessible from the context menu.

Without further explanation, I think it’s obvious to see that these monstrosities took up most of the coding work and testing 😆. I know from a mathematical standpoint that these could have been brought to a simpler form, however, in this form, the predicates translate to specific use-cases which make the code more comprehensible, should modifications be needed in the future.

After seeing how wild these conditions can get I added a new service that only contains helper functions to make these conditions shorter and more understandable.

Takeaways and further improvements

This project was special since it was the first time I had the opportunity to work alone as a developer closely with UX and product management. At first, it seemed a little intimidating, but soon enough fear turned into excitement as I started to embrace the opportunity I was given. Of course, I had the option to consult with my teammates (for which I am thankful), but it was my responsibility to make architectural decisions and move forward with them.

I am glad that everything necessary to consider this project successful got completed more or less in an optimal manner. Of course, I can see areas that can be further improved in the future:

  • Event handling tweaks, since some events might get triggered unnecessarily
  • A more consistent UI
  • Improving abstraction and component hierarchy

All in all, I’ve learned a lot and it was a great experience and change of pace to be a part of this project.

--

--

György Szövérdffy
Tresorit Engineering

Software Engineer at Tresorit, ex-PhD student (with an intent to continue) and sports enthusiast.