Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix long click crash on Android and iOS #168

Merged
merged 1 commit into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
#Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
org.gradle.caching=true
org.gradle.parallel=true

#Kotlin
kotlin.code.style=official

#MPP
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.enableCInteropCommonization=true

#Android
android.useAndroidX=true
android.nonTransitiveRClass=true

#Web
kotlin.js.compiler=ir
org.jetbrains.compose.experimental.jscanvas.enabled=true

#iOS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mohamedrejeb.richeditor.platform

internal actual val currentPlatform: Platform = Platform.Android
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi
import com.mohamedrejeb.richeditor.model.RichParagraph.Type.Companion.startText
import com.mohamedrejeb.richeditor.parser.html.RichTextStateHtmlParser
import com.mohamedrejeb.richeditor.parser.markdown.RichTextStateMarkdownParser
import com.mohamedrejeb.richeditor.platform.currentPlatform
import com.mohamedrejeb.richeditor.utils.*
import com.mohamedrejeb.richeditor.utils.append
import com.mohamedrejeb.richeditor.utils.customMerge
import com.mohamedrejeb.richeditor.utils.isSpecifiedFieldsEquals
import com.mohamedrejeb.richeditor.utils.unmerge
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlin.math.max

@Composable
Expand Down Expand Up @@ -506,6 +511,21 @@ class RichTextState internal constructor(
updateParagraphType(paragraph, RichParagraph.Type.Default)
}

private val textFieldValueSharedFlow = MutableSharedFlow<TextFieldValue>()

internal suspend fun emitTextFieldValue(textFieldValue: TextFieldValue) {
textFieldValueSharedFlow.emit(textFieldValue)
}

internal var collectTextFieldValueSharedFlowJob: Job? = null
internal suspend fun collectTextFieldValueSharedFlow() = coroutineScope {
collectTextFieldValueSharedFlowJob = launch {
textFieldValueSharedFlow.collect { newTextFieldValue ->
onTextFieldValueChange(newTextFieldValue)
}
}
}

