Merge branch 'develop' into feature/fga/improve_node_architecture

This commit is contained in:
ganfra 2023-03-09 12:00:34 +01:00
commit 7455c4923f
57 changed files with 1061 additions and 54 deletions

View file

@ -43,6 +43,7 @@ dependencies {
implementation(projects.features.preferences.api)
implementation(projects.features.logout.api)
implementation(projects.features.onboarding.api)
implementation(projects.features.createroom)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)

View file

@ -35,6 +35,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.createroom.CreateRoomFlowNode
import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.libraries.architecture.BackstackNode
@ -107,6 +108,9 @@ class LoggedInFlowNode @AssistedInject constructor(
@Parcelize
object Settings : NavTarget
@Parcelize
object CreateRoom : NavTarget
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
@ -120,6 +124,10 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun onSettingsClicked() {
backstack.push(NavTarget.Settings)
}
override fun onCreateRoomClicked() {
backstack.push(NavTarget.CreateRoom)
}
}
roomListEntryPoint
.nodeBuilder(this, buildContext)
@ -151,6 +159,9 @@ class LoggedInFlowNode @AssistedInject constructor(
.callback(callback)
.build()
}
NavTarget.CreateRoom -> {
CreateRoomFlowNode(buildContext)
}
}
}

1
changelog.d/94.wip Normal file
View file

@ -0,0 +1 @@
[Create and join rooms] Start new chat screen (UI)

1
features/createroom/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
id("kotlin-parcelize")
}
android {
namespace = "io.element.android.features.createroom"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
anvil(projects.anvilcodegen)
implementation(projects.anvilannotations)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.elementresources)
implementation(projects.libraries.uiStrings)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)
}

View file

