From 1f9308d17fb36c93361f6e4bf228f37f1bfdaed3 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Mon, 28 Feb 2022 18:20:10 +0100
Subject: [PATCH] feat: implement basic theming and message rendering

---
 app/build.gradle.kts                          |   1 +
 .../de/justjanne/quasseldroid/MainActivity.kt |   4 +-
 .../quasseldroid/model/SecurityLevel.kt       |   7 +
 .../sample/SampleSecurityLevelProvider.kt     |  12 ++
 .../ui/components/ConnectedClientCard.kt      |  45 +++---
 .../quasseldroid/ui/components/MessageView.kt | 109 +++++++++++++
 .../ui/components/PasswordTextField.kt        |  29 ++--
 .../quasseldroid/ui/icons/AvatarIcon.kt       |  57 +++++++
 .../quasseldroid/ui/icons/SecurityIcon.kt     |  34 ++++
 .../quasseldroid/ui/routes/HomeRoute.kt       | 149 +-----------------
 .../ui/routes/SampleMessageProvider.kt        |  86 ++++++++++
 .../justjanne/quasseldroid/ui/theme/Color.kt  |  30 ----
 .../justjanne/quasseldroid/ui/theme/Theme.kt  |  92 +++++++----
 .../ui/theme/quassel/ActivityColors.kt        |  24 +++
 .../ui/theme/quassel/ChatColors.kt            |  32 ++++
 .../ui/theme/quassel/MircColors.kt            | 131 +++++++++++++++
 .../ui/theme/quassel/SecurityColors.kt        |  22 +++
 .../ui/theme/quassel/SenderColors.kt          |  35 ++++
 .../quasseldroid/ui/theme/quassel/UiColors.kt |  19 +++
 gradle/libs.versions.toml                     |   1 +
 20 files changed, 674 insertions(+), 245 deletions(-)
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt
 delete mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt
 create mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ea33625d6..5dc226b3e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -68,6 +68,7 @@ dependencies {
   implementation(libs.androidx.compose.compiler)
   implementation(libs.androidx.compose.foundation)
   implementation(libs.androidx.compose.material)
+  implementation(libs.androidx.compose.material.icons)
   implementation(libs.androidx.compose.runtime)
   implementation(libs.androidx.compose.ui)
 
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt
index ecb477957..e3fc55316 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt
@@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material.Surface
 import androidx.compose.ui.Modifier
 import de.justjanne.quasseldroid.service.QuasselBackend
-import de.justjanne.quasseldroid.ui.theme.QuasseldroidTheme
+import de.justjanne.quasseldroid.ui.theme.QuasselTheme
 
 class MainActivity : ComponentActivity() {
   private val backend = QuasselBackend()
@@ -16,7 +16,7 @@ class MainActivity : ComponentActivity() {
     super.onCreate(savedInstanceState)
     backend.onCreate(this)
     setContent {
-      QuasseldroidTheme {
+      QuasselTheme {
         Surface(modifier = Modifier.fillMaxSize()) {
           QuasseldroidRouter(backend = backend)
         }
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt
new file mode 100644
index 000000000..a1c1cb51e
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt
@@ -0,0 +1,7 @@
+package de.justjanne.quasseldroid.model
+
+enum class SecurityLevel {
+  SECURE,
+  UNVERIFIED,
+  INSECURE
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt
new file mode 100644
index 000000000..ef595a103
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt
@@ -0,0 +1,12 @@
+package de.justjanne.quasseldroid.sample
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import de.justjanne.quasseldroid.model.SecurityLevel
+
+class SampleSecurityLevelProvider: PreviewParameterProvider<SecurityLevel> {
+  override val values = sequenceOf(
+    SecurityLevel.SECURE,
+    SecurityLevel.UNVERIFIED,
+    SecurityLevel.INSECURE,
+  )
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt
index 66af09a56..4092eeee0 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt
@@ -6,20 +6,20 @@ import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.material.Card
-import androidx.compose.material.Icon
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.LocalContentAlpha
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import androidx.compose.ui.unit.dp
 import de.charlex.compose.HtmlText
 import de.justjanne.libquassel.protocol.models.ConnectedClient
-import de.justjanne.quasseldroid.R
+import de.justjanne.quasseldroid.model.SecurityLevel
 import de.justjanne.quasseldroid.sample.SampleConnectedClientProvider
-import de.justjanne.quasseldroid.ui.theme.Insecure
-import de.justjanne.quasseldroid.ui.theme.Secure
 import de.justjanne.quasseldroid.ui.theme.Typography
 import org.threeten.bp.ZoneId
 import org.threeten.bp.format.DateTimeFormatter
@@ -34,33 +34,30 @@ fun ConnectedClientCard(
   client: ConnectedClient,
   modifier: Modifier = Modifier
 ) {
-  val secureResource = painterResource(
-    if (client.secure) R.drawable.ic_lock
-    else R.drawable.ic_no_encryption
-  )
-
-  val tint = if (client.secure) Secure else Insecure
-
   Card(modifier = modifier) {
     Row(modifier = Modifier.padding(16.dp)) {
       Column(modifier = Modifier.weight(1.0f)) {
         HtmlText(
           text = client.version,
-          style = Typography.body1
-        )
-        Text(
-          client.remoteAddress,
-          style = Typography.body2
-        )
-        Text(
-          client.connectedSince
-            .atZone(ZoneId.systemDefault())
-            .format(formatter),
-          style = Typography.body2
+          style = Typography.body1,
+          overflow = TextOverflow.Ellipsis
         )
+        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
+          Text(
+            client.remoteAddress,
+            style = Typography.body2,
+            overflow = TextOverflow.Ellipsis
+          )
+          Text(
+            client.connectedSince
+              .atZone(ZoneId.systemDefault())
+              .format(formatter),
+            style = Typography.body2
+          )
+        }
       }
       Spacer(modifier = Modifier.width(16.dp))
-      Icon(secureResource, tint = tint, contentDescription = "")
+      SecurityIcon(level = if (client.secure) SecurityLevel.SECURE else SecurityLevel.INSECURE)
     }
   }
 }
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt
new file mode 100644
index 000000000..c1424a357
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt
@@ -0,0 +1,109 @@
+package de.justjanne.quasseldroid.ui.components
+
+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.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.LocalContentAlpha
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
+import de.justjanne.quasseldroid.ui.icons.AvatarIcon
+import de.justjanne.quasseldroid.ui.routes.SampleMessageProvider
+import de.justjanne.quasseldroid.ui.theme.QuasselTheme
+import de.justjanne.quasseldroid.ui.theme.Typography
+import irc.SenderColorUtil
+import org.threeten.bp.ZoneId
+import org.threeten.bp.format.DateTimeFormatter
+import org.threeten.bp.format.FormatStyle
+
+private val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
+
+@Preview(name = "Message", showBackground = true)
+@Composable
+fun MessageView(
+  @PreviewParameter(SampleMessageProvider::class)
+  message: Message
+) {
+  MessageBaseView(message, false, 32.dp) {
+    Text(
+      message.content,
+      style = Typography.body2,
+    )
+  }
+}
+
+@Composable
+fun MessageBaseView(
+  message: Message,
+  followUp: Boolean,
+  avatarSize: Dp,
+  content: @Composable () -> Unit
+) {
+  val nick = HostmaskHelper.nick(message.sender)
+  val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)]
+
+  Row {
+    if (!followUp) {
+      AvatarIcon(nick, null, modifier = Modifier.padding(2.dp))
+      Spacer(Modifier.width(4.dp))
+    } else {
+      Spacer(Modifier.width(avatarSize + 8.dp))
+    }
+    Column {
+      if (!followUp) {
+        Row {
+          Text(
+            message.senderPrefixes,
+            style = Typography.body2,
+            fontWeight = FontWeight.Bold,
+          )
+          Text(
+            nick,
+            style = Typography.body2,
+            fontWeight = FontWeight.Bold,
+            color = senderColor,
+          )
+          Spacer(Modifier.width(4.dp))
+          CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
+            Text(
+              message.realName,
+              modifier = Modifier.weight(1.0f),
+              style = Typography.body2,
+              overflow = TextOverflow.Ellipsis,
+            )
+          }
+        }
+      }
+      Row {
+        Box(modifier = Modifier.weight(1.0f)) {
+          content()
+        }
+        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
+          Text(
+            message.time
+              .atZone(ZoneId.systemDefault())
+              .format(formatter),
+            style = Typography.body2,
+            fontSize = 12.sp,
+            modifier = Modifier.align(Alignment.Bottom)
+          )
+        }
+      }
+    }
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt
index 978fa20da..fba9e2fdb 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt
@@ -10,6 +10,9 @@ import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
 import androidx.compose.material.TextFieldColors
 import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -21,8 +24,16 @@ import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
 import de.justjanne.quasseldroid.R
 
+@Preview(name = "PasswordTextField", showBackground = true)
+@Composable
+private fun PasswordTextFieldPreview() {
+  val (password, setPassword) = remember { mutableStateOf(TextFieldValue("password")) }
+  PasswordTextField(password, setPassword)
+}
+
 @Composable
 fun PasswordTextField(
   value: TextFieldValue,
@@ -47,10 +58,9 @@ fun PasswordTextField(
   colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
 ) {
   val (showPassword, setShowPassword) = remember { mutableStateOf(false) }
-  val painter = painterResource(
-    if (showPassword) R.drawable.ic_eye_off
-    else R.drawable.ic_eye
-  )
+  val icon =
+    if (showPassword) Icons.Filled.VisibilityOff
+    else Icons.Filled.Visibility
 
   OutlinedTextField(
     value,
@@ -64,7 +74,7 @@ fun PasswordTextField(
     leadingIcon,
     {
       IconButton(onClick = { setShowPassword(!showPassword) }) {
-        Icon(painter = painter, contentDescription = "")
+        Icon(imageVector = icon, contentDescription = "")
       }
       trailingIcon?.invoke()
     },
@@ -105,10 +115,9 @@ fun PasswordTextField(
   colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
 ) {
   val (showPassword, setShowPassword) = remember { mutableStateOf(false) }
-  val painter = painterResource(
-    if (showPassword) R.drawable.ic_eye_off
-    else R.drawable.ic_eye
-  )
+  val icon =
+    if (showPassword) Icons.Filled.VisibilityOff
+    else Icons.Filled.Visibility
 
   OutlinedTextField(
     value,
@@ -122,7 +131,7 @@ fun PasswordTextField(
     leadingIcon,
     {
       IconButton(onClick = { setShowPassword(!showPassword) }) {
-        Icon(painter = painter, contentDescription = "")
+        Icon(imageVector = icon, contentDescription = "")
       }
       trailingIcon?.invoke()
     },
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt
new file mode 100644
index 000000000..e6ac4bd99
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt
@@ -0,0 +1,57 @@
+package de.justjanne.quasseldroid.ui.icons
+
+import android.graphics.Bitmap
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import de.justjanne.quasseldroid.ui.theme.QuasselTheme
+import irc.SenderColorUtil
+import java.util.*
+
+@Preview
+@Composable
+private fun AvatarIconPreview() {
+  AvatarIcon("justJanne", null)
+}
+
+@Composable
+fun AvatarIcon(
+  nick: String,
+  avatar: Bitmap?,
+  modifier: Modifier = Modifier,
+  size: Dp = 32.dp
+) {
+  val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)]
+  val initial = nick.asSequence().map { it.uppercase(Locale.ENGLISH) }.first()
+  val fontSize = with(LocalDensity.current) { (size.toPx() * 0.67f).toSp() }
+
+  Surface(
+    shape = RoundedCornerShape(2.dp),
+    color = senderColor,
+    modifier = modifier.size(size)
+  ) {
+    Box {
+      Text(
+        text = initial,
+        color = MaterialTheme.colors.background.copy(alpha = ContentAlpha.medium),
+        textAlign = TextAlign.Center,
+        fontWeight = FontWeight.Bold,
+        modifier = Modifier.align(Alignment.Center),
+        fontSize = fontSize,
+      )
+    }
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt
new file mode 100644
index 000000000..09d580ae8
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt
@@ -0,0 +1,34 @@
+package de.justjanne.quasseldroid.ui.components
+
+import androidx.compose.material.Icon
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.material.icons.filled.LockOpen
+import androidx.compose.material.icons.filled.NoEncryption
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import de.justjanne.quasseldroid.model.SecurityLevel
+import de.justjanne.quasseldroid.sample.SampleSecurityLevelProvider
+import de.justjanne.quasseldroid.ui.theme.QuasselTheme
+
+@Preview(name = "Security Icon", showBackground = true)
+@Composable
+fun SecurityIcon(
+  @PreviewParameter(SampleSecurityLevelProvider::class)
+  level: SecurityLevel
+) {
+  val vector = when (level) {
+    SecurityLevel.SECURE -> Icons.Filled.Lock
+    SecurityLevel.UNVERIFIED -> Icons.Filled.LockOpen
+    SecurityLevel.INSECURE -> Icons.Filled.NoEncryption
+  }
+
+  val color = when (level) {
+    SecurityLevel.SECURE -> QuasselTheme.security.secure
+    SecurityLevel.UNVERIFIED -> QuasselTheme.security.unverified
+    SecurityLevel.INSECURE -> QuasselTheme.security.insecure
+  }
+
+  Icon(imageVector = vector, contentDescription = level.name, tint = color)
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt
index 436ed14ff..09ee1010f 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt
@@ -1,9 +1,6 @@
 package de.justjanne.quasseldroid.ui.routes
 
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material.Button
@@ -11,39 +8,17 @@ import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 import androidx.navigation.NavController
-import de.justjanne.bitflags.of
-import de.justjanne.libquassel.protocol.models.BufferInfo
 import de.justjanne.libquassel.protocol.models.Message
-import de.justjanne.libquassel.protocol.models.flags.BufferType
-import de.justjanne.libquassel.protocol.models.flags.MessageFlag
-import de.justjanne.libquassel.protocol.models.flags.MessageType
 import de.justjanne.libquassel.protocol.models.ids.BufferId
-import de.justjanne.libquassel.protocol.models.ids.MsgId
-import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.util.flatMap
-import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
 import de.justjanne.quasseldroid.service.QuasselBackend
-import de.justjanne.quasseldroid.ui.theme.SenderColors
-import de.justjanne.quasseldroid.ui.theme.Typography
-import irc.SenderColorUtil
+import de.justjanne.quasseldroid.ui.components.MessageView
 import de.justjanne.quasseldroid.util.mapNullable
 import de.justjanne.quasseldroid.util.rememberFlow
 import de.justjanne.quasseldroid.util.saver.BufferIdSaver
 import kotlinx.coroutines.flow.map
-import org.threeten.bp.Instant
-import org.threeten.bp.ZoneId
-import org.threeten.bp.format.DateTimeFormatter
-import org.threeten.bp.format.FormatStyle
 
 @Composable
 fun HomeRoute(backend: QuasselBackend, navController: NavController) {
@@ -96,125 +71,3 @@ fun HomeRoute(backend: QuasselBackend, navController: NavController) {
 }
 
 
-private val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
-
-@Preview(name = "Message", showBackground = true)
-@Composable
-fun MessageView(
-  @PreviewParameter(SampleMessageProvider::class)
-  message: Message
-) {
-  val nick = HostmaskHelper.nick(message.sender)
-  val senderColor = SenderColors[SenderColorUtil.senderColor(nick)]
-
-  Column {
-    Row {
-      Text(
-        message.senderPrefixes,
-        style = Typography.body2,
-        fontWeight = FontWeight.Bold
-      )
-      Text(
-        nick,
-        style = Typography.body2,
-        fontWeight = FontWeight.Bold,
-        color = senderColor
-      )
-      Spacer(Modifier.width(4.dp))
-      Text(
-        message.realName,
-        modifier = Modifier.weight(1.0f),
-        style = Typography.body2,
-        color = Color(0x8A000000)
-      )
-    }
-    Row {
-      Text(
-        message.content,
-        modifier = Modifier.weight(1.0f),
-        style = Typography.body2
-      )
-      Text(
-        message.time
-          .atZone(ZoneId.systemDefault())
-          .format(formatter),
-        style = Typography.body2,
-        color = Color(0x8A000000),
-        fontSize = 12.sp
-      )
-    }
-  }
-}
-
-class SampleMessageProvider : PreviewParameterProvider<Message> {
-  override val values = sequenceOf(
-    Message(
-      messageId = MsgId(108062924),
-      bufferInfo = BufferInfo(
-        bufferId = BufferId(3746),
-        bufferName = "#quasseldroid",
-        networkId = NetworkId(4),
-        type = BufferType.of(BufferType.Channel)
-      ),
-      time = Instant.parse("2022-02-20T18:24:48.891Z"),
-      type = MessageType.of(MessageType.Quit),
-      sender = "CrazyBonz!~CrazyBonz@user/CrazyBonz",
-      senderPrefixes = "",
-      avatarUrl = "",
-      realName = "CrazyBonz",
-      content = "#quasseldroid",
-      flag = MessageFlag.of()
-    ),
-    Message(
-      messageId = MsgId(108063975),
-      bufferInfo = BufferInfo(
-        bufferId = BufferId(3746),
-        bufferName = "#quasseldroid",
-        networkId = NetworkId(4),
-        type = BufferType.of(BufferType.Channel)
-      ),
-      time = Instant.parse("2022-02-20T19:56:01.588Z"),
-      type = MessageType.of(MessageType.Plain),
-      sender = "winch!~AdminUser@185.14.29.13",
-      senderPrefixes = "",
-      avatarUrl = "",
-      realName = "Wincher,,,",
-      content = "Can i script some actions like in mIRC?",
-      flag = MessageFlag.of()
-    ),
-    Message(
-      messageId = MsgId(108064014),
-      bufferInfo = BufferInfo(
-        bufferId = BufferId(3746),
-        bufferName = "#quasseldroid",
-        networkId = NetworkId(4),
-        type = BufferType.of(BufferType.Channel)
-      ),
-      time = Instant.parse("2022-02-20T20:06:39.159Z"),
-      type = MessageType.of(MessageType.Quit),
-      sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net",
-      senderPrefixes = "",
-      avatarUrl = "",
-      realName = "mavhc",
-      content = "Quit: http://quassel-irc.org - Chat comfortably. Anywhere.",
-      flag = MessageFlag.of()
-    ),
-    Message(
-      messageId = MsgId(108064022),
-      bufferInfo = BufferInfo(
-        bufferId = BufferId(3746),
-        bufferName = "#quasseldroid",
-        networkId = NetworkId(4),
-        type = BufferType.of(BufferType.Channel)
-      ),
-      time = Instant.parse("2022-02-20T20:07:13.45Z"),
-      type = MessageType.of(MessageType.Join),
-      sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net",
-      senderPrefixes = "",
-      avatarUrl = "",
-      realName = "mavhc",
-      content = "#quasseldroid",
-      flag = MessageFlag.of()
-    )
-  )
-}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt
new file mode 100644
index 000000000..ff30e9f8f
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt
@@ -0,0 +1,86 @@
+package de.justjanne.quasseldroid.ui.routes
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import de.justjanne.bitflags.of
+import de.justjanne.libquassel.protocol.models.BufferInfo
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.flags.BufferType
+import de.justjanne.libquassel.protocol.models.flags.MessageFlag
+import de.justjanne.libquassel.protocol.models.flags.MessageType
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import org.threeten.bp.Instant
+
+class SampleMessageProvider : PreviewParameterProvider<Message> {
+  override val values = sequenceOf(
+      Message(
+          messageId = MsgId(108062924),
+          bufferInfo = BufferInfo(
+              bufferId = BufferId(3746),
+              bufferName = "#quasseldroid",
+              networkId = NetworkId(4),
+              type = BufferType.of(BufferType.Channel)
+          ),
+          time = Instant.parse("2022-02-20T18:24:48.891Z"),
+          type = MessageType.of(MessageType.Quit),
+          sender = "CrazyBonz!~CrazyBonz@user/CrazyBonz",
+          senderPrefixes = "",
+          avatarUrl = "",
+          realName = "CrazyBonz",
+          content = "#quasseldroid",
+          flag = MessageFlag.of()
+      ),
+      Message(
+          messageId = MsgId(108063975),
+          bufferInfo = BufferInfo(
+              bufferId = BufferId(3746),
+              bufferName = "#quasseldroid",
+              networkId = NetworkId(4),
+              type = BufferType.of(BufferType.Channel)
+          ),
+          time = Instant.parse("2022-02-20T19:56:01.588Z"),
+          type = MessageType.of(MessageType.Plain),
+          sender = "winch!~AdminUser@185.14.29.13",
+          senderPrefixes = "",
+          avatarUrl = "",
+          realName = "Wincher,,,",
+          content = "Can i script some actions like in mIRC?",
+          flag = MessageFlag.of()
+      ),
+      Message(
+          messageId = MsgId(108064014),
+          bufferInfo = BufferInfo(
+              bufferId = BufferId(3746),
+              bufferName = "#quasseldroid",
+              networkId = NetworkId(4),
+              type = BufferType.of(BufferType.Channel)
+          ),
+          time = Instant.parse("2022-02-20T20:06:39.159Z"),
+          type = MessageType.of(MessageType.Quit),
+          sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net",
+          senderPrefixes = "",
+          avatarUrl = "",
+          realName = "mavhc",
+          content = "Quit: http://quassel-irc.org - Chat comfortably. Anywhere.",
+          flag = MessageFlag.of()
+      ),
+      Message(
+          messageId = MsgId(108064022),
+          bufferInfo = BufferInfo(
+              bufferId = BufferId(3746),
+              bufferName = "#quasseldroid",
+              networkId = NetworkId(4),
+              type = BufferType.of(BufferType.Channel)
+          ),
+          time = Instant.parse("2022-02-20T20:07:13.45Z"),
+          type = MessageType.of(MessageType.Join),
+          sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net",
+          senderPrefixes = "",
+          avatarUrl = "",
+          realName = "mavhc",
+          content = "#quasseldroid",
+          flag = MessageFlag.of()
+      )
+  )
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt
deleted file mode 100644
index 1460baf88..000000000
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.justjanne.quasseldroid.ui.theme
-
-import androidx.compose.ui.graphics.Color
-
-val Primary = Color(0xFF0a70c0)
-val PrimaryDark = Color(0xFF105a94)
-val Accent = Color(0xFFffaf3b)
-
-val Secure = Color(0xFF4CAF50)
-val PartiallySecure = Color(0xFFFFC107)
-val Insecure = Color(0xFFD32F2F)
-
-val SenderColors = listOf(
-  Color(0xFF_b80a73),
-  Color(0xFF_814dd5),
-  Color(0xFF_9f0b0b),
-  Color(0xFF_139f2f),
-  Color(0xFF_4e9c9f),
-  Color(0xFF_8b4a9f),
-  Color(0xFF_9f8669),
-  Color(0xFF_2b6b9f),
-  Color(0xFF_e00d8f),
-  Color(0xFF_995bfd),
-  Color(0xFF_c70e0e),
-  Color(0xFF_18c73e),
-  Color(0xFF_61c6c7),
-  Color(0xFF_af5ec7),
-  Color(0xFF_c7a782),
-  Color(0xFF_3684c7),
-)
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt
index b12c3825b..4b29b45f7 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt
@@ -1,44 +1,74 @@
 package de.justjanne.quasseldroid.ui.theme
 
 import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.Colors
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.darkColors
 import androidx.compose.material.lightColors
 import androidx.compose.runtime.Composable
-
-private val DarkColorPalette = darkColors(
-  primary = Primary,
-  primaryVariant = PrimaryDark,
-  secondary = Accent
-)
-
-private val LightColorPalette = lightColors(
-  primary = Primary,
-  primaryVariant = PrimaryDark,
-  secondary = Accent
-
-  /* Other default colors to override
-  background = Color.White,
-  surface = Color.White,
-  onPrimary = Color.White,
-  onSecondary = Color.Black,
-  onBackground = Color.Black,
-  onSurface = Color.Black,
-  */
-)
+import androidx.compose.runtime.CompositionLocalProvider
+import de.justjanne.quasseldroid.ui.theme.quassel.ActivityColors
+import de.justjanne.quasseldroid.ui.theme.quassel.ChatColors
+import de.justjanne.quasseldroid.ui.theme.quassel.LocalActivityColors
+import de.justjanne.quasseldroid.ui.theme.quassel.LocalChatColors
+import de.justjanne.quasseldroid.ui.theme.quassel.LocalMircColors
+import de.justjanne.quasseldroid.ui.theme.quassel.LocalSecurityColors
+import de.justjanne.quasseldroid.ui.theme.quassel.LocalSenderColors
+import de.justjanne.quasseldroid.ui.theme.quassel.MircColors
+import de.justjanne.quasseldroid.ui.theme.quassel.SecurityColors
+import de.justjanne.quasseldroid.ui.theme.quassel.SenderColors
+import de.justjanne.quasseldroid.ui.theme.quassel.UiColors
 
 @Composable
-fun QuasseldroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
-  val colors = if (darkTheme) {
-    DarkColorPalette
-  } else {
-    LightColorPalette
-  }
-
+fun QuasselTheme(
+  dark: Boolean = isSystemInDarkTheme(),
+  ui: UiColors = UiColors.Default,
+  chat: ChatColors = ChatColors.Default,
+  activity: ActivityColors = ActivityColors.Default,
+  security: SecurityColors = SecurityColors.Default,
+  sender: SenderColors = SenderColors.Default,
+  mirc: MircColors = MircColors.Default,
+  content: @Composable () -> Unit
+) {
   MaterialTheme(
-    colors = colors,
+    colors = buildColors(dark, ui),
     typography = Typography,
-    shapes = Shapes,
-    content = content
+    shapes = Shapes
+  ) {
+    CompositionLocalProvider(
+      LocalChatColors provides chat,
+      LocalActivityColors provides activity,
+      LocalSecurityColors provides security,
+      LocalSenderColors provides sender,
+      LocalMircColors provides mirc,
+      content = content
+    )
+  }
+}
+
+private fun buildColors(dark: Boolean, ui: UiColors): Colors = if (dark) {
+  darkColors(
+    primary = ui.primary,
+    primaryVariant = ui.primaryVariant,
+    secondary = ui.secondary,
+  )
+} else {
+  lightColors(
+    primary = ui.primary,
+    primaryVariant = ui.primaryVariant,
+    secondary = ui.secondary,
   )
 }
+
+object QuasselTheme {
+  val chat: ChatColors
+    @Composable get() = LocalChatColors.current
+  val activity: ActivityColors
+    @Composable get() = LocalActivityColors.current
+  val security: SecurityColors
+    @Composable get() = LocalSecurityColors.current
+  val sender: SenderColors
+    @Composable get() = LocalSenderColors.current
+  val mirc: MircColors
+    @Composable get() = LocalMircColors.current
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt
new file mode 100644
index 000000000..ae7de444e
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt
@@ -0,0 +1,24 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+val LocalActivityColors = compositionLocalOf { ActivityColors.Default }
+
+@Immutable
+data class ActivityColors(
+  val activity: Color,
+  val message: Color,
+  val highlight: Color,
+  val notification: Color,
+) {
+  companion object {
+    val Default = ActivityColors(
+      activity = Color(0xffafb42b),
+      message = Color(0xff1976d2),
+      highlight = Color(0xffffab00),
+      notification = Color(0xffd32f2f),
+    )
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt
new file mode 100644
index 000000000..9f17ac7e8
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt
@@ -0,0 +1,32 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+val LocalChatColors = compositionLocalOf { ChatColors.Default }
+
+@Immutable
+data class ChatColors(
+  val action: Color,
+  val onAction: Color,
+  val notice: Color,
+  val onNotice: Color,
+  val highlight: Color,
+  val onHighlight: Color,
+  val error: Color,
+  val onError: Color,
+) {
+  companion object {
+    val Default = ChatColors(
+      action = Color(0x00000000),
+      onAction = Color(0xff01579b),
+      notice = Color(0x00000000),
+      onNotice = Color(0xffb56a00),
+      highlight = Color(0x40ffaf3b),
+      onHighlight = Color(0xde000000),
+      error = Color(0x00000000),
+      onError = Color(0xffb71c1c),
+    )
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt
new file mode 100644
index 000000000..0ac2933c0
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt
@@ -0,0 +1,131 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+val LocalMircColors = compositionLocalOf { MircColors.Default }
+
+@Immutable
+data class MircColors(
+  val colors: List<Color>,
+) {
+  companion object {
+    val Default = MircColors(
+      colors = listOf(
+        Color(0xffffffff),
+        Color(0xff000000),
+        Color(0xff000080),
+        Color(0xff008000),
+        Color(0xffff0000),
+        Color(0xff800000),
+        Color(0xff800080),
+        Color(0xffffa500),
+        Color(0xffffff00),
+        Color(0xff00ff00),
+        Color(0xff008080),
+        Color(0xff00ffff),
+        Color(0xff4169e1),
+        Color(0xffff00ff),
+        Color(0xff808080),
+        Color(0xffc0c0c0),
+
+        Color(0xff470000),
+        Color(0xff740000),
+        Color(0xffb50000),
+        Color(0xffff0000),
+        Color(0xffff5959),
+        Color(0xffff9c9c),
+
+        Color(0xff472100),
+        Color(0xff743a00),
+        Color(0xffb56300),
+        Color(0xffff8c00),
+        Color(0xffffb459),
+        Color(0xffffd39c),
+
+        Color(0xff474700),
+        Color(0xff747400),
+        Color(0xffb5b500),
+        Color(0xffffff00),
+        Color(0xffffff71),
+        Color(0xffffff9c),
+
+        Color(0xff324700),
+        Color(0xff517400),
+        Color(0xff7db500),
+        Color(0xffb2ff00),
+        Color(0xffcfff60),
+        Color(0xffe2ff9c),
+
+        Color(0xff004700),
+        Color(0xff007400),
+        Color(0xff00b500),
+        Color(0xff00ff00),
+        Color(0xff6fff6f),
+        Color(0xff9cff9c),
+
+        Color(0xff00472c),
+        Color(0xff007449),
+        Color(0xff00b571),
+        Color(0xff00ffa0),
+        Color(0xff65ffc9),
+        Color(0xff9cffdb),
+
+        Color(0xff004747),
+        Color(0xff007474),
+        Color(0xff00b5b5),
+        Color(0xff00ffff),
+        Color(0xff6dffff),
+        Color(0xff9cffff),
+
+        Color(0xff002747),
+        Color(0xff004074),
+        Color(0xff0063b5),
+        Color(0xff008cff),
+        Color(0xff59b4ff),
+        Color(0xff9cd3ff),
+
+        Color(0xff000047),
+        Color(0xff000074),
+        Color(0xff0000b5),
+        Color(0xff0000ff),
+        Color(0xff5959ff),
+        Color(0xff9c9cff),
+
+        Color(0xff2e0047),
+        Color(0xff4b0074),
+        Color(0xff7500b5),
+        Color(0xffa500ff),
+        Color(0xffc459ff),
+        Color(0xffdc9cff),
+
+        Color(0xff470047),
+        Color(0xff740074),
+        Color(0xffb500b5),
+        Color(0xffff00ff),
+        Color(0xffff66ff),
+        Color(0xffff9cff),
+
+        Color(0xff47002a),
+        Color(0xff740045),
+        Color(0xffb5006b),
+        Color(0xffff0098),
+        Color(0xffff59bc),
+        Color(0xffff94d3),
+
+        Color(0xff000000),
+        Color(0xff131313),
+        Color(0xff282828),
+        Color(0xff363636),
+        Color(0xff4d4d4d),
+        Color(0xff656565),
+        Color(0xff818181),
+        Color(0xff9f9f9f),
+        Color(0xffbcbcbc),
+        Color(0xffe2e2e2),
+        Color(0xffffffff),
+      )
+    )
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt
new file mode 100644
index 000000000..dfe6370ff
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt
@@ -0,0 +1,22 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+val LocalSecurityColors = compositionLocalOf { SecurityColors.Default }
+
+@Immutable
+data class SecurityColors(
+  val secure: Color,
+  val unverified: Color,
+  val insecure: Color,
+) {
+  companion object {
+    val Default = SecurityColors(
+      secure = Color(0xff4caf50),
+      unverified = Color(0xffffc107),
+      insecure = Color(0xffd32f2f),
+    )
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt
new file mode 100644
index 000000000..6e957b12b
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt
@@ -0,0 +1,35 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+val LocalSenderColors = compositionLocalOf { SenderColors.Default }
+
+@Immutable
+data class SenderColors(
+  val colors: List<Color>,
+) {
+  companion object {
+    val Default = SenderColors(
+      colors = listOf(
+        Color(0xfff44336),
+        Color(0xff2196f3),
+        Color(0xff7cb342),
+        Color(0xff7b1fa2),
+        Color(0xffda8e00),
+        Color(0xff4caf50),
+        Color(0xff3f51b5),
+        Color(0xffe91e63),
+        Color(0xffb94600),
+        Color(0xff9e9d24),
+        Color(0xff558b2f),
+        Color(0xff009688),
+        Color(0xff0277bd),
+        Color(0xff00838f),
+        Color(0xff9c27b0),
+        Color(0xffc51162),
+      )
+    )
+  }
+}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt
new file mode 100644
index 000000000..beebef92f
--- /dev/null
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt
@@ -0,0 +1,19 @@
+package de.justjanne.quasseldroid.ui.theme.quassel
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.graphics.Color
+
+@Immutable
+data class UiColors(
+  val primary: Color,
+  val primaryVariant: Color,
+  val secondary: Color,
+) {
+  companion object {
+    val Default = UiColors(
+      primary = Color(0xff0a70c0),
+      primaryVariant = Color(0xff105a94),
+      secondary = Color(0xffffaf3b),
+    )
+  }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index bdf9f59c7..4258909f6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -19,6 +19,7 @@ androidx-compose-animation = { module = "androidx.compose.animation:animation",
 androidx-compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "androidx-compose" }
 androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidx-compose" }
 androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose" }
+androidx-compose-material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose" }
 androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "androidx-compose" }
 androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose" }
 androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "androidx-compose" }
-- 
GitLab