mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 11:39:42 +01:00
Merge pull request #4333 from nextcloud/issue-4257-archive-conversation
Archived Conversations 🗃️
This commit is contained in:
commit
085711d077
@ -2,7 +2,7 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 11,
|
||||
"identityHash": "bc802cadfdef41d3eb94ffbb0729eb89",
|
||||
"identityHash": "7edb537b6987d0de6586a6760c970958",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "User",
|
||||
@ -138,7 +138,7 @@
|
||||
},
|
||||
{
|
||||
"tableName": "Conversations",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `displayName` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `avatarVersion` TEXT NOT NULL, `callFlag` INTEGER NOT NULL, `callRecording` INTEGER NOT NULL, `callStartTime` INTEGER NOT NULL, `canDeleteConversation` INTEGER NOT NULL, `canLeaveConversation` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `description` TEXT NOT NULL, `hasCall` INTEGER NOT NULL, `hasPassword` INTEGER NOT NULL, `isCustomAvatar` INTEGER NOT NULL, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `lastMessage` TEXT, `lastPing` INTEGER NOT NULL, `lastReadMessage` INTEGER NOT NULL, `lobbyState` TEXT NOT NULL, `lobbyTimer` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `name` TEXT NOT NULL, `notificationCalls` INTEGER NOT NULL, `notificationLevel` TEXT NOT NULL, `objectType` TEXT NOT NULL, `participantType` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `readOnly` TEXT NOT NULL, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, `sessionId` TEXT NOT NULL, `status` TEXT, `statusClearAt` INTEGER, `statusIcon` TEXT, `statusMessage` TEXT, `type` TEXT NOT NULL, `unreadMention` INTEGER NOT NULL, `unreadMentionDirect` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `displayName` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `avatarVersion` TEXT NOT NULL, `callFlag` INTEGER NOT NULL, `callRecording` INTEGER NOT NULL, `callStartTime` INTEGER NOT NULL, `canDeleteConversation` INTEGER NOT NULL, `canLeaveConversation` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `description` TEXT NOT NULL, `hasCall` INTEGER NOT NULL, `hasPassword` INTEGER NOT NULL, `isCustomAvatar` INTEGER NOT NULL, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `lastMessage` TEXT, `lastPing` INTEGER NOT NULL, `lastReadMessage` INTEGER NOT NULL, `lobbyState` TEXT NOT NULL, `lobbyTimer` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `name` TEXT NOT NULL, `notificationCalls` INTEGER NOT NULL, `notificationLevel` TEXT NOT NULL, `objectType` TEXT NOT NULL, `participantType` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `readOnly` TEXT NOT NULL, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, `sessionId` TEXT NOT NULL, `status` TEXT, `statusClearAt` INTEGER, `statusIcon` TEXT, `statusMessage` TEXT, `type` TEXT NOT NULL, `unreadMention` INTEGER NOT NULL, `unreadMentionDirect` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, `hasArchived` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
@ -409,6 +409,12 @@
|
||||
"columnName": "unreadMessages",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasArchived",
|
||||
"columnName": "hasArchived",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
@ -713,7 +719,7 @@
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bc802cadfdef41d3eb94ffbb0729eb89')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7edb537b6987d0de6586a6760c970958')"
|
||||
]
|
||||
}
|
||||
}
|
@ -110,4 +110,10 @@ interface NcApiCoroutines {
|
||||
|
||||
@DELETE
|
||||
suspend fun deleteConversationAvatar(@Header("Authorization") authorization: String, @Url url: String): RoomOverall
|
||||
|
||||
@POST
|
||||
suspend fun archiveConversation(@Header("Authorization") authorization: String, @Url url: String): GenericOverall
|
||||
|
||||
@DELETE
|
||||
suspend fun unarchiveConversation(@Header("Authorization") authorization: String, @Url url: String): GenericOverall
|
||||
}
|
||||
|
@ -22,8 +22,10 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
@ -86,6 +88,7 @@ import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.util.Calendar
|
||||
@ -756,6 +759,40 @@ class ConversationInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
if (!CapabilitiesUtil.isArchiveConversationsAvailable(spreedCapabilities)) {
|
||||
binding.archiveConversationBtn.visibility = GONE
|
||||
}
|
||||
|
||||
binding.archiveConversationBtn.setOnClickListener {
|
||||
this.lifecycleScope.launch {
|
||||
if (conversation!!.hasArchived) {
|
||||
viewModel.unarchiveConversation(conversationUser, conversationToken)
|
||||
binding.archiveConversationIcon
|
||||
.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.outline_archive_24, null))
|
||||
binding.archiveConversationText.text = resources.getString(R.string.archive_conversation)
|
||||
binding.archiveConversationTextHint.text = resources.getString(R.string.archive_hint)
|
||||
} else {
|
||||
viewModel.archiveConversation(conversationUser, conversationToken)
|
||||
binding.archiveConversationIcon
|
||||
.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_eye, null))
|
||||
binding.archiveConversationText.text = resources.getString(R.string.unarchive_conversation)
|
||||
binding.archiveConversationTextHint.text = resources.getString(R.string.unarchive_hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation!!.hasArchived) {
|
||||
binding.archiveConversationIcon
|
||||
.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_eye, null))
|
||||
binding.archiveConversationText.text = resources.getString(R.string.unarchive_conversation)
|
||||
binding.archiveConversationTextHint.text = resources.getString(R.string.unarchive_hint)
|
||||
} else {
|
||||
binding.archiveConversationIcon
|
||||
.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.outline_archive_24, null))
|
||||
binding.archiveConversationText.text = resources.getString(R.string.archive_conversation)
|
||||
binding.archiveConversationTextHint.text = resources.getString(R.string.archive_hint)
|
||||
}
|
||||
|
||||
if (!isDestroyed) {
|
||||
binding.dangerZoneOptions.visibility = VISIBLE
|
||||
|
||||
|
@ -18,6 +18,7 @@ import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.participants.TalkBan
|
||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
@ -26,7 +27,8 @@ import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationInfoViewModel @Inject constructor(
|
||||
private val chatNetworkDataSource: ChatNetworkDataSource
|
||||
private val chatNetworkDataSource: ChatNetworkDataSource,
|
||||
private val conversationsRepository: ConversationsRepository
|
||||
) : ViewModel() {
|
||||
|
||||
object LifeCycleObserver : DefaultLifecycleObserver {
|
||||
@ -200,6 +202,18 @@ class ConversationInfoViewModel @Inject constructor(
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun archiveConversation(user: User, token: String) {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
|
||||
val url = ApiUtils.getUrlForArchive(apiVersion, user.baseUrl, token)
|
||||
conversationsRepository.archiveConversation(user.getCredentials(), url)
|
||||
}
|
||||
|
||||
suspend fun unarchiveConversation(user: User, token: String) {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
|
||||
val url = ApiUtils.getUrlForArchive(apiVersion, user.baseUrl, token)
|
||||
conversationsRepository.unarchiveConversation(user.getCredentials(), url)
|
||||
}
|
||||
|
||||
inner class GetRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
|
@ -210,7 +210,9 @@ class ConversationsListActivity :
|
||||
private var filterState =
|
||||
mutableMapOf(
|
||||
FilterConversationFragment.MENTION to false,
|
||||
FilterConversationFragment.UNREAD to false
|
||||
FilterConversationFragment.UNREAD to false,
|
||||
FilterConversationFragment.ARCHIVE to false,
|
||||
FilterConversationFragment.DEFAULT to true
|
||||
)
|
||||
val searchBehaviorSubject = BehaviorSubject.createDefault(false)
|
||||
private lateinit var accountIconBadge: BadgeDrawable
|
||||
@ -380,7 +382,7 @@ class ConversationsListActivity :
|
||||
sortConversations(conversationItemsWithHeader)
|
||||
|
||||
// Filter Conversations
|
||||
if (!filterState.containsValue(true)) filterableConversationItems = conversationItems
|
||||
if (!hasFilterEnabled()) filterableConversationItems = conversationItems
|
||||
filterConversation()
|
||||
adapter!!.updateDataSet(filterableConversationItems, false)
|
||||
Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong())
|
||||
@ -395,6 +397,14 @@ class ConversationsListActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasFilterEnabled(): Boolean {
|
||||
for ((k, v) in filterState) {
|
||||
if (k != FilterConversationFragment.DEFAULT && v) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun filterConversation() {
|
||||
val accountId = UserIdUtils.getIdForUser(userManager.currentUser.blockingGet())
|
||||
filterState[FilterConversationFragment.UNREAD] = (
|
||||
@ -413,12 +423,15 @@ class ConversationsListActivity :
|
||||
).blockingGet()?.value ?: ""
|
||||
) == "true"
|
||||
|
||||
filterState[FilterConversationFragment.ARCHIVE] = (
|
||||
arbitraryStorageManager.getStorageSetting(
|
||||
accountId,
|
||||
FilterConversationFragment.ARCHIVE,
|
||||
""
|
||||
).blockingGet()?.value ?: ""
|
||||
) == "true"
|
||||
|
||||
val newItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
if (filterState[FilterConversationFragment.UNREAD] == false &&
|
||||
filterState[FilterConversationFragment.MENTION] == false
|
||||
) {
|
||||
adapter!!.updateDataSet(conversationItems, true)
|
||||
} else {
|
||||
val items = conversationItems
|
||||
for (i in items) {
|
||||
val conversation = (i as ConversationItem).model
|
||||
@ -428,7 +441,6 @@ class ConversationsListActivity :
|
||||
}
|
||||
adapter!!.updateDataSet(newItems, true)
|
||||
setFilterableItems(newItems)
|
||||
}
|
||||
|
||||
updateFilterConversationButtonColor()
|
||||
}
|
||||
@ -449,10 +461,19 @@ class ConversationsListActivity :
|
||||
)
|
||||
|
||||
FilterConversationFragment.UNREAD -> result = result && (conversation.unreadMessages > 0)
|
||||
|
||||
FilterConversationFragment.DEFAULT -> {
|
||||
result = if (filterState[FilterConversationFragment.ARCHIVE] == true) {
|
||||
result && conversation.hasArchived
|
||||
} else {
|
||||
result && !conversation.hasArchived
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Conversation: ${conversation.name} Result: $result")
|
||||
return result
|
||||
}
|
||||
|
||||
@ -649,7 +670,7 @@ class ConversationsListActivity :
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
initSearchDisposable()
|
||||
adapter!!.setHeadersShown(true)
|
||||
if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems
|
||||
if (!hasFilterEnabled()) filterableConversationItems = searchableConversationItems
|
||||
adapter!!.updateDataSet(filterableConversationItems, false)
|
||||
adapter!!.showAllHeaders()
|
||||
binding.swipeRefreshLayoutView?.isEnabled = false
|
||||
@ -659,7 +680,7 @@ class ConversationsListActivity :
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
adapter!!.setHeadersShown(false)
|
||||
if (!filterState.containsValue(true)) filterableConversationItems = conversationItemsWithHeader
|
||||
if (!hasFilterEnabled()) filterableConversationItems = conversationItemsWithHeader
|
||||
adapter!!.updateDataSet(filterableConversationItems, false)
|
||||
adapter!!.hideAllHeaders()
|
||||
if (searchHelper != null) {
|
||||
@ -1826,7 +1847,7 @@ class ConversationsListActivity :
|
||||
}
|
||||
|
||||
fun updateFilterConversationButtonColor() {
|
||||
if (filterState.containsValue(true)) {
|
||||
if (hasFilterEnabled()) {
|
||||
binding.filterConversationsButton.let { viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) }
|
||||
} else {
|
||||
binding.filterConversationsButton.let {
|
||||
|
@ -70,8 +70,12 @@ import okhttp3.OkHttpClient
|
||||
class RepositoryModule {
|
||||
|
||||
@Provides
|
||||
fun provideConversationsRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): ConversationsRepository {
|
||||
return ConversationsRepositoryImpl(ncApi, userProvider)
|
||||
fun provideConversationsRepository(
|
||||
ncApi: NcApi,
|
||||
ncApiCoroutines: NcApiCoroutines,
|
||||
userProvider: CurrentUserProviderNew
|
||||
): ConversationsRepository {
|
||||
return ConversationsRepositoryImpl(ncApi, ncApiCoroutines, userProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -59,7 +59,8 @@ fun ConversationModel.asEntity() =
|
||||
callStartTime = callStartTime,
|
||||
recordingConsentRequired = recordingConsentRequired,
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived
|
||||
)
|
||||
|
||||
fun ConversationEntity.asModel() =
|
||||
@ -109,7 +110,8 @@ fun ConversationEntity.asModel() =
|
||||
callStartTime = callStartTime,
|
||||
recordingConsentRequired = recordingConsentRequired,
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived
|
||||
)
|
||||
|
||||
fun Conversation.asEntity(accountId: Long) =
|
||||
@ -158,5 +160,6 @@ fun Conversation.asEntity(accountId: Long) =
|
||||
callStartTime = callStartTime,
|
||||
recordingConsentRequired = recordingConsentRequired,
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived
|
||||
)
|
||||
|
@ -92,7 +92,8 @@ data class ConversationEntity(
|
||||
@ColumnInfo(name = "type") var type: ConversationEnums.ConversationType,
|
||||
@ColumnInfo(name = "unreadMention") var unreadMention: Boolean = false,
|
||||
@ColumnInfo(name = "unreadMentionDirect") var unreadMentionDirect: Boolean,
|
||||
@ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0
|
||||
@ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0,
|
||||
@ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false
|
||||
// missing/not needed: attendeeId
|
||||
// missing/not needed: attendeePin
|
||||
// missing/not needed: attendeePermissions
|
||||
|
@ -9,6 +9,7 @@ package com.nextcloud.talk.data.source.local
|
||||
import android.util.Log
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import java.sql.SQLException
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
object Migrations {
|
||||
@ -40,6 +41,13 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_11_12 = object : Migration(11, 12) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 11 to 12")
|
||||
addArchiveConversations(db)
|
||||
}
|
||||
}
|
||||
|
||||
fun migrateToRoom(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE User_new (" +
|
||||
@ -237,4 +245,15 @@ object Migrations {
|
||||
"ON `ChatBlocks` (`internalConversationId`)"
|
||||
)
|
||||
}
|
||||
|
||||
fun addArchiveConversations(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
"ALTER TABLE Conversations " +
|
||||
"ADD `hasArchived` INTEGER;"
|
||||
)
|
||||
} catch (e: SQLException) {
|
||||
Log.i("Migrations", "hasArchived already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ import java.util.Locale
|
||||
ChatMessageEntity::class,
|
||||
ChatBlockEntity::class
|
||||
],
|
||||
version = 11,
|
||||
version = 12,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 9, to = 10)
|
||||
AutoMigration(from = 9, to = 11)
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
@ -113,7 +113,8 @@ abstract class TalkDatabase : RoomDatabase() {
|
||||
Migrations.MIGRATION_6_8,
|
||||
Migrations.MIGRATION_7_8,
|
||||
Migrations.MIGRATION_8_9,
|
||||
Migrations.MIGRATION_10_11
|
||||
Migrations.MIGRATION_10_11,
|
||||
Migrations.MIGRATION_11_12
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
.addCallback(
|
||||
|
@ -59,6 +59,7 @@ class ConversationModel(
|
||||
var recordingConsentRequired: Int = 0,
|
||||
var remoteServer: String? = null,
|
||||
var remoteToken: String? = null,
|
||||
var hasArchived: Boolean = false,
|
||||
|
||||
// attributes that don't come from API. This should be changed?!
|
||||
var password: String? = null
|
||||
@ -120,7 +121,8 @@ class ConversationModel(
|
||||
callStartTime = conversation.callStartTime,
|
||||
recordingConsentRequired = conversation.recordingConsentRequired,
|
||||
remoteServer = conversation.remoteServer,
|
||||
remoteToken = conversation.remoteToken
|
||||
remoteToken = conversation.remoteToken,
|
||||
hasArchived = conversation.hasArchived
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -159,5 +159,8 @@ data class Conversation(
|
||||
var remoteServer: String? = "",
|
||||
|
||||
@JsonField(name = ["remoteToken"])
|
||||
var remoteToken: String? = ""
|
||||
var remoteToken: String? = "",
|
||||
|
||||
@JsonField(name = ["isArchived"])
|
||||
var hasArchived: Boolean = false
|
||||
) : Parcelable
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
package com.nextcloud.talk.repositories.conversations
|
||||
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface ConversationsRepository {
|
||||
@ -29,4 +30,8 @@ interface ConversationsRepository {
|
||||
val successful: Boolean
|
||||
)
|
||||
fun resendInvitations(token: String): Observable<ResendInvitationsResult>
|
||||
|
||||
suspend fun archiveConversation(credentials: String, url: String): GenericOverall
|
||||
|
||||
suspend fun unarchiveConversation(credentials: String, url: String): GenericOverall
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ package com.nextcloud.talk.repositories.conversations
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.api.NcApiCoroutines
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.json.conversations.password.PasswordOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.AllowGuestsResult
|
||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.PasswordResult
|
||||
import com.nextcloud.talk.repositories.conversations.ConversationsRepository.ResendInvitationsResult
|
||||
@ -20,6 +22,7 @@ import io.reactivex.Observable
|
||||
|
||||
class ConversationsRepositoryImpl(
|
||||
private val api: NcApi,
|
||||
private val coroutineApi: NcApiCoroutines,
|
||||
private val userProvider: CurrentUserProviderNew
|
||||
) :
|
||||
ConversationsRepository {
|
||||
@ -89,6 +92,14 @@ class ConversationsRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun archiveConversation(credentials: String, url: String): GenericOverall {
|
||||
return coroutineApi.archiveConversation(credentials, url)
|
||||
}
|
||||
|
||||
override suspend fun unarchiveConversation(credentials: String, url: String): GenericOverall {
|
||||
return coroutineApi.unarchiveConversation(credentials, url)
|
||||
}
|
||||
|
||||
private fun apiVersion(): Int {
|
||||
return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4))
|
||||
}
|
||||
|
@ -69,7 +69,8 @@ class FilterConversationFragment : DialogFragment() {
|
||||
binding.run {
|
||||
listOf(
|
||||
unreadFilterChip,
|
||||
mentionedFilterChip
|
||||
mentionedFilterChip,
|
||||
archivedFilterChip
|
||||
)
|
||||
}.forEach(viewThemeUtils.talk::themeChipFilter)
|
||||
|
||||
@ -89,6 +90,12 @@ class FilterConversationFragment : DialogFragment() {
|
||||
processSubmit()
|
||||
}
|
||||
|
||||
binding.archivedFilterChip.setOnCheckedChangeListener { _, isChecked ->
|
||||
filterState[ARCHIVE] = isChecked
|
||||
binding.archivedFilterChip.isChecked = isChecked
|
||||
processSubmit()
|
||||
}
|
||||
|
||||
binding.buttonClose.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
@ -97,6 +104,7 @@ class FilterConversationFragment : DialogFragment() {
|
||||
private fun setUpChips() {
|
||||
binding.unreadFilterChip.isChecked = filterState[UNREAD]!!
|
||||
binding.mentionedFilterChip.isChecked = filterState[MENTION]!!
|
||||
binding.archivedFilterChip.isChecked = filterState[ARCHIVE]!!
|
||||
}
|
||||
|
||||
private fun processSubmit() {
|
||||
@ -104,9 +112,11 @@ class FilterConversationFragment : DialogFragment() {
|
||||
val accountId = UserIdUtils.getIdForUser(userManager.currentUser.blockingGet())
|
||||
val mentionValue = filterState[MENTION] == true
|
||||
val unreadValue = filterState[UNREAD] == true
|
||||
val archivedValue = filterState[ARCHIVE] == true
|
||||
|
||||
arbitraryStorageManager.storeStorageSetting(accountId, MENTION, mentionValue.toString(), "")
|
||||
arbitraryStorageManager.storeStorageSetting(accountId, UNREAD, unreadValue.toString(), "")
|
||||
arbitraryStorageManager.storeStorageSetting(accountId, ARCHIVE, archivedValue.toString(), "")
|
||||
|
||||
(requireActivity() as ConversationsListActivity).filterConversation()
|
||||
}
|
||||
@ -126,5 +136,7 @@ class FilterConversationFragment : DialogFragment() {
|
||||
val TAG: String = FilterConversationFragment::class.java.simpleName
|
||||
const val MENTION: String = "mention"
|
||||
const val UNREAD: String = "unread"
|
||||
const val ARCHIVE: String = "archive"
|
||||
const val DEFAULT: String = "default"
|
||||
}
|
||||
}
|
||||
|
@ -588,4 +588,8 @@ object ApiUtils {
|
||||
fun getUrlForUnban(baseUrl: String, token: String, banId: Int): String {
|
||||
return "${getUrlForBans(baseUrl, token)}/$banId"
|
||||
}
|
||||
|
||||
fun getUrlForArchive(version: Int, baseUrl: String?, token: String?): String {
|
||||
return "${getUrlForRoom(version, baseUrl, token)}/archive"
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ enum class SpreedFeatures(val value: String) {
|
||||
FEDERATION_V1("federation-v1"),
|
||||
DELETE_MESSAGES_UNLIMITED("delete-messages-unlimited"),
|
||||
BAN_V1("ban-v1"),
|
||||
EDIT_MESSAGES_NOTE_TO_SELF("edit-messages-note-to-self")
|
||||
EDIT_MESSAGES_NOTE_TO_SELF("edit-messages-note-to-self"),
|
||||
ARCHIVE_CONVERSATIONS("archived-conversations")
|
||||
}
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@ -255,6 +256,10 @@ object CapabilitiesUtil {
|
||||
user.capabilities!!.spreedCapability!!.config!!["federation"]!!.containsKey("enabled")
|
||||
}
|
||||
|
||||
fun isArchiveConversationsAvailable(spreedCapabilities: SpreedCapability): Boolean {
|
||||
return hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.ARCHIVE_CONVERSATIONS)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
//region ThemingCapabilities
|
||||
|
18
app/src/main/res/drawable/outline_archive_24.xml
Normal file
18
app/src/main/res/drawable/outline_archive_24.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="24dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,720L640,560L584,504L520,568L520,400L440,400L440,568L376,504L320,560L480,720ZM200,320L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,320L200,320ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,261Q120,247 124.5,234Q129,221 138,210L188,149Q199,135 215.5,127.5Q232,120 250,120L710,120Q728,120 744.5,127.5Q761,135 772,149L822,210Q831,221 835.5,234Q840,247 840,261L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM216,240L744,240L710,200Q710,200 710,200Q710,200 710,200L250,200Q250,200 250,200Q250,200 250,200L216,240ZM480,540L480,540L480,540Q480,540 480,540Q480,540 480,540L480,540Q480,540 480,540Q480,540 480,540Z" />
|
||||
|
||||
</vector>
|
@ -383,6 +383,44 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/archive_conversation_btn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_margin"
|
||||
android:paddingTop="@dimen/standard_half_margin"
|
||||
android:paddingEnd="@dimen/standard_margin"
|
||||
android:paddingBottom="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/archive_conversation_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:contentDescription="@null"
|
||||
tools:src="@drawable/outline_archive_24"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/archive_conversation_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="@string/unarchive_conversation"
|
||||
android:textSize="@dimen/headline_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/archive_conversation_text_hint"
|
||||
android:layout_marginHorizontal="@dimen/standard_margin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/archive_hint" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/danger_zone_options"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -32,7 +32,7 @@
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
app:chipSpacingHorizontal="@dimen/standard_margin">
|
||||
app:chipSpacingHorizontal="@dimen/standard_half_margin">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/unread_filter_chip"
|
||||
@ -48,6 +48,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mentioned" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/archived_filter_chip"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/archived" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
|
@ -817,4 +817,9 @@ How to translate with transifex:
|
||||
<string name="show_ban_reason">Show ban reason</string>
|
||||
<string name="error_unbanning">Error occurred when unbanning participant</string>
|
||||
<string name="connection_lost">Connection lost</string>
|
||||
<string name="archive_conversation">Archive Conversation</string>
|
||||
<string name="unarchive_conversation">Unarchive Conversation</string>
|
||||
<string name="archived">Archived</string>
|
||||
<string name="archive_hint">Once a conversation is archived, it will be hidden by default. Select the filter \'Archived\' to view archived conversations. Direct mentions will still be received.</string>
|
||||
<string name="unarchive_hint">Once a conversation is unarchived, it will be shown by default again.</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user