Skip to content
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

Archived Conversations πŸ—ƒοΈ #4333

Merged
merged 7 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 11,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the databse version must be bumped and a database migration must be added to alter the affected table

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was merged too fast. there should have been a 12.json file instead to modify 11.json
should be fixed with #4417

"identityHash": "bc802cadfdef41d3eb94ffbb0729eb89",
"identityHash": "7edb537b6987d0de6586a6760c970958",
"entities": [
{
"tableName": "User",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -409,6 +409,12 @@
"columnName": "unreadMessages",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hasArchived",
"columnName": "hasArchived",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
Expand Down Expand Up @@ -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')"
]
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/nextcloud/talk/api/NcApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -727,4 +727,12 @@ Observable<TalkBan> banActor(@Header("Authorization") String authorization,
@DELETE
Observable<GenericOverall> unbanActor(@Header("Authorization") String authorization,
@Url String url);

@POST
Observable<GenericOverall> archiveConversation(@Header("Authorization") String authorization,
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
@Url String url);

@DELETE
Observable<GenericOverall> unarchiveConversation(@Header("Authorization") String authorization,
@Url String url);
}
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,30 @@ class ConversationInfoActivity :
}
}

if (!spreedCapabilities.features!!.contains(ARCHIVED_CONVERSATIONS)) {
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
binding.archiveConversationBtn.visibility = GONE
}

binding.archiveConversationBtn.setOnClickListener {
if (conversation!!.hasArchived) {
viewModel.unarchiveConversation(conversationUser, conversationToken)
binding.archiveConversationIcon.setImageDrawable(resources.getDrawable(R.drawable.outline_archive_24))
binding.archiveConversationText.text = resources.getString(R.string.archive_conversation)
} else {
viewModel.archiveConversation(conversationUser, conversationToken)
binding.archiveConversationIcon.setImageDrawable(resources.getDrawable(R.drawable.ic_eye))
binding.archiveConversationText.text = resources.getString(R.string.unarchive_conversation)
}
}

if (conversation!!.hasArchived) {
binding.archiveConversationIcon.setImageDrawable(resources.getDrawable(R.drawable.ic_eye))
binding.archiveConversationText.text = resources.getString(R.string.unarchive_conversation)
} else {
binding.archiveConversationIcon.setImageDrawable(resources.getDrawable(R.drawable.outline_archive_24))
binding.archiveConversationText.text = resources.getString(R.string.archive_conversation)
}

if (!isDestroyed) {
binding.dangerZoneOptions.visibility = VISIBLE

Expand Down Expand Up @@ -1445,6 +1469,7 @@ class ConversationInfoActivity :
private const val DEMOTE_OR_PROMOTE = 1
private const val REMOVE_FROM_CONVERSATION = 2
private const val BAN_FROM_CONVERSATION = 3
private const val ARCHIVED_CONVERSATIONS = "archived-conversations"
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -200,6 +202,56 @@ class ConversationInfoViewModel @Inject constructor(
})
}

fun archiveConversation(user: User, token: String) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to implement for now (can be done in followup PR if you are interested), but just as a sidenote:

It would be nice to have the archive/unarchive button also available in the conversations context menu. This would lead to the question how duplicated code can be avoided to call the repository.
In the long term we should use "UseCases" for it to make this more reusable. See explanations at https://medium.com/@ami0275/mvvm-clean-architecture-pattern-in-android-with-use-cases-eff7edc2ef76

But as said, should be done later on and not in this PR. So for now i suggest to don't implement it in the context menu.

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)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(p0: Disposable) {
// unused
}

override fun onError(e: Throwable) {
Log.d("Julius", "Error in archive $e")
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
}

override fun onComplete() {
// unused atm
}

override fun onNext(n: GenericOverall) {
Log.d("Julius", "Archived successful")
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
}
})
}

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)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(p0: Disposable) {
// unused
}

override fun onError(e: Throwable) {
Log.d("Julius", "Error in unarchive $e")
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
}

override fun onComplete() {
// unused atm
}

override fun onNext(n: GenericOverall) {
Log.d("Julius", "unArchived successful")
}
})
}

inner class GetRoomObserver : Observer<ConversationModel> {
override fun onSubscribe(d: Disposable) {
// unused atm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -380,7 +382,7 @@ class ConversationsListActivity :
sortConversations(conversationItemsWithHeader)

// Filter Conversations
if (!filterState.containsValue(true)) filterableConversationItems = conversationItems
if (!containsTrue()) filterableConversationItems = conversationItems
filterConversation()
adapter!!.updateDataSet(filterableConversationItems, false)
Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong())
Expand All @@ -395,6 +397,14 @@ class ConversationsListActivity :
}
}

