Allow users to confirm link preview on text story before it loads.

This commit is contained in:
Alex Hart
2025-11-10 11:36:54 -04:00
committed by GitHub
parent b9a999b0d0
commit 2225a14e13
7 changed files with 31 additions and 38 deletions

View File

@@ -1,20 +0,0 @@
package org.thoughtcrime.securesms.linkpreview;
public class Link {
private final String url;
private final int position;
public Link(String url, int position) {
this.url = url;
this.position = position;
}
public String getUrl() {
return url;
}
public int getPosition() {
return position;
}
}

View File

@@ -0,0 +1,7 @@
package org.thoughtcrime.securesms.linkpreview
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
class Link(@JvmField val url: String, @JvmField val position: Int) : Parcelable

View File

@@ -15,7 +15,8 @@ class LinkPreviewState private constructor(
@JvmField val isLoading: Boolean,
private val hasLinks: Boolean,
private val preview: LinkPreview?,
@JvmField val error: LinkPreviewRepository.Error?
@JvmField val error: LinkPreviewRepository.Error?,
@JvmField val link: Link?
) : Parcelable {
@IgnoredOnParcel
@@ -30,15 +31,18 @@ class LinkPreviewState private constructor(
return isLoading || hasLinks
}
val url: String? = link?.url ?: preview?.url ?: activeUrlForError
companion object {
@JvmStatic
fun forLoading(): LinkPreviewState {
fun forLoading(link: Link): LinkPreviewState {
return LinkPreviewState(
activeUrlForError = null,
isLoading = true,
hasLinks = false,
preview = null,
error = null
error = null,
link = link
)
}
@@ -49,7 +53,8 @@ class LinkPreviewState private constructor(
isLoading = false,
hasLinks = true,
preview = linkPreview,
error = null
error = null,
link = null
)
}
@@ -60,7 +65,8 @@ class LinkPreviewState private constructor(
isLoading = false,
hasLinks = true,
preview = null,
error = error
error = error,
link = null
)
}
@@ -71,7 +77,8 @@ class LinkPreviewState private constructor(
isLoading = false,
hasLinks = false,
preview = null,
error = null
error = null,
link = null
)
}
}

View File

@@ -62,7 +62,7 @@ public final class LinkPreviewUtil {
return new Links(Stream.of(spannable.getSpans(0, spannable.length(), URLSpan.class))
.map(span -> new Link(span.getURL(), spannable.getSpanStart(span)))
.filter(link -> LinkUtil.isValidPreviewUrl(link.getUrl()))
.filter(link -> LinkUtil.isValidPreviewUrl(link.url))
.toList());
}
@@ -180,7 +180,7 @@ public final class LinkPreviewUtil {
private Links(@NonNull List<Link> links) {
this.links = links;
this.urlSet = Stream.of(links)
.map(link -> trimTrailingSlash(link.getUrl()))
.map(link -> trimTrailingSlash(link.url))
.collect(Collectors.toSet());
}

View File

@@ -124,7 +124,7 @@ public class LinkPreviewViewModel extends ViewModel {
Optional<Link> link = LinkPreviewUtil.findValidPreviewUrls(text)
.findFirst();
if (link.isPresent() && link.get().getUrl().equals(activeUrl)) {
if (link.isPresent() && link.get().url.equals(activeUrl)) {
return;
}
@@ -139,9 +139,9 @@ public class LinkPreviewViewModel extends ViewModel {
return;
}
linkPreviewState.setValue(LinkPreviewState.forLoading());
linkPreviewState.setValue(LinkPreviewState.forLoading(link.get()));
activeUrl = link.get().getUrl();
activeUrl = link.get().url;
activeRequest = enabled ? performRequest(activeUrl) : createPlaceholder(activeUrl);
});
}
@@ -186,11 +186,11 @@ public class LinkPreviewViewModel extends ViewModel {
return true;
}
if (text.endsWith(link.getUrl()) && cursorStart == link.getPosition() + link.getUrl().length()) {
if (text.endsWith(link.url) && cursorStart == link.position + link.url.length()) {
return true;
}
return cursorStart < link.getPosition() || cursorStart > link.getPosition() + link.getUrl().length();
return cursorStart < link.position || cursorStart > link.position + link.url.length();
}
private @Nullable RequestController createPlaceholder(String url) {

View File

@@ -88,7 +88,7 @@ class LinkPreviewViewModelV2(
}
val link: Optional<Link> = LinkPreviewUtil.findValidPreviewUrls(text).findFirst()
if (link.isPresent && link.get().url.equals(activeUrl)) {
if (link.isPresent && link.get().url == activeUrl) {
return@publish
}
@@ -100,7 +100,7 @@ class LinkPreviewViewModelV2(
return@publish
}
setLinkPreviewState(LinkPreviewState.forLoading())
setLinkPreviewState(LinkPreviewState.forLoading(link.get()))
val activeUrl = link.get().url
this.activeUrl = activeUrl

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.mediasend.v2.text
import android.content.DialogInterface
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.widget.EditText
import androidx.constraintlayout.widget.Group
@@ -64,7 +63,7 @@ class TextStoryPostLinkEntryFragment(private val shouldPreset: Boolean = false)
confirmButton.setOnClickListener {
val linkPreviewState = linkPreviewViewModel.linkPreviewState.value
if (linkPreviewState != null) {
val url = linkPreviewState.linkPreview.map { it.url }.orElseGet { linkPreviewState.activeUrlForError }
val url = linkPreviewState.url ?: ""
if (LinkUtil.isValidTextStoryPostPreview(url)) {
viewModel.setLinkPreview(url)
@@ -82,7 +81,7 @@ class TextStoryPostLinkEntryFragment(private val shouldPreset: Boolean = false)
linkPreviewViewModel.linkPreviewState.observe(viewLifecycleOwner) { state ->
linkPreview.bind(state, useLargeThumbnail = false)
shareALinkGroup.visible = !state.isLoading && !state.linkPreview.isPresent && (state.error == null && state.activeUrlForError == null)
confirmButton.isEnabled = state.linkPreview.isPresent || !TextUtils.isEmpty(state.activeUrlForError)
confirmButton.isEnabled = state.url != null
}
}