Merge branch 'develop' into feature/fga/update_rust_sdk

This commit is contained in:
ganfra 2023-02-14 21:15:28 +01:00
commit 8bca19a3fa
341 changed files with 9655 additions and 2495 deletions

View file

@ -39,7 +39,8 @@ sealed interface Async<out T> {
suspend fun <T> (suspend () -> T).execute(state: MutableState<Async<T>>) {
try {
state.value = Async.Loading()
state.value = Async.Success(this())
val result = this()
state.value = Async.Success(result)
} catch (error: Throwable) {
state.value = Async.Failure(error)
}

View file

@ -24,6 +24,8 @@ class StableCharSequence(val charSequence: CharSequence) {
override fun hashCode() = hash
override fun equals(other: Any?) = other is StableCharSequence && other.hash == hash
override fun toString(): String = "StableCharSequence(\"$charSequence\")"
}
fun CharSequence.toStableCharSequence() = StableCharSequence(this)

1
libraries/dateformatter/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2022 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-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}
anvil {
generateDaggerFactories.set(true)
}
android {
namespace = "io.element.android.libraries.dateformatter"
dependencies {
anvil(projects.anvilcodegen)
implementation(libs.dagger)
implementation(projects.libraries.di)
implementation(projects.anvilannotations)
api(libs.datetime)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)
}
}

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.kts.
#
# 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,16 @@
<?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 />

View file

