Quoted tweets are now loaded recursively and displayed 2 layers deep

This commit is contained in:
Jocelyn Badgley (Twipped) 2020-03-02 20:27:00 -08:00
parent 5a87f64e9d
commit a7e266a558
4 changed files with 66 additions and 39 deletions

View File

@ -12,15 +12,7 @@ const schema = {
protected: true, protected: true,
}, },
html: true, html: true,
quoted_status: { quoted_status_id_str: true,
user: {
screen_name: true,
avatar: true,
name_html: true,
verified: true,
protected: true,
},
},
entities: { media: [ { entities: { media: [ {
type: true, type: true,
media_url_https: true, media_url_https: true,
@ -51,13 +43,9 @@ var entityProcessors = {
}, },
urls (urls, tweet) { urls (urls, tweet) {
urls.forEach((urlObj) => { urls.forEach(({ url, expanded_url, display_url }) => {
var quotedTweetHtml = ''; const className = (tweet.quoted_status_permalink && url === tweet.quoted_status_permalink.url) ? 'quoted-tweet' : 'url';
var indices = urlObj.indices; tweet.html = tweet.html.replace(url, `<a href="${expanded_url}" class="${className}">${display_url}</a>`);
var urlToReplace = (tweet.full_text || tweet.text).substring(indices[0], indices[1]);
var finalText = quotedTweetHtml || urlObj.display_url.link(urlObj.expanded_url);
tweet.html = tweet.html.replace(urlToReplace, finalText);
}); });
}, },

View File

@ -28,17 +28,23 @@ module.exports = exports = async function tweets (pages) {
/* Load Missing Tweets **************************************************/ /* Load Missing Tweets **************************************************/
if (tweetsNeeded.length) { while (tweetsNeeded.length) {
log('Fetching tweets: ' + tweetsNeeded.join(', ')); log('Fetching tweets: ' + tweetsNeeded.join(', '));
const arriving = await Promise.all(chunk(tweetsNeeded, 99).map(twitter)); const arriving = await Promise.all(chunk(tweetsNeeded, 99).map(twitter));
const tweetsRequested = tweetsNeeded;
tweetsNeeded = [];
const loaded = []; const loaded = [];
for (const tweet of arriving.flat(1)) { for (const tweet of arriving.flat(1)) {
if (!twitterBackup[tweet.id_str]) twitterBackup[tweet.id_str] = tweet; if (tweet.quoted_status_id_str && !twitterCache[tweet.quoted_status_id_str]) {
tweetsNeeded.push(tweet.quoted_status_id_str);
}
// if (!twitterBackup[tweet.id_str]) twitterBackup[tweet.id_str] = tweet;
twitterBackup[tweet.id_str] = tweet;
twitterCache[tweet.id_str] = tweetparse(tweet); twitterCache[tweet.id_str] = tweetparse(tweet);
loaded.push(tweet.id_str); loaded.push(tweet.id_str);
} }
const absent = difference(tweetsNeeded, loaded); const absent = difference(tweetsRequested, loaded);
for (const id of absent) { for (const id of absent) {
if (twitterBackup[id]) { if (twitterBackup[id]) {
log('Pulled tweet from backup ' + id); log('Pulled tweet from backup ' + id);
@ -53,18 +59,24 @@ module.exports = exports = async function tweets (pages) {
const twitterMedia = []; const twitterMedia = [];
function attachTweet (dict, tweetid) {
const tweet = twitterCache[tweetid];
if (!tweet) {
log.error(`Tweet ${tweetid} is missing from the cache.`);
return;
}
dict[tweetid] = tweet;
twitterMedia.push( ...tweet.media );
if (tweet.quoted_status_id_str) attachTweet(dict, tweet.quoted_status_id_str);
}
// now loop through pages and substitute the tweet data for the ids // now loop through pages and substitute the tweet data for the ids
for (const page of pages) { for (const page of pages) {
if (!page.tweets || !page.tweets.length) continue; if (!page.tweets || !page.tweets.length) continue;
page.tweets = page.tweets.reduce((dict, tweetid) => { page.tweets = page.tweets.reduce((dict, tweetid) => {
const tweet = twitterCache[tweetid]; attachTweet(dict, tweetid);
if (!tweet) {
log.error(`Tweet ${tweetid} is missing from the cache.`);
return dict;
}
dict[tweetid] = tweet;
twitterMedia.push( ...tweet.media );
return dict; return dict;
}, {}); }, {});
} }

View File

@ -121,9 +121,10 @@
} }
} }
.tweet-text { .tweet-text, .tweet-quoted-text {
line-height: 1.5em; line-height: 1.5em;
p { margin-bottom: 0.5em; } p { margin-bottom: 0.5em; }
p:last-child { margin-bottom: 0; }
a { color: $textHover; } a { color: $textHover; }
} }
@ -132,13 +133,21 @@
border: 1px solid $borderColor; border: 1px solid $borderColor;
border-radius: .35em; border-radius: .35em;
padding: 10px; padding: 10px;
display: block; display: inline-block;
color: $textDark; color: $textDark;
font-size: 14px; font-size: 0.95em;
margin-top: 10px; margin-top: 0.4em;
margin-bottom: 0.75em;
background: rgba(#ddd, 0.1);
> a {
color: inherit;
display: block;
&:hover { &:hover {
border-color: $borderHover; color: $textHover;
text-decoration: none;
}
} }
span { span {
@ -148,6 +157,10 @@
span, strong { span, strong {
line-height: 1; line-height: 1;
} }
p:last-child {
margin-bottom: 0;
}
} }
.tweet-entities { .tweet-entities {
@ -157,6 +170,7 @@
overflow: hidden; overflow: hidden;
max-width: 600px; max-width: 600px;
max-height: 600px; max-height: 600px;
box-shadow: 0 0 3px rgba(#000, 0.3);
.tweet-entities-inner { .tweet-entities-inner {
padding-bottom: 56.25%; padding-bottom: 56.25%;

View File

@ -12,17 +12,30 @@
<span>@{{user.screen_name}}</span> <span>@{{user.screen_name}}</span>
<img src="/tweets/logo.svg" alt="Twitter Logo" class="tweet-logo"> <img src="/tweets/logo.svg" alt="Twitter Logo" class="tweet-logo">
</a> </a>
{{#if quoted_status_id_str}}{{#with (lookup ../../tweets quoted_status_id_str)}}
<div class="tweet-quoted">
<a href="https://twitter.com/{{user.screen_name}}/status/{{id_str}}">
<strong>{{{user.name_html}}}</strong>
<span>@{{user.screen_name}}</span>
</a>
{{#if quoted_status_id_str}}{{#with (lookup ../../../tweets quoted_status_id_str)}}
<div class="tweet-quoted">
<a href="https://twitter.com/{{user.screen_name}}/status/{{id_str}}">
<strong>{{{user.name_html}}}</strong>
<span>@{{user.screen_name}}</span>
</a>
<div class="tweet-quoted-text">{{{html}}}</div>
</div>
{{/with}}{{/if}}
<div class="tweet-quoted-text">{{{html}}}</div>
</div>
{{/with}}{{/if}}
<div class="tweet-text">{{{html}}}</div> <div class="tweet-text">{{{html}}}</div>
{{#if quoted_status}}<a class="tweet-quoted" href="https://twitter.com/{{quoted_status.user.screen_name}}/status/{{quoted_status.id_str}}">
<strong>{{{quoted_status.user.name_html}}}</strong>
<span>@{{quoted_status.user.screen_name}}</span>
<div class="tweet-quoted-text">{{{quoted_status.html}}}</div>
</a>{{/if}}
{{#any extended_entities.media entities.media}} {{#any extended_entities.media entities.media}}
<div class="tweet-entities"><div class="tweet-entities-inner"><div><div class="tweet-entities-grid count{{this.length}}"> <div class="tweet-entities lightbox"><div class="tweet-entities-inner"><div><div class="tweet-entities-grid count{{this.length}}">
{{#each this}} {{#each this}}
<div class="tweet-entity"> <div class="tweet-entity">
{{#is type 'photo'}}<a class="tweet-photo" style="background-image: url({{media_url_https}}?name=medium);" href="{{media_url_https}}"></a>{{/is}} {{#is type 'photo'}}<a class="tweet-photo lb" style="background-image: url({{media_url_https}}?name=medium);" href="{{media_url_https}}"></a>{{/is}}
{{#is type 'video'}} {{#is type 'video'}}
<video controls poster="{{media_url}}" class="tweet-video"> <video controls poster="{{media_url}}" class="tweet-video">
{{#each video_info.variants}} {{#each video_info.variants}}