szurubooru/client/js/models/post.js
2024-03-28 13:31:48 +01:00

509 lines
14 KiB
JavaScript

"use strict";
const api = require("../api.js");
const uri = require("../util/uri.js");
const tags = require("../tags.js");
const events = require("../events.js");
const TagList = require("./tag_list.js");
const NoteList = require("./note_list.js");
const CommentList = require("./comment_list.js");
const PoolList = require("./pool_list.js");
const Pool = require("./pool.js");
const misc = require("../util/misc.js");
class Post extends events.EventTarget {
constructor() {
super();
this._orig = {};
for (let obj of [this, this._orig]) {
obj._tags = new TagList();
obj._notes = new NoteList();
obj._comments = new CommentList();
obj._pools = new PoolList();
}
this._updateFromResponse({});
}
get id() {
return this._id;
}
get type() {
return this._type;
}
get mimeType() {
return this._mimeType;
}
get checksumSHA1() {
return this._checksumSHA1;
}
get checksumMD5() {
return this._checksumMD5;
}
get creationTime() {
return this._creationTime;
}
get user() {
return this._user;
}
get safety() {
return this._safety;
}
get contentUrl() {
return this._contentUrl;
}
get fullContentUrl() {
return this._fullContentUrl;
}
get thumbnailUrl() {
return this._thumbnailUrl;
}
get source() {
return this._source;
}
get sourceSplit() {
return this._source.split("\n");
}
get canvasWidth() {
return this._canvasWidth || 800;
}
get canvasHeight() {
return this._canvasHeight || 450;
}
get fileSize() {
return this._fileSize || 0;
}
get newContent() {
throw "Invalid operation";
}
get newThumbnail() {
throw "Invalid operation";
}
get flags() {
return this._flags;
}
get tags() {
return this._tags;
}
get tagNames() {
return this._tags.map((tag) => tag.names[0]);
}
get notes() {
return this._notes;
}
get comments() {
return this._comments;
}
get relations() {
return this._relations;
}
get pools() {
return this._pools;
}
get score() {
return this._score;
}
get commentCount() {
return this._commentCount;
}
get favoriteCount() {
return this._favoriteCount;
}
get ownFavorite() {
return this._ownFavorite;
}
get ownScore() {
return this._ownScore;
}
get hasCustomThumbnail() {
return this._hasCustomThumbnail;
}
set flags(value) {
this._flags = value;
}
set safety(value) {
this._safety = value;
}
set relations(value) {
this._relations = value;
}
set newContent(value) {
this._newContent = value;
}
set newThumbnail(value) {
this._newThumbnail = value;
}
set source(value) {
this._source = value;
}
static fromResponse(response) {
const ret = new Post();
ret._updateFromResponse(response);
return ret;
}
static reverseSearch(content) {
let apiPromise = api.post(
uri.formatApiLink("posts", "reverse-search"),
{},
{ content: content }
);
let returnedPromise = apiPromise.then((response) => {
if (response.exactPost) {
response.exactPost = Post.fromResponse(response.exactPost);
}
for (let item of response.similarPosts) {
item.post = Post.fromResponse(item.post);
}
return Promise.resolve(response);
});
returnedPromise.abort = () => apiPromise.abort();
return returnedPromise;
}
static get(id) {
return api.get(uri.formatApiLink("post", id)).then((response) => {
return Promise.resolve(Post.fromResponse(response));
});
}
_savePoolPosts() {
const difference = (a, b) => a.filter((post) => !b.hasPoolId(post.id));
// find the pools where the post was added or removed
const added = difference(this.pools, this._orig._pools);
const removed = difference(this._orig._pools, this.pools);
let ops = [];
// update each pool's list of posts
for (let pool of added) {
let op = Pool.get(pool.id).then((response) => {
if (!response.posts.hasPostId(this._id)) {
response.posts.addById(this._id);
return response.save();
} else {
return Promise.resolve(response);
}
});
ops.push(op);
}
for (let pool of removed) {
let op = Pool.get(pool.id).then((response) => {
if (response.posts.hasPostId(this._id)) {
response.posts.removeById(this._id);
return response.save();
} else {
return Promise.resolve(response);
}
});
ops.push(op);
}
return Promise.all(ops);
}
save(anonymous) {
const files = {};
const detail = { version: this._version };
// send only changed fields to avoid user privilege violation
if (anonymous === true) {
detail.anonymous = true;
}
if (this._safety !== this._orig._safety) {
detail.safety = this._safety;
}
if (misc.arraysDiffer(this._flags, this._orig._flags)) {
detail.flags = this._flags;
}
if (misc.arraysDiffer(this._tags, this._orig._tags)) {
detail.tags = this._tags.map((tag) => tag.names[0]);
}
if (misc.arraysDiffer(this._relations, this._orig._relations)) {
detail.relations = this._relations;
}
if (misc.arraysDiffer(this._notes, this._orig._notes)) {
detail.notes = this._notes.map((note) => ({
polygon: note.polygon.map((point) => [point.x, point.y]),
text: note.text,
}));
}
if (this._newContent) {
files.content = this._newContent;
}
if (this._newThumbnail !== undefined && this._newThumbnail !== null) {
files.thumbnail = this._newThumbnail;
}
if (this._source !== this._orig._source) {
detail.source = this._source;
}
let apiPromise = this._id
? api.put(uri.formatApiLink("post", this.id), detail, files)
: api.post(uri.formatApiLink("posts"), detail, files);
return apiPromise
.then((response) => {
if (misc.arraysDiffer(this._pools, this._orig._pools)) {
return this._savePoolPosts().then(() =>
Promise.resolve(response)
);
}
return Promise.resolve(response);
})
.then(
(response) => {
this._updateFromResponse(response);
this.dispatchEvent(
new CustomEvent("change", { detail: { post: this } })
);
if (this._newContent) {
this.dispatchEvent(
new CustomEvent("changeContent", {
detail: { post: this },
})
);
}
if (this._newThumbnail) {
this.dispatchEvent(
new CustomEvent("changeThumbnail", {
detail: { post: this },
})
);
}
return Promise.resolve();
},
(error) => {
if (
error.response &&
error.response.name === "PostAlreadyUploadedError"
) {
error.message = `Post already uploaded (@${error.response.otherPostId})`;
}
return Promise.reject(error);
}
);
}
feature() {
return api
.post(uri.formatApiLink("featured-post"), { id: this._id })
.then((response) => {
return Promise.resolve();
});
}
delete() {
return api
.delete(uri.formatApiLink("post", this.id), {
version: this._version,
})
.then((response) => {
this.dispatchEvent(
new CustomEvent("delete", {
detail: {
post: this,
},
})
);
return Promise.resolve();
});
}
merge(targetId, useOldContent) {
return api
.get(uri.formatApiLink("post", targetId))
.then((response) => {
return api.post(uri.formatApiLink("post-merge"), {
removeVersion: this._version,
remove: this._id,
mergeToVersion: response.version,
mergeTo: targetId,
replaceContent: useOldContent,
});
})
.then((response) => {
this._updateFromResponse(response);
this.dispatchEvent(
new CustomEvent("change", {
detail: {
post: this,
},
})
);
return Promise.resolve();
});
}
setScore(score) {
return api
.put(uri.formatApiLink("post", this.id, "score"), { score: score })
.then((response) => {
const prevFavorite = this._ownFavorite;
this._updateFromResponse(response);
if (this._ownFavorite !== prevFavorite) {
this.dispatchEvent(
new CustomEvent("changeFavorite", {
detail: {
post: this,
},
})
);
}
this.dispatchEvent(
new CustomEvent("changeScore", {
detail: {
post: this,
},
})
);
return Promise.resolve();
});
}
addToFavorites() {
return api
.post(uri.formatApiLink("post", this.id, "favorite"))
.then((response) => {
const prevScore = this._ownScore;
this._updateFromResponse(response);
if (this._ownScore !== prevScore) {
this.dispatchEvent(
new CustomEvent("changeScore", {
detail: {
post: this,
},
})
);
}
this.dispatchEvent(
new CustomEvent("changeFavorite", {
detail: {
post: this,
},
})
);
return Promise.resolve();
});
}
removeFromFavorites() {
return api
.delete(uri.formatApiLink("post", this.id, "favorite"))
.then((response) => {
const prevScore = this._ownScore;
this._updateFromResponse(response);
if (this._ownScore !== prevScore) {
this.dispatchEvent(
new CustomEvent("changeScore", {
detail: {
post: this,
},
})
);
}
this.dispatchEvent(
new CustomEvent("changeFavorite", {
detail: {
post: this,
},
})
);
return Promise.resolve();
});
}
mutateContentUrl() {
this._contentUrl =
this._orig._contentUrl +
"?bypass-cache=" +
Math.round(Math.random() * 1000);
}
_updateFromResponse(response) {
const map = () => ({
_version: response.version,
_id: response.id,
_type: response.type,
_mimeType: response.mimeType,
_checksumSHA1: response.checksum,
_checksumMD5: response.checksumMD5,
_creationTime: response.creationTime,
_user: response.user,
_safety: response.safety,
_contentUrl: response.contentUrl,
_fullContentUrl: new URL(
response.contentUrl,
document.getElementsByTagName("base")[0].href
).href,
_thumbnailUrl: response.thumbnailUrl,
_source: response.source,
_canvasWidth: response.canvasWidth,
_canvasHeight: response.canvasHeight,
_fileSize: response.fileSize,
_flags: [...(response.flags || [])],
_relations: [...(response.relations || [])],
_score: response.score,
_commentCount: response.commentCount,
_favoriteCount: response.favoriteCount,
_ownScore: response.ownScore,
_ownFavorite: response.ownFavorite,
_hasCustomThumbnail: response.hasCustomThumbnail,
});
for (let obj of [this, this._orig]) {
obj._tags.sync(response.tags);
obj._notes.sync(response.notes);
obj._comments.sync(response.comments);
obj._pools.sync(response.pools);
}
Object.assign(this, map());
Object.assign(this._orig, map());
}
}
module.exports = Post;