From cca38d0026291ccfd3c19fedd551293a7d3d900d Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 9 Jan 2024 08:19:27 +0100 Subject: [PATCH] Add basic invite button --- .../java/eu/steffo/twom/room/RSVPAnswer.kt | 37 +-- .../steffo/twom/room/RoomActivityContent.kt | 244 +++++++++++------- .../twom/room/RoomActivityInviteForm.kt | 76 ++++++ .../java/eu/steffo/twom/theme/iconLater.kt | 6 - .../java/eu/steffo/twom/theme/iconMaybe.kt | 6 - .../java/eu/steffo/twom/theme/iconNoway.kt | 6 - .../java/eu/steffo/twom/theme/iconSure.kt | 6 - .../java/eu/steffo/twom/theme/iconUnknown.kt | 6 - app/src/main/res/values/strings.xml | 7 + 9 files changed, 252 insertions(+), 142 deletions(-) create mode 100644 app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/iconLater.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/iconMaybe.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/iconNoway.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/iconSure.kt delete mode 100644 app/src/main/java/eu/steffo/twom/theme/iconUnknown.kt diff --git a/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt b/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt index dee54d1..0d60373 100644 --- a/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt +++ b/app/src/main/java/eu/steffo/twom/room/RSVPAnswer.kt @@ -1,5 +1,11 @@ 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 @@ -11,11 +17,6 @@ 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 eu.steffo.twom.theme.iconLater -import eu.steffo.twom.theme.iconMaybe -import eu.steffo.twom.theme.iconNoway -import eu.steffo.twom.theme.iconSure -import eu.steffo.twom.theme.iconUnknown 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 @@ -42,11 +43,11 @@ fun RSVPAnswer.toStaticColorRole(): StaticColorRole { fun RSVPAnswer.toIcon(): ImageVector { return when (this) { - RSVPAnswer.SURE -> iconSure - RSVPAnswer.LATER -> iconLater - RSVPAnswer.MAYBE -> iconMaybe - RSVPAnswer.NOWAY -> iconNoway - RSVPAnswer.UNKNOWN -> iconUnknown + 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 } } @@ -80,16 +81,6 @@ fun RSVPAnswer.toPlaceholderResourceId(): Int { } } -fun RSVPAnswer.toEmoji(): String { - return when (this) { - RSVPAnswer.SURE -> "✅" - RSVPAnswer.LATER -> "\uD83D\uDD52️" - RSVPAnswer.MAYBE -> "❔" - RSVPAnswer.NOWAY -> "⛔️" - RSVPAnswer.UNKNOWN -> "ℹ️" - } -} - fun makeRSVP(request: State?>?): Triple? { val event = request?.value?.getOrNull() ?: return null val content = event.content ?: return null @@ -118,10 +109,10 @@ fun makeRSVP(request: State?>?): Triple? { - val request = room.stateService().getStateEventLive( + val stateRequest = room.stateService().getStateEventLive( eventType = "eu.steffo.twom.rsvp", stateKey = QueryStringValue.Equals(userId), ).observeAsState() - return makeRSVP(request) -} \ No newline at end of file + return makeRSVP(stateRequest) +} diff --git a/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt b/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt index e90ae89..99b7493 100644 --- a/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt +++ b/app/src/main/java/eu/steffo/twom/room/RoomActivityContent.kt @@ -1,8 +1,12 @@ 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 @@ -19,7 +23,6 @@ import eu.steffo.twom.matrix.LocalSession import eu.steffo.twom.theme.ErrorText import eu.steffo.twom.theme.TwoMPadding import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.failure.Failure import kotlin.jvm.optionals.getOrNull @@ -77,115 +80,178 @@ fun RoomActivityContent( roomSummary.otherMemberIds.map { it to observeRsvpAsLiveState(room = room, userId = it) } var isUpdatingMyRsvp by rememberSaveable { mutableStateOf(false) } - var errorMyRsvp by rememberSaveable { mutableStateOf(null) } + var errorMyRsvp by rememberSaveable { mutableStateOf(null) } + var isSendingInvite by rememberSaveable { mutableStateOf(false) } + var errorInvite by rememberSaveable { mutableStateOf(null) } - Column(modifier) { - 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: Failure.ServerError) { - 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`!" + 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) + } + } - if (myRsvpRequest != null) { - val myRsvpRequestEventId = myRsvpRequest.first.eventId + 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", - "Attempting to redact old eu.steffo.twom.rsvp event `${myRsvpRequestEventId}`..." + "Updating eu.steffo.twom.rsvp with answer `$answer` and comment `$comment`..." ) try { - room.sendService() - .redactEvent(myRsvpRequest.first, "Replaced with new information") - } catch (error: Failure.ServerError) { - Log.e("Room", "Failed to redact the old eu.steffo.twom.rsvp: $error") + 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 } - } else { - Log.d("Room", "Not doing anything else; there isn't anything to redact.") + 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, + ) - isUpdatingMyRsvp = false + if (errorMyRsvp != null) { + // TODO: Maybe add an human-friendly error message? + Row(TwoMPadding.base) { + ErrorText( + errorMyRsvp.toString() + ) } - }, - isUpdating = isUpdatingMyRsvp, - ) + } - if (errorMyRsvp != null) { - // TODO: Maybe add an human-friendly error message? Row(TwoMPadding.base) { - ErrorText( - errorMyRsvp.toString() + Text( + text = stringResource(R.string.room_invitees_title), + style = MaterialTheme.typography.labelLarge, ) } - } - 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 ?: "", - ) - - otherRsvpRequests.forEach { + Column(TwoMPadding.base) { MemberListItem( - memberId = it.first, - rsvpAnswer = it.second?.second ?: RSVPAnswer.UNKNOWN, - rsvpComment = it.second?.third ?: "", + 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 new file mode 100644 index 0000000..d201440 --- /dev/null +++ b/app/src/main/java/eu/steffo/twom/room/RoomActivityInviteForm.kt @@ -0,0 +1,76 @@ +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/theme/iconLater.kt b/app/src/main/java/eu/steffo/twom/theme/iconLater.kt deleted file mode 100644 index 156de3b..0000000 --- a/app/src/main/java/eu/steffo/twom/theme/iconLater.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.steffo.twom.theme - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Schedule - -val iconLater = Icons.Outlined.Schedule diff --git a/app/src/main/java/eu/steffo/twom/theme/iconMaybe.kt b/app/src/main/java/eu/steffo/twom/theme/iconMaybe.kt deleted file mode 100644 index 53129ce..0000000 --- a/app/src/main/java/eu/steffo/twom/theme/iconMaybe.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.steffo.twom.theme - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.PauseCircle - -val iconMaybe = Icons.Outlined.PauseCircle diff --git a/app/src/main/java/eu/steffo/twom/theme/iconNoway.kt b/app/src/main/java/eu/steffo/twom/theme/iconNoway.kt deleted file mode 100644 index 14ccf28..0000000 --- a/app/src/main/java/eu/steffo/twom/theme/iconNoway.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.steffo.twom.theme - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.RemoveCircleOutline - -val iconNoway = Icons.Outlined.RemoveCircleOutline diff --git a/app/src/main/java/eu/steffo/twom/theme/iconSure.kt b/app/src/main/java/eu/steffo/twom/theme/iconSure.kt deleted file mode 100644 index 0e5b4ff..0000000 --- a/app/src/main/java/eu/steffo/twom/theme/iconSure.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.steffo.twom.theme - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.CheckCircle - -val iconSure = Icons.Outlined.CheckCircle diff --git a/app/src/main/java/eu/steffo/twom/theme/iconUnknown.kt b/app/src/main/java/eu/steffo/twom/theme/iconUnknown.kt deleted file mode 100644 index e3384c1..0000000 --- a/app/src/main/java/eu/steffo/twom/theme/iconUnknown.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.steffo.twom.theme - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Circle - -val iconUnknown = Icons.Outlined.Circle diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e9dcc4..f59cc93 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,4 +61,11 @@ No answer The Matrix room summary context has not been initialized. Could not find the requested Matrix room summary. + \@steffotwo:candy.steffo.eu + Username + Reason + Invite + Send an invite + Not opened + Hasn\'t opened the invite yet \ No newline at end of file