59a0e3087c
Previous code assumed the active account could always be determined from the account manager. This causes a few problems. 1. The account manager has to handle the case where there is no active account (e.g., the user is logging out of the last account). This meant the `activeAccount` property had to be nullable, so every consumer of that property either used it with a `let` or `!!` expression. 2. The active account can change over the life of the UI component, for example, when the user is switching accounts. There's a theoretical race condition where the UI component has started an operation for one account, then the account changes and the network authentication code uses the new account. 3. All operations assume they operate on whatever the active account is, making it difficult to provide any features that allow the user to temporarily operate as another account ("Boost as...", etc). This "ambient account" was effectively global mutable state, with all the problems that can cause. Start to fix this. The changes in this commit do not fix the problem completely, but they are some progress. Each activity (except LoginActivity) is expected to be launched with an intent that includes the ID of the Pachli account it defaults to operating with. This is `pachliAccountId`, and is the *database ID* (not the server ID) of the account. This is non-null, which removes one class of bugs. This account is passed to each fragment and any piece of code that has to perform an operation on behalf of this account. It's not used in most of those places yet, that will be done over a number of followup PRs as part of modernising each module. |
||
---|---|---|
.. | ||
src/main | ||
README.md | ||
build.gradle.kts | ||
lint-baseline.xml |
README.md
:core:navigation
package app.pachli.core.navigation
Intents for starting activities to break circular dependencies.
A common approach for surfacing type-safe (ish) intents to start activities is for the activity-to-be-launched to provide a method in a companion object that returns the relevant intent, possibly taking additional parameters that will be included in the intent as extras.
E.g., if A wants to start B, B provides the method that returns the intent.
This introduces a dependency between A and B.
This is worse if B also wants to start A.
For example, if A is TimelineActivity
and B isViewThreadActivity
. The user might click a status in TimelineActivity
to view the thread, starting ViewThreadActivity
. But from the thread they might click a hashtag to view the list of statuses with that hashtag. Now TimelineActivity
and ViewThreadActivity
have a circular dependency.
Even if that doesn't happen the dependency means that any changes to B will trigger a rebuild of A, even if the changes to B are not relevant.
This package contains Intent
subclasses that should be used instead. The quadrant
plugin is used to generate constants that can be used to launch activities by name instead of by class, breaking the dependency chain.
If the activity's intent requires specific extras those are passed via the constructor, with companion object methods to extract them from the intent.
Using the intent classes from this package is enforced by a lint IntentDetector
which will warn if any intents are created using a class literal.