+11
-1
index.html
+11
-1
index.html
···
49
49
50
50
<li><a href="#" data-action="login">Log in</a></li>
51
51
<li><a href="#" data-action="logout">Log out</a></li>
52
+
53
+
<li class="link"><a href="?">Home</a></li>
54
+
<li class="link"><a href="?page=posting_stats">Posting stats</a></li>
55
+
<li class="link"><a href="?page=like_stats">Like stats</a></li>
56
+
<li class="link"><a href="?page=search">Timeline search</a></li>
57
+
<li class="link"><a href="?page=search&mode=likes">Archive search</a></li>
52
58
</ul>
53
59
</div>
54
60
···
164
170
</div>
165
171
166
172
<div id="private_search_page">
167
-
<h2>Archive search *Beta*</h2>
173
+
<h2>Archive search</h2>
168
174
169
175
<div class="timeline-search">
170
176
<form>
···
195
201
196
202
<div class="lycan-import">
197
203
<form>
204
+
<h4>Data not imported yet</h4>
205
+
198
206
<p>
199
207
In order to search within your likes and bookmarks, the posts you've liked or saved need to be imported into a database.
200
208
This is a one-time process, but it can take several minutes or more, depending on the age of your account.
···
209
217
</form>
210
218
211
219
<div class="import-progress">
220
+
<h4>Import in progress</h4>
221
+
212
222
<p class="import-status"></p>
213
223
<p><progress></progress> <output></output></p>
214
224
</div>
+22
-8
private_search_page.js
+22
-8
private_search_page.js
···
12
12
constructor() {
13
13
this.pageElement = $id('private_search_page');
14
14
15
+
this.header = $(this.pageElement.querySelector('h2'));
16
+
15
17
this.rangeInput = $(this.pageElement.querySelector('input[type="range"]'), HTMLInputElement);
16
18
this.submitButton = $(this.pageElement.querySelector('input[type="submit"]'), HTMLInputElement);
17
19
this.progressBar = $(this.pageElement.querySelector('input[type="submit"] + progress'), HTMLProgressElement);
···
40
42
41
43
let params = new URLSearchParams(location.search);
42
44
this.mode = params.get('mode');
43
-
this.lycanMode = params.get('lycan');
45
+
let lycan = params.get('lycan');
44
46
45
-
if (this.lycanMode == 'local') {
47
+
if (lycan == 'local') {
46
48
this.localLycan = new BlueskyAPI('http://localhost:3000', false);
49
+
} else if (lycan) {
50
+
this.lycanAddress = `did:web:${lycan}#lycan`;
51
+
} else {
52
+
this.lycanAddress = 'did:web:lycan.feeds.blue#lycan';
47
53
}
48
54
}
49
55
···
94
100
this.pageElement.style.display = 'block';
95
101
96
102
if (this.mode == 'likes') {
103
+
this.header.innerText = 'Archive search';
97
104
this.timelineSearch.style.display = 'none';
98
105
this.searchCollections.style.display = 'block';
99
106
this.searchLine.style.display = 'block';
100
107
this.lycanImportSection.style.display = 'none';
101
108
this.checkLycanImportStatus();
102
109
} else {
110
+
this.header.innerText = 'Timeline search';
103
111
this.timelineSearch.style.display = 'block';
104
112
this.searchCollections.style.display = 'none';
105
113
this.lycanImportSection.style.display = 'none';
···
132
140
return await this.localLycan.getRequest('blue.feeds.lycan.getImportStatus', { user: accountAPI.user.did });
133
141
} else {
134
142
return await accountAPI.getRequest('blue.feeds.lycan.getImportStatus', null, {
135
-
headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' }
143
+
headers: { 'atproto-proxy': this.lycanAddress }
136
144
});
137
145
}
138
146
}
···
153
161
this.lycanImportSection.style.display = 'block';
154
162
this.lycanImportForm.style.display = 'block';
155
163
this.importProgress.style.display = 'none';
164
+
this.searchField.disabled = true;
156
165
157
166
this.stopImportTimer();
158
167
} else if (info.status == 'in_progress' || info.status == 'scheduled' || info.status == 'requested') {
159
168
this.lycanImportSection.style.display = 'block';
160
169
this.lycanImportForm.style.display = 'none';
161
170
this.importProgress.style.display = 'block';
171
+
this.searchField.disabled = true;
162
172
163
173
this.showImportProgress(info);
164
174
this.startImportTimer();
165
175
} else if (info.status == 'finished') {
166
176
this.lycanImportForm.style.display = 'none';
167
177
this.importProgress.style.display = 'block';
178
+
this.searchField.disabled = false;
168
179
169
180
this.showImportProgress({ status: 'finished', progress: 1.0 });
170
181
this.stopImportTimer();
···
188
199
this.importStatusLabel.innerText = `Import complete โ`;
189
200
} else if (info.position) {
190
201
let date = new Date(info.position).toLocaleString(window.dateLocale, { day: 'numeric', month: 'short', year: 'numeric' });
191
-
this.importStatusLabel.innerText = `Imported data until: ${date}`;
202
+
this.importStatusLabel.innerText = `Downloaded data until: ${date}`;
192
203
} else if (info.status == 'requested') {
193
204
this.importStatusLabel.innerText = 'Requesting importโฆ';
194
205
} else {
···
202
213
this.lycanImportSection.style.display = 'block';
203
214
this.lycanImportForm.style.display = 'none';
204
215
this.importProgress.style.display = 'block';
216
+
this.searchField.disabled = true;
205
217
206
218
this.importStatusLabel.innerText = message;
207
219
this.stopImportTimer();
···
236
248
});
237
249
} else {
238
250
await accountAPI.postRequest('blue.feeds.lycan.startImport', null, {
239
-
headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' }
251
+
headers: { 'atproto-proxy': this.lycanAddress }
240
252
});
241
253
}
242
254
···
285
297
daysBack = 0;
286
298
}
287
299
288
-
this.timelinePosts = timeline.map(x => Post.parseFeedPost(x));
300
+
this.timelinePosts = timeline;
289
301
290
302
this.archiveStatus.innerText = "Timeline archive fetched: " + ((daysBack == 1) ? '1 day' : `${daysBack} days`);
291
303
this.searchLine.style.display = 'block';
···
304
316
return;
305
317
}
306
318
307
-
let matching = this.timelinePosts.filter(x => x.lowercaseText.includes(query));
319
+
let matching = this.timelinePosts
320
+
.filter(x => x.post.record.text.toLowerCase().includes(query))
321
+
.map(x => Post.parseFeedPost(x));
308
322
309
323
for (let post of matching) {
310
324
let postView = new PostComponent(post, 'feed').buildElement();
···
348
362
if (cursor) params.cursor = cursor;
349
363
350
364
response = await accountAPI.getRequest('blue.feeds.lycan.searchPosts', params, {
351
-
headers: { 'atproto-proxy': 'did:web:lycan.feeds.blue#lycan' }
365
+
headers: { 'atproto-proxy': this.lycanAddress }
352
366
});
353
367
}
354
368
+24
-4
style.css
+24
-4
style.css
···
127
127
padding: 6px 11px;
128
128
}
129
129
130
-
#account_menu li a {
130
+
#account_menu li a[data-action] {
131
131
display: inline-block;
132
132
color: #333;
133
133
font-size: 11pt;
···
138
138
background-color: hsla(210, 100%, 4%, 0.12);
139
139
}
140
140
141
-
#account_menu li a:hover {
141
+
#account_menu li a[data-action]:hover {
142
142
background-color: hsla(210, 100%, 4%, 0.2);
143
143
text-decoration: none;
144
+
}
145
+
146
+
#account_menu li:not(.link) + li.link {
147
+
margin-top: 16px;
148
+
padding-top: 10px;
149
+
border-top: 1px solid #ccc;
150
+
}
151
+
152
+
#account_menu li.link {
153
+
margin-top: 8px;
154
+
margin-left: 2px;
155
+
}
156
+
157
+
#account_menu li.link a {
158
+
font-size: 11pt;
159
+
color: #333;
144
160
}
145
161
146
162
#account_menu li .check {
···
1142
1158
background-color: transparent;
1143
1159
}
1144
1160
1161
+
#account.active {
1162
+
color: #333;
1163
+
}
1164
+
1145
1165
#account_menu {
1146
1166
background: hsl(210, 33.33%, 94.0%);
1147
1167
border-color: #ccc;
1148
1168
}
1149
1169
1150
-
#account_menu li a {
1170
+
#account_menu li a[data-action] {
1151
1171
color: #333;
1152
1172
border-color: #bbb;
1153
1173
background-color: hsla(210, 100%, 4%, 0.12);
1154
1174
}
1155
1175
1156
-
#account_menu li a:hover {
1176
+
#account_menu li a[data-action]:hover {
1157
1177
background-color: hsla(210, 100%, 4%, 0.2);
1158
1178
}
1159
1179