Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

feat: enable user profile embed on external site #9500

Conversation

babblebey
Copy link
Contributor

@babblebey babblebey commented Oct 15, 2023

This Pull Request adds a new feature to enable the embedding of a user profile on external sites. This enhancement was achieved by creating a dedicated embed endpoint. When accessed, this endpoint returns a user component converted to SVG, making it easy for external sites to integrate and display BioDrop user profile card seamlessly.

Fixes Issue

Resolves #9100

Changes proposed

  • Added api/profiles/[username]/embed endpoint which integrates satori to convert a user profile component to svg which it returns.
  • Configured a conditional redirect in /[username] page/route's SSR for redirecting requests coming from a certain url structure i.e. /[username]?embed to the /api/profiles/[username]/embed endpoint for the purpose of ease in readability.
  • Added the Profile component in the component/embed/external directory, which was in turn used in the api/profiles/[username]/embed endpoint
  • Added a section for copying the ready-to-paste embed code on the user profile page share modal, in Markdown and HTML formats.

Check List (Check all the applicable boxes)

  • My code follows the code style of this project.
  • My change requires changes to the documentation.
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • This PR does not contain plagiarized content.
  • The title of my pull request is a short description of the requested changes.

Screenshots

screencast-bpconcjcammlapcogcnnelfmaeghhagj-2023.11.13-11_19_08.webm

Note to reviewers

Thank YOU ❤️

@github-actions github-actions bot added issue linked Pull Request has issue linked dependencies Pull requests that update a dependency file labels Oct 15, 2023
@babblebey babblebey changed the title feat: add embed for BioDrop profile feat: add support for profile embed on external site Oct 15, 2023
@babblebey
Copy link
Contributor Author

Hey @eddiejaoude, Its the weekend and I was to work on this again! Here's what I've been able to get done 😃

  1. Created embed endpoint with boilerplate response

I implemented a embed endpoint with boilerplate function returned component as image response in the api/profiles/[username]/embed.js path.

I had tried an option to implement a embedUser function inside the api/profiles/[username]/index.js endpoint to hold this functionality and conditional run the embedUser function when the embed keyword is present in the req.query like /api/profiles/eddiejaoude?embed, but that wasn't working because the ImageResponse type of response is not supported by node, and we were only going to be able to use the ImageResponse on the edge runtime.

So, creating the separate endpoint where we can configure the environment to the runtime: "edge" now means we have an endpoint that can be accessed at /api/profiles/eddiejaoude/embed. I have also made available the means to collect and extra theme query which can be renamed to anything that can just suite the style desired for the embed and that will look like /api/profiles/eddiejaoude/embed?theme=dark-vertical

  1. Integrate the embed endpoint on the /[username].js (user profile) page via SSR

This was some decision I took in order to make the embed URL some way memorable, the embed profile component with the setup currently if we go like so in production... can only be reached through the endpoint url

https://biodrop.io/api/profiles/eddiejaoude/embed

...but with my integration on the /[username].js via SSR, we can have a more readable url like so in production...

https://biodrop.io/eddiejaoude?embed

I was able to achieve this by setting up a condition in the /[username].js's SSR's getServerSideProps which looks out for the presence of an embed string in the context's query object, and performs a redirect to the appropriate endpoint i.e. the embed endpoint with all appropriate queries required... See implementation below...

if ("embed" in context.query) {
  return {
    redirect: {
      destination: `/api/profiles/${username}/embed?theme=${context.query.theme || undefined}`,
      permanent: true,
    },
  }
}

Implementation (Screencast) Demo

screencast-bpconcjcammlapcogcnnelfmaeghhagj-2023.10.21-17_28_39.webm

I was hoping that this can be embeddable via markdown on take github for example like so..

