From 6122c1842792e2a5612eef2260fbf43bc2ade5a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20K=C3=BCrten?= <sebastian@mobanisto.de>
Date: Thu, 9 Sep 2021 22:46:49 +0200
Subject: [PATCH] Add UI stubs from Paul's prototype

---
 build.gradle.kts                              |   1 +
 .../briar/desktop/BriarService.kt             |   8 +-
 .../briar/desktop/paul/data/Contacts.kt       | 229 +++++++++++++
 .../briar/desktop/paul/data/ContentType.kt    |  16 +
 .../briar/desktop/paul/data/OptionType.kt     |  14 +
 .../briar/desktop/paul/model/Contact.kt       |   9 +
 .../briar/desktop/paul/model/ContentType.kt   |   7 +
 .../briar/desktop/paul/model/Message.kt       |   9 +
 .../briar/desktop/paul/model/OptionType.kt    |   9 +
 .../briar/desktop/paul/theme/colors.kt        |  32 ++
 .../briar/desktop/paul/views/BriarSidebar.kt  | 103 ++++++
 .../desktop/paul/views/BriarUIStateManager.kt |  51 +++
 .../desktop/paul/views/PrivateMessageView.kt  | 319 ++++++++++++++++++
 .../resources/images/profile_images/p0.png    | Bin 0 -> 45426 bytes
 .../resources/images/profile_images/p1.png    | Bin 0 -> 2804 bytes
 .../resources/images/profile_images/p2.png    | Bin 0 -> 45426 bytes
 .../resources/images/profile_images/p3.png    | Bin 0 -> 45426 bytes
 .../resources/images/profile_images/p4.png    | Bin 0 -> 45426 bytes
 .../resources/images/profile_images/p5.png    | Bin 0 -> 45426 bytes
 19 files changed, 800 insertions(+), 7 deletions(-)
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/data/Contacts.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/data/ContentType.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/data/OptionType.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/model/Contact.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/model/ContentType.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/model/Message.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/model/OptionType.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
 create mode 100644 src/main/resources/images/profile_images/p0.png
 create mode 100644 src/main/resources/images/profile_images/p1.png
 create mode 100644 src/main/resources/images/profile_images/p2.png
 create mode 100644 src/main/resources/images/profile_images/p3.png
 create mode 100644 src/main/resources/images/profile_images/p4.png
 create mode 100644 src/main/resources/images/profile_images/p5.png

diff --git a/build.gradle.kts b/build.gradle.kts
index 66f5b38406..af4aeb877c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -24,6 +24,7 @@ dependencies {
 
     implementation("com.fasterxml.jackson.core:jackson-databind:2.10.0")
     implementation("com.github.ajalt:clikt:2.2.0")
+    implementation("org.jetbrains.compose.material:material-icons-extended:0.4.0")
 
     implementation(project(path = ":briar:briar-core", configuration = "default"))
     implementation(project(path = ":briar:bramble-java", configuration = "default"))
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
index ad43f0bb61..18a11ca523 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
@@ -66,13 +66,7 @@ constructor(
                     })
 
                 is Screen.Main ->
