szurubooru/client/js/models/post.js
rr- d2a5e1056d client/models: discard field declarations
This has important side effect that matters when we check for data
changes using _orig dictionary. Previously, _orig was empty (so its
members fields were undefiend) whereas the real fields were declared as
nulls. This meant that for new entities, the conditions were always
true, which is unintended. Now both _orig and the class itself are
initially populated with _updateFromResponse which syncs the state
between them, removing the problem.
2016-07-26 23:15:05 +02:00

211 lines
7.1 KiB
JavaScript

'use strict';
const api = require('../api.js');
const tags = require('../tags.js');
const events = require('../events.js');
const CommentList = require('./comment_list.js');
const misc = require('../util/misc.js');
class Post extends events.EventTarget {
constructor() {
super();
this._orig = {};
this._updateFromResponse({});
}
get id() { return this._id; }
get type() { return this._type; }
get mimeType() { return this._mimeType; }
get creationTime() { return this._creationTime; }
get user() { return this._user; }
get safety() { return this._safety; }
get contentUrl() { return this._contentUrl; }
get thumbnailUrl() { return this._thumbnailUrl; }
get canvasWidth() { return this._canvasWidth || 800; }
get canvasHeight() { return this._canvasHeight || 450; }
get fileSize() { return this._fileSize || 0; }
get flags() { return this._flags; }
get tags() { return this._tags; }
get notes() { return this._notes; }
get comments() { return this._comments; }
get relations() { return this._relations; }
get score() { return this._score; }
get favoriteCount() { return this._favoriteCount; }
get ownFavorite() { return this._ownFavorite; }
get ownScore() { return this._ownScore; }
set flags(value) { this._flags = value; }
set tags(value) { this._tags = value; }
set safety(value) { this._safety = value; }
set relations(value) { this._relations = value; }
static fromResponse(response) {
const ret = new Post();
ret._updateFromResponse(response);
return ret;
}
static get(id) {
return api.get('/post/' + id)
.then(response => {
return Promise.resolve(Post.fromResponse(response));
}, response => {
return Promise.reject(response.description);
});
}
isTaggedWith(tagName) {
return this._tags.map(s => s.toLowerCase()).includes(tagName);
}
addTag(tagName, addImplications) {
if (this.isTaggedWith(tagName)) {
return;
}
this._tags.push(tagName);
if (addImplications !== false) {
for (let otherTag of tags.getAllImplications(tagName)) {
this.addTag(otherTag, addImplications);
}
}
}
removeTag(tagName) {
this._tags = this._tags.filter(
s => s.toLowerCase() != tagName.toLowerCase());
}
save() {
const detail = {};
// send only changed fields to avoid user privilege violation
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;
}
if (misc.arraysDiffer(this._relations, this._orig._relations)) {
detail.relations = this._relations;
}
let promise = this._id ?
api.put('/post/' + this._id, detail) :
api.post('/posts', detail);
return promise.then(response => {
this._updateFromResponse(response);
this.dispatchEvent(
new CustomEvent('change', {detail: {post: this}}));
return Promise.resolve();
}, response => {
return Promise.reject(response.description);
});
}
setScore(score) {
return api.put('/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();
}, response => {
return Promise.reject(response.description);
});
}
addToFavorites() {
return api.post('/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();
}, response => {
return Promise.reject(response.description);
});
}
removeFromFavorites() {
return api.delete('/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();
}, response => {
return Promise.reject(response.description);
});
}
_updateFromResponse(response) {
const map = {
_id: response.id,
_type: response.type,
_mimeType: response.mimeType,
_creationTime: response.creationTime,
_user: response.user,
_safety: response.safety,
_contentUrl: response.contentUrl,
_thumbnailUrl: response.thumbnailUrl,
_canvasWidth: response.canvasWidth,
_canvasHeight: response.canvasHeight,
_fileSize: response.fileSize,
_flags: response.flags || [],
_tags: response.tags || [],
_notes: response.notes || [],
_comments: CommentList.fromResponse(response.comments || []),
_relations: response.relations || [],
_score: response.score,
_favoriteCount: response.favoriteCount,
_ownScore: response.ownScore,
_ownFavorite: response.ownFavorite,
};
Object.assign(this, map);
Object.assign(this._orig, map);
}
};
module.exports = Post;