Commit graph

208 commits

Author SHA1 Message Date
Konrad Pozniak
d0b20cf06e
various not push related notification improvements (#4929)
- support new notification type `severed_relationships`, closes
https://github.com/tuskyapp/Tusky/issues/4835, closes
https://github.com/tuskyapp/Tusky/issues/4334
- support new notification type `moderation_warning`
- the account note is now shown again for follow request and follow
notifcations (was broken since
https://github.com/tuskyapp/Tusky/pull/4026)
- closes https://github.com/tuskyapp/Tusky/issues/4571
- The "unknown notification type" notification now shows the unknown
type and a info dialog when you click it
https://chaos.social/@ConnyDuck/113601791254050485
- The notification policy banner in the notification tab is now cached
for better offline behavior (and less jumping of the list on every load)
and updates when interacting with the requests
- Fixes a bug where some notifications wouldn't be filtered correctly.
Behavior should now match Mastodon.
https://mastodon.social/@alm10965/113639206858728177
- Fixes a bug where some system notifications wouldn't have a body
- For filters and channels, report and signup notifications are now
grouped as "Admin", severed relationship events and moderation warnings
as "other". These lists are super long already.
- The icon for the "`<user>` just posted" notification is now a bell
instead of a home
- Follow requests won't be filtered by default in the notification tab.
No idea why this one got special treatment. This change will only affect
new logins and not existing ones.
- closes #4440 
- Adds info about attached media or poll to
StatusNotificationViewHolder. This is important context that has been
missing before.
- Adds (private) reply/(private) mention text above mention
notification. (Partially?) closes
https://github.com/tuskyapp/Tusky/issues/3883

Some screenshots:

![follow](https://github.com/user-attachments/assets/5f962116-c16f-4574-aae1-b1f931ce1508)

![moderation_warning](https://github.com/user-attachments/assets/55a2ee7e-ebcd-4ae8-9170-f07f9f5df5d2)

![severed_relationship](https://github.com/user-attachments/assets/a8d6b898-eb44-43b4-9b6d-3fb5f7aeb852)

![unknown](https://github.com/user-attachments/assets/c74ee33e-6926-42b1-b952-dc888b72fd27)

![unknown_info](https://github.com/user-attachments/assets/19ff11bf-aaff-4219-87e2-ea980ebbd118)

![notifications](https://github.com/user-attachments/assets/b5021cbb-f6c0-4a17-9e15-73e669504647)
2025-02-24 14:53:05 +01:00
Konrad Pozniak
1157be18cf
Properly handle more than 4 fields in EditProfileViewModel (#4936)
Also read `configuration.accounts.max_profile_fields` from
`api/v2/instance` to get the correct limit for GoToSocial.

Glitch-soc also allows more fields but does not provide configuration
yet, see https://github.com/glitch-soc/mastodon/issues/2973

closes https://github.com/tuskyapp/Tusky/issues/3305
2025-02-24 14:18:48 +01:00
UlrichKu
6450af6edb
Improve push notifications (#4896)
Besides the refactoring these improvements:
* Track last push distributor and reset settings and subscription on any
incompatible change (ie. uninstall)
* Only update (push) notification settings on server if needed
* Allow to only fetch notifications for one account (the one for which a
push message was received)

This is (also) the revival of
https://github.com/tuskyapp/Tusky/pull/3642

It's not really well tested so far. (Ie. with two or more accounts or
two or more push providers.)
2025-02-20 12:27:06 +01:00
UlrichKu
3a3e056572
Refactor notifications (#4883)
Also fixes https://github.com/tuskyapp/Tusky/issues/4858.
But apart from that there should be no functional change.
2025-01-22 21:16:33 +01:00
Konrad Pozniak
9e597800c9
Move all database queries off the ui thread & add a ViewModel for MainActivity (#4786)
- Move all database queries off the ui thread - this is a massive
performance improvement
- ViewModel for MainActivity - this makes MainActivity smaller and
network requests won't be retried when rotating the screen
- removes the Push Notification Migration feature. We had it long
enough, all users who want push notifications should be migrated by now
- AccountEntity is now immutable
- converted BaseActivity to Kotlin
- The header image of Accounts is now cached as well
2025-01-17 12:35:35 +01:00
UlrichKu
6cbcf3eef0
Properly summarize all notifications (#4848)
Do summary notifications like the Api defines it:
* Schedule and summarize without delay (in order for summerization to
work)
* Always have a summary notification: simplify code with this and make
more reliable
* Do not care about single notification count (the system already does
that as well)
* **Bugfix: Schedule summary first: This avoids a rate limit problem
that (then) not groups at all**

Testing this is probably the most difficult part.
For example I couldn't get any notification to ring with older Api
versions in the debugger. (Same as for current develop)
However one hack to always get notifications: Fix "minId" in
"fetchNewNotifications()" to a somewhat older value.


Next possible step: Have only one summary notification at all (for all
channels/notification types). You can still configure single channels
differently.
Or: For very many notifications: Only use a true summary one (something
like "you have 28 favorites and 7 boosts").

Generally: The notification timeline must be improved now. Because that
must be the go-to solution for any large number of notifications. It
must be easy to read. E. g. with grouping per post.
2025-01-12 20:37:05 +01:00
renovate[bot]
ccd9fe9534
fix(deps): update kotlin (#4774)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.devtools.ksp](https://goo.gle/ksp)
([source](https://redirect.github.com/google/ksp)) | `2.0.21-1.0.28` ->
`2.1.0-1.0.29` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.1.0-1.0.29?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.1.0-1.0.29?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.0.21-1.0.28/2.1.0-1.0.29?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.0.21-1.0.28/2.1.0-1.0.29?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| org.jetbrains.kotlin.plugin.parcelize | `2.0.21` -> `2.1.0` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlin.plugin.parcelize:org.jetbrains.kotlin.plugin.parcelize.gradle.plugin/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/org.jetbrains.kotlin.plugin.parcelize:org.jetbrains.kotlin.plugin.parcelize.gradle.plugin/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/org.jetbrains.kotlin.plugin.parcelize:org.jetbrains.kotlin.plugin.parcelize.gradle.plugin/2.0.21/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlin.plugin.parcelize:org.jetbrains.kotlin.plugin.parcelize.gradle.plugin/2.0.21/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| org.jetbrains.kotlin.android | `2.0.21` -> `2.1.0` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.0.21/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.0.21/2.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[org.jetbrains.kotlinx:kotlinx-coroutines-test](https://redirect.github.com/Kotlin/kotlinx.coroutines)
| `1.9.0` -> `1.10.1` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.9.0/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.9.0/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[org.jetbrains.kotlinx:kotlinx-coroutines-android](https://redirect.github.com/Kotlin/kotlinx.coroutines)
| `1.9.0` -> `1.10.1` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.9.0/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.9.0/1.10.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>google/ksp (com.google.devtools.ksp)</summary>

###
[`v2.1.0-1.0.29`](https://redirect.github.com/google/ksp/releases/tag/2.1.0-1.0.29)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.1.0-1.0.28...2.1.0-1.0.29)

##### Updates

- KSP2: Sanitize dots to underscores in suffixes of internals by
[@&#8203;ting-yuan](https://redirect.github.com/ting-yuan) in
[https://github.com/google/ksp/pull/2229](https://redirect.github.com/google/ksp/pull/2229)
- KSP2: clean up legacy incremental code by
[@&#8203;ting-yuan](https://redirect.github.com/ting-yuan) in
[https://github.com/google/ksp/pull/2231](https://redirect.github.com/google/ksp/pull/2231)

##### Contributors

Thanks to everyone who reported bugs and participated in discussions!

###
[`v2.1.0-1.0.28`](https://redirect.github.com/google/ksp/releases/tag/2.1.0-1.0.28)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.0.21-1.0.28...2.1.0-1.0.28)

##### What's Changed

- Bump Kotlin to 2.1.0 by
[@&#8203;mkmuir0](https://redirect.github.com/mkmuir0) in
[https://github.com/google/ksp/pull/2235](https://redirect.github.com/google/ksp/pull/2235)
- Update intellij to 233.13135.128 by
[@&#8203;ting-yuan](https://redirect.github.com/ting-yuan) in
[https://github.com/google/ksp/pull/2238](https://redirect.github.com/google/ksp/pull/2238)

**Full Changelog**:
https://github.com/google/ksp/compare/2.1.0-RC2-1.0.28...2.1.0-1.0.28

</details>

<details>
<summary>Kotlin/kotlinx.coroutines
(org.jetbrains.kotlinx:kotlinx-coroutines-test)</summary>

###
[`v1.10.1`](https://redirect.github.com/Kotlin/kotlinx.coroutines/blob/HEAD/CHANGES.md#Version-1101)

[Compare
Source](https://redirect.github.com/Kotlin/kotlinx.coroutines/compare/1.10.0...1.10.1)

- Fixed binary incompatibility introduced for non-JVM targets in
[#&#8203;4261](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4261)
([#&#8203;4309](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4309)).

###
[`v1.10.0`](https://redirect.github.com/Kotlin/kotlinx.coroutines/blob/HEAD/CHANGES.md#Version-1100)

[Compare
Source](https://redirect.github.com/Kotlin/kotlinx.coroutines/compare/1.9.0...1.10.0)

- Kotlin was updated to 2.1.0
([#&#8203;4284](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4284)).
- Introduced `Flow.any`, `Flow.all`, and `Flow.none`
([#&#8203;4212](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4212)).
Thanks, [@&#8203;CLOVIS-AI](https://redirect.github.com/CLOVIS-AI)!
- Reorganized `kotlinx-coroutines-debug` and `kotlinx-coroutines-core`
code to avoid a split package between the two artifacts
([#&#8203;4247](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4247)).
Note that directly referencing `kotlinx.coroutines.debug.AgentPremain`
must now be replaced with
`kotlinx.coroutines.debug.internal.AgentPremain`. Thanks,
[@&#8203;sellmair](https://redirect.github.com/sellmair)!
- No longer shade byte-buddy in `kotlinx-coroutines-debug`, reducing the
artifact size and simplifying the build configuration of client code.
Thanks, [@&#8203;sellmair](https://redirect.github.com/sellmair)!
- Fixed `NullPointerException` when using Java-deserialized
`kotlinx-coroutines-core` exceptions
([#&#8203;4291](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4291)).
Thanks, [@&#8203;AlexRiedler](https://redirect.github.com/AlexRiedler)!
- Properly report exceptions thrown by `CoroutineDispatcher.dispatch`
instead of raising internal errors
([#&#8203;4091](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4091)).
Thanks, [@&#8203;zuevmaxim](https://redirect.github.com/zuevmaxim)!
- Fixed a bug that delayed scheduling of a `Dispatchers.Default` or
`Dispatchers.IO` task after a `yield()` in rare scenarios
([#&#8203;4248](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4248)).
- Fixed a bug that prevented the `main()` coroutine on Wasm/WASI from
executing after a `delay()` call in some scenarios
([#&#8203;4239](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4239)).
- Fixed scheduling of `runBlocking` tasks on Kotlin/Native that arrive
after the `runBlocking` block was exited
([#&#8203;4245](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4245)).
- Fixed some terminal `Flow` operators sometimes resuming without taking
cancellation into account
([#&#8203;4254](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4254)).
Thanks, [@&#8203;jxdabc](https://redirect.github.com/jxdabc)!
- Fixed a bug on the JVM that caused coroutine-bound `ThreadLocal`
values not to get cleaned when using non-`CoroutineDispatcher`
continuation interceptors
([#&#8203;4296](https://redirect.github.com/Kotlin/kotlinx.coroutines/issues/4296)).
-   Small tweaks, fixes, and documentation improvements.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/tuskyapp/Tusky).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzkuOTIuMCIsInRhcmdldEJyYW5jaCI6ImRldmVsb3AiLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Conny Duck <git@connyduck.at>
Co-authored-by: Konrad Pozniak <connyduck@users.noreply.github.com>
2025-01-10 14:00:00 +01:00
UlrichKu
d6b276d8df
3771: Add "display replied to" functionality (#4834)
Earlier PR: https://github.com/tuskyapp/Tusky/pull/3778

Fixes: https://github.com/tuskyapp/Tusky/issues/3771
2025-01-06 10:27:27 +01:00
Konrad Pozniak
20cb3848ee
fix "delete all notifications by user" query (#4821)
The brackets were at the wrong position and notification types are
actually serialized differently.

Closes #4817
2024-12-21 20:35:21 +01:00
Konrad Pozniak
cd57352cbd
Notification policy (#4768)
This was so much work wow. I think it works pretty well and is the best
compromise between all the alternative we considered. Yes the
pull-to-refreh on the notifications works slightly different now when
the new bar is visible, but I don't think there is a way around that.

Things I plan to do later, i.e. not as part of this PR or release:
- Cache the notification policy summary for better offline behavior and
less view shifting when it loads
- try to reduce some of the code duplications that are now in there
- if there is user demand, add a "legacy mode" setting where this
feature is disabled even if the server would support it

closes #4331
closes #4550 as won't do
closes #4712 as won't do

<img
src="https://github.com/user-attachments/assets/de322d3c-3775-41e7-be57-28ab7fbaecdf"
width="240"/> <img
src="https://github.com/user-attachments/assets/1ce958a4-4f15-484c-a337-5ad93f36046c"
width="240"/> <img
src="https://github.com/user-attachments/assets/98b0482b-1c05-4c99-a371-f7f4d8a69abd"
width="240"/>
2024-12-03 18:46:50 +01:00
Konrad Pozniak
29914f8fd9
modernize tests (#4777)
- use `runTest` instead of `runBlocking`, where possible
- run all Robolectric tests on Api 34 (where we have most users)
- some new testcase for `TimestampUtilsTest`
- move our only instrumented Android Test, `MigrationsTest`, to unit
test so it runs in CI and expand it to test all migrations
- upgrade Robolectric
- removed truth and espresso as they are no longer needed
2024-12-03 18:46:29 +01:00
Konrad Pozniak
05b2a5d70c
fix crash when post is single line with hashtags (#4778)
```
java.lang.StringIndexOutOfBoundsException
     at android.text.SpannableStringBuilder.<init>(SpannableStringBuilder.java:63)
     at android.text.SpannableStringBuilder.subSequence(SpannableStringBuilder.java:1198)
     at com.keylesspalace.tusky.util.LinkHelper.setClickableText(LinkHelper.kt:99)
     at com.keylesspalace.tusky.adapter.StatusBaseViewHolder.setTextVisible(StatusBaseViewHolder.java:289)
     at com.keylesspalace.tusky.adapter.StatusBaseViewHolder.setSpoilerAndContent(StatusBaseViewHolder.java:244)
     at com.keylesspalace.tusky.adapter.StatusBaseViewHolder.setupWithStatus(StatusBaseViewHolder.java:820)
     at com.keylesspalace.tusky.adapter.StatusViewHolder.setupWithStatus(StatusViewHolder.java:91)
     at com.keylesspalace.tusky.components.timeline.TimelinePagingAdapter.bindViewHolder(TimelinePagingAdapter.kt:100)
     at com.keylesspalace.tusky.components.timeline.TimelinePagingAdapter.onBindViewHolder(TimelinePagingAdapter.kt:82)
     at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7847)
     at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6646)
     at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6917)
     at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:288)
     at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:345)
     at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:361)
     at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:368)
     at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:399)
     at android.os.Handler.handleCallback(Handler.java:959)
     at android.os.Handler.dispatchMessage(Handler.java:100)
     at android.os.Looper.loopOnce(Looper.java:232)
     at android.os.Looper.loop(Looper.java:317)
     at android.app.ActivityThread.main(ActivityThread.java:8705)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
```
2024-11-29 17:10:06 +01:00
Konrad Pozniak
fc52074dcd
remove some unused code (#4776) 2024-11-28 19:15:54 +01:00
Levi Bard
d3feca3a10
mastodon-web-like trailing hashtag bar (#4761)
Rationale: Since the mastodon web UI has started stripping "trailing"
hashtags from post content and shoving it into an ellipsized section at
the bottom of posts, the general hashtag : content ratio is rising.

This is an attempt at adopting a similar functionality for Tusky.

Before:

<img width="420" alt="Screenshot of a hashtag-heavy post on Tusky
nightly"
src="https://github.com/user-attachments/assets/09c286e8-6822-482a-904c-5cb3323ea0e1">


After:
![Screenshot of the same post on this
branch](https://github.com/user-attachments/assets/fa99964d-a057-4727-b9f0-1251a199d5f8)
2024-11-28 19:15:31 +01:00
Konrad Pozniak
e758321866
fix updating filter expiration to indefinite (#4743)
Before we would not send `expires_in` when "indefinite" was selected.
But that left the expiration at the value it was before. To actually set
it to indefinite we need to send `expires_in`, but leave it empty.
With a value class this was actually really nice to fix, the code now
self-documents what the special values mean.

Also fixes a regression from the Material 3 redesign where the filter
duration drop down would not get populated when creating a filter.

Found while working on https://github.com/tuskyapp/Tusky/pull/4742
2024-11-05 20:44:08 +01:00
Konrad Pozniak
f8cf38c81b
fix how emojis in link texts are displayed (#4723)
![Screenshot_20241010_204357](https://github.com/user-attachments/assets/6f511231-b89c-4902-ad89-68c1940415a7)

closes https://github.com/tuskyapp/Tusky/issues/4720
2024-10-11 11:25:43 +02:00
Konrad Pozniak
24f227fd4f
fix crash when there are reblogs in notification statuses (#4638)
```
android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
    at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
    at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:961)
    at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
    at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:89)
    at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.kt:42)
    at androidx.room.EntityInsertionAdapter.insertAndReturnId(EntityInsertionAdapter.kt:101)
    at com.keylesspalace.tusky.db.dao.TimelineStatusDao_Impl$insert$2.call(TimelineStatusDao_Impl.kt:345)
    at com.keylesspalace.tusky.db.dao.TimelineStatusDao_Impl$insert$2.call(TimelineStatusDao_Impl.kt:340)
    at androidx.room.CoroutinesRoom$Companion.execute(CoroutinesRoom.kt:56)
    at com.keylesspalace.tusky.db.dao.TimelineStatusDao_Impl.insert(TimelineStatusDao_Impl.kt:340)
    at com.keylesspalace.tusky.components.notifications.NotificationsRemoteMediator.replaceNotificationRange(NotificationsRemoteMediator.kt:169)
    at com.keylesspalace.tusky.components.notifications.NotificationsRemoteMediator.access$replaceNotificationRange(NotificationsRemoteMediator.kt:36)
    at com.keylesspalace.tusky.components.notifications.NotificationsRemoteMediator$load$3.invokeSuspend(NotificationsRemoteMediator.kt:109)
    at com.keylesspalace.tusky.components.notifications.NotificationsRemoteMediator$load$3.invoke(Unknown Source:8)
    at com.keylesspalace.tusky.components.notifications.NotificationsRemoteMediator$load$3.invoke(Unknown Source:2)
    at androidx.room.RoomDatabaseKt$withTransaction$transactionBlock$1.invokeSuspend(RoomDatabaseExt.kt:62)
    at androidx.room.RoomDatabaseKt$withTransaction$transactionBlock$1.invoke(Unknown Source:8)
    at androidx.room.RoomDatabaseKt$withTransaction$transactionBlock$1.invoke(Unknown Source:4)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:61)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:163)
    at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
    at androidx.room.RoomDatabaseKt$startTransactionCoroutine$2$1$1.invokeSuspend(RoomDatabaseExt.kt:103)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:95)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:69)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
    at androidx.room.RoomDatabaseKt$startTransactionCoroutine$2$1.run(RoomDatabaseExt.kt:99)
    at androidx.room.TransactionExecutor.execute$lambda$1$lambda$0(TransactionExecutor.kt:36)
    at androidx.room.TransactionExecutor.$r8$lambda$FZWr2PGmP3sgXLCiri-DCcePXSs(Unknown Source:0)
    at androidx.room.TransactionExecutor$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
    at java.lang.Thread.run(Thread.java:1012)
```

It looks kinda weird because "x just posted" has a different user than
the actual post, but it works for groups I guess? And definitely better
than crashing.

<img
src="https://github.com/user-attachments/assets/8110ff17-674d-4f36-8df0-453a666856a6"
width="320"/>

closes #4563
2024-09-02 20:00:27 +02:00
Konrad Pozniak
4d73a3c2e3
Fix detecting urls (#4642)
closes #4641
2024-09-02 19:56:38 +02:00
Konrad Pozniak
31e4f08966
fix account switching (#4636)
closes #4631 
closes #4629 

and other weirdness introduced in Tusky 26.1.
I did a lot of testing on 2 physical devices and multiple emulators. It
definitely is better than before, but probably still not perfect.
2024-09-02 19:49:22 +02:00
Konrad Pozniak
c7387c7b52
prevent mixup of account timelines (#4599)
This does 2 things:

- Removes `AccountSwitchInterceptor`, the main culprit for the bug. APIs
can no longer change their base url after they have been created. As a
result they are not Singletons anymore.
- Additionally, I refactored how MainActivity handles Intents to make it
less likely to have multiple instances of it active.

Here is how I could reliably reproduce the bug:

- Be logged in with account A and B
- Write a post with account A, cancel it before it sends (go into flight
mode for that)
- Switch to account B
- Open the "this post failed to send" notification from account A,
drafts will open
- Go back. You are in the MainActivity of account A, everything seems
fine.
- Go back again. You are in the old, now broken MainActivity of account
B. It uses the database of account B but the network of account A.
Refreshing will show posts from A.

closes #4567 
closes #4554
closes #4402 
closes #4148
closes #2663
and possibly #4588
2024-08-14 18:58:12 +02:00
Konrad Pozniak
892801b83a
add more options to default reply visibility setting (#4568)
This adds `direct` and `match_default_post_visibility` as options to the
default reply visibility setting. `match_default_post_visibility` will
be the default for new accounts.

closes https://github.com/tuskyapp/Tusky/issues/4555

<img
src="https://github.com/user-attachments/assets/b256ff32-cd49-4274-903b-96da96451e0e"
width="320"/>
2024-08-02 17:15:10 +02:00
Konrad Pozniak
8a57bcc3f4
only check once for filters v2 availability (#4539)
Instead of calling the endpoint every time filters are needed, it will
be called only once and the result cached. This will result in quite
some requests less on instances supporting v2.

I also tested v1 filters and made some small improvements. We should
[remove filters v1
support](https://github.com/tuskyapp/Tusky/issues/4538) some time in the
future though.
2024-07-03 21:18:09 +02:00
Eliot Lash
9883bfa7c2
Add option for default reply privacy set to unlisted by default (#4496)
This PR fixes https://github.com/tuskyapp/Tusky/issues/2798 and is
mostly based on and supersedes
https://github.com/tuskyapp/Tusky/pull/2826 but I have fixed all merge
conflicts and unit tests.

I tested the changes locally and the setting takes effect immediately
for replies, and persists across killing the app.

---------

Co-authored-by: Eva Tatarka <eva@tatarka.me>
Co-authored-by: Konrad Pozniak <connyduck@users.noreply.github.com>
2024-06-09 20:25:03 +02:00
Konrad Pozniak
8aaca3bb2c
improve span highlighting (#4480)
At first I thought simply changing the regex might help, but then I
found more and more differences between Mastodon and Tusky, so I decided
to reimplement the thing. I added 74 testcases that I all compared to
Mastodon to make sure they are correct.

On an Fairphone 4 the new implementation is faster, on an Samsung Galaxy
Tab S3 slower.

Testcases for the benchmark:
```
test of a status with #one hashtag http
```
```
test
http:// #hashtag https://connyduck.at/
http://example.org
this is a #test
and this is a @mention@test.com @test @test@test456@test.com
```
```
@mention@test.social Just your ordinary mention with a hashtag
#test
```
```
@mention@test.social Just your ordinary mention with a url
https://riot.im/app/#/room/#Tusky:matrix.org
```



FP4:
```
       11.159   ns          15 allocs    Benchmark.new_1
      119.701   ns          43 allocs    Benchmark.new_2
       21.895   ns          24 allocs    Benchmark.new_3
       87.512   ns          32 allocs    Benchmark.new_4

       16.592   ns          46 allocs    Benchmark.old_1
      134.381   ns         169 allocs    Benchmark.old_2
       28.355   ns          68 allocs    Benchmark.old_3
       45.221   ns          77 allocs    Benchmark.old_4
```

SGT3:
```
       43,785   ns          18 allocs    Benchmark.new_1
      446,074   ns          43 allocs    Benchmark.new_2
       78,802   ns          26 allocs    Benchmark.new_3
      315,478   ns          32 allocs    Benchmark.new_4

       42,186   ns          45 allocs    Benchmark.old_1
      353,570   ns         157 allocs    Benchmark.old_2
       72,376   ns          66 allocs    Benchmark.old_3
      122,985   ns          74 allocs    Benchmark.old_4
```


benchmark code is here: https://github.com/tuskyapp/tusky-span-benchmark

closes https://github.com/tuskyapp/Tusky/issues/4425
2024-06-02 16:32:58 +02:00
Konrad Pozniak
d554d71958
inject SharedPreferences (#4441)
(this one is for @charlag)

Calling `PreferenceManager.getDefaultSharedPreferences()` will read the
preference file from disk every time. This PR makes `SharedPreferences`
a singleton so they will only be created once at appstart (with a few
exceptions where it is hard to inject, e.g. in the `openLink` helper)
which should help getting our ANRs down.

```
StrictMode policy violation; ~duration=285 ms: android.os.strictmode.DiskReadViolation
    at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1666)
    at libcore.io.BlockGuardOs.access(BlockGuardOs.java:74)
    at libcore.io.ForwardingOs.access(ForwardingOs.java:128)
    at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:8054)
    at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:313)
    at java.io.File.exists(File.java:813)
    at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:790)
    at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:781)
    at android.app.ContextImpl.getPreferencesDir(ContextImpl.java:737)
    at android.app.ContextImpl.getSharedPreferencesPath(ContextImpl.java:962)
    at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:583)
    at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:221)
    at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:221)
    at androidx.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:119)
    at com.keylesspalace.tusky.BaseActivity.onCreate(BaseActivity.java:96)
   ...
```
2024-05-24 08:05:09 +02:00
Christophe Beyls
dc4ca06551
Replace Dagger-Android with Hilt and remove Kapt (#4423)
Hilt is an annotation processor built on top of Dagger which allows to
remove all the Android dependency injection boilerplate code (currently
around 900 lines) by writing it for us.

Hilt can use KSP instead of Kapt so Kapt can be completely removed from
the project. Kapt is slow, deprecated and has a few compatibility
issues. Removing Kapt will improve build times since no Java stubs have
to be generated for Kotlin classes anymore (Note that KSP also processes
annotations in Java classes so it can completely replace Kapt).

- Remove all modules related to manual dependency injection
configuration.
- Rename `AppModule` to `StorageModule` since it now only contains
configuration to retrieve the DataBase and SharedPreferences.
- Annotate all entry points (Activities, Fragments, BroadcastReceivers
and Services) with `@AndroidEntryPoint`.
- Annotate all injected ViewModels with `@HiltViewModel` and replace the
custom ViewModel Factory with the default one (which integrates with the
one generated by Hilt).
- Add a public field to allow overriding the default
ViewModelProvider.Factory in `BaseActivity` in tests.
- Annotate tested Activities with `@OptionalInject` since Activity tests
currently rely on the Activities not being injected automatically.
- Annotate injected `Context` arguments with `@ApplicationContext`. Hilt
provides the `Context` binding automatically but requires to specify if
the Application or Activity Context is wanted.
- Add WorkManager Hilt integration so all Workers are injected by Hilt
automatically using `HiltWorkerFactory`.
- Lazily initialize WorkManager in `TuskyApplication`.
- Remove Kapt and Kapt workarounds.
- ~~Remove toolchain configuration for Java 21. Toolchains force the
Java bytecode to match the JDK version used to build the project, and
apparently Hilt doesn't run inside the toolchain so cannot process the
source code if the JDK version of the toolchain is higher than the JDK
used to run Gradle. [And configuring a toolchain for an older Java
version causes other
issues](https://jakewharton.com/gradle-toolchains-are-rarely-a-good-idea/).
**Removing toolchains configuration doesn't prevent the project from
being built using JDK 21** or more recent versions but allows to build
the project using older JDKs as well.~~
Added a fix to allow Hilt to properly use the JDK toolchain.
- ~~Set the Java and Kotlin bytecode target to Java 17. The standard
bytecode target for Android projects is usually Java 8 or 11 (any higher
version doesn't provide any benefit but may cause compatibility issues).
However, since the app currently uses a library built against Java 17
bytecode (`networkresult-calladapter`), it needs to target at least Java
17 bytecode as well.~~
- Update the Dagger 2 URL in the licenses screen. Hilt is part of Dagger
2 so the label wasn't changed.
2024-05-10 15:55:07 +02:00
Konrad Pozniak
b2c0b18c8e
Refactor notifications to Kotlin & paging (#4026)
This refactors the NotificationsFragment and related classes to Kotlin &
paging.
While trying to preserve as much of the original behavior as possible,
this adds the following improvements as well:
- The "show notifications filter" preference was added again
- The "load more" button now has a background ripple effect when clicked
- The "legal" report category of Mastodon 4.2 is now supported in report
notifications
- Unknown notifications now display "unknown notification type" instead
of an empty line

Other code quality improvements:
- All views from xml layouts are now referenced via ViewBindings
- the classes responsible for showing system notifications were moved to
a new package `systemnotifications` while the classes from this
refactoring are in `notifications`
- the id of the local Tusky account is now called `tuskyAccountId` in
all places I could find

closes https://github.com/tuskyapp/Tusky/issues/3429

---------

Co-authored-by: Zongle Wang <wangzongler@gmail.com>
2024-05-03 18:27:10 +02:00
Christophe Beyls
df7b11afc3
Replace Gson library with Moshi (#4309)
**! ! Warning**: Do not merge before testing every API call and database
read involving JSON !

**Gson** is obsolete and has been superseded by **Moshi**. But more
importantly, parsing Kotlin objects using Gson is _dangerous_ because
Gson uses Java serialization and is **not Kotlin-aware**. This has two
main consequences:

- Fields of non-null types may end up null at runtime. Parsing will
succeed, but the code may crash later with a `NullPointerException` when
trying to access a field member;
- Default values of constructor parameters are always ignored. When
absent, reference types will be null, booleans will be false and
integers will be zero.

On the other hand, Kotlin-aware parsers like **Moshi** or **Kotlin
Serialization** will validate at parsing time that all received fields
comply with the Kotlin contract and avoid errors at runtime, making apps
more stable and schema mismatches easier to detect (as long as logs are
accessible):

- Receiving a null value for a non-null type will generate a parsing
error;
- Optional types are declared explicitly by adding a default value. **A
missing value with no default value declaration will generate a parsing
error.**

Migrating the entity declarations from Gson to Moshi will make the code
more robust but is not an easy task because of the semantic differences.

With Gson, both nullable and optional fields are represented with a null
value. After converting to Moshi, some nullable entities can become
non-null with a default value (if they are optional and not nullable),
others can stay nullable with no default value (if they are mandatory
and nullable), and others can become **nullable with a default value of
null** (if they are optional _or_ nullable _or_ both). That third option
is the safest bet when it's not clear if a field is optional or not,
except for lists which can usually be declared as non-null with a
default value of an empty list (I have yet to see a nullable array type
in the Mastodon API).

Fields that are currently declared as non-null present another
challenge. In theory, they should remain as-is and everything will work
fine. In practice, **because Gson is not aware of nullable types at
all**, it's possible that some non-null fields currently hold a null
value in some cases but the app does not report any error because the
field is not accessed by Kotlin code in that scenario. After migrating
to Moshi however, parsing such a field will now fail early if a null
value or no value is received.

These fields will have to be identified by heavily testing the app and
looking for parsing errors (`JsonDataException`) and/or by going through
the Mastodon documentation. A default value needs to be added for
missing optional fields, and their type could optionally be changed to
nullable, depending on the case.

Gson is also currently used to serialize and deserialize objects to and
from the local database, which is also challenging because backwards
compatibility needs to be preserved. Fortunately, by default Gson omits
writing null fields, so a field of type `List<T>?` could be replaced
with a field of type `List<T>` with a default value of `emptyList()` and
reading back the old data should still work. However, nullable lists
that are written directly (not as a field of another object) will still
be serialized to JSON as `"null"` so the deserializing code must still
be handling null properly.

Finally, changing the database schema is out of scope for this pull
request, so database entities that also happen to be serialized with
Gson will keep their original types even if they could be made non-null
as an improvement.

In the end this is all for the best, because the app will be more
reliable and errors will be easier to detect by showing up earlier with
a clear error message. Not to mention the performance benefits of using
Moshi compared to Gson.

- Replace Gson reflection with Moshi Kotlin codegen to generate all
parsers at compile time.
- Replace custom `Rfc3339DateJsonAdapter` with the one provided by
moshi-adapters.
- Replace custom `JsonDeserializer` classes for Enum types with
`EnumJsonAdapter.create(T).withUnknownFallback()` from moshi-adapters to
support fallback values.
- Replace `GuardedBooleanAdapter` with the more generic `GuardedAdapter`
which works with any type. Any nullable field may now be annotated with
`@Guarded`.
- Remove Proguard rules related to Json entities. Each Json entity needs
to be annotated with `@JsonClass` with no exception, and adding this
annotation will ensure that R8/Proguard will handle the entities
properly.
- Replace some nullable Boolean fields with non-null Boolean fields with
a default value where possible.
- Replace some nullable list fields with non-null list fields with a
default value of `emptyList()` where possible.
- Update `TimelineDao` to perform all Json conversions internally using
`Converters` so no Gson or Moshi instance has to be passed to its
methods.
- ~~Create a custom `DraftAttachmentJsonAdapter` to serialize and
deserialize `DraftAttachment` which is a special entity that supports
more than one json name per field. A custom adapter is necessary because
there is not direct equivalent of `@SerializedName(alternate = [...])`
in Moshi.~~ Remove alternate names for some `DraftAttachment` fields
which were used as a workaround to deserialize local data in 2-years old
builds of Tusky.
- Update tests to make them work with Moshi.
- Simplify a few `equals()` implementations.
- Change a few functions to `val`s
- Turn `NetworkModule` into an `object` (since it contains no abstract
methods).

Please test the app thoroughly before merging. There may be some fields
currently declared as mandatory that are actually optional.
2024-04-02 21:01:04 +02:00
Konrad Pozniak
b85ada930b
fix warnings in test runs (#4340)
Found while working on #4026 but not directly related.

Two cases of unmocked methods and one of unclosed resource.
2024-03-28 09:13:05 +01:00
Willow
fbb22799dc
Machine translation of posts (#4307) 2024-03-09 16:12:18 +01:00
Christophe Beyls
40fde54e0b
Replace RxJava3 code with coroutines (#4290)
This pull request removes the remaining RxJava code and replaces it with
coroutine-equivalent implementations.

- Remove all duplicate methods in `MastodonApi`:
- Methods returning a RxJava `Single` have been replaced by suspending
methods returning a `NetworkResult` in order to be consistent with the
new code.
- _sync_/_async_ method variants are replaced with the _async_ version
only (suspending method), and `runBlocking{}` is used to make the async
variant synchronous.
- Create a custom coroutine-based implementation of `Single` for usage
in Java code where launching a coroutine is not possible. This class can
be deleted after remaining Java code has been converted to Kotlin.
- `NotificationsFragment.java` can subscribe to `EventHub` events by
calling the new lifecycle-aware `EventHub.subscribe()` method. This
allows using the `SharedFlow` as single source of truth for all events.
- Rx Autodispose is replaced by `lifecycleScope.launch()` which will
automatically cancel the coroutine when the Fragment view/Activity is
destroyed.
- Background work is launched in the existing injectable
`externalScope`, since using `GlobalScope` is discouraged.
`externalScope` has been changed to be a `@Singleton` and to use the
main dispatcher by default.
- Transform `ShareShortcutHelper` to an injectable utility class so it
can use the application `Context` and `externalScope` as provided
dependencies to launch a background coroutine.
- Implement a custom Glide extension method
`RequestBuilder.submitAsync()` to do the same thing as
`RequestBuilder.submit().get()` in a non-blocking way. This way there is
no need to switch to a background dispatcher and block a background
thread, and cancellation is supported out-of-the-box.
- An utility method `Fragment.updateRelativeTimePeriodically()` has been
added to remove duplicate logic in `TimelineFragment` and
`NotificationsFragment`, and the logic is now implemented using a simple
coroutine instead of `Observable.interval()`. Note that the periodic
update now happens between onStart and onStop instead of between
onResume and onPause, since the Fragment is not interactive but is still
visible in the started state.
- Rewrite `BottomSheetActivityTest` using coroutines tests.
- Remove all RxJava library dependencies.
2024-02-29 15:28:48 +01:00
Konrad Pozniak
b976fe5296
full sdk 34 support (#4224)
builds upon work from #4082 

Additionally fixes some deprecations and adds support for [predictive
back](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture).
I also refactored how the activity transitions work because they are
closely related to predictive back. The awkward
`finishWithoutSlideOutAnimation` is gone, activities that have been
started with slide in will now automatically close with slide out.

To test predictive back you need an emulator or device with Sdk 34
(Android 14) and then enable it in the developer settings.

Predictive back requires the back action to be determined before it
actually occurs so the system can play the right predictive animation,
which made a few reorganisations necessary.

closes #4082 
closes #4005 
unlocks a bunch of dependency upgrades that require sdk 34

---------

Co-authored-by: Goooler <wangzongler@gmail.com>
2024-02-23 10:27:19 +01:00
Konrad Pozniak
5192fb08a5
upgrade ktlint plugin to 12.0.3 (#4169)
There are some new rules, I think they mostly make sense, except for the
max line length which I had to disable because we are over it in a lot
of places.

---------

Co-authored-by: Goooler <wangzongler@gmail.com>
2024-01-04 17:00:55 +01:00
Konrad Pozniak
ee3760fcc9
correctly count emojis when composing a post (#4152)
Thx to @evant for the help 

closes #4140
2023-12-10 07:38:25 +01:00
Levi Bard
21a4308fef
Fix deserialization of the response from friendica on api/v2/instance (#4103)
Fixes #4100

---------

Co-authored-by: Konrad Pozniak <connyduck@users.noreply.github.com>
2023-11-13 10:05:28 +01:00
Levi Bard
131ebabe85
Add support for v2/instance (#4062)
…with fallback to v1
2023-10-25 12:53:10 +02:00
UlrichKu
2dc27bca2a
use link icon instead of emoji when showing hidden urls (#4031)
Looks way better imho. Also closes #4028.

Before vs After:


![Screenshot_20230924_163714](https://github.com/tuskyapp/Tusky/assets/10157047/5b0b745a-4574-4e37-988e-b04997ac55f1)


![Screenshot_20230924_162657](https://github.com/tuskyapp/Tusky/assets/10157047/58a3482f-6afb-4b10-9891-f7a86af7f2bf)
2023-09-26 21:46:05 +02:00
Konrad Pozniak
54e92b2156
improve local status updates (#3480)
The idea here is: Everytime we get hold of a new version of a post, we
update everything about that post everywhere.
This makes the distincion between different event types unnecessary, as
everythng is just a `StatusChangedEvent`.
The main benefit is that posts should be up-to-date more often, which is
important considering there is now editing and #3413
2023-09-26 09:08:58 +02:00
Conny Duck
31ad946e1a use link icon instead of emoji when showing hidden urls 2023-09-24 16:28:53 +02:00
Angelo Suzuki
fa80a0123a
Add "Trending posts" (statuses) feed (#4007)
Add "Trending posts" (statuses) feed.

This feed is a good source of interesting accounts to follow and,
personally, a sort of "Front page of the Fediverse".

Since #3908 and #3910 (which would provide a more thorough, albeit
complex, access to trending things) won't get merged, I'd like to
address this missing feed by simply adding another tab/feed.

~~If desired, I can move the second commit (fixing lint) to another
PR.~~

## Screenshots
### Tab
<img
src="https://github.com/tuskyapp/Tusky/assets/1063155/6a71a97e-673e-44c7-b67d-9b1df0bed4f5"
width=320 /> <img
src="https://github.com/tuskyapp/Tusky/assets/1063155/9bf60b23-d2f3-4dd8-8af6-e96647b02121"
width=320 />

### Activity
<img
src="https://github.com/tuskyapp/Tusky/assets/1063155/4e07dea3-d97f-42c6-8551-492a3116fcfa"
width=320 /> <img
src="https://github.com/tuskyapp/Tusky/assets/1063155/ad00a134-d622-43f4-8305-84cfa7fed706"
width=320 />
2023-09-14 22:37:41 +02:00
Lakoja
4af160853d Remove unneeded code 2023-09-11 21:58:56 +02:00
Levi Bard
02404564e0
Detect Bookwyrm URL formats (#3936)
While helping test an issue with
[Bookwyrm](https://github.com/bookwyrm-social/bookwyrm) I noticed that
the URL formats used by that project aren't checked as possible profile
or post links. They're quite close to a couple of others, so I just
copied close examples and edited a couple of terms.

It's pretty minor, I just used a previous commit as a reference. Let me
know if it needs anything more though. I've only quickly tested it on a
local build with a couple of links against a live Bookwyrm and it picks
them up as expected now.
2023-09-05 09:33:51 +02:00
Nik Clayton
059352f471
Display notification filter/clear actions as menu items (#3877)
Previously the notification filter and clear actions were shown as
buttons in the UI, with a preference that determined whether they were
displayed.

Remove this preference, and display them as menu items.

- "Filter notifications" is shown as an icon, if possible
- "Clear notifications" is only ever shown as a menu item, to reduce the
chance the user inadvertently selects it

To ensure that the options menu appears correctly, remove the code that
creates a "fake" action bar, and adjust the layouts so that there are
three toolbars;

- mainToolbar -- displays the icons, and the current "location" (Home,
Notifications, etc)
- topNav -- displays the row of tabs at the top
- bottomNav -- displays the row of tabs at the bottom

Only one of them is set as the support action bar (depending on the
user's preferences). This provides the "show a logo" and "show the
options menu" functionality as standard, without needing to re-implement
as the previous code did.
2023-08-19 14:41:10 +02:00
83de22950d
Update LinkHelperTest.kt
Remove negative test for a URL that's invalid for Pleroma, but valid for Bookwyrm
2023-08-08 19:10:36 +10:00
e290df5499
Update LinkHelperTest.kt
Add test URLs for Bookwyrm
2023-08-07 19:43:03 +10:00
Goooler
40bd95d752
Kotlin 1.9.0 (#3835)
Update to Kotlin 1.9.0 and migrate to newer language idioms.

- Remove unnecessary @OptIn for features migrated to mainstream
- Use `data object` where appropriate
- Use new enum `entries` property
2023-08-02 09:04:24 +02:00
Nik Clayton
839d8bcc04
Migrate to AGP 8.0.2 / Android Studio Flamingo / Java 17 (#3541)
- Update AGP in version catalog to 8.0.2
- Set Java version to 17
- Enable non-final resource IDs
2023-07-30 15:50:04 +02:00
SpaceFox
9a7e456edf
Ensures AbsoluteTimeFormatterTest class is not locale-dependant (#3863)
As tests are run against locale JVM and test does not force
a locale to run, so some tests may fail due to a different result only
due to the locale of the JVM used.

Example here with test `same year formatting` in class
`AbsoluteTimeFormatterTest` line 30 on a French JVM. There may be other
lines to fail with other languages.

Fixes #3859
2023-07-19 08:50:41 +02:00
Nik Clayton
5e8a63a046
Throttle UI actions instead of debouncing (#3651)
Introduce Flow<T>.throttleFirst(). In a flow this emits the first value,
and each value afterwards that is > some timeout after the previous
value.

This prevents accidental double-taps on UI elements from generating
multiple-actions.

The previous code used debounce(). That has a similar effect, but with
debounce() the code has to wait until after the timeout period has
elapsed before it can process the action, leading to an unnecessary
UI delay.

With throttleFirst a value is emitted immediately, there's no need
to wait. It's subsequent values that are potentially throttled.
2023-06-11 13:34:22 +02:00
Nik Clayton
071e00774e
Replace shortNumber() with formatNumber() (#3519)
formatNumber() was existing code to show numbers with suffixes like K, M, etc, so re-use that code and delete shortNumber().

Update the tests to (a) test formatNumber(), and (b) be parameterised.
2023-06-10 16:29:26 +02:00