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">
|
android:theme="@style/Theme.TwoM">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -46,13 +45,17 @@
|
||||||
android:theme="@style/Theme.TwoM" />
|
android:theme="@style/Theme.TwoM" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.RoomActivity"
|
android:name=".activities.ViewRoomActivity"
|
||||||
android:theme="@style/Theme.TwoM" />
|
android:theme="@style/Theme.TwoM" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.CreateRoomActivity"
|
android:name=".activities.CreateRoomActivity"
|
||||||
android:theme="@style/Theme.TwoM" />
|
android:theme="@style/Theme.TwoM" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.InviteUserActivity"
|
||||||
|
android:theme="@style/Theme.TwoM.BottomSheetDialog" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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 LoginActivity : ComponentActivity() {
|
||||||
class Contract : ActivityResultContract<Unit, Unit>() {
|
class Contract : ActivityResultContract<Unit, Unit?>() {
|
||||||
override fun createIntent(context: Context, input: Unit): Intent {
|
override fun createIntent(context: Context, input: Unit): Intent {
|
||||||
return Intent(context, LoginActivity::class.java)
|
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() {
|
override fun onStart() {
|
||||||
|
|
|
@ -7,12 +7,10 @@ import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import eu.steffo.twom.composables.main.MainScaffold
|
import eu.steffo.twom.composables.main.MainScaffold
|
||||||
import eu.steffo.twom.matrix.TwoMMatrix
|
import eu.steffo.twom.utils.TwoMGlobals
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
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() {
|
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
|
private var session: Session? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
TwoMMatrix.ensureMatrix(applicationContext)
|
TwoMGlobals.ensureMatrix(applicationContext)
|
||||||
|
|
||||||
fetchLastSession()
|
fetchLastSession()
|
||||||
openSession()
|
openSession()
|
||||||
|
|
||||||
loginLauncher =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult(),
|
|
||||||
this::onLogin
|
|
||||||
)
|
|
||||||
|
|
||||||
roomLauncher =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult(),
|
|
||||||
this::onRoom
|
|
||||||
)
|
|
||||||
|
|
||||||
createLauncher =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult(),
|
|
||||||
this::onCreate
|
|
||||||
)
|
|
||||||
|
|
||||||
resetContent()
|
resetContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +39,7 @@ class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private fun fetchLastSession() {
|
private fun fetchLastSession() {
|
||||||
Log.d("Main", "Fetching the last successfully authenticated session...")
|
Log.d("Main", "Fetching the last successfully authenticated session...")
|
||||||
session = TwoMMatrix.matrix.authenticationService().getLastAuthenticatedSession()
|
session = TwoMGlobals.matrix.authenticationService().getLastAuthenticatedSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSession() {
|
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) {
|
private fun onCreate(result: ActivityResult) {
|
||||||
Log.d("Main", "Received result from create activity: $result")
|
Log.d("Main", "Received result from create activity: $result")
|
||||||
if (result.resultCode == RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
|
@ -146,7 +100,7 @@ class MainActivity : ComponentActivity() {
|
||||||
createRoomParams.name = name
|
createRoomParams.name = name
|
||||||
createRoomParams.topic = description
|
createRoomParams.topic = description
|
||||||
createRoomParams.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT
|
createRoomParams.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT
|
||||||
createRoomParams.roomType = TwoMMatrix.ROOM_TYPE
|
createRoomParams.roomType = TwoMGlobals.ROOM_TYPE
|
||||||
createRoomParams.initialStates = mutableListOf(
|
createRoomParams.initialStates = mutableListOf(
|
||||||
CreateRoomStateEvent(
|
CreateRoomStateEvent(
|
||||||
type = "m.room.power_levels",
|
type = "m.room.power_levels",
|
||||||
|
|
|
@ -7,18 +7,18 @@ import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import eu.steffo.twom.matrix.TwoMMatrix
|
import eu.steffo.twom.composables.viewroom.ViewRoomScaffold
|
||||||
import eu.steffo.twom.room.RoomActivityScaffold
|
import eu.steffo.twom.utils.TwoMGlobals
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
class RoomActivity : ComponentActivity() {
|
class ViewRoomActivity : ComponentActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
const val ROOM_ID_EXTRA = "roomId"
|
const val ROOM_ID_EXTRA = "roomId"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Contract : ActivityResultContract<String, Unit>() {
|
class Contract : ActivityResultContract<String, Unit>() {
|
||||||
override fun createIntent(context: Context, input: String): Intent {
|
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)
|
intent.putExtra(ROOM_ID_EXTRA, input)
|
||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class RoomActivity : ComponentActivity() {
|
||||||
private fun fetchLastSession() {
|
private fun fetchLastSession() {
|
||||||
Log.d("Main", "Fetching the last successfully authenticated session...")
|
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...
|
// 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -47,13 +47,9 @@ class RoomActivity : ComponentActivity() {
|
||||||
val roomId = intent.getStringExtra(ROOM_ID_EXTRA)
|
val roomId = intent.getStringExtra(ROOM_ID_EXTRA)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
RoomActivityScaffold(
|
ViewRoomScaffold(
|
||||||
session = session,
|
session = session,
|
||||||
roomId = roomId!!, // FIXME: Again, this should be set. Should.
|
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.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
@ -8,16 +8,13 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
|
||||||
import androidx.compose.ui.semantics.semantics
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(widthDp = 40, heightDp = 40)
|
||||||
fun AvatarFromDefault(
|
fun AvatarEmpty(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
fallbackText: String = "?",
|
text: String? = null,
|
||||||
contentDescription: String = "",
|
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
@ -26,12 +23,9 @@ fun AvatarFromDefault(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.Center)
|
.align(Alignment.Center),
|
||||||
.semantics {
|
|
||||||
this.contentDescription = ""
|
|
||||||
},
|
|
||||||
color = MaterialTheme.colorScheme.onTertiary,
|
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.foundation.Image
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -6,21 +6,25 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.layout.ContentScale
|
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
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(widthDp = 40, heightDp = 40)
|
||||||
fun AvatarFromImageBitmap(
|
fun AvatarImage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
bitmap: ImageBitmap? = null,
|
bitmap: ImageBitmap? = null,
|
||||||
fallbackText: String = "?",
|
fallbackText: String? = null,
|
||||||
contentDescription: String = "",
|
contentDescription: String = "",
|
||||||
) {
|
) {
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
AvatarFromDefault(
|
AvatarEmpty(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
fallbackText = fallbackText,
|
.semantics {
|
||||||
contentDescription = contentDescription,
|
this.contentDescription = contentDescription
|
||||||
|
},
|
||||||
|
text = fallbackText,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
|
@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.matrix.avatar.AvatarFromImageBitmap
|
|
||||||
import eu.steffo.twom.utils.BitmapUtilities
|
import eu.steffo.twom.utils.BitmapUtilities
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -49,7 +48,7 @@ fun AvatarPicker(
|
||||||
launcher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
|
launcher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
AvatarFromImageBitmap(
|
AvatarImage(
|
||||||
bitmap = selection?.asImageBitmap(),
|
bitmap = selection?.asImageBitmap(),
|
||||||
fallbackText = fallbackText,
|
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.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
@ -12,16 +12,15 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(widthDp = 40, heightDp = 40)
|
||||||
fun AvatarFromURL(
|
fun AvatarURL(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
url: String? = "",
|
url: String? = "",
|
||||||
fallbackText: String = "?",
|
fallbackText: String? = null,
|
||||||
contentDescription: String = "",
|
contentDescription: String = "",
|
||||||
) {
|
) {
|
||||||
val session = LocalSession.current
|
val session = LocalSession.current
|
||||||
|
@ -29,22 +28,22 @@ fun AvatarFromURL(
|
||||||
|
|
||||||
LaunchedEffect(session, url) GetAvatar@{
|
LaunchedEffect(session, url) GetAvatar@{
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
Log.d("Avatar", "Not doing anything, session is null.")
|
Log.d("AvatarURL", "Not doing anything, session is null.")
|
||||||
bitmap = null
|
bitmap = null
|
||||||
return@GetAvatar
|
return@GetAvatar
|
||||||
}
|
}
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
Log.d("Avatar", "URL is null, not downloading anything.")
|
Log.d("AvatarURL", "URL is null, not downloading anything.")
|
||||||
bitmap = null
|
bitmap = null
|
||||||
return@GetAvatar
|
return@GetAvatar
|
||||||
}
|
}
|
||||||
if (url.isEmpty()) {
|
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
|
bitmap = null
|
||||||
return@GetAvatar
|
return@GetAvatar
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("Avatar", "Downloading avatar at: $url")
|
Log.d("AvatarURL", "Downloading avatar at: $url")
|
||||||
lateinit var avatarFile: File
|
lateinit var avatarFile: File
|
||||||
try {
|
try {
|
||||||
avatarFile = session.fileService().downloadFile(
|
avatarFile = session.fileService().downloadFile(
|
||||||
|
@ -53,27 +52,20 @@ fun AvatarFromURL(
|
||||||
mimeType = null,
|
mimeType = null,
|
||||||
elementToDecrypt = null,
|
elementToDecrypt = null,
|
||||||
)
|
)
|
||||||
} catch (f: Failure.OtherServerError) {
|
} catch (e: Throwable) {
|
||||||
Log.e("Avatar", "Unable to download avatar at: $url", f)
|
Log.e("AvatarURL", "Unable to download avatar at: $url", e)
|
||||||
return@GetAvatar
|
return@GetAvatar
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should I check the MIME type? And the size of the image?
|
// 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)
|
bitmap = BitmapFactory.decodeFile(avatarFile.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap == null) {
|
AvatarImage(
|
||||||
AvatarFromDefault(
|
modifier = modifier,
|
||||||
modifier = modifier,
|
bitmap = bitmap?.asImageBitmap(),
|
||||||
fallbackText = fallbackText,
|
fallbackText = fallbackText,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
AvatarFromImageBitmap(
|
|
||||||
modifier = modifier,
|
|
||||||
bitmap = bitmap!!.asImageBitmap(),
|
|
||||||
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 android.util.Log
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -9,11 +9,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(widthDp = 40, heightDp = 40)
|
||||||
fun AvatarFromUserId(
|
fun AvatarUserId(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
userId: String = "",
|
userId: String = "",
|
||||||
fallbackText: String = "?",
|
fallbackText: String = "?",
|
||||||
|
@ -24,30 +24,24 @@ fun AvatarFromUserId(
|
||||||
|
|
||||||
LaunchedEffect(session, userId) GetAvatarUrl@{
|
LaunchedEffect(session, userId) GetAvatarUrl@{
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
Log.d("UserAvatar", "Not doing anything, session is null.")
|
Log.d("AvatarUser", "Not doing anything, session is null.")
|
||||||
return@GetAvatarUrl
|
return@GetAvatarUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userId.isEmpty()) {
|
if (userId.isEmpty()) {
|
||||||
Log.d("UserAvatar", "Not doing anything, userId is empty.")
|
Log.d("AvatarUser", "Not doing anything, userId is empty.")
|
||||||
return@GetAvatarUrl
|
return@GetAvatarUrl
|
||||||
}
|
}
|
||||||
Log.d("UserAvatar", "Retrieving avatar url for: $userId...")
|
|
||||||
|
Log.d("AvatarUser", "Retrieving avatar url for: $userId...")
|
||||||
avatarUrl = session.profileService().getAvatarUrl(userId).getOrNull()
|
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) {
|
AvatarURL(
|
||||||
AvatarFromDefault(
|
modifier = modifier,
|
||||||
modifier = modifier,
|
url = avatarUrl,
|
||||||
fallbackText = fallbackText,
|
fallbackText = fallbackText,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
AvatarFromURL(
|
|
||||||
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 androidx.compose.ui.unit.dp
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
import eu.steffo.twom.composables.avatar.AvatarPicker
|
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
|
import eu.steffo.twom.utils.BitmapUtilities
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -39,7 +39,7 @@ fun CreateRoomForm(
|
||||||
var avatarUri by rememberSaveable { mutableStateOf<Uri?>(null) }
|
var avatarUri by rememberSaveable { mutableStateOf<Uri?>(null) }
|
||||||
|
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
val avatarContentDescription = stringResource(R.string.create_avatar_label)
|
val avatarContentDescription = stringResource(R.string.create_avatar_label)
|
||||||
AvatarPicker(
|
AvatarPicker(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -67,7 +67,7 @@ fun CreateRoomForm(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
TextField(
|
TextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(180.dp)
|
.height(180.dp)
|
||||||
|
@ -80,7 +80,7 @@ fun CreateRoomForm(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.activities.CreateRoomActivity
|
import eu.steffo.twom.activities.CreateRoomActivity
|
||||||
import eu.steffo.twom.theme.TwoMTheme
|
import eu.steffo.twom.composables.theme.TwoMTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@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.ErrorText
|
||||||
import eu.steffo.twom.composables.errorhandling.LocalizableError
|
import eu.steffo.twom.composables.errorhandling.LocalizableError
|
||||||
import eu.steffo.twom.composables.fields.PasswordField
|
import eu.steffo.twom.composables.fields.PasswordField
|
||||||
import eu.steffo.twom.matrix.TwoMMatrix
|
import eu.steffo.twom.composables.theme.basePadding
|
||||||
import eu.steffo.twom.theme.TwoMPadding
|
import eu.steffo.twom.utils.TwoMGlobals
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
|
||||||
|
@ -66,7 +66,7 @@ fun LoginForm(
|
||||||
|
|
||||||
Log.d("Login", "Getting authentication service...")
|
Log.d("Login", "Getting authentication service...")
|
||||||
loginStep = LoginStep.SERVICE
|
loginStep = LoginStep.SERVICE
|
||||||
val auth = TwoMMatrix.matrix.authenticationService()
|
val auth = TwoMGlobals.matrix.authenticationService()
|
||||||
|
|
||||||
Log.d("Login", "Resetting authentication service...")
|
Log.d("Login", "Resetting authentication service...")
|
||||||
auth.reset()
|
auth.reset()
|
||||||
|
@ -174,10 +174,10 @@ fun LoginForm(
|
||||||
progress = loginStep.step.toFloat() / LoginStep.DONE.step.toFloat(),
|
progress = loginStep.step.toFloat() / LoginStep.DONE.step.toFloat(),
|
||||||
color = if (error.occurred()) MaterialTheme.colorScheme.error else ProgressIndicatorDefaults.linearColor
|
color = if (error.occurred()) MaterialTheme.colorScheme.error else ProgressIndicatorDefaults.linearColor
|
||||||
)
|
)
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
Text(LocalContext.current.getString(R.string.login_text))
|
Text(LocalContext.current.getString(R.string.login_text))
|
||||||
}
|
}
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
TextField(
|
TextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
|
@ -194,7 +194,7 @@ fun LoginForm(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
PasswordField(
|
PasswordField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
|
@ -211,7 +211,7 @@ fun LoginForm(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = (username != "" && (loginStep == LoginStep.NONE || error.occurred())),
|
enabled = (username != "" && (loginStep == LoginStep.NONE || error.occurred())),
|
||||||
|
@ -223,7 +223,7 @@ fun LoginForm(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error.Show {
|
error.Show {
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
ErrorText(it)
|
ErrorText(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
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
|
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 androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
import eu.steffo.twom.activities.LoginActivity
|
import eu.steffo.twom.activities.LoginActivity
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.avatar.AvatarUserId
|
||||||
import eu.steffo.twom.matrix.avatar.AvatarFromUserId
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
|
@ -35,7 +35,11 @@ fun AccountIconButton(
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val loginLauncher =
|
val loginLauncher =
|
||||||
rememberLauncherForActivityResult(LoginActivity.Contract()) { processLogin() }
|
rememberLauncherForActivityResult(LoginActivity.Contract()) {
|
||||||
|
if (it != null) {
|
||||||
|
processLogin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(modifier) {
|
Box(modifier) {
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -47,7 +51,7 @@ fun AccountIconButton(
|
||||||
contentDescription = LocalContext.current.getString(R.string.main_account_label),
|
contentDescription = LocalContext.current.getString(R.string.main_account_label),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AvatarFromUserId(
|
AvatarUserId(
|
||||||
userId = session.myUserId,
|
userId = session.myUserId,
|
||||||
contentDescription = LocalContext.current.getString(R.string.main_account_label),
|
contentDescription = LocalContext.current.getString(R.string.main_account_label),
|
||||||
)
|
)
|
||||||
|
@ -60,7 +64,7 @@ fun AccountIconButton(
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text(stringResource(id = R.string.main_account_login_text))
|
Text(stringResource(R.string.main_account_login_text))
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
expanded = false
|
expanded = false
|
||||||
|
@ -70,7 +74,7 @@ fun AccountIconButton(
|
||||||
} else {
|
} else {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text(stringResource(id = R.string.main_account_logout_text))
|
Text(stringResource(R.string.main_account_logout_text))
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
expanded = false
|
expanded = false
|
||||||
|
@ -80,5 +84,4 @@ fun AccountIconButton(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package eu.steffo.twom.composables.main
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
@ -10,16 +12,24 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
|
import eu.steffo.twom.activities.CreateRoomActivity
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun CreateRoomFAB(
|
fun CreateRoomFAB(
|
||||||
modifier: Modifier = Modifier,
|
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(
|
ExtendedFloatingActionButton(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClick = onClick,
|
onClick = { launcher.launch() },
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.Add,
|
Icons.Filled.Add,
|
||||||
|
|
|
@ -8,11 +8,10 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
import eu.steffo.twom.main.RoomListItem
|
import eu.steffo.twom.composables.errorhandling.ErrorText
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
import eu.steffo.twom.matrix.TwoMMatrix
|
import eu.steffo.twom.composables.theme.basePadding
|
||||||
import eu.steffo.twom.theme.ErrorText
|
import eu.steffo.twom.utils.TwoMGlobals
|
||||||
import eu.steffo.twom.theme.TwoMPadding
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
|
|
||||||
|
@ -29,19 +28,19 @@ fun MainContentLoggedIn(
|
||||||
val roomSummaries by session.roomService().getRoomSummariesLive(
|
val roomSummaries by session.roomService().getRoomSummariesLive(
|
||||||
roomSummaryQueryParams {
|
roomSummaryQueryParams {
|
||||||
this.memberships = listOf(Membership.JOIN)
|
this.memberships = listOf(Membership.JOIN)
|
||||||
this.includeType = listOf(TwoMMatrix.ROOM_TYPE)
|
this.includeType = listOf(TwoMGlobals.ROOM_TYPE)
|
||||||
}
|
}
|
||||||
).observeAsState()
|
).observeAsState()
|
||||||
|
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
if (roomSummaries == null) {
|
if (roomSummaries == null) {
|
||||||
Text(
|
Text(
|
||||||
modifier = TwoMPadding.base,
|
modifier = Modifier.basePadding(),
|
||||||
text = stringResource(R.string.loading)
|
text = stringResource(R.string.loading)
|
||||||
)
|
)
|
||||||
} else if (roomSummaries!!.isEmpty()) {
|
} else if (roomSummaries!!.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
modifier = TwoMPadding.base,
|
modifier = Modifier.basePadding(),
|
||||||
text = stringResource(R.string.main_roomlist_empty_text)
|
text = stringResource(R.string.main_roomlist_empty_text)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
import eu.steffo.twom.theme.TwoMPadding
|
import eu.steffo.twom.composables.theme.basePadding
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
|
@ -17,10 +17,10 @@ fun MainContentNotLoggedIn(
|
||||||
onClickLogin: () -> Unit = {},
|
onClickLogin: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
Row(TwoMPadding.base) {
|
Row(Modifier.basePadding()) {
|
||||||
Text(LocalContext.current.getString(R.string.main_notloggedin_text_1))
|
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))
|
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.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
import eu.steffo.twom.theme.TwoMTheme
|
import eu.steffo.twom.composables.theme.TwoMTheme
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -15,6 +15,7 @@ import org.matrix.android.sdk.api.session.Session
|
||||||
fun MainScaffold(
|
fun MainScaffold(
|
||||||
processLogin: () -> Unit = {},
|
processLogin: () -> Unit = {},
|
||||||
processLogout: () -> Unit = {},
|
processLogout: () -> Unit = {},
|
||||||
|
processCreate: (name: String, description: String, avatarUri: String?) -> Unit = { _, _, _ -> },
|
||||||
session: Session? = null,
|
session: Session? = null,
|
||||||
) {
|
) {
|
||||||
TwoMTheme {
|
TwoMTheme {
|
||||||
|
@ -27,7 +28,9 @@ fun MainScaffold(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
CreateRoomFAB()
|
CreateRoomFAB(
|
||||||
|
onCreateParamsSelected = processCreate,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.steffo.twom.main
|
package eu.steffo.twom.composables.main
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
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.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.steffo.twom.R
|
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.composables.errorhandling.LocalizableError
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
import eu.steffo.twom.matrix.avatar.AvatarFromURL
|
|
||||||
import eu.steffo.twom.theme.ErrorText
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
|
@ -50,11 +50,11 @@ fun RoomListItem(
|
||||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||||
val error by remember { mutableStateOf(LocalizableError()) }
|
val error by remember { mutableStateOf(LocalizableError()) }
|
||||||
|
|
||||||
val roomActivityLauncher = rememberLauncherForActivityResult(RoomActivity.Contract()) {}
|
val viewRoomActivityLauncher = rememberLauncherForActivityResult(ViewRoomActivity.Contract()) {}
|
||||||
|
|
||||||
fun openRoom() {
|
fun openRoom() {
|
||||||
Log.i("Main", "Opening room `$roomId`...")
|
Log.i("Main", "Opening room `$roomId`...")
|
||||||
roomActivityLauncher.launch(roomId)
|
viewRoomActivityLauncher.launch(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun leaveRoom() {
|
suspend fun leaveRoom() {
|
||||||
|
@ -83,7 +83,7 @@ fun RoomListItem(
|
||||||
.size(40.dp)
|
.size(40.dp)
|
||||||
.clip(MaterialTheme.shapes.medium)
|
.clip(MaterialTheme.shapes.medium)
|
||||||
) {
|
) {
|
||||||
AvatarFromURL(
|
AvatarURL(
|
||||||
// FIXME: URL can appearently be set before the image is available on the homeserver
|
// FIXME: URL can appearently be set before the image is available on the homeserver
|
||||||
url = roomSummary.avatarUrl,
|
url = roomSummary.avatarUrl,
|
||||||
)
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.steffo.twom.matrix
|
package eu.steffo.twom.composables.matrix
|
||||||
|
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import org.matrix.android.sdk.api.session.Session
|
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 android.app.Activity
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
@ -18,6 +18,7 @@ import androidx.core.view.WindowCompat
|
||||||
fun TwoMTheme(
|
fun TwoMTheme(
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val darkTheme = isSystemInDarkTheme()
|
val darkTheme = isSystemInDarkTheme()
|
||||||
|
@ -27,21 +28,21 @@ fun TwoMTheme(
|
||||||
}
|
}
|
||||||
val typography = Typography()
|
val typography = Typography()
|
||||||
|
|
||||||
MaterialTheme(
|
|
||||||
colorScheme = colorScheme,
|
|
||||||
typography = typography,
|
|
||||||
content = content
|
|
||||||
)
|
|
||||||
|
|
||||||
val view = LocalView.current
|
|
||||||
if (!view.isInEditMode) {
|
if (!view.isInEditMode) {
|
||||||
SideEffect {
|
SideEffect {
|
||||||
val window = (view.context as Activity).window
|
val window = (context as Activity).window
|
||||||
window.statusBarColor = colorScheme.surface.toArgb()
|
window.statusBarColor = colorScheme.surface.toArgb()
|
||||||
window.navigationBarColor = colorScheme.surface.toArgb()
|
window.navigationBarColor = colorScheme.surface.toArgb()
|
||||||
|
|
||||||
val insets = WindowCompat.getInsetsController(window, view)
|
val insets = WindowCompat.getInsetsController(window, view)
|
||||||
insets.isAppearanceLightStatusBars = !darkTheme
|
insets.isAppearanceLightStatusBars = !darkTheme
|
||||||
insets.isAppearanceLightNavigationBars = !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 androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
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 androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
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.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
|
@ -8,47 +8,45 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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 androidx.compose.ui.unit.dp
|
||||||
|
import eu.steffo.twom.utils.RSVPAnswer
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RSVPAnswerFilterChip(
|
@Preview
|
||||||
|
fun RSVPChip(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
representing: RSVPAnswer,
|
|
||||||
selected: Boolean = false,
|
selected: Boolean = false,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
|
representedAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
|
||||||
) {
|
) {
|
||||||
val icon = representing.toIcon()
|
|
||||||
val colorRole = representing.toStaticColorRole()
|
|
||||||
val labelResourceId = representing.toLabelResourceId()
|
|
||||||
|
|
||||||
FilterChip(
|
FilterChip(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
selected = selected,
|
selected = selected,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = representedAnswer.icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(labelResourceId),
|
text = representedAnswer.toLabel() ?: "[missing label]",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
colors = FilterChipDefaults.filterChipColors(
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
iconColor = colorRole.value,
|
iconColor = representedAnswer.staticColorRole.color(),
|
||||||
labelColor = colorRole.value,
|
labelColor = representedAnswer.staticColorRole.color(),
|
||||||
selectedContainerColor = colorRole.valueContainer,
|
selectedContainerColor = representedAnswer.staticColorRole.containerColor(),
|
||||||
selectedLeadingIconColor = colorRole.onValueContainer,
|
selectedLeadingIconColor = representedAnswer.staticColorRole.onContainerColor(),
|
||||||
selectedLabelColor = colorRole.onValueContainer,
|
selectedLabelColor = representedAnswer.staticColorRole.onContainerColor(),
|
||||||
),
|
),
|
||||||
border = FilterChipDefaults.filterChipBorder(
|
border = FilterChipDefaults.filterChipBorder(
|
||||||
borderColor = MaterialTheme.colorScheme.surfaceVariant,
|
borderColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
selectedBorderColor = colorRole.onValueContainer,
|
selectedBorderColor = representedAnswer.staticColorRole.onContainerColor(),
|
||||||
borderWidth = 1.dp,
|
borderWidth = 1.dp,
|
||||||
selectedBorderWidth = 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.horizontalScroll
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
@ -9,11 +9,12 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
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
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun RSVPAnswerSelectRow(
|
fun RSVPChipRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
value: RSVPAnswer = RSVPAnswer.UNKNOWN,
|
value: RSVPAnswer = RSVPAnswer.UNKNOWN,
|
||||||
onChange: (answer: RSVPAnswer) -> Unit = {},
|
onChange: (answer: RSVPAnswer) -> Unit = {},
|
||||||
|
@ -37,27 +38,27 @@ fun RSVPAnswerSelectRow(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 8.dp, end = 8.dp)
|
.padding(start = 8.dp, end = 8.dp)
|
||||||
) {
|
) {
|
||||||
RSVPAnswerFilterChip(
|
RSVPChip(
|
||||||
modifier = TwoMPadding.chips,
|
modifier = Modifier.chipPadding(),
|
||||||
representing = RSVPAnswer.SURE,
|
representedAnswer = RSVPAnswer.SURE,
|
||||||
selected = (value == RSVPAnswer.SURE),
|
selected = (value == RSVPAnswer.SURE),
|
||||||
onClick = toggleSwitch(RSVPAnswer.SURE)
|
onClick = toggleSwitch(RSVPAnswer.SURE)
|
||||||
)
|
)
|
||||||
RSVPAnswerFilterChip(
|
RSVPChip(
|
||||||
modifier = TwoMPadding.chips,
|
modifier = Modifier.chipPadding(),
|
||||||
representing = RSVPAnswer.LATER,
|
representedAnswer = RSVPAnswer.LATER,
|
||||||
selected = (value == RSVPAnswer.LATER),
|
selected = (value == RSVPAnswer.LATER),
|
||||||
onClick = toggleSwitch(RSVPAnswer.LATER)
|
onClick = toggleSwitch(RSVPAnswer.LATER)
|
||||||
)
|
)
|
||||||
RSVPAnswerFilterChip(
|
RSVPChip(
|
||||||
modifier = TwoMPadding.chips,
|
modifier = Modifier.chipPadding(),
|
||||||
representing = RSVPAnswer.MAYBE,
|
representedAnswer = RSVPAnswer.MAYBE,
|
||||||
selected = (value == RSVPAnswer.MAYBE),
|
selected = (value == RSVPAnswer.MAYBE),
|
||||||
onClick = toggleSwitch(RSVPAnswer.MAYBE)
|
onClick = toggleSwitch(RSVPAnswer.MAYBE)
|
||||||
)
|
)
|
||||||
RSVPAnswerFilterChip(
|
RSVPChip(
|
||||||
modifier = TwoMPadding.chips,
|
modifier = Modifier.chipPadding(),
|
||||||
representing = RSVPAnswer.NOWAY,
|
representedAnswer = RSVPAnswer.NOWAY,
|
||||||
selected = (value == RSVPAnswer.NOWAY),
|
selected = (value == RSVPAnswer.NOWAY),
|
||||||
onClick = toggleSwitch(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.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.R
|
import eu.steffo.twom.R
|
||||||
|
import eu.steffo.twom.utils.RSVPAnswer
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
|
@ -16,22 +17,20 @@ fun RSVPUpdateButton(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
rsvpAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
|
currentAnswer: RSVPAnswer = RSVPAnswer.UNKNOWN,
|
||||||
) {
|
) {
|
||||||
val colorRole = rsvpAnswer.toStaticColorRole()
|
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
shape = MaterialTheme.shapes.small,
|
shape = MaterialTheme.shapes.small,
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = colorRole.value,
|
containerColor = currentAnswer.staticColorRole.color(),
|
||||||
contentColor = colorRole.onValue,
|
contentColor = currentAnswer.staticColorRole.onColor(),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
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.foundation.layout.padding
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
@ -7,16 +7,15 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import eu.steffo.twom.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
import eu.steffo.twom.theme.TwoMTheme
|
import eu.steffo.twom.composables.theme.TwoMTheme
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomActivityScaffold(
|
fun ViewRoomScaffold(
|
||||||
session: Session,
|
session: Session,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
onBack: () -> Unit = {},
|
|
||||||
) {
|
) {
|
||||||
val room = Optional.ofNullable(session.roomService().getRoom(roomId))
|
val room = Optional.ofNullable(session.roomService().getRoom(roomId))
|
||||||
val roomSummary by session.roomService().getRoomSummaryLive(roomId).observeAsState()
|
val roomSummary by session.roomService().getRoomSummaryLive(roomId).observeAsState()
|
||||||
|
@ -27,12 +26,10 @@ fun RoomActivityScaffold(
|
||||||
CompositionLocalProvider(LocalRoomSummary provides roomSummary) {
|
CompositionLocalProvider(LocalRoomSummary provides roomSummary) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
RoomActivityTopBar(
|
ViewRoomTopBar()
|
||||||
onBack = onBack,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
RoomActivityContent(
|
ViewRoomContent(
|
||||||
modifier = Modifier.padding(it),
|
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.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -9,7 +9,7 @@ import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
/**
|
/**
|
||||||
* Object containing the global state of the application.
|
* Object containing the global state of the application.
|
||||||
*/
|
*/
|
||||||
object TwoMMatrix {
|
object TwoMGlobals {
|
||||||
/**
|
/**
|
||||||
* The global [Matrix] object of the application.
|
* The global [Matrix] object of the application.
|
||||||
*
|
*
|
||||||
|
@ -38,4 +38,10 @@ object TwoMMatrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
const val ROOM_TYPE = "eu.steffo.twom.happening"
|
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 android.content.Context
|
||||||
import eu.steffo.twom.R
|
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_later_placeholder">Why will you be late?</string>
|
||||||
<string name="room_rsvp_maybe_placeholder">What will determine your partecipation?</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_noway_placeholder">Why won\'t you partecipate?</string>
|
||||||
<string name="room_rsvp_unknown_response">Hasn\'t answered yet</string>
|
<string name="room_rsvp_update_label">Update</string>
|
||||||
<string name="room_rsvp_unknown_placeholder">Leave a comment…</string>
|
|
||||||
<string name="room_update_label">Update</string>
|
|
||||||
<string name="error_session_missing">The Matrix session context has not been initialized.</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_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_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_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_error_roomsummary_notfound">Could not find the requested Matrix room summary.</string>
|
||||||
<string name="room_invite_username_placeholder">\@steffotwo:candy.steffo.eu</string>
|
<string name="room_invite_username_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_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="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="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>
|
</resources>
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
<style name="Theme.TwoM" parent="@style/Theme.Material3.DayNight" />
|
<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>
|
</resources>
|
Loading…
Reference in a new issue