chinwag-android/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt
Levi Bard ff8dd37855
Support the mastodon 4 filter api (#3188)
* Replace "warn"-filtered posts in timelines and thread view with placeholders

* Adapt hashtag muting interface

* Rework filter UI

* Add icon for account preferences

* Clean up UI

* WIP: Use chips instead of a list. Adjust padding

* Scroll the filter edit activity

Nested scrolling views (e.g., an activity that scrolls with an embedded list
that also scrolls) can be difficult UI.

Since the list of contexts is fixed, replace it with a fixed collection of
switches, so there's no need to scroll the list.

Since the list of actions is only two (warn, hide), and are mutually
exclusive, replace the spinner with two radio buttons.

Use the accent colour and title styles on the different heading titles in
the layout, to match the presentation in Preferences.

Add an explicit "Cancel" button.

The layout is a straightforward LinearLayout, so use that instead of
ConstraintLayout, and remove some unncessary IDs.

Update EditFilterActivity to handle the new layout.

* Cleanup

* Add more information to the filter list view

* First pass on code review comments

* Add view model to filters activity

* Add view model to edit filters activity

* Only use the status wrapper for filtered statuses

* Relint

---------

Co-authored-by: Nik Clayton <nik@ngo.org.uk>
2023-03-11 13:12:50 +01:00

117 lines
4.1 KiB
Kotlin

/* Copyright 2021 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.db
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.TypeConverters
import com.keylesspalace.tusky.entity.FilterResult
import com.keylesspalace.tusky.entity.Status
/**
* We're trying to play smart here. Server sends us reblogs as two entities one embedded into
* another (reblogged status is a field inside of "reblog" status). But it's really inefficient from
* the DB perspective and doesn't matter much for the display/interaction purposes.
* What if when we store reblog we don't store almost empty "reblog status" but we store
* *reblogged* status and we embed "reblog status" into reblogged status. This reversed
* relationship takes much less space and is much faster to fetch (no N+1 type queries or JSON
* serialization).
* "Reblog status", if present, is marked by [reblogServerId], and [reblogAccountId]
* fields.
*/
@Entity(
primaryKeys = ["serverId", "timelineUserId"],
foreignKeys = (
[
ForeignKey(
entity = TimelineAccountEntity::class,
parentColumns = ["serverId", "timelineUserId"],
childColumns = ["authorServerId", "timelineUserId"]
)
]
),
// Avoiding rescanning status table when accounts table changes. Recommended by Room(c).
indices = [Index("authorServerId", "timelineUserId")]
)
@TypeConverters(Converters::class)
data class TimelineStatusEntity(
val serverId: String, // id never flips: we need it for sorting so it's a real id
val url: String?,
// our local id for the logged in user in case there are multiple accounts per instance
val timelineUserId: Long,
val authorServerId: String?,
val inReplyToId: String?,
val inReplyToAccountId: String?,
val content: String?,
val createdAt: Long,
val editedAt: Long?,
val emojis: String?,
val reblogsCount: Int,
val favouritesCount: Int,
val repliesCount: Int,
val reblogged: Boolean,
val bookmarked: Boolean,
val favourited: Boolean,
val sensitive: Boolean,
val spoilerText: String,
val visibility: Status.Visibility,
val attachments: String?,
val mentions: String?,
val tags: String?,
val application: String?,
val reblogServerId: String?, // if it has a reblogged status, it's id is stored here
val reblogAccountId: String?,
val poll: String?,
val muted: Boolean?,
/** Also used as the "loading" attribute when this TimelineStatusEntity is a placeholder */
val expanded: Boolean,
val contentCollapsed: Boolean,
val contentShowing: Boolean,
val pinned: Boolean,
val card: String?,
val language: String?,
val filtered: List<FilterResult>?,
) {
val isPlaceholder: Boolean
get() = this.authorServerId == null
}
@Entity(
primaryKeys = ["serverId", "timelineUserId"]
)
data class TimelineAccountEntity(
val serverId: String,
val timelineUserId: Long,
val localUsername: String,
val username: String,
val displayName: String,
val url: String,
val avatar: String,
val emojis: String,
val bot: Boolean
)
data class TimelineStatusWithAccount(
@Embedded
val status: TimelineStatusEntity,
@Embedded(prefix = "a_")
val account: TimelineAccountEntity? = null, // null when placeholder
@Embedded(prefix = "rb_")
val reblogAccount: TimelineAccountEntity? = null // null when no reblog
)