-                    Column(
-                        modifier = Modifier.padding(16.dp).fillMaxSize(),
-                        verticalArrangement = Arrangement.Center,
-                        horizontalAlignment = Alignment.CenterHorizontally
-                    ) {
-                        Text("Welcome to Briar")
-                    }
+                    briarUIStateManager()
             }
         }
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/data/Contacts.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/Contacts.kt
new file mode 100644
index 0000000000..2b7b47c203
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/Contacts.kt
@@ -0,0 +1,229 @@
+package org.briarproject.briar.desktop.paul.data
+
+import org.briarproject.briar.desktop.paul.model.Contact
+import org.briarproject.briar.desktop.paul.model.Message
+
+object ContactList {
+    val msg1: List<Message> = listOf<Message>(
+        Message(from = "Alice", message = "hello!", time = "2 days ago", delivered = true),
+        Message(from = null, message = "yes I hear you", time = "1 day ago", delivered = true),
+        Message(
+            from = null,
+            message = "I am messaging you through this fake Briar Desktop app",
+            time = "18 hrs. ago",
+            delivered = true
+        ),
+        Message(from = "Alice", message = "Ah I see, very neat", time = "12 min. ago", delivered = true),
+        Message(from = null, message = "Loren Ipsum", time = "2 min. ago", delivered = false)
+    )
+    val msg2: List<Message> = listOf<Message>(
+        Message(
+            from = null,
+            message = "The air bites shrewdly; it is very cold.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(from = "Bob", message = "It is a nipping and an eager air.", time = "1 day ago", delivered = true),
+        Message(from = null, message = "What hour now?", time = "18 hrs. ago", delivered = true),
+        Message(from = "Bob", message = "I think it lacks of twelve.", time = "12 min. ago", delivered = true),
+        Message(from = null, message = "No, it is struck.", time = "2 min. ago", delivered = false),
+        Message(
+            from = "Bob",
+            message = "Indeed? I heard it not: then it draws near the season Wherein the spirit held his wont to walk. A flourish of trumpets, and ordnance shot off, within. What does this mean, my lord?",
+            time = "2 min. ago",
+            delivered = false
+        ),
+        Message(
+            from = null,
+            message = "The air bites shrewdly; it is very cold.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(from = "Bob", message = "It is a nipping and an eager air.", time = "1 day ago", delivered = true),
+        Message(from = null, message = "What hour now?", time = "18 hrs. ago", delivered = true),
+        Message(from = "Bob", message = "I think it lacks of twelve.", time = "12 min. ago", delivered = true),
+        Message(from = null, message = "No, it is struck.", time = "2 min. ago", delivered = false),
+        Message(
+            from = null,
+            message = "The air bites shrewdly; it is very cold.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(
+            from = null,
+            message = "The air bites shrewdly; it is very cold.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(from = "Bob", message = "It is a nipping and an eager air.", time = "1 day ago", delivered = true),
+        Message(from = null, message = "What hour now?", time = "18 hrs. ago", delivered = true),
+        Message(from = "Bob", message = "I think it lacks of twelve.", time = "12 min. ago", delivered = true),
+        Message(from = null, message = "No, it is struck.", time = "2 min. ago", delivered = false),
+        Message(from = "Bob", message = "It is a nipping and an eager air.", time = "1 day ago", delivered = true),
+        Message(from = null, message = "What hour now?", time = "18 hrs. ago", delivered = true),
+        Message(from = "Bob", message = "I think it lacks of twelve.", time = "12 min. ago", delivered = true),
+        Message(from = null, message = "No, it is struck.", time = "2 min. ago", delivered = false),
+        Message(
+            from = "Bob",
+            message = "Indeed? I heard it not: then it draws near the season Wherein the spirit held his wont to walk. A flourish of trumpets, and ordnance shot off, within. What does this mean, my lord?",
+            time = "2 min. ago",
+            delivered = false
+        )
+    )
+    val msg3: List<Message> = listOf<Message>(
+        Message(
+            from = null,
+            message = "Give him this money and these notes, Reynaldo.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(from = "Bob", message = "I will, my lord.", time = "1 day ago", delivered = true),
+        Message(
+            from = null,
+            message = "You shall do marvellous wisely, good Reynaldo,Before you visit him, to make inquireOf his behavior.",
+            time = "18 hrs. ago",
+            delivered = true
+        ),
+        Message(from = "Bob", message = "My lord, I did intend it.", time = "12 min. ago", delivered = true),
+        Message(
+            from = null,
+            message = "Marry, well said; very well said. Look you, sir,Inquire me first what Danskers are in Paris;And how, and who, what means, and where they keep,What company, at what expense; and findingBy this encompassment and drift of questionThat they do know my son, come you more nearerThan your particular demands will touch it:Take you, as ’twere, some distant knowledge of him;As thus, ‘I know his father and his friends,And in part him: ‘ do you mark this, Reynaldo?",
+            time = "2 min. ago",
+            delivered = false
+        ),
+    )
+    val msg4: List<Message> = listOf<Message>(
+        Message(
+            from = "Bob",
+            message = "So art thou to revenge, when thou shalt hear.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(from = null, message = "What?", time = "1 day ago", delivered = true),
+        Message(
+            from = "Bob",
+            message = "I am your father’s spirit, doomed for a certain time to walk the night, and for the day to burn in fires, till the foul crimes done during my lifetime have been burnt and purged away. But that I am forbidden to tell the secrets of my prison-house I could tell a tale whose lightest word would shrivel up your soul, freeze your young blood, make your eyes start from their sockets and your hair stand up on end like the quills of a frightened porcupine. But this eternal torture is not for ears of flesh and blood. Listen, oh listen! If you ever loved your dear father ….",
+            time = "18 hrs. ago",
+            delivered = true
+        ),
+        Message(from = null, message = "Oh God!", time = "12 min. ago", delivered = true),
+    )
+    val msg5: List<Message> = listOf<Message>(
+        Message(
+            from = null,
+            message = "Here's a knocking indeed! If a man were porter of hell-gate, he should have old turning the key.  Knocking within Knock, knock, knock! Who's there, i' the name of Beelzebub? Here's a farmer, that hanged himself on the expectation of plenty: come in time; have napkins enow about you; here you'll sweat for't.",
+            time = "2 days ago",
+            delivered = true
+        ),
+        Message(
+            from = null,
+            message = "Knock, knock! Who's there, in the other devil's name? Faith, here's an equivocator, that could swear in both the scales against either scale; who committed treason enough for God's sake, yet could not equivocate to heaven: O, come in, equivocator.",
+            time = "1 day ago",
+            delivered = true
+        ),
+    )
+    val contacts = listOf(
+        Contact(name = "Alice", online = true, profile_pic = "p1.png", last_heard = "now", privateMessages = msg2),
+        Contact(
+            name = "Bob",
+            online = false,
+            profile_pic = "p2.png",
+            last_heard = "22 min. ago",
+            privateMessages = msg1
+        ),
+        Contact(
+            name = "Carl",
+            online = true,
+            profile_pic = "p3.png",
+            last_heard = "2 hr. ago ",
+            privateMessages = msg3
+        ),
+        Contact(name = "Dan", online = false, profile_pic = "p4.png", last_heard = "1 day ago", privateMessages = msg4),
+        Contact(
+            name = "Eve",
+            online = false,
+            profile_pic = "p5.png",
+            last_heard = "3 days ago",
+            privateMessages = msg5
+        ),
+        Contact(
+            name = "Fred",
+            online = false,
+            profile_pic = "p2.png",
+            last_heard = "22 min. ago",
+            privateMessages = msg1
+        ),
+        Contact(
+            name = "Greg",
+            online = true,
+            profile_pic = "p3.png",
+            last_heard = "2 hr. ago ",
+            privateMessages = msg3
+        ),
+        Contact(
+            name = "Harold",
+            online = false,
+            profile_pic = "p4.png",
+            last_heard = "1 day ago",
+            privateMessages = msg4
+        ),
+        Contact(
+            name = "Irene",
+            online = false,
+            profile_pic = "p5.png",
+            last_heard = "3 days ago",
+            privateMessages = msg5
+        ),
+        Contact(
+            name = "Jeanne",
+            online = false,
+            profile_pic = "p2.png",
+            last_heard = "22 min. ago",
+            privateMessages = msg1
+        ),
+        Contact(
+            name = "Karl",
+            online = true,
+            profile_pic = "p3.png",
+            last_heard = "2 hr. ago ",
+            privateMessages = msg3
+        ),
+        Contact(
+            name = "Lorn",
+            online = false,
+            profile_pic = "p4.png",
+            last_heard = "1 day ago",
+            privateMessages = msg4
+        ),
+        Contact(
+            name = "Meg",
+            online = false,
+            profile_pic = "p5.png",
+            last_heard = "3 days ago",
+            privateMessages = msg5
+        ),
+        Contact(
+            name = "Nile",
+            online = false,
+            profile_pic = "p2.png",
+            last_heard = "22 min. ago",
+            privateMessages = msg1
+        ),
+        Contact(
+            name = "Oscar",
+            online = true,
+            profile_pic = "p3.png",
+            last_heard = "2 hr. ago ",
+            privateMessages = msg3
+        ),
+        Contact(
+            name = "Paul",
+            online = false,
+            profile_pic = "p4.png",
+            last_heard = "1 day ago",
+            privateMessages = msg4
+        ),
+        Contact(name = "Qi", online = false, profile_pic = "p5.png", last_heard = "3 days ago", privateMessages = msg5),
+    )
+}
+
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/data/ContentType.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/ContentType.kt
new file mode 100644
index 0000000000..e29adb5f0b
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/ContentType.kt
@@ -0,0 +1,16 @@
+package org.briarproject.briar.desktop.paul.data
+
+import org.briarproject.briar.desktop.paul.model.ContentType
+
+object ContentTypeList {
+    val types = listOf(
+        ContentType(id = -1, name = "Profile"),
+        ContentType(id = 0, name = "Contacts"),
+        ContentType(id = 1, name = "Private Groups"),
+        ContentType(id = 2, name = "Forums"),
+        ContentType(id = 3, name = "Blogs"),
+        ContentType(id = 4, name = "Transports"),
+        ContentType(id = 5, name = "Settings"),
+        ContentType(id = 6, name = "Sign Out")
+    )
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/data/OptionType.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/OptionType.kt
new file mode 100644
index 0000000000..5dbb5d8e4c
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/data/OptionType.kt
@@ -0,0 +1,14 @@
+package org.briarproject.briar.desktop.paul.data
+
+import org.briarproject.briar.desktop.paul.model.OptionType
+
+object OptionTypeList {
+    val msgTypes = listOf(
+        OptionType(name = "Alice", unread = 2, online = true),
+        OptionType(name = "Bob", unread = 0, online = true),
+        OptionType(name = "Carl", unread = 0, online = false),
+        OptionType(name = "Dan", unread = 1, online = false),
+        OptionType(name = "Eve", unread = 0, online = false),
+    )
+}
+
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Contact.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Contact.kt
new file mode 100644
index 0000000000..22085dff5b
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Contact.kt
@@ -0,0 +1,9 @@
+package org.briarproject.briar.desktop.paul.model
+
+data class Contact(
+    val name: String,
+    val online: Boolean,
+    val profile_pic: String,
+    val last_heard: String,
+    val privateMessages: List<Message>
+)
\ No newline at end of file
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/model/ContentType.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/ContentType.kt
new file mode 100644
index 0000000000..0db88d3071
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/ContentType.kt
@@ -0,0 +1,7 @@
+package org.briarproject.briar.desktop.paul.model
+
+data class ContentType(
+    val id: Int,
+    val name: String,
+    //val icon: Icon,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Message.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Message.kt
new file mode 100644
index 0000000000..a7a79f553f
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/Message.kt
@@ -0,0 +1,9 @@
+package org.briarproject.briar.desktop.paul.model
+
+data class Message(
+    val from: String?,
+    val message: String,
+    val time: String,
+    val delivered: Boolean,
+    //val read: Boolean,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/model/OptionType.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/OptionType.kt
new file mode 100644
index 0000000000..8367e1eb87
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/model/OptionType.kt
@@ -0,0 +1,9 @@
+package org.briarproject.briar.desktop.paul.model
+
+data class OptionType(
+    val name: String,
+    val unread: Int,
+    val online: Boolean
+    //val icon: Icon,
+)
+
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt
new file mode 100644
index 0000000000..2d443639ee
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt
@@ -0,0 +1,32 @@
+package org.briarproject.briar.desktop.paul.theme
+
+import androidx.compose.material.darkColors
+import androidx.compose.ui.graphics.Color
+
+val graySurface = Color(0xFF2A2A2A)
+val lightGray = Color(0xFFD3D3D3)
+val briarGray = Color(0xff222222)
+val briarBlack = Color(0xff1E2228)
+val briarSelBlack = Color(0xff495261)
+val briarDarkGray = Color(0xFF3D4552)
+val darkGray = Color(0xFF151515)
+val divider = Color(0xff35383D)
+val briarBlue = Color(0xFF2D3E50)
+val briarLightBlue = Color(0xFFEBEFF2)
+val briarGreen = Color(0xFF97D323)
+val briarBlueMsg = Color(0xFF1b69b6)
+val briarBlueSpecialMsg = Color(0xFF134a80)
+val briarGrayMsg = Color(0xff3b4047)
+val briarGraySpecialMsg = Color(0xFF212d3b)
+
+val DarkColorPallet = darkColors(
+    primary = Color.White,
+    secondary = graySurface,
+    background = briarLightBlue,
+    surface = briarBlue,
+    onPrimary = Color.White,
+    onSecondary = lightGray,
+    onBackground = Color.White,
+    onSurface = Color.White,
+    error = Color.Red,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
new file mode 100644
index 0000000000..85454f9c57
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
@@ -0,0 +1,103 @@
+package org.briarproject.briar.desktop.paul.views
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.Surface
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+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.graphics.Color
+import androidx.compose.ui.graphics.imageFromResource
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+import org.briarproject.briar.desktop.paul.theme.briarBlack
+import org.briarproject.briar.desktop.paul.theme.briarBlue
+
+@Composable
+fun briarSidebar(UIMode: String, onModeChange: (String) -> Unit) {
+    Surface(modifier = Modifier.width(66.dp).fillMaxHeight(), color = briarBlue) {
+        Column(verticalArrangement = Arrangement.Top) {
+            IconButton(
+                modifier = Modifier.align(Alignment.CenterHorizontally)
+                    .padding(top = 9.dp, bottom = 10.dp), onClick = {}) {
+                Image(
+                    bitmap = imageFromResource("images/profile_images/p0.png"),
+                    "my_profile_image",
+                    modifier = Modifier.size(48.dp).align(Alignment.CenterHorizontally).clip(
+                        CircleShape
+                    ).border(2.dp, color = Color.White, CircleShape)
+                )
+            }
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Contacts",
+                Icons.Filled.Contacts
+            )
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Private Groups",
+                Icons.Filled.Group
+            )
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Forums",
+                Icons.Filled.Forum
+            )
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Blogs",
+                Icons.Filled.ChromeReaderMode
+            )
+        }
+        Column(verticalArrangement = Arrangement.Bottom) {
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Transports",
+                Icons.Filled.WifiTethering
+            )
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Settings",
+                Icons.Filled.Settings
+            )
+            briarSidebarButton(
+                UIMode = UIMode,
+                onModeChange = onModeChange,
+                "Sign Out",
+                Icons.Filled.Logout
+            )
+        }
+    }
+}
+
+@Composable
+fun briarSidebarButton(
+    UIMode: String,
+    onModeChange: (String) -> Unit,
+    thisMode: String,
+    icon: ImageVector
+) {
+    val bg = if (UIMode == thisMode) briarBlack else briarBlue;
+    Column() {
+        IconButton(
+            modifier = Modifier.align(Alignment.CenterHorizontally).background(color = bg)
+                .padding(vertical = 9.dp, horizontal = 12.dp),
+            onClick = { onModeChange(thisMode) }) {
+            Icon(icon, thisMode, tint = Color.White, modifier = Modifier.size(30.dp))
+        }
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt
new file mode 100644
index 0000000000..a58fa8f036
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt
@@ -0,0 +1,51 @@
+package org.briarproject.briar.desktop.paul.views
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import org.briarproject.briar.desktop.paul.data.ContactList
+import org.briarproject.briar.desktop.paul.theme.briarBlack
+
+/*
+ * This is the root of the tree, all state is held here and passed down to stateless composables, which render the UI
+ * Desktop specific kotlin files are found in briarComposeDesktop (possibly briar-compose-desktop project in the future)
+ * Multiplatform, stateless, composable are found in briarCompose (possible briar-compose project in the future)
+ */
+@Composable
+fun briarUIStateManager() {
+    //current selected mode, changed using the sidebar buttons
+    val (UIMode, onModeChange) = remember { mutableStateOf("Contacts") }
+    //current selected contact
+    val (UIContact, onContactSelect) = remember { mutableStateOf(ContactList.contacts[0]) }
+    //current selected private message
+    val (UIPrivateMsg, onPMSelect) = remember { mutableStateOf(0) }
+    //current selected forum
+    val (UIForum, onForumSelect) = remember { mutableStateOf(0) }
+    //current blog state
+    val (UIBlog, onBlogSelect) = remember { mutableStateOf(0) }
+    //current transport state
+    val (UITransports, onTransportSelect) = remember { mutableStateOf(0) }
+    //current settings state
+    val (UISettings, onSettingSelect) = remember { mutableStateOf(0) }
+    //current profile
+    var Profile: String;
+    //Other global state that we need to track should go here also
+    Row() {
+        briarSidebar(UIMode, onModeChange)
+        when (UIMode) {
+            "Contacts" -> privateMessageView(UIContact, onContactSelect)
+            else -> Box(modifier = Modifier.fillMaxSize().background(briarBlack)) {
+                Text("TBD", modifier = Modifier.align(Alignment.Center), color = Color.White)
+            }
+        }
+    }
+
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
new file mode 100644
index 0000000000..fc8544d5e9
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
@@ -0,0 +1,319 @@
+package org.briarproject.briar.desktop.paul.views
+
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.imageFromResource
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.v1.DialogProperties
+import org.briarproject.briar.desktop.paul.data.ContactList
+import org.briarproject.briar.desktop.paul.model.Contact
+import org.briarproject.briar.desktop.paul.model.Message
+import org.briarproject.briar.desktop.paul.theme.*
+
+
+val HEADER_SIZE = 66.dp;
+
+@Composable
+fun privateMessageView(UIContact: Contact, onContactSelect: (Contact) -> Unit) {
+    //Local State for managing the Add Contact Popup
+    val (AddContactDialog, onCancelAdd) = remember { mutableStateOf(false) }
+    addContactDialog(AddContactDialog, onCancelAdd)
+    Column(modifier = Modifier.fillMaxHeight()) {
+        Row(modifier = Modifier.fillMaxWidth()) {
+            Divider(color = divider, modifier = Modifier.fillMaxHeight().width(1.dp))
+            Column(modifier = Modifier.fillMaxHeight().background(color = briarBlack).width(275.dp)) {
+                Row(
+                    modifier = Modifier.fillMaxWidth().height(HEADER_SIZE).padding(horizontal = 16.dp),
+                    horizontalArrangement = Arrangement.SpaceBetween,
+                ) {
+                    Text(
+                        "Contacts",
+                        fontSize = 24.sp,
+                        color = Color.White,
+                        modifier = Modifier.align(Alignment.CenterVertically)
+                    )
+                    IconButton(
+                        onClick = { onCancelAdd(true) },
+                        modifier = Modifier.align(Alignment.CenterVertically).background(color = briarDarkGray)
+                    ) {
+                        Icon(Icons.Filled.Add, "add contact", tint = Color.White, modifier = Modifier.size(24.dp))
+                    }
+                }
+                Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth())
+                Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
+                    for (c in ContactList.contacts) {
+                        contactCard(c, UIContact, onSel = onContactSelect)
+                    }
+                }
+
+            }
+            Divider(color = divider, modifier = Modifier.fillMaxHeight().width(1.dp))
+            Column(modifier = Modifier.weight(1f).fillMaxHeight().background(color = darkGray)) {
+                drawMessageRow(UIContact)
+            }
+        }
+    }
+}
+
+@Composable
+fun addContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) {
+    if (isVisible) {
+        AlertDialog(
+            onDismissRequest = {
+                onCancel(false)
+            },
+            text = {
+                Column(modifier = Modifier.fillMaxWidth()) {
+                    Row(Modifier.fillMaxWidth().padding(vertical = 16.dp)) {
+                        Text(
+                            text = "Add Contact at a Distance",
+                            fontSize = 24.sp,
+                            color = Color.White,
+                            modifier = Modifier.align(Alignment.CenterVertically)
+                        )
+                    }
+                    Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
+                        Text(
+                            "Contact's Link",
+                            Modifier.width(128.dp).align(Alignment.CenterVertically),
+                            color = lightGray
+                        )
+                        TextField("", onValueChange = {}, modifier = Modifier.fillMaxWidth())
+                    }
+                    Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
+                        Text(
+                            "Contact's Name",
+                            Modifier.width(128.dp).align(Alignment.CenterVertically),
+                            color = lightGray
+                        )
+                        TextField("", onValueChange = {}, modifier = Modifier.fillMaxWidth())
+                    }
+                    Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
+                        Text(
+                            "Your Link",
+                            modifier = Modifier.width(128.dp).align(Alignment.CenterVertically),
+                            color = lightGray
+                        )
+                        TextField(
+                            "briar://ksdjlfgakslhjgaklsjdhglkasjdlk3j12h4lk2j3tkj4",
+                            onValueChange = {},
+                            modifier = Modifier.fillMaxWidth()
+                        );
+                    }
+                }
+            },
+            confirmButton = {
+                TextButton(
+                    onClick = {
+                        onCancel(false)
+                    },
+                    modifier = Modifier.background(briarGreen)
+                ) {
+                    Text("Add")
+                }
+            },
+            dismissButton = {
+                TextButton(
+                    onClick = {
+                        onCancel(false)
+                    }, modifier = Modifier.background(briarBlack)
+                ) {
+                    Text("Cancel")
+                }
+            },
+
+            backgroundColor = briarBlue,
+            contentColor = Color.White,
+            modifier = Modifier.border(1.dp, color = divider),
+            properties = DialogProperties(resizable = false, undecorated = true, size = IntSize(600, 300))
+        )
+    }
+}
+
+@Composable
+fun contactCard(contact: Contact, selContact: Contact, onSel: (Contact) -> Unit) {
+    var bgColor = briarBlack
+    if (selContact.name == contact.name) {
+        bgColor = darkGray
+    }
+    Row(
+        modifier = Modifier.fillMaxWidth().height(HEADER_SIZE).background(bgColor)
+            .clickable(onClick = { onSel(contact) }), horizontalArrangement = Arrangement.SpaceBetween
+    ) {
+        Row(modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)) {
+            Image(
+                bitmap = imageFromResource("images/profile_images/" + contact.profile_pic),
+                "image",
+                modifier = Modifier.size(40.dp).align(Alignment.CenterVertically).clip(
+                    CircleShape
+                ).border(2.dp, color = Color.White, CircleShape)
+            )
+            Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) {
+                Text(
+                    contact.name,
+                    fontSize = 14.sp,
+                    color = Color.White,
+                    modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp)
+                )
+                Text(
+                    contact.last_heard,
+                    fontSize = 10.sp,
+                    color = Color.LightGray,
+                    modifier = Modifier.align(Alignment.Start)
+                )
+            }
+        }
+        androidx.compose.foundation.Canvas(
+            modifier = Modifier.padding(horizontal = 29.dp).size(22.dp).align(Alignment.CenterVertically), onDraw = {
+                val size = 16.dp.toPx()
+                drawCircle(
+                    color = Color.White,
+                    radius = size / 2f
+                )
+                if (contact.online) {
+                    drawCircle(
+                        color = briarGreen,
+                        radius = 14.dp.toPx() / 2f
+                    )
+                } else {
+                    drawCircle(
+                        color = briarBlack,
+                        radius = 14.dp.toPx() / 2f
+                    )
+                }
+            })
+    }
+
+    Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth())
+}
+
+@Composable
+fun textBubble(m: Message) {
+    Column(Modifier.fillMaxWidth()) {
+        if (m.from == null) {
+            Column(Modifier.fillMaxWidth(fraction = 0.9f).align(Alignment.End)) {
+                Column(Modifier.background(briarBlueMsg).padding(8.dp).align(Alignment.End)) {
+                    Text(m.message, fontSize = 14.sp, color = Color.White, modifier = Modifier.align(Alignment.Start))
+                    Row(modifier = Modifier.padding(top = 4.dp)) {
+                        Text(m.time, Modifier.padding(end = 4.dp), fontSize = 10.sp, color = Color.LightGray)
+                        if (m.delivered) {
+                            Icon(
+                                Icons.Filled.Check,
+                                "sent",
+                                tint = Color.LightGray,
+                                modifier = Modifier.size(10.dp).align(Alignment.CenterVertically)
+                            )
+                        } else {
+                            Icon(
+                                Icons.Filled.Send,
+                                "sending",
+                                tint = Color.LightGray,
+                                modifier = Modifier.size(10.dp).align(Alignment.CenterVertically)
+                            )
+                        }
+                    }
+                }
+            }
+        } else {
+            Column(Modifier.fillMaxWidth(fraction = 0.9f).align(Alignment.Start)) {
+                Column(Modifier.background(briarGrayMsg).padding(8.dp).align(Alignment.Start)) {
+                    Text(m.message, fontSize = 14.sp, color = Color.White, modifier = Modifier.align(Alignment.Start))
+                    Row(modifier = Modifier.padding(top = 4.dp)) {
+                        Text(m.time, Modifier.padding(end = 4.dp), fontSize = 10.sp, color = Color.LightGray)
+                        if (m.delivered) {
+                            Icon(
+                                Icons.Filled.Check,
+                                "sent",
+                                tint = Color.LightGray,
+                                modifier = Modifier.size(10.dp).align(Alignment.CenterVertically)
+                            )
+                        } else {
+                            Icon(
+                                Icons.Filled.Send,
+                                "sending",
+                                tint = Color.LightGray,
+                                modifier = Modifier.size(10.dp).align(Alignment.CenterVertically)
+                            )
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun drawTextBubbles(msgList: List<Message>) {
+    LazyColumn(
+        Modifier.fillMaxWidth().padding(horizontal = 8.dp),
+        verticalArrangement = Arrangement.spacedBy(8.dp),
+        reverseLayout = true,
+        contentPadding = PaddingValues(vertical = 8.dp)
+    ) {
+        items(msgList) { m ->
+            textBubble(m)
+        }
+    }
+}
+
+@Composable
+fun drawMessageRow(UIContact: Contact) {
+    Box(Modifier.fillMaxHeight()) {
+        Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) {
+            Row(modifier = Modifier.align(Alignment.Center)) {
+                Image(
+                    bitmap = imageFromResource("images/profile_images/" + UIContact.profile_pic),
+                    "sel_contact_prof",
+                    modifier = Modifier.size(36.dp).align(
+                        Alignment.CenterVertically
+                    ).clip(
+                        CircleShape
+                    ).border(2.dp, color = Color.White, CircleShape)
+                )
+                Text(
+                    UIContact.name,
+                    color = Color.White,
+                    modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp),
+                    fontSize = 24.sp
+                )
+            }
+            IconButton(onClick = {}, modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp)) {
+                Icon(Icons.Filled.MoreVert, "contact info", tint = Color.White, modifier = Modifier.size(24.dp))
+            }
+            Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter))
+        }
+        Box(Modifier.padding(top = HEADER_SIZE + 1.dp, bottom = HEADER_SIZE)) {
+            drawTextBubbles(UIContact.privateMessages)
+        }
+        var text by remember { mutableStateOf(TextFieldValue("")) }
+        Box(Modifier.align(Alignment.BottomCenter).background(darkGray)) {
+            OutlinedTextField(
+                value = text,
+                trailingIcon = { Icon(Icons.Filled.Send, "send message", tint = briarGreen) },
+                leadingIcon = { Icon(Icons.Filled.AddCircle, contentDescription = "add file") },
+                modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp).fillMaxWidth(),
+                label = { Text(text = "Message") },
+                textStyle = TextStyle(color = Color.White),
+                placeholder = { Text(text = "Your message to " + UIContact.name) },
+                onValueChange = {
+                    text = it
+                },
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/images/profile_images/p0.png b/src/main/resources/images/profile_images/p0.png
new file mode 100644
index 0000000000000000000000000000000000000000..8c67a3c592c8267f56a4c810cb2d561c88d05e18
GIT binary patch
literal 45426
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

literal 0
HcmV?d00001

diff --git a/src/main/resources/images/profile_images/p1.png b/src/main/resources/images/profile_images/p1.png
new file mode 100644
index 0000000000000000000000000000000000000000..50155c8a1ebc3513e3b6e3cc2936881b594f92f4
GIT binary patch
literal 2804
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

literal 0
HcmV?d00001

diff --git a/src/main/resources/images/profile_images/p2.png b/src/main/resources/images/profile_images/p2.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ff9a6e10f151d8e9913a968a535cf7b3e542c0f
GIT binary patch
literal 45426
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
V^V|P;y}^qQUq1iC_kZ%6e*sZkT)O}O

literal 0
HcmV?d00001

diff --git a/src/main/resources/images/profile_images/p3.png b/src/main/resources/images/profile_images/p3.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b8509e1e21afe1863ad0d35395ef11cdd9113b4
GIT binary patch
literal 45426
zcmeI1F=~}T00#e%CIr%nMLHV`Z=oc_hY~`}0kkn<Ar>Omc7m1nh^0sH05*y@@D$Ia
z+G&QoBFIW6pniNi-(%mb*!bz>_-Jo;e|K4yz18D`)5|}9T>aYFzWjaX=*`<@*<Mbb
zo*k~&>(AeQU6%c1;1(LV^J@RgvTQByPLH3h*SAmrzVd_fpWm0|;@;}u;n|1ZTX!%1
zT>ZPzPX;oOfed6I0~yFb1~QO=3~XXx>-wJ!KIZxl+4^Unvdnauo4HG0$^Q1-jNhC2
zlwPS<;@>2*fu6Wz&>CgRTDRkXxa0wAk|k^1;()wl&=zIM+NS7<O9rh`rmS^44v0%0
zuqIiu)-4XmO9pLGmaJ`xp15St8fD5_x8s1g<N<4vC2QT{fV^bT7G=rWrs#=F2CY%1
ztaUpMh)W)@CRwu9Ee^;_25nK6tZj;(xMa{8Wy)H&<AAv20c(;aYu)02ykyW8Wy#v6
z=!r`Ptx=||bvq7-OCGQ$S+dqG4#-OeZBdr2ZHk__WY8LA%38PMfVkuVYmy~v-Qs|}
zWY896$=asqiAx5pQKqbQI}V6T9<U}^veqpQ$V&!oQI@Q2ik`S+&>CgRTDRkXxa0wA
zk|k^1;()wl&=zIM+NS7<O9rh`rmS^44v0%0uqIiu)-4XmO9pLGmaJ`xp15St8fD5_
zx8s1g<N<4vC2QT{fV^bT7G=rWrs#=F2CY%1taUpMh)W)@CRwu9Ee^;_25nK6tZj;(
zxMa{8Wy)H&<AAv20c(;aYu)02ykyW8Wy#v6=!r`Ptx=||bvq7-OCGQ$S+dqG4#-Oe
zZBdr2ZHk__WYEP7WFP|>$Up`%kbw+jAOji5Kn609fed6I0~yFb1~QO=3}he!|E~f0
zKhat1O+?Rncez(zNyJQ5_exVehg_yt5;0TNz0y?AA(!cuM9frmuQb(j$YpvZ5i?cY
zD^2wra+zL9#7tH9N>e?DT&7nNF;msO(p1kOm+6&6%v5!+G}Uv+WqKtMGgaLyP4ygd
znOpiwn|$q<3}he!8OT5eGLV4`WFP|>$Up`%kbw+jAOji5Kn609fed6I0~yFb1~QO=
z3}he!8OT5eGLV4`WFP|>$Up`%kbw+jAOji5Kn609fed6I0~yFb1~QO=3}he!8OT5e
cGBBrs^OyHm?_WH5eRaFl;qk%ON6$b01wq6_2mk;8

literal 0
HcmV?d00001

diff --git a/src/main/resources/images/profile_images/p4.png b/src/main/resources/images/profile_images/p4.png
new file mode 100644
index 0000000000000000000000000000000000000000..96ed7fbc717af1d66c71fd5b9376b6a32dfc6e5b
GIT binary patch
literal 45426
zcmeI1v1*i26omgJO$em5u(Q-gUqMNT0SO_dvC=}Y3n?tD?F3&XHu?-cfQ_PsjZL0n
z-E=mGd~?rm6*CJlaG5jb+wVXc-yI(v?C#v&S(at@@bTWs=J%JYpY5&9zuO0A=gYFS
z96veTU$58izkS*~<`V-q(7@%hU(c6iwY)w#db(cUKmmVz<$JHbeqNS~JBND@Pv3lB
z-MYBE`n!=&48%YT#6S$hKn%n{48%YT#K1fQtLs1WU_6_y|3)j%qIoU(O7mQgZ_1U%
z-OZcKmFBq~-;^tjyPG$eE6sB~zA0B4cQ<b`SDNQ~d{eG8?rz>>t~Af}_@-QG+}*s%
zTxp)`@lCnXxVw3ixzaq><C}7&ad-13bESE%$2a9l<L>57{!>>1Fdzctfff(}ixyA=
zB486}0THk$U#ap4<W-~EJ6Mz}RZ4-pYE*j%i*lt(DUer<YVTlCu2d-n@~Tno9W2U~
zDy2YPHLAUXMY&R?6v(SawRf;6SE`f(dDW=)4i@D~l~N$D8r9yxqFkv`3glIz+B;a3
zD^*H?ylPZ?2a9s0N-2<6jcV^;QLa=e1@fv<?Hw%2l`5q`UNx${gGISgr4-1kMzwda
zC|9bK0(sS__6`>1N|jO|uNu|f!J_~1O5kg29vf-EVu~~%wNVo<sL{9@X~1HNG$6H6
z6ECRIxEg7|Vu~~%wNVo<sL{9@X~1HNG$6H66ECRIxEg7|Vu~~%wNVo<sL{9@X~1HN
zG$6H66ECRIxEg7|Vu~~%wNVo<sL{9@X~1HNG$6H66ECRIxEg7|Vu~~%wNVo<sL{9@
zX~1HNG$6H66ECRIxEg7|Vu~~%wNVo<sL{9@X~1HNG$6H66ECRIxEg7|Vu~~%wNVo<
zsL{9@X~1HNG$6H66ECRIxEg7|Vu~~%wNVo<sL{9@X~1HNG$6H66ECRIxEg7|Vu~~%
zwNVo<sL{9@X~2?548%YT#6S$hKn%n{48%YT#6S$hKn%n{48%YT#6S$hKn%n{48%YT
z+!O=ge_w$LPYYN?A|4n|3*iMB4~(Y;;F5?3#?wN0LB<2)X#uz-;(_tB5MGe+z<62!
zE{S+xJS~J5WIQmQ7Jy449vDvx;RP8FjHd<Ql86V!(?WPb#slMN0k|aMf$_8uUXby?
zcv=81iFjZ<Erb_jJTRUXfJ-7C7*7k~1sM;Frv>1WhzG{gLU=*O1LJ7{xFq6%@w5<L
zknzBHS^zGIcwjs&gcoEyFrF5GOClZ^PYdA%84rx71>lm12gcJvctOSk<7ol7B;tYb
zv=Cm9@xXXm04|AmU_32^7i2szo)&;hA|4n|3*iMB4~(Y;;3g6SF%SbW5Cbs~12GT-
iF%SbW5Cbvr7Y&@hy#M3p-H#8h5Dxc`_C7p%@%A@gC{OkP

literal 0
HcmV?d00001

diff --git a/src/main/resources/images/profile_images/p5.png b/src/main/resources/images/profile_images/p5.png
new file mode 100644
index 0000000000000000000000000000000000000000..72348d6d73b3a8cb15aa7f1269e5e980256c7388
GIT binary patch
literal 45426
zcmeI4KWfwg6oh|4ix9B2un`LzZ=pmqAR)vYz)nFB3k$){PO!1|27>lpzysJQc9x!E
zO~609@Fsi%Lj*GmNp@%7oB4KLNSRj$`*$wwT-jNc<<j2W-NWObpHIHFFC2g0zVqbi
zvRqgW?j7A;tyXVee?1oaXanc4fh!;1y;zp5<=Nr>{nhFm7O?h_Z#?|+X<6Q1+uOZ)
z^!(e_<@Y~M{%-Wq2HHRyXajAa4YYwa&<5H-8)yThfvv~`Bt>$zK+tk#NoN++kEE&w
zzYl8F(?L+Lq$&mFsZ~!0LA{cy6qKh{Jskw~N~%&&o?7*E5Y#KFN<n#Q)zd*xucRsk
z<*8Lq2SL4(suYx`Ry`dA^-8KzP@Y=#bP&`lsY*e4YSq(0P_Lva1?8z#PX|H0lByJx
zr&c{31ocX)Qc#{+^>h%_E2&CBd1}?uK~S%xDh1`KRZj;&y^^XFl&4lb9R&4Cs!~v%
zTJ>}g^gmt+d~DF;nYn-<GSZJETFMk7%w0JHlW}p`#e4=P<024UIRle%aoNRu1}5Vo
z5M4O~lW}p`#e4=P<024UIRle%aoNRu1}5Vo5M4O~lW}p`#e4=P<024UIRle%aoNRu
z1}5Vo5M4O~lW}p`#e4=P<024UIRle%aoNRu1}5Vo5M4O~lW}p`#e4=P<024UIRle%
zaoNRu1}5Vo5M4O~lW}p`#e4=P<024UIRle%aoNRu1}5Vo5M4O~lW}p`#e4=P<024U
zIRle%aoNRu1}5Vo5M4O~lW}p`#e4=P<024UIRle%aoNRu1}5Vo5I5wrOWKGXUxZjf
zHY1`dhFC&2F8w$kVhP!dh^`o73E8;x<9vuEWHTbVVu&SV<I<1wA(oKMi0FzTmXM80
zKhB3(LN+6!D~4D?HZJ`*A7TmFjEJrnVhP!}^y7SpC1f)qx?+eWWaHA0^C6ay&4}oV
zA(oJhOFzzsSVA@<qAP}2LN+e_I3HpO*^G#;7-9+8xb)+Eh$Un*BD!LTC1m5$kMkjx
zkj;qbiXoPejY~hyhgd>3BcdyYSVA@~{Wu?D3E7N@t{7qo*|_xMe267vGa|ZTh$Up>
z(vR~YmXOVe=!zkhkc~?}&WBh+HY1`dhFC&2F8w$kVgWW_WF*`afeRSf^qgM-8!$3^
zZ>YcpjBHqn7r+LL2=jFrxPXy$NA(=ofDz@srUMr+vSuis0vj+Q?f<?3T)@a*Km82Y
zfD!Hg=N;ezM*i5H@&wp`ky8SGzXe>t$ZxmPegYdXa$3T#_kar+0YU%cKSuz~fDuq0
zZ~-GAs8>=wg7Va=r-PtgNmUBUQ>&g1f_f!YDJV~^dO8T|l~kpmJhkfSAgEVTm4for
zs;7gXUP)C7%2TVJ4uX0mRVgS>t$I2L>XlTbpggte=^&_AQk8=8)T*b0pk7H;3d&Qf
zo(_U~B~>XXPpx`72<nworJy{u>ggb;S5lRN^3<xQgP>kXRSL>etDX*mdL>mUC{L|=
zItc2ORHdLiwd(00s8>>zg7Va=r-PtgNmUBUQ>&g1f_f!YDJV~^dO8T|l~kpmJhkfS
zAgEVTm4fors;7gXUP)C7%2TVJ4uX0mRVgS>t$I2L>XlTbpggte=^&_AQk8=8)T*b0
zpk7H;3d&Qfo(_U~B~>XXPpx`72<nworJy{u>ggb;S5lRN^3<xQgP>kXRSL>etDX*m
zI?@K(KpSWSZJ-Ubfi}<v+CUp<18txU{KE#WUcdL|(c|wIPZ0KQ@9%!N_2A`C<{xVo

literal 0
HcmV?d00001

-- 
GitLab