From 01e744883f5b3fce5ac2fd4e132f560fd2c9cb6f Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Fri, 12 Jan 2024 17:59:16 +0100 Subject: [PATCH] Bulk rewrite continues --- app/src/main/AndroidManifest.xml | 7 +- .../twom/activities/InviteUserActivity.kt | 35 +++ .../steffo/twom/activities/LoginActivity.kt | 9 +- .../eu/steffo/twom/activities/MainActivity.kt | 54 +--- .../{RoomActivity.kt => ViewRoomActivity.kt} | 16 +- .../avatar/AvatarEmpty.kt} | 18 +- .../avatar/AvatarImage.kt} | 18 +- .../twom/composables/avatar/AvatarPicker.kt | 3 +- .../avatar/AvatarURL.kt} | 42 ++- .../twom/composables/avatar/AvatarUser.kt | 23 ++ .../avatar/AvatarUserId.kt} | 38 ++- .../composables/createroom/CreateRoomForm.kt | 8 +- .../createroom/CreateRoomScaffold.kt | 2 +- .../errorhandling/ErrorIconButton.kt | 66 +++++ .../errorhandling/LocalizableError.kt | 56 ++++ .../inviteuser/InviteUserContent.kt | 47 ++++ .../composables/inviteuser/InviteUserForm.kt | 54 ++++ .../twom/composables/login/LoginForm.kt | 16 +- .../twom/composables/login/LoginScaffold.kt | 2 +- .../composables/main/AccountIconButton.kt | 17 +- .../twom/composables/main/CreateRoomFAB.kt | 14 +- .../composables/main/MainContentLoggedIn.kt | 15 +- .../main/MainContentNotLoggedIn.kt | 6 +- .../twom/composables/main/MainScaffold.kt | 9 +- .../{ => composables}/main/RoomListItem.kt | 16 +- .../{ => composables}/matrix/LocalSession.kt | 2 +- .../composables/navigation/BackIconButton.kt | 34 +++ .../twom/composables/theme/LaterColorRole.kt | 15 + .../twom/composables/theme/MaybeColorRole.kt | 15 + .../twom/composables/theme/NowayColorRole.kt | 15 + .../composables/theme/NullishColorRole.kt | 15 + .../twom/composables/theme/StaticColorRole.kt | 69 +++++ .../twom/composables/theme/SureColorRole.kt | 15 + .../twom/composables/theme/TwoMPadding.kt | 14 + .../twom/{ => composables}/theme/TwoMTheme.kt | 19 +- .../twom/composables/viewroom/InviteFAB.kt | 43 +++ .../viewroom}/LocalRoom.kt | 2 +- .../viewroom}/LocalRoomSummary.kt | 2 +- .../composables/viewroom/MemberListItem.kt | 123 +++++++++ .../viewroom/RSVPChip.kt} | 30 +- .../viewroom/RSVPChipRow.kt} | 31 ++- .../composables/viewroom/RSVPCommentField.kt | 40 +++ .../twom/composables/viewroom/RSVPForm.kt | 46 ++++ .../viewroom}/RSVPUpdateButton.kt | 13 +- .../composables/viewroom/RoomIconButton.kt | 64 +++++ .../composables/viewroom/ViewRoomContent.kt | 41 +++ .../twom/composables/viewroom/ViewRoomForm.kt | 127 +++++++++ .../composables/viewroom/ViewRoomMembers.kt | 48 ++++ .../viewroom/ViewRoomScaffold.kt} | 15 +- .../composables/viewroom/ViewRoomTopBar.kt | 62 +++++ .../composables/viewroom/ViewRoomTopic.kt | 39 +++ .../twom/composables/viewroom/observeRSVP.kt | 92 +++++++ .../eu/steffo/twom/room/MemberListItem.kt | 76 ------ .../java/eu/steffo/twom/room/RSVPAnswer.kt | 118 -------- .../eu/steffo/twom/room/RSVPCommentField.kt | 43 --- .../twom/room/RoomActivityAnswerForm.kt | 47 ---- .../steffo/twom/room/RoomActivityContent.kt | 258 ------------------ .../twom/room/RoomActivityInviteForm.kt | 76 ------ .../twom/room/RoomActivityRoomIconButton.kt | 71 ----- .../eu/steffo/twom/room/RoomActivityTopBar.kt | 60 ---- .../java/eu/steffo/twom/theme/ErrorText.kt | 15 - .../eu/steffo/twom/theme/StaticColorRole.kt | 10 - .../java/eu/steffo/twom/theme/TwoMPadding.kt | 11 - .../eu/steffo/twom/theme/colorRoleLater.kt | 29 -- .../eu/steffo/twom/theme/colorRoleMaybe.kt | 29 -- .../eu/steffo/twom/theme/colorRoleNoway.kt | 29 -- .../eu/steffo/twom/theme/colorRoleSure.kt | 28 -- .../eu/steffo/twom/theme/colorRoleUnknown.kt | 14 - .../main/java/eu/steffo/twom/utils/RSVP.kt | 9 + .../java/eu/steffo/twom/utils/RSVPAnswer.kt | 227 +++++++++++++++ .../TwoMMatrix.kt => utils/TwoMGlobals.kt} | 10 +- .../TwoMRoomDisplayNameFallbackProvider.kt | 2 +- app/src/main/res/values/strings.xml | 16 +- app/src/main/res/values/themes.xml | 2 +- 74 files changed, 1644 insertions(+), 1158 deletions(-) create mode 100644 app/src/main/java/eu/steffo/twom/activities/InviteUserActivity.kt rename app/src/main/java/eu/steffo/twom/activities/{RoomActivity.kt => ViewRoomActivity.kt} (77%) rename app/src/main/java/eu/steffo/twom/{matrix/avatar/AvatarFromDefault.kt => composables/avatar/AvatarEmpty.kt} (64%) rename app/src/main/java/eu/steffo/twom/{matrix/avatar/AvatarFromImageBitmap.kt => composables/avatar/AvatarImage.kt} (65%) rename app/src/main/java/eu/steffo/twom/{matrix/avatar/AvatarFromURL.kt => composables/avatar/AvatarURL.kt} (60%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/avatar/AvatarUser.kt rename app/src/main/java/eu/steffo/twom/{matrix/avatar/AvatarFromUserId.kt => composables/avatar/AvatarUserId.kt} (56%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/errorhandling/ErrorIconButton.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/errorhandling/LocalizableError.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserContent.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/inviteuser/InviteUserForm.kt rename app/src/main/java/eu/steffo/twom/{ => composables}/main/RoomListItem.kt (88%) rename app/src/main/java/eu/steffo/twom/{ => composables}/matrix/LocalSession.kt (80%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/navigation/BackIconButton.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/LaterColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/MaybeColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/NowayColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/NullishColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/StaticColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/SureColorRole.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/theme/TwoMPadding.kt rename app/src/main/java/eu/steffo/twom/{ => composables}/theme/TwoMTheme.kt (93%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/InviteFAB.kt rename app/src/main/java/eu/steffo/twom/{room => composables/viewroom}/LocalRoom.kt (82%) rename app/src/main/java/eu/steffo/twom/{room => composables/viewroom}/LocalRoomSummary.kt (85%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/MemberListItem.kt rename app/src/main/java/eu/steffo/twom/{room/RSVPAnswerFilterChip.kt => composables/viewroom/RSVPChip.kt} (58%) rename app/src/main/java/eu/steffo/twom/{room/RSVPAnswerSelectRow.kt => composables/viewroom/RSVPChipRow.kt} (70%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPCommentField.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/RSVPForm.kt rename app/src/main/java/eu/steffo/twom/{room => composables/viewroom}/RSVPUpdateButton.kt (69%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/RoomIconButton.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomContent.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomForm.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomMembers.kt rename app/src/main/java/eu/steffo/twom/{room/RoomActivityScaffold.kt => composables/viewroom/ViewRoomScaffold.kt} (77%) create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopBar.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/ViewRoomTopic.kt create mode 100644 app/src/main/java/eu/steffo/twom/composables/viewroom/observeRSVP.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/MemberListItem.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RSVPCommentField.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityAnswerForm.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityRoomIconButton.kt delete mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityTopBar.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/ErrorText.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/StaticColorRole.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/TwoMPadding.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/colorRoleLater.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/colorRoleMaybe.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/colorRoleNoway.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/colorRoleSure.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/colorRoleUnknown.kt create mode 100644 app/src/main/java/eu/steffo/twom/utils/RSVP.kt create mode 100644 app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt rename app/src/main/java/eu/steffo/twom/{matrix/TwoMMatrix.kt => utils/TwoMGlobals.kt} (85%) rename app/src/main/java/eu/steffo/twom/{matrix => utils}/TwoMRoomDisplayNameFallbackProvider.kt (98%) 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 @@