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

feat: Multipart message support (#WPB-15746) #3320

Merged
merged 10 commits into from
Mar 10, 2025

Conversation

sbakhtiarov
Copy link
Contributor

@sbakhtiarov sbakhtiarov commented Mar 5, 2025

TaskWPB-15746 [Android] Send message with cell attachments

https://wearezeta.atlassian.net/browse/WPB-15746

What's new in this PR?

Support for sending multipart messages for conversations with Wire Cell support. New message type is only supported for sending assets uploaded to wire cell storage. Support for sending regular encrypted assets with new Multipart message will be added later (so there are some TODOs in the code for handling regular assets with Multipart)

Some interactions with the new message type are no supported at the moment:

  • Replies/quotes - not supported
  • Edit - only for editing text (no attachments edit)
  • Delete - work in progress to remove assets when deleting a message.

1. Protocol update
New multipart message combining text with multiple attachments (Regular or Cell). Update code for packing / unpacking and all handlers.

2. Database
99.sqm migration file is a summary of new/updated tables.
[Updated] Conversation/ConversationDetails - added wire_cell column to enable/disable wire cell per conversation. Currently just a local client-side value. Once BE is ready it will be synced with the server.
[Updated]MessageAttachmentDraft: Asset metadata is now stored in the database for attachment drafts for sending in Multipart message.
[New]MessageAttachments: new table for storing message attachments with one-to-many relation (one message/conversation to many asset ids)
[Updated] MessageDetailsView : updated to return list of attachments for each message from the MessageAttachments table. Implementation is similar to handling reactions/mentions for messages.

3. Use case
[New]SendMultipartMessageUseCase new use case for sending multipart message. For conversations with Wire Cell support the new Multipart message is used, for regular conversations assets are sent as individual asset messages.
[Updated] RetryFailedMessageUseCase updated to support re-sending of the failed Multipart message.

Copy link
Contributor

github-actions bot commented Mar 5, 2025

Test Results

318 tests   309 ✅  41s ⏱️
  6 suites    9 💤
  6 files      0 ❌

Results for commit 8688912.

♻️ This comment has been updated with latest results.

@datadog-wireapp
Copy link

datadog-wireapp bot commented Mar 5, 2025

Datadog Report

Branch report: multipart-message
Commit report: 3217c7d
Test service: kalium-jvm

✅ 0 Failed, 347 Passed, 12 Skipped, 15.61s Total Time

Copy link
Contributor

@yamilmedina yamilmedina left a comment

Choose a reason for hiding this comment

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

Looking good 🚀 I have some small solvable comments about redundant modifiers, docs. As well as something maybe more demanding to solve regarding entities accessed in the use case layer.


public val observeFiles: ObserveCellFilesUseCase
get() = ObserveCellFilesUseCaseImpl(conversationsDAO, cellsRepository)

public val enableWireCell: SetWireCellForConversationUseCase
get() = SetWireCellForConversationUseCase(conversationsDAO)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this (if not mistaken) should fail by our arch tests, since the idea of use cases is to use repositories to fulfill their needs, and avoiding accessing directly the entities.

It would be ideal to not bypass this.

EDIT: I think the tests are not running since this is a new module, but also exposed to consumers. We should add the tests to this project as well or maybe we can solve it later to have it resolved by CI at least and/or gradle itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will fix it. I think I just made a shortcut here by using Dao directly, but it is better to do it via repository.

import kotlinx.coroutines.withContext
import okio.Path

public class DownloadCellFileUseCase internal constructor(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: We try to keep our Usecases documented with Kdocs, for the never-ending desire of using Kalium for other clients.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, will add documentation for this one.


val destNodePath = "$ROOT_CELL/$conversationId/$fileName"
val cellName = conversationDao.getCellName(QualifiedIDEntity(conversationId.value, conversationId.domain))
Copy link
Contributor

Choose a reason for hiding this comment

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

As a prev. comment, we shouldn't be able to use. Entities on the use cases since they can leak by mistake to consumers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will replace with repository method call.

Copy link
Member

@vitorhugods vitorhugods left a comment

Choose a reason for hiding this comment

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

LGTM. Just some small details / nits and general questions that reflect my lack of context :P

Comment on lines +22 to +23
val density: Int,
)
Copy link
Member

Choose a reason for hiding this comment

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

As I have zero context... what is density? :P Could you maybe add some Kdocs? Specially around the domain / usecase packages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a preview thumbnail resolution (max dimension). Server generates multiple 300, 1024 but I am using only the best one with 1024

Comment on lines +30 to +31
public class DownloadCellFileUseCase internal constructor(
private val cellsRepository: CellsRepository,
Copy link
Member

Choose a reason for hiding this comment

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

We have some tests on logic that check for things like "data layer can't depend on use cases" and "all use cases must be documented", etc.

I just realised that with modularisation, this can be left without check. I don't think it's a rule that is always necessary. But it's better to be 10% annoying than 90% missing in documentation. This usecase seems pretty self-explanatory.

In general, with this comment, I just want to raise awareness about the tests we had on logic and that are no longer being checked in feature modules.

Something that we - as a team - should resolve at some point.

delay(500)
}

} while (previewList.isEmpty() && retry++ < 20)
Copy link
Member

Choose a reason for hiding this comment

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

Not a deal-breaker. But I think it's worth going the small effort of extracting this 20 to a const val MAX_RETRY_COUNT = 20. Makes it easier to quickly find and change these values if/when needed, also helps a bit with readability in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This Use Case is work in progress. I should have mentioned it in the comment. We are still working on the final solution for handling asset preview thumbnails.

cellsRepository.savePreviewUrl(assetId, it.url)
}

return Unit.right()
Copy link
Member

Choose a reason for hiding this comment

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

Do we ever return Either.Left inthis UseCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will refactor it.

Comment on lines +183 to +184
content = if (isCellEnabled) {
MessageContent.Multipart(
Copy link
Member

Choose a reason for hiding this comment

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

Could also check if there are attachments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This use case should only be called to send a message with attachments, without attachments client must send a simple Text message. Maybe it could even throw error if it is used to send message without attachments.

Comment on lines +226 to +227
attachments.forEach { attachment ->
with(attachment as CellAssetContent) {
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if it is possible to add attachments, then the team enables cells, then we have a crash here of some sort, as this cast could fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The implementation for sending regular encrypted attachments is not done yet. I will implement it later and handle the other content type.

Copy link

sonarqubecloud bot commented Mar 7, 2025

@sbakhtiarov sbakhtiarov merged commit 48755b7 into epic/wire-cells Mar 10, 2025
17 of 20 checks passed
@sbakhtiarov sbakhtiarov deleted the multipart-message branch March 10, 2025 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants