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

Link previews #148

Merged
merged 10 commits into from
Mar 28, 2025
Merged

Link previews #148

merged 10 commits into from
Mar 28, 2025

Conversation

matthewrfennell
Copy link
Contributor

demo.mp4.compressed.mp4

This PR includes:

  • Link previews, that work in both normal and reply messages
  • A configurable limit to the number of link previews per message (including disabling the previews)
  • An in-memory cache that keeps the enriched preview around on scroll and re-entering the view

Let me know if you have any thoughts or suggestions. Have a good day!

Closes #74

@matthewrfennell matthewrfennell marked this pull request as draft March 25, 2025 13:32
@matthewrfennell matthewrfennell force-pushed the link-preview branch 2 times, most recently from 153ca1a to a874a5b Compare March 25, 2025 16:40
@matthewrfennell matthewrfennell marked this pull request as ready for review March 25, 2025 16:40
@matthewrfennell
Copy link
Contributor Author

I found a crash after rebasing on 1e4718e (swift 6 change), that didn't show up on swift 5.7.

It turned out to be a new concurrency assertion:

  • LinkPresentation hasn't been updated with strict concurrency in mind
  • As a result, I couldn't use LPLinkMetadata in the main actor (since it is not Sendable, and startFetchingMetadata(for:) runs in a nonisolated context)

So, I added the @preconcurrency annotation to the LinkPresentation import for now. That prevents that assertion from being triggered, while we wait for Apple to update the library.

While at it, I made some minor style changes (moved the link pill code to Extensions and improved the naming of those classes), and used await syntax instead of callbacks when fetching the metadata.

This view lightly wraps LPLinkView, which does not have a SwiftUI
equivalent.

It is created so we can start adding support for link previews in
messages, without requiring the caller to use UIKit.

It also applies a light amount of custom styling: restricting the frame
of the view so that it appears like a "pill" instead of a full preview
to avoid taking up too much space in the message view.
This cache stores the metadata for link previews in memory. It has
performance benefits in two situations:

When scrolling past a MessageView, the metadata can be retrieved from
cache instead of regenerated (preventing the link preview from
flickering on scroll).

A ReplyMessage containing a link preview can instantly display the
cached preview, that was generated by the original Message.
This view abstracts away managing the placeholder and enriched
metadata, and swapping out the link preview views when needed.

It also transparently manages the link preview metadata cache, allowing
the consumer to simply pass a URL and not have to know whether there's
already a corresponding entry in the cache.

Note that we need to disable strict concurrency checking for the
LinkPresentation framework. This is because LPLinkMetadata is not
Sendable and startFetchingMetadata(for:) uses a background thread and
nonisolated context in order to generate the metadata.

As far as I can tell, there is no way to use LinkPresentation with
strict concurrency checking, as the preview view requires a metadata
instance, and this can't cross isolation boundaries. So, the
alternative would be to write our own version of the LinkPresentation
framework that supports strict concurrency checks, which seems to be
too heavy a solution.
This commit works around a bug in SwiftUI, where applying a .animation
modifier to a Group doesn't have any effect. The transition is animated
if ZStack is used instead.

Meanwhile, make the LinkPreviewMetadata enum equatable, so that the
.animation modifier can detect changes to the value. Using the
synthesized implementation, two LinkPreviewMetadatas are equal if they
are the same case, and have payloads that are memberwise-equal (which
is the behaviour we want in this case).
This property extracts all URLs from the given AttributedString.

It relies on the AttributedString's link attribute, instead of
inferring link position from the text, to allow for custom styling
(where consumers of the library annotate where the links should be
themselves, via the AttributedString).

This property is useful when generating link previews, and prevents us
needing to create a new property in Message itself.
A spacer was used to push the time view to the right of the bubble.
However, this spacer also pushed the edge of the bubble to the trailing
edge of the screen, even if this extra space was not needed to fit the
message text in the bubble.

This did not cause a noticeable problem, since vstack was only used
when the text was long enough to spill onto multiple lines, meaning the
spacer would only cause a difference of a few pixels.

However, now we are planning to add link previews (and we want a vstack
configuration in this case even if the link is only a few characters
long), we want to constrain the width of a vstack bubble so that it is
only wide enough to hold the text.

Otherwise, a single-character message containing a link would take up
the full width of the screen, making it look out of place.
@f3dm76 f3dm76 merged commit 738bca8 into exyte:main Mar 28, 2025
@f3dm76
Copy link
Collaborator

f3dm76 commented Mar 28, 2025

Hey @matthewrfennell, thank you very much for this addition to the lib, have a spectacular day!

@f3dm76
Copy link
Collaborator

f3dm76 commented Mar 28, 2025

@matthewrfennell Hm, it looks like you didn't include LinkPillView file into the project, could you please add it?

EDIT: ah, sorry, just had to re-add the lib to account for the new file, spm is a tad strange on this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: support for links (and maybe links previews)
2 participants