Replace remaining avatars with robot fallback images

This commit is contained in:
maxmoney21m
2023-03-11 13:52:17 +08:00
parent 9f6867b6ab
commit 0eb21a6650
12 changed files with 498 additions and 477 deletions
@@ -1,11 +1,6 @@
package com.vitorpamplona.amethyst.ui.components package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.ColorFilter import androidx.compose.ui.graphics.ColorFilter
@@ -31,50 +26,6 @@ data class ResizeImage(val url: String?, val size: Dp) {
} }
} }
@Composable fun AsyncUserImageProxy(
pubkeyHex: String,
model: ResizeImage,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
var loading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf(false) }
Box() {
AsyncImage(
model = model.proxyUrl(),
contentDescription = contentDescription,
modifier = modifier,
onLoading = { loading = true },
onSuccess = { loading = false; error = false },
onError = { loading = false; error = true },
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
if (model.url == null || loading || error) {
RoboHashAsyncImage(
message = pubkeyHex,
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
}
}
}
@Composable @Composable
fun AsyncImageProxy( fun AsyncImageProxy(
model: ResizeImage, model: ResizeImage,
@@ -1,19 +1,8 @@
package com.vitorpamplona.amethyst.ui.components package com.vitorpamplona.amethyst.ui.components
import android.content.Context import android.content.Context
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.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import coil.request.ImageRequest import coil.request.ImageRequest
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.security.MessageDigest import java.security.MessageDigest
@@ -35,7 +24,7 @@ private fun bytesToRGB(b1: Byte, b2: Byte, b3: Byte): Color {
return Color(b1.toUByte().toInt(), b2.toUByte().toInt(), b3.toUByte().toInt()) return Color(b1.toUByte().toInt(), b2.toUByte().toInt(), b3.toUByte().toInt())
} }
fun generateRoboHashSvg(msg: String): String { private fun svgString(msg: String): String {
val hash = sha256.digest(msg.toByteArray()) val hash = sha256.digest(msg.toByteArray())
val hashHex = hash.joinToString(separator = "") { b -> "%02x".format(b) } val hashHex = hash.joinToString(separator = "") { b -> "%02x".format(b) }
val bgColor1 = bytesToRGB(hash[0], hash[1], hash[2]) val bgColor1 = bytesToRGB(hash[0], hash[1], hash[2])
@@ -68,42 +57,17 @@ fun generateRoboHashSvg(msg: String): String {
""".trimIndent() """.trimIndent()
} }
fun roboHashImageRequest(context: Context, message: String): ImageRequest { object Robohash {
return ImageRequest fun imageRequest(context: Context, message: String): ImageRequest {
.Builder(context) return ImageRequest
.data( .Builder(context)
ByteBuffer.wrap( .data(
generateRoboHashSvg(message).toByteArray() ByteBuffer.wrap(
svgString(message).toByteArray()
)
) )
) .build()
.build() }
}
@Composable
fun RoboHashAsyncImage(
message: String,
modifier: Modifier = Modifier,
contentDescription: String? = null,
transform: (AsyncImagePainter.State) -> AsyncImagePainter.State = AsyncImagePainter.DefaultTransform,
onState: ((AsyncImagePainter.State) -> Unit)? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
AsyncImage(
model = roboHashImageRequest(LocalContext.current, message),
contentDescription = contentDescription,
modifier = modifier,
transform = transform,
onState = onState,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
} }
private data class Part(val style: String, val paths: String) private data class Part(val style: String, val paths: String)
@@ -0,0 +1,128 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
@Composable
fun RobohashAsyncImage(
robot: String,
modifier: Modifier = Modifier,
contentDescription: String? = null,
transform: (AsyncImagePainter.State) -> AsyncImagePainter.State = AsyncImagePainter.DefaultTransform,
onState: ((AsyncImagePainter.State) -> Unit)? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
AsyncImage(
model = Robohash.imageRequest(LocalContext.current, robot),
contentDescription = contentDescription,
modifier = modifier,
transform = transform,
onState = onState,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
}
@Composable
fun RobohashFallbackAsyncImage(
robot: String = "aaaa",
model: Any?,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
var loading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf(false) }
Box {
AsyncImage(
model = model,
contentDescription = contentDescription,
modifier = modifier,
onLoading = { loading = true },
onSuccess = { loading = false; error = false },
onError = { error = true },
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
if (loading || error) {
RobohashAsyncImage(
robot = robot,
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
}
}
}
@Composable
fun RobohashAsyncImageProxy(
robot: String,
model: ResizeImage,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
if (model.url == null) {
RobohashAsyncImage(
robot = robot,
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
} else {
RobohashFallbackAsyncImage(
robot = robot,
model = model.proxyUrl(),
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
)
}
}
@@ -57,8 +57,8 @@ import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.RelayPool import com.vitorpamplona.amethyst.service.relays.RelayPool
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -195,12 +195,9 @@ fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel)
}, },
modifier = Modifier modifier = Modifier
) { ) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = accountUser.pubkeyHex, robot = accountUser.pubkeyHex,
model = ResizeImage(accountUser.profilePicture(), 34.dp), model = ResizeImage(accountUser.profilePicture(), 34.dp),
// placeholder = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
// fallback = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
// error = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier modifier = Modifier
.width(34.dp) .width(34.dp)
@@ -49,8 +49,8 @@ import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -135,13 +135,10 @@ fun ProfileContent(baseAccountUser: User, modifier: Modifier = Modifier, scaffol
} }
Column(modifier = modifier) { Column(modifier = modifier) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = accountUser.pubkeyHex, robot = accountUser.pubkeyHex,
model = ResizeImage(accountUser.profilePicture(), 100.dp), model = ResizeImage(accountUser.profilePicture(), 100.dp),
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
// placeholder = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
// fallback = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
// error = BitmapPainter(RoboHashCache.get(ctx, accountUser.pubkeyHex)),
modifier = Modifier modifier = Modifier
.width(100.dp) .width(100.dp)
.height(100.dp) .height(100.dp)
@@ -27,8 +27,6 @@ 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.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
@@ -43,12 +41,11 @@ import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.ui.components.AsyncImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -98,13 +95,8 @@ fun ChatroomCompose(
} }
ChannelName( ChannelName(
channelIdHex = channel.idHex,
channelPicture = channel.profilePicture(), channelPicture = channel.profilePicture(),
channelPicturePlaceholder = BitmapPainter(
RoboHashCache.get(
context,
channel.idHex
)
),
channelTitle = { channelTitle = {
Text( Text(
text = buildAnnotatedString { text = buildAnnotatedString {
@@ -183,8 +175,8 @@ fun ChatroomCompose(
@Composable @Composable
fun ChannelName( fun ChannelName(
channelIdHex: String,
channelPicture: String?, channelPicture: String?,
channelPicturePlaceholder: Painter?,
channelTitle: @Composable (Modifier) -> Unit, channelTitle: @Composable (Modifier) -> Unit,
channelLastTime: Long?, channelLastTime: Long?,
channelLastContent: String?, channelLastContent: String?,
@@ -193,11 +185,9 @@ fun ChannelName(
) { ) {
ChannelName( ChannelName(
channelPicture = { channelPicture = {
AsyncImageProxy( RobohashAsyncImageProxy(
robot = channelIdHex,
model = ResizeImage(channelPicture, 55.dp), model = ResizeImage(channelPicture, 55.dp),
placeholder = channelPicturePlaceholder,
fallback = channelPicturePlaceholder,
error = channelPicturePlaceholder,
contentDescription = stringResource(R.string.channel_image), contentDescription = stringResource(R.string.channel_image),
modifier = Modifier modifier = Modifier
.width(55.dp) .width(55.dp)
@@ -40,7 +40,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
@@ -51,17 +50,16 @@ import androidx.compose.ui.unit.IntSize
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 androidx.navigation.NavController import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -149,12 +147,14 @@ fun ChatroomMessageCompose(
val modif = if (innerQuote) { val modif = if (innerQuote) {
Modifier.padding(top = 10.dp, end = 5.dp) Modifier.padding(top = 10.dp, end = 5.dp)
} else { } else {
Modifier.fillMaxWidth(1f).padding( Modifier
start = 12.dp, .fillMaxWidth(1f)
end = 12.dp, .padding(
top = 5.dp, start = 12.dp,
bottom = 5.dp end = 12.dp,
) top = 5.dp,
bottom = 5.dp
)
} }
Row( Row(
@@ -182,9 +182,11 @@ fun ChatroomMessageCompose(
var bubbleSize by remember { mutableStateOf(IntSize.Zero) } var bubbleSize by remember { mutableStateOf(IntSize.Zero) }
Column( Column(
modifier = Modifier.padding(start = 10.dp, end = 5.dp, bottom = 5.dp).onSizeChanged { modifier = Modifier
bubbleSize = it .padding(start = 10.dp, end = 5.dp, bottom = 5.dp)
} .onSizeChanged {
bubbleSize = it
}
) { ) {
val authorState by note.author!!.live().metadata.observeAsState() val authorState by note.author!!.live().metadata.observeAsState()
val author = authorState?.user!! val author = authorState?.user!!
@@ -195,12 +197,9 @@ fun ChatroomMessageCompose(
horizontalArrangement = alignment, horizontalArrangement = alignment,
modifier = Modifier.padding(top = 5.dp) modifier = Modifier.padding(top = 5.dp)
) { ) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = author.pubkeyHex, robot = author.pubkeyHex,
model = ResizeImage(author.profilePicture(), 25.dp), model = ResizeImage(author.profilePicture(), 25.dp),
// placeholder = BitmapPainter(RoboHashCache.get(context, author.pubkeyHex)),
// fallback = BitmapPainter(RoboHashCache.get(context, author.pubkeyHex)),
// error = BitmapPainter(RoboHashCache.get(context, author.pubkeyHex)),
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier modifier = Modifier
.width(25.dp) .width(25.dp)
@@ -308,11 +307,16 @@ fun ChatroomMessageCompose(
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.padding(top = 5.dp).then( modifier = Modifier
with(LocalDensity.current) { .padding(top = 5.dp)
Modifier.widthIn(bubbleSize.width.toDp(), availableBubbleSize.width.toDp()) .then(
} with(LocalDensity.current) {
) Modifier.widthIn(
bubbleSize.width.toDp(),
availableBubbleSize.width.toDp()
)
}
)
) { ) {
Row() { Row() {
Text( Text(
@@ -366,18 +370,16 @@ private fun RelayBadges(baseNote: Note) {
.size(15.dp) .size(15.dp)
.padding(1.dp) .padding(1.dp)
) { ) {
AsyncImage( RobohashFallbackAsyncImage(
robot = "https://$url/favicon.ico",
model = "https://$url/favicon.ico", model = "https://$url/favicon.ico",
placeholder = BitmapPainter(RoboHashCache.get(ctx, url)),
fallback = BitmapPainter(RoboHashCache.get(ctx, url)),
error = BitmapPainter(RoboHashCache.get(ctx, url)),
contentDescription = stringResource(id = R.string.relay_icon), contentDescription = stringResource(id = R.string.relay_icon),
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }), colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }),
modifier = Modifier modifier = Modifier
.fillMaxSize(1f) .fillMaxSize(1f)
.clip(shape = CircleShape) .clip(shape = CircleShape)
.background(MaterialTheme.colors.background) .background(MaterialTheme.colors.background)
.clickable(onClick = { uri.openUri("https://" + url) }) .clickable(onClick = { uri.openUri("https://$url") })
) )
} }
} }
@@ -19,7 +19,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -38,7 +37,6 @@ import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
@@ -53,9 +51,11 @@ import com.vitorpamplona.amethyst.service.model.ReactionEvent
import com.vitorpamplona.amethyst.service.model.ReportEvent import com.vitorpamplona.amethyst.service.model.ReportEvent
import com.vitorpamplona.amethyst.service.model.RepostEvent import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.service.model.TextNoteEvent import com.vitorpamplona.amethyst.service.model.TextNoteEvent
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader
@@ -217,12 +217,9 @@ fun NoteCompose(
.height(30.dp) .height(30.dp)
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
) { ) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = channel.idHex, robot = channel.idHex,
model = ResizeImage(channel.profilePicture(), 30.dp), model = ResizeImage(channel.profilePicture(), 30.dp),
// placeholder = BitmapPainter(RoboHashCache.get(context, channel.idHex)),
// fallback = BitmapPainter(RoboHashCache.get(context, channel.idHex)),
// error = BitmapPainter(RoboHashCache.get(context, channel.idHex)),
contentDescription = stringResource(R.string.group_picture), contentDescription = stringResource(R.string.group_picture),
modifier = Modifier modifier = Modifier
.width(30.dp) .width(30.dp)
@@ -603,11 +600,9 @@ private fun RelayBadges(baseNote: Note) {
.size(15.dp) .size(15.dp)
.padding(1.dp) .padding(1.dp)
) { ) {
AsyncImage( RobohashFallbackAsyncImage(
robot = "https://$url/favicon.ico",
model = "https://$url/favicon.ico", model = "https://$url/favicon.ico",
placeholder = BitmapPainter(RoboHashCache.get(ctx, url)),
fallback = BitmapPainter(RoboHashCache.get(ctx, url)),
error = BitmapPainter(RoboHashCache.get(ctx, url)),
contentDescription = stringResource(R.string.relay_icon), contentDescription = stringResource(R.string.relay_icon),
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }), colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }),
modifier = Modifier modifier = Modifier
@@ -677,8 +672,8 @@ fun NoteAuthorPicture(
.height(size) .height(size)
) { ) {
if (author == null) { if (author == null) {
Image( RobohashAsyncImage(
painter = BitmapPainter(RoboHashCache.get(ctx, "ohnothisauthorisnotfound")), robot = "authornotfound",
contentDescription = stringResource(R.string.unknown_author), contentDescription = stringResource(R.string.unknown_author),
modifier = pictureModifier modifier = pictureModifier
.fillMaxSize(1f) .fillMaxSize(1f)
@@ -724,8 +719,8 @@ fun UserPicture(
.width(size) .width(size)
.height(size) .height(size)
) { ) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = user.pubkeyHex, robot = user.pubkeyHex,
model = ResizeImage(user.profilePicture(), size), model = ResizeImage(user.profilePicture(), size),
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
modifier = pictureModifier modifier = pictureModifier
@@ -66,8 +66,8 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.NostrChannelDataSource import com.vitorpamplona.amethyst.service.NostrChannelDataSource
import com.vitorpamplona.amethyst.ui.actions.NewChannelView import com.vitorpamplona.amethyst.ui.actions.NewChannelView
import com.vitorpamplona.amethyst.ui.actions.PostButton import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.dal.ChannelFeedFilter import com.vitorpamplona.amethyst.ui.dal.ChannelFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
@@ -226,8 +226,8 @@ fun ChannelHeader(baseChannel: Channel, account: Account, navController: NavCont
Column() { Column() {
Column(modifier = Modifier.padding(12.dp)) { Column(modifier = Modifier.padding(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = channel.idHex, robot = channel.idHex,
model = ResizeImage(channel.profilePicture(), 35.dp), model = ResizeImage(channel.profilePicture(), 35.dp),
contentDescription = context.getString(R.string.profile_image), contentDescription = context.getString(R.string.profile_image),
modifier = Modifier modifier = Modifier
@@ -52,9 +52,9 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.ui.actions.PostButton import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.components.AsyncUserImageProxy
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.dal.ChatroomFeedFilter import com.vitorpamplona.amethyst.ui.dal.ChatroomFeedFilter
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
@@ -211,12 +211,9 @@ fun ChatroomHeader(baseUser: User, accountViewModel: AccountViewModel, navContro
val authorState by baseUser.live().metadata.observeAsState() val authorState by baseUser.live().metadata.observeAsState()
val author = authorState?.user!! val author = authorState?.user!!
AsyncUserImageProxy( RobohashAsyncImageProxy(
pubkeyHex = author.pubkeyHex, robot = author.pubkeyHex,
model = ResizeImage(author.profilePicture(), 35.dp), model = ResizeImage(author.profilePicture(), 35.dp),
// placeholder = BitmapPainter(RoboHashCache.get(ctx, author.pubkeyHex)),
// fallback = BitmapPainter(RoboHashCache.get(ctx, author.pubkeyHex)),
// error = BitmapPainter(RoboHashCache.get(ctx, author.pubkeyHex)),
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier modifier = Modifier
.width(35.dp) .width(35.dp)
@@ -21,7 +21,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -51,7 +50,6 @@ import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.pagerTabIndicatorOffset import com.google.accompanist.pager.pagerTabIndicatorOffset
import com.google.accompanist.pager.rememberPagerState import com.google.accompanist.pager.rememberPagerState
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
@@ -65,6 +63,8 @@ import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus
import com.vitorpamplona.amethyst.ui.components.InvoiceRequest import com.vitorpamplona.amethyst.ui.components.InvoiceRequest
import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog
import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileFollowersFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileFollowersFeedFilter
@@ -432,13 +432,17 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, navController:
) )
IconButton( IconButton(
modifier = Modifier.size(30.dp).padding(start = 5.dp), modifier = Modifier
.size(30.dp)
.padding(start = 5.dp),
onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); } onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); }
) { ) {
Icon( Icon(
imageVector = Icons.Default.ContentCopy, imageVector = Icons.Default.ContentCopy,
null, null,
modifier = Modifier.padding(end = 5.dp).size(15.dp), modifier = Modifier
.padding(end = 5.dp)
.size(15.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
) )
} }
@@ -580,20 +584,18 @@ fun BadgeThumb(
.height(size) .height(size)
) { ) {
if (image == null) { if (image == null) {
Image( RobohashAsyncImage(
painter = BitmapPainter(RoboHashCache.get(ctx, "ohnothisauthorisnotfound")), robot = "authornotfound",
contentDescription = stringResource(R.string.unknown_author), contentDescription = stringResource(R.string.unknown_author),
modifier = pictureModifier modifier = pictureModifier
.fillMaxSize(1f) .fillMaxSize(1f)
.background(MaterialTheme.colors.background) .background(MaterialTheme.colors.background)
) )
} else { } else {
AsyncImage( RobohashFallbackAsyncImage(
robot = note.idHex,
model = image, model = image,
contentDescription = stringResource(id = R.string.profile_image), contentDescription = stringResource(id = R.string.profile_image),
placeholder = BitmapPainter(RoboHashCache.get(ctx, note.idHex)),
fallback = BitmapPainter(RoboHashCache.get(ctx, note.idHex)),
error = BitmapPainter(RoboHashCache.get(ctx, note.idHex)),
modifier = pictureModifier modifier = pictureModifier
.fillMaxSize(1f) .fillMaxSize(1f)
.clip(shape = CircleShape) .clip(shape = CircleShape)
@@ -36,7 +36,6 @@ import androidx.compose.runtime.setValue
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
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -49,7 +48,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.NavController import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
@@ -245,8 +243,8 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
itemsIndexed(searchResultsChannels.value, key = { _, item -> "c" + item.idHex }) { index, item -> itemsIndexed(searchResultsChannels.value, key = { _, item -> "c" + item.idHex }) { index, item ->
ChannelName( ChannelName(
channelIdHex = item.idHex,
channelPicture = item.profilePicture(), channelPicture = item.profilePicture(),
channelPicturePlaceholder = BitmapPainter(RoboHashCache.get(ctx, item.idHex)),
channelTitle = { channelTitle = {
Text( Text(
"${item.info.name}", "${item.info.name}",