Fractal is currently structured into two parts: The API part (fractal-matrix-api) and GTK part (fractal-gtk). The first one mostly just does the https calls to the Matrix server, the GTK part does everything else. This post will not talk about the API part since that will remain more or less the same (at least for now).
Why do this split?
Our goal is to create two separate Apps for two different use cases: Banquets and Barbecues. For this split to happen we first need to make some changes to Fractal’s architecture.
I think to keep Fractal maintainable in the future we need to simplify especially the AppOp struct, since it has been growing at an alarming pace (Don’t get me wrong I’m really happy that Fractal improves so quickly). AppOp is the struct which contains all widget references and most methods. Basically, it contains everything important.
Also, a lot of Matrix related code is intertwined with GTK code, and therefore many tasks are handled in the main thread.
New architecture
The communication between fractal-matrix-api and the Fractal backend will stay the same. Therefore, the backend-loop will live in the backend, but instead of making UI changes directly, it will just store the new data, and then signal the main GTK application that it has to update the related UI component. The backend will expose a set of methods to give the main application access to the data and to change them.
The main application will create all UI components and keep them up-to-date. At last we have the UI components which are “custom widgets”, such as the sidebar. A custom widgets has two public methods, one to create a new component, and another one to update its content.
When the main application receives a UI update event, containing only information about which UI component should be updated, but no other data, the application uses the interface offered by the backend to get all needed information to call the update method of each custom widget. The update method then decides then which GTK widgets have to be destroyed, and which can be reused to display the new data.
How to get started?
The first thing we can do is switch form Mutex to RwLock, this will already solve many UI freezes since we don’t need to look the data anymore to read it.
Then we can start moving the current code form AppOp, where possible, to a custom widget (the Rust structs we are using right now). Each widget should have a create and an update method, for creating the new component and for keeping it updated without the need of destroying it first. (Address.rs is a widget example created this way). Most custom widgets already have the create method called widget() (if I remember it correctly) but they are missing the update method.
Nothing is set in stone
We didn’t decide anything yet. I wrote this blog post to start the discussion about how to tackle the refactor and split, so input/comments/feedback are very welcome!