Created
March 25, 2022 13:25
-
-
Save alextes/2318083aabf80849c0164b601ef33360 to your computer and use it in GitHub Desktop.
Turning a plain text Twitter bio into one containing urls, mentions, hashtags, and cashtags using TypeScript and React.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Types describing the various objects found in twitter's entities. | |
type LinkableUrl = { | |
display_url: string; | |
end: number; | |
expanded_url: string; | |
start: number; | |
}; | |
type LinkableMention = { | |
start: number; | |
end: number; | |
username: string; | |
}; | |
type LinkableCashtag = { start: number; end: number; tag: string }; | |
type LinkableHashtag = { start: number; end: number; tag: string }; | |
// This example only links entities found in the bio. That's `entities.description` in the V2 API. | |
type Linkables = { | |
cashtags?: LinkableCashtag[]; | |
hashtags?: LinkableHashtag[]; | |
mentions?: LinkableMention[]; | |
urls?: LinkableUrl[]; | |
}; | |
// These are the elements that together encode a rich bio. | |
type Text = { type: "text"; text: string[] }; | |
type Url = { type: "url"; linkable: LinkableUrl }; | |
type Hashtag = { type: "hashtag"; linkable: LinkableHashtag }; | |
type Cashtag = { type: "cashtag"; linkable: LinkableCashtag }; | |
type Mention = { type: "mention"; linkable: LinkableMention }; | |
type BioElement = Text | Url | Hashtag | Cashtag | Mention; | |
// Helps identify what kind of linkable we're working with, then sorts them into a list. | |
const getSortedLinkables = (linkables: Linkables) => { | |
const urlLinkables = | |
linkables.urls?.map((linkable) => ({ | |
...linkable, | |
type: "url" as const, | |
})) ?? []; | |
const mentionLinkables = | |
linkables.mentions?.map((linkable) => ({ | |
...linkable, | |
type: "mention" as const, | |
})) ?? []; | |
const hashtagLinkables = | |
linkables.hashtags?.map((linkable) => ({ | |
...linkable, | |
type: "hashtag" as const, | |
})) ?? []; | |
const cashtagLinkables = | |
linkables.cashtags?.map((linkable) => ({ | |
...linkable, | |
type: "cashtag" as const, | |
})) ?? []; | |
return [ | |
...urlLinkables, | |
...mentionLinkables, | |
...hashtagLinkables, | |
...cashtagLinkables, | |
].sort((l1, l2) => (l1.start < l2.start ? -1 : 1)); | |
}; | |
const buildBioElements = (bio: string, linkables: Linkables) => { | |
// Linkable indices appear to assume a list of code points not UTF code units which JS uses by default. | |
const bioCodePoints = Array.from(bio); | |
const sortedLinkables = getSortedLinkables(linkables); | |
const bioElements: BioElement[] = []; | |
let lastEndIndex = 0; | |
for (const linkable of sortedLinkables) { | |
const text = bioCodePoints.slice(lastEndIndex, linkable.start); | |
bioElements.push({ type: "text", text }); | |
bioElements.push({ type: linkable.type, linkable } as BioElement); | |
lastEndIndex = linkable.end; | |
} | |
if (lastEndIndex !== bioCodePoints.length - 1) { | |
bioElements.push({ | |
type: "text", | |
text: bioCodePoints.slice(lastEndIndex ?? 0), | |
}); | |
} | |
return bioElements; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was a pain to write, I didn't find any helpful code, just old libraries or gists using jQuery. Thought I'd share.
This snippet effectively produces a list of objects telling you exactly what you need to render. i.e. piece of text, now a link with this text but that href, now a mention etc. How you render each of those is completely up to you.