Merge pull request #2706 from vector-im/feature/ons/fix_url_preview_on_edit
Update url preview when the event is edited.
This commit is contained in:
commit
fef0404ac7
@ -10,6 +10,7 @@ Improvements 🙌:
|
|||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started.
|
- Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started.
|
||||||
- Sidebar too large in horizontal orientation or tablets (#475)
|
- Sidebar too large in horizontal orientation or tablets (#475)
|
||||||
|
- UrlPreview should be updated when the url is edited and changed (#2678)
|
||||||
- When receiving a new pepper from identity server, use it on the next hash lookup (#2708)
|
- When receiving a new pepper from identity server, use it on the next hash lookup (#2708)
|
||||||
- Crashes reported by PlayStore (new in 1.0.14) (#2707)
|
- Crashes reported by PlayStore (new in 1.0.14) (#2707)
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||||
|
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
internal class UrlsExtractorTest : InstrumentedTest {
|
internal class UrlsExtractorTest : InstrumentedTest {
|
||||||
@ -36,6 +38,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
fun wrongEventTypeTest() {
|
fun wrongEventTypeTest() {
|
||||||
createEvent(body = "https://matrix.org")
|
createEvent(body = "https://matrix.org")
|
||||||
.copy(type = EventType.STATE_ROOM_GUEST_ACCESS)
|
.copy(type = EventType.STATE_ROOM_GUEST_ACCESS)
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.size shouldBeEqualTo 0
|
.size shouldBeEqualTo 0
|
||||||
}
|
}
|
||||||
@ -43,6 +46,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun oneUrlTest() {
|
fun oneUrlTest() {
|
||||||
createEvent(body = "https://matrix.org")
|
createEvent(body = "https://matrix.org")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.let { result ->
|
.let { result ->
|
||||||
result.size shouldBeEqualTo 1
|
result.size shouldBeEqualTo 1
|
||||||
@ -53,6 +57,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun withoutProtocolTest() {
|
fun withoutProtocolTest() {
|
||||||
createEvent(body = "www.matrix.org")
|
createEvent(body = "www.matrix.org")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.size shouldBeEqualTo 0
|
.size shouldBeEqualTo 0
|
||||||
}
|
}
|
||||||
@ -60,6 +65,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun oneUrlWithParamTest() {
|
fun oneUrlWithParamTest() {
|
||||||
createEvent(body = "https://matrix.org?foo=bar")
|
createEvent(body = "https://matrix.org?foo=bar")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.let { result ->
|
.let { result ->
|
||||||
result.size shouldBeEqualTo 1
|
result.size shouldBeEqualTo 1
|
||||||
@ -70,6 +76,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun oneUrlWithParamsTest() {
|
fun oneUrlWithParamsTest() {
|
||||||
createEvent(body = "https://matrix.org?foo=bar&bar=foo")
|
createEvent(body = "https://matrix.org?foo=bar&bar=foo")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.let { result ->
|
.let { result ->
|
||||||
result.size shouldBeEqualTo 1
|
result.size shouldBeEqualTo 1
|
||||||
@ -80,6 +87,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun oneUrlInlinedTest() {
|
fun oneUrlInlinedTest() {
|
||||||
createEvent(body = "Hello https://matrix.org, how are you?")
|
createEvent(body = "Hello https://matrix.org, how are you?")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.let { result ->
|
.let { result ->
|
||||||
result.size shouldBeEqualTo 1
|
result.size shouldBeEqualTo 1
|
||||||
@ -90,6 +98,7 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
@Test
|
@Test
|
||||||
fun twoUrlsTest() {
|
fun twoUrlsTest() {
|
||||||
createEvent(body = "https://matrix.org https://example.org")
|
createEvent(body = "https://matrix.org https://example.org")
|
||||||
|
.toFakeTimelineEvent()
|
||||||
.let { urlsExtractor.extract(it) }
|
.let { urlsExtractor.extract(it) }
|
||||||
.let { result ->
|
.let { result ->
|
||||||
result.size shouldBeEqualTo 2
|
result.size shouldBeEqualTo 2
|
||||||
@ -99,10 +108,26 @@ internal class UrlsExtractorTest : InstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createEvent(body: String): Event = Event(
|
private fun createEvent(body: String): Event = Event(
|
||||||
|
eventId = "!fake",
|
||||||
type = EventType.MESSAGE,
|
type = EventType.MESSAGE,
|
||||||
content = MessageTextContent(
|
content = MessageTextContent(
|
||||||
msgType = MessageType.MSGTYPE_TEXT,
|
msgType = MessageType.MSGTYPE_TEXT,
|
||||||
body = body
|
body = body
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun Event.toFakeTimelineEvent(): TimelineEvent {
|
||||||
|
return TimelineEvent(
|
||||||
|
root = this,
|
||||||
|
localId = 0L,
|
||||||
|
eventId = eventId!!,
|
||||||
|
displayIndex = 0,
|
||||||
|
senderInfo = SenderInfo(
|
||||||
|
userId = "",
|
||||||
|
displayName = null,
|
||||||
|
isUniqueDisplayName = true,
|
||||||
|
avatarUrl = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,16 @@
|
|||||||
package org.matrix.android.sdk.api.session.media
|
package org.matrix.android.sdk.api.session.media
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
|
||||||
interface MediaService {
|
interface MediaService {
|
||||||
/**
|
/**
|
||||||
* Extract URLs from an Event.
|
* Extract URLs from a TimelineEvent.
|
||||||
* @return the list of URLs contains in the body of the Event. It does not mean that URLs in this list have UrlPreview data
|
* @param event TimelineEvent to extract the URL from.
|
||||||
|
* @return the list of URLs contains in the body of the TimelineEvent. It does not mean that URLs in this list have UrlPreview data
|
||||||
*/
|
*/
|
||||||
fun extractUrls(event: Event): List<String>
|
fun extractUrls(event: TimelineEvent): List<String>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Raw Url Preview data from the homeserver. There is no cache management for this request
|
* Get Raw Url Preview data from the homeserver. There is no cache management for this request
|
||||||
|
@ -89,6 +89,17 @@ data class TimelineEvent(
|
|||||||
*/
|
*/
|
||||||
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest known eventId for an edited event, or the eventId for an Event which has not been edited
|
||||||
|
*/
|
||||||
|
fun TimelineEvent.getLatestEventId(): String {
|
||||||
|
return annotations
|
||||||
|
?.editSummary
|
||||||
|
?.sourceEvents
|
||||||
|
?.lastOrNull()
|
||||||
|
?: eventId
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the relation content if any
|
* Get the relation content if any
|
||||||
*/
|
*/
|
||||||
|
@ -18,9 +18,10 @@ package org.matrix.android.sdk.internal.session.media
|
|||||||
|
|
||||||
import androidx.collection.LruCache
|
import androidx.collection.LruCache
|
||||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
|
||||||
import org.matrix.android.sdk.api.session.media.MediaService
|
import org.matrix.android.sdk.api.session.media.MediaService
|
||||||
import org.matrix.android.sdk.api.session.media.PreviewUrlData
|
import org.matrix.android.sdk.api.session.media.PreviewUrlData
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.getLatestEventId
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.internal.util.getOrPut
|
import org.matrix.android.sdk.internal.util.getOrPut
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -34,11 +35,12 @@ internal class DefaultMediaService @Inject constructor(
|
|||||||
// Cache of extracted URLs
|
// Cache of extracted URLs
|
||||||
private val extractedUrlsCache = LruCache<String, List<String>>(1_000)
|
private val extractedUrlsCache = LruCache<String, List<String>>(1_000)
|
||||||
|
|
||||||
override fun extractUrls(event: Event): List<String> {
|
override fun extractUrls(event: TimelineEvent): List<String> {
|
||||||
return extractedUrlsCache.getOrPut(event.cacheKey()) { urlsExtractor.extract(event) }
|
return extractedUrlsCache.getOrPut(event.cacheKey()) { urlsExtractor.extract(event) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Event.cacheKey() = "${eventId ?: ""}-${roomId ?: ""}"
|
// Use the id of the latest Event edition
|
||||||
|
private fun TimelineEvent.cacheKey() = "${getLatestEventId()}-${root.roomId ?: ""}"
|
||||||
|
|
||||||
override suspend fun getRawPreviewUrl(url: String, timestamp: Long?): JsonDict {
|
override suspend fun getRawPreviewUrl(url: String, timestamp: Long?): JsonDict {
|
||||||
return getRawPreviewUrlTask.execute(GetRawPreviewUrlTask.Params(url, timestamp))
|
return getRawPreviewUrlTask.execute(GetRawPreviewUrlTask.Params(url, timestamp))
|
||||||
|
@ -17,21 +17,19 @@
|
|||||||
package org.matrix.android.sdk.internal.session.media
|
package org.matrix.android.sdk.internal.session.media
|
||||||
|
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class UrlsExtractor @Inject constructor() {
|
internal class UrlsExtractor @Inject constructor() {
|
||||||
// Sadly Patterns.WEB_URL_WITH_PROTOCOL is not public so filter the protocol later
|
// Sadly Patterns.WEB_URL_WITH_PROTOCOL is not public so filter the protocol later
|
||||||
private val urlRegex = Patterns.WEB_URL.toRegex()
|
private val urlRegex = Patterns.WEB_URL.toRegex()
|
||||||
|
|
||||||
fun extract(event: Event): List<String> {
|
fun extract(event: TimelineEvent): List<String> {
|
||||||
return event.takeIf { it.getClearType() == EventType.MESSAGE }
|
return event.takeIf { it.root.getClearType() == EventType.MESSAGE }
|
||||||
?.getClearContent()
|
?.getLastMessageContent()
|
||||||
?.toModel<MessageContent>()
|
|
||||||
?.takeIf {
|
?.takeIf {
|
||||||
it.msgType == MessageType.MSGTYPE_TEXT
|
it.msgType == MessageType.MSGTYPE_TEXT
|
||||||
|| it.msgType == MessageType.MSGTYPE_NOTICE
|
|| it.msgType == MessageType.MSGTYPE_NOTICE
|
||||||
|
@ -141,7 +141,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
||||||
val params = FetchEditHistoryTask.Params(roomId, cryptoSessionInfoProvider.isRoomEncrypted(roomId), eventId)
|
val params = FetchEditHistoryTask.Params(roomId, eventId)
|
||||||
fetchEditHistoryTask
|
fetchEditHistoryTask
|
||||||
.configureWith(params) {
|
.configureWith(params) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.relation
|
|||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||||
|
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
@ -25,25 +26,27 @@ import org.matrix.android.sdk.internal.task.Task
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface FetchEditHistoryTask : Task<FetchEditHistoryTask.Params, List<Event>> {
|
internal interface FetchEditHistoryTask : Task<FetchEditHistoryTask.Params, List<Event>> {
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val isRoomEncrypted: Boolean,
|
|
||||||
val eventId: String
|
val eventId: String
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultFetchEditHistoryTask @Inject constructor(
|
internal class DefaultFetchEditHistoryTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
|
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider
|
||||||
) : FetchEditHistoryTask {
|
) : FetchEditHistoryTask {
|
||||||
|
|
||||||
override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
|
override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
|
||||||
|
val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId)
|
||||||
val response = executeRequest<RelationsResponse>(globalErrorReceiver) {
|
val response = executeRequest<RelationsResponse>(globalErrorReceiver) {
|
||||||
apiCall = roomAPI.getRelations(params.roomId,
|
apiCall = roomAPI.getRelations(
|
||||||
params.eventId,
|
roomId = params.roomId,
|
||||||
RelationType.REPLACE,
|
eventId = params.eventId,
|
||||||
if (params.isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE)
|
relationType = RelationType.REPLACE,
|
||||||
|
eventType = if (isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val events = response.chunks.toMutableList()
|
val events = response.chunks.toMutableList()
|
||||||
|
@ -1396,7 +1396,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
snapshot
|
snapshot
|
||||||
.takeIf { state.asyncRoomSummary.invoke()?.isEncrypted == false }
|
.takeIf { state.asyncRoomSummary.invoke()?.isEncrypted == false }
|
||||||
?.forEach {
|
?.forEach {
|
||||||
previewUrlRetriever.getPreviewUrl(it.root, viewModelScope)
|
previewUrlRetriever.getPreviewUrl(it, viewModelScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,41 +16,60 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.detail.timeline.url
|
package im.vector.app.features.home.room.detail.timeline.url
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.getLatestEventId
|
||||||
|
|
||||||
class PreviewUrlRetriever(session: Session) {
|
class PreviewUrlRetriever(session: Session) {
|
||||||
private val mediaService = session.mediaService()
|
private val mediaService = session.mediaService()
|
||||||
|
|
||||||
private val data = mutableMapOf<String, PreviewUrlUiState>()
|
private data class EventIdPreviewUrlUiState(
|
||||||
|
// Id of the latest event in the case of an edited event, or the eventId for an event which has not been edited
|
||||||
|
val latestEventId: String,
|
||||||
|
val previewUrlUiState: PreviewUrlUiState
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keys are the main eventId
|
||||||
|
private val data = mutableMapOf<String, EventIdPreviewUrlUiState>()
|
||||||
private val listeners = mutableMapOf<String, MutableSet<PreviewUrlRetrieverListener>>()
|
private val listeners = mutableMapOf<String, MutableSet<PreviewUrlRetrieverListener>>()
|
||||||
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
// In memory list
|
// In memory list
|
||||||
private val blockedUrl = mutableSetOf<String>()
|
private val blockedUrl = mutableSetOf<String>()
|
||||||
|
|
||||||
fun getPreviewUrl(event: Event, coroutineScope: CoroutineScope) {
|
fun getPreviewUrl(event: TimelineEvent, coroutineScope: CoroutineScope) {
|
||||||
val eventId = event.eventId ?: return
|
val eventId = event.root.eventId ?: return
|
||||||
|
val latestEventId = event.getLatestEventId()
|
||||||
|
|
||||||
synchronized(data) {
|
synchronized(data) {
|
||||||
if (data[eventId] == null) {
|
val current = data[eventId]
|
||||||
|
if (current?.latestEventId != latestEventId) {
|
||||||
|
// The event is not known or it has been edited
|
||||||
// Keep only the first URL for the moment
|
// Keep only the first URL for the moment
|
||||||
val url = mediaService.extractUrls(event)
|
val url = mediaService.extractUrls(event)
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.takeIf { it !in blockedUrl }
|
?.takeIf { it !in blockedUrl }
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
updateState(eventId, PreviewUrlUiState.NoUrl)
|
updateState(eventId, latestEventId, PreviewUrlUiState.NoUrl)
|
||||||
} else {
|
null
|
||||||
updateState(eventId, PreviewUrlUiState.Loading)
|
} else if (url != (current?.previewUrlUiState as? PreviewUrlUiState.Data)?.url) {
|
||||||
}
|
// There is a not known URL, or the Event has been edited and the URL has changed
|
||||||
|
updateState(eventId, latestEventId, PreviewUrlUiState.Loading)
|
||||||
url
|
url
|
||||||
} else {
|
} else {
|
||||||
// Already handled
|
// Already handled
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Already handled
|
||||||
|
null
|
||||||
|
}
|
||||||
}?.let { urlToRetrieve ->
|
}?.let { urlToRetrieve ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -64,15 +83,15 @@ class PreviewUrlRetriever(session: Session) {
|
|||||||
synchronized(data) {
|
synchronized(data) {
|
||||||
// Blocked after the request has been sent?
|
// Blocked after the request has been sent?
|
||||||
if (urlToRetrieve in blockedUrl) {
|
if (urlToRetrieve in blockedUrl) {
|
||||||
updateState(eventId, PreviewUrlUiState.NoUrl)
|
updateState(eventId, latestEventId, PreviewUrlUiState.NoUrl)
|
||||||
} else {
|
} else {
|
||||||
updateState(eventId, PreviewUrlUiState.Data(eventId, urlToRetrieve, it))
|
updateState(eventId, latestEventId, PreviewUrlUiState.Data(eventId, urlToRetrieve, it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
synchronized(data) {
|
synchronized(data) {
|
||||||
updateState(eventId, PreviewUrlUiState.Error(it))
|
updateState(eventId, latestEventId, PreviewUrlUiState.Error(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -86,20 +105,22 @@ class PreviewUrlRetriever(session: Session) {
|
|||||||
// Notify the listener
|
// Notify the listener
|
||||||
synchronized(data) {
|
synchronized(data) {
|
||||||
data[eventId]
|
data[eventId]
|
||||||
?.takeIf { it is PreviewUrlUiState.Data && it.url == url }
|
?.takeIf { it.previewUrlUiState is PreviewUrlUiState.Data && it.previewUrlUiState.url == url }
|
||||||
?.let {
|
?.let {
|
||||||
updateState(eventId, PreviewUrlUiState.NoUrl)
|
updateState(eventId, it.latestEventId, PreviewUrlUiState.NoUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateState(eventId: String, state: PreviewUrlUiState) {
|
private fun updateState(eventId: String, latestEventId: String, state: PreviewUrlUiState) {
|
||||||
data[eventId] = state
|
data[eventId] = EventIdPreviewUrlUiState(latestEventId, state)
|
||||||
// Notify the listener
|
// Notify the listener
|
||||||
|
uiHandler.post {
|
||||||
listeners[eventId].orEmpty().forEach {
|
listeners[eventId].orEmpty().forEach {
|
||||||
it.onStateUpdated(state)
|
it.onStateUpdated(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Called by the Epoxy item during binding
|
// Called by the Epoxy item during binding
|
||||||
fun addListener(key: String, listener: PreviewUrlRetrieverListener) {
|
fun addListener(key: String, listener: PreviewUrlRetrieverListener) {
|
||||||
@ -107,7 +128,7 @@ class PreviewUrlRetriever(session: Session) {
|
|||||||
|
|
||||||
// Give the current state if any
|
// Give the current state if any
|
||||||
synchronized(data) {
|
synchronized(data) {
|
||||||
listener.onStateUpdated(data[key] ?: PreviewUrlUiState.Unknown)
|
listener.onStateUpdated(data[key]?.previewUrlUiState ?: PreviewUrlUiState.Unknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user