mirror of
https://github.com/Steffo99/twom.git
synced 2024-11-25 01:24:24 +00:00
Add basic invite button
This commit is contained in:
parent
acf9f34f59
commit
cca38d0026
9 changed files with 252 additions and 142 deletions
|
@ -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<Optional<Event>?>?): Triple<Event, RSVPAnswer, String>? {
|
||||
val event = request?.value?.getOrNull() ?: return null
|
||||
val content = event.content ?: return null
|
||||
|
@ -118,10 +109,10 @@ fun makeRSVP(request: State<Optional<Event>?>?): Triple<Event, RSVPAnswer, Strin
|
|||
|
||||
@Composable
|
||||
fun observeRsvpAsLiveState(room: Room, userId: String): Triple<Event, RSVPAnswer, String>? {
|
||||
val request = room.stateService().getStateEventLive(
|
||||
val stateRequest = room.stateService().getStateEventLive(
|
||||
eventType = "eu.steffo.twom.rsvp",
|
||||
stateKey = QueryStringValue.Equals(userId),
|
||||
).observeAsState()
|
||||
|
||||
return makeRSVP(request)
|
||||
return makeRSVP(stateRequest)
|
||||
}
|
|
@ -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<Failure.ServerError?>(null) }
|
||||
var errorMyRsvp by rememberSaveable { mutableStateOf<Throwable?>(null) }
|
||||
var isSendingInvite by rememberSaveable { mutableStateOf(false) }
|
||||
var errorInvite by rememberSaveable { mutableStateOf<Throwable?>(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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -61,4 +61,11 @@
|
|||
<string name="room_rsvp_unknown_label">No answer</string>
|
||||
<string name="room_error_roomsummary_missing">The Matrix room summary context has not been initialized.</string>
|
||||
<string name="room_error_roomsummary_notfound">Could not find the requested Matrix room summary.</string>
|
||||
<string name="room_invite_username_placeholder">\@steffotwo:candy.steffo.eu</string>
|
||||
<string name="room_invite_username_label">Username</string>
|
||||
<string name="room_rsvp_comment_label">Reason</string>
|
||||
<string name="room_invite_button_label">Invite</string>
|
||||
<string name="room_invite_title">Send an invite</string>
|
||||
<string name="room_rsvp_invited_label">Not opened</string>
|
||||
<string name="room_rsvp_invited_response">Hasn\'t opened the invite yet</string>
|
||||
</resources>
|
Loading…
Reference in a new issue