feat: Add Lottie animation for avatar in attachment UI and refactor file layout by removing search functionality
This commit is contained in:
1
app/src/main/assets/lottie/avatar.json
Normal file
1
app/src/main/assets/lottie/avatar.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
|||||||
package com.rosetta.messenger
|
package com.rosetta.messenger
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.airbnb.lottie.L
|
||||||
import com.rosetta.messenger.data.DraftManager
|
import com.rosetta.messenger.data.DraftManager
|
||||||
import com.rosetta.messenger.utils.CrashReportManager
|
import com.rosetta.messenger.utils.CrashReportManager
|
||||||
|
|
||||||
@@ -16,6 +17,8 @@ class RosettaApplication : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
// Убираем красную букву "L" от Lottie
|
||||||
|
L.setTraceEnabled(false)
|
||||||
|
|
||||||
// Инициализируем crash reporter
|
// Инициализируем crash reporter
|
||||||
initCrashReporting()
|
initCrashReporting()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -11,7 +12,11 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.rosetta.messenger.ui.icons.TelegramIcons
|
import com.airbnb.lottie.compose.LottieAnimation
|
||||||
|
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||||
|
import com.airbnb.lottie.compose.LottieConstants
|
||||||
|
import com.airbnb.lottie.compose.animateLottieCompositionAsState
|
||||||
|
import com.airbnb.lottie.compose.rememberLottieComposition
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,6 +32,12 @@ internal fun AttachAlertAvatarLayout(
|
|||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||||
val secondaryTextColor = Color(0xFF8E8E93)
|
val secondaryTextColor = Color(0xFF8E8E93)
|
||||||
|
|
||||||
|
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie/avatar.json"))
|
||||||
|
val progress by animateLottieCompositionAsState(
|
||||||
|
composition = composition,
|
||||||
|
iterations = LottieConstants.IterateForever
|
||||||
|
)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
@@ -35,11 +46,10 @@ internal fun AttachAlertAvatarLayout(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.padding(32.dp)
|
modifier = Modifier.padding(32.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
LottieAnimation(
|
||||||
painter = TelegramIcons.Contact,
|
composition = composition,
|
||||||
contentDescription = null,
|
progress = { progress },
|
||||||
tint = PrimaryBlue,
|
modifier = Modifier.size(100.dp)
|
||||||
modifier = Modifier.size(72.dp)
|
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -106,8 +105,6 @@ internal fun AttachAlertFileLayout(
|
|||||||
var currentDir by remember { mutableStateOf<File?>(null) }
|
var currentDir by remember { mutableStateOf<File?>(null) }
|
||||||
var currentTitle by remember { mutableStateOf("Select File") }
|
var currentTitle by remember { mutableStateOf("Select File") }
|
||||||
var history by remember { mutableStateOf(listOf<HistoryEntry>()) }
|
var history by remember { mutableStateOf(listOf<HistoryEntry>()) }
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
|
||||||
var isSearching by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
// ── Gallery mode state ──
|
// ── Gallery mode state ──
|
||||||
var showGallery by remember { mutableStateOf(false) }
|
var showGallery by remember { mutableStateOf(false) }
|
||||||
@@ -127,11 +124,8 @@ internal fun AttachAlertFileLayout(
|
|||||||
} else emptyList()
|
} else emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Search filter ──
|
val displayedRecentFiles = recentFiles
|
||||||
val displayedRecentFiles = if (searchQuery.isBlank()) recentFiles
|
val displayedDirContents = dirContents
|
||||||
else recentFiles.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
|
||||||
val displayedDirContents = if (searchQuery.isBlank()) dirContents
|
|
||||||
else dirContents.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
|
||||||
|
|
||||||
// ── Back handler ──
|
// ── Back handler ──
|
||||||
val isAtRoot = currentDir == null
|
val isAtRoot = currentDir == null
|
||||||
@@ -146,8 +140,6 @@ internal fun AttachAlertFileLayout(
|
|||||||
currentDir = null
|
currentDir = null
|
||||||
currentTitle = "Select File"
|
currentTitle = "Select File"
|
||||||
}
|
}
|
||||||
searchQuery = ""
|
|
||||||
isSearching = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun navigateToDir(dir: File) {
|
fun navigateToDir(dir: File) {
|
||||||
@@ -155,8 +147,6 @@ internal fun AttachAlertFileLayout(
|
|||||||
history = history + HistoryEntry(currentDir ?: Environment.getExternalStorageDirectory(), currentTitle)
|
history = history + HistoryEntry(currentDir ?: Environment.getExternalStorageDirectory(), currentTitle)
|
||||||
currentDir = dir
|
currentDir = dir
|
||||||
currentTitle = dir.name
|
currentTitle = dir.name
|
||||||
searchQuery = ""
|
|
||||||
isSearching = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGallery) {
|
if (showGallery) {
|
||||||
@@ -182,8 +172,6 @@ internal fun AttachAlertFileLayout(
|
|||||||
FilePickerTopBar(
|
FilePickerTopBar(
|
||||||
title = displayTitle,
|
title = displayTitle,
|
||||||
showBack = showGallery || !isAtRoot,
|
showBack = showGallery || !isAtRoot,
|
||||||
isSearching = isSearching,
|
|
||||||
searchQuery = searchQuery,
|
|
||||||
onBackClick = {
|
onBackClick = {
|
||||||
if (showGallery) {
|
if (showGallery) {
|
||||||
showGallery = false
|
showGallery = false
|
||||||
@@ -192,8 +180,6 @@ internal fun AttachAlertFileLayout(
|
|||||||
navigateBack()
|
navigateBack()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSearchToggle = { isSearching = !isSearching; if (!isSearching) searchQuery = "" },
|
|
||||||
onSearchQueryChange = { searchQuery = it },
|
|
||||||
textColor = textColor,
|
textColor = textColor,
|
||||||
bgColor = bgColor
|
bgColor = bgColor
|
||||||
)
|
)
|
||||||
@@ -357,7 +343,7 @@ internal fun AttachAlertFileLayout(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (searchQuery.isBlank()) {
|
} else {
|
||||||
item(key = "recent_empty_divider") {
|
item(key = "recent_empty_divider") {
|
||||||
Spacer(
|
Spacer(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -390,7 +376,7 @@ internal fun AttachAlertFileLayout(
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if (searchQuery.isNotBlank()) "No results" else "Empty folder",
|
text = "Empty folder",
|
||||||
color = secondaryText,
|
color = secondaryText,
|
||||||
fontSize = 15.sp
|
fontSize = 15.sp
|
||||||
)
|
)
|
||||||
@@ -437,11 +423,7 @@ internal fun AttachAlertFileLayout(
|
|||||||
private fun FilePickerTopBar(
|
private fun FilePickerTopBar(
|
||||||
title: String,
|
title: String,
|
||||||
showBack: Boolean,
|
showBack: Boolean,
|
||||||
isSearching: Boolean,
|
|
||||||
searchQuery: String,
|
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
onSearchToggle: () -> Unit,
|
|
||||||
onSearchQueryChange: (String) -> Unit,
|
|
||||||
textColor: Color,
|
textColor: Color,
|
||||||
bgColor: Color
|
bgColor: Color
|
||||||
) {
|
) {
|
||||||
@@ -465,26 +447,6 @@ private fun FilePickerTopBar(
|
|||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSearching) {
|
|
||||||
TextField(
|
|
||||||
value = searchQuery,
|
|
||||||
onValueChange = onSearchQueryChange,
|
|
||||||
placeholder = {
|
|
||||||
Text("Search", color = Color(0xFF8E8E93), fontSize = 16.sp)
|
|
||||||
},
|
|
||||||
singleLine = true,
|
|
||||||
colors = TextFieldDefaults.colors(
|
|
||||||
focusedContainerColor = Color.Transparent,
|
|
||||||
unfocusedContainerColor = Color.Transparent,
|
|
||||||
focusedIndicatorColor = Color.Transparent,
|
|
||||||
unfocusedIndicatorColor = Color.Transparent,
|
|
||||||
cursorColor = PrimaryBlue,
|
|
||||||
focusedTextColor = textColor,
|
|
||||||
unfocusedTextColor = textColor
|
|
||||||
),
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
@@ -495,15 +457,6 @@ private fun FilePickerTopBar(
|
|||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(onClick = onSearchToggle) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Search,
|
|
||||||
contentDescription = "Search",
|
|
||||||
tint = textColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Root‑level special item row (Internal Storage, Rosetta, Gallery) */
|
/** Root‑level special item row (Internal Storage, Rosetta, Gallery) */
|
||||||
|
|||||||
@@ -675,26 +675,13 @@ fun ImageCollage(
|
|||||||
val totalRows = rows.size
|
val totalRows = rows.size
|
||||||
rows.forEachIndexed { rowIndex, rowItems ->
|
rows.forEachIndexed { rowIndex, rowItems ->
|
||||||
val isLastRow = rowIndex == totalRows - 1
|
val isLastRow = rowIndex == totalRows - 1
|
||||||
val isIncompleteRow = rowItems.size < 3
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = if (isIncompleteRow)
|
horizontalArrangement = Arrangement.spacedBy(spacing)
|
||||||
Arrangement.Start
|
|
||||||
else
|
|
||||||
Arrangement.spacedBy(spacing)
|
|
||||||
) {
|
) {
|
||||||
rowItems.forEachIndexed { index, attachment ->
|
rowItems.forEachIndexed { index, attachment ->
|
||||||
val isLastItem = isLastRow && index == rowItems.size - 1
|
val isLastItem = isLastRow && index == rowItems.size - 1
|
||||||
// Для неполных рядов используем фиксированную ширину = 1/3 от общей
|
Box(modifier = Modifier.weight(1f).aspectRatio(1f)) {
|
||||||
val cellModifier = if (isIncompleteRow) {
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth(1f / 3f)
|
|
||||||
.padding(end = if (index < rowItems.size - 1) spacing else 0.dp)
|
|
||||||
.aspectRatio(1f)
|
|
||||||
} else {
|
|
||||||
Modifier.weight(1f).aspectRatio(1f)
|
|
||||||
}
|
|
||||||
Box(modifier = cellModifier) {
|
|
||||||
ImageAttachment(
|
ImageAttachment(
|
||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
|
|||||||
Reference in New Issue
Block a user