mirror of
https://github.com/Steffo99/twom.git
synced 2024-11-25 01:24:24 +00:00
Bulk rewrite continues
This commit is contained in:
parent
5befff9864
commit
01e744883f
74 changed files with 1644 additions and 1158 deletions
|
@ -36,7 +36,6 @@
|
|||
android:theme="@style/Theme.TwoM">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
@ -46,13 +45,17 @@
|
|||
android:theme="@style/Theme.TwoM" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.RoomActivity"
|
||||
android:name=".activities.ViewRoomActivity"
|
||||
android:theme="@style/Theme.TwoM" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.CreateRoomActivity"
|
||||
android:theme="@style/Theme.TwoM" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.InviteUserActivity"
|
||||
android:theme="@style/Theme.TwoM.BottomSheetDialog" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -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<Unit, String?>() {
|
||||
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") }
|
||||
}
|
||||
}
|
|
@ -9,12 +9,17 @@ import eu.steffo.twom.composables.login.LoginScaffold
|
|||
|
||||
|
||||
class LoginActivity : ComponentActivity() {
|
||||
class Contract : ActivityResultContract<Unit, Unit>() {
|
||||
class Contract : ActivityResultContract<Unit, Unit?>() {
|
||||
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() {
|
||||
|
|
|
@ -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<Intent>
|
||||
private lateinit var roomLauncher: ActivityResultLauncher<Intent>
|
||||
private lateinit var createLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
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",
|
||||
|
|
|
@ -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<String, Unit>() {
|
||||
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()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 ?: "?",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
AvatarURL(
|
||||
modifier = modifier,
|
||||
url = avatarUrl,
|
||||
fallbackText = fallbackText,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
|
@ -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<Uri?>(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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
)
|
|
@ -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
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
)
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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<User?>(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 }
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
|
@ -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)
|
||||
)
|
|
@ -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(),
|
||||
)
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
)
|
||||
},
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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<Optional<Event>?>?): Triple<Event, RSVPAnswer, String>? {
|
||||
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<Event, RSVPAnswer, String>? {
|
||||
val stateRequest = room.stateService().getStateEventLive(
|
||||
eventType = "eu.steffo.twom.rsvp",
|
||||
stateKey = QueryStringValue.Equals(userId),
|
||||
).observeAsState()
|
||||
|
||||
return makeRSVP(stateRequest)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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<Throwable?>(null) }
|
||||
var isSendingInvite by rememberSaveable { mutableStateOf(false) }
|
||||
var errorInvite by rememberSaveable { mutableStateOf<Throwable?>(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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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,
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
9
app/src/main/java/eu/steffo/twom/utils/RSVP.kt
Normal file
9
app/src/main/java/eu/steffo/twom/utils/RSVP.kt
Normal file
|
@ -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,
|
||||
)
|
227
app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt
Normal file
227
app/src/main/java/eu/steffo/twom/utils/RSVPAnswer.kt
Normal file
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package eu.steffo.twom.matrix
|
||||
package eu.steffo.twom.utils
|
||||
|
||||
import android.content.Context
|
||||
import eu.steffo.twom.R
|
|
@ -52,13 +52,10 @@
|
|||
<string name="room_rsvp_later_placeholder">Why will you be late?</string>
|
||||
<string name="room_rsvp_maybe_placeholder">What will determine your partecipation?</string>
|
||||
<string name="room_rsvp_noway_placeholder">Why won\'t you partecipate?</string>
|
||||
<string name="room_rsvp_unknown_response">Hasn\'t answered yet</string>
|
||||
<string name="room_rsvp_unknown_placeholder">Leave a comment…</string>
|
||||
<string name="room_update_label">Update</string>
|
||||
<string name="room_rsvp_update_label">Update</string>
|
||||
<string name="error_session_missing">The Matrix session context has not been initialized.</string>
|
||||
<string name="room_error_room_notfound">Could not find the requested Matrix room.</string>
|
||||
<string name="room_error_room_missing">The Matrix room context has not been initialized.</string>
|
||||
<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>
|
||||
|
@ -74,4 +71,15 @@
|
|||
<string name="login_error_wizard_generic">Something went wrong while setting up the login wizard: %1$s</string>
|
||||
<string name="login_error_login_generic">Something went wrong while logging in: %1$s</string>
|
||||
<string name="main_error_leave_generic">Something went wrong while leaving the room: %1$s</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="room_options_edit_text">Edit party</string>
|
||||
<string name="room_options_zoom_text">Zoom on avatar</string>
|
||||
<string name="room_error_members_notfound">Could not retrieve the list of room members.</string>
|
||||
<string name="room_rsvp_nullish_placeholder">Leave a comment...</string>
|
||||
<string name="room_rsvp_unknown_response">Has given an unsupported response</string>
|
||||
<string name="room_rsvp_none_response">Hasn\'t responded yet</string>
|
||||
<string name="room_rsvp_pending_response">Hasn\'t opened the invite yet</string>
|
||||
<string name="room_uninvite_label">Uninvite</string>
|
||||
<string name="room_error_publish_generic">Something went wrong while updating your RSVP: %1$s</string>
|
||||
<string name="room_error_redact_generic">Your response has been updated, but something went wrong while attempting to remove your previous one: %1$s</string>
|
||||
</resources>
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
<style name="Theme.TwoM" parent="@style/Theme.Material3.DayNight" />
|
||||
|
||||
<style name="Theme.TwoM.Dialog" parent="@style/Theme.Material3.DayNight.Dialog" />
|
||||
<style name="Theme.TwoM.BottomSheetDialog" parent="@style/Theme.Material3.DayNight.BottomSheetDialog" />
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue