509 lines
14 KiB
JavaScript
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;
|