private fun containsTrue(): Boolean {
rapterjet2004 marked this conversation as resolved.
Show resolved Hide resolved
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] = (
Expand All @@ -413,22 +423,24 @@ 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
if (filter(conversation)) {
newItems.add(i)
}
val items = conversationItems
for (i in items) {
val conversation = (i as ConversationItem).model
if (filter(conversation)) {
newItems.add(i)
}
adapter!!.updateDataSet(newItems, true)
setFilterableItems(newItems)
}
adapter!!.updateDataSet(newItems, true)
setFilterableItems(newItems)

updateFilterConversationButtonColor()
}
Expand All @@ -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("Julius", "Conversation: ${conversation.name} Result: $result")
return result
}

Expand Down Expand Up @@ -649,7 +670,7 @@ class ConversationsListActivity :
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
initSearchDisposable()
adapter!!.setHeadersShown(true)
if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems
if (!containsTrue()) filterableConversationItems = searchableConversationItems
adapter!!.updateDataSet(filterableConversationItems, false)
adapter!!.showAllHeaders()
binding.swipeRefreshLayoutView?.isEnabled = false
Expand All @@ -659,7 +680,7 @@ class ConversationsListActivity :

override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
adapter!!.setHeadersShown(false)
if (!filterState.containsValue(true)) filterableConversationItems = conversationItemsWithHeader
if (!containsTrue()) filterableConversationItems = conversationItemsWithHeader
adapter!!.updateDataSet(filterableConversationItems, false)
adapter!!.hideAllHeaders()
if (searchHelper != null) {
Expand Down Expand Up @@ -1826,7 +1847,7 @@ class ConversationsListActivity :
}

fun updateFilterConversationButtonColor() {
if (filterState.containsValue(true)) {
if (containsTrue()) {
binding.filterConversationsButton.let { viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) }
} else {
binding.filterConversationsButton.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ fun ConversationModel.asEntity() =
callStartTime = callStartTime,
recordingConsentRequired = recordingConsentRequired,
remoteServer = remoteServer,
remoteToken = remoteToken
remoteToken = remoteToken,
hasArchived = hasArchived
)

fun ConversationEntity.asModel() =
Expand Down Expand Up @@ -109,7 +110,8 @@ fun ConversationEntity.asModel() =
callStartTime = callStartTime,
recordingConsentRequired = recordingConsentRequired,
remoteServer = remoteServer,
remoteToken = remoteToken
remoteToken = remoteToken,
hasArchived = hasArchived
)

fun Conversation.asEntity(accountId: Long) =
Expand Down Expand Up @@ -158,5 +160,6 @@ fun Conversation.asEntity(accountId: Long) =
callStartTime = callStartTime,
recordingConsentRequired = recordingConsentRequired,
remoteServer = remoteServer,
remoteToken = remoteToken
remoteToken = remoteToken,
hasArchived = hasArchived
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -120,7 +121,8 @@ class ConversationModel(
callStartTime = conversation.callStartTime,
recordingConsentRequired = conversation.recordingConsentRequired,
remoteServer = conversation.remoteServer,
remoteToken = conversation.remoteToken
remoteToken = conversation.remoteToken,
hasArchived = conversation.hasArchived
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
package com.nextcloud.talk.repositories.conversations

import com.nextcloud.talk.models.json.generic.GenericOverall
import io.reactivex.Observable

interface ConversationsRepository {
Expand All @@ -29,4 +30,8 @@ interface ConversationsRepository {
val successful: Boolean
)
fun resendInvitations(token: String): Observable<ResendInvitationsResult>

fun archiveConversation(credentials: String, url: String): Observable<GenericOverall>

fun unarchiveConversation(credentials: String, url: String): Observable<GenericOverall>
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.api.NcApi
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
Expand Down Expand Up @@ -89,6 +90,14 @@ class ConversationsRepositoryImpl(
}
}

override fun archiveConversation(credentials: String, url: String): Observable<GenericOverall> {
return api.archiveConversation(credentials, url)
}

override fun unarchiveConversation(credentials: String, url: String): Observable<GenericOverall> {
return api.unarchiveConversation(credentials, url)
}

private fun apiVersion(): Int {
return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4))
}
Expand Down
Loading
Loading