Skip to content

[stable-21.1] Follow up fixes for Improving Chat Message Search #4976

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

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ android {

// mayor.minor.hotfix.increment (for increment: 01-50=Alpha / 51-89=RC / 90-99=stable)
// xx .xxx .xx .xx
versionCode 210010018
versionName "21.1.0 Alpha 18"
versionCode 210010051
versionName "21.1.0 RC1"

flavorDimensions "default"
renderscriptTargetApi 19
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
import com.nextcloud.talk.models.json.participants.TalkBan
import com.nextcloud.talk.models.json.participants.TalkBanOverall
import com.nextcloud.talk.models.json.profile.ProfileOverall
import com.nextcloud.talk.models.json.testNotification.TestNotificationOverall
import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall
import okhttp3.MultipartBody
Expand Down Expand Up @@ -254,4 +255,7 @@ interface NcApiCoroutines {

@GET
suspend fun getNoteToSelfRoom(@Header("Authorization") authorization: String, @Url url: String): RoomOverall

@GET
suspend fun getProfile(@Header("Authorization") authorization: String, @Url url: String): ProfileOverall
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import coil.decode.SvgDecoder
import coil.memory.MemoryCache
import coil.util.DebugLogger
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.filebrowser.webdav.DavUtils
import com.nextcloud.talk.dagger.modules.BusModule
import com.nextcloud.talk.dagger.modules.ContextModule
import com.nextcloud.talk.dagger.modules.DaosModule
Expand All @@ -43,6 +42,7 @@ import com.nextcloud.talk.dagger.modules.RepositoryModule
import com.nextcloud.talk.dagger.modules.RestModule
import com.nextcloud.talk.dagger.modules.UtilsModule
import com.nextcloud.talk.dagger.modules.ViewModelModule
import com.nextcloud.talk.filebrowser.webdav.DavUtils
import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.CapabilitiesWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker
Expand Down
55 changes: 42 additions & 13 deletions app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ContextThemeWrapper
import androidx.cardview.widget.CardView
Expand Down Expand Up @@ -124,6 +125,7 @@ import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
import com.nextcloud.talk.conversationinfo.ConversationInfoActivity
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
import com.nextcloud.talk.conversationlist.ConversationsListActivity
import com.nextcloud.talk.data.network.NetworkMonitor
import com.nextcloud.talk.data.user.model.User
Expand All @@ -142,6 +144,7 @@ import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.polls.ui.PollCreateDialogFragment
import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity
Expand All @@ -153,6 +156,7 @@ import com.nextcloud.talk.ui.PlaybackSpeed
import com.nextcloud.talk.ui.PlaybackSpeedControl
import com.nextcloud.talk.ui.StatusDrawable
import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
import com.nextcloud.talk.ui.dialog.ContextChatCompose
import com.nextcloud.talk.ui.dialog.DateTimeCompose
import com.nextcloud.talk.ui.dialog.FileAttachmentPreviewFragment
import com.nextcloud.talk.ui.dialog.MessageActionsDialog
Expand Down Expand Up @@ -208,6 +212,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
Expand All @@ -224,9 +229,6 @@ import java.util.Locale
import java.util.concurrent.ExecutionException
import javax.inject.Inject
import kotlin.math.roundToInt
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
import com.nextcloud.talk.models.json.participants.Participant

@AutoInjector(NextcloudTalkApplication::class)
class ChatActivity :
Expand Down Expand Up @@ -296,9 +298,38 @@ class ChatActivity :
private val startMessageSearchForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
executeIfResultOk(it) { intent ->
onMessageSearchResult(intent)
runBlocking {
val id = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID)
id?.let {
val isSaved = chatViewModel.isMessageSaved(id.toLong())
if (isSaved) {
onMessageSearchResult(intent)
} else {
startContextChatWindowForMessage(id)
}
}
}
}
}

private fun startContextChatWindowForMessage(id: String?) {
binding.genericComposeView.apply {
val shouldDismiss = mutableStateOf(false)
setContent {
val bundle = bundleOf()
bundle.putString(BundleKeys.KEY_CREDENTIALS, credentials!!)
bundle.putString(BundleKeys.KEY_BASE_URL, conversationUser!!.baseUrl)
bundle.putString(KEY_ROOM_TOKEN, roomToken)
bundle.putString(BundleKeys.KEY_MESSAGE_ID, id)
bundle.putString(
KEY_CONVERSATION_NAME,
currentConversation!!.displayName
)
ContextChatCompose(bundle).GetDialogView(shouldDismiss, context)
}
}
Log.d(TAG, "Should open something else")
}

