Skip to content

Commit

Permalink
unfurling contact link works
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-sirotin committed Sep 26, 2023
1 parent 185b8c3 commit cfdfac5
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 83 deletions.
30 changes: 19 additions & 11 deletions protocol/common/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ func (preview *LinkPreviewThumbnail) ConvertToProto() (*protobuf.UnfurledLinkThu
}, nil
}

func (m *Message) ConvertStatusLinkPreviewsToProto() ([]*protobuf.UnfurledStatusLink, error) {
func (m *Message) ConvertStatusLinkPreviewsToProto() (*protobuf.UnfurledStatusLinks, error) {
if len(m.StatusLinkPreviews) == 0 {
return nil, nil
}
Expand Down Expand Up @@ -1023,21 +1023,26 @@ func (m *Message) ConvertStatusLinkPreviewsToProto() ([]*protobuf.UnfurledStatus
unfurledLinks = append(unfurledLinks, ul)
}

return unfurledLinks, nil
return &protobuf.UnfurledStatusLinks{UnfurledStatusLinks: unfurledLinks}, nil
}

func (m *Message) ConvertFromProtoToStatusLinkPreviews(makeMediaServerURL func(msgID string, previewURL string) string) []StatusLinkPreview {
var links []*protobuf.UnfurledStatusLink
func (m *Message) ConvertFromProtoToStatusLinkPreviews(makeMediaServerURL func(msgID string, previewURL string, imageId string) string) []StatusLinkPreview {

if m.GetUnfurledStatusLinks() == nil {
return nil
}

links := m.UnfurledStatusLinks.GetUnfurledStatusLinks()

if links = m.GetUnfurledStatusLinks(); links == nil {
if links == nil {
return nil
}

createThumbnail := func(thumbnail *protobuf.UnfurledLinkThumbnail, URL string) LinkPreviewThumbnail {
createThumbnail := func(thumbnail *protobuf.UnfurledLinkThumbnail, URL string, imageId string) LinkPreviewThumbnail {
return LinkPreviewThumbnail{
Width: int(thumbnail.Width),
Height: int(thumbnail.Height),
URL: makeMediaServerURL(m.ID, URL),
URL: makeMediaServerURL(m.ID, URL, imageId),
}
}

Expand All @@ -1054,7 +1059,7 @@ func (m *Message) ConvertFromProtoToStatusLinkPreviews(makeMediaServerURL func(m
Description: c.Description,
}
if icon := c.GetIcon(); icon != nil {
lp.Contact.Icon = createThumbnail(icon, link.Url)
lp.Contact.Icon = createThumbnail(icon, link.Url, "contact-icon")
}
}

Expand All @@ -1068,10 +1073,10 @@ func (m *Message) ConvertFromProtoToStatusLinkPreviews(makeMediaServerURL func(m
TagIndices: c.TagIndices,
}
if icon := c.GetIcon(); icon != nil {
lp.Community.Icon = createThumbnail(icon, link.Url)
lp.Community.Icon = createThumbnail(icon, link.Url, "community-icon")
}
if banner := c.GetBanner(); banner != nil {
lp.Community.Banner = createThumbnail(banner, link.Url)
lp.Community.Banner = createThumbnail(banner, link.Url, "community-banner")
}
}

Expand All @@ -1084,7 +1089,10 @@ func (m *Message) ConvertFromProtoToStatusLinkPreviews(makeMediaServerURL func(m
Color: c.Color,
}
if icon := c.GetCommunityIcon(); icon != nil {
lp.Channel.CommunityIcon = createThumbnail(icon, link.Url)
lp.Channel.CommunityIcon = createThumbnail(icon, link.Url, "channel-community-icon")
}
if banner := c.GetCommunityBanner(); banner != nil {
lp.Channel.CommunityBanner = createThumbnail(banner, link.Url, "channel-community-banner")
}
}

Expand Down
55 changes: 34 additions & 21 deletions protocol/common/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import (
const expectedJPEG = "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="
const expectedAAC = "data:audio/aac;base64,//FQgBw//NoATGF2YzUyLjcwLjAAQniptokphEFCg5qs1v9fn48+qz1rfWNhwvz+CqB5dipmq3T2PlT1Ld6sPj+19fUt1C3NKV0KowiqohZVCrdf19WMatvV3YbIvAuy/q2RafA8UiZPmZY7DdmHZtP9ri25kedWSiMKQRt79ttlod55LkuX7/f7/f7/f7/YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYHNqo8g5qs1v9fn48+qz1rfWNhwvz+CqAAAAAAAAAAAAAAAAAAAAAAABw//FQgCNf/CFXbUZfDKFRgsYlKDegtXJH9eLkT54uRM1ckDYDcXRzZGF6Kz5Yps5fTeLY6w7gclwly+0PJL3udY3PyekTFI65bdniF3OjvHeafzZfWTs0qRMSkdll1sbb4SNT5e8vX98ytot6jEZ0NhJi2pBVP/tKV2JMyo36n9uxR2tKR+FoLCsP4SVi49kmvaSCWm5bQD96OmVQA9Q40bqnOa7rT8j9N0TlK991XdcenGTLbyS6eUnN2U1ckf14uRPni5EzVyQAAAAAAAAAAx6Q1flBp+KH2LhgH2Xx+14QB2/jcizm6ngck4vB9DoH9/Vcb7E8Dy+D/1ii1pSPwsUUUXCSsXHsk17SBfKwn2uHr6QAAAAAAAHA//FQgBt//CF3VO1KFCFWcd/r04m+O0758/tXHUlvaqEK9lvhUZXEZMXKMV/LQ6B3/mOl/Mrfs6jpD2b7f+n4yt+tm2x5ZmnpD++dZo/V9VgblI3OW/s1b8qt0h1RBiIRIIYIYQIBeCM8yy7etkwt1JAajRSoZGwwNZ07TTFTyMR1mTUVVUTW97vaDaHU5DV1snBf0mN4fraa+rf/vpdZ8FxqatGjNxPh35UuVfpNqc48W4nZ6rOO/16cTfHad8+f2rjqS3tVAAAAAAAAAAAAAAAAAAAAAAAAAAAO//FQgBm//CEXVPU+GiFsPr7x6+N6v+m+q511I4SgtYVyoyWjcMWMxkaxxDGSx1qVcarjDESt8zLQehx/lkil/GrHBy/NfJcHek0XtfanZJLHNXO2rUnFklPAlQSBS4l0pIoXIfORcXx0UYj1nTsSe1/0wXDkkFCfxWHtqRayOmWm3oS6JGdnZdtjesjByefiS8dLW1tVVVC58ijoxN3gmGFYj07+YJ6eth9fePXxvV/031XOupHCUAAAAAAAAAAAAAAAAAAAAAAAAAAA4P/xUIAcf/whN1T9NsMOEK5rxxxxXnid+f0/Ia195vi6oGH1ZVr6kjqScdSF9lt3qXH+Lxf0fo/Oe53r99IUPzybv/YWGZ7Vgk31MGw+DMp05+3y9fPERUTHlt1c9sUyoqCaD5bdXVz2wkG0hnpDmFy8r0fr3VBn/C7Rmg+L0/45EWfdocGq3HQ1uRro0GJK+vsvo837NR82s01l/n97rsWn7RYNBM3WRcDY3cJKosqMJhgdHtj9yflthd65rxxxxXnid+f0/Ia195vi6oAAAAAAAAAAAAAAAAAAAAAAAAAAAABw"

func linkPreviewUrlMaker(msgID string, linkURL string) string {
return "https://localhost:6666/" + msgID + "-" + linkURL
}

func TestPrepareContentImage(t *testing.T) {
file, err := os.Open("../../_assets/tests/test.jpg")
require.NoError(t, err)
Expand Down Expand Up @@ -213,7 +209,11 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
},
}

previews := msg.ConvertFromProtoToLinkPreviews(linkPreviewUrlMaker)
urlMaker := func(msgID string, linkURL string) string {
return "https://localhost:6666/" + msgID + "-" + linkURL
}

previews := msg.ConvertFromProtoToLinkPreviews(urlMaker)
require.Len(t, previews, 1)
p := previews[0]
require.Equal(t, l.Type, p.Type)
Expand All @@ -230,7 +230,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
// Test when the URL is not parseable by url.Parse.
l.Url = "postgres://user:abc{[email protected]:5432/db?sslmode=require"
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
previews = msg.ConvertFromProtoToLinkPreviews(linkPreviewUrlMaker)
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
require.Len(t, previews, 1)
p = previews[0]
require.Equal(t, l.Url, p.Hostname)
Expand All @@ -242,7 +242,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
Url: "https://github.com",
}
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
previews = msg.ConvertFromProtoToLinkPreviews(linkPreviewUrlMaker)
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
require.Len(t, previews, 1)
p = previews[0]
require.Equal(t, 0, p.Thumbnail.Height)
Expand All @@ -256,16 +256,15 @@ func TestConvertFromProtoToStatusLinkPreviews(t *testing.T) {
PublicKey: "1",
DisplayName: "2",
Description: "3",

//Icon: "4",

//ThumbnailPayload: []byte(""),
//ThumbnailWidth: 100,
//ThumbnailHeight: 200,
Icon: &protobuf.UnfurledLinkThumbnail{
Width: 100,
Height: 200,
Payload: []byte(""),
},
}

l := &protobuf.UnfurledStatusLink{
Url: "https://status.app/",
Url: "https://status.app",
Payload: &protobuf.UnfurledStatusLink_Contact{
Contact: contact,
},
Expand All @@ -274,23 +273,37 @@ func TestConvertFromProtoToStatusLinkPreviews(t *testing.T) {
msg := Message{
ID: "42",
ChatMessage: &protobuf.ChatMessage{
UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{l},
UnfurledStatusLinks: &protobuf.UnfurledStatusLinks{
UnfurledStatusLinks: []*protobuf.UnfurledStatusLink{l},
},
},
}

previews := msg.ConvertFromProtoToStatusLinkPreviews(linkPreviewUrlMaker)
urlMaker := func(msgID string, linkURL string, imageID string) string {
return "https://localhost:6666/" + msgID + "-" + linkURL + "-" + imageID
}

previews := msg.ConvertFromProtoToStatusLinkPreviews(urlMaker)
require.Len(t, previews, 1)

p := previews[0]
require.Equal(t, l.Url, p.URL)

c := l.GetContact()
require.Nil(t, p.Community)
require.Nil(t, p.Channel)

c := p.Contact
require.NotNil(t, c)
require.Equal(t, "1", c.PublicKey)
require.Equal(t, contact.PublicKey, c.GetPublicKey())
require.Equal(t, contact.DisplayName, c.GetDisplayName())
require.Equal(t, contact.Description, c.GetDescription())
require.Nil(t, contact.Icon)
require.Equal(t, contact.PublicKey, c.PublicKey)
require.Equal(t, contact.DisplayName, c.DisplayName)
require.Equal(t, contact.Description, c.Description)
icon := c.Icon
require.NotNil(t, icon)
require.Equal(t, int(contact.Icon.Width), icon.Width)
require.Equal(t, int(contact.Icon.Height), icon.Height)
require.Equal(t, "", icon.DataURI)
require.Equal(t, "https://localhost:6666/42-https://status.app-contact-icon", icon.URL)
}

func assertMarshalAndUnmarshalJSON[T any](t *testing.T, obj *T, msgAndArgs ...any) {
Expand Down
65 changes: 51 additions & 14 deletions protocol/linkpreview_unfurler_status.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package protocol

import (
"fmt"
"github.com/status-im/status-go/api/multiformat"
"go.uber.org/zap"

"github.com/status-im/status-go/images"
Expand All @@ -21,31 +23,59 @@ func NewStatusUnfurler(URL string, messenger *Messenger, logger *zap.Logger) *St
}
}

func (u *StatusUnfurler) createContactData(contactData *ContactURLData) *common.StatusContactLinkPreview {
func buildThumbnail(image *images.IdentityImage, thumbnail *common.LinkPreviewThumbnail) error {
if image.IsEmpty() {
return nil
}

var err error

thumbnail.Width, thumbnail.Height, err = images.GetImageDimensions(image.Payload)
if err != nil {
return fmt.Errorf("failed to get image dimensions: %w", err)
}

thumbnail.DataURI, err = image.GetDataURI()
if err != nil {
return fmt.Errorf("failed to get data uri: %w", err)
}

return nil
}

func (u *StatusUnfurler) buildContactData(contactData *ContactURLData) (*common.StatusContactLinkPreview, error) {
c := new(common.StatusContactLinkPreview)
c.PublicKey = contactData.PublicKey
c.DisplayName = contactData.DisplayName
c.Description = contactData.Description

var err error
contact := u.m.GetContactByID(contactData.PublicKey)
contactID, err := multiformat.DeserializeCompressedKey(contactData.PublicKey)

if err != nil {
return nil, err
}

contact := u.m.GetContactByID(contactID)

// TODO: Should we do this?
//if contact == nil {
// if contact, err = u.m.RequestContactInfoFromMailserver(contactData.PublicKey, true); err != nil {
// u.logger.Warn("StatusUnfurler: failed to request contact info from mailserver")
// return c, err
// }
//}

if contact == nil {
if contact, err = u.m.RequestContactInfoFromMailserver(contactData.PublicKey, true); err != nil {
u.logger.Warn("StatusUnfurler: failed to request contact info from mailserver")
return c
}
return c, nil
}

if thumbImage, ok := contact.Images[images.SmallDimName]; ok {
if imageBase64, err := thumbImage.GetDataURI(); err == nil {
c.Icon.Width = thumbImage.Width
c.Icon.Height = thumbImage.Height
c.Icon.DataURI = imageBase64
if image, ok := contact.Images[images.SmallDimName]; ok {
if err = buildThumbnail(&image, &c.Icon); err != nil {
u.logger.Warn("unfurling status link: failed to set thumbnail", zap.Error(err))
}
}

return c
return c, nil
}

func (u *StatusUnfurler) Unfurl() (common.StatusLinkPreview, error) {
Expand All @@ -59,20 +89,27 @@ func (u *StatusUnfurler) Unfurl() (common.StatusLinkPreview, error) {
}

if resp.Contact != nil {
preview.Contact = u.createContactData(resp.Contact)
preview.Contact, err = u.buildContactData(resp.Contact)
}

if resp.Community != nil {
// TODO: move to a separate func, finish
preview.Community = new(common.StatusCommunityLinkPreview)
preview.Community.DisplayName = resp.Community.DisplayName
preview.Community.Description = resp.Community.Description
}

if resp.Channel != nil {
// TODO: move to a separate func, finish
preview.Channel = new(common.StatusCommunityChannelLinkPreview)
preview.Channel.DisplayName = resp.Channel.DisplayName
preview.Channel.Description = resp.Channel.Description
}

u.logger.Info("<<< StatusUnfurler::Unfurl",
zap.Any("contact", preview.Contact),
zap.Any("community", preview.Community),
zap.Any("channel", preview.Channel))

return preview, nil
}
9 changes: 7 additions & 2 deletions protocol/message_persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
"sort"
"strings"

Expand Down Expand Up @@ -419,10 +420,13 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
}

if serializedUnfurledStatusLinks != nil {
err = json.Unmarshal(serializedUnfurledStatusLinks, &message.UnfurledStatusLinks)
// use proto.Marshal, because json.Marshal doesn't support `oneof` fields
var links protobuf.UnfurledStatusLinks
err = proto.Unmarshal(serializedUnfurledStatusLinks, &links)
if err != nil {
return err
}
message.UnfurledStatusLinks = &links
}

if attachment.Id != "" {
Expand Down Expand Up @@ -515,7 +519,8 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *common.Message)

var serializedUnfurledStatusLinks []byte
if links := message.GetUnfurledStatusLinks(); links != nil {
serializedUnfurledStatusLinks, err = json.Marshal(links)
// use proto.Marshal, because json.Marshal doesn't support `oneof` fields
serializedUnfurledStatusLinks, err = proto.Marshal(links)
if err != nil {
return nil, err
}
Expand Down
2 changes: 2 additions & 0 deletions protocol/messenger_linkpreview.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,7 @@ func (m *Messenger) UnfurlURLs(httpClient *http.Client, urls []string) (UnfurlUR
r.LinkPreviews = append(r.LinkPreviews, p)
}

m.logger.Info("<<< UnfurlURLs", zap.Any("response", r))

return r, nil
}
Loading

0 comments on commit cfdfac5

Please sign in to comment.