Skip to content

Commit 2785da1

Browse files
authored
Merge branch 'trunk' into update-compile-sdk-to-33
2 parents 0d2a0db + 9cd1615 commit 2785da1

File tree

3 files changed

+139
-25
lines changed

3 files changed

+139
-25
lines changed

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
22032203
}
22042204

22052205
fun removeMedia(attributePredicate: AttributePredicate) {
2206+
history.beforeTextChanged(this@AztecText)
22062207
text.getSpans(0, text.length, AztecMediaSpan::class.java)
22072208
.filter {
22082209
attributePredicate.matches(it.attributes)
@@ -2250,6 +2251,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
22502251
}
22512252
mediaSpan.onMediaDeleted()
22522253
}
2254+
contentChangeWatcher.notifyContentChanged()
22532255
}
22542256

22552257
fun replaceMediaSpan(aztecMediaSpan: AztecMediaSpan, predicate: (Attributes) -> Boolean) {

media-placeholders/src/main/java/org/wordpress/aztec/placeholders/PlaceholderManager.kt

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,35 +124,74 @@ class PlaceholderManager(
124124
val targetSpan = targetItem?.span
125125
val currentType = targetSpan?.attributes?.getValue(TYPE_ATTRIBUTE)
126126
if (currentType != null && shouldMergeItem(currentType)) {
127-
val adapter = adapters[type]
128-
?: throw IllegalArgumentException("Adapter for inserted type not found. Register it with `registerAdapter` method")
129-
val currentAttributes = mutableMapOf<String, String>()
130-
val uuid = targetSpan.attributes.getValue(UUID_ATTRIBUTE)
131-
for (i in 0 until targetSpan.attributes.length) {
132-
val name = targetSpan.attributes.getQName(i)
133-
val value = targetSpan.attributes.getValue(name)
134-
currentAttributes[name] = value
135-
}
136-
val updatedAttributes = updateItem(currentAttributes, currentType, targetItem.placeAtStart)
137-
val attrs = AztecAttributes().apply {
138-
updatedAttributes.forEach { (key, value) ->
139-
setValue(key, value)
140-
}
141-
}
142-
attrs.setValue(UUID_ATTRIBUTE, uuid)
143-
attrs.setValue(TYPE_ATTRIBUTE, type)
144-
val drawable = buildPlaceholderDrawable(adapter, attrs)
145-
val span = AztecPlaceholderSpan(aztecText.context, drawable, 0, attrs,
146-
this, aztecText, WeakReference(adapter), TAG = htmlTag)
147-
aztecText.replaceMediaSpan(span) { attributes ->
148-
attributes.getValue(UUID_ATTRIBUTE) == uuid
149-
}
150-
insertContentOverSpanWithId(uuid)
127+
updateSpan(type, targetItem.span, targetItem.placeAtStart, updateItem, currentType)
151128
} else {
152129
insertItem(type, *updateItem(null, null, false).toList().toTypedArray())
153130
}
154131
}
155132

133+
private suspend fun updateSpan(
134+
type: String,
135+
targetSpan: AztecPlaceholderSpan,
136+
placeAtStart: Boolean,
137+
updateItem: (currentAttributes: Map<String, String>, currentType: String, placeAtStart: Boolean) -> Map<String, String>,
138+
currentType: String
139+
) {
140+
val adapter = adapters[type]
141+
?: throw IllegalArgumentException("Adapter for inserted type not found. Register it with `registerAdapter` method")
142+
val currentAttributes = mutableMapOf<String, String>()
143+
val uuid = targetSpan.attributes.getValue(UUID_ATTRIBUTE)
144+
for (i in 0 until targetSpan.attributes.length) {
145+
val name = targetSpan.attributes.getQName(i)
146+
val value = targetSpan.attributes.getValue(name)
147+
currentAttributes[name] = value
148+
}
149+
val updatedAttributes = updateItem(currentAttributes, currentType, placeAtStart)
150+
val attrs = AztecAttributes().apply {
151+
updatedAttributes.forEach { (key, value) ->
152+
setValue(key, value)
153+
}
154+
}
155+
attrs.setValue(UUID_ATTRIBUTE, uuid)
156+
attrs.setValue(TYPE_ATTRIBUTE, type)
157+
val drawable = buildPlaceholderDrawable(adapter, attrs)
158+
val span = AztecPlaceholderSpan(aztecText.context, drawable, 0, attrs,
159+
this, aztecText, WeakReference(adapter), TAG = htmlTag)
160+
aztecText.replaceMediaSpan(span) { attributes ->
161+
attributes.getValue(UUID_ATTRIBUTE) == uuid
162+
}
163+
insertContentOverSpanWithId(uuid)
164+
}
165+
166+
/**
167+
* Use this function to either update or remove an item. The decision whether to remove or update will be made
168+
* based upon the results of the parameter functions. An example of usage is a gallery of images. If the user wants
169+
* to remove one image in the gallery, they would call this method. If the removed image is one of many, they might
170+
* want to update the current parameters instead of removing the entire gallery. However, if the removed image is
171+
* the last one in the gallery, they will probably want to remove the entire gallery.
172+
* @param uuid UUID of the span we want to remove or update
173+
* @param shouldUpdateItem This function should return true if the span can be updated, false if it should be removed
174+
* @param updateItem Function that updates the selected item
175+
*/
176+
suspend fun removeOrUpdate(uuid: String, shouldUpdateItem: (Attributes) -> Boolean, updateItem: (currentAttributes: Map<String, String>) -> Map<String, String>): Boolean {
177+
val currentItem = aztecText.editableText.getSpans(0, aztecText.length(), AztecPlaceholderSpan::class.java).find {
178+
it.attributes.getValue(UUID_ATTRIBUTE) == uuid
179+
} ?: return false
180+
if (shouldUpdateItem(currentItem.attributes)) {
181+
val type = currentItem.attributes.getValue(TYPE_ATTRIBUTE)
182+
val selectionStart = aztecText.selectionStart
183+
val selectionEnd = aztecText.selectionEnd
184+
aztecText.setSelection(aztecText.editableText.getSpanStart(currentItem))
185+
updateSpan(type, currentItem, updateItem = { attributes, _, _ ->
186+
updateItem(attributes)
187+
}, placeAtStart = false, currentType = type)
188+
aztecText.setSelection(selectionStart, selectionEnd)
189+
} else {
190+
removeItem(uuid)
191+
}
192+
return true
193+
}
194+
156195
private data class TargetItem(val span: AztecPlaceholderSpan, val placeAtStart: Boolean)
157196

158197
private fun getTargetItem(): TargetItem? {
@@ -189,13 +228,21 @@ class PlaceholderManager(
189228
}
190229

191230
/**
192-
* Call this method to remove a placeholder from both the AztecText and the overlaying layer programatically.
231+
* Call this method to remove a placeholder from both the AztecText and the overlaying layer programmatically.
193232
* @param predicate determines whether a span should be removed
194233
*/
195234
fun removeItem(predicate: (Attributes) -> Boolean) {
196235
aztecText.removeMedia { predicate(it) }
197236
}
198237

238+
/**
239+
* Call this method to remove a placeholder from both the AztecText and the overlaying layer programmatically.
240+
* @param uuid of the removed item
241+
*/
242+
fun removeItem(uuid: String) {
243+
aztecText.removeMedia { it.getValue(UUID_ATTRIBUTE) == uuid }
244+
}
245+
199246
private suspend fun buildPlaceholderDrawable(adapter: PlaceholderAdapter, attrs: AztecAttributes): Drawable {
200247
val drawable = ContextCompat.getDrawable(aztecText.context, android.R.color.transparent)!!
201248
val editorWidth = if (aztecText.width > 0) {

media-placeholders/src/test/java/org/wordpress/aztec/placeholders/PlaceholderTest.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,69 @@ class PlaceholderTest {
147147
private fun placeholderWithCaption(caption: String): String {
148148
return "<placeholder src=\"image.jpg\" caption=\"$caption\" uuid=\"uuid123\" type=\"image_with_caption\" />"
149149
}
150+
151+
@Test
152+
@Throws(Exception::class)
153+
fun updatePlaceholderWhenItShouldBe() {
154+
runBlocking {
155+
val initialHtml = "<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
156+
editText.fromHtml(initialHtml)
157+
158+
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
159+
true
160+
}) { currentAttributes ->
161+
val result = mutableMapOf<String, String>()
162+
result["src"] = currentAttributes["src"]?.split(";")?.firstOrNull() ?: ""
163+
result["caption"] = "Updated caption"
164+
result
165+
}
166+
167+
Assert.assertEquals("<placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid123\" type=\"image_with_caption\" /><p>Line</p>", editText.toHtml())
168+
}
169+
}
170+
171+
@Test
172+
@Throws(Exception::class)
173+
fun updatePlaceholderAtTheEnd() {
174+
runBlocking {
175+
val initialHtml = "<p>First Line</p><placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Second Line</p>"
176+
editText.fromHtml(initialHtml)
177+
editText.setSelection(editText.editableText.indexOf("First") + 1)
178+
val initialSelectionStart = editText.selectionStart
179+
val initialSelectionEnd = editText.selectionEnd
180+
181+
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
182+
true
183+
}) { currentAttributes ->
184+
val result = mutableMapOf<String, String>()
185+
result["src"] = currentAttributes["src"]?.split(";")?.firstOrNull() ?: ""
186+
result["caption"] = "Updated caption"
187+
result
188+
}
189+
190+
Assert.assertEquals("<p>First Line</p><placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid123\" type=\"image_with_caption\" /><p>Second Line</p>", editText.toHtml())
191+
Assert.assertEquals(initialSelectionStart, editText.selectionStart)
192+
Assert.assertEquals(initialSelectionEnd, editText.selectionEnd)
193+
}
194+
}
195+
196+
@Test
197+
@Throws(Exception::class)
198+
fun removePlaceholderWhenItShouldNotBeUpdated() {
199+
runBlocking {
200+
val initialHtml = "<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
201+
editText.fromHtml(initialHtml)
202+
203+
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
204+
false
205+
}) { currentAttributes ->
206+
val result = mutableMapOf<String, String>()
207+
result["src"] = currentAttributes["src"]?.split(";")?.firstOrNull() ?: ""
208+
result["caption"] = "Updated caption"
209+
result
210+
}
211+
212+
Assert.assertEquals("<p>Line</p>", editText.toHtml())
213+
}
214+
}
150215
}

0 commit comments

Comments
 (0)