21
features/createroom/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest>
</manifest>

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.navmodel.backstack.BackStack
import io.element.android.features.createroom.root.CreateRoomRootNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
import kotlinx.parcelize.Parcelize
class CreateRoomFlowNode(
buildContext: BuildContext,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
),
) : ParentNode<CreateRoomFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
) {
sealed interface NavTarget : Parcelable {
@Parcelize
object Root : NavTarget
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.Root -> createNode<CreateRoomRootNode>(buildContext)
}
}
@Composable
override fun View(modifier: Modifier) {
Children(
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
sealed interface CreateRoomRootEvents {
object CreateRoom : CreateRoomRootEvents
object InvitePeople : CreateRoomRootEvents
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.libraries.di.SessionScope
import kotlinx.parcelize.Parcelize
@ContributesNode(SessionScope::class)
class CreateRoomRootNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: CreateRoomRootPresenter,
) : Node(buildContext, plugins = plugins) {
sealed interface NavTarget : Parcelable {
@Parcelize
object Root : NavTarget
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
CreateRoomRootScreen(
state = state,
modifier = modifier,
onClosePressed = this::navigateUp,
)
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
import androidx.compose.runtime.Composable
import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject
class CreateRoomRootPresenter @Inject constructor() : Presenter<CreateRoomRootState> {
@Composable
override fun present(): CreateRoomRootState {
fun handleEvents(event: CreateRoomRootEvents) {
when (event) {
CreateRoomRootEvents.CreateRoom -> Unit // Todo Handle create room action
CreateRoomRootEvents.InvitePeople -> Unit // Todo Handle invite people action
}
}
return CreateRoomRootState(
eventSink = ::handleEvents
)
}
}

View file

@ -0,0 +1,236 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SearchBarDefaults
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
import io.element.android.libraries.designsystem.theme.components.DockedSearchBar
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.R as DrawableR
import io.element.android.libraries.ui.strings.R as StringR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateRoomRootScreen(
state: CreateRoomRootState,
modifier: Modifier = Modifier,
onClosePressed: () -> Unit = {},
) {
var searchText by rememberSaveable { mutableStateOf("") }
var isSearchActive by rememberSaveable { mutableStateOf(false) }
Scaffold(
modifier = modifier.fillMaxWidth(),
topBar = {
if (!isSearchActive) {
CreateRoomRootViewTopBar(onClosePressed = onClosePressed)
}
}
) { paddingValues ->
Column(
modifier = Modifier.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
CreateRoomSearchBar(
modifier = Modifier.fillMaxWidth(),
text = searchText,
placeHolderTitle = stringResource(StringR.string.search_for_someone),
active = isSearchActive,
onActiveChanged = { isSearchActive = it },
onTextChanged = { searchText = it },
)
if (!isSearchActive) {
CreateRoomActionButtonsList(
onNewRoomClicked = { state.eventSink(CreateRoomRootEvents.CreateRoom) },
onInvitePeopleClicked = { state.eventSink(CreateRoomRootEvents.InvitePeople) },
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateRoomRootViewTopBar(
modifier: Modifier = Modifier,
onClosePressed: () -> Unit = {},
) {
CenterAlignedTopAppBar(
modifier = modifier,
title = {
Text(
text = stringResource(id = StringR.string.start_chat),
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
)
},
actions = {
IconButton(onClick = onClosePressed) {
Icon(imageVector = Icons.Default.Close, contentDescription = stringResource(id = StringR.string.action_close))
}
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateRoomSearchBar(
text: String,
placeHolderTitle: String,
active: Boolean,
modifier: Modifier = Modifier,
onActiveChanged: (Boolean) -> Unit = {},
onTextChanged: (String) -> Unit = {},
) {
val focusManager = LocalFocusManager.current
if (!active) {
onTextChanged("")
focusManager.clearFocus()
}
DockedSearchBar(
query = text,
onQueryChange = onTextChanged,
onSearch = { focusManager.clearFocus() },
active = active,
onActiveChange = onActiveChanged,
modifier = modifier
.padding(horizontal = if (!active) 16.dp else 0.dp),
placeholder = {
Text(
text = placeHolderTitle,
modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine)
)
},
leadingIcon = if (active) {
{ BackButton(onClick = { onActiveChanged(false) }) }
} else null,
trailingIcon = {
if (active) {
IconButton(onClick = { onTextChanged("") }) {
Icon(Icons.Default.Close, stringResource(StringR.string.a11y_clear))
}
} else {
Icon(
imageVector = Icons.Default.Search,
contentDescription = stringResource(StringR.string.search),
modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine)
)
}
},
shape = if (!active) SearchBarDefaults.dockedShape else SearchBarDefaults.fullScreenShape,
colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent),
content = {},
)
}
@Composable
fun CreateRoomActionButtonsList(
modifier: Modifier = Modifier,
onNewRoomClicked: () -> Unit = {},
onInvitePeopleClicked: () -> Unit = {},
) {
Column(modifier = modifier) {
CreateRoomActionButton(
iconRes = DrawableR.drawable.ic_groups,
text = stringResource(id = StringR.string.new_room),
onClick = onNewRoomClicked,
)
CreateRoomActionButton(
iconRes = DrawableR.drawable.ic_share,
text = stringResource(id = StringR.string.invite_people_menu),
onClick = onInvitePeopleClicked,
)
}
}
@Composable
fun CreateRoomActionButton(
@DrawableRes iconRes: Int,
text: String,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
Row(
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
.clickable { onClick() }
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.alpha(0.5f), // FIXME align on Design system theme (removing alpha should be fine)
resourceId = iconRes,
contentDescription = null,
)
Text(text = text)
}
}
@Preview
@Composable
fun CreateRoomRootViewLightPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
fun CreateRoomRootViewDarkPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
private fun ContentToPreview(state: CreateRoomRootState) {
CreateRoomRootScreen(
state = state,
)
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
// TODO add your ui models. Remove the eventSink if you don't have events.
// Do not use default value, so no member get forgotten in the presenters.
data class CreateRoomRootState(
val eventSink: (CreateRoomRootEvents) -> Unit
)

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.root
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRootState> {
override val values: Sequence<CreateRoomRootState>
get() = sequenceOf(
aCreateRoomRootState(),
// Add other state here
)
}
fun aCreateRoomRootState() = CreateRoomRootState(
eventSink = {}
)

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalCoroutinesApi::class)
package io.element.android.features.createroom.root
import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
class CreateRoomRootPresenterTests {
@Test
fun `present - initial state`() = runTest {
val presenter = CreateRoomRootPresenter()
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState)
}
}
@Test
fun `present - send event`() = runTest {
val presenter = CreateRoomRootPresenter()
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(CreateRoomRootEvents.CreateRoom) // Not implemented yet
initialState.eventSink(CreateRoomRootEvents.InvitePeople) // Not implemented yet
}
}
}

View file

@ -60,7 +60,7 @@ import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.LocalColors
import io.element.android.libraries.designsystem.theme.components.BackButton
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon
@ -104,7 +104,7 @@ fun ChangeServerView(
topBar = {
TopAppBar(
title = {},
navigationIcon = { BackButton(action = onBackPressed, enabled = interactionEnabled) }
navigationIcon = { BackButton(onClick = onBackPressed, enabled = interactionEnabled) }
)
}
) { padding ->
@ -192,7 +192,7 @@ fun ChangeServerView(
IconButton(onClick = {
homeserverFieldState = ""
}, enabled = interactionEnabled) {
Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.action_clear))
Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.a11y_clear))
}
}
} else null,

View file

@ -68,7 +68,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.BackButton
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon
@ -101,7 +101,7 @@ fun LoginRootView(
topBar = {
TopAppBar(
title = {},
navigationIcon = { BackButton(action = onBackPressed, enabled = interactionEnabled) },
navigationIcon = { BackButton(onClick = onBackPressed, enabled = interactionEnabled) },
)
}
) { padding ->
@ -267,7 +267,7 @@ internal fun LoginForm(
IconButton(onClick = {
loginFieldState = ""
}) {
Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.action_clear))
Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(StringR.string.a11y_clear))
}
}
} else null,

