2019-10-05 02:09:52 +02:00
|
|
|
import { $ } from './utils/dom';
|
|
|
|
import parseSearch from './match_query';
|
|
|
|
import store from './utils/store';
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* Store a tag locally, marking the retrieval time
|
|
|
|
* @param {TagData} tagData
|
|
|
|
*/
|
2019-10-05 02:09:52 +02:00
|
|
|
function persistTag(tagData) {
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* @type {TagData}
|
|
|
|
*/
|
|
|
|
const persistData = {
|
|
|
|
...tagData,
|
|
|
|
fetchedAt: new Date().getTime() / 1000,
|
|
|
|
};
|
|
|
|
store.set(`bor_tags_${tagData.id}`, persistData);
|
2019-10-05 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* @param {TagData} tag
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
2019-10-05 02:09:52 +02:00
|
|
|
function isStale(tag) {
|
|
|
|
const now = new Date().getTime() / 1000;
|
|
|
|
return tag.fetchedAt === null || tag.fetchedAt < (now - 604800);
|
|
|
|
}
|
|
|
|
|
|
|
|
function clearTags() {
|
|
|
|
Object.keys(localStorage).forEach(key => {
|
|
|
|
if (key.substring(0, 9) === 'bor_tags_') {
|
|
|
|
store.remove(key);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* @param {unknown} value
|
|
|
|
* @returns {value is TagData}
|
|
|
|
*/
|
|
|
|
function isValidStoredTag(value) {
|
2023-05-29 11:06:39 +02:00
|
|
|
if (value !== null && 'id' in value && 'name' in value && 'images' in value && 'spoiler_image_uri' in value) {
|
2022-03-25 22:03:42 +01:00
|
|
|
return typeof value.id === 'number'
|
|
|
|
&& typeof value.name === 'string'
|
|
|
|
&& typeof value.images === 'number'
|
|
|
|
&& (value.spoiler_image_uri === null || typeof value.spoiler_image_uri === 'string')
|
|
|
|
&& (value.fetchedAt === null || typeof value.fetchedAt === 'number');
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a single tag, or a dummy tag object if we don't know about it yet
|
|
|
|
* @param {number} tagId
|
|
|
|
* @returns {TagData}
|
|
|
|
*/
|
2019-10-05 02:09:52 +02:00
|
|
|
function getTag(tagId) {
|
|
|
|
const stored = store.get(`bor_tags_${tagId}`);
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
if (isValidStoredTag(stored)) {
|
2019-10-05 02:09:52 +02:00
|
|
|
return stored;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: tagId,
|
|
|
|
name: '(unknown tag)',
|
|
|
|
images: 0,
|
|
|
|
spoiler_image_uri: null,
|
2022-03-25 22:03:42 +01:00
|
|
|
fetchedAt: null,
|
2019-10-05 02:09:52 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* Fetches lots of tags in batches and stores them locally
|
|
|
|
* @param {number[]} tagIds
|
|
|
|
*/
|
2019-10-05 02:09:52 +02:00
|
|
|
function fetchAndPersistTags(tagIds) {
|
2020-04-19 02:14:12 +02:00
|
|
|
if (!tagIds.length) return;
|
2019-10-05 02:09:52 +02:00
|
|
|
|
2020-04-19 02:14:12 +02:00
|
|
|
const ids = tagIds.slice(0, 40);
|
|
|
|
const remaining = tagIds.slice(41);
|
|
|
|
|
2021-02-09 23:37:54 +01:00
|
|
|
fetch(`/fetch/tags?ids[]=${ids.join('&ids[]=')}`)
|
2020-04-19 02:14:12 +02:00
|
|
|
.then(response => response.json())
|
|
|
|
.then(data => data.tags.forEach(tag => persistTag(tag)))
|
|
|
|
.then(() => fetchAndPersistTags(remaining));
|
2019-10-05 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 22:03:42 +01:00
|
|
|
/**
|
|
|
|
* Figure out which tags in the list we don't know about
|
|
|
|
* @param {number[]} tagIds
|
|
|
|
*/
|
2019-10-05 02:09:52 +02:00
|
|
|
function fetchNewOrStaleTags(tagIds) {
|
|
|
|
const fetchIds = [];
|
|
|
|
|
|
|
|
tagIds.forEach(t => {
|
|
|
|
const stored = store.get(`bor_tags_${t}`);
|
|
|
|
if (!stored || isStale(stored)) {
|
|
|
|
fetchIds.push(t);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
fetchAndPersistTags(fetchIds);
|
|
|
|
}
|
|
|
|
|
|
|
|
function verifyTagsVersion(latest) {
|
|
|
|
if (store.get('bor_tags_version') !== latest) {
|
|
|
|
clearTags();
|
|
|
|
store.set('bor_tags_version', latest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function initializeFilters() {
|
|
|
|
const tags = window.booru.spoileredTagList
|
|
|
|
.concat(window.booru.hiddenTagList)
|
|
|
|
.filter((a, b, c) => c.indexOf(a) === b);
|
|
|
|
|
|
|
|
verifyTagsVersion(window.booru.tagsVersion);
|
|
|
|
fetchNewOrStaleTags(tags);
|
|
|
|
}
|
|
|
|
|
|
|
|
function unmarshal(data) {
|
2021-10-09 21:58:07 +02:00
|
|
|
try { return JSON.parse(data); }
|
|
|
|
catch (_) { return data; }
|
2019-10-05 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function loadBooruData() {
|
|
|
|
const booruData = document.querySelector('.js-datastore').dataset;
|
|
|
|
|
|
|
|
// Assign all elements to booru because lazy
|
|
|
|
for (const prop in booruData) {
|
|
|
|
window.booru[prop] = unmarshal(booruData[prop]);
|
|
|
|
}
|
|
|
|
|
|
|
|
window.booru.hiddenFilter = parseSearch(window.booru.hiddenFilter);
|
|
|
|
window.booru.spoileredFilter = parseSearch(window.booru.spoileredFilter);
|
|
|
|
|
|
|
|
// Fetch tag metadata and set up filtering
|
|
|
|
initializeFilters();
|
|
|
|
|
|
|
|
// CSRF
|
2019-11-13 05:49:37 +01:00
|
|
|
window.booru.csrfToken = $('meta[name="csrf-token"]').content;
|
2019-10-05 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function BooruOnRails() {
|
|
|
|
this.apiEndpoint = '/api/v2/';
|
2019-11-30 19:14:01 +01:00
|
|
|
this.hiddenTag = '/images/tagblocked.svg';
|
2023-03-30 15:45:14 +02:00
|
|
|
this.tagsVersion = 6;
|
2019-10-05 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
window.booru = new BooruOnRails();
|
|
|
|
|
2020-04-19 02:14:12 +02:00
|
|
|
export { getTag, loadBooruData };
|