@ -0,0 +1,21 @@
/*
* 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.dateformatter
interface LastMessageFormatter {
fun format(timestamp: Long?): String
}

View file

@ -0,0 +1,38 @@
/*
* 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.dateformatter.di
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import io.element.android.libraries.di.AppScope
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import java.util.*
@Module
@ContributesTo(AppScope::class)
object DateFormatterModule {
@Provides
fun providesClock(): Clock = Clock.System
@Provides
fun providesLocale(): Locale = Locale.getDefault()
@Provides
fun providesTimezone(): TimeZone = TimeZone.currentSystemDefault()
}

View file

@ -0,0 +1,106 @@
/*
* 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.dateformatter.impl
import android.text.format.DateFormat
import android.text.format.DateUtils
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.dateformatter.LastMessageFormatter
import io.element.android.libraries.di.AppScope
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toJavaLocalDate
import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.datetime.toLocalDateTime
import java.time.Period
import java.time.format.DateTimeFormatter
import java.util.Locale
import javax.inject.Inject
import kotlin.math.absoluteValue
@ContributesBinding(AppScope::class)
class DefaultLastMessageFormatter @Inject constructor(
private val clock: Clock,
private val locale: Locale,
private val timezone: TimeZone,
) : LastMessageFormatter {
private val onlyTimeFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "HH:mm") ?: "HH:mm"
DateTimeFormatter.ofPattern(pattern)
}
private val dateWithMonthFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "d MMM") ?: "d MMM"
DateTimeFormatter.ofPattern(pattern)
}
private val dateWithYearFormatter: DateTimeFormatter by lazy {
val pattern = DateFormat.getBestDateTimePattern(locale, "dd.MM.yyyy") ?: "dd.MM.yyyy"
DateTimeFormatter.ofPattern(pattern)
}
override fun format(timestamp: Long?): String {
if (timestamp == null) return ""
val now: Instant = clock.now()
val tsInstant = Instant.fromEpochMilliseconds(timestamp)
val nowDateTime = now.toLocalDateTime(timezone)
val tsDateTime = tsInstant.toLocalDateTime(timezone)
val isSameDay = nowDateTime.date == tsDateTime.date
return when {
isSameDay -> {
onlyTimeFormatter.format(tsDateTime.toJavaLocalDateTime())
}
else -> {
formatDate(tsDateTime, nowDateTime)
}
}
}
private fun formatDate(
date: LocalDateTime,
currentDate: LocalDateTime,
): String {
val period = Period.between(date.date.toJavaLocalDate(), currentDate.date.toJavaLocalDate())
return if (period.years.absoluteValue >= 1) {
formatDateWithYear(date)
} else if (period.days.absoluteValue < 2 && period.months.absoluteValue < 1) {
getRelativeDay(date.toInstant(timezone).toEpochMilliseconds())
} else {
formatDateWithMonth(date)
}
}
private fun formatDateWithMonth(localDateTime: LocalDateTime): String {
return dateWithMonthFormatter.format(localDateTime.toJavaLocalDateTime())
}
private fun formatDateWithYear(localDateTime: LocalDateTime): String {
return dateWithYearFormatter.format(localDateTime.toJavaLocalDateTime())
}
private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString(
ts,
clock.now().toEpochMilliseconds(),
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_WEEKDAY
)?.toString() ?: ""
}
}

View file

@ -0,0 +1,106 @@
/*
* 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.dateformatter.impl
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.dateformatter.LastMessageFormatter
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import org.junit.Test
import java.util.Locale
class DefaultLastMessageFormatterTest {
@Test
fun `test null`() {
val now = "1980-04-06T18:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(null)).isEmpty()
}
@Test
fun `test epoch`() {
val now = "1980-04-06T18:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(0)).isEqualTo("01.01.1970")
}
@Test
fun `test now`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-04-06T18:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:35")
}
@Test
fun `test one second before`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-04-06T18:35:23.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:35")
}
@Test
fun `test one minute before`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-04-06T18:34:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("18:34")
}
@Test
fun `test one hour before`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-04-06T17:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("17:35")
}
@Test
fun `test one day before same time`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-04-05T18:35:24.00Z"
val formatter = createFormatter(now)
// TODO DateUtils.getRelativeTimeSpanString returns null.
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("")
}
@Test
fun `test one month before same time`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1980-03-06T18:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("6 Mar")
}
@Test
fun `test one year before same time`() {
val now = "1980-04-06T18:35:24.00Z"
val dat = "1979-04-06T18:35:24.00Z"
val formatter = createFormatter(now)
assertThat(formatter.format(Instant.parse(dat).toEpochMilliseconds())).isEqualTo("06.04.1979")
}
/**
* Create DefaultLastMessageFormatter and set current time to the provided date.
*/
private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageFormatter {
val clock = FakeClock().also { it.givenInstant(Instant.parse(currentDate)) }
return DefaultLastMessageFormatter(clock, Locale.US, TimeZone.UTC)
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@ -14,20 +14,17 @@
* limitations under the License.
*/
package io.element.android.libraries.designsystem.components
package io.element.android.libraries.dateformatter.impl
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
@Composable
fun VectorButton(text: String, enabled: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(
onClick = onClick,
enabled = enabled,
modifier = modifier
) {
Text(text = text)
class FakeClock : Clock {
private var instant: Instant = Instant.fromEpochMilliseconds(0)
fun givenInstant(instant: Instant) {
this.instant = instant
}
override fun now(): Instant = instant
}

View file

@ -52,10 +52,4 @@ val ElementGreen = Color(0xFF0DBD8B)
val ElementOrange = Color(0xFFD9B072)
val Vermilion = Color(0xFFFF5B55)
// TODO Update color
val MessageHighlightLight = Azure
// TODO Update color
val MessageHighlightDark = Azure
val LinkColor = Color(0xFF054F6E)

View file

@ -25,6 +25,6 @@ fun Boolean.toEnabledColor(): Color {
return if (this) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.secondary
MaterialTheme.colorScheme.primary.copy(alpha = 0.40f)
}
}

View file

@ -16,55 +16,13 @@
package io.element.android.libraries.designsystem
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp
import com.airbnb.android.showkase.annotation.ShowkaseTypography
@ShowkaseTypography(name = "Body Large", group = "Element")
val bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
@ShowkaseTypography(name = "Headline Small", group = "Element")
val headlineSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 32.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = bodyLarge,
headlineSmall = headlineSmall,
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
// TODO Remove
object ElementTextStyles {
object Bold {

View file

@ -21,7 +21,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import io.element.android.libraries.designsystem.theme.components.Text
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
@ -83,6 +84,7 @@ fun ClickableLinkText(
onTextLayout = {
layoutResult.value = it
},
inlineContent = inlineContent
inlineContent = inlineContent,
color = MaterialTheme.colorScheme.primary,
)
}

View file

@ -18,12 +18,15 @@ package io.element.android.libraries.designsystem.components
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
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
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun LabelledCheckbox(
@ -42,13 +45,23 @@ fun LabelledCheckbox(
onCheckedChange = onCheckedChange,
enabled = enabled,
)
Text(text = text)
Text(
text = text,
color = MaterialTheme.colorScheme.primary,
)
}
}
@Preview
@Composable
fun LabelledCheckboxPreview() {
fun LabelledCheckboxLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun LabelledCheckboxDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
LabelledCheckbox(
checked = true,
text = "Some text",

View file

@ -22,9 +22,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -32,6 +30,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun ProgressDialog(
@ -48,19 +50,19 @@ fun ProgressDialog(
modifier = modifier
.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.onBackground,
color = MaterialTheme.colorScheme.surfaceVariant,
shape = RoundedCornerShape(8.dp)
)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator(
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.background
color = MaterialTheme.colorScheme.onSurfaceVariant
)
if (!text.isNullOrBlank()) {
Text(
text = text,
color = MaterialTheme.colorScheme.background,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(16.dp)
)
}
@ -69,8 +71,15 @@ fun ProgressDialog(
}
}
@Composable
@Preview
fun ProgressDialogPreview() {
@Composable
fun ProgressDialogLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun ProgressDialogDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
ProgressDialog(text = "test dialog content")
}

View file

@ -16,7 +16,6 @@
package io.element.android.libraries.designsystem.components
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -29,7 +28,7 @@ fun VectorIcon(
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current,
) {
Icon(
androidx.compose.material3.Icon(
painter = painterResource(id = resourceId),
contentDescription = null,
modifier = modifier,

View file

@ -20,7 +20,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -34,6 +33,9 @@ import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import io.element.android.libraries.designsystem.AvatarGradientEnd
import io.element.android.libraries.designsystem.AvatarGradientStart
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import timber.log.Timber
@Composable
@ -97,6 +99,13 @@ private fun InitialsAvatar(
@Preview
@Composable
fun InitialsAvatarPreview() {
fun AvatarLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun AvatarDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Avatar(AvatarData(name = "A"))
}

View file

@ -22,13 +22,20 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
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 io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@ -43,6 +50,12 @@ fun ConfirmationDialog(
onCancelClicked: () -> Unit = {},
onThirdButtonClicked: () -> Unit = {},
onDismiss: () -> Unit = {},
shape: Shape = AlertDialogDefaults.shape,
containerColor: Color = MaterialTheme.colorScheme.surfaceVariant,
iconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
titleContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
textContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
AlertDialog(
modifier = modifier,
@ -93,12 +106,25 @@ fun ConfirmationDialog(
}
}
},
shape = shape,
containerColor = containerColor,
iconContentColor = iconContentColor,
titleContentColor = titleContentColor,
textContentColor = textContentColor,
tonalElevation = tonalElevation,
)
}
@Composable
@Preview
fun ConfirmationDialogPreview() {
@Composable
fun ConfirmationDialogLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun ConfirmationDialogDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
ConfirmationDialog(
title = "Title",
content = "Content",

View file

@ -21,13 +21,20 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
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 io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@ -37,6 +44,12 @@ fun ErrorDialog(
title: String = stringResource(id = StringR.string.dialog_title_error),
submitText: String = stringResource(id = StringR.string.ok),
onDismiss: () -> Unit = {},
shape: Shape = AlertDialogDefaults.shape,
containerColor: Color = MaterialTheme.colorScheme.surfaceVariant,
iconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
titleContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
textContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
AlertDialog(
modifier = modifier,
@ -62,12 +75,25 @@ fun ErrorDialog(
}
}
},
shape = shape,
containerColor = containerColor,
iconContentColor = iconContentColor,
titleContentColor = titleContentColor,
textContentColor = textContentColor,
tonalElevation = tonalElevation,
)
}
@Composable
@Preview
fun ErrorDialogPreview() {
@Composable
fun ErrorDialogLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun ErrorDialogDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
ErrorDialog(
content = "Content",
)

View file

@ -20,13 +20,18 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Divider
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Announcement
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
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
import io.element.android.libraries.designsystem.theme.components.Divider
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun PreferenceCategory(
@ -46,20 +51,39 @@ fun PreferenceCategory(
Text(
modifier = Modifier.padding(top = 8.dp, start = 56.dp),
style = MaterialTheme.typography.titleSmall,
text = title
color = MaterialTheme.colorScheme.primary,
text = title,
)
content()
}
}
@Composable
@Preview
fun PreferenceCategoryPreview() {
@Composable
fun PreferenceCategoryLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun PreferenceCategoryDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
PreferenceCategory(
title = "Category title",
) {
PreferenceTextPreview()
PreferenceSwitchPreview()
PreferenceSlidePreview()
PreferenceText(
title = "Title",
icon = Icons.Default.BugReport,
)
PreferenceSwitch(
title = "Switch",
icon = Icons.Default.Announcement,
isChecked = true
)
PreferenceSlide(
title = "Slide",
summary = "Summary",
value = 0.75F
)
}
}

View file

@ -28,13 +28,10 @@ import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Announcement
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -42,6 +39,13 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
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.theme.components.TopAppBar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -110,12 +114,36 @@ fun PreferenceTopAppBar(
)
}
@Composable
@Preview
fun PreferenceScreenPreview() {
@Composable
fun PreferenceViewLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun PreferenceViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
PreferenceView(
title = "Preference screen"
) {
PreferenceCategoryPreview()
PreferenceCategory(
title = "Category title",
) {
PreferenceText(
title = "Title",
icon = Icons.Default.BugReport,
)
PreferenceSwitch(
title = "Switch",
icon = Icons.Default.Announcement,
isChecked = true
)
PreferenceSlide(
title = "Slide",
summary = "Summary",
value = 0.75F
)
}
}
}

View file

@ -24,14 +24,16 @@ import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Slider
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
@Composable
@ -84,9 +86,16 @@ fun PreferenceSlide(
}
}
@Composable
@Preview
fun PreferenceSlidePreview() {
@Composable
fun PreferenceSlideLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun PreferenceSlideDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
PreferenceSlide(
title = "Slide",
summary = "Summary",

View file

@ -24,15 +24,17 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Announcement
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
@Composable
@ -75,12 +77,20 @@ fun PreferenceSwitch(
}
}
@Composable
@Preview
fun PreferenceSwitchPreview() {
@Composable
fun PreferenceSwitchLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun PreferenceSwitchDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
PreferenceSwitch(
title = "Switch",
icon = Icons.Default.Announcement,
enabled = true,
isChecked = true
)
}

View file

@ -25,13 +25,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun PreferenceText(
@ -57,15 +59,23 @@ fun PreferenceText(
.weight(1f)
.padding(end = preferencePaddingEnd),
style = MaterialTheme.typography.bodyLarge,
text = title
text = title,
color = MaterialTheme.colorScheme.primary,
)
}
}
}
@Composable
@Preview
fun PreferenceTextPreview() {
@Composable
fun PreferenceTextLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun PreferenceTextDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
PreferenceText(
title = "Title",
icon = Icons.Default.BugReport,

View file

@ -19,11 +19,11 @@ package io.element.android.libraries.designsystem.components.preferences.compone
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.toEnabledColor
@Composable

View file

@ -0,0 +1,66 @@
/*
* 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.preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.libraries.designsystem.theme.ElementTheme
@Composable
fun ElementPreviewLight(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = false,
showBackground = showBackground,
content = content
)
}
@Composable
fun ElementPreviewDark(
showBackground: Boolean = true,
content: @Composable () -> Unit
) {
ElementPreview(
darkTheme = true,
showBackground = showBackground,
content = content
)
}
@Composable
private fun ElementPreview(
darkTheme: Boolean,
showBackground: Boolean,
content: @Composable () -> Unit
) {
ElementTheme(darkTheme = darkTheme) {
if (showBackground) {
Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
content()
}
} else {
content()
}
}
}

View file

@ -0,0 +1,69 @@
/*
* 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
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.SystemGrey4Dark
import io.element.android.libraries.designsystem.SystemGrey6Light
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.previews.ColorListPreview
import kotlinx.collections.immutable.persistentMapOf
/**
* Room list.
*/
@Composable
fun MaterialTheme.roomListRoomName() = colorScheme.primary
@Composable
fun MaterialTheme.roomListRoomMessage() = colorScheme.secondary
@Composable
fun MaterialTheme.roomListRoomMessageDate() = colorScheme.secondary
@Composable
fun MaterialTheme.roomListUnreadIndicator() = colorScheme.primary
@Composable
fun ElementColors.roomListPlaceHolder() = if (isLight) SystemGrey6Light else SystemGrey4Dark
@Preview
@Composable
fun ColorAliasesLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun ColorAliasesDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
ColorListPreview(
backgroundColor = Color.Black,
foregroundColor = Color.White,
colors = persistentMapOf(
"roomListRoomName" to MaterialTheme.roomListRoomName(),
"roomListRoomMessage" to MaterialTheme.roomListRoomMessage(),
"roomListRoomMessageDate" to MaterialTheme.roomListRoomMessageDate(),
"roomListUnreadIndicator" to MaterialTheme.roomListUnreadIndicator(),
"roomListPlaceHolder" to ElementTheme.colors.roomListPlaceHolder(),
)
)
}

View file

@ -0,0 +1,75 @@
/*
* 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
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.Azure
import io.element.android.libraries.designsystem.DarkGrey
import io.element.android.libraries.designsystem.SystemGrey5Dark
import io.element.android.libraries.designsystem.SystemGrey6Dark
import io.element.android.libraries.designsystem.theme.previews.ColorsSchemePreview
fun elementColorsDark() = ElementColors(
messageFromMeBackground = SystemGrey5Dark,
messageFromOtherBackground = SystemGrey6Dark,
messageHighlightedBackground = Azure,
isLight = false,
)
// TODO Lots of colors are missing
val materialColorSchemeDark = darkColorScheme(
primary = Color.White,
// TODO onPrimary = ColorDarkTokens.OnPrimary,
// TODO primaryContainer = ColorDarkTokens.PrimaryContainer,
// TODO onPrimaryContainer = ColorDarkTokens.OnPrimaryContainer,
// TODO inversePrimary = ColorDarkTokens.InversePrimary,
secondary = DarkGrey,
// TODO onSecondary = ColorDarkTokens.OnSecondary,
// TODO secondaryContainer = ColorDarkTokens.SecondaryContainer,
// TODO onSecondaryContainer = ColorDarkTokens.OnSecondaryContainer,
tertiary = Color.White,
// TODO onTertiary = ColorDarkTokens.OnTertiary,
// TODO tertiaryContainer = ColorDarkTokens.TertiaryContainer,
// TODO onTertiaryContainer = ColorDarkTokens.OnTertiaryContainer,
background = Color.Black,
onBackground = Color.White,
surface = Color.Black,
onSurface = Color.White,
surfaceVariant = SystemGrey5Dark,
// TODO onSurfaceVariant = ColorDarkTokens.OnSurfaceVariant,
// TODO surfaceTint = primary,
// TODO inverseSurface = ColorDarkTokens.InverseSurface,
// TODO inverseOnSurface = ColorDarkTokens.InverseOnSurface,
// TODO error = ColorDarkTokens.Error,
// TODO onError = ColorDarkTokens.OnError,
// TODO errorContainer = ColorDarkTokens.ErrorContainer,
// TODO onErrorContainer = ColorDarkTokens.OnErrorContainer,
// TODO outline = ColorDarkTokens.Outline,
// TODO outlineVariant = ColorDarkTokens.OutlineVariant,
// TODO scrim = ColorDarkTokens.Scrim,
)
@Preview
@Composable
fun ColorsSchemePreviewDark() = ColorsSchemePreview(
Color.White,
Color.Black,
materialColorSchemeDark,
)

View file

@ -0,0 +1,75 @@
/*
* 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
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.Azure
import io.element.android.libraries.designsystem.LightGrey
import io.element.android.libraries.designsystem.SystemGrey5Light
import io.element.android.libraries.designsystem.SystemGrey6Light
import io.element.android.libraries.designsystem.theme.previews.ColorsSchemePreview
fun elementColorsLight() = ElementColors(
messageFromMeBackground = SystemGrey5Light,
messageFromOtherBackground = SystemGrey6Light,
messageHighlightedBackground = Azure,
isLight = true,
)
// TODO Lots of colors are missing
val materialColorSchemeLight = lightColorScheme(
primary = Color.Black,
// TODO onPrimary = ColorLightTokens.OnPrimary,
// TODO primaryContainer = ColorLightTokens.PrimaryContainer,
// TODO onPrimaryContainer = ColorLightTokens.OnPrimaryContainer,
// TODO inversePrimary = ColorLightTokens.InversePrimary,
secondary = LightGrey,
// TODO onSecondary = ColorLightTokens.OnSecondary,
// TODO secondaryContainer = ColorLightTokens.SecondaryContainer,
// TODO onSecondaryContainer = ColorLightTokens.OnSecondaryContainer,
tertiary = Color.Black,
// TODO onTertiary = ColorLightTokens.OnTertiary,
// TODO tertiaryContainer = ColorLightTokens.TertiaryContainer,
// TODO onTertiaryContainer = ColorLightTokens.OnTertiaryContainer,
background = Color.White,
onBackground = Color.Black,
surface = Color.White,
onSurface = Color.Black,
surfaceVariant = SystemGrey5Light,
onSurfaceVariant = Color.Black,
// TODO surfaceTint = primary,
// TODO inverseSurface = ColorLightTokens.InverseSurface,
// TODO inverseOnSurface = ColorLightTokens.InverseOnSurface,
// TODO error = ColorLightTokens.Error,
// TODO onError = ColorLightTokens.OnError,
// TODO errorContainer = ColorLightTokens.ErrorContainer,
// TODO onErrorContainer = ColorLightTokens.OnErrorContainer,
// TODO outline = ColorLightTokens.Outline,
// TODO outlineVariant = ColorLightTokens.OutlineVariant,
// TODO scrim = ColorLightTokens.Scrim,
)
@Preview
@Composable
fun ColorsSchemePreviewLight() = ColorsSchemePreview(
Color.Black,
Color.White,
materialColorSchemeLight,
)

View file

@ -0,0 +1,58 @@
/*
* 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
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
class ElementColors(
messageFromMeBackground: Color,
messageFromOtherBackground: Color,
messageHighlightedBackground: Color,
isLight: Boolean,
) {
var messageFromMeBackground by mutableStateOf(messageFromMeBackground)
private set
var messageFromOtherBackground by mutableStateOf(messageFromOtherBackground)
private set
var messageHighlightedBackground by mutableStateOf(messageHighlightedBackground)
private set
var isLight by mutableStateOf(isLight)
private set
fun copy(
messageFromMeBackground: Color = this.messageFromMeBackground,
messageFromOtherBackground: Color = this.messageFromOtherBackground,
messageHighlightedBackground: Color = this.messageHighlightedBackground,
isLight: Boolean = this.isLight,
) = ElementColors(
messageFromMeBackground = messageFromMeBackground,
messageFromOtherBackground = messageFromOtherBackground,
messageHighlightedBackground = messageHighlightedBackground,
isLight = isLight,
)
fun updateColorsFrom(other: ElementColors) {
messageFromMeBackground = other.messageFromMeBackground
messageFromOtherBackground = other.messageFromOtherBackground
messageHighlightedBackground = other.messageHighlightedBackground
isLight = other.isLight
}
}

View file

@ -14,77 +14,58 @@
* limitations under the License.
*/
package io.element.android.libraries.designsystem
package io.element.android.libraries.designsystem.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.google.accompanist.systemuicontroller.rememberSystemUiController
private val DarkColorScheme = darkColorScheme(
primary = Color.White,
secondary = DarkGrey,
tertiary = Color.White,
background = Color.Black,
onBackground = Color.White,
surface = Color.Black,
surfaceVariant = SystemGrey5Dark,
onSurface = Color.White,
onSurfaceVariant = Color.White,
)
/**
* Inspired from https://medium.com/@lucasyujideveloper/54cbcbde1ace
*/
object ElementTheme {
val colors: ElementColors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
}
private val LightColorScheme = lightColorScheme(
primary = Color.Black,
secondary = LightGrey,
tertiary = Color.Black,
background = Color.White,
onBackground = Color.Black,
surface = Color.White,
surfaceVariant = SystemGrey5Light,
onSurface = Color.Black,
onSurfaceVariant = Color.Black,
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Suppress("CompositionLocalAllowlist")
val LocalIsDarkTheme = compositionLocalOf<Boolean> { error("Not defined") }
/* Global variables (application level) */
val LocalColors = staticCompositionLocalOf { elementColorsLight() }
@Composable
fun ElementXTheme(
fun ElementTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = false,
content: @Composable () -> Unit
dynamicColor: Boolean = false, /* true to enable MaterialYou */
lightColors: ElementColors = elementColorsLight(),
darkColors: ElementColors = elementColorsDark(),
materialLightColors: ColorScheme = materialColorSchemeLight,
materialDarkColors: ColorScheme = materialColorSchemeDark,
content: @Composable () -> Unit,
) {
val systemUiController = rememberSystemUiController()
val useDarkIcons = !darkTheme
val currentColor = remember { if (darkTheme) darkColors else lightColors }
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
darkTheme -> materialDarkColors
else -> materialLightColors
}
val systemUiController = rememberSystemUiController()
val useDarkIcons = !darkTheme
SideEffect {
systemUiController.setStatusBarColor(
color = colorScheme.background
@ -94,11 +75,13 @@ fun ElementXTheme(
darkIcons = useDarkIcons
)
}
CompositionLocalProvider(LocalIsDarkTheme provides darkTheme) {
val rememberedColors = remember { currentColor.copy() }.apply { updateColorsFrom(currentColor) }
CompositionLocalProvider(
LocalColors provides rememberedColors,
) {
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
// TODO typography =
content = content
)
}

View file

@ -0,0 +1,109 @@
/*
* 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
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.airbnb.android.showkase.annotation.ShowkaseTypography
/**
* TODO Provide the typo to Material3 theme.
*/
@ShowkaseTypography(name = "H1", group = "Element")
val h1Default: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Bold,
fontSize = 24.sp
)
@ShowkaseTypography(name = "Body1", group = "Element")
val body1Default: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
@ShowkaseTypography(name = "BodySmall", group = "Element")
val bodySmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
)
@ShowkaseTypography(name = "bodyMedium", group = "Element")
val bodyMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 18.sp
)
@ShowkaseTypography(name = "Body Large", group = "Element")
val bodyLargeDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
@ShowkaseTypography(name = "Headline Small", group = "Element")
val headlineSmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
lineHeight = 30.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "Headline Medium", group = "Element")
val headlineMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 28.sp,
lineHeight = 34.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "Headline Large", group = "Element")
val headlineLargeDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 32.sp,
lineHeight = 38.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "titleSmall", group = "Element")
val titleSmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.5.sp
)
@ShowkaseTypography(name = "titleMedium", group = "Element")
val titleMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 18.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)

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 Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = ButtonDefaults.shape,
colors: ButtonColors = ButtonDefaults.buttonColors(),
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
border: BorderStroke? = null,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
) {
androidx.compose.material3.Button(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
colors = colors,
elevation = elevation,
border = border,
contentPadding = contentPadding,
interactionSource = interactionSource,
content = content,
)
}
@Preview
@Composable
fun ButtonsLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun ButtonsDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
Button(onClick = {}, enabled = true) {
Text(text = "Click me! - Enabled")
}
Button(onClick = {}, enabled = false) {
Text(text = "Click me! - Disabled")
}
}
}