View file

@ -32,6 +32,7 @@ interface RoomListEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun onRoomClicked(roomId: RoomId)
fun onCreateRoomClicked()
fun onSettingsClicked()
}
}

View file

@ -44,6 +44,10 @@ class RoomListNode @AssistedInject constructor(
plugins<RoomListEntryPoint.Callback>().forEach { it.onSettingsClicked() }
}
private fun onCreateRoomClicked() {
plugins<RoomListEntryPoint.Callback>().forEach { it.onCreateRoomClicked() }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
@ -51,7 +55,8 @@ class RoomListNode @AssistedInject constructor(
state = state,
modifier = modifier,
onRoomClicked = this::onRoomClicked,
onOpenSettings = this::onOpenSettings
onOpenSettings = this::onOpenSettings,
onCreateRoomClicked = this::onCreateRoomClicked,
)
}
}

View file

@ -22,6 +22,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
@ -31,6 +32,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Velocity
@ -39,11 +41,15 @@ import io.element.android.features.roomlist.impl.components.RoomSummaryRow
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import io.element.android.libraries.designsystem.R as DrawableR
import io.element.android.libraries.ui.strings.R as StringR
@Composable
fun RoomListView(
@ -51,6 +57,7 @@ fun RoomListView(
modifier: Modifier = Modifier,
onRoomClicked: (RoomId) -> Unit = {},
onOpenSettings: () -> Unit = {},
onCreateRoomClicked: () -> Unit = {},
) {
fun onFilterChanged(filter: String) {
state.eventSink(RoomListEvents.UpdateFilter(filter))
@ -69,6 +76,7 @@ fun RoomListView(
onFilterChanged = ::onFilterChanged,
onOpenSettings = onOpenSettings,
onScrollOver = ::onVisibleRangedChanged,
onCreateRoomClicked = onCreateRoomClicked,
)
}
@ -83,6 +91,7 @@ fun RoomListContent(
onFilterChanged: (String) -> Unit = {},
onOpenSettings: () -> Unit = {},
onScrollOver: (IntRange) -> Unit = {},
onCreateRoomClicked: () -> Unit = {},
) {
fun onRoomClicked(room: RoomListRoomSummary) {
onRoomClicked(room.roomId)
@ -145,7 +154,16 @@ fun RoomListContent(
}
}
}
}
},
floatingActionButton = {
FloatingActionButton(
// FIXME align on Design system theme
containerColor = MaterialTheme.colorScheme.primary,
onClick = onCreateRoomClicked
) {
Icon(resourceId = DrawableR.drawable.ic_edit_square, contentDescription = stringResource(id = StringR.string.a11y_create_message))
}
},
)
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.designsystem.theme.components
package io.element.android.libraries.designsystem.components.button
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
@ -26,22 +26,24 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.ui.strings.R.string as StringR
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.ui.strings.R as StringR
@Composable
fun BackButton(
action: () -> Unit,
onClick: () -> Unit,
modifier: Modifier = Modifier,
icon: ImageVector = Icons.Default.ArrowBack,
contentDescription: String = stringResource(StringR.action_back),
imageVector: ImageVector = Icons.Default.ArrowBack,
contentDescription: String = stringResource(StringR.string.a11y_back),
enabled: Boolean = true
) {
IconButton(
modifier = modifier,
onClick = action,
onClick = onClick,
enabled = enabled,
) {
Icon(imageVector = icon, contentDescription = contentDescription)
Icon(imageVector = imageVector, contentDescription = contentDescription)
}
}
@ -56,7 +58,7 @@ internal fun BackButtonPreviewDark() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
BackButton(action = { }, enabled = true, contentDescription = "Back")
BackButton(action = { }, enabled = false, contentDescription = "Back")
BackButton(onClick = { }, enabled = true, contentDescription = "Back")
BackButton(onClick = { }, enabled = false, contentDescription = "Back")
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CenterAlignedTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
windowInsets: WindowInsets = TopAppBarDefaults.windowInsets,
colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
androidx.compose.material3.CenterAlignedTopAppBar(
title = title,
modifier = modifier,
navigationIcon = navigationIcon,
actions = actions,
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior,
)
}
@Preview
@Composable
internal fun CenterAlignedTopAppBarLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun CenterAlignedTopAppBarDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
CenterAlignedTopAppBar(title = { Text(text = "Title") })
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SearchBarColors
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DockedSearchBar(
query: String,
onQueryChange: (String) -> Unit,
onSearch: (String) -> Unit,
active: Boolean,
onActiveChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
shape: Shape = SearchBarDefaults.dockedShape,
colors: SearchBarColors = SearchBarDefaults.colors(),
tonalElevation: Dp = SearchBarDefaults.Elevation,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable ColumnScope.() -> Unit,
) {
androidx.compose.material3.DockedSearchBar(
query = query,
onQueryChange = onQueryChange,
onSearch = onSearch,
active = active,
onActiveChange = onActiveChange,
modifier = modifier,
enabled = enabled,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
shape = shape,
colors = colors,
tonalElevation = tonalElevation,
interactionSource = interactionSource,
content = content,
)
}
@Preview
@Composable
internal fun DockedSearchBarLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun DockedSearchBarDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
DockedSearchBar(
query = "Some text",
onQueryChange = {},
onSearch = {},
active = false,
onActiveChange = {},
content = {},
)
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ButtonElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@Composable
fun TextButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = ButtonDefaults.textShape,
colors: ButtonColors = ButtonDefaults.textButtonColors(),
elevation: ButtonElevation? = null,
border: BorderStroke? = null,
contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
) {
androidx.compose.material3.TextButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
colors = colors,
elevation = elevation,
border = border,
contentPadding = contentPadding,
interactionSource = interactionSource,
content = content,
)
}
@Preview
@Composable
internal fun TextButtonLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TextButtonDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
TextButton(onClick = {}, enabled = true) {
Text(text = "Click me! - Enabled")
}
TextButton(onClick = {}, enabled = false) {
Text(text = "Click me! - Disabled")
}
}
}

