Commit Graph

133 Commits

Author SHA1 Message Date
Nik Clayton 8257ded395
refactor: Remove `TabData` type (#576)
`TabData` recorded the type of the timeline the user had added to a tab.
`TimelineKind` is another type that records general information about
configured timelines, with identical properties.

There's no need for both, so remove `TabData` and use `TimelineKind` in
its place.

`TimelineKind` is itself mis-named; it's not just the timeline's kind
but also holds data necessary to display that timeline (e.g., the list
ID if it's a `.UserList`, or the hashtags if it's a `.Hashtags`) so
rename to `Timeline` to better reflect its usage. Move it to a new
`core.model` module.
2024-03-30 23:27:25 +01:00
Nik Clayton d3c7c7c89a
fix: Surface all exceptions to the user instead of crashing (#565)
Previous code would handle some expected exceptions (IO, HTTP) when
fetching a timeline, and show them to the user. Any other exception
would crash.

Now, surface all exceptions. Treat IO and HTTP exceptions as retryable
and show the "Retry" option, all others are considered non-retryable.

Provide a specific error string for exceptions caused by bad JSON.
2024-03-24 18:36:28 +01:00
Nik Clayton 2a4126a542
fix: Improve URL / tag / mention extraction when composing (#564)
Previous code used custom regular expressions to extract URLs, hashtags,
and mentions from text while the user was writing a post. These were
inconsistent with the ones that Mastodon uses so the derived character
count could be wrong.

As well as being visually incorrect this could prevent the user from
posting a status that was within the length limit, or allow them to
attempt to post a status that was over the length limit (which would
then fail).

Fix this by dropping the homegrown regular expressions and using the
same text parsing library that Mastodon users; twitter-text. This has
been converted to Kotlin and the functionality related to Twitter
specific features has been removed.

The hashtag handling has been adjusted, as Mastodon is more permissive
about the positions where hashtags can appear than Twitter is, in
particular, a hashtag does not need to be preceded with whitespace if
the tag appears after some scripts, such as Hirigana.
2024-03-23 14:14:07 +01:00
Nik Clayton 783d4e5cca
feat: Notify the user about severed relationships (#557) 2024-03-21 17:27:48 +01:00
Nik Clayton 4f9b283432
refactor: Simplify and optimize viewBinding delegate (#544)
Based on Christophe Beyl's technique described in

https://bladecoder.medium.com/viewlifecyclelazy-and-other-ways-to-avoid-view-memory-leaks-in-android-fragments-4aa982e6e579
2024-03-19 16:42:08 +01:00
ButterflyOfFire b478f38e19 fix(l10n): Update Kabyle translations
Currently translated at 72.7% (8 of 11 strings)

Translation: Pachli/Core/Ui : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/coreui-main/kab/
2024-03-18 19:31:42 +01:00
Weblate (bot) f64f9f03ae
fix(l10n): Translations update from Hosted Weblate (#541)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pachli/Fastlane
Metadata](https://hosted.weblate.org/projects/pachli/fastlane-metadata/).


It also includes following components:

* [Pachli/Core/Network :
Main](https://hosted.weblate.org/projects/pachli/corenetwork-main/)

* [Pachli/App :
Main](https://hosted.weblate.org/projects/pachli/app-main/)

* [Pachli/Feature/About :
Main](https://hosted.weblate.org/projects/pachli/featureabout-main/)

* [Pachli/Feature/Lists :
Main](https://hosted.weblate.org/projects/pachli/featurelists-main/)

* [Pachli/Core/Ui :
Main](https://hosted.weblate.org/projects/pachli/coreui-main/)

* [Pachli/App :
Google](https://hosted.weblate.org/projects/pachli/app-google/)

*
[Pachli/Feature/Login](https://hosted.weblate.org/projects/pachli/featurelogin/)

* [Pachli/Core/Designsystem :
Main](https://hosted.weblate.org/projects/pachli/coredesignsystem-main/)

* [Pachli/Core/Activity :
Orange](https://hosted.weblate.org/projects/pachli/coreactivity-orange/)

* [Pachli/Core/Activity :
Main](https://hosted.weblate.org/projects/pachli/coreactivity-main/)

* [Pachli/App :
Fdroid](https://hosted.weblate.org/projects/pachli/app-fdroid/)



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pachli/fastlane-metadata/horizontal-auto.svg)

---------

Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
2024-03-17 00:24:12 +01:00
Nik Clayton 9e0a1f4015
refactor: Simplify use of BackgroundMessageView (#539)
Previous code expected callers to typically provide the drawable and the
error message string resource, resulting in duplicate code at many
callsites.

Replace with three canned messages for empty containers, generic errors,
and network errors respectively. The images for these are fixed, the
caller may choose a different string resource for the error if there is
a more specific option.

Update and simplify the call sites.
2024-03-16 22:12:54 +01:00
Nik Clayton e41722e16f
refactor: Extract user list functionality to `feature:lists` (#537)
Move `ListsActivity`, along with fragments and viewmodels, to a new
`feature:lists` module.

Previous code used the `item_follow_request` layout, which was not
ideal, so update it to use a dedicated layout, `item_account_in_list`.

The UI uses strings and views originally defined in the main app, so
move them elsewhere so they can be re-used.

- `BackgroundMessageView` moves to `core.ui`.

- `Lazy` moves to `core.common`.

- `ThrowableExtensions` split; the extensions specific to throwables
from network activity move to `core.network`, others move to `core.ui`.

- `BindingHolder` moves to `core.ui`

- Shared drawables and strings move to `core.ui`.
2024-03-16 18:42:11 +01:00
Nik Clayton bdbe2f85c2
feat: Allow the user to set a list's replies policy (#534)
The replies policy controls whether replies from members of the list
also appear in the list.

Display the replies policy as three radio buttons when a list is created
or updated, and send the chosen replies policy via the API.

Default value if not specified is always "list", for consistency with
the Mastodon API defaults.

While I'm here:

- Ensure the list dialog layout is inflated using the dialog's themed
context
- Use a `TextInputLayout` wrapper around the list name in the list
dialog for better UX
- Simplify the dialog layout, use LinearLayout, and standard padding and
margins
2024-03-14 23:56:16 +01:00
Nik Clayton cfc15c8f64
refactor: Use LinearProgressIndicator with delayed show/hide (#525)
Previous code used a normal ProgressBar and a coroutine to delay
hiding/showing the bar for a snappier UI perception.

This is built-in functionality in LinearProgressIndicator, so switch to
that.

While I'm here, implement the "Select list" dialog's layout as a layout
resource.
2024-03-13 10:03:10 +01:00
Nik Clayton 006c358053 fix: Sort lists by title wherever they occur 2024-03-12 19:52:45 +01:00
Nik Clayton 7da4bc090b fix: Update tabs when lists are renamed or deleted
If the user has tabs containing one or more lists, and any of those
lists are renamed or deleted then the change should be reflected in the
tabs.

To do that:

`MainActivity`:

- Re-create tabs whenever lists are loaded and there's a list in a tab
- Compare lists-in-tabs by the ID of the list when restoring the user's
  tab, so that a list rename doesn't lose their position.

`NetworkListsRepository`:

- Update the user's tab preferences whenever lists are loaded, removing
  tabs that contain lists that have been deleted, and updating the
  list's title for lists that have been renamed.

Fixes #192
2024-03-12 19:52:45 +01:00
Nik Clayton a973f67ac8 refactor: Store tab preferences as polymorphic JSON 2024-03-12 19:52:45 +01:00
Nik Clayton e93e4ffb53
feat: Support client filters in GoToSocial 0.15.0 (#523) 2024-03-11 23:14:57 +01:00
Nik Clayton 442f3bc80c
feat: Show user's lists in the left-side navigation menu (#514)
Previously to view a list the user either had to add it to a tab, or tap
through "Lists" in the navigation menu to their list of lists, and then
tap the list they want.

Fix that, and show all their lists in a dedicated section in the menu,
with a new "Manage lists" entry that's functionality identical to the
old "Lists" entry (i.e., it shows their lists and allows them to create,
delete, and edit list settings).

To do that:

- Implement a proper `ListsRepository` as the single source of truth for
list implementation throughout the app. Expose the current list of lists
as a flow, with methods to perform operations on the lists.

- Collect the `ListsRepository` flow in `MainActivity` and use that to
populate the menu and update it whenever the user's lists change.

- Rewrite the activities and fragments that manipulate lists to use
`ListRepository`.

- Always show error snackbars when list operations fail. In particular,
the HTTP code and error are always shown.

- Delete the custom `Either` implementation, it's no longer used.

- Add types for modelling API responses and errors, `ApiResponse` and
`ApiError` respectively. The response includes the headers as well as
the body, so it can replace the use of `NetworkResult` and `Response`.
The actual result of the operation is expected to be carried in a
`com.github.michaelbull.result.Result` type. Implement a Retrofit call
adapter for these types.

Unit tests for these borrow heavily from
https://github.com/connyduck/networkresult-calladapter

Additional user-visible changes:

- Add an accessible "Refresh" menu item to `ListsActivity`.

- Adding a list to a tab has a dialog with a "Manage lists" option.
Previously that would close the dialog when clicked, so the user had to
re-open it on returning from list management. Now the dialog stays open.

- The soft keyboard automatically opens when creating or editing a list.
2024-03-10 23:14:21 +01:00
Nik Clayton a4dc3b85bd
fix: Show sized placeholder for hidden account media (#516)
Previous code showed a small icon for account media that the user has
hidden.

Now determine the correct size / aspect ratio for the media and use that
to compute the placeholder (either a blurhash, or the link colour for
consistency with the view on a timeline).

Fixes #513
2024-03-10 23:13:58 +01:00
Nik Clayton 0445e187df
fix: Ensure logging out accounts completes (#515)
The account logout process could fail due to API exceptions; network
errors for example, or if the user had already revoked the app's token
for that account. This would prevent the rest of the logout process
(cleaning database, etc) from completing.

Fix this by ignoring network errors during the logout process, and
always cleaning up account content in the database.

Fix a related issue where a deleted account might be recreated in a
partial state if the account's visible position was saved after it was
deleted. The recreated account couldn't do anything as it had no tokens,
but is very confusing.
2024-03-10 12:25:12 +01:00
sanao 0105a8179c
test: close DB in TimelineDaoTest (#512)
The previous code forgot to close the DB after TimelineDaoTest was run,
so a warning message was displayed when the test was run locally.

Close the database using the `@After` annotation.

Fixes #511
2024-03-08 11:40:51 +01:00
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
Juan M Sevilla f9e2c7a47c fix(l10n): Update Spanish translations
Currently translated at 100.0% (4 of 4 strings)

Translation: Pachli/Core/Network : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/corenetwork-main/es/
2024-03-04 20:33:08 +01:00
Nik Clayton 72e5ca887d
fix(deps): update agp to v8.3.0, lint to 31.3.0 (#483)
New lint rules highlighted a potential crash; the use of named match
groups (used here when extracting server versions) requires API >= 26 or
throws an exception.

Use the group numbers instead of names when extracting the value, but
keep the group names in the regular expressions for readability.
2024-03-01 23:07:14 +01:00
Nik Clayton fcae44110b
refactor: Remove synchronous search functions (#481)
The previous code used synchronous (i.e., non-suspending) functions to
call the /api/v2/search and /api/v2/accounts/search endpoints.

This is not necessary as the search was always performed in a separate
thread.

Remove, and replace their usage with the equivalent functions that
suspend.
2024-03-01 18:55:59 +01:00
Nik Clayton fb66293713
refactor: Remove duplicate strings from Filter.Action (#479)
Simplify code that users `Filter.Action` by deserialising directly into
the type instead of storing / using strings throughout the API.
2024-02-29 09:49:17 +01:00
Nik Clayton af58de5a8f
refactor: Enable core library desugaring as build convention logic (#480)
Once desugaring is enabled it needs to be enabled for up/down the
dependency chain, so enable it in the shared configuration defined by
the build convention code.

Highlighted a failing test that wasn't being run, so fix that too.
2024-02-29 09:43:44 +01:00
Nik Clayton 9a23439d04
refactor: Remove duplicate strings from Filter contexts (#478)
A filter's context (previously referred to as its `kind`) controls where
the filter is applied.

This was implemented as an enum with a specific property to control how
it would serialise when @FormUrlEncoded, and with a @Json annotation for
Moshi.

In addition, the model objects kept the filter context in its string
form throughout Pachli, requiring periodic conversion to/from the enum
type, making the code more complicated.

Fix this, by:

1. Converting the incoming JSON value to the enum type immediately, so
the rest of the code uses the enum constants exclusively.

2. Implement a Retrofit converter that serialises the enum value when
@FormUrlEncoded to the same string used in JSON serialisation
2024-02-28 19:09:54 +01:00
Nik Clayton d943fa1aca
fix: Update InstanceV1/V2 related types based on real-world usage (#476)
Many servers that claim to be Mastodon-API compatible are not, as
evidenced by the content they include in the responses to
`/api/v1/instance` and `/api/v2/instance` requests.

Work around the worst of the breakage by providing defaults or marking
some fields as nullable (with a default null).

Bugs have been reported against the relevant projects.
2024-02-28 00:02:03 +01:00
Nik Clayton f5d90081ff
fix: Provide v2/instance configuration defaults (#474)
Some servers don't include a `urls` or `translation` block, which was
preventing parsing of the block, and falling back to the v1 instance
data.

Fix this by providing sensible defaults.
2024-02-26 17:21:33 +01:00
Martijn de Boer 6ca085089e fix(l10n): Update Dutch translations
Currently translated at 100.0% (4 of 4 strings)

Translation: Pachli/Core/Activity : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/coreactivity-main/nl/
2024-02-25 15:53:32 +01:00
Martijn de Boer be245ab2c4 fix(l10n): Update Dutch translations
Currently translated at 100.0% (4 of 4 strings)

Translation: Pachli/Core/Network : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/corenetwork-main/nl/
2024-02-25 15:53:32 +01:00
Nik Clayton 51c64a74dc
fix: Parse roles without a `highlighted` property (#467)
The `highlighted` property on a role may be absent. If it is this breaks
account parsing, including accounts in an /api/v2/instance response.
This, in turn, breaks determining server capabilities, including whether
or not translation is supported.

Set the default to `true`, which matches observed Mastodon behaviour.
2024-02-22 21:17:34 +01:00
Nik Clayton 2162e03e1f
fix: Handle JSON enums with unknown values (#462)
Previous code expected all incoming enums values to map directly to
Kotlin enum constants.

This is a problem for servers with additional features -- e.g.,
"reaction" as a notification type.

Fix this with a new Moshi adapter that will set the incoming value to a
given constant if it's not recognised.

Apply this to the enum constants in core.network to ensure they are
handled.

Clean up enum handling in Converters.kt, ComposeViewModel.kt, and
Status.kt by using the existing `.ordinal` property and some extension
functions for idiomatic code.

Fixes #461
2024-02-21 23:36:24 +01:00
Nik Clayton 941f4677eb
fix: Show previews for playable audio media from accounts (#460)
Previous code showed a generic placeholder for audio media on the
account's "Media" tab.

Fix this so the preview image is shown (if it's available).

- Move the "is this attachment previewable?" code to `Attachment` so it
can be reused here.

- Restructure the logic in `AccountMediaGridAdapter` to use the new
`isPreviewable()` method when deciding whether to show a preview.

- Attachments have dedicated placeholder drawables, use those when the
preview is not available.
2024-02-20 16:20:34 +01:00
Nik Clayton 8293e90b73
feat: Allow the user to trigger update checks (#458)
Add an additional preference entry that triggers an update when tapped.
It also displays the earliest time of the next automatic update check as
the preference summary.

Move the code that performs the update check (and the logic for whether
to perform the check) out of `MainActivity` and in to `UpdateCheck` so
it's available from `PreferencesFragment`.
2024-02-20 14:50:59 +01:00
Nik Clayton 13cfa1a15d
fix: Remove filters from Firefish capabilities (#459)
Firefish doesn't support the filters API, so remove filters from the
list of capabilities.

Fixes #248
2024-02-20 14:41:48 +01:00
Nik Clayton 73c947edfa
fix: Prevent crash when Friendica returns a null `voted_on` property (#456)
Friendica can return a null `voted_on` property, in violation of the API
spec.

Introduce a `BooleanIfNull` annotation that will convert the `null` to
`false` if encountered.

While I'm here update the other adapters as classes on their relevant
annotations instead of standalone classes to keep the code consistent.

Fixes #455
2024-02-19 19:04:29 +01:00
Nik Clayton 23e3cf1035
feat: Show information about notification fetches on "About" screen (#454)
Some users report that Pachli is not retrieving/displaying notifications
in a timely fashion.

To assist in diagnosing these errors, provide an additional set of tabs
on the "About" screen that contain information about how Pachli is
fetching notifications, and if not, why not.

Allow the user to save notification related logs and other details to a
file that can be attached to an e-mail or bug report.

Recording data:

- Provide a `NotificationConfig` singleton with properties to record
different aspects of the notification configuration. Update these
properties as different notification actions occur.

- Store logs in a `LogEntryEntity` table. Log events of interest with a
new `Timber` `LogEntryTree` that is planted in all cases.

- Add `PruneLogEntryEntityWorker` to trim saved logs to the last 48
hours.

Display:

- Add a `NotificationFragment` to `AboutActivity`. It hosts two other
fragments in tabs to show details from `NotificationConfig` and the
relevant logs, as well as controls for interacting with them.

Bug fixes:

- Filter out notifications with a null tag when processing active
notifications, prevents an NPE crash

Other changes:

- Log more details when errors occur so the bug reports are more helpful
2024-02-17 15:57:32 +01:00
JuanM 55ee447bdb fix(l10n): Update Spanish translations
Currently translated at 100.0% (3 of 3 strings)

Translation: Pachli/Core/Activity : Orange
Translate-URL: https://hosted.weblate.org/projects/pachli/coreactivity-orange/es/
2024-02-17 10:01:04 +01:00
JuanM 086d6bc85e fix(l10n): Update Spanish translations
Currently translated at 100.0% (4 of 4 strings)

Translation: Pachli/Core/Activity : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/coreactivity-main/es/
2024-02-17 10:01:04 +01:00
JuanM 0f13db8fb2 fix(l10n): Update Spanish translations
Currently translated at 75.0% (3 of 4 strings)

Translation: Pachli/Core/Network : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/corenetwork-main/es/
2024-02-17 10:01:04 +01:00
Nik Clayton b3978c4af7
change: Move CustomFragmentStateAdapter to core.activity (#452) 2024-02-16 20:56:00 +01:00
Nik Clayton 4dbf6065fd
change: Move RefreshableFragment to core.activity (#450) 2024-02-16 19:58:11 +01:00
Nik Clayton 11bf47e3b3
fix: Handle null properties in `Attachment.Focus` (#449)
The `x` and `y` properties in `Attachment.Focus` may be null (not
documented as such, but observed in the wild).

Provide a `DefaultIfNull` adapter that can be applied to these to
replace null values with a sensible default.
2024-02-15 20:32:22 +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 6c33204569
fix: Prevent crashes with bugs in Friendica and Pleroma servers (#441)
Neither server type implements the Mastodon API correctly, see

- https://github.com/friendica/friendica/issues/13887
- https://git.pleroma.social/pleroma/pleroma/-/issues/3238

Prevent crashes when parsing their broken JSON by providing default
values for properties they omit.

Fixes #433
2024-02-14 12:36:06 +01:00
Nik Clayton 2d03e6cf7b
change: Move AlertDialogExtensions to core.ui (#438) 2024-02-14 11:26:28 +01:00
Nik Clayton 96474a9ac3
change: Improve logging calls to Timber (#430)
- Use format strings so any overhead of building the string is only
incurred if the message is actually logged

- Pass throwables as the first parameter so they are logged with the
stacktrace
2024-02-09 17:33:01 +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 d01c72b7d7
change: Disable SyntheticAccessor lint rule (#424)
Does not appear to be adding value, and R8 optimises the code to remove
the generated accessors.
2024-02-06 19:51:37 +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