diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b85341f..316915d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,7 +36,6 @@
android:theme="@style/Theme.TwoM">
-
@@ -46,13 +45,17 @@
android:theme="@style/Theme.TwoM" />
+
+
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/activities/InviteUserActivity.kt b/app/src/main/java/eu/steffo/twom/activities/InviteUserActivity.kt
new file mode 100644
index 0000000..ff615a2
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/activities/InviteUserActivity.kt
@@ -0,0 +1,35 @@
+package eu.steffo.twom.activities
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.compose.material3.Text
+
+
+class InviteUserActivity : ComponentActivity() {
+ companion object {
+ const val USER_EXTRA = "user"
+ }
+
+ class Contract : ActivityResultContract() {
+ override fun createIntent(context: Context, input: Unit): Intent {
+ return Intent(context, CreateRoomActivity::class.java)
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): String? {
+ return when (resultCode) {
+ RESULT_OK -> intent!!.getStringExtra(USER_EXTRA)
+ else -> null
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent { Text("Garasauto Prime") }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/activities/LoginActivity.kt b/app/src/main/java/eu/steffo/twom/activities/LoginActivity.kt
index 73926c0..7ca7819 100644
--- a/app/src/main/java/eu/steffo/twom/activities/LoginActivity.kt
+++ b/app/src/main/java/eu/steffo/twom/activities/LoginActivity.kt
@@ -9,12 +9,17 @@ import eu.steffo.twom.composables.login.LoginScaffold
class LoginActivity : ComponentActivity() {
- class Contract : ActivityResultContract() {
+ class Contract : ActivityResultContract() {
override fun createIntent(context: Context, input: Unit): Intent {
return Intent(context, LoginActivity::class.java)
}
- override fun parseResult(resultCode: Int, intent: Intent?) {}
+ override fun parseResult(resultCode: Int, intent: Intent?): Unit? {
+ return when (resultCode) {
+ RESULT_OK -> Unit
+ else -> null
+ }
+ }
}
override fun onStart() {
diff --git a/app/src/main/java/eu/steffo/twom/activities/MainActivity.kt b/app/src/main/java/eu/steffo/twom/activities/MainActivity.kt
index 5357450..ef062be 100644
--- a/app/src/main/java/eu/steffo/twom/activities/MainActivity.kt
+++ b/app/src/main/java/eu/steffo/twom/activities/MainActivity.kt
@@ -7,12 +7,10 @@ import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResult
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.net.toFile
import androidx.lifecycle.lifecycleScope
import eu.steffo.twom.composables.main.MainScaffold
-import eu.steffo.twom.matrix.TwoMMatrix
+import eu.steffo.twom.utils.TwoMGlobals
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
@@ -21,38 +19,15 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomStateEvent
class MainActivity : ComponentActivity() {
- private lateinit var loginLauncher: ActivityResultLauncher
- private lateinit var roomLauncher: ActivityResultLauncher
- private lateinit var createLauncher: ActivityResultLauncher
-
private var session: Session? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- TwoMMatrix.ensureMatrix(applicationContext)
+ TwoMGlobals.ensureMatrix(applicationContext)
fetchLastSession()
openSession()
-
- loginLauncher =
- registerForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- this::onLogin
- )
-
- roomLauncher =
- registerForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- this::onRoom
- )
-
- createLauncher =
- registerForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- this::onCreate
- )
-
resetContent()
}
@@ -64,7 +39,7 @@ class MainActivity : ComponentActivity() {
private fun fetchLastSession() {
Log.d("Main", "Fetching the last successfully authenticated session...")
- session = TwoMMatrix.matrix.authenticationService().getLastAuthenticatedSession()
+ session = TwoMGlobals.matrix.authenticationService().getLastAuthenticatedSession()
}
private fun openSession() {
@@ -99,27 +74,6 @@ class MainActivity : ComponentActivity() {
}
}
- private fun destroySession() {
-
- }
-
- private fun onClickRoom(roomId: String) {
- Log.d("Main", "Clicked a room, launching room activity...")
- val intent = Intent(applicationContext, RoomActivity::class.java)
- intent.putExtra(RoomActivity.ROOM_ID_EXTRA, roomId)
- roomLauncher.launch(intent)
- }
-
- private fun onRoom(result: ActivityResult) {
- Log.d("Main", "Received result from room activity: $result")
- }
-
- private fun onClickCreate() {
- Log.d("Main", "Clicked the New button, launching create activity...")
- val intent = Intent(applicationContext, CreateRoomActivity::class.java)
- createLauncher.launch(intent)
- }
-
private fun onCreate(result: ActivityResult) {
Log.d("Main", "Received result from create activity: $result")
if (result.resultCode == RESULT_OK) {
@@ -146,7 +100,7 @@ class MainActivity : ComponentActivity() {
createRoomParams.name = name
createRoomParams.topic = description
createRoomParams.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT
- createRoomParams.roomType = TwoMMatrix.ROOM_TYPE
+ createRoomParams.roomType = TwoMGlobals.ROOM_TYPE
createRoomParams.initialStates = mutableListOf(
CreateRoomStateEvent(
type = "m.room.power_levels",
diff --git a/app/src/main/java/eu/steffo/twom/activities/RoomActivity.kt b/app/src/main/java/eu/steffo/twom/activities/ViewRoomActivity.kt
similarity index 77%
rename from app/src/main/java/eu/steffo/twom/activities/RoomActivity.kt
rename to app/src/main/java/eu/steffo/twom/activities/ViewRoomActivity.kt
index 6ecfdb8..4a33792 100644
--- a/app/src/main/java/eu/steffo/twom/activities/RoomActivity.kt
+++ b/app/src/main/java/eu/steffo/twom/activities/ViewRoomActivity.kt
@@ -7,18 +7,18 @@ import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContract
-import eu.steffo.twom.matrix.TwoMMatrix
-import eu.steffo.twom.room.RoomActivityScaffold
+import eu.steffo.twom.composables.viewroom.ViewRoomScaffold
+import eu.steffo.twom.utils.TwoMGlobals
import org.matrix.android.sdk.api.session.Session
-class RoomActivity : ComponentActivity() {
+class ViewRoomActivity : ComponentActivity() {
companion object {
const val ROOM_ID_EXTRA = "roomId"
}
class Contract : ActivityResultContract() {
override fun createIntent(context: Context, input: String): Intent {
- val intent = Intent(context, RoomActivity::class.java)
+ val intent = Intent(context, ViewRoomActivity::class.java)
intent.putExtra(ROOM_ID_EXTRA, input)
return intent
}
@@ -31,7 +31,7 @@ class RoomActivity : ComponentActivity() {
private fun fetchLastSession() {
Log.d("Main", "Fetching the last successfully authenticated session...")
// FIXME: If this is null, it means that something launched this while no session was authenticated...
- session = TwoMMatrix.matrix.authenticationService().getLastAuthenticatedSession()!!
+ session = TwoMGlobals.matrix.authenticationService().getLastAuthenticatedSession()!!
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -47,13 +47,9 @@ class RoomActivity : ComponentActivity() {
val roomId = intent.getStringExtra(ROOM_ID_EXTRA)
setContent {
- RoomActivityScaffold(
+ ViewRoomScaffold(
session = session,
roomId = roomId!!, // FIXME: Again, this should be set. Should.
- onBack = {
- setResult(RESULT_CANCELED)
- finish()
- },
)
}
}
diff --git a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromDefault.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarEmpty.kt
similarity index 64%
rename from app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromDefault.kt
rename to app/src/main/java/eu/steffo/twom/composables/avatar/AvatarEmpty.kt
index ff40acf..ec632e4 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromDefault.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarEmpty.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix.avatar
+package eu.steffo.twom.composables.avatar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -8,16 +8,13 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
@Composable
@Preview(widthDp = 40, heightDp = 40)
-fun AvatarFromDefault(
+fun AvatarEmpty(
modifier: Modifier = Modifier,
- fallbackText: String = "?",
- contentDescription: String = "",
+ text: String? = null,
) {
Box(
modifier = modifier
@@ -26,12 +23,9 @@ fun AvatarFromDefault(
) {
Text(
modifier = Modifier
- .align(Alignment.Center)
- .semantics {
- this.contentDescription = ""
- },
+ .align(Alignment.Center),
color = MaterialTheme.colorScheme.onTertiary,
- text = fallbackText,
+ text = text ?: "?",
)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromImageBitmap.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarImage.kt
similarity index 65%
rename from app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromImageBitmap.kt
rename to app/src/main/java/eu/steffo/twom/composables/avatar/AvatarImage.kt
index c42d7d1..2b614ea 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromImageBitmap.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarImage.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix.avatar
+package eu.steffo.twom.composables.avatar
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
@@ -6,21 +6,25 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
@Composable
@Preview(widthDp = 40, heightDp = 40)
-fun AvatarFromImageBitmap(
+fun AvatarImage(
modifier: Modifier = Modifier,
bitmap: ImageBitmap? = null,
- fallbackText: String = "?",
+ fallbackText: String? = null,
contentDescription: String = "",
) {
if (bitmap == null) {
- AvatarFromDefault(
- modifier = modifier,
- fallbackText = fallbackText,
- contentDescription = contentDescription,
+ AvatarEmpty(
+ modifier = modifier
+ .semantics {
+ this.contentDescription = contentDescription
+ },
+ text = fallbackText,
)
} else {
Image(
diff --git a/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarPicker.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarPicker.kt
index 8f6d829..ea78937 100644
--- a/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarPicker.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarPicker.kt
@@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
-import eu.steffo.twom.matrix.avatar.AvatarFromImageBitmap
import eu.steffo.twom.utils.BitmapUtilities
@Composable
@@ -49,7 +48,7 @@ fun AvatarPicker(
launcher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
) {
- AvatarFromImageBitmap(
+ AvatarImage(
bitmap = selection?.asImageBitmap(),
fallbackText = fallbackText,
)
diff --git a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromURL.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarURL.kt
similarity index 60%
rename from app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromURL.kt
rename to app/src/main/java/eu/steffo/twom/composables/avatar/AvatarURL.kt
index 230e94c..c08bb51 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromURL.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarURL.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix.avatar
+package eu.steffo.twom.composables.avatar
import android.graphics.Bitmap
import android.graphics.BitmapFactory
@@ -12,16 +12,15 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.tooling.preview.Preview
-import eu.steffo.twom.matrix.LocalSession
-import org.matrix.android.sdk.api.failure.Failure
+import eu.steffo.twom.composables.matrix.LocalSession
import java.io.File
@Composable
@Preview(widthDp = 40, heightDp = 40)
-fun AvatarFromURL(
+fun AvatarURL(
modifier: Modifier = Modifier,
url: String? = "",
- fallbackText: String = "?",
+ fallbackText: String? = null,
contentDescription: String = "",
) {
val session = LocalSession.current
@@ -29,22 +28,22 @@ fun AvatarFromURL(
LaunchedEffect(session, url) GetAvatar@{
if (session == null) {
- Log.d("Avatar", "Not doing anything, session is null.")
+ Log.d("AvatarURL", "Not doing anything, session is null.")
bitmap = null
return@GetAvatar
}
if (url == null) {
- Log.d("Avatar", "URL is null, not downloading anything.")
+ Log.d("AvatarURL", "URL is null, not downloading anything.")
bitmap = null
return@GetAvatar
}
if (url.isEmpty()) {
- Log.d("Avatar", "URL is a zero-length string, not downloading anything.")
+ Log.d("AvatarURL", "URL is a zero-length string, not downloading anything.")
bitmap = null
return@GetAvatar
}
- Log.d("Avatar", "Downloading avatar at: $url")
+ Log.d("AvatarURL", "Downloading avatar at: $url")
lateinit var avatarFile: File
try {
avatarFile = session.fileService().downloadFile(
@@ -53,27 +52,20 @@ fun AvatarFromURL(
mimeType = null,
elementToDecrypt = null,
)
- } catch (f: Failure.OtherServerError) {
- Log.e("Avatar", "Unable to download avatar at: $url", f)
+ } catch (e: Throwable) {
+ Log.e("AvatarURL", "Unable to download avatar at: $url", e)
return@GetAvatar
}
// TODO: Should I check the MIME type? And the size of the image?
- Log.d("Avatar", "File for $url is: $avatarFile")
+ Log.d("AvatarURL", "File for $url is: $avatarFile")
bitmap = BitmapFactory.decodeFile(avatarFile.absolutePath)
}
- if (bitmap == null) {
- AvatarFromDefault(
- modifier = modifier,
- fallbackText = fallbackText,
- contentDescription = contentDescription
- )
- } else {
- AvatarFromImageBitmap(
- modifier = modifier,
- bitmap = bitmap!!.asImageBitmap(),
- contentDescription = contentDescription,
- )
- }
+ AvatarImage(
+ modifier = modifier,
+ bitmap = bitmap?.asImageBitmap(),
+ fallbackText = fallbackText,
+ contentDescription = contentDescription,
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUser.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUser.kt
new file mode 100644
index 0000000..781707a
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUser.kt
@@ -0,0 +1,23 @@
+package eu.steffo.twom.composables.avatar
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import org.matrix.android.sdk.api.session.user.model.User
+import org.matrix.android.sdk.api.util.toMatrixItem
+
+@Composable
+@Preview(widthDp = 40, heightDp = 40)
+fun AvatarUser(
+ modifier: Modifier = Modifier,
+ user: User? = null,
+ fallbackText: String? = null,
+ contentDescription: String = "",
+) {
+ AvatarURL(
+ modifier = modifier,
+ url = user?.avatarUrl,
+ fallbackText = user?.toMatrixItem()?.firstLetterOfDisplayName(),
+ contentDescription = contentDescription,
+ )
+}
diff --git a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromUserId.kt b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUserId.kt
similarity index 56%
rename from app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromUserId.kt
rename to app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUserId.kt
index 1a1ea85..2d1f7a7 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/avatar/AvatarFromUserId.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUserId.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix.avatar
+package eu.steffo.twom.composables.avatar
import android.util.Log
import androidx.compose.runtime.Composable
@@ -9,11 +9,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
-import eu.steffo.twom.matrix.LocalSession
+import eu.steffo.twom.composables.matrix.LocalSession
@Composable
@Preview(widthDp = 40, heightDp = 40)
-fun AvatarFromUserId(
+fun AvatarUserId(
modifier: Modifier = Modifier,
userId: String = "",
fallbackText: String = "?",
@@ -24,30 +24,24 @@ fun AvatarFromUserId(
LaunchedEffect(session, userId) GetAvatarUrl@{
if (session == null) {
- Log.d("UserAvatar", "Not doing anything, session is null.")
+ Log.d("AvatarUser", "Not doing anything, session is null.")
return@GetAvatarUrl
}
+
if (userId.isEmpty()) {
- Log.d("UserAvatar", "Not doing anything, userId is empty.")
+ Log.d("AvatarUser", "Not doing anything, userId is empty.")
return@GetAvatarUrl
}
- Log.d("UserAvatar", "Retrieving avatar url for: $userId...")
+
+ Log.d("AvatarUser", "Retrieving avatar url for: $userId...")
avatarUrl = session.profileService().getAvatarUrl(userId).getOrNull()
- Log.d("UserAvatar", "Retrieved avatar url for $userId: $avatarUrl")
+ Log.d("AvatarUser", "Retrieved avatar url for $userId: $avatarUrl")
}
- if (avatarUrl == null) {
- AvatarFromDefault(
- modifier = modifier,
- fallbackText = fallbackText,
- contentDescription = contentDescription,
- )
- } else {
- AvatarFromURL(
- modifier = modifier,
- url = avatarUrl!!,
- fallbackText = fallbackText,
- contentDescription = contentDescription,
- )
- }
-}
\ No newline at end of file
+ AvatarURL(
+ modifier = modifier,
+ url = avatarUrl,
+ fallbackText = fallbackText,
+ contentDescription = contentDescription,
+ )
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomForm.kt b/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomForm.kt
index a40bb0a..f06842e 100644
--- a/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomForm.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomForm.kt
@@ -25,7 +25,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import eu.steffo.twom.R
import eu.steffo.twom.composables.avatar.AvatarPicker
-import eu.steffo.twom.theme.TwoMPadding
+import eu.steffo.twom.composables.theme.basePadding
import eu.steffo.twom.utils.BitmapUtilities
@Composable
@@ -39,7 +39,7 @@ fun CreateRoomForm(
var avatarUri by rememberSaveable { mutableStateOf(null) }
Column(modifier) {
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
val avatarContentDescription = stringResource(R.string.create_avatar_label)
AvatarPicker(
modifier = Modifier
@@ -67,7 +67,7 @@ fun CreateRoomForm(
)
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
TextField(
modifier = Modifier
.height(180.dp)
@@ -80,7 +80,7 @@ fun CreateRoomForm(
)
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
Button(
modifier = Modifier
.fillMaxWidth(),
diff --git a/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomScaffold.kt b/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomScaffold.kt
index ce969f8..9516d9c 100644
--- a/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomScaffold.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/createroom/CreateRoomScaffold.kt
@@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.activities.CreateRoomActivity
-import eu.steffo.twom.theme.TwoMTheme
+import eu.steffo.twom.composables.theme.TwoMTheme
@Composable
@Preview
diff --git a/app/src/main/java/eu/steffo/twom/composables/errorhandling/ErrorIconButton.kt b/app/src/main/java/eu/steffo/twom/composables/errorhandling/ErrorIconButton.kt
new file mode 100644
index 0000000..c263ea4
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/errorhandling/ErrorIconButton.kt
@@ -0,0 +1,66 @@
+package eu.steffo.twom.composables.errorhandling
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Warning
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import eu.steffo.twom.R
+
+
+@Composable
+@Preview
+fun ErrorIconButton(
+ message: String = "Placeholder",
+) {
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ IconButton(
+ onClick = { expanded = true },
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Warning,
+ contentDescription = stringResource(R.string.error),
+ tint = MaterialTheme.colorScheme.error,
+ )
+ }
+
+ if (expanded) {
+ AlertDialog(
+ onDismissRequest = { expanded = false },
+ icon = {
+ Icon(
+ imageVector = Icons.Filled.Warning,
+ contentDescription = stringResource(R.string.error),
+ )
+ },
+ title = {
+ Text(stringResource(R.string.error))
+ },
+ text = {
+ Text(message)
+ },
+ confirmButton = {
+ TextButton(
+ onClick = { expanded = false },
+ ) {
+ Text(stringResource(R.string.close))
+ }
+ },
+ containerColor = MaterialTheme.colorScheme.errorContainer,
+ iconContentColor = MaterialTheme.colorScheme.onErrorContainer,
+ titleContentColor = MaterialTheme.colorScheme.onErrorContainer,
+ textContentColor = MaterialTheme.colorScheme.onErrorContainer,
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/errorhandling/LocalizableError.kt b/app/src/main/java/eu/steffo/twom/composables/errorhandling/LocalizableError.kt
new file mode 100644
index 0000000..4f05014
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/errorhandling/LocalizableError.kt
@@ -0,0 +1,56 @@
+package eu.steffo.twom.composables.errorhandling
+
+import androidx.annotation.StringRes
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+
+class LocalizableError {
+ @StringRes
+ var stringResourceId: Int? = null
+ private set
+
+ var throwable: Throwable? = null
+ private set
+
+ fun set(stringResourceId: Int) {
+ this.stringResourceId = stringResourceId
+ this.throwable = null
+ }
+
+ fun set(stringResourceId: Int, throwable: Throwable) {
+ this.stringResourceId = stringResourceId
+ this.throwable = throwable
+ }
+
+ fun clear() {
+ this.stringResourceId = null
+ this.throwable = null
+ }
+
+ fun occurred(): Boolean {
+ return stringResourceId != null
+ }
+
+ @Composable
+ fun renderString(): String? {
+ val stringResourceId = this.stringResourceId
+ val throwable = this.throwable
+
+ return if (stringResourceId == null) {
+ null
+ } else if (throwable == null) {
+ stringResource(stringResourceId)
+ } else {
+ stringResource(stringResourceId, throwable.toString())
+ }
+ }
+
+ @Composable
+ fun Show(contents: @Composable (rendered: String) -> Unit) {
+ val rendered = renderString()
+
+ if (rendered != null) {
+ contents(rendered)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserContent.kt b/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserContent.kt
new file mode 100644
index 0000000..59ee7e7
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserContent.kt
@@ -0,0 +1,47 @@
+package eu.steffo.twom.composables.inviteuser
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.theme.basePadding
+
+@Composable
+fun InviteUserContent() {
+ Row(Modifier.basePadding()) {
+ Text(
+ text = stringResource(R.string.room_invite_title),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+
+ Row(Modifier.basePadding()) {
+ InviteUserForm(
+ /*
+ onConfirm = {
+ scope.launch SendInvite@{
+ isSendingInvite = true
+ errorInvite = null
+
+ Log.d("Room", "Sending invite to `$it`...")
+
+ try {
+ room.membershipService().invite(it)
+ } catch (error: Throwable) {
+ Log.e("Room", "Failed to send invite to `$it`: $error")
+ errorInvite = error
+ isSendingInvite = false
+ return@SendInvite
+ }
+
+ Log.d("Room", "Successfully sent invite to `$it`!")
+ isSendingInvite = false
+ }
+ }
+ */
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserForm.kt b/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserForm.kt
new file mode 100644
index 0000000..d0782d5
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserForm.kt
@@ -0,0 +1,54 @@
+package eu.steffo.twom.composables.inviteuser
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import eu.steffo.twom.R
+
+@Composable
+@Preview
+fun InviteUserForm(
+ onConfirm: (userId: String) -> Unit = {},
+) {
+ var value by rememberSaveable { mutableStateOf("") }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth(),
+ value = value,
+ onValueChange = { value = it },
+ singleLine = true,
+ shape = MaterialTheme.shapes.small,
+ placeholder = {
+ Text(
+ text = stringResource(R.string.room_invite_username_placeholder)
+ )
+ },
+ )
+
+ Button(
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxWidth(),
+ onClick = { onConfirm(value) },
+ shape = MaterialTheme.shapes.small,
+ // FIXME: Maybe I should validate usernames with a regex
+ enabled = (value.contains("@") && value.contains(":")),
+ ) {
+ Text(
+ text = stringResource(R.string.room_invite_button_label)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/login/LoginForm.kt b/app/src/main/java/eu/steffo/twom/composables/login/LoginForm.kt
index fcb4f6b..a8b5be5 100644
--- a/app/src/main/java/eu/steffo/twom/composables/login/LoginForm.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/login/LoginForm.kt
@@ -24,8 +24,8 @@ import eu.steffo.twom.R
import eu.steffo.twom.composables.errorhandling.ErrorText
import eu.steffo.twom.composables.errorhandling.LocalizableError
import eu.steffo.twom.composables.fields.PasswordField
-import eu.steffo.twom.matrix.TwoMMatrix
-import eu.steffo.twom.theme.TwoMPadding
+import eu.steffo.twom.composables.theme.basePadding
+import eu.steffo.twom.utils.TwoMGlobals
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
@@ -66,7 +66,7 @@ fun LoginForm(
Log.d("Login", "Getting authentication service...")
loginStep = LoginStep.SERVICE
- val auth = TwoMMatrix.matrix.authenticationService()
+ val auth = TwoMGlobals.matrix.authenticationService()
Log.d("Login", "Resetting authentication service...")
auth.reset()
@@ -174,10 +174,10 @@ fun LoginForm(
progress = loginStep.step.toFloat() / LoginStep.DONE.step.toFloat(),
color = if (error.occurred()) MaterialTheme.colorScheme.error else ProgressIndicatorDefaults.linearColor
)
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
Text(LocalContext.current.getString(R.string.login_text))
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
TextField(
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -194,7 +194,7 @@ fun LoginForm(
},
)
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
PasswordField(
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -211,7 +211,7 @@ fun LoginForm(
},
)
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
Button(
modifier = Modifier.fillMaxWidth(),
enabled = (username != "" && (loginStep == LoginStep.NONE || error.occurred())),
@@ -223,7 +223,7 @@ fun LoginForm(
}
}
error.Show {
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
ErrorText(it)
}
}
diff --git a/app/src/main/java/eu/steffo/twom/composables/login/LoginScaffold.kt b/app/src/main/java/eu/steffo/twom/composables/login/LoginScaffold.kt
index 19f4bb8..e0015f9 100644
--- a/app/src/main/java/eu/steffo/twom/composables/login/LoginScaffold.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/login/LoginScaffold.kt
@@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
-import eu.steffo.twom.theme.TwoMTheme
+import eu.steffo.twom.composables.theme.TwoMTheme
import org.matrix.android.sdk.api.session.Session
diff --git a/app/src/main/java/eu/steffo/twom/composables/main/AccountIconButton.kt b/app/src/main/java/eu/steffo/twom/composables/main/AccountIconButton.kt
index 68cd56f..16e05e9 100644
--- a/app/src/main/java/eu/steffo/twom/composables/main/AccountIconButton.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/AccountIconButton.kt
@@ -21,8 +21,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.R
import eu.steffo.twom.activities.LoginActivity
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.matrix.avatar.AvatarFromUserId
+import eu.steffo.twom.composables.avatar.AvatarUserId
+import eu.steffo.twom.composables.matrix.LocalSession
@Composable
@Preview(showBackground = true)
@@ -35,7 +35,11 @@ fun AccountIconButton(
var expanded by remember { mutableStateOf(false) }
val loginLauncher =
- rememberLauncherForActivityResult(LoginActivity.Contract()) { processLogin() }
+ rememberLauncherForActivityResult(LoginActivity.Contract()) {
+ if (it != null) {
+ processLogin()
+ }
+ }
Box(modifier) {
IconButton(
@@ -47,7 +51,7 @@ fun AccountIconButton(
contentDescription = LocalContext.current.getString(R.string.main_account_label),
)
} else {
- AvatarFromUserId(
+ AvatarUserId(
userId = session.myUserId,
contentDescription = LocalContext.current.getString(R.string.main_account_label),
)
@@ -60,7 +64,7 @@ fun AccountIconButton(
if (session == null) {
DropdownMenuItem(
text = {
- Text(stringResource(id = R.string.main_account_login_text))
+ Text(stringResource(R.string.main_account_login_text))
},
onClick = {
expanded = false
@@ -70,7 +74,7 @@ fun AccountIconButton(
} else {
DropdownMenuItem(
text = {
- Text(stringResource(id = R.string.main_account_logout_text))
+ Text(stringResource(R.string.main_account_logout_text))
},
onClick = {
expanded = false
@@ -80,5 +84,4 @@ fun AccountIconButton(
}
}
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/main/CreateRoomFAB.kt b/app/src/main/java/eu/steffo/twom/composables/main/CreateRoomFAB.kt
index e3c2213..50ecbad 100644
--- a/app/src/main/java/eu/steffo/twom/composables/main/CreateRoomFAB.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/CreateRoomFAB.kt
@@ -1,5 +1,7 @@
package eu.steffo.twom.composables.main
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.launch
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.ExtendedFloatingActionButton
@@ -10,16 +12,24 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.R
+import eu.steffo.twom.activities.CreateRoomActivity
@Composable
@Preview
fun CreateRoomFAB(
modifier: Modifier = Modifier,
- onClick: () -> Unit = {},
+ onCreateParamsSelected: (name: String, description: String, avatarUri: String?) -> Unit = { _, _, _ -> },
) {
+ val launcher =
+ rememberLauncherForActivityResult(CreateRoomActivity.Contract()) {
+ if (it != null) {
+ onCreateParamsSelected(it.name, it.description, it.avatarUri)
+ }
+ }
+
ExtendedFloatingActionButton(
modifier = modifier,
- onClick = onClick,
+ onClick = { launcher.launch() },
icon = {
Icon(
Icons.Filled.Add,
diff --git a/app/src/main/java/eu/steffo/twom/composables/main/MainContentLoggedIn.kt b/app/src/main/java/eu/steffo/twom/composables/main/MainContentLoggedIn.kt
index bb2ee84..48b65e8 100644
--- a/app/src/main/java/eu/steffo/twom/composables/main/MainContentLoggedIn.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/MainContentLoggedIn.kt
@@ -8,11 +8,10 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.steffo.twom.R
-import eu.steffo.twom.main.RoomListItem
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.matrix.TwoMMatrix
-import eu.steffo.twom.theme.ErrorText
-import eu.steffo.twom.theme.TwoMPadding
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.matrix.LocalSession
+import eu.steffo.twom.composables.theme.basePadding
+import eu.steffo.twom.utils.TwoMGlobals
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
@@ -29,19 +28,19 @@ fun MainContentLoggedIn(
val roomSummaries by session.roomService().getRoomSummariesLive(
roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN)
- this.includeType = listOf(TwoMMatrix.ROOM_TYPE)
+ this.includeType = listOf(TwoMGlobals.ROOM_TYPE)
}
).observeAsState()
Column(modifier) {
if (roomSummaries == null) {
Text(
- modifier = TwoMPadding.base,
+ modifier = Modifier.basePadding(),
text = stringResource(R.string.loading)
)
} else if (roomSummaries!!.isEmpty()) {
Text(
- modifier = TwoMPadding.base,
+ modifier = Modifier.basePadding(),
text = stringResource(R.string.main_roomlist_empty_text)
)
} else {
diff --git a/app/src/main/java/eu/steffo/twom/composables/main/MainContentNotLoggedIn.kt b/app/src/main/java/eu/steffo/twom/composables/main/MainContentNotLoggedIn.kt
index 966dc0b..9531b04 100644
--- a/app/src/main/java/eu/steffo/twom/composables/main/MainContentNotLoggedIn.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/MainContentNotLoggedIn.kt
@@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.R
-import eu.steffo.twom.theme.TwoMPadding
+import eu.steffo.twom.composables.theme.basePadding
@Composable
@Preview(showBackground = true)
@@ -17,10 +17,10 @@ fun MainContentNotLoggedIn(
onClickLogin: () -> Unit = {},
) {
Column(modifier) {
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
Text(LocalContext.current.getString(R.string.main_notloggedin_text_1))
}
- Row(TwoMPadding.base) {
+ Row(Modifier.basePadding()) {
Text(LocalContext.current.getString(R.string.main_notloggedin_text_2))
}
}
diff --git a/app/src/main/java/eu/steffo/twom/composables/main/MainScaffold.kt b/app/src/main/java/eu/steffo/twom/composables/main/MainScaffold.kt
index 8acc0bd..59c2a04 100644
--- a/app/src/main/java/eu/steffo/twom/composables/main/MainScaffold.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/MainScaffold.kt
@@ -6,8 +6,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.theme.TwoMTheme
+import eu.steffo.twom.composables.matrix.LocalSession
+import eu.steffo.twom.composables.theme.TwoMTheme
import org.matrix.android.sdk.api.session.Session
@Composable
@@ -15,6 +15,7 @@ import org.matrix.android.sdk.api.session.Session
fun MainScaffold(
processLogin: () -> Unit = {},
processLogout: () -> Unit = {},
+ processCreate: (name: String, description: String, avatarUri: String?) -> Unit = { _, _, _ -> },
session: Session? = null,
) {
TwoMTheme {
@@ -27,7 +28,9 @@ fun MainScaffold(
)
},
floatingActionButton = {
- CreateRoomFAB()
+ CreateRoomFAB(
+ onCreateParamsSelected = processCreate,
+ )
},
content = {
if (session == null) {
diff --git a/app/src/main/java/eu/steffo/twom/main/RoomListItem.kt b/app/src/main/java/eu/steffo/twom/composables/main/RoomListItem.kt
similarity index 88%
rename from app/src/main/java/eu/steffo/twom/main/RoomListItem.kt
rename to app/src/main/java/eu/steffo/twom/composables/main/RoomListItem.kt
index 79584e2..ddf8f12 100644
--- a/app/src/main/java/eu/steffo/twom/main/RoomListItem.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/main/RoomListItem.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.main
+package eu.steffo.twom.composables.main
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -23,11 +23,11 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.steffo.twom.R
-import eu.steffo.twom.activities.RoomActivity
+import eu.steffo.twom.activities.ViewRoomActivity
+import eu.steffo.twom.composables.avatar.AvatarURL
+import eu.steffo.twom.composables.errorhandling.ErrorText
import eu.steffo.twom.composables.errorhandling.LocalizableError
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.matrix.avatar.AvatarFromURL
-import eu.steffo.twom.theme.ErrorText
+import eu.steffo.twom.composables.matrix.LocalSession
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -50,11 +50,11 @@ fun RoomListItem(
var expanded by rememberSaveable { mutableStateOf(false) }
val error by remember { mutableStateOf(LocalizableError()) }
- val roomActivityLauncher = rememberLauncherForActivityResult(RoomActivity.Contract()) {}
+ val viewRoomActivityLauncher = rememberLauncherForActivityResult(ViewRoomActivity.Contract()) {}
fun openRoom() {
Log.i("Main", "Opening room `$roomId`...")
- roomActivityLauncher.launch(roomId)
+ viewRoomActivityLauncher.launch(roomId)
}
suspend fun leaveRoom() {
@@ -83,7 +83,7 @@ fun RoomListItem(
.size(40.dp)
.clip(MaterialTheme.shapes.medium)
) {
- AvatarFromURL(
+ AvatarURL(
// FIXME: URL can appearently be set before the image is available on the homeserver
url = roomSummary.avatarUrl,
)
diff --git a/app/src/main/java/eu/steffo/twom/matrix/LocalSession.kt b/app/src/main/java/eu/steffo/twom/composables/matrix/LocalSession.kt
similarity index 80%
rename from app/src/main/java/eu/steffo/twom/matrix/LocalSession.kt
rename to app/src/main/java/eu/steffo/twom/composables/matrix/LocalSession.kt
index 0441c04..a60d1fe 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/LocalSession.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/matrix/LocalSession.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix
+package eu.steffo.twom.composables.matrix
import androidx.compose.runtime.staticCompositionLocalOf
import org.matrix.android.sdk.api.session.Session
diff --git a/app/src/main/java/eu/steffo/twom/composables/navigation/BackIconButton.kt b/app/src/main/java/eu/steffo/twom/composables/navigation/BackIconButton.kt
new file mode 100644
index 0000000..33b7d99
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/navigation/BackIconButton.kt
@@ -0,0 +1,34 @@
+package eu.steffo.twom.composables.navigation
+
+import android.app.Activity
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import eu.steffo.twom.R
+
+@Composable
+fun BackIconButton(
+ modifier: Modifier = Modifier,
+) {
+ val context = LocalContext.current
+ val activity = context as Activity
+
+ fun cancelActivity() {
+ activity.setResult(Activity.RESULT_CANCELED)
+ activity.finish()
+ }
+
+ IconButton(
+ modifier = modifier,
+ onClick = { cancelActivity() }
+ ) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = LocalContext.current.getString(R.string.back)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/LaterColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/LaterColorRole.kt
new file mode 100644
index 0000000..c40fae1
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/LaterColorRole.kt
@@ -0,0 +1,15 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.ui.graphics.Color
+
+object LaterColorRole : StaticColorRole {
+ override val lightColor = Color(0xFF00658B)
+ override val lightOnColor = Color(0xFFFFFFFF)
+ override val lightContainerColor = Color(0xFFC4E7FF)
+ override val lightOnContainerColor = Color(0xFF001E2C)
+
+ override val darkColor = Color(0xFF7DD0FF)
+ override val darkOnColor = Color(0xFF00344A)
+ override val darkContainerColor = Color(0xFF004C69)
+ override val darkOnContainerColor = Color(0xFFC4E7FF)
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/MaybeColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/MaybeColorRole.kt
new file mode 100644
index 0000000..a163523
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/MaybeColorRole.kt
@@ -0,0 +1,15 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.ui.graphics.Color
+
+object MaybeColorRole : StaticColorRole {
+ override val lightColor = Color(0xFF765B00)
+ override val lightOnColor = Color(0xFFFFFFFF)
+ override val lightContainerColor = Color(0xFFFFDF94)
+ override val lightOnContainerColor = Color(0xFF241A00)
+
+ override val darkColor = Color(0xFFEDC148)
+ override val darkOnColor = Color(0xFF3E2E00)
+ override val darkContainerColor = Color(0xFF594400)
+ override val darkOnContainerColor = Color(0xFFFFDF94)
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/NowayColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/NowayColorRole.kt
new file mode 100644
index 0000000..bb03068
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/NowayColorRole.kt
@@ -0,0 +1,15 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.ui.graphics.Color
+
+object NowayColorRole : StaticColorRole {
+ override val lightColor = Color(0xFFAB3520)
+ override val lightOnColor = Color(0xFFFFFFFF)
+ override val lightContainerColor = Color(0xFFFFDAD3)
+ override val lightOnContainerColor = Color(0xFF3F0400)
+
+ override val darkColor = Color(0xFFFFB4A5)
+ override val darkOnColor = Color(0xFF650A00)
+ override val darkContainerColor = Color(0xFF891D0A)
+ override val darkOnContainerColor = Color(0xFFFFDAD3)
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/NullishColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/NullishColorRole.kt
new file mode 100644
index 0000000..7dbd2e8
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/NullishColorRole.kt
@@ -0,0 +1,15 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.ui.graphics.Color
+
+object NullishColorRole : StaticColorRole {
+ override val lightColor = Color(0xFF666666)
+ override val lightOnColor = Color(0xFFFFFFFF)
+ override val lightContainerColor = Color(0xFFE6E6E6)
+ override val lightOnContainerColor = Color(0xFF222222)
+
+ override val darkColor = Color(0xFFDDDDDD)
+ override val darkOnColor = Color(0xFF333333)
+ override val darkContainerColor = Color(0xFF555555)
+ override val darkOnContainerColor = Color(0xFFFFFFFF)
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/StaticColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/StaticColorRole.kt
new file mode 100644
index 0000000..232107b
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/StaticColorRole.kt
@@ -0,0 +1,69 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import com.google.android.material.color.MaterialColors
+
+
+interface StaticColorRole {
+ val lightColor: Color
+ val lightOnColor: Color
+ val lightContainerColor: Color
+ val lightOnContainerColor: Color
+
+ val darkColor: Color
+ val darkOnColor: Color
+ val darkContainerColor: Color
+ val darkOnContainerColor: Color
+
+ @Composable
+ fun harmonize(color: Color): Color {
+ val ctx = LocalContext.current
+ val colorArgb = color.toArgb()
+ val colorArgbHarmonized = MaterialColors.harmonizeWithPrimary(ctx, colorArgb)
+ return Color(colorArgbHarmonized)
+ }
+
+ @Composable
+ fun color(): Color {
+ return harmonize(
+ when (isSystemInDarkTheme()) {
+ false -> lightColor
+ true -> darkColor
+ }
+ )
+ }
+
+ @Composable
+ fun onColor(): Color {
+ return harmonize(
+ when (isSystemInDarkTheme()) {
+ false -> lightOnColor
+ true -> darkOnColor
+ }
+ )
+ }
+
+ @Composable
+ fun containerColor(): Color {
+ return harmonize(
+ when (isSystemInDarkTheme()) {
+ false -> lightContainerColor
+ true -> darkContainerColor
+ }
+ )
+ }
+
+ @Composable
+ fun onContainerColor(): Color {
+ return harmonize(
+ when (isSystemInDarkTheme()) {
+ false -> lightOnContainerColor
+ true -> darkOnContainerColor
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/SureColorRole.kt b/app/src/main/java/eu/steffo/twom/composables/theme/SureColorRole.kt
new file mode 100644
index 0000000..4ae03c9
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/SureColorRole.kt
@@ -0,0 +1,15 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.ui.graphics.Color
+
+object SureColorRole : StaticColorRole {
+ override val lightColor = Color(0xFF006E2C)
+ override val lightOnColor = Color(0xFFFFFFFF)
+ override val lightContainerColor = Color(0xFF7FFC95)
+ override val lightOnContainerColor = Color(0xFF002108)
+
+ override val darkColor = Color(0xFF62DF7C)
+ override val darkOnColor = Color(0xFF003913)
+ override val darkContainerColor = Color(0xFF00531F)
+ override val darkOnContainerColor = Color(0xFF7FFC95)
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/theme/TwoMPadding.kt b/app/src/main/java/eu/steffo/twom/composables/theme/TwoMPadding.kt
new file mode 100644
index 0000000..aab87b3
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/TwoMPadding.kt
@@ -0,0 +1,14 @@
+package eu.steffo.twom.composables.theme
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+
+fun Modifier.basePadding(): Modifier {
+ return this.padding(all = 10.dp)
+}
+
+fun Modifier.chipPadding(): Modifier {
+ return this.padding(start = 2.5.dp, end = 2.5.dp)
+}
diff --git a/app/src/main/java/eu/steffo/twom/theme/TwoMTheme.kt b/app/src/main/java/eu/steffo/twom/composables/theme/TwoMTheme.kt
similarity index 93%
rename from app/src/main/java/eu/steffo/twom/theme/TwoMTheme.kt
rename to app/src/main/java/eu/steffo/twom/composables/theme/TwoMTheme.kt
index 545cdc6..753bfdd 100644
--- a/app/src/main/java/eu/steffo/twom/theme/TwoMTheme.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/theme/TwoMTheme.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.theme
+package eu.steffo.twom.composables.theme
import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
@@ -18,6 +18,7 @@ import androidx.core.view.WindowCompat
fun TwoMTheme(
content: @Composable () -> Unit
) {
+ val view = LocalView.current
val context = LocalContext.current
val darkTheme = isSystemInDarkTheme()
@@ -27,21 +28,21 @@ fun TwoMTheme(
}
val typography = Typography()
- MaterialTheme(
- colorScheme = colorScheme,
- typography = typography,
- content = content
- )
-
- val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
- val window = (view.context as Activity).window
+ val window = (context as Activity).window
window.statusBarColor = colorScheme.surface.toArgb()
window.navigationBarColor = colorScheme.surface.toArgb()
+
val insets = WindowCompat.getInsetsController(window, view)
insets.isAppearanceLightStatusBars = !darkTheme
insets.isAppearanceLightNavigationBars = !darkTheme
}
}
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = typography,
+ content = content
+ )
}
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/InviteFAB.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/InviteFAB.kt
new file mode 100644
index 0000000..e103d2d
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/InviteFAB.kt
@@ -0,0 +1,43 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.launch
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.ExtendedFloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import eu.steffo.twom.R
+import eu.steffo.twom.activities.InviteUserActivity
+
+@Composable
+@Preview
+fun InviteFAB(
+ modifier: Modifier = Modifier,
+ onUserSelected: (userId: String) -> Unit = {},
+) {
+ val launcher =
+ rememberLauncherForActivityResult(InviteUserActivity.Contract()) {
+ if (it != null) {
+ onUserSelected(it)
+ }
+ }
+
+ ExtendedFloatingActionButton(
+ modifier = modifier,
+ onClick = { launcher.launch() },
+ icon = {
+ Icon(
+ Icons.Filled.Add,
+ contentDescription = null
+ )
+ },
+ text = {
+ Text(stringResource(R.string.room_invite_button_label))
+ }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/LocalRoom.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoom.kt
similarity index 82%
rename from app/src/main/java/eu/steffo/twom/room/LocalRoom.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoom.kt
index e0fa8e8..412fc4e 100644
--- a/app/src/main/java/eu/steffo/twom/room/LocalRoom.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoom.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.runtime.staticCompositionLocalOf
import org.matrix.android.sdk.api.session.room.Room
diff --git a/app/src/main/java/eu/steffo/twom/room/LocalRoomSummary.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoomSummary.kt
similarity index 85%
rename from app/src/main/java/eu/steffo/twom/room/LocalRoomSummary.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoomSummary.kt
index 0775145..0ace9c5 100644
--- a/app/src/main/java/eu/steffo/twom/room/LocalRoomSummary.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/LocalRoomSummary.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.runtime.staticCompositionLocalOf
import org.matrix.android.sdk.api.session.room.model.RoomSummary
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/MemberListItem.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/MemberListItem.kt
new file mode 100644
index 0000000..569f5cb
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/MemberListItem.kt
@@ -0,0 +1,123 @@
+package eu.steffo.twom.composables.viewroom
+
+import android.util.Log
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.avatar.AvatarUser
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.matrix.LocalSession
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.user.model.User
+import kotlin.jvm.optionals.getOrNull
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun MemberListItem(
+ member: RoomMemberSummary,
+ modifier: Modifier = Modifier,
+) {
+ val session = LocalSession.current
+ if (session == null) {
+ ErrorText(stringResource(R.string.error_session_missing))
+ return
+ }
+
+ val roomRequest = LocalRoom.current
+ if (roomRequest == null) {
+ ErrorText(stringResource(R.string.room_error_room_missing))
+ return
+ }
+
+ val room = roomRequest.getOrNull()
+ if (room == null) {
+ ErrorText(stringResource(R.string.room_error_room_notfound))
+ return
+ }
+
+ // TODO: Is this necessary?
+
+ var user by remember { mutableStateOf(null) }
+
+ LaunchedEffect(session, member.userId) {
+ val memberId = member.userId
+ Log.d("UserListItem", "Resolving user: $memberId")
+ user = session.userService().resolveUser(memberId)
+ Log.d("UserListItem", "Resolved user: $memberId")
+ }
+
+ val rsvp = observeRSVP(room = room, member = member)
+
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ ListItem(
+ modifier = modifier.combinedClickable(
+ onClick = {},
+ onLongClick = { expanded = true },
+ ),
+ headlineContent = {
+ Text(
+ text = user?.displayName ?: stringResource(R.string.user_unresolved_name),
+ )
+ },
+ leadingContent = {
+ Box(
+ Modifier
+ .padding(end = 10.dp)
+ .size(40.dp)
+ .clip(MaterialTheme.shapes.extraLarge)
+ ) {
+ AvatarUser(
+ user = user,
+ )
+ }
+ },
+ trailingContent = {
+ Icon(
+ imageVector = rsvp.answer.icon,
+ contentDescription = rsvp.answer.toResponse(),
+ tint = rsvp.answer.staticColorRole.color(),
+ )
+ },
+ supportingContent = {
+ if (rsvp.comment != "") {
+ Text(
+ text = rsvp.comment,
+ color = rsvp.answer.staticColorRole.color(),
+ )
+ }
+ },
+ )
+
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false }
+ ) {
+ DropdownMenuItem(
+ text = {
+ Text(stringResource(R.string.room_uninvite_label))
+ },
+ onClick = { expanded = false }
+ )
+ }
+}
diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPAnswerFilterChip.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChip.kt
similarity index 58%
rename from app/src/main/java/eu/steffo/twom/room/RSVPAnswerFilterChip.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChip.kt
index d276a1d..64d653f 100644
--- a/app/src/main/java/eu/steffo/twom/room/RSVPAnswerFilterChip.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChip.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
@@ -8,47 +8,45 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import eu.steffo.twom.utils.RSVPAnswer
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun RSVPAnswerFilterChip(
+@Preview
+fun RSVPChip(
modifier: Modifier = Modifier,
- representing: RSVPAnswer,
selected: Boolean = false,
onClick: () -> Unit = {},
+ representedAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
) {
- val icon = representing.toIcon()
- val colorRole = representing.toStaticColorRole()
- val labelResourceId = representing.toLabelResourceId()
-
FilterChip(
modifier = modifier,
selected = selected,
onClick = onClick,
leadingIcon = {
Icon(
- imageVector = icon,
+ imageVector = representedAnswer.icon,
contentDescription = null,
)
},
label = {
Text(
- text = stringResource(labelResourceId),
+ text = representedAnswer.toLabel() ?: "[missing label]",
style = MaterialTheme.typography.bodyLarge,
)
},
colors = FilterChipDefaults.filterChipColors(
- iconColor = colorRole.value,
- labelColor = colorRole.value,
- selectedContainerColor = colorRole.valueContainer,
- selectedLeadingIconColor = colorRole.onValueContainer,
- selectedLabelColor = colorRole.onValueContainer,
+ iconColor = representedAnswer.staticColorRole.color(),
+ labelColor = representedAnswer.staticColorRole.color(),
+ selectedContainerColor = representedAnswer.staticColorRole.containerColor(),
+ selectedLeadingIconColor = representedAnswer.staticColorRole.onContainerColor(),
+ selectedLabelColor = representedAnswer.staticColorRole.onContainerColor(),
),
border = FilterChipDefaults.filterChipBorder(
borderColor = MaterialTheme.colorScheme.surfaceVariant,
- selectedBorderColor = colorRole.onValueContainer,
+ selectedBorderColor = representedAnswer.staticColorRole.onContainerColor(),
borderWidth = 1.dp,
selectedBorderWidth = 1.dp,
)
diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPAnswerSelectRow.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChipRow.kt
similarity index 70%
rename from app/src/main/java/eu/steffo/twom/room/RSVPAnswerSelectRow.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChipRow.kt
index 3b946a0..1c82e64 100644
--- a/app/src/main/java/eu/steffo/twom/room/RSVPAnswerSelectRow.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPChipRow.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
@@ -9,11 +9,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import eu.steffo.twom.theme.TwoMPadding
+import eu.steffo.twom.composables.theme.chipPadding
+import eu.steffo.twom.utils.RSVPAnswer
@Composable
@Preview
-fun RSVPAnswerSelectRow(
+fun RSVPChipRow(
modifier: Modifier = Modifier,
value: RSVPAnswer = RSVPAnswer.UNKNOWN,
onChange: (answer: RSVPAnswer) -> Unit = {},
@@ -37,27 +38,27 @@ fun RSVPAnswerSelectRow(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp)
) {
- RSVPAnswerFilterChip(
- modifier = TwoMPadding.chips,
- representing = RSVPAnswer.SURE,
+ RSVPChip(
+ modifier = Modifier.chipPadding(),
+ representedAnswer = RSVPAnswer.SURE,
selected = (value == RSVPAnswer.SURE),
onClick = toggleSwitch(RSVPAnswer.SURE)
)
- RSVPAnswerFilterChip(
- modifier = TwoMPadding.chips,
- representing = RSVPAnswer.LATER,
+ RSVPChip(
+ modifier = Modifier.chipPadding(),
+ representedAnswer = RSVPAnswer.LATER,
selected = (value == RSVPAnswer.LATER),
onClick = toggleSwitch(RSVPAnswer.LATER)
)
- RSVPAnswerFilterChip(
- modifier = TwoMPadding.chips,
- representing = RSVPAnswer.MAYBE,
+ RSVPChip(
+ modifier = Modifier.chipPadding(),
+ representedAnswer = RSVPAnswer.MAYBE,
selected = (value == RSVPAnswer.MAYBE),
onClick = toggleSwitch(RSVPAnswer.MAYBE)
)
- RSVPAnswerFilterChip(
- modifier = TwoMPadding.chips,
- representing = RSVPAnswer.NOWAY,
+ RSVPChip(
+ modifier = Modifier.chipPadding(),
+ representedAnswer = RSVPAnswer.NOWAY,
selected = (value == RSVPAnswer.NOWAY),
onClick = toggleSwitch(RSVPAnswer.NOWAY)
)
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPCommentField.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPCommentField.kt
new file mode 100644
index 0000000..c50eeb0
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPCommentField.kt
@@ -0,0 +1,40 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import eu.steffo.twom.utils.RSVPAnswer
+
+@Composable
+@Preview
+fun RSVPCommentField(
+ modifier: Modifier = Modifier,
+ value: String = "",
+ onChange: (value: String) -> Unit = {},
+ currentAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
+) {
+ OutlinedTextField(
+ modifier = modifier,
+ value = value,
+ onValueChange = onChange,
+ singleLine = true,
+ shape = MaterialTheme.shapes.small,
+ placeholder = {
+ Text(currentAnswer.toCommentPlaceholder())
+ },
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = currentAnswer.staticColorRole.containerColor(),
+ unfocusedContainerColor = currentAnswer.staticColorRole.containerColor(),
+ focusedTextColor = currentAnswer.staticColorRole.onContainerColor(),
+ unfocusedTextColor = currentAnswer.staticColorRole.onContainerColor(),
+ focusedBorderColor = currentAnswer.staticColorRole.onContainerColor(),
+ unfocusedBorderColor = currentAnswer.staticColorRole.onContainerColor()
+ .copy(alpha = 0.3f),
+ cursorColor = currentAnswer.staticColorRole.onContainerColor(),
+ )
+ )
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPForm.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPForm.kt
new file mode 100644
index 0000000..81583e6
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPForm.kt
@@ -0,0 +1,46 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import eu.steffo.twom.utils.RSVP
+import eu.steffo.twom.utils.RSVPAnswer
+
+@Composable
+fun RSVPForm(
+ published: RSVP,
+ onRequestPublish: (newAnswer: RSVPAnswer, newComment: String) -> Unit = { _, _ -> },
+ isPublishRunning: Boolean = false,
+) {
+ var currentAnswer by rememberSaveable { mutableStateOf(published.answer) }
+ var currentComment by rememberSaveable { mutableStateOf(published.comment) }
+
+ val hasChanged = (currentAnswer != published.answer || currentComment != published.comment)
+
+ RSVPChipRow(
+ value = currentAnswer,
+ onChange = { currentAnswer = it },
+ )
+ RSVPCommentField(
+ modifier = Modifier
+ .padding(start = 10.dp, end = 10.dp)
+ .fillMaxWidth(),
+ value = currentComment,
+ onChange = { currentComment = it },
+ currentAnswer = currentAnswer,
+ )
+ RSVPUpdateButton(
+ modifier = Modifier
+ .padding(start = 10.dp, end = 10.dp, top = 4.dp, bottom = 4.dp)
+ .fillMaxWidth(),
+ onClick = { onRequestPublish(currentAnswer, currentComment) },
+ enabled = hasChanged && !isPublishRunning,
+ currentAnswer = currentAnswer,
+ )
+}
diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPUpdateButton.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPUpdateButton.kt
similarity index 69%
rename from app/src/main/java/eu/steffo/twom/room/RSVPUpdateButton.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPUpdateButton.kt
index 7569bb6..b519f30 100644
--- a/app/src/main/java/eu/steffo/twom/room/RSVPUpdateButton.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPUpdateButton.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.R
+import eu.steffo.twom.utils.RSVPAnswer
@Composable
@Preview
@@ -16,22 +17,20 @@ fun RSVPUpdateButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit = {},
- rsvpAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
+ currentAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
) {
- val colorRole = rsvpAnswer.toStaticColorRole()
-
Button(
modifier = modifier,
enabled = enabled,
onClick = onClick,
shape = MaterialTheme.shapes.small,
colors = ButtonDefaults.buttonColors(
- containerColor = colorRole.value,
- contentColor = colorRole.onValue,
+ containerColor = currentAnswer.staticColorRole.color(),
+ contentColor = currentAnswer.staticColorRole.onColor(),
)
) {
Text(
- text = stringResource(R.string.room_update_label)
+ text = stringResource(R.string.room_rsvp_update_label)
)
}
}
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/RoomIconButton.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/RoomIconButton.kt
new file mode 100644
index 0000000..0fd21ce
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/RoomIconButton.kt
@@ -0,0 +1,64 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.avatar.AvatarURL
+
+@Composable
+fun RoomIconButton(
+ modifier: Modifier = Modifier,
+ avatarUrl: String? = null,
+ canEdit: Boolean = true,
+) {
+ var expanded by remember { mutableStateOf(false) }
+
+ Box(modifier) {
+ IconButton(
+ onClick = { expanded = true },
+ ) {
+ AvatarURL(
+ url = avatarUrl,
+ contentDescription = LocalContext.current.getString(R.string.room_options_label),
+ )
+ }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ ) {
+ DropdownMenuItem(
+ text = {
+ Text(stringResource(R.string.room_options_zoom_text))
+ },
+ onClick = {
+ // TODO
+ expanded = false
+ }
+ )
+
+ if (canEdit) {
+ DropdownMenuItem(
+ text = {
+ Text(stringResource(R.string.room_options_edit_text))
+ },
+ onClick = {
+ // TODO
+ expanded = false
+ }
+ )
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomContent.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomContent.kt
new file mode 100644
index 0000000..6230893
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomContent.kt
@@ -0,0 +1,41 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.matrix.LocalSession
+
+
+@Composable
+fun ViewRoomContent(
+ modifier: Modifier = Modifier,
+) {
+ val scope = rememberCoroutineScope()
+
+ val session = LocalSession.current
+ if (session == null) {
+ ErrorText(stringResource(R.string.error_session_missing))
+ return
+ }
+
+ Box(
+ modifier = modifier
+ .verticalScroll(rememberScrollState())
+ ) {
+ Column(
+ modifier = Modifier.fillMaxHeight()
+ ) {
+ ViewRoomTopic()
+ ViewRoomForm()
+ ViewRoomMembers()
+ }
+ }
+}
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomForm.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomForm.kt
new file mode 100644
index 0000000..c2465ac
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomForm.kt
@@ -0,0 +1,127 @@
+package eu.steffo.twom.composables.viewroom
+
+import android.util.Log
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.errorhandling.LocalizableError
+import eu.steffo.twom.composables.matrix.LocalSession
+import eu.steffo.twom.composables.theme.basePadding
+import kotlinx.coroutines.launch
+import kotlin.jvm.optionals.getOrNull
+
+@Composable
+fun ViewRoomForm() {
+ val scope = rememberCoroutineScope()
+
+ val session = LocalSession.current
+ if (session == null) {
+ ErrorText(stringResource(R.string.error_session_missing))
+ return
+ }
+
+ val roomRequest = LocalRoom.current
+ if (roomRequest == null) {
+ ErrorText(stringResource(R.string.room_error_room_missing))
+ return
+ }
+
+ val room = roomRequest.getOrNull()
+ if (room == null) {
+ ErrorText(stringResource(R.string.room_error_room_notfound))
+ return
+ }
+
+ // FIXME: This breaks if the member is kicked from the chat
+ val member = room.membershipService().getRoomMember(session.myUserId)
+ if (member == null) {
+ ErrorText(stringResource(R.string.room_error_members_notfound))
+ return
+ }
+
+ val published = observeRSVP(room = room, member = member)
+
+ var isPublishRunning by rememberSaveable { mutableStateOf(false) }
+ val publishError by remember { mutableStateOf(LocalizableError()) }
+
+ Row(Modifier.basePadding()) {
+ Text(
+ text = stringResource(R.string.room_rsvp_title),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ RSVPForm(
+ published = published,
+ onRequestPublish = { answer, comment ->
+ isPublishRunning = true
+ publishError.clear()
+
+ scope.launch Publish@{
+ Log.d(
+ "ViewRoomForm",
+ "Updating RSVP with answer `$answer` and comment `$comment`..."
+ )
+ try {
+ room.stateService().sendStateEvent(
+ eventType = "eu.steffo.twom.rsvp",
+ stateKey = session.myUserId,
+ body = mapOf(
+ "answer" to answer.value,
+ "comment" to comment,
+ ),
+ )
+ } catch (e: Throwable) {
+ Log.e("Room", "Failed to update eu.steffo.twom.rsvp: $publishError")
+ publishError.set(R.string.room_error_publish_generic, e)
+ isPublishRunning = false
+ return@Publish
+ }
+ Log.d(
+ "ViewRoomForm",
+ "Updated RSVP with answer `$answer` and comment `$comment`!"
+ )
+
+ if (published.event != null) {
+ Log.d(
+ "Room",
+ "Attempting to redact old RSVP `${published.event.eventId}`..."
+ )
+ try {
+ room.sendService()
+ .redactEvent(published.event, "Replaced with new information")
+ } catch (e: Throwable) {
+ Log.e(
+ "Room",
+ "Failed to redact old RSVP: $publishError"
+ )
+ publishError.set(R.string.room_error_redact_generic, e)
+ isPublishRunning = false
+ return@Publish
+ }
+ } else {
+ Log.d(
+ "Room",
+ "Not doing anything else; there isn't anything to redact."
+ )
+ }
+
+ isPublishRunning = false
+ }
+ },
+ isPublishRunning = isPublishRunning,
+ )
+ publishError.Show {
+ ErrorText(it)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomMembers.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomMembers.kt
new file mode 100644
index 0000000..acf044d
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomMembers.kt
@@ -0,0 +1,48 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.theme.basePadding
+import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
+import kotlin.jvm.optionals.getOrNull
+
+@Composable
+fun ViewRoomMembers() {
+ val roomRequest = LocalRoom.current
+ if (roomRequest == null) {
+ ErrorText(stringResource(R.string.room_error_room_missing))
+ return
+ }
+
+ val room = roomRequest.getOrNull()
+ if (room == null) {
+ ErrorText(stringResource(R.string.room_error_room_notfound))
+ return
+ }
+
+ val roomMembers by room.membershipService().getRoomMembersLive(
+ RoomMemberQueryParams.Builder().build()
+ ).observeAsState()
+ if (roomMembers == null) {
+ ErrorText(stringResource(R.string.room_error_members_notfound))
+ return
+ }
+
+ Row(Modifier.basePadding()) {
+ Text(
+ text = stringResource(R.string.room_invitees_title),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ roomMembers!!.forEach {
+ MemberListItem(member = it)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityScaffold.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomScaffold.kt
similarity index 77%
rename from app/src/main/java/eu/steffo/twom/room/RoomActivityScaffold.kt
rename to app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomScaffold.kt
index cffed8c..cd89d50 100644
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityScaffold.kt
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomScaffold.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.room
+package eu.steffo.twom.composables.viewroom
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
@@ -7,16 +7,15 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.theme.TwoMTheme
+import eu.steffo.twom.composables.matrix.LocalSession
+import eu.steffo.twom.composables.theme.TwoMTheme
import org.matrix.android.sdk.api.session.Session
import java.util.Optional
@Composable
-fun RoomActivityScaffold(
+fun ViewRoomScaffold(
session: Session,
roomId: String,
- onBack: () -> Unit = {},
) {
val room = Optional.ofNullable(session.roomService().getRoom(roomId))
val roomSummary by session.roomService().getRoomSummaryLive(roomId).observeAsState()
@@ -27,12 +26,10 @@ fun RoomActivityScaffold(
CompositionLocalProvider(LocalRoomSummary provides roomSummary) {
Scaffold(
topBar = {
- RoomActivityTopBar(
- onBack = onBack,
- )
+ ViewRoomTopBar()
},
content = {
- RoomActivityContent(
+ ViewRoomContent(
modifier = Modifier.padding(it),
)
},
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopBar.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopBar.kt
new file mode 100644
index 0000000..8c11506
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopBar.kt
@@ -0,0 +1,62 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.errorhandling.ErrorIconButton
+import eu.steffo.twom.composables.errorhandling.LocalizableError
+import eu.steffo.twom.composables.navigation.BackIconButton
+
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+@Preview
+fun ViewRoomTopBar(
+ modifier: Modifier = Modifier,
+ roomName: String? = null,
+ roomAvatarUrl: String? = null,
+ isLoading: Boolean = false,
+ error: LocalizableError? = null,
+) {
+ TopAppBar(
+ modifier = modifier,
+ navigationIcon = {
+ BackIconButton()
+ },
+ title = {
+ if (roomName != null) {
+ Text(
+ text = roomName,
+ style = MaterialTheme.typography.titleLarge,
+ )
+ } else {
+ Text(
+ text = stringResource(R.string.loading),
+ style = MaterialTheme.typography.titleLarge,
+ color = LocalContentColor.current.copy(0.4f)
+ )
+ }
+ },
+ actions = {
+ if (isLoading) {
+ CircularProgressIndicator()
+ } else if (error != null && error.occurred()) {
+ ErrorIconButton(
+ message = error.renderString()!!
+ )
+ } else {
+ RoomIconButton(
+ avatarUrl = roomAvatarUrl,
+ )
+ }
+ },
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopic.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopic.kt
new file mode 100644
index 0000000..1e098c0
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopic.kt
@@ -0,0 +1,39 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.errorhandling.ErrorText
+import eu.steffo.twom.composables.theme.basePadding
+
+@Composable
+@Preview
+fun ViewRoomTopic() {
+ val roomSummaryRequest = LocalRoomSummary.current
+ if (roomSummaryRequest == null) {
+ ErrorText(stringResource(R.string.room_error_roomsummary_missing))
+ return
+ }
+
+ val roomSummary = roomSummaryRequest.getOrNull()
+ if (roomSummary == null) {
+ ErrorText(stringResource(R.string.room_error_roomsummary_notfound))
+ return
+ }
+
+ Row(Modifier.basePadding()) {
+ Text(
+ text = stringResource(R.string.room_topic_title),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+
+ Row(Modifier.basePadding()) {
+ Text(roomSummary.topic)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/composables/viewroom/observeRSVP.kt b/app/src/main/java/eu/steffo/twom/composables/viewroom/observeRSVP.kt
new file mode 100644
index 0000000..80e3371
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/composables/viewroom/observeRSVP.kt
@@ -0,0 +1,92 @@
+package eu.steffo.twom.composables.viewroom
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import eu.steffo.twom.utils.RSVP
+import eu.steffo.twom.utils.RSVPAnswer
+import eu.steffo.twom.utils.TwoMGlobals
+import org.matrix.android.sdk.api.query.QueryStringValue
+import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+
+@Composable
+fun observeRSVP(room: Room, member: RoomMemberSummary): RSVP {
+ if (member.membership == Membership.INVITE) {
+ return RSVP(
+ event = null,
+ answer = RSVPAnswer.PENDING,
+ comment = "",
+ )
+ }
+
+ val request by room.stateService().getStateEventLive(
+ eventType = TwoMGlobals.RSVP_STATE_TYPE,
+ stateKey = QueryStringValue.Equals(member.userId),
+ ).observeAsState()
+
+ if (request == null) {
+ return RSVP(
+ event = null,
+ answer = RSVPAnswer.LOADING,
+ comment = "",
+ )
+ }
+
+ val event = request!!.getOrNull()
+ ?: return RSVP(
+ event = null,
+ answer = RSVPAnswer.NONE,
+ comment = "",
+ )
+
+ val content = event.content
+ ?: return RSVP(
+ event = event,
+ answer = RSVPAnswer.UNKNOWN,
+ comment = "",
+ )
+
+ val commentField = content[TwoMGlobals.RSVP_STATE_COMMENT_FIELD]
+ ?: return RSVP(
+ event = event,
+ answer = RSVPAnswer.UNKNOWN,
+ comment = "",
+ )
+
+ val comment = commentField as? String
+ ?: return RSVP(
+ event = event,
+ answer = RSVPAnswer.UNKNOWN,
+ comment = "",
+ )
+
+ val answerField = content[TwoMGlobals.RSVP_STATE_ANSWER_FIELD]
+ ?: return RSVP(
+ event = event,
+ answer = RSVPAnswer.UNKNOWN,
+ comment = comment,
+ )
+
+ val answerString = answerField as? String
+ ?: return RSVP(
+ event = event,
+ answer = RSVPAnswer.UNKNOWN,
+ comment = comment,
+ )
+
+ val answer = when (answerString) {
+ RSVPAnswer.SURE.value -> RSVPAnswer.SURE
+ RSVPAnswer.LATER.value -> RSVPAnswer.LATER
+ RSVPAnswer.MAYBE.value -> RSVPAnswer.MAYBE
+ RSVPAnswer.NOWAY.value -> RSVPAnswer.NOWAY
+ else -> RSVPAnswer.UNKNOWN
+ }
+
+ return RSVP(
+ event = event,
+ answer = answer,
+ comment = comment,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/MemberListItem.kt b/app/src/main/java/eu/steffo/twom/room/MemberListItem.kt
deleted file mode 100644
index 8f4ad80..0000000
--- a/app/src/main/java/eu/steffo/twom/room/MemberListItem.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Icon
-import androidx.compose.material3.ListItem
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import eu.steffo.twom.R
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.matrix.avatar.AvatarFromURL
-import org.matrix.android.sdk.api.session.getUser
-
-// TODO: Check this with brain on
-
-@Composable
-fun MemberListItem(
- modifier: Modifier = Modifier,
- memberId: String,
- onClickMember: (memberId: String) -> Unit = {},
- rsvpAnswer: RSVPAnswer,
- rsvpComment: String,
-) {
- val session = LocalSession.current
-
- val user = session?.getUser(memberId)
-
- val icon = rsvpAnswer.toIcon()
- val responseResourceId = rsvpAnswer.toResponseResourceId()
- val colorRole = rsvpAnswer.toStaticColorRole()
-
- ListItem(
- modifier = modifier.clickable {
- onClickMember(memberId)
- },
- headlineContent = {
- Text(
- text = user?.displayName ?: stringResource(R.string.user_unresolved_name),
- )
- },
- leadingContent = {
- Box(
- Modifier
- .padding(end = 10.dp)
- .size(40.dp)
- .clip(MaterialTheme.shapes.extraLarge)
- ) {
- AvatarFromURL(
- url = user?.avatarUrl,
- )
- }
- },
- trailingContent = {
- Icon(
- imageVector = icon,
- contentDescription = stringResource(responseResourceId),
- tint = colorRole.value,
- )
- },
- supportingContent = {
- if (rsvpComment != "") {
- Text(
- text = rsvpComment,
- color = colorRole.value,
- )
- }
- },
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt b/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt
deleted file mode 100644
index 0d60373..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Cancel
-import androidx.compose.material.icons.outlined.CheckCircle
-import androidx.compose.material.icons.outlined.Circle
-import androidx.compose.material.icons.outlined.Help
-import androidx.compose.material.icons.outlined.Schedule
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.ui.graphics.vector.ImageVector
-import eu.steffo.twom.R
-import eu.steffo.twom.theme.StaticColorRole
-import eu.steffo.twom.theme.colorRoleLater
-import eu.steffo.twom.theme.colorRoleMaybe
-import eu.steffo.twom.theme.colorRoleNoway
-import eu.steffo.twom.theme.colorRoleSure
-import eu.steffo.twom.theme.colorRoleUnknown
-import org.matrix.android.sdk.api.query.QueryStringValue
-import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.session.room.Room
-import org.matrix.android.sdk.api.util.Optional
-
-enum class RSVPAnswer {
- SURE,
- LATER,
- MAYBE,
- NOWAY,
- UNKNOWN,
-}
-
-@Composable
-fun RSVPAnswer.toStaticColorRole(): StaticColorRole {
- return when (this) {
- RSVPAnswer.SURE -> colorRoleSure()
- RSVPAnswer.LATER -> colorRoleLater()
- RSVPAnswer.MAYBE -> colorRoleMaybe()
- RSVPAnswer.NOWAY -> colorRoleNoway()
- RSVPAnswer.UNKNOWN -> colorRoleUnknown()
- }
-}
-
-fun RSVPAnswer.toIcon(): ImageVector {
- return when (this) {
- RSVPAnswer.SURE -> Icons.Outlined.CheckCircle
- RSVPAnswer.LATER -> Icons.Outlined.Schedule
- RSVPAnswer.MAYBE -> Icons.Outlined.Help
- RSVPAnswer.NOWAY -> Icons.Outlined.Cancel
- RSVPAnswer.UNKNOWN -> Icons.Outlined.Circle
- }
-}
-
-fun RSVPAnswer.toLabelResourceId(): Int {
- return when (this) {
- RSVPAnswer.SURE -> R.string.room_rsvp_sure_label
- RSVPAnswer.LATER -> R.string.room_rsvp_later_label
- RSVPAnswer.MAYBE -> R.string.room_rsvp_maybe_label
- RSVPAnswer.NOWAY -> R.string.room_rsvp_noway_label
- RSVPAnswer.UNKNOWN -> R.string.room_rsvp_unknown_label
- }
-}
-
-fun RSVPAnswer.toResponseResourceId(): Int {
- return when (this) {
- RSVPAnswer.SURE -> R.string.room_rsvp_sure_response
- RSVPAnswer.LATER -> R.string.room_rsvp_later_response
- RSVPAnswer.MAYBE -> R.string.room_rsvp_maybe_response
- RSVPAnswer.NOWAY -> R.string.room_rsvp_noway_response
- RSVPAnswer.UNKNOWN -> R.string.room_rsvp_unknown_response
- }
-}
-
-fun RSVPAnswer.toPlaceholderResourceId(): Int {
- return when (this) {
- RSVPAnswer.SURE -> R.string.room_rsvp_sure_placeholder
- RSVPAnswer.LATER -> R.string.room_rsvp_later_placeholder
- RSVPAnswer.MAYBE -> R.string.room_rsvp_maybe_placeholder
- RSVPAnswer.NOWAY -> R.string.room_rsvp_noway_placeholder
- RSVPAnswer.UNKNOWN -> R.string.room_rsvp_unknown_placeholder
- }
-}
-
-fun makeRSVP(request: State?>?): Triple? {
- val event = request?.value?.getOrNull() ?: return null
- val content = event.content ?: return null
-
- val answerAny = content["answer"]
- val commentAny = content["comment"]
-
- val answer = if (answerAny is String) {
- try {
- RSVPAnswer.valueOf(answerAny)
- } catch (_: IllegalArgumentException) {
- RSVPAnswer.UNKNOWN
- }
- } else {
- RSVPAnswer.UNKNOWN
- }
-
- val comment = if (commentAny is String) {
- commentAny
- } else {
- ""
- }
-
- return Triple(event, answer, comment)
-}
-
-@Composable
-fun observeRsvpAsLiveState(room: Room, userId: String): Triple? {
- val stateRequest = room.stateService().getStateEventLive(
- eventType = "eu.steffo.twom.rsvp",
- stateKey = QueryStringValue.Equals(userId),
- ).observeAsState()
-
- return makeRSVP(stateRequest)
-}
diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPCommentField.kt b/app/src/main/java/eu/steffo/twom/room/RSVPCommentField.kt
deleted file mode 100644
index 5b73a5a..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RSVPCommentField.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.OutlinedTextFieldDefaults
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
-
-@Composable
-@Preview
-fun RSVPCommentField(
- modifier: Modifier = Modifier,
- value: String = "",
- onChange: (value: String) -> Unit = {},
- rsvpAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
-) {
- val colorRole = rsvpAnswer.toStaticColorRole()
-
- OutlinedTextField(
- modifier = modifier,
- value = value,
- onValueChange = onChange,
- singleLine = true,
- shape = MaterialTheme.shapes.small,
- placeholder = {
- Text(
- text = stringResource(rsvpAnswer.toPlaceholderResourceId())
- )
- },
- colors = OutlinedTextFieldDefaults.colors(
- focusedContainerColor = colorRole.valueContainer,
- unfocusedContainerColor = colorRole.valueContainer,
- focusedTextColor = colorRole.onValueContainer,
- unfocusedTextColor = colorRole.onValueContainer,
- focusedBorderColor = colorRole.onValueContainer,
- unfocusedBorderColor = colorRole.onValueContainer.copy(alpha = 0.3f),
- cursorColor = colorRole.onValueContainer,
- )
- )
-}
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityAnswerForm.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityAnswerForm.kt
deleted file mode 100644
index 151dc7e..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityAnswerForm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-
-@Composable
-@Preview
-fun RoomActivityAnswerForm(
- currentRsvpAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
- currentRsvpComment: String = "",
- onUpdate: (rsvpAnswer: RSVPAnswer, rsvpComment: String) -> Unit = { _, _ -> },
- isUpdating: Boolean = false,
-) {
- var rsvpAnswer by rememberSaveable { mutableStateOf(currentRsvpAnswer) }
- var rsvpComment by rememberSaveable { mutableStateOf(currentRsvpComment) }
-
- val hasChanged = (rsvpAnswer != currentRsvpAnswer || rsvpComment != currentRsvpComment)
-
- RSVPAnswerSelectRow(
- value = rsvpAnswer,
- onChange = { rsvpAnswer = it },
- )
- RSVPCommentField(
- modifier = Modifier
- .padding(start = 10.dp, end = 10.dp)
- .fillMaxWidth(),
- value = rsvpComment,
- onChange = { rsvpComment = it },
- rsvpAnswer = rsvpAnswer,
- )
- RSVPUpdateButton(
- modifier = Modifier
- .padding(start = 10.dp, end = 10.dp, top = 4.dp, bottom = 4.dp)
- .fillMaxWidth(),
- onClick = { onUpdate(rsvpAnswer, rsvpComment) },
- enabled = hasChanged && !isUpdating,
- rsvpAnswer = rsvpAnswer,
- )
-}
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt
deleted file mode 100644
index f5ee28d..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt
+++ /dev/null
@@ -1,258 +0,0 @@
-package eu.steffo.twom.room
-
-import android.util.Log
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import eu.steffo.twom.R
-import eu.steffo.twom.matrix.LocalSession
-import eu.steffo.twom.theme.ErrorText
-import eu.steffo.twom.theme.TwoMPadding
-import kotlinx.coroutines.launch
-import kotlin.jvm.optionals.getOrNull
-
-
-@Composable
-fun RoomActivityContent(
- modifier: Modifier = Modifier,
-) {
- val scope = rememberCoroutineScope()
-
- val session = LocalSession.current
- if (session == null) {
- ErrorText(stringResource(R.string.error_session_missing))
- return
- }
-
- val roomRequest = LocalRoom.current
- if (roomRequest == null) {
- ErrorText(stringResource(R.string.room_error_room_missing))
- return
- }
-
- val room = roomRequest.getOrNull()
- if (room == null) {
- ErrorText(stringResource(R.string.room_error_room_notfound))
- return
- }
-
- val roomSummaryRequest = LocalRoomSummary.current
- if (roomSummaryRequest == null) {
- ErrorText(stringResource(R.string.room_error_roomsummary_missing))
- return
- }
-
- val roomSummary = roomSummaryRequest.getOrNull()
- if (roomSummary == null) {
- ErrorText(stringResource(R.string.room_error_roomsummary_notfound))
- return
- }
-
- LaunchedEffect(roomSummary.otherMemberIds) ResolveUnknownUsers@{
- // Resolve unknown users, one at a time
- roomSummary.otherMemberIds.map {
- if (session.userService().getUser(it) == null) {
- Log.i("Room", "Resolving unknown user: $it")
- session.userService().resolveUser(it)
- Log.d("Room", "Successfully resolved unknown user: $it")
- } else {
- Log.v("Room", "Not resolving known user: $it")
- }
- }
- }
-
- val myRsvpRequest = observeRsvpAsLiveState(room = room, userId = session.myUserId)
- val otherRsvpRequests =
- roomSummary.otherMemberIds.map { it to observeRsvpAsLiveState(room = room, userId = it) }
-
- var isUpdatingMyRsvp by rememberSaveable { mutableStateOf(false) }
- var errorMyRsvp by rememberSaveable { mutableStateOf(null) }
- var isSendingInvite by rememberSaveable { mutableStateOf(false) }
- var errorInvite by rememberSaveable { mutableStateOf(null) }
-
- Box(
- modifier = modifier
- .verticalScroll(rememberScrollState())
- ) {
- Column(
- modifier = Modifier.fillMaxHeight()
- ) {
- if (roomSummary.topic != "") {
- Row(TwoMPadding.base) {
- Text(
- text = stringResource(R.string.room_topic_title),
- style = MaterialTheme.typography.labelLarge,
- )
- }
- Row(TwoMPadding.base) {
- Text(roomSummary.topic)
- }
- }
-
- Row(TwoMPadding.base) {
- Text(
- text = stringResource(R.string.room_rsvp_title),
- style = MaterialTheme.typography.labelLarge,
- )
- }
-
- RoomActivityAnswerForm(
- // FIXME: This always set the request to UNKNOWN
- currentRsvpAnswer = myRsvpRequest?.second ?: RSVPAnswer.UNKNOWN,
- currentRsvpComment = myRsvpRequest?.third ?: "",
- onUpdate = { answer, comment ->
- isUpdatingMyRsvp = true
- errorMyRsvp = null
-
- scope.launch SendRSVP@{
- Log.d(
- "Room",
- "Updating eu.steffo.twom.rsvp with answer `$answer` and comment `$comment`..."
- )
- try {
- room.stateService().sendStateEvent(
- eventType = "eu.steffo.twom.rsvp",
- stateKey = session.myUserId,
- body = mapOf(
- pairs = arrayOf(
- "answer" to answer.toString(),
- "comment" to comment,
- )
- ),
- )
- } catch (error: Exception) {
- Log.e("Room", "Failed to update eu.steffo.twom.rsvp: $error")
- errorMyRsvp = error
- isUpdatingMyRsvp = false
- return@SendRSVP
- }
- Log.d(
- "Room",
- "Updated eu.steffo.twom.rsvp with answer `$answer` and comment `$comment`!"
- )
-
- if (myRsvpRequest != null) {
- val myRsvpRequestEventId = myRsvpRequest.first.eventId
- Log.d(
- "Room",
- "Attempting to redact old eu.steffo.twom.rsvp event `${myRsvpRequestEventId}`..."
- )
- try {
- room.sendService()
- .redactEvent(
- myRsvpRequest.first,
- "Replaced with new information"
- )
- } catch (error: Throwable) {
- Log.e(
- "Room",
- "Failed to redact the old eu.steffo.twom.rsvp: $error"
- )
- errorMyRsvp = error
- isUpdatingMyRsvp = false
- return@SendRSVP
- }
- } else {
- Log.d(
- "Room",
- "Not doing anything else; there isn't anything to redact."
- )
- }
-
- isUpdatingMyRsvp = false
- }
- },
- isUpdating = isUpdatingMyRsvp,
- )
-
- if (errorMyRsvp != null) {
- // TODO: Maybe add an human-friendly error message?
- Row(TwoMPadding.base) {
- ErrorText(
- errorMyRsvp.toString()
- )
- }
- }
-
- Row(TwoMPadding.base) {
- Text(
- text = stringResource(R.string.room_invitees_title),
- style = MaterialTheme.typography.labelLarge,
- )
- }
-
- Column(TwoMPadding.base) {
- MemberListItem(
- memberId = LocalSession.current!!.myUserId,
- rsvpAnswer = myRsvpRequest?.second ?: RSVPAnswer.UNKNOWN,
- rsvpComment = myRsvpRequest?.third ?: "",
- )
-
- // FIXME: This also displays invited members!
- otherRsvpRequests.forEach {
- MemberListItem(
- memberId = it.first,
- rsvpAnswer = it.second?.second ?: RSVPAnswer.UNKNOWN,
- rsvpComment = it.second?.third ?: "",
- )
- }
- }
-
- Row(TwoMPadding.base) {
- Text(
- text = stringResource(R.string.room_invite_title),
- style = MaterialTheme.typography.labelLarge,
- )
- }
-
- Row(TwoMPadding.base) {
- RoomActivityInviteForm(
- busy = isSendingInvite,
- onSend = {
- scope.launch SendInvite@{
- isSendingInvite = true
- errorInvite = null
-
- Log.d("Room", "Sending invite to `$it`...")
-
- try {
- room.membershipService().invite(it)
- } catch (error: Throwable) {
- Log.e("Room", "Failed to send invite to `$it`: $error")
- errorInvite = error
- isSendingInvite = false
- return@SendInvite
- }
-
- Log.d("Room", "Successfully sent invite to `$it`!")
- isSendingInvite = false
- }
- }
- )
- }
-
- if (errorInvite != null) {
- // TODO: Maybe add an human-friendly error message?
- Row(TwoMPadding.base) {
- ErrorText(
- errorInvite.toString()
- )
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt
deleted file mode 100644
index d201440..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.OutlinedTextFieldDefaults
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import eu.steffo.twom.R
-import eu.steffo.twom.theme.colorRoleUnknown
-
-@Composable
-@Preview
-fun RoomActivityInviteForm(
- modifier: Modifier = Modifier,
- busy: Boolean = false,
- onSend: (userId: String) -> Unit = {},
-) {
- var value by rememberSaveable { mutableStateOf("") }
- val colorRole = colorRoleUnknown()
-
- Column(modifier) {
- OutlinedTextField(
- modifier = Modifier
- .fillMaxWidth(),
- value = value,
- onValueChange = { value = it },
- singleLine = true,
- shape = MaterialTheme.shapes.small,
- placeholder = {
- Text(
- text = stringResource(R.string.room_invite_username_placeholder)
- )
- },
- colors = OutlinedTextFieldDefaults.colors(
- focusedContainerColor = colorRole.valueContainer,
- unfocusedContainerColor = colorRole.valueContainer,
- focusedTextColor = colorRole.onValueContainer,
- unfocusedTextColor = colorRole.onValueContainer,
- focusedBorderColor = colorRole.onValueContainer,
- unfocusedBorderColor = colorRole.onValueContainer.copy(alpha = 0.3f),
- cursorColor = colorRole.onValueContainer,
- )
- )
-
- Button(
- modifier = Modifier
- .padding(top = 4.dp)
- .fillMaxWidth(),
- onClick = { onSend(value) },
- shape = MaterialTheme.shapes.small,
- // FIXME: Maybe I should validate usernames with a regex
- enabled = (value.contains("@") && value.contains(":") && !busy),
- colors = ButtonDefaults.buttonColors(
- containerColor = colorRole.value,
- contentColor = colorRole.onValue,
- )
- ) {
- Text(
- text = stringResource(R.string.room_invite_button_label)
- )
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityRoomIconButton.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityRoomIconButton.kt
deleted file mode 100644
index 8912635..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityRoomIconButton.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.size
-import androidx.compose.material.ripple.rememberRipple
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.minimumInteractiveComponentSize
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.unit.dp
-import eu.steffo.twom.R
-import eu.steffo.twom.matrix.avatar.AvatarFromURL
-
-@Composable
-fun RoomActivityRoomIconButton(
- modifier: Modifier = Modifier,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- avatarUrl: String,
-) {
- var expanded by remember { mutableStateOf(false) }
-
- Box(modifier) {
- // Mostly copied from IconButton's source
- // TODO: Make sure accessibility works right
- Box(
- modifier = Modifier
- .minimumInteractiveComponentSize()
- .size(40.dp)
- .clip(MaterialTheme.shapes.medium)
- .clickable(
- role = Role.Button,
- interactionSource = interactionSource,
- indication = rememberRipple(
- bounded = false,
- radius = 28.dp
- )
- ) { expanded = true },
- ) {
- AvatarFromURL(
- url = avatarUrl,
- contentDescription = LocalContext.current.getString(R.string.room_options_label),
- )
- }
- DropdownMenu(
- expanded = expanded,
- onDismissRequest = { expanded = false },
- ) {
- DropdownMenuItem(
- text = {
- Text("garasauto")
- },
- onClick = {
- expanded = false
- }
- )
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityTopBar.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityTopBar.kt
deleted file mode 100644
index b053ea0..0000000
--- a/app/src/main/java/eu/steffo/twom/room/RoomActivityTopBar.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package eu.steffo.twom.room
-
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.icons.filled.Warning
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import eu.steffo.twom.R
-
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun RoomActivityTopBar(
- modifier: Modifier = Modifier,
- onBack: () -> Unit = {},
-) {
- val isLoading = (LocalRoomSummary.current == null)
- val roomSummary = LocalRoomSummary.current?.getOrNull()
- val isError = (!isLoading && roomSummary == null)
-
- TopAppBar(
- modifier = modifier,
- navigationIcon = {
- IconButton(onClick = onBack) {
- Icon(
- imageVector = Icons.Filled.ArrowBack,
- contentDescription = LocalContext.current.getString(R.string.back)
- )
- }
- },
- title = {
- if (roomSummary != null) {
- Text(
- text = roomSummary.displayName,
- style = MaterialTheme.typography.titleLarge,
- )
- }
- },
- actions = {
- if (isLoading) {
- CircularProgressIndicator()
- } else if (isError) {
- Icon(Icons.Filled.Warning, stringResource(R.string.error))
- } else {
- RoomActivityRoomIconButton(
- avatarUrl = roomSummary!!.avatarUrl,
- )
- }
- },
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/theme/ErrorText.kt b/app/src/main/java/eu/steffo/twom/theme/ErrorText.kt
deleted file mode 100644
index 772c536..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/ErrorText.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-
-@Composable
-fun ErrorText(
- text: String
-) {
- Text(
- text = text,
- color = MaterialTheme.colorScheme.error,
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/steffo/twom/theme/StaticColorRole.kt b/app/src/main/java/eu/steffo/twom/theme/StaticColorRole.kt
deleted file mode 100644
index 852b915..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/StaticColorRole.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.ui.graphics.Color
-
-data class StaticColorRole(
- val value: Color,
- val onValue: Color,
- val valueContainer: Color,
- val onValueContainer: Color,
-)
diff --git a/app/src/main/java/eu/steffo/twom/theme/TwoMPadding.kt b/app/src/main/java/eu/steffo/twom/theme/TwoMPadding.kt
deleted file mode 100644
index 0fb55cb..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/TwoMPadding.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-
-
-object TwoMPadding {
- val base = Modifier.padding(all = 10.dp)
- val chips = Modifier.padding(start = 2.5.dp, end = 2.5.dp)
-}
diff --git a/app/src/main/java/eu/steffo/twom/theme/colorRoleLater.kt b/app/src/main/java/eu/steffo/twom/theme/colorRoleLater.kt
deleted file mode 100644
index 07f88a2..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/colorRoleLater.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-import com.google.android.material.color.MaterialColors
-
-
-@Composable
-fun colorRoleLater(): StaticColorRole {
- val ctx = LocalContext.current
-
- return when (isSystemInDarkTheme()) {
- false -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x00658B)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFFFFF)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xC4E7FF)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x001E2C)),
- )
-
- true -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x7DD0FF)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x00344A)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x004C69)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xC4E7FF)),
- )
- }
-}
diff --git a/app/src/main/java/eu/steffo/twom/theme/colorRoleMaybe.kt b/app/src/main/java/eu/steffo/twom/theme/colorRoleMaybe.kt
deleted file mode 100644
index 47d8f45..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/colorRoleMaybe.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-import com.google.android.material.color.MaterialColors
-
-
-@Composable
-fun colorRoleMaybe(): StaticColorRole {
- val ctx = LocalContext.current
-
- return when (isSystemInDarkTheme()) {
- false -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x765B00)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFFFFF)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFDF94)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x241A00)),
- )
-
- true -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xEDC148)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x3E2E00)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x594400)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFDF94)),
- )
- }
-}
diff --git a/app/src/main/java/eu/steffo/twom/theme/colorRoleNoway.kt b/app/src/main/java/eu/steffo/twom/theme/colorRoleNoway.kt
deleted file mode 100644
index cd72ef4..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/colorRoleNoway.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-import com.google.android.material.color.MaterialColors
-
-
-@Composable
-fun colorRoleNoway(): StaticColorRole {
- val ctx = LocalContext.current
-
- return when (isSystemInDarkTheme()) {
- false -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xAB3520)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFFFFF)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFDAD3)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x3F0400)),
- )
-
- true -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFB4A5)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x650A00)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x891D0A)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFDAD3)),
- )
- }
-}
diff --git a/app/src/main/java/eu/steffo/twom/theme/colorRoleSure.kt b/app/src/main/java/eu/steffo/twom/theme/colorRoleSure.kt
deleted file mode 100644
index 2e51ccb..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/colorRoleSure.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-import com.google.android.material.color.MaterialColors
-
-@Composable
-fun colorRoleSure(): StaticColorRole {
- val ctx = LocalContext.current
-
- return when (isSystemInDarkTheme()) {
- false -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x006E2C)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0xFFFFFF)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x7FFC95)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x002108)),
- )
-
- true -> StaticColorRole(
- value = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x62DF7C)),
- onValue = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x003913)),
- valueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x00531F)),
- onValueContainer = Color(MaterialColors.harmonizeWithPrimary(ctx, 0x7FFC95)),
- )
- }
-}
diff --git a/app/src/main/java/eu/steffo/twom/theme/colorRoleUnknown.kt b/app/src/main/java/eu/steffo/twom/theme/colorRoleUnknown.kt
deleted file mode 100644
index 090b982..0000000
--- a/app/src/main/java/eu/steffo/twom/theme/colorRoleUnknown.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.steffo.twom.theme
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-
-@Composable
-fun colorRoleUnknown(): StaticColorRole {
- return StaticColorRole(
- value = MaterialTheme.colorScheme.inverseSurface,
- onValue = MaterialTheme.colorScheme.inverseOnSurface,
- valueContainer = MaterialTheme.colorScheme.surface,
- onValueContainer = MaterialTheme.colorScheme.onSurface,
- )
-}
diff --git a/app/src/main/java/eu/steffo/twom/utils/RSVP.kt b/app/src/main/java/eu/steffo/twom/utils/RSVP.kt
new file mode 100644
index 0000000..cbcb703
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/utils/RSVP.kt
@@ -0,0 +1,9 @@
+package eu.steffo.twom.utils
+
+import org.matrix.android.sdk.api.session.events.model.Event
+
+data class RSVP(
+ val event: Event?,
+ val answer: RSVPAnswer,
+ val comment: String,
+)
diff --git a/app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt b/app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt
new file mode 100644
index 0000000..0187a02
--- /dev/null
+++ b/app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt
@@ -0,0 +1,227 @@
+package eu.steffo.twom.utils
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.BuildCircle
+import androidx.compose.material.icons.outlined.Cancel
+import androidx.compose.material.icons.outlined.CheckCircle
+import androidx.compose.material.icons.outlined.Circle
+import androidx.compose.material.icons.outlined.HelpOutline
+import androidx.compose.material.icons.outlined.HourglassEmpty
+import androidx.compose.material.icons.outlined.MoreHoriz
+import androidx.compose.material.icons.outlined.Schedule
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import eu.steffo.twom.R
+import eu.steffo.twom.composables.theme.LaterColorRole
+import eu.steffo.twom.composables.theme.MaybeColorRole
+import eu.steffo.twom.composables.theme.NowayColorRole
+import eu.steffo.twom.composables.theme.NullishColorRole
+import eu.steffo.twom.composables.theme.StaticColorRole
+import eu.steffo.twom.composables.theme.SureColorRole
+
+enum class RSVPAnswer {
+ // Will be there!
+ SURE {
+ override val value: String
+ get() = "SURE"
+
+ override val staticColorRole: StaticColorRole
+ get() = SureColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.CheckCircle
+
+ @Composable
+ override fun toLabel(): String =
+ stringResource(R.string.room_rsvp_sure_label)
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_sure_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_sure_placeholder)
+ },
+
+ // Will be there, but later!
+ LATER {
+ override val value: String
+ get() = "LATER"
+
+ override val staticColorRole: StaticColorRole
+ get() = LaterColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.Schedule
+
+ @Composable
+ override fun toLabel(): String =
+ stringResource(R.string.room_rsvp_later_label)
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_later_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_later_placeholder)
+ },
+
+ // Might be there...
+ MAYBE {
+ override val value: String
+ get() = "MAYBE"
+
+ override val staticColorRole: StaticColorRole
+ get() = MaybeColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.HelpOutline
+
+ @Composable
+ override fun toLabel(): String =
+ stringResource(R.string.room_rsvp_maybe_label)
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_maybe_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_maybe_placeholder)
+ },
+
+ // Won't be there.
+ NOWAY {
+ override val value: String
+ get() = "NOWAY"
+
+ override val staticColorRole: StaticColorRole
+ get() = NowayColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.Cancel
+
+ @Composable
+ override fun toLabel(): String =
+ stringResource(R.string.room_rsvp_noway_label)
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_noway_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_noway_placeholder)
+ },
+
+ // An option differing from the previous ones.
+ UNKNOWN {
+ override val value: String?
+ get() = null
+
+ override val staticColorRole: StaticColorRole
+ get() = NullishColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.BuildCircle
+
+ @Composable
+ override fun toLabel(): String? =
+ null
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_unknown_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_nullish_placeholder)
+ },
+
+ // The answer is still being loaded.
+ LOADING {
+ override val value: String?
+ get() = null
+
+ override val staticColorRole: StaticColorRole
+ get() = NullishColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.HourglassEmpty
+
+ @Composable
+ override fun toLabel(): String? = null
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.loading)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_nullish_placeholder)
+ },
+
+ // No answer has been provided yet.
+ NONE {
+ override val value: String?
+ get() = null
+
+ override val staticColorRole: StaticColorRole
+ get() = NullishColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.Circle
+
+ @Composable
+ override fun toLabel(): String? =
+ null
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_none_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_nullish_placeholder)
+ },
+
+ // Has been invited, but has not accepted yet.
+ PENDING {
+ override val value: String?
+ get() = null
+
+ override val staticColorRole: StaticColorRole
+ get() = NullishColorRole
+
+ override val icon: ImageVector
+ get() = Icons.Outlined.MoreHoriz
+
+ @Composable
+ override fun toLabel(): String? =
+ null
+
+ @Composable
+ override fun toResponse(): String =
+ stringResource(R.string.room_rsvp_pending_response)
+
+ @Composable
+ override fun toCommentPlaceholder(): String =
+ stringResource(R.string.room_rsvp_nullish_placeholder)
+ };
+
+ abstract val value: String?
+ abstract val staticColorRole: StaticColorRole
+ abstract val icon: ImageVector
+
+ @Composable
+ abstract fun toLabel(): String?
+
+ @Composable
+ abstract fun toResponse(): String
+
+ @Composable
+ abstract fun toCommentPlaceholder(): String
+}
diff --git a/app/src/main/java/eu/steffo/twom/matrix/TwoMMatrix.kt b/app/src/main/java/eu/steffo/twom/utils/TwoMGlobals.kt
similarity index 85%
rename from app/src/main/java/eu/steffo/twom/matrix/TwoMMatrix.kt
rename to app/src/main/java/eu/steffo/twom/utils/TwoMGlobals.kt
index 1880bf8..5d685ac 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/TwoMMatrix.kt
+++ b/app/src/main/java/eu/steffo/twom/utils/TwoMGlobals.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix
+package eu.steffo.twom.utils
import android.content.Context
import android.util.Log
@@ -9,7 +9,7 @@ import org.matrix.android.sdk.api.MatrixConfiguration
/**
* Object containing the global state of the application.
*/
-object TwoMMatrix {
+object TwoMGlobals {
/**
* The global [Matrix] object of the application.
*
@@ -38,4 +38,10 @@ object TwoMMatrix {
}
const val ROOM_TYPE = "eu.steffo.twom.happening"
+
+ const val RSVP_STATE_TYPE = "eu.steffo.twom.rsvp"
+
+ const val RSVP_STATE_ANSWER_FIELD = "answer"
+
+ const val RSVP_STATE_COMMENT_FIELD = "comment"
}
diff --git a/app/src/main/java/eu/steffo/twom/matrix/TwoMRoomDisplayNameFallbackProvider.kt b/app/src/main/java/eu/steffo/twom/utils/TwoMRoomDisplayNameFallbackProvider.kt
similarity index 98%
rename from app/src/main/java/eu/steffo/twom/matrix/TwoMRoomDisplayNameFallbackProvider.kt
rename to app/src/main/java/eu/steffo/twom/utils/TwoMRoomDisplayNameFallbackProvider.kt
index 9f4387c..f403ec0 100644
--- a/app/src/main/java/eu/steffo/twom/matrix/TwoMRoomDisplayNameFallbackProvider.kt
+++ b/app/src/main/java/eu/steffo/twom/utils/TwoMRoomDisplayNameFallbackProvider.kt
@@ -1,4 +1,4 @@
-package eu.steffo.twom.matrix
+package eu.steffo.twom.utils
import android.content.Context
import eu.steffo.twom.R
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6ccae6c..59a11ee 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,13 +52,10 @@
Why will you be late?
What will determine your partecipation?
Why won\'t you partecipate?
- Hasn\'t answered yet
- Leave a comment…
- Update
+ Update
The Matrix session context has not been initialized.
Could not find the requested Matrix room.
The Matrix room context has not been initialized.
- No answer
The Matrix room summary context has not been initialized.
Could not find the requested Matrix room summary.
\@steffotwo:candy.steffo.eu
@@ -74,4 +71,15 @@
Something went wrong while setting up the login wizard: %1$s
Something went wrong while logging in: %1$s
Something went wrong while leaving the room: %1$s
+ Close
+ Edit party
+ Zoom on avatar
+ Could not retrieve the list of room members.
+ Leave a comment...
+ Has given an unsupported response
+ Hasn\'t responded yet
+ Hasn\'t opened the invite yet
+ Uninvite
+ Something went wrong while updating your RSVP: %1$s
+ Your response has been updated, but something went wrong while attempting to remove your previous one: %1$s
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 580acb6..c4cad98 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -3,6 +3,6 @@
-
+
\ No newline at end of file