View file

@ -0,0 +1,26 @@
<!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#000000"
android:width="21dp"
android:height="22dp"
android:viewportWidth="21"
android:viewportHeight="22">
<path
android:pathData="M1.5,21.7C1.1,21.7 0.75,21.55 0.45,21.25C0.15,20.95 0,20.6 0,20.2V5.2C0,4.8 0.15,4.45 0.45,4.15C0.75,3.85 1.1,3.7 1.5,3.7H11.625L10.125,5.2H1.5V20.2H16.5V11.5L18,10V20.2C18,20.6 17.85,20.95 17.55,21.25C17.25,21.55 16.9,21.7 16.5,21.7H1.5ZM13.55,3.9L14.625,4.95L7.5,12.05V14.2H9.625L16.775,7.05L17.825,8.1L10.7,15.25C10.567,15.383 10.404,15.492 10.212,15.575C10.021,15.658 9.825,15.7 9.625,15.7H6.75C6.533,15.7 6.354,15.629 6.213,15.488C6.071,15.346 6,15.167 6,14.95V12.075C6,11.875 6.042,11.679 6.125,11.488C6.208,11.296 6.317,11.133 6.45,11L13.55,3.9ZM17.825,8.1L13.55,3.9L16.05,1.4C16.333,1.117 16.688,0.975 17.112,0.975C17.538,0.975 17.892,1.125 18.175,1.425L20.275,3.55C20.558,3.85 20.7,4.204 20.7,4.613C20.7,5.021 20.55,5.367 20.25,5.65L17.825,8.1Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,25 @@
<!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/black"
android:pathData="M1.365,17.788C1.12,17.788 0.915,17.705 0.749,17.54C0.583,17.374 0.5,17.168 0.5,16.923V16.569C0.5,15.894 0.844,15.346 1.531,14.927C2.219,14.508 3.126,14.298 4.251,14.298C4.454,14.298 4.647,14.305 4.829,14.318C5.011,14.332 5.187,14.355 5.358,14.389C5.168,14.681 5.028,14.995 4.94,15.332C4.852,15.668 4.808,16.024 4.808,16.4V17.788H1.365ZM7.408,17.788C7.147,17.788 6.931,17.702 6.759,17.529C6.586,17.355 6.5,17.141 6.5,16.885V16.438C6.5,15.483 7.006,14.713 8.018,14.128C9.03,13.543 10.356,13.25 11.996,13.25C13.651,13.25 14.982,13.543 15.989,14.128C16.996,14.713 17.5,15.483 17.5,16.438V16.885C17.5,17.141 17.413,17.355 17.24,17.529C17.067,17.702 16.852,17.788 16.596,17.788H7.408ZM19.192,17.788V16.4C19.192,16.024 19.145,15.668 19.05,15.332C18.955,14.995 18.819,14.681 18.642,14.389C18.813,14.355 18.989,14.332 19.17,14.318C19.352,14.305 19.545,14.298 19.75,14.298C20.875,14.298 21.781,14.508 22.469,14.927C23.156,15.346 23.5,15.894 23.5,16.569V16.923C23.5,17.168 23.417,17.374 23.251,17.54C23.085,17.705 22.88,17.788 22.635,17.788H19.192ZM12,14.75C10.954,14.75 10.059,14.892 9.315,15.176C8.572,15.46 8.159,15.799 8.077,16.192V16.288H15.923V16.192C15.831,15.788 15.415,15.447 14.677,15.168C13.938,14.889 13.046,14.75 12,14.75ZM4.25,13.327C3.777,13.327 3.375,13.159 3.044,12.824C2.713,12.489 2.548,12.086 2.548,11.615C2.548,11.145 2.713,10.742 3.044,10.407C3.375,10.071 3.777,9.904 4.25,9.904C4.723,9.904 5.127,10.071 5.461,10.407C5.794,10.742 5.961,11.145 5.961,11.615C5.961,12.086 5.794,12.489 5.459,12.824C5.124,13.159 4.721,13.327 4.25,13.327ZM19.75,13.327C19.277,13.327 18.873,13.159 18.539,12.824C18.205,12.489 18.038,12.086 18.038,11.615C18.038,11.145 18.206,10.742 18.541,10.407C18.876,10.071 19.279,9.904 19.75,9.904C20.223,9.904 20.625,10.071 20.956,10.407C21.286,10.742 21.452,11.145 21.452,11.615C21.452,12.086 21.286,12.489 20.956,12.824C20.625,13.159 20.223,13.327 19.75,13.327ZM12,12.5C11.279,12.5 10.666,12.248 10.161,11.743C9.656,11.238 9.404,10.625 9.404,9.904C9.404,9.183 9.656,8.57 10.161,8.065C10.666,7.56 11.279,7.308 12,7.308C12.721,7.308 13.334,7.56 13.839,8.065C14.344,8.57 14.596,9.183 14.596,9.904C14.596,10.625 14.344,11.238 13.839,11.743C13.334,12.248 12.721,12.5 12,12.5ZM12.002,8.808C11.691,8.808 11.431,8.913 11.22,9.122C11.009,9.332 10.904,9.592 10.904,9.902C10.904,10.212 11.009,10.473 11.218,10.684C11.428,10.895 11.688,11 11.998,11C12.308,11 12.569,10.895 12.78,10.685C12.991,10.476 13.096,10.216 13.096,9.906C13.096,9.595 12.991,9.335 12.781,9.124C12.572,8.913 12.312,8.808 12.002,8.808Z" />
</vector>

