diff --git a/app/src/main/java/chat/stoat/composables/generic/UserAvatar.kt b/app/src/main/java/chat/stoat/composables/generic/UserAvatar.kt
index 6d44f06b..12cca3a7 100644
--- a/app/src/main/java/chat/stoat/composables/generic/UserAvatar.kt
+++ b/app/src/main/java/chat/stoat/composables/generic/UserAvatar.kt
@@ -2,31 +2,37 @@ package chat.stoat.composables.generic
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
-import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
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.Icon
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.draw.clip
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import chat.stoat.R
-import chat.stoat.core.model.schemas.AutumnResource
import chat.stoat.api.settings.LoadedSettings
import chat.stoat.core.model.data.STOAT_BASE
import chat.stoat.core.model.data.STOAT_FILES
+import chat.stoat.core.model.schemas.AutumnResource
enum class Presence {
Online,
@@ -68,17 +74,61 @@ fun presenceColour(presence: Presence): Color {
}
}
+private fun Presence.iconResource(): Int = when (this) {
+ Presence.Online -> R.drawable.ic_circle_24dp__fill
+ Presence.Idle -> R.drawable.ic_bedtime_24dp
+ Presence.Dnd -> R.drawable.ic_do_not_disturb_on_24dp
+ Presence.Focus -> R.drawable.ic_circle_circle_24dp
+ Presence.Offline -> R.drawable.ic_circle_24dp
+}
+
+private fun Presence.descriptionResource(): Int = when (this) {
+ Presence.Online -> R.string.status_online
+ Presence.Idle -> R.string.status_idle
+ Presence.Dnd -> R.string.status_dnd
+ Presence.Focus -> R.string.status_focus
+ Presence.Offline -> R.string.status_offline
+}
+
@Composable
fun PresenceBadge(presence: Presence, size: Dp = 16.dp) {
Box(
- modifier = Modifier
- .size(size)
- .clip(CircleShape)
- .border(2.dp, MaterialTheme.colorScheme.background, CircleShape)
- .background(presenceColour(presence))
- )
+ contentAlignment = Alignment.Center,
+ modifier = Modifier.size(size)
+ ) {
+ Icon(
+ painter = painterResource(presence.iconResource()),
+ contentDescription = stringResource(presence.descriptionResource()),
+ tint = presenceColour(presence),
+ modifier = Modifier
+ .size((size - PresenceCutoutPadding * 2).coerceAtLeast(0.dp))
+ )
+ }
}
+private val PresenceCutoutPadding = 2.dp
+
+private fun Modifier.presenceCutout(diameter: Dp): Modifier =
+ graphicsLayer {
+ compositingStrategy = CompositingStrategy.Offscreen
+ }.drawWithCache {
+ val radius = diameter.toPx() / 2
+ val center = Offset(
+ x = if (layoutDirection == LayoutDirection.Ltr) size.width - radius else radius,
+ y = size.height - radius
+ )
+
+ onDrawWithContent {
+ drawContent()
+ drawCircle(
+ color = Color.Transparent,
+ radius = radius,
+ center = center,
+ blendMode = BlendMode.Clear
+ )
+ }
+ }
+
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun UserAvatar(
@@ -109,6 +159,13 @@ fun UserAvatar(
modifier = Modifier
.clip(shape)
.size(size)
+ .then(
+ if (presence != null) {
+ Modifier.presenceCutout(presenceSize)
+ } else {
+ Modifier
+ }
+ )
.then(
if (onLongClick != null || onClick != null) {
Modifier
@@ -129,6 +186,13 @@ fun UserAvatar(
modifier = Modifier
.clip(shape)
.size(size)
+ .then(
+ if (presence != null) {
+ Modifier.presenceCutout(presenceSize)
+ } else {
+ Modifier
+ }
+ )
.then(
if (onLongClick != null || onClick != null) {
Modifier
diff --git a/app/src/main/res/drawable/ic_adjust_24dp.xml b/app/src/main/res/drawable/ic_adjust_24dp.xml
new file mode 100644
index 00000000..d34d6a5e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_adjust_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_bedtime_24dp.xml b/app/src/main/res/drawable/ic_bedtime_24dp.xml
new file mode 100644
index 00000000..155abb46
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bedtime_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_circle_24dp.xml b/app/src/main/res/drawable/ic_circle_24dp.xml
new file mode 100644
index 00000000..29228525
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_circle_24dp__fill.xml b/app/src/main/res/drawable/ic_circle_24dp__fill.xml
new file mode 100644
index 00000000..d7e8c25b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle_24dp__fill.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_circle_circle_24dp.xml b/app/src/main/res/drawable/ic_circle_circle_24dp.xml
new file mode 100644
index 00000000..67e81edd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle_circle_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_do_not_disturb_on_24dp.xml b/app/src/main/res/drawable/ic_do_not_disturb_on_24dp.xml
new file mode 100644
index 00000000..3d31aa17
--- /dev/null
+++ b/app/src/main/res/drawable/ic_do_not_disturb_on_24dp.xml
@@ -0,0 +1,10 @@
+
+
+