forked from nornagon/twitter-bookmark-archiver
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
123 lines (123 loc) · 3.99 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="all_bookmarks.json"></script>
<style>
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
#tweets {
width: 600px;
margin: 0 auto;
}
.tweet {
display: flex;
flex-direction: row;
max-width: 100%;
padding-bottom: 16px;
border-bottom: 1px solid lightgray;
margin-bottom: 16px;
}
.tweet .content {
margin-left: 8px;
}
.tweet .content .text {
white-space: pre-wrap;
}
.tweet .header .name {
font-weight: bold;
}
.tweet .header .username::before {
content: '@';
}
.media:has(*) {
margin-top: 8px;
}
.media > img, .media > video {
max-width: 100%;
}
</style>
</head>
<body>
<template id="tweet">
<div class="tweet">
<div class="profile-pic">
<img>
</div>
<div class="content">
<div class="header">
<span class="name"></span>
<span class="username"></span>
<span class="date"></span>
</div>
<div class="text"></div>
<div class="media"></div>
</div>
</div>
</template>
<div id="tweets"></div>
<script>
const tweetTemplate = document.getElementById('tweet')
const fixURL = (url) => {
const u = new URL(url)
return new URL('./' + u.host + u.pathname, location.href)
}
function renderTweetText(el, tweet) {
const urls = tweet.entities?.urls ?? []
urls.sort((a, b) => a.start - b.start)
const text = tweet.text
const codepoints = Array.from(text)
let i = 0
for (const u of urls) {
if (u.start > i) {
el.appendChild(document.createTextNode(codepoints.slice(i, u.start).join('')))
}
if (!u.media_key) {
const link = document.createElement('a')
link.setAttribute('href', u.expanded_url)
link.textContent = u.display_url
el.appendChild(link)
}
i = u.end
}
if (i < text.length) {
el.appendChild(document.createTextNode(codepoints.slice(i).join('')))
}
}
for (const tweet of TWEETS) {
const div = tweetTemplate.content.cloneNode(true)
const url = fixURL(tweet.author.profile_image_url)
div.querySelector('.profile-pic img').src = url.toString()
div.querySelector('.header .name').textContent = tweet.author.name
div.querySelector('.header .username').textContent = tweet.author.username
renderTweetText(div.querySelector('.text'), tweet.tweet)
for (const media_key of tweet.tweet.attachments?.media_keys ?? []) {
const media = tweet.medias.find(m => m.media_key === media_key)
let mediaEl
if (media.type === 'photo') {
const img = new Image
img.src = fixURL(media.url)
img.setAttribute('alt', media.alt_text)
mediaEl = img
}
if (media.type === 'video' || media.type === 'animated_gif') {
const v = document.createElement('video')
//v.setAttribute('playsinline', '')
v.setAttribute('preload', 'none')
v.setAttribute('controls', '')
v.setAttribute('disablepictureinpicture', '')
v.setAttribute('poster', media.preview_image_url)
const mp4s = media.variants.filter(t => t.content_type === 'video/mp4')
mp4s.sort((a, b) => b.bit_rate - a.bit_rate)
const highestQuality = mp4s[0]
v.setAttribute('src', fixURL(highestQuality.url))
mediaEl = v
}
if (mediaEl) div.querySelector('.media').appendChild(mediaEl)
}
tweets.appendChild(div)
}
</script>
</body>
</html>