View file

@ -0,0 +1,25 @@
<!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/black"
android:pathData="M18.001,21.75C17.237,21.75 16.588,21.483 16.053,20.948C15.518,20.413 15.25,19.764 15.25,19C15.25,18.875 15.26,18.746 15.28,18.612C15.3,18.478 15.33,18.355 15.369,18.242L7.973,13.911C7.709,14.174 7.408,14.38 7.071,14.528C6.734,14.676 6.377,14.75 6,14.75C5.236,14.75 4.587,14.483 4.052,13.948C3.517,13.414 3.25,12.765 3.25,12.001C3.25,11.238 3.517,10.588 4.052,10.053C4.587,9.518 5.236,9.25 6,9.25C6.377,9.25 6.734,9.324 7.071,9.472C7.408,9.62 7.709,9.826 7.973,10.089L15.369,5.758C15.33,5.645 15.3,5.522 15.28,5.388C15.26,5.254 15.25,5.125 15.25,5C15.25,4.236 15.517,3.587 16.052,3.052C16.586,2.517 17.235,2.25 17.999,2.25C18.762,2.25 19.412,2.517 19.947,3.052C20.482,3.586 20.75,4.235 20.75,4.999C20.75,5.762 20.483,6.412 19.948,6.947C19.413,7.482 18.764,7.75 18,7.75C17.623,7.75 17.266,7.676 16.929,7.528C16.592,7.38 16.291,7.174 16.027,6.912L8.631,11.242C8.67,11.355 8.7,11.478 8.72,11.611C8.74,11.745 8.75,11.874 8.75,11.998C8.75,12.122 8.74,12.252 8.72,12.387C8.7,12.521 8.67,12.645 8.631,12.758L16.027,17.088C16.291,16.826 16.592,16.62 16.929,16.472C17.266,16.324 17.623,16.25 18,16.25C18.764,16.25 19.413,16.517 19.948,17.052C20.483,17.586 20.75,18.235 20.75,18.999C20.75,19.762 20.483,20.412 19.948,20.947C19.414,21.482 18.765,21.75 18.001,21.75ZM18,6.25C18.347,6.25 18.643,6.129 18.886,5.886C19.128,5.643 19.25,5.347 19.25,5C19.25,4.653 19.128,4.357 18.886,4.114C18.643,3.871 18.347,3.75 18,3.75C17.653,3.75 17.357,3.871 17.114,4.114C16.871,4.357 16.75,4.653 16.75,5C16.75,5.347 16.871,5.643 17.114,5.886C17.357,6.129 17.653,6.25 18,6.25ZM6,13.25C6.347,13.25 6.643,13.128 6.886,12.886C7.129,12.643 7.25,12.347 7.25,12C7.25,11.653 7.129,11.357 6.886,11.114C6.643,10.871 6.347,10.75 6,10.75C5.653,10.75 5.357,10.871 5.114,11.114C4.871,11.357 4.75,11.653 4.75,12C4.75,12.347 4.871,12.643 5.114,12.886C5.357,13.128 5.653,13.25 6,13.25ZM18,20.25C18.347,20.25 18.643,20.128 18.886,19.886C19.128,19.643 19.25,19.347 19.25,19C19.25,18.653 19.128,18.357 18.886,18.114C18.643,17.871 18.347,17.75 18,17.75C17.653,17.75 17.357,17.871 17.114,18.114C16.871,18.357 16.75,18.653 16.75,19C16.75,19.347 16.871,19.643 17.114,19.886C17.357,20.128 17.653,20.25 18,20.25Z" />
</vector>