![EddieJaoude - BioDrop](https://biodrop.io/eddiejaoude?embed&theme=dark)

😉

Next Step

Implement main embed components...

We can have various cards we can use, Tailwind components we paid for has many we can get started with (horizontal and vertical)...

https://tailwindui.com/components/marketing/sections/team-sections#component-4e063e98aa321fbeac0d9cae08a36a7c

With the technical part of getting the profile embedded via Markdown pretty much ready. I sort'o need access to getting the component as mentioned in the issue #9100 description though, If I am to really implement the variations as stated.

I hope to create these components inside the /components/embeds directory, I made sense to me a good location 😃

What do you say??

@eddiejaoude
Copy link
Member

Wow great explanation 🚀

I like the redirect idea, and I actually prefer the main content not on the profile page as that page has a lot of code in it already, so that worked out well

I hope to create these components inside the /components/embeds directory, I made sense to me a good location 😃

Yes I think that is a good location, but maybe move the YouTube component to /components/embeds/internal and then use /components/embeds/external so there is no confusion?

Note the final output should not be an image but SVG, so we can have clickable sections

@babblebey
Copy link
Contributor Author

Oh sure, SVG yes! I'll look to work with that.

So, you haven't given any response to getting me the Tailwind UI Components for the variation of embed components that I'll be implementing.

I await your response.

@eddiejaoude
Copy link
Member

Oh sure, SVG yes! I'll look to work with that.

Great 👍

So, you haven't given any response to getting me the Tailwind UI Components for the variation of embed components that I'll be implementing.

OMG I am so sorry, I missed that 🤦‍♂️ , here it is...

const people = [
  {
    name: 'Leonard Krasner',
    role: 'Senior Designer',
    imageUrl:
      'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80',
    twitterUrl: '#',
    linkedinUrl: '#',
  },
  // More people...
]

export default function Example() {
  return (
    <div className="bg-gray-900 py-24 sm:py-32">
      <div className="mx-auto max-w-7xl px-6 text-center lg:px-8">
        <div className="mx-auto max-w-2xl">
          <h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">Meet our team</h2>
          <p className="mt-4 text-lg leading-8 text-gray-400">
            We’re a dynamic group of individuals who are passionate about what we do.
          </p>
        </div>
        <ul
          role="list"
          className="mx-auto mt-20 grid max-w-2xl grid-cols-1 gap-6 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-3 lg:gap-8"
        >
          {people.map((person) => (
            <li key={person.name} className="rounded-2xl bg-gray-800 px-8 py-10">
              <img className="mx-auto h-48 w-48 rounded-full md:h-56 md:w-56" src={person.imageUrl} alt="" />
              <h3 className="mt-6 text-base font-semibold leading-7 tracking-tight text-white">{person.name}</h3>
              <p className="text-sm leading-6 text-gray-400">{person.role}</p>
              <ul role="list" className="mt-6 flex justify-center gap-x-6">
                <li>
                  <a href={person.twitterUrl} className="text-gray-400 hover:text-gray-300">
                    <span className="sr-only">Twitter</span>
                    <svg className="h-5 w-5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
                      <path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84" />
                    </svg>
                  </a>
                </li>
                <li>
                  <a href={person.linkedinUrl} className="text-gray-400 hover:text-gray-300">
                    <span className="sr-only">LinkedIn</span>
                    <svg className="h-5 w-5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
                      <path
                        fillRule="evenodd"
                        d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z"
                        clipRule="evenodd"
                      />
                    </svg>
                  </a>
                </li>
              </ul>
            </li>
          ))}
        </ul>
      </div>
    </div>
  )
}

@babblebey
Copy link
Contributor Author

Yo @eddiejaoude, I trust you're having great weekend 😃

Quick update, I have a demo below after forwarding a port to see what the result would be like during embed.

screencast-github.com-2023.10.28-20_24_07.webm

Don't mind the card's look, it got better.

image but SVG, so we can have clickable sections

I want to clarify that we cannot have a tags in the svg work anymore the moment its rendered on markdown, I guess it gets flattened out by GitHub.. So this means parts inside the svg cannot be independently clicked but we can surely create a link around the entire card to the user profile on biodrop.

@eddiejaoude
Copy link
Member

I want to clarify that we cannot have a tags in the svg work anymore the moment its rendered on markdown, I guess it gets flattened out by GitHub.. So this means parts inside the svg cannot be independently clicked but we can surely create a link around the entire card to the user profile on biodrop.

Looks great! If the card can only have a single link, we might change the data it displays, but we can do that in a future PR, it will be better to get a basic card out first

@babblebey
Copy link
Contributor Author

babblebey commented Oct 29, 2023

Looks great! If the card can only have a single link, we might change the data it displays, but we can do that in a future PR, it will be better to get a basic card out first

Haha, the card Looks even better now... I got the BioDrop logo display on the README as muse for the current looks... Now I've got certain blocker....I'm trying to fix it though 😉

screencast-github.com-2023.10.29-11_47_42.webm

I'm using only satori, there was no longer need for the @vercel/og.... Satori converts the component to svg and the response we return as image/svg type yea.

Now, satori naturally isn't friend with emoji texts so it doesn't convert them correctly hence the description in the card "Open-source advocate 🥑 community manager 🚀 coder 💻" missing the emoji getting replace with the block looking stuff.

In order to fix this... satori suggests using the graphemeImages at https://github.com/vercel/satori#emojis, which sort of stands in as a find and replace, for finding an emoji within the converted component and replacing it with an appropriate svg found within the graphemeImages object for correct conversion.

Now, all I need is an object with emoji-svg key value pair for all possible emoji to address that.

{
  '🤯': 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/1f92f.svg',
  '🥑': 'https://appropriateurl.svg',
  '🚀': 'https://appropriateurl.svg',
  '💻': 'https://appropriateurl.svg',
  ...allOtherEmojis
}

@babblebey
Copy link
Contributor Author

Another Blocker 😢

After bringing in the user data via implementing the getUserApi function within the embed endpoint, I couldn't get the Image part of the to render anymore.

I see that the image url is generated via the GitHub's dynamic avatar url, i.e. https://github.com/${username}.png?size=460.

Now this I think is the issue, as its not allowing any image render within the card... on the other hand, image url that are more prepared or static like so...

...works just fine!

Screencast

screencast-bpconcjcammlapcogcnnelfmaeghhagj-2023.10.29-20_31_32.webm

Quick Caveat

Its important to state that GitHub sort'o mis-behaves on my machine/network (not showing media sometimes), So maybe that's the reason why my the github avatar url isn't working. But I have committed my changes and maybe you can stage a local test.

@eddiejaoude
Copy link
Member

Its important to state that GitHub sort'o mis-behaves on my machine/network (not showing media sometimes), So maybe that's the reason why my the github avatar url isn't working. But I have committed my changes and maybe you can stage a local test

This can happen with GitHub's rate limit. I can test tomorrow

@eddiejaoude
Copy link
Member

It works well for me 👍 you must have hit the GitHub rate limit

Screenshot 2023-10-30 at 14 29 45

@github-actions github-actions bot added the 📖 docs Documentation pages label Nov 5, 2023
@babblebey
Copy link
Contributor Author

babblebey commented Nov 5, 2023

It works well for me 👍 you must have hit the GitHub rate limit

Hey @eddiejaoude, I don't seem to be able to get the images working consistently on my end, could you please give this a thorough testing trying different usernames including mine babblebey!?

Well, I have moved further to adding the embed url to the user profile on the Share Modal where I have avail the link in 3 different format. See below..

image

<!--- Markdown --->
[![babblebey | BioDrop](http://localhost:3000/babblebey?embed)](http://localhost:3000/babblebey)

<!--- HTML + Markdown --->
[<img src="http://localhost:3000/babblebey?embed" />](http://localhost:3000/babblebey)

<!--- HTML --->
<a href="http://localhost:3000/babblebey"><img src="http://localhost:3000/babblebey?embed" alt="babblebey | BioDrop" /></a>

Wondering whether we can have 2 at least as options!?

It is also important to state that...

  • This embed URL is available to see on other users share profile modal, i.e. it can be seen or copied for use by anybody. Should this stay this way?
  • For everytime the embed URL is requested to embed a profile, the profile stat is incremented. Do we want to keep this? If might be useful if we can make it work in such a way we are also tracking times when the embed is viewed on the place where its embedded... We can also make a thing where we are tracking traffic to the link page as something that comes from the embed also.

@eddiejaoude
Copy link
Member

Looks great 👍

Wondering whether we can have 2 at least as options!?

I think 2 options of html and markdown is enough, sometimes too many can cause confusion

This embed URL is available to see on other users share profile modal, i.e. it can be seen or copied for use by anybody. Should this stay this way?

I don't see this an a problem, people can always guess the url anyway

For everytime the embed URL is requested to embed a profile, the profile stat is incremented. Do we want to keep this? If might be useful if we can make it work in such a way we are also tracking times when the embed is viewed on the place where its embedded... We can also make a thing where we are tracking traffic to the link page as something that comes from the embed also.

Good spot, for this is fine, but yes maybe in the future we can separate the stats

@a0m0rajab
Copy link

a0m0rajab commented Nov 12, 2023

Hi, @babblebey 👋 . I am just following up about yesterday 🔥 . I think what @eddiejaoude already suggested about merging this PR would be great for these reasons:

  • Review offload: Small iterations make reviewing and understanding easier.
  • **Separate of concerns: ** Small PRs make it easier to reference in the future, like the Open sauced emoji PR.
  • Going Agile: Agile usually does not require you to finish all work at once, yet have a prototype and develop it over time.
  • Faster feedback cycle: If this went to production, there would be more people to see the work, and it would benefit to detect extra edge cases that we were unaware of.

If you would like, I would suggest doing the following:

  • merge this PR,
  • open an issue with the edge cases that you have in mind (markdown as an example)
  • and work on them

This approach would streamline the review process and subsequent iterations. 🚀 Let me know your thoughts!

@babblebey
Copy link
Contributor Author

Yea thanks @a0m0rajab

Exactly what I had in mind after our emoji issues fix 😉, I will leave the Markdown till a follow-up PR yea.

But, I will adjust the bits that finalises this PR and commit them for the merge.

@babblebey babblebey marked this pull request as ready for review November 13, 2023 11:12
@babblebey babblebey changed the title feat: add support for profile embed on external site feat: enable user profile embed on external site Nov 13, 2023
Copy link

@a0m0rajab a0m0rajab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, it's great to see that we have the same ideas.

I just left few comments about the code which could improve the readability and suggest a new feature.

services/utils/twemoji.js Show resolved Hide resolved
pages/api/profiles/[username]/embed.js Outdated Show resolved Hide resolved
components/user/UserProfile.js Outdated Show resolved Hide resolved
@eddiejaoude
Copy link
Member

Great work and love the collaboration 💪 It seems there are ongoing discussions though. Once resolved we can deploy to preview environment and test 🎉

@babblebey
Copy link
Contributor Author

Yo @eddiejaoude, discussions resolved 🚀

@eddiejaoude
Copy link
Member

Great, thank you 👍

Copy link

@a0m0rajab a0m0rajab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks you @babblebey for doing the suggestions! I appreciate that.

@eddiejaoude
Copy link
Member

I am going to merge to a temporary branch to test on Preview environment

@eddiejaoude eddiejaoude changed the base branch from main to feat-babblebey-embed-profile November 29, 2023 07:30
@eddiejaoude eddiejaoude merged commit b098d3c into EddieHubCommunity:feat-babblebey-embed-profile Nov 29, 2023
13 checks passed
@eddiejaoude
Copy link
Member

Great collaboration! PR to test to Preview #9851

Note there are some errors when deploying

  • middleware (I am looking into this)
  • font file not found - are these fonts needed, can we use a default available font?

import Profile from "@components/embeds/external/Profile";
import { loadEmoji, getIconCode } from "@services/utils/twemoji";

const inter = await fs.readFile(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../../public", "Inter-Regular.ttf"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this, but I would take a peek next week or the week after (if ola has not resolved it at that time)

Copy link
Contributor Author

@babblebey babblebey Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will sure give this a try, it's requires a font file in the buffer... So I want to assume that's exactly what gets returned from invoking the font's function.

like so...

import { Inter } from 'next/font/google'

// this should maybe return the .TTF 
const inter = Inter()

Copy link
Member

@eddiejaoude eddiejaoude Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a quick try and couldn't get it to work, it wasn't a buffer.

But with using the font file in public, there is an error that file is not found #9851 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
dependencies Pull requests that update a dependency file 📖 docs Documentation pages issue linked Pull Request has issue linked
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Embed BioDrop Profile on external site
3 participants