private val startPickCameraIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
Expand Down Expand Up @@ -2149,7 +2180,7 @@ class ChatActivity :
if (position != null && position >= 0) {
binding.messagesListView.scrollToPosition(position)
} else {
Log.d(TAG, "message $messageId that should be scrolled to was not found (scrollToMessageWithId)")
startContextChatWindowForMessage(messageId)
}
}

Expand All @@ -2162,12 +2193,10 @@ class ChatActivity :
binding.messagesListView.height / 2
)
} else {
Log.d(
TAG,
"message $messageId that should be scrolled to was not found " +
"(scrollToAndCenterMessageWithId)"
)
startContextChatWindowForMessage(messageId)
}
} ?: run {
startContextChatWindowForMessage(messageId)
}
}

Expand Down Expand Up @@ -3159,9 +3188,11 @@ class ChatActivity :
context.resources.getString(R.string.nc_tomorrow_meeting),
startDateTime.format(DateTimeFormatter.ofPattern("HH:mm"))
)

else -> startDateTime.format(DateTimeFormatter.ofPattern("MMM d, yyyy, HH:mm"))
}
}

currentTime.isAfter(endDateTime) -> context.resources.getString(R.string.nc_meeting_ended)
else -> context.resources.getString(R.string.nc_ongoing_meeting)
}
Expand Down Expand Up @@ -3916,9 +3947,7 @@ class ChatActivity :
}
if (!foundMessage) {
Log.d(TAG, "quoted message with id " + parentMessage.id + " was not found in adapter")
// TODO: show better info
// TODO: improve handling how this can be avoided. E.g. loading chat until message is reached...
Snackbar.make(binding.root, "Message was not found", Snackbar.LENGTH_LONG).show()
startContextChatWindowForMessage(parentMessage.id)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ interface ChatMessageRepository : LifecycleAwareManager {
*/
suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage>

suspend fun checkIfMessageIsSaved(messageId: Long): Boolean

@Suppress("LongParameterList")
suspend fun sendChatMessage(
credentials: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,15 @@ class OfflineFirstChatRepository @Inject constructor(
.map(ChatMessageEntity::asModel)
}

override suspend fun checkIfMessageIsSaved(messageId: Long): Boolean {
try {
chatDao.getChatMessageForConversation(internalConversationId, messageId)
return true
} catch (_: Exception) {
return false
}
}

@Suppress("UNCHECKED_CAST", "MagicNumber", "Detekt.TooGenericExceptionCaught")
private fun getMessagesFromServer(bundle: Bundle): Pair<Int, List<ChatMessageJson>>? {
val fieldMap = bundle.getSerializable(BundleKeys.KEY_FIELD_MAP) as HashMap<String, Int>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ class ChatViewModel @Inject constructor(
conversationRepository.getRoom(token)
}

suspend fun isMessageSaved(messageId: Long): Boolean {
return chatRepository.checkIfMessageIsSaved(messageId)
}

fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) {
Log.d(TAG, "Remote server ${conversationModel.remoteServer}")
if (conversationModel.remoteServer.isNullOrEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2025 Your Name <[email protected]>
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <[email protected]>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2025 Your Name <your@email.com>
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.time.Instant
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Calendar
import java.util.Collections
import java.util.Locale
Expand Down Expand Up @@ -150,7 +155,7 @@ class ConversationInfoActivity :
get() {
if (!TextUtils.isEmpty(conversationToken)) {
val data = Data.Builder()
data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
data.putString(KEY_ROOM_TOKEN, conversationToken)
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!)
return data.build()
}
Expand Down Expand Up @@ -192,7 +197,7 @@ class ConversationInfoActivity :

conversationUser = currentUserProvider.currentUser.blockingGet()

conversationToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
conversationToken = intent.getStringExtra(KEY_ROOM_TOKEN)!!
hasAvatarSpacing = intent.getBooleanExtra(BundleKeys.KEY_ROOM_ONE_TO_ONE, false)
credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)!!
}
Expand Down Expand Up @@ -335,6 +340,7 @@ class ConversationInfoActivity :
}
}

@SuppressLint("SetTextI18n")
private fun initViewStateObserver() {
viewModel.viewState.observe(this) { state ->
when (state) {
Expand All @@ -354,6 +360,10 @@ class ConversationInfoActivity :
canGeneratePrettyURL
)
}

if (conversation!!.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
viewModel.getProfileData(conversationUser, conversation!!.name)
}
}

is ConversationInfoViewModel.GetRoomErrorState -> {
Expand All @@ -363,6 +373,37 @@ class ConversationInfoActivity :
else -> {}
}
}