View file

@ -0,0 +1,65 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
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
@Composable
fun Checkbox(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
androidx.compose.material3.Checkbox(
checked = checked,
onCheckedChange = onCheckedChange,
modifier = modifier,
enabled = enabled,
colors = colors,
interactionSource = interactionSource,
)
}
@Preview
@Composable
fun CheckboxesLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun CheckboxesDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
Checkbox(onCheckedChange = {}, enabled = true, checked = true)
Checkbox(onCheckedChange = {}, enabled = true, checked = false)
Checkbox(onCheckedChange = {}, enabled = false, checked = true)
Checkbox(onCheckedChange = {}, enabled = false, checked = false)
}
}

View file

@ -0,0 +1,51 @@
/*
* 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.material3.ProgressIndicatorDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
@Composable
fun CircularProgressIndicator(
progress: Float,
modifier: Modifier = Modifier,
color: Color = ProgressIndicatorDefaults.circularColor,
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth
) {
androidx.compose.material3.CircularProgressIndicator(
modifier = modifier,
progress = progress,
color = color,
strokeWidth = strokeWidth,
)
}
@Composable
fun CircularProgressIndicator(
modifier: Modifier = Modifier,
color: Color = ProgressIndicatorDefaults.circularColor,
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
) {
androidx.compose.material3.CircularProgressIndicator(
modifier = modifier,
color = color,
strokeWidth = strokeWidth,
)
}

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.libraries.designsystem.theme.components
import androidx.compose.material3.DividerDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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
@Composable
fun Divider(
modifier: Modifier = Modifier,
thickness: Dp = DividerDefaults.Thickness,
color: Color = DividerDefaults.color,
) {
androidx.compose.material3.Divider(
modifier = modifier,
thickness = thickness,
color = color,
)
}
@Preview
@Composable
fun DividerLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun DividerDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Divider()
}

View file

@ -0,0 +1,50 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.FloatingActionButtonElevation
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
@Composable
fun FloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
shape: Shape = FloatingActionButtonDefaults.shape,
containerColor: Color = FloatingActionButtonDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable () -> Unit,
) {
androidx.compose.material3.FloatingActionButton(
onClick = onClick,
modifier = modifier,
shape = shape,
containerColor = containerColor,
contentColor = contentColor,
elevation = elevation,
interactionSource = interactionSource,
content = content,
)
}

View file

@ -0,0 +1,54 @@
/*
* 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.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
@Composable
fun Icon(
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current
) {
androidx.compose.material3.Icon(
imageVector = imageVector,
contentDescription = contentDescription,
modifier = modifier,
tint = tint,
)
}
@Composable
fun Icon(
bitmap: ImageBitmap,
contentDescription: String?,
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current
) {
androidx.compose.material3.Icon(
bitmap = bitmap,
contentDescription = contentDescription,
modifier = modifier,
tint = tint,
)
}

View file

@ -0,0 +1,41 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@Composable
fun IconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable () -> Unit
) {
androidx.compose.material3.IconButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
colors = IconButtonDefaults.iconButtonColors(),
interactionSource = interactionSource,
content = content,
)
}

View file

@ -0,0 +1,48 @@
/*
* 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.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
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediumTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
windowInsets: WindowInsets = TopAppBarDefaults.windowInsets,
colors: TopAppBarColors = TopAppBarDefaults.mediumTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
androidx.compose.material3.MediumTopAppBar(
title = title,
modifier = modifier,
navigationIcon = navigationIcon,
actions = actions,
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior,
)
}

View file

@ -0,0 +1,57 @@
/*
* 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.layout.ColumnScope
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetDefaults
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.contentColorFor
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ModalBottomSheetLayout(
sheetContent: @Composable ColumnScope.() -> Unit,
modifier: Modifier = Modifier,
sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
sheetShape: Shape = MaterialTheme.shapes.large,
sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
sheetBackgroundColor: Color = MaterialTheme.colors.surface,
sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
content: @Composable () -> Unit = {}
) {
androidx.compose.material.ModalBottomSheetLayout(
sheetContent = sheetContent,
modifier = modifier,
sheetState = sheetState,
sheetShape = sheetShape,
sheetElevation = sheetElevation,
sheetBackgroundColor = sheetBackgroundColor,
sheetContentColor = sheetContentColor,
scrimColor = scrimColor,
content = content,
)
}

View file

@ -0,0 +1,105 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
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.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
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 OutlinedTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.outlinedShape,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
androidx.compose.material3.OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
supportingText = supportingText,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
interactionSource = interactionSource,
shape = shape,
colors = colors,
)
}
@Preview
@Composable
fun OutlinedTextFieldsLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun OutlinedTextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
OutlinedTextField(onValueChange = {}, value = "Content", isError = false, enabled = true, readOnly = true)
OutlinedTextField(onValueChange = {}, value = "Content", isError = false, enabled = true, readOnly = false)
OutlinedTextField(onValueChange = {}, value = "Content", isError = false, enabled = false, readOnly = true)
OutlinedTextField(onValueChange = {}, value = "Content", isError = false, enabled = false, readOnly = false)
OutlinedTextField(onValueChange = {}, value = "Content", isError = true, enabled = true, readOnly = true)
OutlinedTextField(onValueChange = {}, value = "Content", isError = true, enabled = true, readOnly = false)
OutlinedTextField(onValueChange = {}, value = "Content", isError = true, enabled = false, readOnly = true)
OutlinedTextField(onValueChange = {}, value = "Content", isError = true, enabled = false, readOnly = false)
}
}

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.
*/
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable (PaddingValues) -> Unit
) {
androidx.compose.material3.Scaffold(
modifier = modifier,
topBar = topBar,
bottomBar = bottomBar,
snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition,
containerColor = containerColor,
contentColor = contentColor,
contentWindowInsets = contentWindowInsets,
content = content,
)
}

