kibun-embed.js
1/* INSTRUCTIONS:
2add this code snippet somewhere on the page:
3<div id="kibun" data-username="your.username"></div>
4optionally, add the attribute `data-styles="none"` if you just want to theme it yourself
5also optionally, add `data-kibun="hide"` if you don't want to put a link to kibun.social in the bottom corner
6*/
7
8function timeAgo (dateString) {
9 const date = Date.parse(dateString);
10 const curDate = new Date(date);
11 const now = Date.now();
12 const yest = new Date(Date.parse(dateString));
13 const today = new Date(date);
14 yest.setDate(today - 1);
15 const diff = (now - date) / 1000; // difference in seconds
16 if (diff < 5) {
17 return "just now";
18 } else if (diff < 60) {
19 return `${diff} seconds ago`;
20 } else if (diff < 60*60) {
21 const min = Math.floor(diff / 60);
22 return `${min} minute${min > 1 ? 's' : ''} ago`;
23 } else if (diff < 60*60*24) {
24 const hr = Math.floor(diff / (60*60));
25 return `${hr} hour${hr > 1 ? 's' : ''} ago`;
26 } else if (date.getDate() === yest.getDate() && date.getMonth() === yest.getMonth() && date.getYear() === yest.getYear()) {
27 return "yesterday";
28 }
29 return `${curDate.toLocaleDateString(undefined, {
30 weekday: 'short',
31 year: 'numeric',
32 month: 'short',
33 day: 'numeric'
34 }).toLowerCase()}`;
35}
36
37async function getLatest() {
38 const kibunBase = document.getElementById('kibun');
39 const username = kibunBase.getAttribute('data-username');
40 const userDidData = await fetch(`https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${username}`);
41 const userDidDoc = await userDidData.json();
42 const userDid = userDidDoc.did;
43 const userPds = userDidDoc.pds;
44 const userInfoReq = await fetch(`
45 ${userPds}/xrpc/com.atproto.repo.getRecord?repo=${userDid}&collection=app.bsky.actor.profile&rkey=self`);
46 const userInfoData = await userInfoReq.json();
47 const displayName = userInfoData.value.displayName;
48 console.log(userInfoData);
49 const statusData = await fetch(`${userPds}/xrpc/com.atproto.repo.listRecords?repo=${userDid}&collection=social.kibun.status&limit=1`);
50 const statuses = await statusData.json();
51 if (statuses.records.length === 0) return;
52 const status = statuses.records[0];
53 const container = document.createElement('div');
54 container.id = 'kibun-container';
55
56 const header = document.createElement('div');
57 header.id = 'kibun-header';
58
59 const userLink = document.createElement('a');
60 userLink.href = `https://www.kibun.social/users/${username}`;
61 userLink.textContent = displayName;
62 userLink.id = 'kibun-displayname';
63
64 console.log(status.value.createdAt);
65 const parsedDate = Date.parse(status.value.createdAt);
66 const postTime = document.createElement('span');
67 postTime.textContent = timeAgo(status.value.createdAt);
68 postTime.id = 'kibun-datetime';
69
70 const emoji = status.value.emoji;
71 const emojiSign = document.createElement('span')
72 emojiSign.textContent = emoji;
73 emojiSign.id = 'kibun-emoji';
74
75 const userHandle = document.createElement('a');
76 userHandle.href = `https://kibun.social/users/${username}`;
77 userHandle.textContent = '@'+username;
78 userHandle.id = 'kibun-handle';
79
80 header.append(userLink);
81 header.append(emojiSign);
82 header.append(userHandle);
83 header.append(postTime);
84 container.append(header);
85
86 const statusText = document.createElement('div');
87 statusText.textContent = status.value.text;
88 statusText.id = 'kibun-status';
89 container.append(statusText);
90
91 if (kibunBase.getAttribute('data-kibun') !== 'hide') {
92 const kibunLink = document.createElement('a');
93 kibunLink.id = 'kibun-link';
94 kibunLink.href = 'https://www.kibun.social/';
95 kibunLink.setAttribute('target', '_blank');
96 kibunLink.setAttribute('rel', 'external');
97 kibunLink.textContent = 'kibun.social';
98 container.append(kibunLink);
99 }
100
101 if (kibunBase.getAttribute('data-styles') !== 'none') {
102 const styles = document.createElement('style');
103 styles.setAttribute('type', 'text/css');
104 styles.innerHTML = `
105 #kibun-container {
106 border: 1px #7dd3fc solid;
107 box-shadow: 4px 4px 0 #7dd3fc;
108 padding: 20px;
109 max-width: 400px;
110 background-color: #FFFFFF;
111 font-family: 'Inter', 'San Francisco', 'Lucida Grande', Arial, sans-serif;
112 font-size: 14px;
113 position: relative;
114 }
115
116 #kibun-header {
117 display: flex;
118 gap: 10px;
119 align-items: center;
120 flex-wrap: wrap;
121 }
122
123 #kibun-displayname {
124 color: black;
125 font-weight: bold;
126 }
127
128 #kibun-handle {
129 color: #666666;
130 font-size: .8em;
131 }
132
133 #kibun-datetime {
134 color: #666666;
135 font-size: .8em;
136 }
137
138 #kibun-datetime:before {
139 content: "•";
140 margin-right: 10px;
141 }
142
143 #kibun-status {
144 margin-top: 10px;
145 }
146
147 #kibun-link {
148 position: absolute;
149 bottom: 5px;
150 right: 5px;
151 font-size: .6em;
152 color: #666666;
153 }
154 `;
155 document.body.append(styles);
156 }
157 document.body.append(container);
158}
159
160getLatest();