Commit Graph

8 Commits

Author SHA1 Message Date
Nik Clayton 32a6d3465b
feat: Include "Notifications" in the left-side navigation menu (#504)
Previously the only way to access notifications was to dedicate a tab to
them. Now notifications are available from the left-side navigation menu
so they're always accessible.

Add them to the top of the list, and swap the order of bookmarks and
favourites, assuming that users are more likely to want to see their
bookmarks than their favourites.

Move "Edit profile" to the bottom with the other settings options,
assuming that editing their profile does not happen very often, so
should not be at the top of the list.
2024-03-05 14:45:28 +01:00
Nik Clayton b8ac1f3944
fix: Show correct account/timeline when composing from notifications (#445)
The previous code didn't clear the task stack or recreate `MainActivity`
when the active user was changed.

So if the user was logged in with account A and used "Compose" from a
notification sent to account B, the active account was switched to B but
the UI chrome wasn't. After exiting `ComposeActivity` they would be
looking at the timeline for account B but with a toolbar that showed
account A.

Fix this by clearing the task stack and explicitly recreating
`MainActivity` when forwarding intents to `ComposeActivity`.
2024-02-15 13:10:00 +01:00
Nik Clayton a3d45ca9ec
refactor: Convert from Gson to Moshi (#428)
Moshi is faster to decode JSON at runtime, is actively maintained, has a
smaller memory and method footprint, and a slightly smaller APK size.
Moshi also correctly creates default constructor arguments instead of
leaving them null, which was a source of `NullPointerExceptions` when
using Gson.

The conversion broadly consisted of:

- Adding `@JsonClass(generateAdapter = true)` to data classes that
marshall to/from JSON.

- Replacing `@SerializedName(value = ...)` with `@Json(name = ...)`.

- Replacing Gson instances with Moshi in Retrofit, Hilt, and tests.

- Using Moshi adapters to marshall to/from JSON instead of Gson `toJson`
/ `fromJson`.

- Deleting `Rfc3339DateJsonAdapter` and related code, and using the
equivalent adapter bundled with Moshi.

- Rewriting `GuardedBooleanAdapter` as a more generic `GuardedAdapter`.

- Deleting unused ProGuard rules; Moshi generates adapters using code
generation, not runtime reflection.

The conversion surfaced some bugs which have been fixed.

- Not all audio attachments have attachment size metadata. Don't show
the attachment preview if the metadata is missing.

- Some `throwable` were not being logged correctly.

- The wrong type was being used when parsing the response when sending a
scheduled status.

- Exceptions other than `HttpException` or `IoException` would also
cause a status to be resent. If there's a JSON error parsing a response
the status would be repeatedly sent.

- In tests strings containing error responses were not valid JSON.

- Workaround Mastodon a bug and ensure `filter.keywords` is populated,
https://github.com/mastodon/mastodon/issues/29142
2024-02-09 12:41:13 +01:00
Nik Clayton 41c702fc1b
change: Display "About" information in tabs (#420)
Previously, `AboutActivity` had buttons and links to show the privacy
policy and licenses of dependencies.

Change this to a selection of fragments in tabs, one tab each for:

- General "About" information
- Licenses
- Privacy Policy

The information shown hasn't changed, but this lays the groundwork for
including additional tabs in the future for information like server
rules, detected capabilities, or troubleshooting information.
2024-02-06 00:43:26 +01:00
Nik Clayton 4750ad78a7
chore(deps): update dependency com.gaelmarhic.quadrant to v1.9 (#400)
1.9 no longer needs the hardcoded "app.pachli" in the class names.
2024-01-30 16:40:11 +01:00
Nik Clayton f0fc0fd530
refactor: Modularise core activity classes, (#393)
Continue modularisation by moving core activity classes that almost all
activities depende on to a `core.activity` module. This includes
core "helper" classes as well.

Implement core.activity:
- Contains BaseActivity, BottomSheetActivity
- Contains LinkHelper and other utility classes used by activities

Implement core.common.extensions:
- Move ViewBindingExtensions and ViewExtensions here

Implement core.common.util:
- Move BlurHashDecoder and VersionName here

Implement core.designsystem:
- Holds common resources (animations, colours, drawables, etc) used
  through the app
- Import "core.designsystem.R as DR" through the app to distinguish
  from the module's own resources

Implement feature.login:
- Move the LoginActivity and related code/resources to its own module

Implement tools/mvstring
- Moves string resources (and all translations) from one module to
  another
2024-01-30 11:37:00 +01:00
Nik Clayton 993b74691a
chore(deps): update plugin ktlint to v12.1.0 (#358) 2024-01-09 17:50:20 +01:00
Nik Clayton 1214cf7c8a
refactor: Break navigation dependency cycles with :core:navigation (#305)
The previous code generally started an activity by having the activity
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 that starts B.

This introduces a dependency between A and B.

This is worse if B also wants to start A.

For example, if A is `StatusListActivity` and B is`ViewThreadActivity`.
The user might click a status in `StatusListActivity` 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
`StatusListActivity` 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.

Break this dependency by adding a `:core:navigation` module with an
`app.pachli.core.navigation` package that 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.

The plugin uses the `Activity` names from the manifest, so when an
activity is moved in the future the constant will automatically update
to reflect the new package name.

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.

See #291
2023-12-07 18:36:00 +01:00