View file

@ -0,0 +1,70 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.SliderColors
import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
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
@Composable
fun Slider(
value: Float,
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
/*@IntRange(from = 0)*/
steps: Int = 0,
onValueChangeFinished: (() -> Unit)? = null,
colors: SliderColors = SliderDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
androidx.compose.material3.Slider(
value = value,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
valueRange = valueRange,
steps = steps,
onValueChangeFinished = onValueChangeFinished,
colors = colors,
interactionSource = interactionSource,
)
}
@Preview
@Composable
fun SlidersLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun SlidersDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
Slider(onValueChange = {}, value = 0.33f, enabled = true)
Slider(onValueChange = {}, value = 0.33f, enabled = false)
}
}

View file

@ -0,0 +1,51 @@
/*
* 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.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(color),
tonalElevation: Dp = 0.dp,
shadowElevation: Dp = 0.dp,
border: BorderStroke? = null,
content: @Composable () -> Unit
) {
androidx.compose.material3.Surface(
modifier = modifier,
shape = shape,
color = color,
contentColor = contentColor,
tonalElevation = tonalElevation,
shadowElevation = shadowElevation,
border = border,
content = content,
)
}

View file

@ -0,0 +1,178 @@
/*
* 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.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.utils.toHrf
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
@Composable
fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
androidx.compose.material3.Text(
text = text,
modifier = modifier,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
onTextLayout = onTextLayout,
style = style,
)
}
@Composable
fun Text(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
inlineContent: ImmutableMap<String, InlineTextContent> = persistentMapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
androidx.compose.material3.Text(
text = text,
modifier = modifier,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
inlineContent = inlineContent,
onTextLayout = onTextLayout,
style = style,
)
}
@Preview
@Composable
fun TextLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun TextDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
val colors = mapOf(
"primary" to MaterialTheme.colorScheme.primary,
"secondary" to MaterialTheme.colorScheme.secondary,
"tertiary" to MaterialTheme.colorScheme.tertiary,
"background" to MaterialTheme.colorScheme.background,
"error" to MaterialTheme.colorScheme.error,
"surface" to MaterialTheme.colorScheme.surface,
"surfaceVariant" to MaterialTheme.colorScheme.surfaceVariant,
"primaryContainer" to MaterialTheme.colorScheme.primaryContainer,
"secondaryContainer" to MaterialTheme.colorScheme.secondaryContainer,
"tertiaryContainer" to MaterialTheme.colorScheme.tertiaryContainer,
// "inversePrimary" to MaterialTheme.colorScheme.inversePrimary,
"errorContainer" to MaterialTheme.colorScheme.errorContainer,
"inverseSurface" to MaterialTheme.colorScheme.inverseSurface,
)
Column(
modifier = Modifier.width(IntrinsicSize.Max)
) {
colors.keys.forEach { name ->
val color = colors[name]!!
val textColor = contentColorFor(backgroundColor = color)
Box(
modifier = Modifier
.background(color = color)
.fillMaxWidth()
.padding(2.dp)
) {
Text(
text = "Text on $name\n${textColor.toHrf()} on ${color.toHrf()}",
color = textColor,
)
}
Spacer(modifier = Modifier.height(2.dp))
}
}
}

View file

@ -0,0 +1,79 @@
/*
* 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.interaction.MutableInteractionSource
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
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.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
androidx.compose.material3.TextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
supportingText = supportingText,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
interactionSource = interactionSource,
shape = shape,
colors = colors,
)
}

View file

@ -0,0 +1,48 @@
/*
* 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.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
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
windowInsets: WindowInsets = TopAppBarDefaults.windowInsets,
colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
androidx.compose.material3.TopAppBar(
title = title,
modifier = modifier,
navigationIcon = navigationIcon,
actions = actions,
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior,
)
}

View file

@ -0,0 +1,48 @@
/*
* 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.previews
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.collections.immutable.ImmutableMap
@Composable
internal fun ColorListPreview(
backgroundColor: Color,
foregroundColor: Color,
colors: ImmutableMap<String, Color>,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(color = backgroundColor)
.fillMaxWidth()
) {
colors.keys.forEach { name ->
val color = colors[name]!!
ColorPreview(backgroundColor = backgroundColor, foregroundColor = foregroundColor, name = name, color = color)
}
Spacer(modifier = Modifier.height(2.dp))
}
}

View file

@ -0,0 +1,64 @@
/*
* 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.previews
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.toHrf
@Composable
internal fun ColorPreview(
backgroundColor: Color,
foregroundColor: Color,
name: String, color: Color,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier.fillMaxWidth()) {
Text(text = name + " " + color.toHrf(), fontSize = 6.sp, color = foregroundColor)
val backgroundBrush = Brush.linearGradient(
listOf(
backgroundColor,
foregroundColor,
)
)
Row(
modifier = Modifier.background(backgroundBrush)
) {
repeat(2) {
Box(
modifier = Modifier
.padding(1.dp)
.background(color = color)
.height(10.dp)
.weight(1f)
)
}
}
}
}

View file

@ -0,0 +1,69 @@
/*
* 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.previews
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import kotlinx.collections.immutable.persistentMapOf
@Composable
internal fun ColorsSchemePreview(
backgroundColor: Color,
foregroundColor: Color,
colorScheme: ColorScheme,
modifier: Modifier = Modifier,
) {
val colors = persistentMapOf(
"primary" to colorScheme.primary,
"onPrimary" to colorScheme.onPrimary,
"primaryContainer" to colorScheme.primaryContainer,
"onPrimaryContainer" to colorScheme.onPrimaryContainer,
"inversePrimary" to colorScheme.inversePrimary,
"secondary" to colorScheme.secondary,
"onSecondary" to colorScheme.onSecondary,
"secondaryContainer" to colorScheme.secondaryContainer,
"onSecondaryContainer" to colorScheme.onSecondaryContainer,
"tertiary" to colorScheme.tertiary,
"onTertiary" to colorScheme.onTertiary,
"tertiaryContainer" to colorScheme.tertiaryContainer,
"onTertiaryContainer" to colorScheme.onTertiaryContainer,
"background" to colorScheme.background,
"onBackground" to colorScheme.onBackground,
"surface" to colorScheme.surface,
"onSurface" to colorScheme.onSurface,
"surfaceVariant" to colorScheme.surfaceVariant,
"onSurfaceVariant" to colorScheme.onSurfaceVariant,
"surfaceTint" to colorScheme.surfaceTint,
"inverseSurface" to colorScheme.inverseSurface,
"inverseOnSurface" to colorScheme.inverseOnSurface,
"error" to colorScheme.error,
"onError" to colorScheme.onError,
"errorContainer" to colorScheme.errorContainer,
"onErrorContainer" to colorScheme.onErrorContainer,
"outline" to colorScheme.outline,
"outlineVariant" to colorScheme.outlineVariant,
"scrim" to colorScheme.scrim,
)
ColorListPreview(
backgroundColor = backgroundColor,
foregroundColor = foregroundColor,
colors = colors,
modifier = modifier,
)
}

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.
*/
package io.element.android.libraries.designsystem.utils
import androidx.compose.ui.graphics.Color
/**
* Convert color to Human Readable Format.
*/
internal fun Color.toHrf(): String {
return "0x" + value.toString(16).take(8).uppercase()
}

View file

@ -23,5 +23,5 @@ android {
}
dependencies {
implementation("com.google.android.material:material:1.7.0")
implementation(libs.androidx.material)
}

View file

