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