Thread viewer for Bluesky
1import { ATProtoRecord } from './records.js';
2import { PostDataError, parseViewRecord } from './posts.js';
3
4/**
5 * Base class for embed objects.
6 */
7
8export class Embed {
9
10 /** @type {json} */
11 json;
12
13 /**
14 * More hydrated view of an embed, taken from a full post view (#postView).
15 *
16 * @param {json} json, @returns {Embed}
17 */
18
19 static parseInlineEmbed(json) {
20 switch (json.$type) {
21 case 'app.bsky.embed.record#view':
22 return new InlineRecordEmbed(json);
23
24 case 'app.bsky.embed.recordWithMedia#view':
25 return new InlineRecordWithMediaEmbed(json);
26
27 case 'app.bsky.embed.images#view':
28 return new InlineImageEmbed(json);
29
30 case 'app.bsky.embed.external#view':
31 return new InlineLinkEmbed(json);
32
33 case 'app.bsky.embed.video#view':
34 return new InlineVideoEmbed(json);
35
36 default:
37 if (location.protocol == 'file:') {
38 throw new PostDataError(`Unexpected embed type: ${json.$type}`);
39 } else {
40 console.warn('Unexpected embed type:', json.$type);
41 return new Embed(json);
42 }
43 }
44 }
45
46 /**
47 * Raw embed extracted from raw record data of a post. Does not include quoted post contents.
48 *
49 * @param {json} json, @returns {Embed}
50 */
51
52 static parseRawEmbed(json) {
53 switch (json.$type) {
54 case 'app.bsky.embed.record':
55 return new RawRecordEmbed(json);
56
57 case 'app.bsky.embed.recordWithMedia':
58 return new RawRecordWithMediaEmbed(json);
59
60 case 'app.bsky.embed.images':
61 return new RawImageEmbed(json);
62
63 case 'app.bsky.embed.external':
64 return new RawLinkEmbed(json);
65
66 case 'app.bsky.embed.video':
67 return new RawVideoEmbed(json);
68
69 default:
70 if (location.protocol == 'file:') {
71 throw new PostDataError(`Unexpected embed type: ${json.$type}`);
72 } else {
73 console.warn('Unexpected embed type:', json.$type);
74 return new Embed(json);
75 }
76 }
77 }
78
79 /** @param {json} json */
80 constructor(json) {
81 this.json = json;
82 }
83
84 /** @returns {string} */
85 get type() {
86 return this.json.$type;
87 }
88}
89
90export class RawImageEmbed extends Embed {
91
92 /** @type {json[]} */
93 images;
94
95 /** @param {json} json */
96 constructor(json) {
97 super(json);
98 this.images = json.images;
99 }
100}
101
102export class RawLinkEmbed extends Embed {
103
104 /** @type {string | undefined} */
105 url;
106
107 /** @type {string | undefined} */
108 title;
109
110 /** @type {json | undefined} */
111 thumb;
112
113 /** @param {json} json */
114 constructor(json) {
115 super(json);
116
117 this.url = json.external.uri;
118 this.title = json.external.title;
119 this.thumb = json.external.thumb;
120 }
121}
122
123export class RawVideoEmbed extends Embed {
124
125 /** @type {json | undefined} */
126 video;
127
128 /** @param {json} json */
129 constructor(json) {
130 super(json);
131 this.video = json.video;
132 }
133}
134
135export class RawRecordEmbed extends Embed {
136
137 /** @type {ATProtoRecord} */
138 record;
139
140 /** @param {json} json */
141 constructor(json) {
142 super(json);
143 this.record = new ATProtoRecord(json.record);
144 }
145}
146
147export class RawRecordWithMediaEmbed extends Embed {
148
149 /** @type {ATProtoRecord} */
150 record;
151
152 /** @type {Embed} */
153 media;
154
155 /** @param {json} json */
156 constructor(json) {
157 super(json);
158 this.record = new ATProtoRecord(json.record.record);
159 this.media = Embed.parseRawEmbed(json.media);
160 }
161}
162
163export class InlineRecordEmbed extends Embed {
164
165 /** @type {ATProtoRecord} */
166 record;
167
168 /**
169 * app.bsky.embed.record#view
170 * @param {json} json
171 */
172 constructor(json) {
173 super(json);
174 this.record = parseViewRecord(json.record);
175 }
176}
177
178export class InlineRecordWithMediaEmbed extends Embed {
179
180 /** @type {ATProtoRecord} */
181 record;
182
183 /** @type {Embed} */
184 media;
185
186 /**
187 * app.bsky.embed.recordWithMedia#view
188 * @param {json} json
189 */
190 constructor(json) {
191 super(json);
192 this.record = parseViewRecord(json.record.record);
193 this.media = Embed.parseInlineEmbed(json.media);
194 }
195}
196
197export class InlineLinkEmbed extends Embed {
198
199 /** @type {string | undefined} */
200 url;
201
202 /** @type {string | undefined} */
203 title;
204
205 /** @type {string | undefined} */
206 description;
207
208 /** @type {json | undefined} */
209 thumb;
210
211 /**
212 * app.bsky.embed.external#view
213 * @param {json} json
214 */
215 constructor(json) {
216 super(json);
217
218 this.url = json.external.uri;
219 this.title = json.external.title;
220 this.description = json.external.description;
221 this.thumb = json.external.thumb;
222 }
223}
224
225export class InlineImageEmbed extends Embed {
226
227 /** @type {json[]} */
228 images;
229
230 /**
231 * app.bsky.embed.images#view
232 * @param {json} json
233 */
234 constructor(json) {
235 super(json);
236 this.images = json.images;
237 }
238}
239
240export class InlineVideoEmbed extends Embed {
241
242 /** @type {string | undefined} */
243 playlistURL;
244
245 /** @type {string | undefined} */
246 alt;
247
248 /**
249 * app.bsky.embed.video#view
250 * @param {json} json
251 */
252 constructor(json) {
253 super(json);
254 this.playlistURL = json.playlist;
255 this.alt = json.alt;
256 }
257}