@ -19,7 +19,7 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.0"
kotlin("plugin.serialization") version "1.8.10"
}
android {

View file

@ -25,10 +25,18 @@ import io.element.android.libraries.matrix.room.MatrixRoom
import io.element.android.libraries.matrix.room.RoomSummaryDataSource
import io.element.android.libraries.matrixtest.media.FakeMediaResolver
import io.element.android.libraries.matrixtest.room.FakeMatrixRoom
import io.element.android.libraries.matrixtest.room.InMemoryRoomSummaryDataSource
import io.element.android.libraries.matrixtest.room.FakeRoomSummaryDataSource
import kotlinx.coroutines.delay
import org.matrix.rustcomponents.sdk.MediaSource
class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient {
class FakeMatrixClient(
override val sessionId: SessionId = SessionId(A_SESSION_ID),
private val userDisplayName: Result<String> = Result.success(A_USER_NAME),
private val userAvatarURLString: Result<String> = Result.success(AN_AVATAR_URL),
val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource()
) : MatrixClient {
private var logoutFailure: Throwable? = null
override fun getRoom(roomId: RoomId): MatrixRoom? {
return FakeMatrixRoom(roomId)
@ -39,23 +47,30 @@ class FakeMatrixClient(override val sessionId: SessionId) : MatrixClient {
override fun stopSync() = Unit
override fun roomSummaryDataSource(): RoomSummaryDataSource {
return InMemoryRoomSummaryDataSource()
return roomSummaryDataSource
}
override fun mediaResolver(): MediaResolver {
return FakeMediaResolver()
}
override suspend fun logout() = Unit
fun givenLogoutError(failure: Throwable) {
logoutFailure = failure
}
override fun userId(): UserId = UserId("")
override suspend fun logout() {
delay(100)
logoutFailure?.let { throw it }
}
override fun userId(): UserId = A_USER_ID
override suspend fun loadUserDisplayName(): Result<String> {
return Result.success("")
return userDisplayName
}
override suspend fun loadUserAvatarURLString(): Result<String> {
return Result.success("")
return userAvatarURLString
}
override suspend fun loadMediaContentForSource(source: MediaSource): Result<ByteArray> {

View file

@ -0,0 +1,44 @@
/*
* 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.matrixtest
import io.element.android.libraries.matrix.core.EventId
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.matrix.core.UserId
const val A_USER_NAME = "alice"
const val A_PASSWORD = "password"
val A_USER_ID = UserId("@alice:server.org")
val A_ROOM_ID = RoomId("!aRoomId")
val AN_EVENT_ID = EventId("\$anEventId")
const val A_ROOM_NAME = "A room name"
const val A_MESSAGE = "Hello world!"
const val A_REPLY = "OK, I'll be there!"
const val ANOTHER_MESSAGE = "Hello universe!"
const val A_HOMESERVER = "matrix.org"
const val A_HOMESERVER_2 = "matrix-client.org"
const val A_SESSION_ID = "sessionId"
const val AN_AVATAR_URL = "mxc://data"
const val A_FAILURE_REASON = "There has been a failure"
val A_THROWABLE = Throwable(A_FAILURE_REASON)
val AN_EXCEPTION = Exception(A_FAILURE_REASON)

View file

@ -0,0 +1,69 @@
/*
* 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.matrixtest.auth
import io.element.android.libraries.matrix.MatrixClient
import io.element.android.libraries.matrix.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.core.SessionId
import io.element.android.libraries.matrixtest.A_HOMESERVER
import io.element.android.libraries.matrixtest.A_SESSION_ID
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class FakeAuthenticationService : MatrixAuthenticationService {
private var homeserver: String = A_HOMESERVER
private var loginError: Throwable? = null
override fun isLoggedIn(): Flow<Boolean> {
return flowOf(false)
}
override suspend fun getLatestSessionId(): SessionId? {
return null
}
override suspend fun restoreSession(sessionId: SessionId): MatrixClient? {
return null
}
override fun getHomeserver(): String? {
return null
}
fun givenHomeserver(homeserver: String) {
this.homeserver = homeserver
}
override fun getHomeserverOrDefault(): String {
return homeserver
}
override suspend fun setHomeserver(homeserver: String) {
delay(100)
}
override suspend fun login(username: String, password: String): SessionId {
delay(100)
loginError?.let { throw it }
return SessionId(A_SESSION_ID)
}
fun givenLoginError(throwable: Throwable?) {
loginError = throwable
}
}

View file

@ -20,17 +20,20 @@ import io.element.android.libraries.matrix.core.EventId
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.matrix.room.MatrixRoom
import io.element.android.libraries.matrix.timeline.MatrixTimeline
import io.element.android.libraries.matrixtest.A_ROOM_ID
import io.element.android.libraries.matrixtest.timeline.FakeMatrixTimeline
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
class FakeMatrixRoom(
override val roomId: RoomId,
override val roomId: RoomId = A_ROOM_ID,
override val name: String? = null,
override val bestName: String = "",
override val displayName: String = "",
override val topic: String? = null,
override val avatarUrl: String? = null
override val avatarUrl: String? = null,
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
) : MatrixRoom {
override fun syncUpdateFlow(): Flow<Long> {
@ -38,7 +41,7 @@ class FakeMatrixRoom(
}
override fun timeline(): MatrixTimeline {
return FakeMatrixTimeline()
return matrixTimeline
}
override suspend fun fetchMembers(): Result<Unit> {
@ -54,18 +57,34 @@ class FakeMatrixRoom(
}
override suspend fun sendMessage(message: String): Result<Unit> {
TODO("Not yet implemented")
delay(100)
return Result.success(Unit)
}
var editMessageParameter: String? = null
private set
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
TODO("Not yet implemented")
editMessageParameter = message
delay(100)
return Result.success(Unit)
}
var replyMessageParameter: String? = null
private set
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
TODO("Not yet implemented")
replyMessageParameter = message
delay(100)
return Result.success(Unit)
}
var redactEventEventIdParam: EventId? = null
private set
override suspend fun redactEvent(eventId: EventId, reason: String?): Result<Unit> {
TODO("Not yet implemented")
redactEventEventIdParam = eventId
delay(100)
return Result.success(Unit)
}
}

View file

@ -21,11 +21,22 @@ import io.element.android.libraries.matrix.room.RoomSummaryDataSource
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class InMemoryRoomSummaryDataSource : RoomSummaryDataSource {
class FakeRoomSummaryDataSource : RoomSummaryDataSource {
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
return MutableStateFlow(emptyList())
private val roomSummariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
suspend fun postRoomSummary(roomSummaries: List<RoomSummary>) {
roomSummariesFlow.emit(roomSummaries)
}
override fun setSlidingSyncRange(range: IntRange) = Unit
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
return roomSummariesFlow
}
var latestSlidingSyncRange: IntRange? = null
private set
override fun setSlidingSyncRange(range: IntRange) {
latestSlidingSyncRange = range
}
}

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.libraries.matrixtest.room
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.matrix.room.RoomSummary
import io.element.android.libraries.matrix.room.RoomSummaryDetails
import io.element.android.libraries.matrixtest.A_MESSAGE
import io.element.android.libraries.matrixtest.A_ROOM_ID
import io.element.android.libraries.matrixtest.A_ROOM_NAME
fun aRoomSummaryFilled(
roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME,
isDirect: Boolean = false,
avatarURLString: String? = null,
lastMessage: CharSequence? = A_MESSAGE,
lastMessageTimestamp: Long? = null,
unreadNotificationCount: Int = 2,
) = RoomSummary.Filled(
aRoomSummaryDetail(
roomId = roomId,
name = name,
isDirect = isDirect,
avatarURLString = avatarURLString,
lastMessage = lastMessage,
lastMessageTimestamp = lastMessageTimestamp,
unreadNotificationCount = unreadNotificationCount,
)
)
fun aRoomSummaryDetail(
roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME,
isDirect: Boolean = false,
avatarURLString: String? = null,
lastMessage: CharSequence? = A_MESSAGE,
lastMessageTimestamp: Long? = null,
unreadNotificationCount: Int = 2,
) = RoomSummaryDetails(
roomId = roomId,
name = name,
isDirect = isDirect,
avatarURLString = avatarURLString,
lastMessage = lastMessage,
lastMessageTimestamp = lastMessageTimestamp,
unreadNotificationCount = unreadNotificationCount,
)

View file

@ -19,21 +19,20 @@ package io.element.android.libraries.matrixtest.timeline
import io.element.android.libraries.matrix.core.EventId
import io.element.android.libraries.matrix.timeline.MatrixTimeline
import io.element.android.libraries.matrix.timeline.MatrixTimelineItem
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import org.matrix.rustcomponents.sdk.TimelineListener
class FakeMatrixTimeline : MatrixTimeline {
override var callback: MatrixTimeline.Callback?
get() = null
set(value) {}
override var callback: MatrixTimeline.Callback? = null
override fun timelineItems(): Flow<List<MatrixTimelineItem>> {
return emptyFlow()
}
override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> {
delay(100)
return Result.success(Unit)
}

View file

@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -36,6 +35,9 @@ import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import io.element.android.libraries.matrix.ui.model.getBestName
@ -64,8 +66,9 @@ fun MatrixUserHeader(
fontWeight = FontWeight.SemiBold,
text = matrixUser.getBestName(),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
)
// Id
if (matrixUser.username.isNullOrEmpty().not()) {
Spacer(modifier = Modifier.height(4.dp))
@ -82,7 +85,14 @@ fun MatrixUserHeader(
@Preview
@Composable
fun MatrixUserHeaderPreview() {
fun MatrixUserHeaderLightPreview() = ElementPreviewLight { ContentToPreview1() }
@Preview
@Composable
fun MatrixUserHeaderDarkPreview() = ElementPreviewDark { ContentToPreview1() }
@Composable
private fun ContentToPreview1() {
MatrixUserHeader(
MatrixUser(
id = UserId("@alice:server.org"),
@ -94,7 +104,14 @@ fun MatrixUserHeaderPreview() {
@Preview
@Composable
fun MatrixUserHeaderNoUsernamePreview() {
fun MatrixUserHeaderNoUserNameLightPreview() = ElementPreviewLight { ContentToPreview2() }
@Preview
@Composable
fun MatrixUserHeaderNoUserNameDarkPreview() = ElementPreviewDark { ContentToPreview2() }
@Composable
private fun ContentToPreview2() {
MatrixUserHeader(
MatrixUser(
id = UserId("@alice:server.org"),

View file

@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -35,6 +34,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import io.element.android.libraries.matrix.ui.model.getBestName
@ -68,7 +70,8 @@ fun MatrixUserRow(
fontWeight = FontWeight.SemiBold,
text = matrixUser.getBestName(),
maxLines = 1,
overflow = TextOverflow.Ellipsis
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
)
// Id
if (matrixUser.username.isNullOrEmpty().not()) {
@ -86,7 +89,14 @@ fun MatrixUserRow(
@Preview
@Composable
fun MatrixUserRowPreview() {
fun MatrixUserRowLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun MatrixUserRowDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
MatrixUserRow(
MatrixUser(
id = UserId("@alice:server.org"),

View file

@ -35,8 +35,9 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.matrix)
implementation(projects.libraries.designsystem)
implementation(libs.wysiwyg)
implementation(libs.androidx.constraintlayout)
implementation("com.google.android.material:material:1.7.0")
implementation(libs.androidx.material)
ksp(libs.showkase.processor)
}

View file

@ -21,7 +21,7 @@ import android.net.Uri
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -33,6 +33,9 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@ -123,7 +126,8 @@ private fun FakeComposer(
.align(Alignment.Center),
textAlign = TextAlign.Center,
text = "Composer Preview",
fontSize = 20.sp
fontSize = 20.sp,
color = MaterialTheme.colorScheme.secondary,
)
}
}
@ -145,7 +149,14 @@ private fun MessageComposerView.setup(isDarkMode: Boolean, composerMode: Message
@Preview
@Composable
fun TextComposerPreview() {
fun TextComposerLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun TextComposerDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TextComposer(
onSendMessage = {},
fullscreen = false,

View file

@ -2979,4 +2979,5 @@
<string name="error_voice_message_broadcast_in_progress_message">Hlasovou zprávu nelze spustit, protože právě nahráváte živé vysílání. Ukončete prosím živé vysílání, abyste mohli začít nahrávat hlasovou zprávu</string>
<string name="error_voice_message_broadcast_in_progress">Nelze spustit hlasovou zprávu</string>
<string name="error_voice_broadcast_no_connection_recording">Chyba připojení - nahrávání pozastaveno</string>
<string name="rich_text_editor_inline_code">Použít formát inline kódu</string>
</resources>

View file

@ -2918,4 +2918,5 @@
<string name="error_voice_message_broadcast_in_progress_message">Du kannst keine Sprachnachricht beginnen, da du im Moment eine Echtzeitübertragung aufzeichnest. Bitte beende deine Sprachübertragung, um ein Gespräch zu beginnen</string>
<string name="error_voice_message_broadcast_in_progress">Kann Sprachnachricht nicht beginnen</string>
<string name="error_voice_broadcast_no_connection_recording">Verbindungsfehler Aufnahme pausiert</string>
<string name="rich_text_editor_inline_code">Als Inline-Code formatieren</string>
</resources>

View file

@ -2256,7 +2256,7 @@
<string name="create_poll_options_title">Koosta valikud</string>
<string name="create_poll_question_hint">Küsimus või teema</string>
<string name="create_poll_question_title">Küsitluse küsimus või teema</string>
<string name="create_poll_title">Koosta üks küsitlus</string>
<string name="create_poll_title">Loo selline küsitlus</string>
<string name="attachment_type_poll">Küsitlus</string>
<string name="identity_server_consent_dialog_title_2">Saada e-posti aadressid ja telefoninumbrid %s serverisse</string>
<string name="settings_discovery_consent_notice_off_2">Sinu kontaktid on vaid sinu teada. Kui tahad nende hulgast leida Matrix\'i kasutajaid, siis me vajame sinu luba nende andmete saatmiseks räsitud kujul isikutuvastusserverisse.</string>
@ -2330,9 +2330,9 @@
<string name="location_activity_title_preview">Asukoht</string>
<string name="location_activity_title_static_sharing">Jaga asukohta</string>
<string name="closed_poll_option_description">Tulemusi kuvame vaid siis, kui küsitlus on lõppenud</string>
<string name="closed_poll_option_title">Küsitlus on lõppenud</string>
<string name="closed_poll_option_title">Suletud valikutega küsitlus</string>
<string name="open_poll_option_description">Osalejad näevad tulemusi peale oma valiku salvestamist</string>
<string name="open_poll_option_title">Ava küsitlus</string>
<string name="open_poll_option_title">Avatud valikutega küsitlus</string>
<string name="poll_type_title">Küsitluse tüüp</string>
<string name="edit_poll_title">Muuda küsitlust</string>
<string name="poll_no_votes_cast">Hääletanuid ei ole</string>
@ -2910,4 +2910,5 @@
<string name="error_voice_message_broadcast_in_progress">Häälsõnumi esitamine ei õnnestu</string>
<string name="error_voice_message_broadcast_in_progress_message">Kuna sa hetkel salvestad ringhäälingukõnet, siis häälsõnumi salvestamine või esitamine ei õnnestu. Selleks palun lõpeta ringhäälingukõne</string>
<string name="error_voice_broadcast_no_connection_recording">Viga võrguühenduses - salvestamine on peatatud</string>
<string name="rich_text_editor_inline_code">Kasuta lõimitud koodi vormingut</string>
</resources>

View file

@ -2919,4 +2919,5 @@
<string name="error_voice_message_broadcast_in_progress_message">از آن‌جا که در حال ضبط پخشی زنده‌اید، نمی‌توانید پیامی صوتی را آغاز کنید. لطفاً برای آغاز ضبط یک پیام صوتی، پخش زنده‌تان را پایان دهید</string>
<string name="error_voice_message_broadcast_in_progress">نمی‌توان پخش صوتی را آغاز کرد</string>
<string name="error_voice_broadcast_no_connection_recording">خطای اتّصال - ضبط مکث شد</string>
<string name="rich_text_editor_inline_code">اعمال قالب کد درون‌خط</string>
</resources>

View file

@ -252,7 +252,7 @@
<string name="incoming_video_call">Saapuva videopuhelu</string>
<string name="incoming_voice_call">Saapuva puhelu</string>
<string name="call_in_progress">Puhelu käynnissä…</string>
<string name="call_error_user_not_responding">Toinen puoli ei vastannut.</string>
<string name="call_error_user_not_responding">Toinen osapuoli ei vastannut.</string>
<string name="permissions_rationale_popup_title">Huomio</string>
<string name="permissions_rationale_msg_record_audio">${app_name} tarvitsee käyttöluvan mikrofoniin suorittakseen puheluita.</string>
<string name="permissions_rationale_msg_camera_and_audio">${app_name} tarvitsee käyttöluvan kameraan ja mikrofoniin suorittakseen videopuheluita.
@ -887,7 +887,7 @@
<string name="settings_discovery_disconnect_with_bound_pid">Jaat sähköpostiosoitteita tai puhelinnumeroita identiteettipalvelimella %1$s. Sinun täytyy yhdistää uudelleen palvelimeen %2$s, jotta voit lopettaa niiden jakamisen.</string>
<string name="settings_agree_to_terms">Hyväksy identiteettipalvelimen (%s) käyttöehdot salliaksesi, että sinut voi löytää sähköpostiosoitteen tai puhelinnumeron perusteella.</string>
<string name="settings_discovery_disconnect_identity_server_info">Yhteyden katkaiseminen identiteettipalvelimeesi tarkoittaa, että muut käyttäjät eivät voi etsiä sinua etkä voi kutsua muita sähköpostin tai puhelinnumeron perusteella.</string>
<string name="settings_discovery_confirm_mail">Lähetimme sinulle vahvistussähköpostin osoitteeseen %s, tarkista sähköpostisi ja klikkaa vahvistuslinkkiä</string>
<string name="settings_discovery_confirm_mail">Lähetimme sinulle sähköpostia osoitteeseen %s. Tarkista sähköpostisi ja klikkaa vahvistuslinkkiä.</string>
<string name="labs_allow_extended_logging">Ota yksityiskohtaiset lokit käyttöön.</string>
<string name="error_terms_not_accepted">Yritä uudelleen, kun olet hyväksynyt kotipalvelimesi käyttöehdot.</string>
<string name="error_network_timeout">Palvelimen vastaus näyttäisi olevan liian hidas. Tämä voi johtua kehnosta yhteydestä tai palvelimella olevasta ongelmasta. Yritä hetken kuluttua uudelleen.</string>
@ -1026,7 +1026,7 @@
<string name="login_signin_to">Kirjaudu sisään palvelimeen %1$s</string>
<string name="login_signup">Rekisteröidy</string>
<string name="login_signin">Kirjaudu sisään</string>
<string name="login_signin_sso">Jatka kertakirjautumiseen</string>
<string name="login_signin_sso">Jatka kertakirjautumisella</string>
<string name="login_server_url_form_modular_hint">Element Matrix Services in osoite</string>
<string name="login_server_url_form_other_hint">Osoite</string>
<string name="login_server_url_form_modular_text">Korkealuokkaista isännöintiä organisaatioille</string>
@ -1097,8 +1097,8 @@
<string name="login_validation_code_is_not_correct">Syöttämäsi koodi ei ole kelvollinen. Tarkista se.</string>
<string name="login_error_outdated_homeserver_title">Vanhentunut kotipalvelin</string>
<plurals name="login_error_limit_exceeded_retry_after">
<item quantity="one">Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen 1 sekunnissa…</item>
<item quantity="other">Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen %1$d sekunnissa…</item>
<item quantity="one">Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen sekunnin kuluttua…</item>
<item quantity="other">Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen %1$d sekunnin kuluttua…</item>
</plurals>
<string name="seen_by">Nähneet</string>
<string name="signed_out_title">Olet kirjautunut ulos</string>
@ -2068,7 +2068,7 @@
<string name="ftue_account_created_congratulations_title">Onnittelut!</string>
<string name="ftue_account_created_personalize">Personoi profiili</string>
<string name="ftue_auth_use_case_skip_partial">ohittaa tämän kysymyksen</string>
<string name="ftue_auth_use_case_skip">Ei varmuutta vielä\? Voit %s</string>
<string name="ftue_auth_use_case_skip">Etkö ole vielä varma\? Voit %s</string>
<string name="settings_discovery_no_policy_provided">Identiteettipalvelin ei tarjoa käytäntöä</string>
<string name="settings_discovery_hide_identity_server_policy_title">Piilota identiteettipalvelimen käytäntö</string>
<string name="settings_discovery_show_identity_server_policy_title">Näytä identiteettipalvelimen käytäntö</string>
@ -2307,4 +2307,54 @@
<item quantity="one">%1$d valittu</item>
<item quantity="other">%1$d valittu</item>
</plurals>
</resources>
<string name="voice_broadcast_buffering">Puskuroidaan…</string>
<string name="error_voice_message_broadcast_in_progress">Ääniviestiä ei voi aloittaa</string>
<string name="room_using_unstable_room_version">Tässä huoneessa on käytössä huoneversio %s, jonka tämä kotipalvelin on merkinnyt epävakaaksi.</string>
<string name="space_leave_radio_button_none">Älä poistu mistään</string>
<string name="space_leave_radio_button_all">Poistu kaikista</string>
<string name="a11y_delete_avatar">Poista profiilikuva</string>
<string name="a11y_change_avatar">Vaihda profiilikuva</string>
<string name="call_dial_pad_lookup_error">Puhelinnumeron haussa tapahtui virhe</string>
<plurals name="invitations_sent_to_one_and_more_users">
<item quantity="one">Kutsut lähetetty käyttäjälle %1$s ja yhdelle muulle</item>
<item quantity="other">Kutsut lähetetty käyttäjälle %1$s ja %2$d muulle</item>
</plurals>
<string name="invitations_sent_to_two_users">Kutsu lähetetty käyttäjille %1$s ja %2$s</string>
<string name="invitation_sent_to_one_user">Kutsu lähetetty käyttäjälle %1$s</string>
<string name="send_your_first_msg_to_invite">Kutsu %s keskusteluun lähettämällä ensimmäinen viesti</string>
<string name="this_is_the_beginning_of_dm">Tästä alkaa yksityisviestihistoriasi sinun ja käyttäjän %s välillä.</string>
<string name="this_is_the_beginning_of_room">%s alkaa tästä.</string>
<string name="encryption_misconfigured">Salaus on säädetty väärin</string>
<string name="encryption_has_been_misconfigured">Salaus on säädetty väärin.</string>
<string name="login_error_outdated_homeserver_warning_content">Tällä kotipalvelimella on vanha versio. Pyydä kotipalvelimesi ylläpitäjää päivittämään se. Voit jatkaa, mutta jotkin ominaisuudet eivät välttämättä toimi oikein.</string>
<string name="ftue_auth_choose_server_ems_cta">Ota yhteyttä</string>
<string name="login_social_continue_with">Jatka %s-kirjautumisella</string>
<string name="ftue_auth_create_account_sso_section_header">tai</string>
<string name="ftue_auth_sign_in_choose_server_header">Keskustelujesi koti</string>
<string name="ftue_auth_create_account_choose_server_header">Keskustelujesi koti</string>
<string name="ftue_auth_use_case_subtitle">Laitetaan yhteydet kuntoon</string>
<string name="ftue_auth_use_case_title">Kenen kanssa juttelet eniten\?</string>
<string name="ftue_auth_carousel_workplace_body">${app_name} toimii mainiosti työpaikallakin. Siihen luottavat maailman turvallisimmat organisaatiot.</string>
<string name="jitsi_leave_conf_to_join_another_one_content">Poistutaanko nykyisestä ryhmäpuhelusta ja vaihdetaan toiseen\?</string>
<string name="directory_add_a_new_server_error_already_added">Tämä palvelin on jo luettelossa</string>
<string name="directory_add_a_new_server_error">Tätä palvelinta tai sen huoneluetteloa ei löydy</string>
<string name="room_settings_room_access_entry_knock">Kuka vain voi koputtaa huoneeseen ja jäsenet voivat sen jälkeen hyväksyä tai hylätä</string>
<string name="room_alias_unpublish_confirmation">Poista osoitteen \"%1$s\" julkaiseminen\?</string>
<string name="room_settings_room_notifications_encryption_notice">Huomaa, että maininnat ja avainsanailmoitukset eivät ole käytössä salausta käyttävissä huoneissa mobiililaitteilla.</string>
<string name="settings_enable_direct_share_title">Ota suora jako käyttöön</string>
<string name="settings_autoplay_animated_images_summary">Toista aikajanalla olevat animoidut kuvat heti, kun ne näkyvät</string>
<string name="settings_autoplay_animated_images_title">Toista animoidut kuvat automaattisesti</string>
<string name="settings_mentions_and_keywords_encryption_notice">Et saa ilmoituksia maininnoista ja avainsanoista salausta käyttävissä huoneissa mobiililaitteilla.</string>
<string name="settings_room_upgrades">Huonepäivitykset</string>
<string name="settings_messages_by_bot">Botin lähettämät viestit</string>
<string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} tarvitsee luvan ilmoitusten näyttämiseen.
\nAnna lupa.</string>
<string name="room_permissions_upgrade_the_room">Päivitä huone</string>
<string name="labs_enable_deferred_dm_title">Ota lykätyt yksityisviestit käyttöön</string>
<string name="action_deselect_all">Poista valinta kaikista</string>
<string name="action_select_all">Valitse kaikki</string>
<string name="denied_permission_voice_message">Anna mikrofonin käyttöoikeus ääniviestien lähettämiseksi.</string>
<string name="denied_permission_camera">Anna kameran käyttöoikeus järjestelmän asetuksista tämän toiminnon suorittamiseksi.</string>
<string name="denied_permission_generic">Tämän toiminnon suorittaminen vaatii enemmän oikeuksia. Anna oikeudet järjestelmän asetuksista.</string>
<string name="notification_listening_for_notifications">Kuunnellaan ilmoituksia</string>
</resources>

View file

@ -2919,4 +2919,5 @@
<string name="error_voice_message_broadcast_in_progress_message">Vous ne pouvez pas commencer un message vocal car vous êtes en train denregistrer une diffusion en direct. Veuillez terminer cette diffusion pour commencer un message vocal</string>
<string name="error_voice_message_broadcast_in_progress">Impossible de démarrer un message vocal</string>
<string name="error_voice_broadcast_no_connection_recording">Erreur de connexion Enregistrement en pause</string>
<string name="rich_text_editor_inline_code">Appliquer le formatage de code en ligne</string>
</resources>

View file

@ -2919,4 +2919,5 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
<string name="error_voice_message_broadcast_in_progress_message">Nem lehet hang üzenetet indítani élő közvetítés felvétele közben. Az élő közvetítés bejezése szükséges a hang üzenet indításához</string>
<string name="error_voice_message_broadcast_in_progress">Hang üzenetet nem lehet elindítani</string>
<string name="error_voice_broadcast_no_connection_recording">Kapcsolódási hiba Felvétel szüneteltetve</string>
<string name="rich_text_editor_inline_code">Beágyazott kód formátum alkalmazása</string>
</resources>

View file

@ -2861,4 +2861,5 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
<string name="error_voice_broadcast_no_connection_recording">Kesalahan koneksi - Perekaman dijeda</string>
<string name="error_voice_message_broadcast_in_progress_message">Anda tidak dapat memulai sebuah pesan suara karena Anda saat ini merekam sebuah siaran langsung. Silakan mengakhiri siaran langsung Anda untuk memulai merekam sebuah pesan suara</string>
<string name="error_voice_message_broadcast_in_progress">Tidak dapat memulai pesan suara</string>
<string name="rich_text_editor_inline_code">Terapkan format kode dalam baris</string>
</resources>

View file

@ -2382,4 +2382,44 @@
<string name="action_got_it">Náði því</string>
<string name="notice_voice_broadcast_ended_by_you">Þú endaðir talútsendingu.</string>
<string name="notice_voice_broadcast_ended">%1$s endaði talútsendingu.</string>
</resources>
<string name="rich_text_editor_full_screen_toggle">Víxla heilskjásham af/á</string>
<string name="rich_text_editor_bullet_list">Víxla punktalista af/á</string>
<string name="rich_text_editor_numbered_list">Víxla tölusettum lista af/á</string>
<string name="rich_text_editor_link">Setja tengil</string>
<string name="rich_text_editor_format_underline">Virkja undirstrikun</string>
<string name="rich_text_editor_format_strikethrough">Virkja yfirstrikun</string>
<string name="rich_text_editor_format_italic">Virkja skáletrað snið</string>
<string name="rich_text_editor_format_bold">Virkja feitletrað snið</string>
<string name="device_manager_other_sessions_description_unverified_current_session">Óstaðfest · Núverandi setan þín</string>
<string name="device_manager_other_sessions_description_unverified">Óstaðfest - Síðasta virkni %1$s</string>
<string name="device_manager_other_sessions_description_verified">Staðfest - Síðasta virkni %1$s</string>
<string name="settings_troubleshoot_test_current_gateway">Núverandi gátt: %s</string>
<string name="settings_troubleshoot_test_current_endpoint_failed">Finn ekki endapunktinn.</string>
<string name="settings_troubleshoot_test_current_endpoint_success">Núverandi endapunktur: %s</string>
<string name="settings_troubleshoot_test_current_endpoint_title">Endapunktur</string>
<string name="settings_troubleshoot_test_distributors_title">Tiltækar aðferðir</string>
<string name="stop_voice_broadcast_content">Ertu viss um að þú viljir stöðva þessa beinu útsendingu\? Þetta mun stöðva útsendinguna og full skráning hennar verður tiltæk á spjallrásinni.</string>
<string name="stop_voice_broadcast_dialog_title">Stöðva beina útsendingu\?</string>
<string name="error_voice_broadcast_no_connection_recording">Villa í tengingu - Upptaka í bið</string>
<string name="error_voice_broadcast_unable_to_play">Tekst ekki að spila þessa talútsendingu.</string>
<string name="error_voice_broadcast_unauthorized_title">Get ekki byrjað nýja talútsendingu</string>
<string name="a11y_pause_voice_broadcast">setja talútsendingu í bið</string>
<string name="a11y_play_voice_broadcast">Spila eða halda áfram með talútsendingu</string>
<string name="a11y_stop_voice_broadcast_record">Stöðva upptöku á talútsendingu</string>
<string name="a11y_pause_voice_broadcast_record">Setja upptöku á talútsendingu í bið</string>
<string name="a11y_resume_voice_broadcast_record">Halda áfram með upptöku á talútsendingu</string>
<string name="settings_security_incognito_keyboard_title">Nafnlaust lyklaborð</string>
<string name="delete_event_dialog_reason_checkbox">Tilgreindu ástæðu</string>
<string name="settings_server_upload_size_title">Takmörk netþjóns á innsendingum skráa</string>
<string name="settings_rageshake_detection_threshold">Takmörk fyrir greiningu</string>
<string name="uploads_files_no_result">Það eru engar skrár í þessari spjallrás</string>
<string name="a11y_create_message">Útbúa nýtt samtal eða spjallrás</string>
<string name="settings_text_message_sent_wrong_code">Staðfestingarkóðinn er ekki réttur.</string>
<string name="settings_discovery_msisdn_title">Uppgötvanleg símanúmer</string>
<string name="send_feedback_threads_title">Umsögn um beta-útgáfu spjallþráða</string>
<string name="settings_show_avatar_display_name_changes_messages_summary">Innifelur breytingar á auðkennismynd og birtingarnafni.</string>
<string name="settings_show_avatar_display_name_changes_messages">Birta atburði notandaaðgangs</string>
<string name="settings_enable_direct_share_title">Virkja beina deilingu</string>
<string name="threads_labs_enable_notice_title">Beta-útgáfa spjallþráða</string>
<string name="threads_beta_enable_notice_title">Beta-útgáfa spjallþráða</string>
</resources>

View file

@ -2909,4 +2909,6 @@
</plurals>
<string name="error_voice_message_broadcast_in_progress_message">Non puoi iniziare un messaggio vocale perché stai registrando una trasmissione in diretta. Termina la trasmissione per potere iniziare un messaggio vocale</string>
<string name="error_voice_message_broadcast_in_progress">Impossibile iniziare il messaggio vocale</string>
</resources>
<string name="rich_text_editor_inline_code">Applica formato codice interlinea</string>
<string name="error_voice_broadcast_no_connection_recording">Errore di connessione - Registrazione in pausa</string>
</resources>

File diff suppressed because it is too large Load diff

View file

@ -345,7 +345,7 @@
<string name="encryption_import_room_keys_summary">Importuj klucze z lokalnego pliku</string>
<string name="encryption_import_import">Importuj</string>
<string name="encryption_never_send_to_unverified_devices_title">Szyfruj wiadomości tylko do zaufanych sesji</string>
<string name="encryption_never_send_to_unverified_devices_summary">Nigdy nie wysyłaj szyfrowanych wiadomości do niezweryfikowanych sesji (bez zielonej tarczy) z tego urządzenia.</string>
<string name="encryption_never_send_to_unverified_devices_summary">Nigdy nie wysyłaj szyfrowanych wiadomości do niezweryfikowanych sesji (bez zielonej tarczy) z tej sesji.</string>
<string name="encryption_information_verify_device_warning">Aby sprawdzić czy ta sesja jest zaufana, skontaktuj się z jej właścicielem używając innych form (np. osobiście lub telefonicznie) i zapytaj czy klucz, który widzą w ustawieniach użytkownika dla tego urządzenia pasuje do klucza poniżej:</string>
<string name="encryption_information_verify_device_warning2">Jeśli klucz pasuje, potwierdź to przyciskiem poniżej. Jeśli nie, to ktoś inny najprawdopodobniej przejmuje lub podszywa się pod tą sesję i powinieneś dodać tę sesję do czarnej listy. W przyszłości proces weryfikacji będzie bardziej skomplikowany.</string>
<string name="title_activity_choose_sticker">Wyślij naklejkę</string>

View file

@ -2540,7 +2540,7 @@
<string name="error_forbidden_digits_only_username">Домашний сервер не принимает имя пользователя, состоящее только из цифр.</string>
<string name="ftue_personalize_skip_this_step">Пропустить этот шаг</string>
<string name="ftue_personalize_submit">Сохранить и продолжить</string>
<string name="ftue_personalize_complete_subtitle">Зайдите в настройки чтобы изменить Ваш профиль</string>
<string name="ftue_personalize_complete_subtitle">Ваши предпочтения были сохранены</string>
<string name="ftue_personalize_complete_title">Выглядит хорошо!</string>
<string name="ftue_auth_carousel_workplace_body">${app_name} также отлично подходит для работы. Ему доверяют самые надёжные организации в мире.</string>
<string name="keys_backup_settings_signature_from_this_user">Резервная копия имеет действительную подпись для данного пользователя.</string>

View file

@ -2979,4 +2979,5 @@
<string name="error_voice_message_broadcast_in_progress_message">Nemôžete spustiť hlasovú správu, pretože práve nahrávate živé vysielanie. Ukončite prosím živé vysielanie, aby ste mohli začať nahrávať hlasovú správu</string>
<string name="error_voice_message_broadcast_in_progress">Nemožno spustiť hlasovú správu</string>
<string name="error_voice_broadcast_no_connection_recording">Chyba pripojenia - nahrávanie pozastavené</string>
<string name="rich_text_editor_inline_code">Použiť formát riadkového kódu</string>
</resources>

View file

@ -2887,4 +2887,23 @@
<string name="error_voice_broadcast_unable_to_play">Sarrihet të luhet ky transmetim zanor.</string>
<string name="started_a_voice_broadcast">Nisni një transmetim zanor</string>
<string name="thread_list_not_available">Shërbyesi juaj Home smbulon ende paraqitje rrjedhash.</string>
</resources>
<string name="rich_text_editor_inline_code">Apliko format kodi brendazi</string>
<string name="room_polls_loading_error">Gabim në sjellje pyetësorësh.</string>
<string name="room_polls_load_more">Ngarko më tepër pyetësorë</string>
<string name="room_polls_wait_for_display">Shfaqje pyetësorësh</string>
<plurals name="room_polls_ended_no_item_for_loaded_period">
<item quantity="one">Ska pyetësorë të kaluar për ditën e kaluar.
\nQë të shihni pyetësorë për ditët e kaluara, ngarkoni më tepër pyetësorë.</item>
<item quantity="other">Ska pyetësorë aktivë për %1$d ditët e kaluara.
\nQë të shihni pyetësorë për ditët e kaluara, ngarkoni më tepër pyetësorë.</item>
</plurals>
<plurals name="room_polls_active_no_item_for_loaded_period">
<item quantity="one">Ska pyetësorë aktivë për ditën e kaluar.
\nQë të shihni pyetësorë për ditët e kaluara, ngarkoni më tepër pyetësorë.</item>
<item quantity="other">Ska pyetësorë aktivë për%1$d ditët e kaluara.
\nQë të shihni pyetësorë për ditët e kaluara, ngarkoni më tepër pyetësorë.</item>
</plurals>
<string name="error_voice_broadcast_no_connection_recording">Gabim lidhjeje - Incizimi u ndal</string>
<string name="error_voice_message_broadcast_in_progress_message">Smund të nisni një mesazh zanor teksa jeni aktualisht duke incizuar një transmetim të drejtpërdrejtë. Ju lutemi, përfundoni transmetimin tuaj të drejtpërdrejtë, që të mund të nisni incizimin e një mesazhi zanor</string>
<string name="error_voice_message_broadcast_in_progress">Sniset dot mesazh zanor</string>
</resources>

View file

@ -2898,4 +2898,26 @@
<string name="room_profile_section_more_polls">Omröstningshistorik</string>
<string name="thread_list_not_available">Din hemserver har inte stöd för att lista trådar än.</string>
<string name="action_stop">Ja, sluta</string>
</resources>
<string name="room_polls_loading_error">Fel vid hämtning av omröstningar.</string>
<string name="room_polls_load_more">Laddar fler omröstning</string>
<string name="room_polls_wait_for_display">Visar omröstningar</string>
<plurals name="room_polls_active_no_item_for_loaded_period">
<item quantity="one">Det finns inga aktiva omröstningar från förra dagen.
\nLadda fler omröstningar för att se omröstningar från tidigare dagar.</item>
<item quantity="other">Det finns inga aktiva omröstningar från senaste %1$d dagarna.
\nLadda fler omröstningar för att se omröstningar från tidigare dagar.</item>
</plurals>
<plurals name="room_polls_ended_no_item_for_loaded_period">
<item quantity="one">Det finns inga omröstningar från förra dagen.
\nLadda fler omröstningar för att se omröstningar från tidigare dagar.</item>
<item quantity="other">Det finns inga omröstningar från senaste %1$d dagarna.
\nLadda fler omröstningar för att se omröstningar från tidigare dagar.</item>
</plurals>
<string name="unable_to_decrypt_some_events_in_poll">På grund av avkrypteringsfel så kanske vissa röster inte räknas</string>
<string name="error_voice_broadcast_no_connection_recording">Anslutningsfel - Inspelning pausad</string>
<string name="error_voice_broadcast_unable_to_play">Kan inte spela den här röstsändningen.</string>
<string name="error_voice_message_broadcast_in_progress_message">Du kan inte påbörja ett röstmeddelande eftersom du för närvarande spelar in en röstsändning. Vänligen avsluta din röstsändning för att börja spela in ett röstmeddelande</string>
<string name="error_voice_message_broadcast_in_progress">Kan inte starta röstsändning</string>
<string name="started_a_voice_broadcast">Startade en röstsändning</string>
<string name="rich_text_editor_inline_code">Använd inline-kodformat</string>
</resources>

View file

@ -3039,4 +3039,5 @@
<string name="error_voice_message_broadcast_in_progress_message">Ви не можете розпочати запис голосового повідомлення, оскільки ви записуєте трансляцію наживо. Будь ласка, заверште її, щоб розпочати запис голосового повідомлення</string>
<string name="error_voice_message_broadcast_in_progress">Не вдалося розпочати запис голосового повідомлення</string>
<string name="error_voice_broadcast_no_connection_recording">Помилка з\'єднання - Запис призупинено</string>
<string name="rich_text_editor_inline_code">Застосовувати вбудований формат коду</string>
</resources>

View file

@ -1803,7 +1803,7 @@
<item quantity="other">%d 个条目</item>
</plurals>
<string name="not_a_valid_qr_code">这不是有效的 Matrix QR码</string>
<string name="user_code_scan">扫描二维</string>
<string name="user_code_scan">扫描QR</string>
<string name="add_people">添加人员</string>
<string name="invite_friends">邀请朋友</string>
<string name="settings_server_version">服务器版本</string>
@ -2819,4 +2819,5 @@
<string name="error_voice_broadcast_unable_to_play">无法播放此语音广播。</string>
<string name="error_voice_message_broadcast_in_progress_message">你无法启动语音消息因为你正在录制实时广播。请终止实时广播以开始录制语音消息</string>
<string name="error_voice_message_broadcast_in_progress">无法启动语音消息</string>
</resources>
<string name="ended_poll_indicator">结束了投票。</string>
</resources>

View file

@ -2856,4 +2856,8 @@
<item quantity="other">過去 %1$d 天沒有活躍的投票。
\n載入更多投票以檢視過去幾天的投票。</item>
</plurals>
</resources>
<string name="error_voice_broadcast_no_connection_recording">連線錯誤 - 錄製已暫停</string>
<string name="error_voice_message_broadcast_in_progress_message">您無法開始語音訊息,因為您目前正在錄製直播。請結束您的直播以開始錄製語音訊息</string>
<string name="error_voice_message_broadcast_in_progress">無法開始語音訊息</string>
<string name="rich_text_editor_inline_code">套用內嵌程式碼格式</string>
</resources>

View file

@ -1063,6 +1063,9 @@
<string name="settings_discovery_category">Discovery</string>
<string name="settings_discovery_manage">Manage your discovery settings.</string>
<string name="settings_external_account_management_title">Account</string>
<string name="settings_external_account_management">Your account details are managed separately at %1$s.</string>
<!-- analytics -->
<string name="settings_analytics">Analytics</string>
<string name="settings_opt_in_of_analytics">Send analytics data</string>
@ -3120,6 +3123,7 @@
<string name="error_voice_broadcast_already_in_progress_message">You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.</string>
<string name="error_voice_broadcast_unable_to_play">Unable to play this voice broadcast.</string>
<string name="error_voice_broadcast_no_connection_recording">Connection error - Recording paused</string>
<string name="error_voice_broadcast_unable_to_decrypt">Unable to decrypt this voice broadcast.</string>
<!-- Examples of usage: 6h 15min 30sec left / 15min 30sec left / 30sec left -->
<string name="voice_broadcast_recording_time_left">%1$s left</string>
<string name="stop_voice_broadcast_dialog_title">Stop live broadcasting?</string>
@ -3207,6 +3211,7 @@
<string name="room_polls_wait_for_display">Displaying polls</string>
<string name="room_polls_load_more">Load more polls</string>
<string name="room_polls_loading_error">Error fetching polls.</string>
<string name="room_poll_details_go_to_timeline">View poll in timeline</string>
<!-- Location -->
<string name="location_activity_title_static_sharing">Share location</string>
@ -3502,7 +3507,11 @@
<string name="rich_text_editor_link">Set link</string>
<string name="rich_text_editor_numbered_list">Toggle numbered list</string>
<string name="rich_text_editor_bullet_list">Toggle bullet list</string>
<string name="rich_text_editor_indent">Indent</string>
<string name="rich_text_editor_unindent">Unindent</string>
<string name="rich_text_editor_quote">Toggle quote</string>
<string name="rich_text_editor_inline_code">Apply inline code format</string>
<string name="rich_text_editor_code_block">Toggle code block</string>
<string name="rich_text_editor_full_screen_toggle">Toggle full screen mode</string>
<string name="set_link_text">Text</string>