-
-
Notifications
You must be signed in to change notification settings - Fork 389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor notifications to Kotlin & paging #4026
Refactor notifications to Kotlin & paging #4026
Conversation
The basic database setup is done, it is a bit more complicated than the timeline one and needs some cleanup but works fine. @charlag maybe you can have a look at it, I would be very interested in your opinion. |
# Conflicts: # app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationFetcher.kt # app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java # app/src/main/res/values/strings.xml
import com.keylesspalace.tusky.entity.Status | ||
import java.util.Date | ||
|
||
data class NotificationDataEntity( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the end we should give each class here a one-sentence doc explaining what they are for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was confused for a while before I realized that this class is not actually a DB table
I couldn't get too deeply into it but as far as I understand you did the same trick as with posts, just more complicated. Nice! I think merging accounts could work if we can take care of cleanup but it gets a bit complicated. We could merge statuses but it would be a lot of work. We would need a table with just IDs of the posts in timeline and a separate table with just posts (that's how Masto work internally too, with timelines being in Redis and posts in db). Then we could cache arbitrary posts in there. I think at least for now your approach makes a lot of sense? |
# Conflicts: # app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt # app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt # app/src/main/java/com/keylesspalace/tusky/components/notifications/ReportNotificationViewHolder.kt # app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelinePagingAdapter.kt # app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java # app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
Ok I think I have addressed everything |
ListStatusAccessibilityDelegate( | ||
binding.recyclerView, | ||
this, | ||
StatusProvider { pos: Int -> | ||
if (pos in 0 until adapter.itemCount) { | ||
val notification = adapter.peek(pos) | ||
// We support replies only for now | ||
if (notification is NotificationViewData.Concrete) { | ||
return@StatusProvider notification.statusViewData | ||
} else { | ||
return@StatusProvider null | ||
} | ||
} else { | ||
null | ||
} | ||
} | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ListStatusAccessibilityDelegate( | |
binding.recyclerView, | |
this, | |
StatusProvider { pos: Int -> | |
if (pos in 0 until adapter.itemCount) { | |
val notification = adapter.peek(pos) | |
// We support replies only for now | |
if (notification is NotificationViewData.Concrete) { | |
return@StatusProvider notification.statusViewData | |
} else { | |
return@StatusProvider null | |
} | |
} else { | |
null | |
} | |
} | |
) | |
) | |
ListStatusAccessibilityDelegate( | |
binding.recyclerView, | |
this | |
) { pos: Int -> | |
if (pos in 0 until adapter.itemCount) { | |
val notification = adapter.peek(pos) | |
// We support replies only for now | |
if (notification is NotificationViewData.Concrete) { | |
notification.statusViewData | |
} else { | |
null | |
} | |
} else { | |
null | |
} | |
} |
import com.keylesspalace.tusky.util.StatusDisplayOptions | ||
import com.keylesspalace.tusky.viewdata.NotificationViewData | ||
|
||
interface NotificationActionListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface NotificationActionListener { | |
fun interface NotificationActionListener { |
fun onViewReport(reportId: String?) | ||
} | ||
|
||
interface NotificationsViewHolder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface NotificationsViewHolder { | |
fun interface NotificationsViewHolder { |
sealed class NotificationViewData { | ||
|
||
abstract val id: String | ||
|
||
data class Concrete( | ||
override val id: String, | ||
val type: Notification.Type, | ||
val account: TimelineAccount, | ||
val statusViewData: StatusViewData.Concrete?, | ||
val report: Report? | ||
) : NotificationViewData() | ||
|
||
data class Placeholder( | ||
override val id: String, | ||
val isLoading: Boolean | ||
) : NotificationViewData() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sealed class NotificationViewData { | |
abstract val id: String | |
data class Concrete( | |
override val id: String, | |
val type: Notification.Type, | |
val account: TimelineAccount, | |
val statusViewData: StatusViewData.Concrete?, | |
val report: Report? | |
) : NotificationViewData() | |
data class Placeholder( | |
override val id: String, | |
val isLoading: Boolean | |
) : NotificationViewData() | |
sealed interface NotificationViewData { | |
val id: String | |
data class Concrete( | |
override val id: String, | |
val type: Notification.Type, | |
val account: TimelineAccount, | |
val statusViewData: StatusViewData.Concrete?, | |
val report: Report? | |
) : NotificationViewData | |
data class Placeholder( | |
override val id: String, | |
val isLoading: Boolean | |
) : NotificationViewData |
Should the android notifications get dismissed when we open the notifications view? |
yes |
bindViewHolder(viewHolder, position, payloads) | ||
} | ||
|
||
private fun bindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int, payloads: List<Any>?) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
payloads
argument in aRecyclerView.Adapter
is actually nevernull
, it will be an empty list by default. You can change all the associated methods signatures to non-null.
I still recommend to change all the payloads
arguments to be non-null, then all the associated .isNullOrEmpty()
tests to .isEmpty()
shouldFilterStatus(notificationViewData) != Filter.Action.HIDE | ||
} | ||
} | ||
.flowOn(Dispatchers.Default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to change the upstream dispatcher if you already specify it in pagingData.map(<Dispatcher>)
and pagingData.filter(<Dispatcher>)
.
It's probably better to keep this line but remove the custom dispatcher for map and filter.
...c/main/java/com/keylesspalace/tusky/components/notifications/StatusNotificationViewHolder.kt
Outdated
Show resolved
Hide resolved
bindingAdapterPosition | ||
) | ||
} | ||
binding.notificationContent.visibility = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also use ktx extension properties for view visibility:
binding.notificationContent.isVisible = statusViewData.isExpanded
Note that the custom View.visible
extension method used in the project serves the same purpose with a different syntax.
...src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsPagingAdapter.kt
Show resolved
Hide resolved
val translation = translations[timelineStatus.status.serverId] | ||
timelineStatus.toViewData( | ||
moshi, | ||
pagingData.map(Dispatchers.Default.asExecutor()) { timelineData -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same remark here, it's probably overkill to specify a dispatcher both here and in flowOn()
app/src/main/java/com/keylesspalace/tusky/viewdata/NotificationViewData.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt
Outdated
Show resolved
Hide resolved
# Conflicts: # app/src/main/java/com/keylesspalace/tusky/util/ShareShortcutHelper.kt
# Conflicts: # app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
The menu doesn't show up anymore after merging #4026. I assume this was unintentional since the Fragment still implements `MenuProvider`.
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:
Other code quality improvements:
systemnotifications
while the classes from this refactoring are innotifications
tuskyAccountId
in all places I could findcloses #3429