View file

@ -2,17 +2,20 @@
<resources>
<!-- Add new strings for Element X Android here -->
<string name="action_back">Back</string>
<string name="action_clear">Clear</string>
<!-- Common -->
<string name="a11y_back">Back</string>
<string name="a11y_clear">Clear</string>
<!-- Login -->
<string name="login_form_title">Enter your details</string>
<string name="ex_login_username_hint">Email or username</string>
<string name="login_show_password">Show password</string>
<string name="login_hide_password">Hide password</string>
<string name="ex_choose_server_subtitle">What is the address of your server?</string>
<string name="server_selection_server_footer">You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it.</string>
<string name="server_selection_sliding_sync_alert_title">Server not supported</string>
<string name="server_selection_sliding_sync_alert_message">This server currently doesn\'t support sliding sync.</string>
<!-- Create room -->
<string name="search_for_someone">Search for someone</string>
<string name="new_room">New room</string>
</resources>

View file

@ -39,7 +39,9 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) {
androidTestImplementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material:material")
implementation("androidx.compose.material3:material3")
// Override BOM version, SearchBar is not available in the actual version
// do not use latest version because of clashes on androidx lifecycle dependency
implementation("androidx.compose.material3:material3:1.1.0-alpha04")
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.activity:activity-compose:1.6.1")
@ -67,4 +69,5 @@ fun DependencyHandlerScope.allFeatures() {
implementation(project(":features:messages:impl"))
implementation(project(":features:rageshake:impl"))
implementation(project(":features:preferences:impl"))
implementation(project(":features:createroom"))
}

View file

@ -80,3 +80,4 @@ include(":features:messages:api")
include(":features:messages:impl")
include(":features:login:api")
include(":features:login:impl")
include(":features:createroom")

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:510c17406a13ce64a9b7578d70535dac7fe20e9c3a9a4ab3c8cab775bc46f568
size 19718

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:611b1efa9cf46316f96738130f326061595d2648ac7fb059df299e7c95f4e2a8
size 18480

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3460e8f37e746e0ae7a30c1a055a3b68cc013197c4f2567d19ad51c00f8d6385
size 34814
oid sha256:a339ab789eb6710aa5edba53acc46a61de373566089590940ab4c8233e97558b
size 34721

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f86089af6c02e8eb4811ae0092150c2d5e439731ae970e7954a66fca7f98fd11
size 34173
oid sha256:675c1cbfc80acec11434153dacb1d69b0cf6122fb068db8a92ae86e039a54c71
size 34091

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b21168d83ed8d7c116e72dd76292975530f1c898ef32502aa382f292aef4f8aa
size 30951
oid sha256:8137f8ed97a1a924fc76e16c8f0f0fc5a4a866d989460859b98edfcb5c7f98b7
size 30996

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3460e8f37e746e0ae7a30c1a055a3b68cc013197c4f2567d19ad51c00f8d6385
size 34814
oid sha256:a339ab789eb6710aa5edba53acc46a61de373566089590940ab4c8233e97558b
size 34721

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f81c7b793611e5f3f975245bf5ba52d59bf5725519292f05dd7840c2e38d3166
size 33237
oid sha256:04f73ec1830baa28808881f040f6bfc2b1ce0de1ca2d4b3e16a3b57513814a7f
size 33173

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:04c34dbc9fe2df9af3133e1b7c444d58e7d8a35161a97155c384dceac28ee698
size 32324
oid sha256:da84d9195ed0cac06efff562156ed8abc161a6dbd3272727c1cb787b83c62558
size 32253

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:922ee759a8e4b689ced8ade809c651464956395ffc0a2b3d9025f067a9807aa9
size 30066
oid sha256:ea2dce0fde5ff6563f283e643edadd5c95c29a4084c8f1d3902ec3c759cccea7
size 30070

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f81c7b793611e5f3f975245bf5ba52d59bf5725519292f05dd7840c2e38d3166
size 33237
oid sha256:04f73ec1830baa28808881f040f6bfc2b1ce0de1ca2d4b3e16a3b57513814a7f
size 33173

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81905bde677949780ef6c850be8dde97c88895b82d83414589e07f0725064050
size 13530
oid sha256:ff04bf07f8c24b3b19d4926bb3c51b257166befe062fe8b8012d2e92dbfe3491
size 13477

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:edd3b0f29a3fbf9035cc8ba1d7a5a3a74be30d4e6ec8f654832f7ee916497453
size 12457
oid sha256:e50325c75193e47958862ea9cb515d7c84d2c47a00b01256fc244319780c107f
size 12425

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0
size 4462
oid sha256:328963ebd74fbbe47fd4a4d1f0edce5bf014d88a266c114e50a5274e87a88fe8
size 7154

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0
size 4462
oid sha256:007fd7051cb4b8059dc33ff1cd2b4be4cffaa5e1c129adb61dd4d9e8a561953f
size 7096

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a50e661d6c3c45574fa3d6fc64b0d5f2a1c9dbba18a77be900df1dfce9df1f69
size 35103
oid sha256:3c8868d4974f23637afed9ecd062b88421e2b39ffe22f19742b946afc91caeb6
size 37838

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7d27f9fbe2b6d803198f0015cc391d6dfdd5d2708e837319c6eb0e82f8ab832c
size 33488
oid sha256:43b69859fa3ee38d2b7f7415b87738db65dc6dac3d2fabddc1f1346b0b64932b
size 37329

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:17b110e460f4212927c46d593352dac88493bb7be9a5d2ae8b4e0b8e6c79d0bf
size 5695

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:29c30e97a5c1de9e855001c6f27322bce8795954ac5c134c34ea3c9de417acd2
size 5250

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b662595580f6e0dd4ff6722fc0c8db63b0811a615a41356ba6b28d22285b6765
size 5813

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b8a9745199ef10a4e0c8e101203ce51999449b7c43bf8b764f162ea975ebaefe
size 5548

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e89807d72107f1f8337e8de803791249b56bde6c4961805520195ea55563749a
size 8470

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6381c125b8eaf6b116218bc4b0efda4866f19514139f4b45604909e462931c1f
size 8320

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fde95a558052ae09123d459249e9b2d8ba3a3f6adb1bba4002ac0aca90d5b657
size 11668

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81be7efc599fbe85a373aecfe85047f521ad609ce6b85b343386bb101aaddb68
size 10611

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a796bba1f316b03793b415d109940940a6d81a924f55a745764bb6ec46cab40b
size 39585
oid sha256:03bfd855f6e010d2d4dc0cb9f3c13e76a4f2bbcda98b086dabcd33f201ebe10a
size 39486

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5386114bbb3cbf6c8a3de0642f1efa7a3b5d7efa46168d70aeffc413e37ec803
size 39014
oid sha256:29bbaae8c846dfe05bb66e08debeb84422d45f88982f79faefb94f332843c26e
size 39069