Skip to content

Commit

Permalink
Fix #17, add a Segmenter class
Browse files Browse the repository at this point in the history
  • Loading branch information
tiffany352 committed Oct 9, 2019
1 parent f74a7ec commit 01bab2e
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 79 deletions.
58 changes: 58 additions & 0 deletions src/Segmenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export default class Segmenter {
constructor(length, defaultValue = null) {
this.length = length
this.array = [
{ start: 0, end: length - 1, value: defaultValue }
]
}

setSpan(start, end, value) {
if (end < start) {
return
}
const newArray = []

// edge cases:
// old: AAAAAABBBBB
// new: CCCCCC
// old: AAAAAAAAAAA
// new: CCC
// old: AAAAAAAAAAA
// new: CCCC
// old: AAAAAAAAAAA
// new: CCCCAAAAAAA

const insert = (entry) => {
if (entry.end >= entry.start) {
newArray.push(entry)
}
}

for (const entry of this.array) {
if (entry.start < start) {
insert({
...entry,
end: Math.min(entry.end, start - 1),
})
}
else {
break
}
}

newArray.push({
start, end, value
})

for (const entry of this.array) {
if (entry.end > start) {
insert({
...entry,
start: Math.max(entry.start, end + 1),
})
}
}

this.array = newArray
}
}
129 changes: 50 additions & 79 deletions src/components/Tweet.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react'
import { useSelector } from 'react-redux'
import ExternalLink from './ExternalLink'
import './Tweet.css'
import Segmenter from '../Segmenter'

function Media(props) {
const [ mediaUrl, setMediaUrl ] = useState(null)
Expand Down Expand Up @@ -97,89 +98,58 @@ export default function Tweet(props) {
authorDisplayName = data.entities.user_mentions[0].name
}

const spans = [
{
start: 0,
style: 'normal'
}
]

const setSpan = (start, end, style, data = {}) => {
let startIndex;
let endIndex = 1;
for (let i = 0; i < spans.length; i++) {
const span = spans[i]
if (!startIndex && span.start >= start) {
startIndex = i
}
if (span.start < end) {
endIndex = i
}
}

if (!startIndex) {
spans.push({
start, style,
...data
})
spans.push({
start: end,
style: 'normal'
})
}
else {
spans.splice(startIndex, endIndex - startIndex, { start, style, ...data })
if (spans.length > startIndex) {
spans[startIndex + 1].start = end
}
}
}

for (let i = 0; i < data.entities.user_mentions.length; i++) {
const mention = data.entities.user_mentions[i]
const start = parseInt(mention.indices[0])
const end = parseInt(mention.indices[1])
const url = "https://twitter.com/i/user/" + mention.id_str
setSpan(start, end, 'link', {
display: '@' + mention.screen_name,
const segmenter = new Segmenter(data.full_text.length, {
style: 'normal',
})

for (const entity of data.entities.user_mentions) {
const start = parseInt(entity.indices[0])
const end = parseInt(entity.indices[1]) - 1
const url = "https://twitter.com/i/user/" + entity.id_str
segmenter.setSpan(start, end, {
style: 'link',
display: '@' + entity.screen_name,
href: url
})
}

// Do after mentions parsing
if (isRetweet) {
setSpan(0, retweetMatches[0].length, 'hide')
segmenter.setSpan(0, retweetMatches[0].length, {
style: 'hide',
})
}

if (data.entities.media) {
for (let i = 0; i < data.entities.media.length; i++) {
const media = data.entities.media[i]
const start = parseInt(media.indices[0])
const end = parseInt(media.indices[1])
setSpan(start, end, 'hide')
for (const entity of data.entities.media) {
const start = parseInt(entity.indices[0])
const end = parseInt(entity.indices[1]) - 1
segmenter.setSpan(start, end, {
style: 'hide',
})
}
}

if (data.entities.urls) {
for (let i = 0; i < data.entities.urls.length; i++) {
const url = data.entities.urls[i]
const start = parseInt(url.indices[0])
const end = parseInt(url.indices[1])
setSpan(start, end, 'link', {
display: url.display_url,
href: url.expanded_url,
for (const entity of data.entities.urls) {
const start = parseInt(entity.indices[0])
const end = parseInt(entity.indices[1]) - 1
segmenter.setSpan(start, end, {
style: 'link',
display: entity.display_url,
href: entity.expanded_url,
})
}
}

if (data.entities.hashtags) {
for (let i = 0; i < data.entities.hashtags.length; i++) {
const hashtag = data.entities.hashtags[i]
const start = parseInt(hashtag.indices[0])
const end = parseInt(hashtag.indices[1])
setSpan(start, end, 'link', {
display: '#' + hashtag.text,
href: 'https://twitter.com/hashtag/' + hashtag.text,
for (const entity of data.entities.hashtags) {
const start = parseInt(entity.indices[0])
const end = parseInt(entity.indices[1]) - 1
segmenter.setSpan(start, end, {
style: 'link',
display: '#' + entity.text,
href: 'https://twitter.com/hashtag/' + entity.text,
})
}
}
Expand All @@ -203,28 +173,29 @@ export default function Tweet(props) {
return text
}

const formattedText = []
for (let i = 0; i < spans.length; i++) {
const span = spans[i]
const next = spans[i + 1] || { start: parseInt(data.display_text_range[1]) }
const text = data.full_text.slice(span.start, next.start)
switch (span.style) {
const formattedText = segmenter.array.map((segment, index) => {
const text = data.full_text.slice(segment.start, segment.end + 1)
switch (segment.value.style) {
case 'normal':
formattedText.push(decodeHTMLEntities(text))
break
return (
<span>
{decodeHTMLEntities(text)}
</span>
)
case 'link':
formattedText.push(
<ExternalLink key={i} href={span.href}>{span.display}</ExternalLink>
return (
<ExternalLink key={index} href={segment.value.href}>{segment.value.display}</ExternalLink>
)
break
case 'hide':
default:
break
return null
}
}
})

const logContents = () => {
console.log('tweet', data)
console.log('tweet', {
data, segmenter
})
}

const authorProfileUrl = "https://twitter.com/i/user/" + authorId
Expand Down

0 comments on commit 01bab2e

Please sign in to comment.