mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
@@ -13,7 +13,8 @@ actual fun rememberSystemBarsController(): ((Boolean) -> Unit)? {
|
|||||||
return remember(view) {
|
return remember(view) {
|
||||||
val activity = view.context as? Activity ?: return@remember null
|
val activity = view.context as? Activity ?: return@remember null
|
||||||
val window = activity.window ?: return@remember null
|
val window = activity.window ?: return@remember null
|
||||||
val controller = WindowCompat.getInsetsController(window, view) ?: return@remember null
|
val controller = WindowCompat.getInsetsController(window, view);
|
||||||
|
|
||||||
{ visible: Boolean ->
|
{ visible: Boolean ->
|
||||||
if (visible) {
|
if (visible) {
|
||||||
controller.show(WindowInsetsCompat.Type.statusBars())
|
controller.show(WindowInsetsCompat.Type.statusBars())
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ import androidx.compose.ui.unit.sp
|
|||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.compose.rememberAsyncImagePainter
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
import com.pr0gramm3r101.utils.conditional
|
import com.pr0gramm3r101.utils.conditional
|
||||||
|
import com.pr0gramm3r101.utils.crypto.Base64
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import com.pr0gramm3r101.utils.crypto.Base64
|
|
||||||
import ru.fromchat.api.DmEnvelope
|
import ru.fromchat.api.DmEnvelope
|
||||||
import ru.fromchat.api.DmFile
|
import ru.fromchat.api.DmFile
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ fun AttachmentPreview(
|
|||||||
onFullyLoaded = { isFullyLoaded = it }
|
onFullyLoaded = { isFullyLoaded = it }
|
||||||
)
|
)
|
||||||
isPendingImage -> PendingImageContent(
|
isPendingImage -> PendingImageContent(
|
||||||
uri = pendingFileUri!!,
|
uri = pendingFileUri,
|
||||||
isUploading = isUploading,
|
isUploading = isUploading,
|
||||||
uploadProgress = uploadProgress,
|
uploadProgress = uploadProgress,
|
||||||
isImage = true
|
isImage = true
|
||||||
@@ -277,6 +277,7 @@ private fun UnifiedImageContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun PendingImageContent(
|
private fun PendingImageContent(
|
||||||
uri: String,
|
uri: String,
|
||||||
@@ -391,6 +392,7 @@ private fun DeterminateCircularProgress(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun DecryptedImageContent(
|
private fun DecryptedImageContent(
|
||||||
messageId: Int,
|
messageId: Int,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package ru.fromchat.ui.chat
|
package ru.fromchat.ui.chat
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.AnimatedVisibilityScope
|
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -18,12 +18,12 @@ import androidx.compose.foundation.layout.BoxWithConstraints
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
@@ -36,15 +36,14 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.TransformOrigin
|
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.geometry.Rect
|
import androidx.compose.ui.geometry.Rect
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.TransformOrigin
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.layout.positionInRoot
|
import androidx.compose.ui.layout.positionInRoot
|
||||||
@@ -300,7 +299,7 @@ fun MessageItem(
|
|||||||
pendingFilename = message.pendingFilename,
|
pendingFilename = message.pendingFilename,
|
||||||
isUploading = false,
|
isUploading = false,
|
||||||
uploadProgress = null,
|
uploadProgress = null,
|
||||||
fileThumbnail = message.fileThumbnails!!.first().takeIf { it.isNotBlank() },
|
fileThumbnail = message.fileThumbnails.first().takeIf { it.isNotBlank() },
|
||||||
fileAspectRatio = message.fileAspectRatios?.firstOrNull()?.takeIf { it > 0f }
|
fileAspectRatio = message.fileAspectRatios?.firstOrNull()?.takeIf { it > 0f }
|
||||||
?: message.pendingFileAspectRatio,
|
?: message.pendingFileAspectRatio,
|
||||||
fileSizeBytes = message.fileSizes?.firstOrNull(),
|
fileSizeBytes = message.fileSizes?.firstOrNull(),
|
||||||
|
|||||||
@@ -4,26 +4,24 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import kotlinx.serialization.json.jsonArray
|
import kotlinx.serialization.json.jsonArray
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import ru.fromchat.api.ApiClient
|
import ru.fromchat.api.ApiClient
|
||||||
import ru.fromchat.api.DmEnvelope
|
import ru.fromchat.api.DmEnvelope
|
||||||
import ru.fromchat.api.ProfileCache
|
|
||||||
import ru.fromchat.api.Message
|
import ru.fromchat.api.Message
|
||||||
|
import ru.fromchat.api.ProfileCache
|
||||||
import ru.fromchat.api.WebSocketMessage
|
import ru.fromchat.api.WebSocketMessage
|
||||||
import ru.fromchat.core.Logger
|
import ru.fromchat.core.Logger
|
||||||
import ru.fromchat.crypto.decryptEnvelope
|
import ru.fromchat.crypto.decryptEnvelope
|
||||||
|
import ru.fromchat.ui.chat.AvatarInfo
|
||||||
import ru.fromchat.ui.chat.ChatPanel
|
import ru.fromchat.ui.chat.ChatPanel
|
||||||
import ru.fromchat.ui.chat.DecryptedImageCache
|
import ru.fromchat.ui.chat.DecryptedImageCache
|
||||||
import ru.fromchat.ui.chat.DmTypingHandler
|
import ru.fromchat.ui.chat.DmTypingHandler
|
||||||
import ru.fromchat.ui.chat.TypingHandler
|
import ru.fromchat.ui.chat.TypingHandler
|
||||||
import ru.fromchat.ui.chat.TypingUser
|
|
||||||
import ru.fromchat.ui.chat.AvatarInfo
|
|
||||||
|
|
||||||
class DmPanel(
|
class DmPanel(
|
||||||
private val otherUserId: Int,
|
private val otherUserId: Int,
|
||||||
@@ -79,8 +77,8 @@ class DmPanel(
|
|||||||
}.onSuccess { response ->
|
}.onSuccess { response ->
|
||||||
clearMessages()
|
clearMessages()
|
||||||
val decryptedForLog = mutableListOf<Pair<Int, String>>()
|
val decryptedForLog = mutableListOf<Pair<Int, String>>()
|
||||||
val messages = response.messages.mapNotNull { envelope ->
|
val messages = response.messages.map { envelope ->
|
||||||
decryptEnvelope(envelope, currentUserId)?.let { plaintext ->
|
decryptEnvelope(envelope, currentUserId).let { plaintext ->
|
||||||
decryptedForLog.add(envelope.id to plaintext)
|
decryptedForLog.add(envelope.id to plaintext)
|
||||||
createMessage(envelope, plaintext)
|
createMessage(envelope, plaintext)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.filled.Chat
|
import androidx.compose.material.icons.automirrored.filled.Chat
|
||||||
import androidx.compose.material.icons.filled.AlternateEmail
|
import androidx.compose.material.icons.filled.AlternateEmail
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.CalendarMonth
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.Link
|
import androidx.compose.material.icons.filled.Link
|
||||||
@@ -136,7 +136,7 @@ fun ProfileScreen(
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
@@ -178,8 +178,8 @@ fun ProfileScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 16.dp)
|
.padding(top = 16.dp)
|
||||||
.sharedElement(
|
.sharedElement(
|
||||||
rememberSharedContentState(key = sharedAvatarKey!!),
|
rememberSharedContentState(key = sharedAvatarKey),
|
||||||
animatedVisibilityScope = animatedVisibilityScope!!
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
)
|
)
|
||||||
.size(128.dp)
|
.size(128.dp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ru.fromchat.utils
|
package ru.fromchat.utils
|
||||||
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a user-friendly string for online status / last seen from an ISO-8601 timestamp.
|
* Returns a user-friendly string for online status / last seen from an ISO-8601 timestamp.
|
||||||
|
|||||||
Reference in New Issue
Block a user