commit
d7feb6dd5c
@ -10,7 +10,7 @@ Improvements 🙌:
|
|||||||
- Hide the algorithm when turning on e2e (#897)
|
- Hide the algorithm when turning on e2e (#897)
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
- Add support for /rainbow and /rainbowme commands (#879)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
-
|
-
|
||||||
|
@ -113,3 +113,39 @@ fun containsOnlyEmojis(str: String?): Boolean {
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as split, but considering emojis
|
||||||
|
*/
|
||||||
|
fun CharSequence.splitEmoji(): List<CharSequence> {
|
||||||
|
val result = mutableListOf<CharSequence>()
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
|
||||||
|
while (index < length) {
|
||||||
|
val firstChar = get(index)
|
||||||
|
|
||||||
|
if (firstChar.toInt() == 0x200e) {
|
||||||
|
// Left to right mark. What should I do with it?
|
||||||
|
} else if (firstChar.toInt() in 0xD800..0xDBFF && index + 1 < length) {
|
||||||
|
// We have the start of a surrogate pair
|
||||||
|
val secondChar = get(index + 1)
|
||||||
|
|
||||||
|
if (secondChar.toInt() in 0xDC00..0xDFFF) {
|
||||||
|
// We have an emoji
|
||||||
|
result.add("$firstChar$secondChar")
|
||||||
|
index++
|
||||||
|
} else {
|
||||||
|
// Not sure what we have here...
|
||||||
|
result.add("$firstChar")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Regular char
|
||||||
|
result.add("$firstChar")
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -37,6 +37,8 @@ enum class Command(val command: String, val parameters: String, @StringRes val d
|
|||||||
KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user),
|
KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user),
|
||||||
CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick),
|
CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick),
|
||||||
MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown),
|
MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown),
|
||||||
|
RAINBOW("/rainbow", "<message>", R.string.command_description_rainbow),
|
||||||
|
RAINBOW_EMOTE("/rainbowme", "<message>", R.string.command_description_rainbow_emote),
|
||||||
CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token),
|
CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token),
|
||||||
SPOILER("/spoiler", "<message>", R.string.command_description_spoiler);
|
SPOILER("/spoiler", "<message>", R.string.command_description_spoiler);
|
||||||
|
|
||||||
|
@ -80,6 +80,16 @@ object CommandParser {
|
|||||||
|
|
||||||
ParsedCommand.SendEmote(message)
|
ParsedCommand.SendEmote(message)
|
||||||
}
|
}
|
||||||
|
Command.RAINBOW.command -> {
|
||||||
|
val message = textMessage.subSequence(Command.RAINBOW.command.length, textMessage.length).trim()
|
||||||
|
|
||||||
|
ParsedCommand.SendRainbow(message)
|
||||||
|
}
|
||||||
|
Command.RAINBOW_EMOTE.command -> {
|
||||||
|
val message = textMessage.subSequence(Command.RAINBOW_EMOTE.command.length, textMessage.length).trim()
|
||||||
|
|
||||||
|
ParsedCommand.SendRainbowEmote(message)
|
||||||
|
}
|
||||||
Command.JOIN_ROOM.command -> {
|
Command.JOIN_ROOM.command -> {
|
||||||
if (messageParts.size >= 2) {
|
if (messageParts.size >= 2) {
|
||||||
val roomAlias = messageParts[1]
|
val roomAlias = messageParts[1]
|
||||||
|
@ -34,6 +34,8 @@ sealed class ParsedCommand {
|
|||||||
// Valid commands:
|
// Valid commands:
|
||||||
|
|
||||||
class SendEmote(val message: CharSequence) : ParsedCommand()
|
class SendEmote(val message: CharSequence) : ParsedCommand()
|
||||||
|
class SendRainbow(val message: CharSequence) : ParsedCommand()
|
||||||
|
class SendRainbowEmote(val message: CharSequence) : ParsedCommand()
|
||||||
class BanUser(val userId: String, val reason: String?) : ParsedCommand()
|
class BanUser(val userId: String, val reason: String?) : ParsedCommand()
|
||||||
class UnbanUser(val userId: String, val reason: String?) : ParsedCommand()
|
class UnbanUser(val userId: String, val reason: String?) : ParsedCommand()
|
||||||
class SetUserPowerLevel(val userId: String, val powerLevel: Int) : ParsedCommand()
|
class SetUserPowerLevel(val userId: String, val powerLevel: Int) : ParsedCommand()
|
||||||
|
@ -56,6 +56,7 @@ import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventConten
|
|||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.matrix.rx.unwrap
|
import im.vector.matrix.rx.unwrap
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.extensions.postLiveEvent
|
import im.vector.riotx.core.extensions.postLiveEvent
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
@ -64,6 +65,7 @@ import im.vector.riotx.core.utils.LiveEvent
|
|||||||
import im.vector.riotx.core.utils.subscribeLogError
|
import im.vector.riotx.core.utils.subscribeLogError
|
||||||
import im.vector.riotx.features.command.CommandParser
|
import im.vector.riotx.features.command.CommandParser
|
||||||
import im.vector.riotx.features.command.ParsedCommand
|
import im.vector.riotx.features.command.ParsedCommand
|
||||||
|
import im.vector.riotx.features.home.room.detail.composer.rainbow.RainbowGenerator
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
import im.vector.riotx.features.home.room.typing.TypingHelper
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
@ -83,6 +85,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val typingHelper: TypingHelper,
|
private val typingHelper: TypingHelper,
|
||||||
|
private val rainbowGenerator: RainbowGenerator,
|
||||||
private val session: Session
|
private val session: Session
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
|
||||||
|
|
||||||
@ -385,6 +388,20 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
|
is ParsedCommand.SendRainbow -> {
|
||||||
|
slashCommandResult.message.toString().let {
|
||||||
|
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it))
|
||||||
|
}
|
||||||
|
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||||
|
popDraft()
|
||||||
|
}
|
||||||
|
is ParsedCommand.SendRainbowEmote -> {
|
||||||
|
slashCommandResult.message.toString().let {
|
||||||
|
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE)
|
||||||
|
}
|
||||||
|
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||||
|
popDraft()
|
||||||
|
}
|
||||||
is ParsedCommand.SendSpoiler -> {
|
is ParsedCommand.SendSpoiler -> {
|
||||||
room.sendFormattedTextMessage(
|
room.sendFormattedTextMessage(
|
||||||
"[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})",
|
"[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})",
|
||||||
@ -401,7 +418,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
// TODO
|
// TODO
|
||||||
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
|
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
|
||||||
}
|
}
|
||||||
}
|
}.exhaustive
|
||||||
}
|
}
|
||||||
is SendMode.EDIT -> {
|
is SendMode.EDIT -> {
|
||||||
// is original event a reply?
|
// is original event a reply?
|
||||||
@ -459,7 +476,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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 im.vector.riotx.features.home.room.detail.composer.rainbow
|
||||||
|
|
||||||
|
import im.vector.riotx.core.utils.splitEmoji
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspired from React-Sdk
|
||||||
|
* Ref: https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/utils/colour.js
|
||||||
|
*/
|
||||||
|
class RainbowGenerator @Inject constructor() {
|
||||||
|
|
||||||
|
fun generate(text: String): String {
|
||||||
|
val split = text.splitEmoji()
|
||||||
|
val frequency = 360f / split.size
|
||||||
|
|
||||||
|
return split
|
||||||
|
.mapIndexed { idx, letter ->
|
||||||
|
// Do better than React-Sdk: Avoid adding font color for spaces
|
||||||
|
if (letter == " ") {
|
||||||
|
"$letter"
|
||||||
|
} else {
|
||||||
|
val dashColor = hueToRGB(idx * frequency, 1.0f, 0.5f).toDashColor()
|
||||||
|
"<font color=\"$dashColor\">$letter</font>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.joinToString(separator = "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hueToRGB(h: Float, s: Float, l: Float): RgbColor {
|
||||||
|
val c = s * (1 - abs(2 * l - 1))
|
||||||
|
val x = c * (1 - abs((h / 60) % 2 - 1))
|
||||||
|
val m = l - c / 2
|
||||||
|
|
||||||
|
var r = 0f
|
||||||
|
var g = 0f
|
||||||
|
var b = 0f
|
||||||
|
|
||||||
|
when {
|
||||||
|
h < 60f -> {
|
||||||
|
r = c
|
||||||
|
g = x
|
||||||
|
}
|
||||||
|
h < 120f -> {
|
||||||
|
r = x
|
||||||
|
g = c
|
||||||
|
}
|
||||||
|
h < 180f -> {
|
||||||
|
g = c
|
||||||
|
b = x
|
||||||
|
}
|
||||||
|
h < 240f -> {
|
||||||
|
g = x
|
||||||
|
b = c
|
||||||
|
}
|
||||||
|
h < 300f -> {
|
||||||
|
r = x
|
||||||
|
b = c
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
r = c
|
||||||
|
b = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RgbColor(
|
||||||
|
((r + m) * 255).roundToInt(),
|
||||||
|
((g + m) * 255).roundToInt(),
|
||||||
|
((b + m) * 255).roundToInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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 im.vector.riotx.features.home.room.detail.composer.rainbow
|
||||||
|
|
||||||
|
data class RgbColor(
|
||||||
|
val r: Int,
|
||||||
|
val g: Int,
|
||||||
|
val b: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
fun RgbColor.toDashColor(): String {
|
||||||
|
return listOf(r, g, b)
|
||||||
|
.joinToString(separator = "", prefix = "#") {
|
||||||
|
it.toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,9 @@
|
|||||||
<string name="room_list_sharing_header_recent_rooms">Recent rooms</string>
|
<string name="room_list_sharing_header_recent_rooms">Recent rooms</string>
|
||||||
<string name="room_list_sharing_header_other_rooms">Other rooms</string>
|
<string name="room_list_sharing_header_other_rooms">Other rooms</string>
|
||||||
|
|
||||||
|
<string name="command_description_rainbow">Sends the given message colored as a rainbow</string>
|
||||||
|
<string name="command_description_rainbow_emote">Sends the given emote colored as a rainbow</string>
|
||||||
|
|
||||||
<!-- Title for category in the settings which affect what is displayed in the timeline (ex: show read receipts, etc.) -->
|
<!-- Title for category in the settings which affect what is displayed in the timeline (ex: show read receipts, etc.) -->
|
||||||
<string name="settings_category_timeline">Timeline</string>
|
<string name="settings_category_timeline">Timeline</string>
|
||||||
|
|
||||||
|
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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 im.vector.riotx.features.home.room.detail.composer.rainbow
|
||||||
|
|
||||||
|
import im.vector.riotx.test.trimIndentOneLine
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@Suppress("SpellCheckingInspection")
|
||||||
|
class RainbowGeneratorTest {
|
||||||
|
|
||||||
|
private val rainbowGenerator = RainbowGenerator()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmpty() {
|
||||||
|
assertEquals("", rainbowGenerator.generate(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAscii1() {
|
||||||
|
assertEquals("""<font color="#ff0000">a</font>""", rainbowGenerator.generate("a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAscii2() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">a</font>
|
||||||
|
<font color="#00ffff">b</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("ab"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAscii3() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">T</font>
|
||||||
|
<font color="#ff5500">h</font>
|
||||||
|
<font color="#ffaa00">i</font>
|
||||||
|
<font color="#ffff00">s</font>
|
||||||
|
|
||||||
|
<font color="#55ff00">i</font>
|
||||||
|
<font color="#00ff00">s</font>
|
||||||
|
|
||||||
|
<font color="#00ffaa">a</font>
|
||||||
|
|
||||||
|
<font color="#00aaff">r</font>
|
||||||
|
<font color="#0055ff">a</font>
|
||||||
|
<font color="#0000ff">i</font>
|
||||||
|
<font color="#5500ff">n</font>
|
||||||
|
<font color="#aa00ff">b</font>
|
||||||
|
<font color="#ff00ff">o</font>
|
||||||
|
<font color="#ff00aa">w</font>
|
||||||
|
<font color="#ff0055">!</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("This is a rainbow!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmoji1() {
|
||||||
|
assertEquals("""<font color="#ff0000">🤞</font>""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🤞
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmoji2() {
|
||||||
|
assertEquals("""<font color="#ff0000">🤞</font>""", rainbowGenerator.generate("🤞"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmoji3() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">🤞</font>
|
||||||
|
<font color="#00ffff">🙂</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("🤞🙂"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmojiMix1() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">H</font>
|
||||||
|
<font color="#ff6d00">e</font>
|
||||||
|
<font color="#ffdb00">l</font>
|
||||||
|
<font color="#b6ff00">l</font>
|
||||||
|
<font color="#49ff00">o</font>
|
||||||
|
|
||||||
|
<font color="#00ff92">🤞</font>
|
||||||
|
|
||||||
|
<font color="#0092ff">w</font>
|
||||||
|
<font color="#0024ff">o</font>
|
||||||
|
<font color="#4900ff">r</font>
|
||||||
|
<font color="#b600ff">l</font>
|
||||||
|
<font color="#ff00db">d</font>
|
||||||
|
<font color="#ff006d">!</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("Hello 🤞 world!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmojiMix2() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">a</font>
|
||||||
|
<font color="#00ffff">🤞</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("a🤞"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmojiMix3() {
|
||||||
|
val expected = """
|
||||||
|
<font color="#ff0000">🤞</font>
|
||||||
|
<font color="#00ffff">a</font>
|
||||||
|
""".trimIndentOneLine()
|
||||||
|
|
||||||
|
assertEquals(expected, rainbowGenerator.generate("🤞a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testError1() {
|
||||||
|
assertEquals("<font color=\"#ff0000\">\uD83E</font>", rainbowGenerator.generate("\uD83E"))
|
||||||
|
}
|
||||||
|
}
|
19
vector/src/test/java/im/vector/riotx/test/Extensions.kt
Normal file
19
vector/src/test/java/im/vector/riotx/test/Extensions.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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 im.vector.riotx.test
|
||||||
|
|
||||||
|
fun String.trimIndentOneLine() = trimIndent().replace("\n", "")
|
Loading…
Reference in New Issue
Block a user