mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
Cleanup ViewModel
This commit is contained in:
parent
821327569e
commit
a1e8ad2c37
@ -10,13 +10,16 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
@ -61,9 +64,6 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt
|
||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.database.Storage
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.Slide
|
||||
import org.thoughtcrime.securesms.ui.AppTheme
|
||||
import org.thoughtcrime.securesms.ui.Avatar
|
||||
import org.thoughtcrime.securesms.ui.CarouselNextButton
|
||||
@ -103,21 +103,15 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
|
||||
timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L)
|
||||
title = resources.getString(R.string.conversation_context__menu_message_details)
|
||||
|
||||
val messageRecord =
|
||||
DatabaseComponent.get(this).mmsSmsDatabase().getMessageForTimestamp(timestamp) ?: run {
|
||||
intent.getLongExtra(MESSAGE_TIMESTAMP, -1L).let(viewModel::setMessageTimestamp)
|
||||
|
||||
if (viewModel.details.value == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val error = DatabaseComponent.get(this).lokiMessageDatabase()
|
||||
.getErrorMessage(messageRecord.getId())
|
||||
|
||||
viewModel.setMessageRecord(messageRecord, error)
|
||||
|
||||
title = resources.getString(R.string.conversation_context__menu_message_details)
|
||||
|
||||
ComposeView(this)
|
||||
.apply { setContent { MessageDetailsScreen() } }
|
||||
.let(::setContentView)
|
||||
@ -125,28 +119,27 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
||||
|
||||
@Composable
|
||||
private fun MessageDetailsScreen() {
|
||||
val details by viewModel.details.observeAsState(MessageDetails())
|
||||
val threadDb = DatabaseComponent.get(this@MessageDetailActivity).threadDatabase()
|
||||
val state by viewModel.details.observeAsState(MessageDetailsState())
|
||||
AppTheme {
|
||||
MessageDetails(
|
||||
threadDb = threadDb,
|
||||
messageDetails = details,
|
||||
state = state,
|
||||
onReply = { setResultAndFinish(ON_REPLY) },
|
||||
onResend = details.error?.let { { setResultAndFinish(ON_RESEND) } },
|
||||
onResend = state.error?.let { { setResultAndFinish(ON_RESEND) } },
|
||||
onDelete = { setResultAndFinish(ON_DELETE) },
|
||||
onClickImage = { slide ->
|
||||
onClickImage = { i ->
|
||||
val slide = state.attachments[i].slide
|
||||
// only open to downloaded images
|
||||
if (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED) {
|
||||
// Restart download here (on IO thread)
|
||||
(slide.asAttachment() as? DatabaseAttachment)?.let { attachment ->
|
||||
onAttachmentNeedsDownload(attachment.attachmentId.rowId, details.mmsRecord!!.getId())
|
||||
onAttachmentNeedsDownload(attachment.attachmentId.rowId, state.mmsRecord!!.getId())
|
||||
}
|
||||
}
|
||||
if (!slide.isInProgress) MediaPreviewActivity.getPreviewIntent(
|
||||
this,
|
||||
slide,
|
||||
details.mmsRecord,
|
||||
threadDb.getRecipientForThreadId(details.mmsRecord!!.threadId),
|
||||
state.mmsRecord,
|
||||
state.thread,
|
||||
).let(::startActivity)
|
||||
},
|
||||
onAttachmentNeedsDownload = ::onAttachmentNeedsDownload,
|
||||
@ -174,7 +167,7 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
||||
fun PreviewMessageDetails() {
|
||||
AppTheme {
|
||||
MessageDetails(
|
||||
messageDetails = MessageDetails(
|
||||
state = MessageDetailsState(
|
||||
attachments = listOf(),
|
||||
sent = TitledText("Sent:", "6:12 AM Tue, 09/08/2022"),
|
||||
received = TitledText("Received:", "6:12 AM Tue, 09/08/2022"),
|
||||
@ -188,12 +181,11 @@ fun PreviewMessageDetails() {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Composable
|
||||
fun MessageDetails(
|
||||
threadDb: ThreadDatabase? = null,
|
||||
messageDetails: MessageDetails,
|
||||
state: MessageDetailsState,
|
||||
onReply: () -> Unit = {},
|
||||
onResend: (() -> Unit)? = null,
|
||||
onDelete: () -> Unit = {},
|
||||
onClickImage: (Slide) -> Unit = {},
|
||||
onClickImage: (Int) -> Unit = {},
|
||||
onAttachmentNeedsDownload: (Long, Long) -> Unit = { _, _ -> }
|
||||
) {
|
||||
Column(
|
||||
@ -202,14 +194,14 @@ fun MessageDetails(
|
||||
.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
messageDetails.record?.let { message ->
|
||||
state.record?.let { message ->
|
||||
AndroidView(
|
||||
modifier = Modifier.padding(horizontal = 32.dp),
|
||||
factory = {
|
||||
ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
|
||||
bind(
|
||||
message,
|
||||
thread = threadDb?.getRecipientForThreadId(message.threadId)!!,
|
||||
thread = state.thread!!,
|
||||
onAttachmentNeedsDownload = onAttachmentNeedsDownload,
|
||||
suppressThumbnails = true
|
||||
)
|
||||
@ -222,8 +214,9 @@ fun MessageDetails(
|
||||
}
|
||||
)
|
||||
}
|
||||
Carousel(messageDetails.attachments) { onClickImage(it) }
|
||||
MetadataCell(messageDetails)
|
||||
Carousel(state.imageAttachments) { onClickImage(it) }
|
||||
state.nonImageAttachment?.fileDetails?.let { FileDetails(it) }
|
||||
MetadataCell(state)
|
||||
Buttons(
|
||||
onReply,
|
||||
onResend,
|
||||
@ -234,9 +227,9 @@ fun MessageDetails(
|
||||
|
||||
@Composable
|
||||
fun MetadataCell(
|
||||
messageDetails: MessageDetails,
|
||||
state: MessageDetailsState,
|
||||
) {
|
||||
messageDetails.apply {
|
||||
state.apply {
|
||||
if (sent != null || received != null || senderInfo != null) CellWithPaddingAndMargin {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
sent?.let { TitledText(it) }
|
||||
@ -289,25 +282,26 @@ fun Buttons(
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
|
||||
val imageAttachments = attachments.filter { it.hasImage() }.takeIf { it.isNotEmpty() } ?: return
|
||||
val pagerState = rememberPagerState { imageAttachments.size }
|
||||
fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
|
||||
if (attachments.isEmpty()) return
|
||||
|
||||
val pagerState = rememberPagerState { attachments.size }
|
||||
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Row {
|
||||
CarouselPrevButton(pagerState)
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
CellCarousel(pagerState, imageAttachments, onClick)
|
||||
CellCarousel(pagerState, attachments, onClick)
|
||||
HorizontalPagerIndicator(pagerState)
|
||||
ExpandButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(8.dp)
|
||||
) { onClick(imageAttachments[pagerState.currentPage].slide) }
|
||||
) { onClick(pagerState.currentPage) }
|
||||
}
|
||||
CarouselNextButton(pagerState)
|
||||
}
|
||||
FileDetails(attachments, pagerState)
|
||||
attachments.getOrNull(pagerState.currentPage)?.fileDetails?.let { FileDetails(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,19 +312,18 @@ fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
|
||||
@Composable
|
||||
private fun CellCarousel(
|
||||
pagerState: PagerState,
|
||||
imageAttachments: List<Attachment>,
|
||||
onClick: (Slide) -> Unit
|
||||
attachments: List<Attachment>,
|
||||
onClick: (Int) -> Unit
|
||||
) {
|
||||
CellNoMargin {
|
||||
HorizontalPager(state = pagerState) { i ->
|
||||
val slide = imageAttachments[i].slide
|
||||
GlideImage(
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.clickable { onClick(slide) },
|
||||
model = slide.uri,
|
||||
contentDescription = slide.fileName.orNull() ?: stringResource(id = R.string.image)
|
||||
.clickable { onClick(i) },
|
||||
model = attachments[i].uri,
|
||||
contentDescription = attachments[i].fileName ?: stringResource(id = R.string.image)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -346,7 +339,7 @@ fun ExpandButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_expand),
|
||||
contentDescription = "",
|
||||
contentDescription = stringResource(id = R.string.expand),
|
||||
modifier = Modifier.clickable { onClick() },
|
||||
)
|
||||
}
|
||||
@ -379,12 +372,6 @@ fun PreviewMessageDetails(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FileDetails(attachments: List<Attachment>, pagerState: PagerState) {
|
||||
FileDetails(attachments[pagerState.currentPage].fileDetails)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun FileDetails(fileDetails: List<TitledText>) {
|
||||
@ -393,17 +380,18 @@ fun FileDetails(fileDetails: List<TitledText>) {
|
||||
CellWithPaddingAndMargin {
|
||||
FlowRow(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
fileDetails.forEach {
|
||||
BoxWithConstraints {
|
||||
TitledText(
|
||||
it,
|
||||
modifier = Modifier
|
||||
.widthIn(min = 100.dp) // set minimum width
|
||||
.width(IntrinsicSize.Max) // make the text as wide as necessary
|
||||
.weight(1f) // space evenly
|
||||
.widthIn(min = maxWidth.div(2))
|
||||
.width(IntrinsicSize.Max)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TitledErrorText(titledText: TitledText, modifier: Modifier = Modifier) {
|
||||
@ -431,7 +419,7 @@ fun TitledText(
|
||||
) {
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Title(titledText.title)
|
||||
Text(titledText.value, style = valueStyle)
|
||||
Text(titledText.value, style = valueStyle, modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,19 @@ package org.thoughtcrime.securesms.conversation.v2
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.utilities.Util
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
||||
import org.thoughtcrime.securesms.database.LokiMessageDatabase
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||
@ -18,7 +26,7 @@ import javax.inject.Inject
|
||||
|
||||
data class TitledText(val title: String, val value: String)
|
||||
|
||||
data class MessageDetails(
|
||||
data class MessageDetailsState(
|
||||
val attachments: List<Attachment> = emptyList(),
|
||||
val record: MessageRecord? = null,
|
||||
val mmsRecord: MmsMessageRecord? = null,
|
||||
@ -26,44 +34,57 @@ data class MessageDetails(
|
||||
val received: TitledText? = null,
|
||||
val error: TitledText? = null,
|
||||
val senderInfo: TitledText? = null,
|
||||
val sender: Recipient? = null
|
||||
)
|
||||
val sender: Recipient? = null,
|
||||
val thread: Recipient? = null,
|
||||
) {
|
||||
val imageAttachments = attachments.filter { it.hasImage() }
|
||||
val nonImageAttachment: Attachment? = attachments.firstOrNull { !it.hasImage() }
|
||||
}
|
||||
|
||||
data class Attachment(
|
||||
val slide: Slide,
|
||||
val fileDetails: List<TitledText>
|
||||
) {
|
||||
val fileName: String? get() = slide.fileName.orNull()
|
||||
val uri get() = slide.uri
|
||||
|
||||
fun hasImage() = slide is ImageSlide
|
||||
}
|
||||
|
||||
@HiltViewModel
|
||||
class MessageDetailsViewModel @Inject constructor(
|
||||
private val attachmentDb: AttachmentDatabase
|
||||
private val attachmentDb: AttachmentDatabase,
|
||||
private val lokiMessageDatabase: LokiMessageDatabase,
|
||||
private val mmsSmsDatabase: MmsSmsDatabase,
|
||||
private val threadDb: ThreadDatabase,
|
||||
) : ViewModel() {
|
||||
|
||||
fun setMessageRecord(record: MessageRecord?, error: String?) {
|
||||
fun setMessageTimestamp(timestamp: Long) {
|
||||
mmsSmsDatabase.getMessageForTimestamp(timestamp).let(::setMessageRecord)
|
||||
}
|
||||
|
||||
fun setMessageRecord(record: MessageRecord?) {
|
||||
val mmsRecord = record as? MmsMessageRecord
|
||||
|
||||
val slides: List<Slide> = mmsRecord?.slideDeck?.thumbnailSlides?.toList() ?: emptyList()
|
||||
|
||||
_details.value = record?.run {
|
||||
MessageDetails(
|
||||
MessageDetailsState(
|
||||
attachments = slides.map { Attachment(it, it.details) },
|
||||
record = record,
|
||||
mmsRecord = mmsRecord,
|
||||
attachments = slides.map { Attachment(it, it.details) },
|
||||
sent = dateSent.let(::Date).toString().let { TitledText("Sent:", it) },
|
||||
received = dateReceived.let(::Date).toString().let { TitledText("Received:", it) },
|
||||
error = error?.let { TitledText("Error:", it) },
|
||||
senderInfo = individualRecipient.run {
|
||||
name?.let { TitledText(it, address.serialize()) }
|
||||
},
|
||||
sender = individualRecipient
|
||||
error = lokiMessageDatabase.getErrorMessage(id)?.let { TitledText("Error:", it) },
|
||||
senderInfo = individualRecipient.run { name?.let { TitledText(it, address.serialize()) } },
|
||||
sender = individualRecipient,
|
||||
thread = threadDb.getRecipientForThreadId(threadId)!!,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var _details = MutableLiveData(MessageDetails())
|
||||
val details: LiveData<MessageDetails> = _details
|
||||
private var _details = MutableLiveData(MessageDetailsState())
|
||||
val details: LiveData<MessageDetailsState> = _details
|
||||
|
||||
private val Slide.details: List<TitledText>
|
||||
get() = listOfNotNull(
|
||||
@ -88,4 +109,5 @@ class MessageDetailsViewModel @Inject constructor(
|
||||
TimeUnit.MILLISECONDS.toSeconds(it) % 60
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,6 @@ const val classicDark4 = 0xff767676
|
||||
const val classicDark5 = 0xffA1A2A1
|
||||
const val classicDark6 = 0xffFFFFFF
|
||||
|
||||
|
||||
|
||||
const val classicLight0 = 0xff000000
|
||||
const val classicLight1 = 0xff6D6D6D
|
||||
const val classicLight2 = 0xffA1A2A1
|
||||
|
@ -56,12 +56,6 @@ import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.components.ProfilePictureView
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private val Colors.cellColors: Colors
|
||||
@Composable
|
||||
get() = MaterialTheme.colors.copy(
|
||||
surface = LocalExtraColors.current.settingsBackground,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ItemButton(
|
||||
text: String,
|
||||
@ -105,8 +99,8 @@ fun CellWithPaddingAndMargin(
|
||||
margin: Dp = 32.dp,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
MaterialTheme(colors = MaterialTheme.colors.cellColors) {
|
||||
Card(
|
||||
backgroundColor = MaterialTheme.colors.cellColor,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
elevation = 0.dp,
|
||||
modifier = Modifier
|
||||
@ -117,7 +111,10 @@ fun CellWithPaddingAndMargin(
|
||||
Box(Modifier.padding(padding)) { content() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val Colors.cellColor: Color
|
||||
@Composable
|
||||
get() = LocalExtraColors.current.settingsBackground
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
|
@ -12,6 +12,7 @@
|
||||
<string name="image">Image</string>
|
||||
<string name="note_to_self">Note to Self</string>
|
||||
<string name="version_s">Version %s</string>
|
||||
<string name="expand">Expand</string>
|
||||
<!--Accessibility ID's-->
|
||||
<!-- Landing Page -->
|
||||
<string name="AccessibilityId_create_session_id">Create session ID</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user