v0.3.4
fix: event passive. remove: Post sidebar copy link buttons. fix: page transition transform value undefined. enhance: $PostGrid loader will stop when no more post can fetched. enhance: reset $PostGrid loader when booru switched. enhance: searchbar autocomplete result same as official site. new: class Autocomplete. new: post sidebar add File, Booru, Webm, Source url.
This commit is contained in:
parent
86c2d5aa9f
commit
315246a37a
1
dist/assets/index-B240dcNf.css
vendored
1
dist/assets/index-B240dcNf.css
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/index-C21fHnwq.js
vendored
1
dist/assets/index-C21fHnwq.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/index-DaDrA_5o.js
vendored
Normal file
1
dist/assets/index-DaDrA_5o.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/index-DeBFHjT4.css
vendored
Normal file
1
dist/assets/index-DeBFHjT4.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
dist/index.html
vendored
4
dist/index.html
vendored
@ -7,8 +7,8 @@
|
||||
<title>Danbooru Viewer v0.2.5</title>
|
||||
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
|
||||
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-C21fHnwq.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B240dcNf.css">
|
||||
<script type="module" crossorigin src="/assets/index-DaDrA_5o.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DeBFHjT4.css">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
@ -156,4 +156,9 @@ ion-icon {
|
||||
&:hover {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--secondary-color);
|
||||
text-decoration: none;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"name": "danbooru-viewer",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"vite": "^5.4.8"
|
||||
|
@ -10,6 +10,7 @@ export class $PostGrid extends $Layout {
|
||||
posts = new Set<Post>();
|
||||
$posts = new Set<$PostTile>();
|
||||
tags?: string;
|
||||
finished = false;
|
||||
constructor(options?: $PostGridOptions) {
|
||||
super();
|
||||
this.tags = options?.tags;
|
||||
@ -20,7 +21,13 @@ export class $PostGrid extends $Layout {
|
||||
|
||||
protected async init() {
|
||||
setInterval(() => { if (this.inDOM() && document.documentElement.scrollTop === 0) this.updateNewest(); }, 10000);
|
||||
Booru.events.on('set', () => { this.removeAll(); })
|
||||
Booru.events.on('set', () => {
|
||||
this.removeAll();
|
||||
if (this.finished) {
|
||||
this.finished = false;
|
||||
this.loader();
|
||||
}
|
||||
})
|
||||
this.on('resize', () => this.resize())
|
||||
this.loader();
|
||||
}
|
||||
@ -28,10 +35,13 @@ export class $PostGrid extends $Layout {
|
||||
protected async loader() {
|
||||
if (!this.inDOM()) return setTimeout(() => this.loader(), 100);;
|
||||
while (this.inDOM() && document.documentElement.scrollHeight <= innerHeight * 2) {
|
||||
await this.getPosts();
|
||||
if (!this.posts.size) return;
|
||||
const posts = await this.getPosts();
|
||||
if (!posts.length) return this.finished = true;
|
||||
}
|
||||
if (document.documentElement.scrollTop + innerHeight > document.documentElement.scrollHeight - innerHeight * 2) {
|
||||
const posts = await this.getPosts();
|
||||
if (!posts.length) return this.finished = true;
|
||||
}
|
||||
if (document.documentElement.scrollTop + innerHeight > document.documentElement.scrollHeight - innerHeight * 2) await this.getPosts();
|
||||
setTimeout(() => this.loader(), 100);
|
||||
}
|
||||
|
||||
@ -65,14 +75,14 @@ export class $PostGrid extends $Layout {
|
||||
const latestPost = this.sortedPosts.at(0);
|
||||
const posts = await Post.fetchMultiple(Booru.used, {tags: this.tags, id: latestPost ? `>${latestPost.id}` : undefined}, 100);
|
||||
this.addPost(posts);
|
||||
return this;
|
||||
return posts;
|
||||
}
|
||||
|
||||
async getPosts() {
|
||||
const oldestPost = this.sortedPosts.at(-1);
|
||||
const posts = await Post.fetchMultiple(Booru.used, {tags: this.tags, id: oldestPost ? `<${oldestPost.id}` : undefined}, 100);
|
||||
this.addPost(posts);
|
||||
return this;
|
||||
return posts;
|
||||
}
|
||||
|
||||
get sortedPosts() { return this.posts.array.sort((a, b) => +b.createdDate - +a.createdDate); }
|
||||
|
@ -46,14 +46,14 @@ export class $PostTile extends $Container {
|
||||
.once('load', (e, $img) => {
|
||||
$img
|
||||
.src(this.post.previewURL)
|
||||
.on(['mouseenter', 'touchstart'], () => { if (this.post.isGif) { $img.src(this.post.large_file_url) } })
|
||||
.on(['mouseleave', 'touchend', 'touchcancel'], () => { if (this.post.isGif) { $img.src(this.post.previewURL) } })
|
||||
.on(['mouseenter', 'touchstart'], () => { if (this.post.isGif) { $img.src(this.post.large_file_url) } }, {passive: true})
|
||||
.on(['mouseleave', 'touchend', 'touchcancel'], () => { if (this.post.isGif) { $img.src(this.post.previewURL) } }, {passive: true})
|
||||
.animate({opacity: [0, 1]}, {duration: 300, fill: 'both'});
|
||||
this.removeClass('loading');
|
||||
})
|
||||
])
|
||||
.on(['mouseenter', 'touchstart'], () => { if (!this.$video?.isPlaying) { this.$video?.src(this.post.large_file_url).hide(false).play().catch(err => undefined) } })
|
||||
.on(['mouseleave', 'touchend', 'touchcancel'], () => { this.$video?.pause().currentTime(0).hide(true); })
|
||||
.on(['mouseenter', 'touchstart'], () => { if (!this.$video?.isPlaying) { this.$video?.src(this.post.large_file_url).hide(false).play().catch(err => undefined) } }, {passive: true})
|
||||
.on(['mouseleave', 'touchend', 'touchcancel'], () => { this.$video?.pause().currentTime(0).hide(true); }, {passive: true})
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { $Container } from "elexis";
|
||||
import { Tag, TagCategory } from "../../structure/Tag";
|
||||
import { Booru } from "../../structure/Booru";
|
||||
import { User } from "../../structure/User";
|
||||
import { Autocomplete } from "../../structure/Autocomplete";
|
||||
|
||||
export class $Searchbar extends $Container {
|
||||
$tagInput = new $TagInput(this);
|
||||
@ -128,21 +130,31 @@ export class $Searchbar extends $Container {
|
||||
async getSearchSuggestions() {
|
||||
const input = this.$tagInput.$input.value()
|
||||
if (!input.length) return this.$selectionList.clearSelections();
|
||||
const tags = await Tag.fetchMultiple(Booru.used, {fuzzy_name_matches: input, order: 'similarity'});
|
||||
const results = await Autocomplete.fetch(Booru.used, input, 20);
|
||||
this.$selectionList
|
||||
.clearSelections()
|
||||
.addSelections(tags.map(tag => new $Selection().value(tag.name)
|
||||
.addSelections(results.map(data => new $Selection().value(data.value)
|
||||
.content([
|
||||
$('span').class('tag-name').content(tag.name),
|
||||
$('span').class('tag-category').content(TagCategory[tag.category])
|
||||
$('div').class('selection-label').content([
|
||||
data.isTagAntecedent() ? $('span').class('tag-antecedent').self($span => $span.dom.innerHTML = data.antecedent.replaceAll(input, `<b>${input}</b>`)) : null,
|
||||
$('div').class('label-container').content([
|
||||
data.isTagAntecedent() ? $('ion-icon').name('arrow-forward-outline') : null,
|
||||
$('span').class('label').self($span => $span.dom.innerHTML = data.label.replaceAll(input, `<b>${input}</b>`))
|
||||
])
|
||||
]),
|
||||
data.isTag() ? $('div').class('tag-detail').content([
|
||||
$('span').class('tag-post-count').content(new Intl.NumberFormat('en', {notation: 'compact'}).format(data.post_count)),
|
||||
$('span').class('tag-category').content(TagCategory[data.category])
|
||||
]) : null,
|
||||
data.isUser() ? $('span').class('user-level').content(data.level) : null
|
||||
])
|
||||
.on('click', () => {this.$tagInput.addTag(tag.name).input()})
|
||||
.on('click', () => {this.$tagInput.addTag(data.label).input()})
|
||||
))
|
||||
if (!this.$tagInput.$input.value().length) this.$selectionList.clearSelections();
|
||||
}
|
||||
|
||||
search() {
|
||||
$.replace(`/posts?tags=${this.$tagInput.query}`);
|
||||
$.replace(`/posts?tags=${this.$tagInput.query.replace(':', '%3A')}`);
|
||||
this.$tagInput.clearAll();
|
||||
this.inactivate();
|
||||
return this;
|
||||
@ -177,10 +189,12 @@ class $SelectionList extends $Container {
|
||||
return this;
|
||||
}
|
||||
|
||||
focusSelection(selection: $Selection) {
|
||||
focusSelection($selection: $Selection) {
|
||||
this.blurSelection();
|
||||
this.focused = selection;
|
||||
selection.focus();
|
||||
this.focused = $selection;
|
||||
$selection.focus();
|
||||
if ($selection.offsetTop < this.scrollTop()) this.scrollTop($selection.offsetTop);
|
||||
if ($selection.offsetTop + $selection.offsetHeight > this.scrollTop() + this.offsetHeight) this.scrollTop($selection.offsetTop + $selection.offsetHeight - this.offsetHeight);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ searchbar {
|
||||
align-items: center;
|
||||
padding: 0.4rem 1rem;
|
||||
cursor: pointer;
|
||||
gap: 1rem;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--background-color-lighter) 50%, transparent);
|
||||
@ -88,9 +89,30 @@ searchbar {
|
||||
&.active {
|
||||
background-color: var(--background-color-lighter);
|
||||
}
|
||||
.tag-name {
|
||||
div.selection-label {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
.label-container {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
|
||||
ion-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tag-category {
|
||||
|
||||
div.tag-detail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
.tag-post-count {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
.tag-category, .user-level {
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 0.4rem;
|
||||
font-size: 0.9rem;
|
||||
|
@ -81,7 +81,7 @@ $(document.body).content([
|
||||
switch ($Router.navigationDirection) {
|
||||
case $RouterNavigationDirection.Forward: return [`translateX(${TX}%)`, `translateX(0%)`];
|
||||
case $RouterNavigationDirection.Back: return [`translateX(-${TX}%)`, `translateX(0%)`];
|
||||
case $RouterNavigationDirection.Replace: return undefined;
|
||||
case $RouterNavigationDirection.Replace: return '';
|
||||
}
|
||||
})
|
||||
e.$view.content(e.nextContent);
|
||||
@ -102,7 +102,7 @@ $(document.body).content([
|
||||
switch ($Router.navigationDirection) {
|
||||
case $RouterNavigationDirection.Forward: return [`translateX(0%)`, `translateX(-${TX}%)`];
|
||||
case $RouterNavigationDirection.Back: return [`translateX(0%)`, `translateX(${TX}%)`];
|
||||
case $RouterNavigationDirection.Replace: return undefined;
|
||||
case $RouterNavigationDirection.Replace: return '';
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -29,53 +29,60 @@ export const post_route = $('route').path('/posts/:id').id('post').builder(({$ro
|
||||
$('div').class('sidebar')
|
||||
.self($sidebar => {
|
||||
let scrollTop = 0;
|
||||
addEventListener('scroll', () => { if ($sidebar.inDOM()) scrollTop = document.documentElement.scrollTop })
|
||||
addEventListener('scroll', () => { if ($sidebar.inDOM()) scrollTop = document.documentElement.scrollTop }, {passive: true})
|
||||
$route
|
||||
.on('beforeShift', () => { if (innerWidth > 800) $sidebar.css({position: `absolute`, top: `calc(${scrollTop}px + var(--nav-height) + var(--padding))`}) })
|
||||
.on('afterShift', () => $sidebar.css({position: '', top: ''}))
|
||||
})
|
||||
.content([
|
||||
$('section').class('post-info').content([
|
||||
new $Property('id').name('Post').value(`#${params.id}`),
|
||||
new $Property('uploader').name('Uploader').value(post.uploader$),
|
||||
new $Property('approver').name('Approver').value(post.approver$),
|
||||
new $Property('date').name('Date').value(post.created_date$),
|
||||
new $Property('size').name('Size').value([post.file_size$, post.dimension$]),
|
||||
new $Property('file').name('File Type').value(post.file_ext$),
|
||||
new $Property('id').name('Post').content(`#${params.id}`),
|
||||
new $Property('uploader').name('Uploader').content(post.uploader$),
|
||||
new $Property('approver').name('Approver').content(post.approver$),
|
||||
new $Property('date').name('Date').content(post.created_date$),
|
||||
new $Property('size').name('Size').content([post.file_size$, post.dimension$]),
|
||||
new $Property('file-type').name('File Type').content(post.file_ext$),
|
||||
$('div').class('inline').content([
|
||||
new $Property('favorites').name('Favorites').value(post.favorites$),
|
||||
new $Property('score').name('Score').value(post.score$)
|
||||
]),
|
||||
$('div').class('buttons').content([
|
||||
$('icon-button').class('vertical').icon('link-outline').content(Booru.name$)
|
||||
.on('click', (e, $button) => {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(`${Booru.used.origin}${location.pathname}`);
|
||||
$button.content('Copied!');
|
||||
setTimeout(() => {
|
||||
$button.content(Booru.name$)
|
||||
}, 2000);
|
||||
}),
|
||||
$('icon-button').class('vertical').icon('link-outline').content(`File`)
|
||||
.on('click', (e, $button) => {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(post.file_url);
|
||||
$button.content('Copied!');
|
||||
setTimeout(() => {
|
||||
$button.content('File')
|
||||
}, 2000);
|
||||
}),
|
||||
$('icon-button').class('vertical').icon('link-outline').content(`Webm`)
|
||||
.on('click', (e, $button) => {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(post.previewURL);
|
||||
$button.content('Copied!');
|
||||
setTimeout(() => {
|
||||
$button.content('Webm')
|
||||
}, 2000);
|
||||
})
|
||||
.hide(true).self(async ($button) => { await post.ready; if (post.file_ext === 'zip') $button.hide(false) })
|
||||
new $Property('favorites').name('Favorites').content(post.favorites$),
|
||||
new $Property('score').name('Score').content(post.score$)
|
||||
]),
|
||||
new $Property('file-url').name('File').content($('a').href(post.file_url$).content(post.file_url$.convert((value) => value.replace('https://', ''))).target('_blank')),
|
||||
new $Property('source-url').name('Source').content($('a').href(post.source$).content(post.source$.convert((value) => value.replace('https://', ''))).target('_blank')),
|
||||
new $Property('booru-url').name(Booru.name$).content($('a').href(post.url$).content(post.url$.convert((value) => value.replace('https://', ''))).target('_blank')),
|
||||
new $Property('booru-url').name('Webm').hide(true).self(async ($property) => {
|
||||
await post.ready;
|
||||
if (post.isUgoria) $property.content($('a').href(post.webm_url$).content(post.webm_url$.convert((value) => value.replace('https://', ''))).target('_blank')).hide(false);
|
||||
}),
|
||||
// $('div').class('buttons').content([
|
||||
// $('icon-button').class('vertical').icon('link-outline').content(Booru.name$)
|
||||
// .on('click', (e, $button) => {
|
||||
// e.preventDefault();
|
||||
// navigator.clipboard.writeText(`${Booru.used.origin}${location.pathname}`);
|
||||
// $button.content('Copied!');
|
||||
// setTimeout(() => {
|
||||
// $button.content(Booru.name$)
|
||||
// }, 2000);
|
||||
// }),
|
||||
// $('icon-button').class('vertical').icon('link-outline').content(`File`)
|
||||
// .on('click', (e, $button) => {
|
||||
// e.preventDefault();
|
||||
// navigator.clipboard.writeText(post.file_url);
|
||||
// $button.content('Copied!');
|
||||
// setTimeout(() => {
|
||||
// $button.content('File')
|
||||
// }, 2000);
|
||||
// }),
|
||||
// $('icon-button').class('vertical').icon('link-outline').content(`Webm`)
|
||||
// .on('click', (e, $button) => {
|
||||
// e.preventDefault();
|
||||
// navigator.clipboard.writeText(post.previewURL);
|
||||
// $button.content('Copied!');
|
||||
// setTimeout(() => {
|
||||
// $button.content('Webm')
|
||||
// }, 2000);
|
||||
// })
|
||||
// .hide(true).self(async ($button) => { await post.ready; if (post.file_ext === 'zip') $button.hide(false) })
|
||||
// ]),
|
||||
]),
|
||||
$('div').class('post-tags').content(async $tags => {
|
||||
const tags = await post.fetchTags();
|
||||
@ -116,9 +123,9 @@ class $Property extends $Container {
|
||||
constructor(id: string) {
|
||||
super('div');
|
||||
this.staticClass('property').attribute('property-id', id);
|
||||
this.content([
|
||||
super.content([
|
||||
this.$name,
|
||||
this.$values
|
||||
this.$values.hide(true)
|
||||
])
|
||||
}
|
||||
|
||||
@ -127,7 +134,8 @@ class $Property extends $Container {
|
||||
return this;
|
||||
}
|
||||
|
||||
value(content: OrMatrix<$ContainerContentType>) {
|
||||
content(content: OrMatrix<$ContainerContentType>) {
|
||||
this.$values.hide(false);
|
||||
const list = $.orArrayResolve(content);
|
||||
this.$values.content(list.map($item => $('span').staticClass('property-value').content($item)));
|
||||
return this;
|
||||
|
@ -129,14 +129,22 @@
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
span.property-name {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
div.property-values {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
overflow: hidden;
|
||||
span.property-value {
|
||||
background-color: var(--secondary-color-dark);
|
||||
color: var(--secondary-color);
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
src/structure/Autocomplete.ts
Normal file
75
src/structure/Autocomplete.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import type { Booru } from "./Booru";
|
||||
import type { TagCategory } from "./Tag";
|
||||
import type { UserLevelString } from "./User";
|
||||
|
||||
export class Autocomplete {
|
||||
static async fetch(booru: Booru, query: string, limit: number = 20) {
|
||||
const res = await fetch(`${booru.origin}/autocomplete.json?search[query]=${query}&search[type]=tag_query&version=1&limit=${limit}`).then(res => res.json()) as AutocompleteData[];
|
||||
return res.map(data => new AutocompleteResult(data));
|
||||
}
|
||||
}
|
||||
|
||||
export interface AutocompleteResult extends AutocompleteBaseData {}
|
||||
export class AutocompleteResult {
|
||||
constructor(data: AutocompleteData) {
|
||||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
isTag(): this is AutocompleteResult & (AutocompleteTagData | AutocompleteTagAliasData | AutocompleteTagAutocorrectData) {
|
||||
return this.type === 'tag' || this.type === 'tag-autocorrect' || this.type === 'tag-alias' || this.type === 'tag-word';
|
||||
}
|
||||
|
||||
isTagAutocorrect(): this is AutocompleteResult & AutocompleteTagAutocorrectData {
|
||||
return this.type === 'tag-autocorrect';
|
||||
}
|
||||
|
||||
isTagAntecedent(): this is Autocomplete & AutocompleteTagAutocorrectData {
|
||||
//@ts-expect-error
|
||||
return !!this['antecedent' as any]
|
||||
}
|
||||
|
||||
isTagWord(): this is AutocompleteResult & AutocompleteTagWordData {
|
||||
return this.type === 'tag-word'
|
||||
}
|
||||
|
||||
isUser(): this is AutocompleteResult & AutocompleteUserData {
|
||||
return this.type === 'user';
|
||||
}
|
||||
}
|
||||
|
||||
type AutocompleteData = AutocompleteBaseData & (AutocompleteUserData | AutocompleteTagData | AutocompleteTagAutocorrectData | AutocompleteTagAliasData);
|
||||
|
||||
interface AutocompleteBaseData {
|
||||
type: 'user' | 'tag' | 'tag-autocorrect' | 'tag-alias' | 'tag-word';
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AutocompleteUserData {
|
||||
type: 'user';
|
||||
id: number;
|
||||
level: Lowercase<UserLevelString>;
|
||||
}
|
||||
interface AutocompleteTagData {
|
||||
type: 'tag';
|
||||
category: TagCategory;
|
||||
post_count: number;
|
||||
}
|
||||
interface AutocompleteTagAutocorrectData {
|
||||
type: 'tag-autocorrect';
|
||||
category: TagCategory;
|
||||
post_count: number;
|
||||
antecedent: string;
|
||||
}
|
||||
interface AutocompleteTagAliasData {
|
||||
type: 'tag-alias';
|
||||
category: TagCategory;
|
||||
post_count: number;
|
||||
antecedent: string;
|
||||
}
|
||||
interface AutocompleteTagWordData{
|
||||
type: 'tag-word';
|
||||
category: TagCategory;
|
||||
post_count: number;
|
||||
antecedent: string;
|
||||
}
|
@ -16,9 +16,13 @@ export class Post extends $EventManager<{update: []}> {
|
||||
score$ = $.state(0);
|
||||
file_size$ = $.state(LOADING_STRING);
|
||||
file_ext$ = $.state(LOADING_STRING);
|
||||
file_url$ = $.state(LOADING_STRING);
|
||||
source$ = $.state(LOADING_STRING);
|
||||
dimension$ = $.state(LOADING_STRING);
|
||||
url$ = $.state(LOADING_STRING);
|
||||
createdDate = new Date(this.created_at);
|
||||
ready?: Promise<this>;
|
||||
webm_url$ = $.state(LOADING_STRING);
|
||||
|
||||
booru: Booru;
|
||||
constructor(booru: Booru, id: id, data?: PostData) {
|
||||
@ -56,6 +60,7 @@ export class Post extends $EventManager<{update: []}> {
|
||||
}
|
||||
const req = await fetch(`${booru.origin}/posts.json?limit=${limit}&tags=${tagsQuery}&_method=get`);
|
||||
const dataArray: PostData[] = await req.json();
|
||||
if (dataArray instanceof Array === false) return [];
|
||||
const list = dataArray.map(data => {
|
||||
const instance = booru.posts.get(data.id)?.update(data) ?? new this(booru, data.id, data);
|
||||
booru.posts.set(instance.id, instance);
|
||||
@ -75,7 +80,11 @@ export class Post extends $EventManager<{update: []}> {
|
||||
this.score$.set(this.score);
|
||||
this.file_size$.set(digitalUnit(this.file_size));
|
||||
this.file_ext$.set(this.file_ext as any);
|
||||
this.file_url$.set(this.file_url);
|
||||
this.source$.set(this.source);
|
||||
this.dimension$.set(`${this.image_width}x${this.image_height}`);
|
||||
this.url$.set(`${this.url}`);
|
||||
if (this.isUgoria) this.webm_url$.set(this.large_file_url);
|
||||
this.createdDate = new Date(this.created_at);
|
||||
this.fire('update');
|
||||
}
|
||||
@ -102,7 +111,8 @@ export class Post extends $EventManager<{update: []}> {
|
||||
const tag_list = this.tag_string.split(' ');
|
||||
return [...this.booru.tags.values()].filter(tag => tag_list.includes(tag.name))
|
||||
}
|
||||
get previewURL() { return this.media_asset.variants.find(variant => variant.file_ext === 'webp')?.url ?? this.large_file_url }
|
||||
get previewURL() { return this.media_asset.variants?.find(variant => variant.file_ext === 'webp')?.url ?? this.large_file_url }
|
||||
get url() { return `${this.booru.origin}/posts/${this.id}` }
|
||||
}
|
||||
|
||||
export interface PostData extends PostOptions {
|
||||
|
@ -66,7 +66,7 @@ export interface UserData {
|
||||
}
|
||||
|
||||
export type UserLevel = 10 | 20 | 30 | 31 | 32 | 40 | 50;
|
||||
export type UserLevelString = "Member" | "Gold" | "Platinum" | "Admin";
|
||||
export type UserLevelString = "Member" | "Gold" | "Platinum" | "Admin" | "Contributor" | "Builder" | "Approver";
|
||||
|
||||
export interface UserProfileData extends UserData {
|
||||
"last_logged_in_at": ISOString,
|
||||
|
Loading…
Reference in New Issue
Block a user