diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index b67486ec..fb93d254 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -3,15 +3,19 @@
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 2450edf5..9a9a96ce 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -277,6 +277,9 @@ dependencies {
// Firebase - Cloud Messaging
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
implementation("com.google.firebase:firebase-messaging")
+
+ // Shimmer - loading animations
+ implementation "com.valentinilk.shimmer:compose-shimmer:1.3.1"
}
sqldelight {
diff --git a/app/src/main/java/chat/revolt/components/skeletons/MessageSkeleton.kt b/app/src/main/java/chat/revolt/components/skeletons/MessageSkeleton.kt
new file mode 100644
index 00000000..c856b04c
--- /dev/null
+++ b/app/src/main/java/chat/revolt/components/skeletons/MessageSkeleton.kt
@@ -0,0 +1,84 @@
+package chat.revolt.components.skeletons
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+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.unit.dp
+import chat.revolt.api.settings.GlobalState
+
+enum class MessageSkeletonVariant {
+ One,
+ Two,
+ Three
+}
+
+@Composable
+fun MessageSkeleton(variant: MessageSkeletonVariant, modifier: Modifier = Modifier) {
+ Row(
+ modifier = Modifier
+ .padding(horizontal = 10.dp)
+ .fillMaxWidth()
+ ) {
+ Column {
+ Spacer(modifier = Modifier.height(4.dp))
+ Box(
+ Modifier
+ .clip(RoundedCornerShape(GlobalState.avatarRadius))
+ .size(40.dp)
+ .background(skeletonColourOnBackground())
+ )
+ }
+ Column(modifier = Modifier.padding(start = 10.dp, top = 4.dp)) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Box(
+ Modifier
+ .width(
+ when (variant) {
+ // Chaotic widths. Boo!
+ MessageSkeletonVariant.One -> 66.dp
+ MessageSkeletonVariant.Two -> 29.dp
+ MessageSkeletonVariant.Three -> 97.dp
+ }
+ )
+ .height(16.dp)
+ .background(skeletonColourOnBackground())
+ )
+
+ Spacer(modifier = Modifier.width(5.dp))
+
+ Box(
+ Modifier
+ .width(42.dp)
+ .height(13.dp)
+ .background(skeletonColourOnBackground())
+ )
+ }
+
+ Spacer(modifier = Modifier.height(4.dp))
+ Box(
+ Modifier
+ .fillMaxWidth(
+ 1 / when (variant) {
+ MessageSkeletonVariant.One -> 1.5f
+ MessageSkeletonVariant.Two -> 1.2f
+ MessageSkeletonVariant.Three -> 1.8f
+ }
+ )
+ .height(16.dp)
+ .background(skeletonColourOnBackground())
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/chat/revolt/components/skeletons/SkeletonGlobals.kt b/app/src/main/java/chat/revolt/components/skeletons/SkeletonGlobals.kt
new file mode 100644
index 00000000..dee83238
--- /dev/null
+++ b/app/src/main/java/chat/revolt/components/skeletons/SkeletonGlobals.kt
@@ -0,0 +1,7 @@
+package chat.revolt.components.skeletons
+
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.runtime.Composable
+
+@Composable
+fun skeletonColourOnBackground() = LocalContentColor.current.copy(alpha = 0.4f)
\ No newline at end of file
diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
index f1b5c0f1..7d2a0643 100644
--- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
+++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
@@ -120,12 +120,17 @@ import chat.revolt.components.screens.chat.AttachmentManager
import chat.revolt.components.screens.chat.ChannelIcon
import chat.revolt.components.screens.chat.ReplyManager
import chat.revolt.components.screens.chat.TypingIndicator
+import chat.revolt.components.skeletons.MessageSkeleton
+import chat.revolt.components.skeletons.MessageSkeletonVariant
import chat.revolt.internals.extensions.BottomSheetInsets
import chat.revolt.internals.extensions.rememberChannelPermissions
import chat.revolt.internals.extensions.zero
import chat.revolt.sheets.ChannelInfoSheet
import chat.revolt.sheets.MessageContextSheet
import chat.revolt.sheets.ReactSheet
+import com.valentinilk.shimmer.ShimmerBounds
+import com.valentinilk.shimmer.rememberShimmer
+import com.valentinilk.shimmer.shimmer
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlinx.datetime.Instant
@@ -699,14 +704,16 @@ fun ChannelScreen(
}
is ChannelScreenItem.Loading -> {
- Box(
- modifier = Modifier.fillMaxWidth(),
- contentAlignment = Alignment.Center
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .shimmer(rememberShimmer(ShimmerBounds.Window)),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
) {
- CircularProgressIndicator(
- modifier = Modifier
- .padding(16.dp)
- )
+ MessageSkeleton(MessageSkeletonVariant.One)
+ MessageSkeleton(MessageSkeletonVariant.Two)
+ MessageSkeleton(MessageSkeletonVariant.Three)
}
}
}