Compare commits
1 Commits
e10e47d6c0
..
feat
| Author | SHA1 | Date | |
|---|---|---|---|
| a91bbaf129 |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+6
-16
@@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* Created by sweetbread
|
|
||||||
* Copyright (c) 2026. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by sweetbread
|
* Created by sweetbread
|
||||||
* Copyright (c) 2026. All rights reserved.
|
* Copyright (c) 2026. All rights reserved.
|
||||||
@@ -12,13 +5,14 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
// alias(libs.plugins.ksp)
|
// alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "ru.risdeveau.pixeldragon"
|
namespace = "ru.risdeveau.pixeldragon"
|
||||||
compileSdk = 36
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "ru.risdeveau.pixeldragon"
|
applicationId = "ru.risdeveau.pixeldragon"
|
||||||
@@ -43,17 +37,14 @@ android {
|
|||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(JvmTarget.JVM_17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
@@ -63,7 +54,6 @@ dependencies {
|
|||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.androidx.compose.ui.unit)
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@@ -104,5 +94,5 @@ dependencies {
|
|||||||
// Others
|
// Others
|
||||||
implementation(libs.splitties.base) // Syntax sugar
|
implementation(libs.splitties.base) // Syntax sugar
|
||||||
implementation(libs.jsoup) // HTML parser
|
implementation(libs.jsoup) // HTML parser
|
||||||
implementation(libs.iconsax.compose) // Material icons
|
|
||||||
}
|
}
|
||||||
@@ -5,81 +5,52 @@
|
|||||||
|
|
||||||
package ru.risdeveau.pixeldragon.ui.activity
|
package ru.risdeveau.pixeldragon.ui.activity
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
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.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavHostController
|
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import de.connect2x.trixnity.client.CryptoDriverModule
|
import de.connect2x.trixnity.client.CryptoDriverModule
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import de.connect2x.trixnity.client.MatrixClient
|
import de.connect2x.trixnity.client.MatrixClient
|
||||||
import de.connect2x.trixnity.client.create
|
import de.connect2x.trixnity.client.create
|
||||||
import de.connect2x.trixnity.client.cryptodriver.vodozemac.vodozemac
|
import de.connect2x.trixnity.client.cryptodriver.vodozemac.vodozemac
|
||||||
import de.connect2x.trixnity.client.flattenValues
|
|
||||||
import de.connect2x.trixnity.client.room
|
|
||||||
import de.connect2x.trixnity.client.store.type
|
|
||||||
import de.connect2x.trixnity.clientserverapi.client.SyncState
|
import de.connect2x.trixnity.clientserverapi.client.SyncState
|
||||||
import de.connect2x.trixnity.clientserverapi.model.user.avatarUrl
|
|
||||||
import de.connect2x.trixnity.clientserverapi.model.user.displayName
|
|
||||||
import de.connect2x.trixnity.core.model.events.m.room.CreateEventContent
|
|
||||||
import io.github.rabehx.iconsax.Iconsax
|
|
||||||
import io.github.rabehx.iconsax.automirrored.outline.ArrowLeft2
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import ru.risdeveau.pixeldragon.R
|
import ru.risdeveau.pixeldragon.R
|
||||||
import ru.risdeveau.pixeldragon.client
|
import ru.risdeveau.pixeldragon.client
|
||||||
import ru.risdeveau.pixeldragon.ui.item.Avatar
|
|
||||||
import ru.risdeveau.pixeldragon.ui.layout.Room
|
|
||||||
import ru.risdeveau.pixeldragon.ui.layout.RoomList
|
import ru.risdeveau.pixeldragon.ui.layout.RoomList
|
||||||
import ru.risdeveau.pixeldragon.ui.theme.PixelDragonTheme
|
import ru.risdeveau.pixeldragon.ui.theme.PixelDragonTheme
|
||||||
import ru.risdeveau.pixeldragon.util.getMediaStore
|
import ru.risdeveau.pixeldragon.util.getMediaStore
|
||||||
import ru.risdeveau.pixeldragon.util.getRoomStore
|
import ru.risdeveau.pixeldragon.util.getRoomStore
|
||||||
import splitties.activities.start
|
import splitties.activities.start
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import de.connect2x.trixnity.client.store.Room as TrixnityRoom
|
import splitties.resources.str
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -87,28 +58,34 @@ class MainActivity : ComponentActivity() {
|
|||||||
setContent {
|
setContent {
|
||||||
PixelDragonTheme {
|
PixelDragonTheme {
|
||||||
var isClientReady by remember { mutableStateOf(false) }
|
var isClientReady by remember { mutableStateOf(false) }
|
||||||
|
val syncState by remember { mutableStateOf(SyncState.STOPPED) }
|
||||||
|
|
||||||
if (!isClientReady || client == null) {
|
if (!isClientReady) {
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val navController = rememberNavController()
|
|
||||||
val syncState by client!!.api.sync.currentSyncState
|
|
||||||
.collectAsState(initial = SyncState.STOPPED)
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
PixelDragonTopBar(
|
CenterAlignedTopAppBar(
|
||||||
navController = navController,
|
colors = topAppBarColors(
|
||||||
syncState = syncState,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
title = {
|
||||||
|
when (syncState) {
|
||||||
|
SyncState.STARTED -> Text("Syncing...")
|
||||||
|
SyncState.INITIAL_SYNC -> Text("Initial sync...")
|
||||||
|
SyncState.STOPPED,
|
||||||
|
SyncState.RUNNING -> Text(str(R.string.app_name))
|
||||||
|
SyncState.TIMEOUT -> Text("No network connection")
|
||||||
|
SyncState.ERROR -> Text("Error syncing")
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
NavHost(navController = navController, startDestination = "rooms") {
|
NavHost(navController = navController, startDestination = "rooms") {
|
||||||
composable("rooms") {
|
composable("rooms") {
|
||||||
RoomList(Modifier.padding(innerPadding), navController)
|
RoomList(Modifier.padding(innerPadding), navController)
|
||||||
@@ -117,14 +94,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
"room/{rid}",
|
"room/{rid}",
|
||||||
arguments = listOf(navArgument("rid") { type = NavType.StringType })
|
arguments = listOf(navArgument("rid") { type = NavType.StringType })
|
||||||
) { navBackStackEntry ->
|
) { navBackStackEntry ->
|
||||||
Room(Modifier
|
// Room(Modifier
|
||||||
.padding(innerPadding)
|
// .padding(innerPadding)
|
||||||
.fillMaxSize(), navBackStackEntry.arguments!!.getString("rid")!!)
|
// .fillMaxSize(), navBackStackEntry.arguments!!.getString("rid")!!)
|
||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
"space/{rid}",
|
"space/{rid}",
|
||||||
arguments = listOf(navArgument("rid") { type = NavType.StringType })
|
arguments = listOf(navArgument("rid") { type = NavType.StringType })
|
||||||
) {
|
) { navBackStackEntry ->
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
text = "Not implemented"
|
text = "Not implemented"
|
||||||
@@ -164,156 +141,3 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("FlowOperatorInvokedInComposition")
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun PixelDragonTopBar(
|
|
||||||
navController: NavHostController,
|
|
||||||
syncState: SyncState,
|
|
||||||
) {
|
|
||||||
val backStackEntry by navController.currentBackStackEntryAsState()
|
|
||||||
val route = backStackEntry?.destination?.route
|
|
||||||
val rid = backStackEntry?.arguments?.getString("rid")
|
|
||||||
val isRoomLikeScreen = route == "room/{rid}" || route == "space/{rid}"
|
|
||||||
|
|
||||||
val roomsFlow = remember(client) {
|
|
||||||
client!!.room.getAll().flattenValues().map { it.toList() }
|
|
||||||
}
|
|
||||||
val rooms by roomsFlow.collectAsState(initial = emptyList())
|
|
||||||
|
|
||||||
val currentRoom = remember(isRoomLikeScreen, rid, rooms) {
|
|
||||||
if (isRoomLikeScreen && rid != null) {
|
|
||||||
rooms.firstOrNull { it.roomId.toString() == rid }
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRoomLikeScreen) {
|
|
||||||
RoomTopBar(
|
|
||||||
room = currentRoom,
|
|
||||||
fallbackTitle = rid ?: stringResource(R.string.app_name),
|
|
||||||
onBack = { navController.popBackStack() },
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
HomeTopBar(syncState = syncState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun HomeTopBar(syncState: SyncState) {
|
|
||||||
CenterAlignedTopAppBar(
|
|
||||||
colors = topAppBarColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
titleContentColor = MaterialTheme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
title = {
|
|
||||||
Text(syncState.toStatusTitle() ?: stringResource(R.string.app_name))
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
val client = client!!
|
|
||||||
|
|
||||||
var userName by remember { mutableStateOf("?") }
|
|
||||||
var userAvatar: String? by remember { mutableStateOf(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(client, syncState) {
|
|
||||||
val profile = client.api.user
|
|
||||||
.getProfile(client.userId)
|
|
||||||
.getOrNull()
|
|
||||||
|
|
||||||
userName = profile?.displayName ?: "?"
|
|
||||||
userAvatar = profile?.avatarUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar(
|
|
||||||
Modifier.size(32.dp).clip(CircleShape),
|
|
||||||
userAvatar,
|
|
||||||
userName,
|
|
||||||
MaterialTheme.colorScheme.background
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun RoomTopBar(
|
|
||||||
room: TrixnityRoom?,
|
|
||||||
fallbackTitle: String,
|
|
||||||
onBack: () -> Unit,
|
|
||||||
) {
|
|
||||||
CenterAlignedTopAppBar(
|
|
||||||
colors = topAppBarColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface.copy(.5f),
|
|
||||||
titleContentColor = MaterialTheme.colorScheme.onSurface,
|
|
||||||
navigationIconContentColor = MaterialTheme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
navigationIcon = {
|
|
||||||
IconButton(onClick = onBack) {
|
|
||||||
Icon(
|
|
||||||
Iconsax.AutoMirrored.Outline.ArrowLeft2,
|
|
||||||
"To home",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
if (room != null) {
|
|
||||||
RoomTopBarTitle(room)
|
|
||||||
} else {
|
|
||||||
Text(
|
|
||||||
text = fallbackTitle,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RoomTopBarTitle(room: TrixnityRoom) {
|
|
||||||
val title = remember(room) { room.displayName() }
|
|
||||||
val isSpace = room.type == CreateEventContent.RoomType.Space
|
|
||||||
val avatarShape = if (isSpace) RoundedCornerShape(8.dp) else CircleShape
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Avatar(
|
|
||||||
Modifier
|
|
||||||
.size(36.dp)
|
|
||||||
.clip(avatarShape),
|
|
||||||
room.avatarUrl,
|
|
||||||
title
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.width(10.dp))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TrixnityRoom.displayName(): String {
|
|
||||||
return name?.explicitName
|
|
||||||
?: name?.heroes?.firstNotNullOfOrNull { it.localpart }
|
|
||||||
?: roomId.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SyncState.toStatusTitle(): String? {
|
|
||||||
return when (this) {
|
|
||||||
SyncState.INITIAL_SYNC -> "Initial sync..."
|
|
||||||
SyncState.STARTED -> "Syncing..."
|
|
||||||
SyncState.TIMEOUT -> "No network connection"
|
|
||||||
SyncState.ERROR -> "Error syncing"
|
|
||||||
SyncState.RUNNING,
|
|
||||||
SyncState.STOPPED -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by sweetbread
|
|
||||||
* Copyright (c) 2026. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ru.risdeveau.pixeldragon.ui.item
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Avatar(
|
|
||||||
modifier: Modifier,
|
|
||||||
url: String?,
|
|
||||||
fallbackName: String,
|
|
||||||
fallbackColor: Color = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
) {
|
|
||||||
if (url != null) {
|
|
||||||
MXCImage(
|
|
||||||
mxcUrl = url,
|
|
||||||
modifier = modifier,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
contentDescription = fallbackName,
|
|
||||||
showProgress = false,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
AvatarPlaceholder(
|
|
||||||
modifier,
|
|
||||||
fallbackName,
|
|
||||||
fallbackColor,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AvatarPlaceholder(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
fallbackName: String,
|
|
||||||
fallbackColor: Color,
|
|
||||||
) {
|
|
||||||
val initial = fallbackName
|
|
||||||
.trim()
|
|
||||||
.firstOrNull()
|
|
||||||
?.uppercaseChar()
|
|
||||||
?.toString()
|
|
||||||
?: "?"
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = modifier.background(fallbackColor),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = initial,
|
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,8 @@ package ru.risdeveau.pixeldragon.ui.item
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import io.github.rabehx.iconsax.Iconsax
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Warning
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -27,7 +28,6 @@ import coil3.request.ImageRequest
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import de.connect2x.trixnity.client.media
|
import de.connect2x.trixnity.client.media
|
||||||
import de.connect2x.trixnity.clientserverapi.model.media.FileTransferProgress
|
import de.connect2x.trixnity.clientserverapi.model.media.FileTransferProgress
|
||||||
import io.github.rabehx.iconsax.outline.Warning2
|
|
||||||
import ru.risdeveau.pixeldragon.client
|
import ru.risdeveau.pixeldragon.client
|
||||||
|
|
||||||
enum class ImageLoadState {
|
enum class ImageLoadState {
|
||||||
@@ -119,7 +119,7 @@ fun MXCImage(
|
|||||||
}
|
}
|
||||||
imageLoadState == ImageLoadState.Error -> {
|
imageLoadState == ImageLoadState.Error -> {
|
||||||
Icon(
|
Icon(
|
||||||
Iconsax.Outline.Warning2,
|
Icons.Outlined.Warning,
|
||||||
contentDescription = "Error",
|
contentDescription = "Error",
|
||||||
modifier = Modifier.align(Alignment.Center)
|
modifier = Modifier.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,21 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "9.1.0"
|
agp = "8.13.0"
|
||||||
coil = "3.4.0"
|
coil = "3.1.0"
|
||||||
iconsaxCompose = "0.0.5"
|
jsoup = "1.20.1"
|
||||||
jsoup = "1.22.1"
|
|
||||||
kotlin = "2.2.21"
|
kotlin = "2.2.21"
|
||||||
coreKtx = "1.15.0"
|
coreKtx = "1.15.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.3.0"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.7.0"
|
espressoCore = "3.6.1"
|
||||||
kotlinxSerializationJson = "1.7.3"
|
kotlinxSerializationJson = "1.7.3"
|
||||||
ktor = "3.4.1"
|
ktor = "3.1.0"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
activityCompose = "1.10.0"
|
activityCompose = "1.10.0"
|
||||||
composeBom = "2026.03.00"
|
composeBom = "2025.02.00"
|
||||||
navigationCompose = "2.9.7"
|
navigationCompose = "2.8.8"
|
||||||
room = "2.6.1"
|
room = "2.6.1"
|
||||||
splittiesFunPackAndroidBase = "3.0.0"
|
splittiesFunPackAndroidBase = "3.0.0"
|
||||||
trixnityClient = "5.2.0"
|
trixnityClient = "5.2.0"
|
||||||
uiUnit = "1.10.6"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -31,7 +29,6 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
|||||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||||
iconsax-compose = { module = "io.github.rabehx:iconsax-compose", version.ref = "iconsaxCompose" }
|
|
||||||
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
@@ -54,7 +51,6 @@ trixnity-client = { module = "de.connect2x.trixnity:trixnity-client", version.re
|
|||||||
trixnity-client-media-okio = { module = "de.connect2x.trixnity:trixnity-client-media-okio", version.ref = "trixnityClient" }
|
trixnity-client-media-okio = { module = "de.connect2x.trixnity:trixnity-client-media-okio", version.ref = "trixnityClient" }
|
||||||
trixnity-client-repository-room = { module = "de.connect2x.trixnity:trixnity-client-repository-room", version.ref = "trixnityClient" }
|
trixnity-client-repository-room = { module = "de.connect2x.trixnity:trixnity-client-repository-room", version.ref = "trixnityClient" }
|
||||||
trixnity-client-cryptodriver-vodozemac = { module = "de.connect2x.trixnity:trixnity-client-cryptodriver-vodozemac", version.ref = "trixnityClient" }
|
trixnity-client-cryptodriver-vodozemac = { module = "de.connect2x.trixnity:trixnity-client-cryptodriver-vodozemac", version.ref = "trixnityClient" }
|
||||||
androidx-compose-ui-unit = { group = "androidx.compose.ui", name = "ui-unit", version.ref = "uiUnit" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
+1
-1
@@ -6,6 +6,6 @@
|
|||||||
#Thu Feb 20 10:45:47 GMT 2025
|
#Thu Feb 20 10:45:47 GMT 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
Reference in New Issue
Block a user