viewModel.getProfileViewState.observe(this) { state ->
when (state) {
is ConversationInfoViewModel.GetProfileSuccessState -> {
val profile = state.profile
val pronouns = profile.pronouns ?: ""
binding.pronouns.text = pronouns

val concat1 = if (profile.role != null && profile.company != null) " @ " else ""
val role = profile.role ?: ""
val company = profile.company ?: ""
val professionCompanyText = "$role$concat1$company"
binding.professionCompany.text = professionCompanyText

val profileZoneOffset = ZoneOffset.ofTotalSeconds(0)
val secondsToAdd = profile.timezoneOffset?.toLong() ?: 0
val localTime = ZonedDateTime.ofInstant(Instant.now().plusSeconds(secondsToAdd), profileZoneOffset)
val localTimeString = localTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))
val concat2 = if (profile.address != null) " · " else ""
val address = profile.address ?: ""
val localTimeLocation = "$localTimeString$concat2$address"
binding.locationTime.text = resources.getString(R.string.local_time, localTimeLocation)

binding.pronouns.visibility = VISIBLE
binding.professionCompany.visibility = if (professionCompanyText.isNotEmpty()) VISIBLE else GONE
binding.locationTime.visibility = VISIBLE
}

else -> {}
}
}
}

private fun setupActionBar() {
Expand Down Expand Up @@ -404,7 +445,7 @@ class ConversationInfoActivity :
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.edit) {
val bundle = Bundle()
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
bundle.putString(KEY_ROOM_TOKEN, conversationToken)

val intent = Intent(this, ConversationInfoEditActivity::class.java)
intent.putExtras(bundle)
Expand Down Expand Up @@ -445,7 +486,7 @@ class ConversationInfoActivity :
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName)
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
intent.putExtra(KEY_ROOM_TOKEN, conversationToken)
intent.putExtra(
SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR,
ConversationUtils.isParticipantOwnerOrModerator(conversation!!)
Expand Down Expand Up @@ -538,7 +579,7 @@ class ConversationInfoActivity :
) {
binding.webinarInfoView.startTimeButtonSummary.text = (
dateUtils.getLocalDateTimeStringFromTimestamp(
conversation!!.lobbyTimer!! * DateConstants.SECOND_DIVIDER
conversation!!.lobbyTimer * DateConstants.SECOND_DIVIDER
)
)
} else {
Expand Down Expand Up @@ -1039,7 +1080,7 @@ class ConversationInfoActivity :

binding.displayNameText.text = conversation!!.displayName

if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) {
if (conversation!!.description != null && conversation!!.description.isNotEmpty()) {
binding.descriptionText.text = conversation!!.description
binding.conversationDescription.visibility = VISIBLE
}
Expand Down Expand Up @@ -1185,7 +1226,7 @@ class ConversationInfoActivity :
val stringValue: String =
when (
DomainEnumNotificationLevelConverter()
.convertToInt(conversation!!.notificationLevel!!)
.convertToInt(conversation!!.notificationLevel)
) {
NOTIFICATION_LEVEL_ALWAYS -> resources.getString(R.string.nc_notify_me_always)
NOTIFICATION_LEVEL_MENTION -> resources.getString(R.string.nc_notify_me_mention)
Expand Down Expand Up @@ -1234,7 +1275,7 @@ class ConversationInfoActivity :
conversation!!.name
)
) {
conversation!!.name?.let {
conversation!!.name.let {
binding.avatarImage.loadUserAvatar(
conversationUser,
it,
Expand Down
Loading