/**
* Temporarily stores the new text field value, before it is validated.
*/
Expand All @@ -517,6 +537,11 @@ class RichTextState internal constructor(
* @param newTextFieldValue the new text field value.
*/
internal fun onTextFieldValueChange(newTextFieldValue: TextFieldValue) {
println("onTextFieldValueChange: ${newTextFieldValue.text.replace("\n", "<br/>")}")
println("same text field value: ${newTextFieldValue == tempTextFieldValue}")
println("same text: ${newTextFieldValue.text == tempTextFieldValue.text}")
println("same selection: ${newTextFieldValue.selection == tempTextFieldValue.selection}")
if (newTextFieldValue == tempTextFieldValue) return
tempTextFieldValue = newTextFieldValue

if (tempTextFieldValue.text.length > textFieldValue.text.length)
Expand Down Expand Up @@ -576,6 +601,12 @@ class RichTextState internal constructor(

// Clear [tempTextFieldValue]
tempTextFieldValue = TextFieldValue()

println("end edit")
richParagraphList.forEachIndexed { index, paragraph ->
println("Paragraph: $index")
println(paragraph.toString())
}
}

/**
Expand Down Expand Up @@ -628,7 +659,12 @@ class RichTextState internal constructor(

// Add empty space to the last paragraph if it's empty.
// Workaround to fix an issue with Compose TextField that causes a crash on long click
if (i > 0 && i == richParagraphList.lastIndex && richParagraph.isEmpty()) {
if (
(currentPlatform.isAndroid ||
currentPlatform.isIOS) &&
i > 0 && i == richParagraphList.lastIndex &&
richParagraph.isEmpty()
) {
richParagraph.getFirstNonEmptyChild()?.text = " "
append(" ")
index++
Expand Down Expand Up @@ -1027,6 +1063,10 @@ class RichTextState internal constructor(
val sliceIndex = max(index, richSpan.textRange.min)

// Create a new paragraph style

println("oldParagraphType: ${richSpan.paragraph.type}")
println("oldParagraphStartText: ${richSpan.paragraph.type.startText}")

val newParagraph = richSpan.paragraph.slice(
startIndex = sliceIndex,
richSpan = richSpan,
Expand All @@ -1050,6 +1090,10 @@ class RichTextState internal constructor(

// Update the paragraph type of the paragraphs after the new paragraph
val newParagraphType = newParagraph.type

println("newParagraphType: $newParagraphType")
println("newParagraphStartText: ${newParagraphType.startText}")

if (newParagraphType is RichParagraph.Type.OrderedList) {
tempTextFieldValue = adjustOrderedListsNumbers(
startParagraphIndex = paragraphIndex + 2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mohamedrejeb.richeditor.platform

internal enum class Platform {
Android, IOS, Desktop, Web;

val isAndroid: Boolean
get() = this == Android

val isIOS: Boolean
get() = this == IOS

val isDesktop: Boolean
get() = this == Desktop

val isWeb: Boolean
get() = this == Web
}

internal expect val currentPlatform: Platform
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.sp
import com.mohamedrejeb.richeditor.model.RichParagraph
import com.mohamedrejeb.richeditor.model.RichTextState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch


/**
Expand Down Expand Up @@ -197,6 +198,7 @@ internal fun BasicRichTextEditor(
@Composable { innerTextField -> innerTextField() },
contentPadding: PaddingValues
) {
val scope = rememberCoroutineScope()
val density = LocalDensity.current
val localTextStyle = LocalTextStyle.current
val layoutDirection = LocalLayoutDirection.current
Expand Down Expand Up @@ -233,12 +235,25 @@ internal fun BasicRichTextEditor(
}
}

DisposableEffect(state) {
scope.launch {
state.collectTextFieldValueSharedFlow()
}

onDispose {
state.collectTextFieldValueSharedFlowJob?.cancel()
}
}

CompositionLocalProvider(LocalClipboardManager provides richClipboardManager) {
BasicTextField(
value = state.textFieldValue,
onValueChange = {
if (readOnly) return@BasicTextField
if (it.text.length > maxLength) return@BasicTextField
// scope.launch {
// state.emitTextFieldValue(it)
// }
state.onTextFieldValueChange(it)
},
modifier = modifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mohamedrejeb.richeditor.platform

internal actual val currentPlatform: Platform = Platform.Desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mohamedrejeb.richeditor.platform

internal actual val currentPlatform: Platform = Platform.IOS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mohamedrejeb.richeditor.platform

internal actual val currentPlatform: Platform = Platform.Web
Binary file not shown.
8 changes: 6 additions & 2 deletions sample/web/src/jsMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.CanvasBasedWindow
import com.mohamedrejeb.richeditor.sample.common.App
import org.jetbrains.skiko.wasm.onWasmReady

@OptIn(ExperimentalComposeUiApi::class)
fun main() {
onWasmReady {
Window("Compose Rich Editor") {
CanvasBasedWindow(
title = "Compose Rich Editor"
) {
Box(Modifier.fillMaxSize()) {
App()
}
Expand Down
23 changes: 9 additions & 14 deletions sample/web/src/jsMain/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,17 @@
<head>
<meta charset="UTF-8">
<title>Compose Rich Editor</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
<script src="skiko.js"></script>
</head>
<body>
<canvas id="ComposeTarget"></canvas>
<script>
var canvas = document.getElementById("ComposeTarget");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
</script>
<script src="skiko.js"></script>
<noscript>
This page requires JavaScript.
</noscript>
<div id="root" class="d-flex flex-column h-100">
</div>
<div>
<canvas id="ComposeTarget" width="800" height="600"></canvas>
</div>
<script src="web.js"></script>
</body>
</html>
Empty file removed sample/web/webpack.config.d/fs.js
Empty file.