[
'Ajax', 'Core', 'Dictionary', 'Language',
'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
- 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User'
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User', 'WoltLabSuite/Core/Ui/Reaction/Handler'
],
function(
Ajax, Core, Dictionary, Language,
ObjectMap, StringUtil, DomChangeListener, DomUtil,
- UiDialog, UiUserList, User
+ UiDialog, UiUserList, User, UiReactionHandler
)
{
"use strict";
- var _isBusy = false;
-
/**
* @constructor
*/
this.initContainers(options, objectType);
DomChangeListener.add('WoltLabSuite/Core/Ui/Like/Handler-' + objectType, this.initContainers.bind(this));
+
+ new UiReactionHandler(this._objectType, {
+ // permissions
+ canReact: this._options.canLike,
+ canReactToOwnContent: this._options.canLikeOwnContent,
+ canViewReactions: this._options.canViewSummary,
+
+ // selectors
+ containerSelector: this._options.containerSelector,
+ summaryListSelector: this._options.summarySelector
+ });
},
/**
* @param {object} elementData like data
*/
_buildWidget: function(element, elementData) {
- // build summary
- if (this._options.canViewSummary) {
- var summary, summaryContent, summaryIcon;
- var summaryContainer = (this._options.isSingleItem) ? elBySel(this._options.summarySelector) : elBySel(this._options.summarySelector, element);
- if (summaryContainer !== null) {
- summary = elCreate('div');
- summary.className = 'likesSummary';
+ // build reaction summary list
+ var summaryList, listItem, badgeContainer, isSummaryPosition = true;
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.summarySelector) : elBySel(this._options.summarySelector, element);
+ if (badgeContainer === null) {
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.badgeContainerSelector) : elBySel(this._options.badgeContainerSelector, element);
+ isSummaryPosition = false;
+ }
+
+ if (badgeContainer !== null) {
+ summaryList = elCreate('ul');
+ summaryList.className = 'reactionSummaryList' + (isSummaryPosition ? ' likesSummary' : ' reactionSummaryListTiny') + ((isSummaryPosition && this._options.badgeClassNames) ? ' ' + this._options.badgeClassNames : '');
+
+ for (var key in elementData.users) {
+ if (key === "reactionTypeID") continue;
+ if (!REACTION_TYPES.hasOwnProperty(key)) continue;
- if (this._options.summaryUseIcon) {
- summaryIcon = elCreate('span');
- summaryIcon.className = 'icon icon16 fa-thumbs-o-up';
- summary.appendChild(summaryIcon);
- }
+ // create element
+ var createdElement = elCreate('li');
+ createdElement.className = 'reactCountButton';
+ createdElement.innerHTML = REACTION_TYPES[key].renderedIcon +' ';
+ elData(createdElement, 'reaction-type-id', key);
+
+ var countSpan = elCreate('span');
+ countSpan.className = 'reactionCount';
+ countSpan.innerHTML = StringUtil.shortUnit(elementData.users[key]);
+ createdElement.appendChild(countSpan);
- summaryContent = elCreate('span');
- summaryContent.className = 'likesSummaryContent';
- summaryContent.addEventListener(WCF_CLICK_EVENT, this._showSummary.bind(this, element));
- summary.appendChild(summaryContent);
+ summaryList.appendChild(createdElement);
+ }
+
+ if (isSummaryPosition) {
if (this._options.summaryPrepend) {
- DomUtil.prepend(summary, summaryContainer);
+ DomUtil.prepend(summaryList, badgeContainer);
}
else {
- summaryContainer.appendChild(summary);
+ badgeContainer.appendChild(summaryList);
}
-
- elementData.summary = summaryContent;
-
- this._updateSummary(element);
- }
- }
-
- // cumulative likes
- var badge, listItem;
- var badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.badgeContainerSelector) : elBySel(this._options.badgeContainerSelector, element);
- if (badgeContainer !== null) {
- badge = elCreate('a');
- badge.href = '#';
- badge.className = 'wcfLikeCounter jsTooltip' + (this._options.badgeClassNames ? ' ' + this._options.badgeClassNames : '');
- badge.addEventListener(WCF_CLICK_EVENT, this._showSummary.bind(this, element));
-
- if (badgeContainer.nodeName === 'OL' || badgeContainer.nodeName === 'UL') {
- listItem = elCreate('li');
- listItem.appendChild(badge);
- badgeContainer.appendChild(listItem);
}
else {
- badgeContainer.appendChild(badge);
+ if (badgeContainer.nodeName === 'OL' || badgeContainer.nodeName === 'UL') {
+ listItem = elCreate('li');
+ listItem.appendChild(summaryList);
+ badgeContainer.appendChild(listItem);
+ }
+ else {
+ badgeContainer.appendChild(summaryList);
+ }
}
- elementData.badge = badge;
-
- this._updateBadge(element);
+ elementData.badge = summaryList;
}
+ // build reaction button
if (this._options.canLike && (User.userId != elData(element, 'user-id') || this._options.canLikeOwnContent)) {
var appendTo = (this._options.buttonAppendToSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonAppendToSelector) : elBySel(this._options.buttonAppendToSelector, element)) : null;
var insertPosition = (this._options.buttonBeforeSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonBeforeSelector) : elBySel(this._options.buttonBeforeSelector, element)) : null;
throw new Error("Unable to find insert location for like/dislike buttons.");
}
else {
- // like button
- elementData.likeButton = this._createButton(element, true, insertPosition, appendTo);
-
- // dislike button
- if (this._options.canDislike) {
- elementData.dislikeButton = this._createButton(element, false, insertPosition, appendTo);
- }
-
- this._updateActiveState(element);
+ elementData.likeButton = this._createButton(element, elementData.users.reactionTypeID, insertPosition, appendTo);
}
}
},
/**
- * Creates a like or dislike button.
+ * Creates a reaction button.
*
- * @param {Element} element container element
- * @param {boolean} isLike false if this is a dislike button
- * @param {Element?} insertBefore insert button before given element
- * @param {Element?} appendTo append button to given element
+ * @param {Element} element container element
+ * @param {int} reactionTypeID the reactionTypeID of the current state
+ * @param {Element?} insertBefore insert button before given element
+ * @param {Element?} appendTo append button to given element
* @return {Element} button element
*/
- _createButton: function(element, isLike, insertBefore, appendTo) {
- var title = Language.get('wcf.like.button.' + (isLike ? 'like' : 'dislike'));
+ _createButton: function(element, reactionTypeID, insertBefore, appendTo) {
+ var title = Language.get('wcf.reactions.react');
var listItem = elCreate('li');
- listItem.className = 'wcf' + (isLike ? 'Like' : 'Dislike') + 'Button';
+ listItem.className = 'wcfReactButton';
var button = elCreate('a');
- button.className = 'jsTooltip' + (this._options.renderAsButton ? ' button' : '');
+ button.className = 'jsTooltip reactButton' + (this._options.renderAsButton ? ' button' : '');
button.href = '#';
button.title = title;
- button.innerHTML = '<span class="icon icon16 fa-thumbs-o-' + (isLike ? 'up' : 'down') + '"></span> <span class="invisible">' + title + '</span>';
- button.addEventListener(WCF_CLICK_EVENT, this._like.bind(this, element));
- elData(button, 'type', (isLike ? 'like' : 'dislike'));
- listItem.appendChild(button);
-
- if (insertBefore) {
- insertBefore.parentNode.insertBefore(listItem, insertBefore);
- }
- else {
- appendTo.appendChild(listItem);
- }
-
- return button;
- },
-
- /**
- * Shows the summary of likes/dislikes.
- *
- * @param {Element} element container element
- * @param {object} event event object
- */
- _showSummary: function(element, event) {
- event.preventDefault();
-
- if (!this._details.has(element)) {
- this._details.set(element, new UiUserList({
- className: 'wcf\\data\\like\\LikeAction',
- dialogTitle: Language.get('wcf.like.details'),
- parameters: {
- data: {
- containerID: DomUtil.identify(element),
- objectID: this._containers.get(element).objectId,
- objectType: this._objectType
- }
- }
- }));
- }
-
- this._details.get(element).open();
- },
-
- /**
- * Updates the display of cumulative likes.
- *
- * @param {Element} element container element
- */
- _updateBadge: function(element) {
- var data = this._containers.get(element);
+ var icon = elCreate('img');
+ icon.className = 'reactionType';
- if (data.likes === 0 && data.dislikes === 0) {
- elHide(data.badge);
+ if (reactionTypeID === undefined || reactionTypeID == 0) {
+ icon.src = WCF_PATH+'images/reaction/reactionIcon.svg';
+ elData(icon, 'reaction-type-id', 0);
}
else {
- elShow(data.badge);
-
- // remove old classes
- data.badge.classList.remove('likeCounterLiked', 'likeCounterDisliked');
+ icon.src = REACTION_TYPES[reactionTypeID].iconPath;
+ elData(icon, 'reaction-type-id', reactionTypeID);
- // update like counter
- var cumulativeLikes = data.likes - data.dislikes;
- var content = '<span class="icon icon16 fa-thumbs-o-' + (cumulativeLikes < 0 ? 'down' : 'up' ) + '"></span><span class="wcfLikeValue">';
- if (cumulativeLikes > 0) {
- content += '+' + StringUtil.addThousandsSeparator(cumulativeLikes);
- data.badge.classList.add('likeCounterLiked');
- }
- else if (cumulativeLikes < 0) {
- // U+2212 = minus sign
- content += '\u2212' + StringUtil.addThousandsSeparator(Math.abs(cumulativeLikes));
- data.badge.classList.add('likeCounterDisliked');
- }
- else {
- // U+00B1 = plus-minus sign
- content += '\u00B1' + '0';
- }
-
- data.badge.innerHTML = content + '</span>';
- data.badge.setAttribute('data-tooltip', Language.get('wcf.like.tooltip', {
- dislikes: data.dislikes,
- likes: data.likes
- }));
- }
- },
-
- /**
- * Updates the like summary.
- *
- * @param {Element} element container element
- */
- _updateSummary: function(element) {
- var data = this._containers.get(element);
-
- if (data.likes) {
- elShow(data.summary.parentNode);
-
- var usernames = [];
- var keys = Object.keys(data.users);
- for (var i = 0, length = keys.length; i < length; i++) {
- usernames.push(data.users[keys[i]]);
- }
-
- var others = data.likes - usernames.length;
- data.summary.innerHTML = Language.get('wcf.like.summary', { users: usernames, others: others });
- }
- else {
- elHide(data.summary.parentNode);
+ button.classList.add("active");
}
- },
-
- /**
- * Updates the active like/dislike button state.
- *
- * @param {Element} element container element
- */
- _updateActiveState: function(element) {
- var data = this._containers.get(element);
- var likeTarget = (this._options.markListItemAsActive) ? data.likeButton.parentNode : data.likeButton;
- likeTarget.classList.remove('active');
-
- if (data.liked === 1) {
- likeTarget.classList.add('active');
- }
+ button.appendChild(icon);
- if (this._options.canDislike) {
- var dislikeTarget = (this._options.markListItemAsActive) ? data.dislikeButton.parentNode : data.dislikeButton;
- dislikeTarget.classList.remove('active');
-
- if (data.liked === -1) {
- dislikeTarget.classList.add('active');
- }
- }
- },
-
- /**
- * Likes or dislikes an element.
- *
- * @param {Element} element container element
- * @param {object} event event object
- */
- _like: function(element, event) {
- event.preventDefault();
+ var invisibleText = elCreate("span");
+ invisibleText.className = "invisible";
+ invisibleText.innerHTML = title;
- if (_isBusy) {
- return;
- }
+ button.appendChild(document.createTextNode (" "));
+ button.appendChild(invisibleText);
- _isBusy = true;
+ listItem.appendChild(button);
- Ajax.api(this, {
- actionName: elData(event.currentTarget, 'type'),
- parameters: {
- data: {
- containerID: DomUtil.identify(element),
- objectID: this._containers.get(element).objectId,
- objectType: this._objectType
- }
- }
- });
- },
-
- _ajaxSuccess: function(data) {
- var element = elById(data.returnValues.containerID);
- var elementData = this._containers.get(element);
- if (elementData === undefined) {
- return;
+ if (insertBefore) {
+ insertBefore.parentNode.insertBefore(listItem, insertBefore);
}
-
- elementData.dislikes = ~~data.returnValues.dislikes;
- elementData.likes = ~~data.returnValues.likes;
-
- var users = data.returnValues.users;
- elementData.users = [];
- var keys = Object.keys(users);
- for (var i = 0, length = keys.length; i < length; i++) {
- elementData.users.push(StringUtil.escapeHTML(users[keys[i]].username));
+ else {
+ appendTo.appendChild(listItem);
}
- if (data.returnValues.isLiked == 1) elementData.liked = 1;
- else if (data.returnValues.isDisliked == 1) elementData.liked = -1;
- else elementData.liked = 0;
-
- // update label
- this._updateBadge(element);
-
- // update summary
- if (this._options.canViewSummary) this._updateSummary(element);
-
- // mark button as active
- this._updateActiveState(element);
-
- // invalidate cache for like details
- this._details['delete'](element);
-
- _isBusy = false;
- },
-
- _ajaxSetup: function() {
- return {
- data: {
- className: 'wcf\\data\\like\\LikeAction'
- }
- };
+ return button;
}
};
}
/**
- * Returns the first 3 users who liked this object.
+ * Since version 3.2, this method returns all reactionCounts for the different reactionTypes,
+ * instead of the user (as the method name suggests). This behavior is intentional and helps
+ * to establish backward compatibility.
*
- * @return User[]
- * @deprecated since 3.2, this value is no longer maintained
+ * @return mixed[]
+ * @deprecated since 3.2
*/
public function getUsers() {
- return $this->users;
+ $returnValues = [];
+
+ foreach ($this->getReactions() as $reactionID => $reaction) {
+ $returnValues[] = (object) [
+ 'userID' => $reactionID,
+ 'username' => $reaction['reactionCount'],
+ ];
+ }
+
+ // this value is only set, if the object was loaded over the ReactionHandler::loadLikeObjects()
+ if ($this->reactionTypeID) {
+ $returnValues[] = (object) [
+ 'userID' => 'reactionTypeID',
+ 'username' => $this->reactionTypeID,
+ ];
+ }
+
+ return $returnValues;
}
/**
namespace wcf\system\like;
use wcf\data\like\object\ILikeObject;
use wcf\data\like\object\LikeObject;
-use wcf\data\like\object\LikeObjectEditor;
-use wcf\data\like\object\LikeObjectList;
use wcf\data\like\Like;
-use wcf\data\like\LikeEditor;
-use wcf\data\like\LikeList;
use wcf\data\object\type\ObjectType;
-use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\reaction\type\ReactionType;
+use wcf\data\reaction\type\ReactionTypeCache;
use wcf\data\user\User;
-use wcf\data\user\UserEditor;
-use wcf\system\database\util\PreparedStatementConditionBuilder;
-use wcf\system\database\DatabaseException;
-use wcf\system\user\activity\event\UserActivityEventHandler;
-use wcf\system\user\activity\point\UserActivityPointHandler;
-use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\reaction\ReactionHandler;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
* Creates a new LikeHandler instance.
*/
protected function init() {
- // load cache
- $this->cache = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.like.likeableObject');
+ // does nothing
}
/**
* @return ObjectType
*/
public function getObjectType($objectName) {
- if (isset($this->cache[$objectName])) {
- return $this->cache[$objectName];
- }
-
- return null;
+ return ReactionHandler::getInstance()->getObjectType($objectName);
}
/**
* @return LikeObject|null
*/
public function getLikeObject(ObjectType $objectType, $objectID) {
- if (isset($this->likeObjectCache[$objectType->objectTypeID][$objectID])) {
- return $this->likeObjectCache[$objectType->objectTypeID][$objectID];
- }
-
- return null;
+ return ReactionHandler::getInstance()->getLikeObject($objectType, $objectID);
}
/**
* @return LikeObject[]
*/
public function getLikeObjects(ObjectType $objectType) {
- if (isset($this->likeObjectCache[$objectType->objectTypeID])) {
- return $this->likeObjectCache[$objectType->objectTypeID];
- }
-
- return [];
+ return ReactionHandler::getInstance()->getLikeObjects($objectType);
}
/**
* @return integer
*/
public function loadLikeObjects(ObjectType $objectType, array $objectIDs) {
- if (empty($objectIDs)) {
- return 0;
- }
-
- $i = 0;
-
- $conditions = new PreparedStatementConditionBuilder();
- $conditions->add("like_object.objectTypeID = ?", [$objectType->objectTypeID]);
- $conditions->add("like_object.objectID IN (?)", [$objectIDs]);
- $parameters = $conditions->getParameters();
-
- if (WCF::getUser()->userID) {
- $sql = "SELECT like_object.*,
- CASE WHEN like_table.userID IS NOT NULL THEN like_table.likeValue ELSE 0 END AS liked
- FROM wcf".WCF_N."_like_object like_object
- LEFT JOIN wcf".WCF_N."_like like_table
- ON (like_table.objectTypeID = like_object.objectTypeID
- AND like_table.objectID = like_object.objectID
- AND like_table.userID = ?)
- ".$conditions;
-
- array_unshift($parameters, WCF::getUser()->userID);
- }
- else {
- $sql = "SELECT like_object.*, 0 AS liked
- FROM wcf".WCF_N."_like_object like_object
- ".$conditions;
- }
-
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute($parameters);
- while ($row = $statement->fetchArray()) {
- $this->likeObjectCache[$objectType->objectTypeID][$row['objectID']] = new LikeObject(null, $row);
- $i++;
- }
-
- return $i;
+ return ReactionHandler::getInstance()->loadLikeObjects($objectType, $objectIDs);
}
/**
* @return array
*/
public function like(ILikeObject $likeable, User $user, $likeValue, $time = TIME_NOW) {
- // verify if object is already liked by user
- $like = Like::getLike($likeable->getObjectType()->objectTypeID, $likeable->getObjectID(), $user->userID);
-
- // get like object
- $likeObject = LikeObject::getLikeObject($likeable->getObjectType()->objectTypeID, $likeable->getObjectID());
+ if ($likeValue == 1) {
+ $reactionTypeID = ReactionHandler::getInstance()->getLegacyReactionTypeID(ReactionType::REACTION_TYPE_POSITIVE);
+ }
+ else {
+ $reactionTypeID = ReactionHandler::getInstance()->getLegacyReactionTypeID(ReactionType::REACTION_TYPE_NEGATIVE);
+ }
- // if vote is identically just revert the vote
- if ($like->likeID && ($like->likeValue == $likeValue)) {
- return $this->revertLike($like, $likeable, $likeObject, $user);
+ if ($reactionTypeID === null) {
+ return [
+ 'data' => [],
+ 'like' => 0,
+ 'newValue' => 0,
+ 'oldValue' => 0,
+ 'users' => []
+ ];
}
- // like data
- /** @noinspection PhpUnusedLocalVariableInspection */
- $cumulativeLikes = 0;
- /** @noinspection PhpUnusedLocalVariableInspection */
- $newValue = $oldValue = null;
- $users = [];
- try {
- WCF::getDB()->beginTransaction();
-
- // update existing object
- if ($likeObject->likeObjectID) {
- $likes = $likeObject->likes;
- $dislikes = $likeObject->dislikes;
- $cumulativeLikes = $likeObject->cumulativeLikes;
-
- // previous (dis-)like already exists
- if ($like->likeID) {
- $oldValue = $like->likeValue;
-
- // revert like and replace it with a dislike
- if ($like->likeValue == Like::LIKE) {
- $likes--;
- $dislikes++;
- $cumulativeLikes -= 2;
- $newValue = Like::DISLIKE;
- }
- else {
- // revert dislike and replace it with a like
- $likes++;
- $dislikes--;
- $cumulativeLikes += 2;
- $newValue = Like::LIKE;
- }
- }
- else {
- if ($likeValue == Like::LIKE) {
- $likes++;
- $cumulativeLikes++;
- $newValue = Like::LIKE;
- }
- else {
- $dislikes++;
- $cumulativeLikes--;
- $newValue = Like::DISLIKE;
- }
- }
-
- // build update date
- $updateData = [
- 'likes' => $likes,
- 'dislikes' => $dislikes,
- 'cumulativeLikes' => $cumulativeLikes
- ];
-
- if ($likeValue == 1) {
- $users = unserialize($likeObject->cachedUsers);
- if (count($users) < 3) {
- $users[$user->userID] = ['userID' => $user->userID, 'username' => $user->username];
- $updateData['cachedUsers'] = serialize($users);
- }
- }
- else if ($likeValue == -1) {
- $users = unserialize($likeObject->cachedUsers);
- if (isset($users[$user->userID])) {
- unset($users[$user->userID]);
- $updateData['cachedUsers'] = serialize($users);
- }
- }
-
- // update data
- $likeObjectEditor = new LikeObjectEditor($likeObject);
- $likeObjectEditor->update($updateData);
- }
- else {
- $cumulativeLikes = $likeValue;
- $newValue = $likeValue;
- $users = [];
- if ($likeValue == 1) $users = [$user->userID => ['userID' => $user->userID, 'username' => $user->username]];
-
- // create cache
- $likeObject = LikeObjectEditor::create([
- 'objectTypeID' => $likeable->getObjectType()->objectTypeID,
- 'objectID' => $likeable->getObjectID(),
- 'objectUserID' => $likeable->getUserID() ?: null,
- 'likes' => ($likeValue == Like::LIKE) ? 1 : 0,
- 'dislikes' => ($likeValue == Like::DISLIKE) ? 1 : 0,
- 'cumulativeLikes' => $cumulativeLikes,
- 'cachedUsers' => serialize($users)
- ]);
- }
-
- // update owner's like counter
- if ($likeable->getUserID()) {
- if ($like->likeID) {
- $userEditor = new UserEditor(new User($likeable->getUserID()));
- $userEditor->updateCounters([
- 'likesReceived' => $like->likeValue == Like::LIKE ? -1 : 1
- ]);
- }
- else if ($likeValue == Like::LIKE) {
- $userEditor = new UserEditor(new User($likeable->getUserID()));
- $userEditor->updateCounters([
- 'likesReceived' => 1
- ]);
- }
- }
-
- if (!$like->likeID) {
- // save like
- $like = LikeEditor::create([
- 'objectID' => $likeable->getObjectID(),
- 'objectTypeID' => $likeable->getObjectType()->objectTypeID,
- 'objectUserID' => $likeable->getUserID() ?: null,
- 'userID' => $user->userID,
- 'time' => $time,
- 'likeValue' => $likeValue
- ]);
-
- if ($likeValue == Like::LIKE && $likeable->getUserID()) {
- UserActivityPointHandler::getInstance()->fireEvent('com.woltlab.wcf.like.activityPointEvent.receivedLikes', $like->likeID, $likeable->getUserID());
- $likeable->sendNotification($like);
- }
- }
- else {
- $likeEditor = new LikeEditor($like);
- $likeEditor->update([
- 'time' => $time,
- 'likeValue' => $likeValue
- ]);
-
- if ($likeable->getUserID()) {
- if ($likeValue == Like::DISLIKE) {
- UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', [$likeable->getUserID() => 1]);
- }
- else {
- UserActivityPointHandler::getInstance()->fireEvent('com.woltlab.wcf.like.activityPointEvent.receivedLikes', $like->likeID, $likeable->getUserID());
- $likeable->sendNotification($like);
- }
- }
- }
-
- // update object's like counter
- $likeable->updateLikeCounter($cumulativeLikes);
-
- WCF::getDB()->commitTransaction();
+ $reactData = ReactionHandler::getInstance()->react($likeable, $user, $reactionTypeID, $time);
+ if ($reactData['reactionTypeID'] === null) {
+ $newValue = 0;
}
- catch (DatabaseException $e) {
- WCF::getDB()->rollBackTransaction();
+ else if (ReactionTypeCache::getInstance()->getReactionTypeByID($reactData['reactionTypeID'])->type == ReactionType::REACTION_TYPE_NEGATIVE) {
+ $newValue = -1;
+ }
+ else {
+ $newValue = 1;
}
return [
- 'data' => $this->loadLikeStatus($likeObject, $user),
- 'like' => $like,
+ 'data' => $this->loadLikeStatus($reactData['likeObject'], $user),
+ 'like' => $reactData['likeObject'],
'newValue' => $newValue,
- 'oldValue' => $oldValue,
- 'users' => $users
+ 'oldValue' => 0, // this value is currently a dummy value, maybe determine a real value
+ 'users' => []
];
}
* @return array
*/
public function revertLike(Like $like, ILikeObject $likeable, LikeObject $likeObject, User $user) {
- $usersArray = [];
-
- try {
- WCF::getDB()->beginTransaction();
-
- // delete like
- $editor = new LikeEditor($like);
- $editor->delete();
-
- // update like object cache
- $likes = $likeObject->likes;
- $dislikes = $likeObject->dislikes;
- $cumulativeLikes = $likeObject->cumulativeLikes;
-
- if ($like->likeValue == Like::LIKE) {
- $likes--;
- $cumulativeLikes--;
- }
- else {
- $dislikes--;
- $cumulativeLikes++;
- }
-
- // build update data
- $updateData = [
- 'likes' => $likes,
- 'dislikes' => $dislikes,
- 'cumulativeLikes' => $cumulativeLikes
- ];
-
- $users = $likeObject->getUsers();
- foreach ($users as $user2) {
- $usersArray[$user2->userID] = ['userID' => $user2->userID, 'username' => $user2->username];
- }
-
- if (isset($usersArray[$user->userID])) {
- unset($usersArray[$user->userID]);
- $updateData['cachedUsers'] = serialize($usersArray);
- }
-
- $likeObjectEditor = new LikeObjectEditor($likeObject);
- if (!$updateData['likes'] && !$updateData['dislikes']) {
- // remove object instead
- $likeObjectEditor->delete();
- }
- else {
- // update data
- $likeObjectEditor->update($updateData);
- }
-
- // update owner's like counter and activity points
- if ($likeable->getUserID()) {
- if ($like->likeValue == Like::LIKE) {
- $userEditor = new UserEditor(new User($likeable->getUserID()));
- $userEditor->updateCounters([
- 'likesReceived' => -1
- ]);
-
- UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', [$likeable->getUserID() => 1]);
- }
- }
-
- // update object's like counter
- $likeable->updateLikeCounter($cumulativeLikes);
-
- WCF::getDB()->commitTransaction();
- }
- catch (DatabaseException $e) {
- WCF::getDB()->rollBackTransaction();
- }
+ $reactData = ReactionHandler::getInstance()->revertReact($like, $likeable, $likeObject, $user);
return [
- 'data' => $this->loadLikeStatus($likeObject, $user),
- 'newValue' => null,
- 'oldValue' => $like->likeValue,
- 'users' => $usersArray
+ 'data' => $this->loadLikeStatus($reactData['likeObject'], $user),
+ 'like' => null,
+ 'newValue' => 0,
+ 'oldValue' => 0, // this value is currently a dummy value, maybe determine a real value
+ 'users' => []
];
}
* @param string[] $notificationObjectTypes
*/
public function removeLikes($objectType, array $objectIDs, array $notificationObjectTypes = []) {
- $objectTypeObj = $this->getObjectType($objectType);
-
- // get like objects
- $likeObjectList = new LikeObjectList();
- $likeObjectList->getConditionBuilder()->add('like_object.objectTypeID = ?', [$objectTypeObj->objectTypeID]);
- $likeObjectList->getConditionBuilder()->add('like_object.objectID IN (?)', [$objectIDs]);
- $likeObjectList->readObjects();
- $likeObjects = $likeObjectList->getObjects();
- $likeObjectIDs = $likeObjectList->getObjectIDs();
-
- // reduce count of received users
- $users = [];
- foreach ($likeObjects as $likeObject) {
- if ($likeObject->likes) {
- if (!isset($users[$likeObject->objectUserID])) $users[$likeObject->objectUserID] = 0;
- $users[$likeObject->objectUserID] += $likeObject->likes;
- }
- }
- foreach ($users as $userID => $receivedLikes) {
- $userEditor = new UserEditor(new User(null, ['userID' => $userID]));
- $userEditor->updateCounters([
- 'likesReceived' => $receivedLikes * -1
- ]);
- }
-
- // get like ids
- $likeList = new LikeList();
- $likeList->getConditionBuilder()->add('like_table.objectTypeID = ?', [$objectTypeObj->objectTypeID]);
- $likeList->getConditionBuilder()->add('like_table.objectID IN (?)', [$objectIDs]);
- $likeList->readObjects();
-
- if (count($likeList)) {
- $likeData = [];
- foreach ($likeList as $like) {
- $likeData[$like->likeID] = $like->userID;
- }
-
- // delete like notifications
- if (!empty($notificationObjectTypes)) {
- foreach ($notificationObjectTypes as $notificationObjectType) {
- UserNotificationHandler::getInstance()->removeNotifications($notificationObjectType, $likeList->getObjectIDs());
- }
- }
- else if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.notification')) {
- UserNotificationHandler::getInstance()->removeNotifications($objectType.'.notification', $likeList->getObjectIDs());
- }
-
- // revoke activity points
- UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', $likeData);
-
- // delete likes
- LikeEditor::deleteAll(array_keys($likeData));
- }
-
- // delete like objects
- if (!empty($likeObjectIDs)) {
- LikeObjectEditor::deleteAll($likeObjectIDs);
- }
-
- // delete activity events
- if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.recentActivityEvent')) {
- UserActivityEventHandler::getInstance()->removeEvents($objectTypeObj->objectType.'.recentActivityEvent', $objectIDs);
- }
+ ReactionHandler::getInstance()->removeReacts($objectType, $objectIDs, $notificationObjectTypes);
}
/**
if (WCF::getUser()->userID) {
$sql = "SELECT like_object.*,
- COALESCE(like_table.reactionTypeID, 0) AS reactionTypeID
+ COALESCE(like_table.reactionTypeID, 0) AS reactionTypeID,
+ COALESCE(like_table.likeValue, 0) AS liked
FROM wcf".WCF_N."_like_object like_object
LEFT JOIN wcf".WCF_N."_like like_table
ON (like_table.objectTypeID = like_object.objectTypeID
return [
'cachedReactions' => $likeObjectData['cachedReactions'],
- 'reactionTypeID' => $reactionTypeID
+ 'reactionTypeID' => $reactionTypeID,
+ 'likeObject' => $likeObjectData['likeObject']
];
}
catch (DatabaseQueryException $e) {
// @TODO return some dummy values
return [
'cachedReactions' => [],
- 'reactionTypeID' => null
];
}
];
// create cache
- LikeObjectEditor::create([
+ $likeObject = LikeObjectEditor::create([
'objectTypeID' => $likeable->getObjectType()->objectTypeID,
'objectID' => $likeable->getObjectID(),
'objectUserID' => $likeable->getUserID() ?: null,
return [
'cumulativeLikes' => $cumulativeLikes,
- 'cachedReactions' => $cachedReactions
+ 'cachedReactions' => $cachedReactions,
+ 'likeObject' => $likeObject
];
}
return [
'cachedReactions' => $likeObjectData['cachedReactions'],
- 'reactionTypeID' => null
+ 'reactionTypeID' => null,
+ 'likeObject' => $likeObjectData['likeObject']
];
}
catch (DatabaseQueryException $e) {
// @TODO return some dummy values
return [
'cachedReactions' => [],
- 'reactionTypeID' => null
+ 'reactionTypeID' => null,
+ 'likeObject' => []
];
}
return [
'cumulativeLikes' => $cumulativeLikes,
- 'cachedReactions' => $cachedReactions
+ 'cachedReactions' => $cachedReactions,
+ 'likeObject' => $likeObject
];
}
*/
protected function loadLikeStatus(LikeObject $likeObject, User $user) {
$sql = "SELECT like_object.likes, like_object.dislikes, like_object.cumulativeLikes,
- COALESCE(like_table.reactionTypeID, 0) AS reactionTypeID
+ COALESCE(like_table.reactionTypeID, 0) AS reactionTypeID,
+ COALESCE(like_table.likeValue, 0) AS liked
FROM wcf".WCF_N."_like_object like_object
LEFT JOIN wcf".WCF_N."_like like_table
ON (like_table.objectTypeID = ?
return $statement->fetchArray();
}
+
+ /**
+ * Returns the legacy reactionTypeID for a specific type. The given type must be
+ * ReactionType::REACTION_TYPE_POSITIVE for a like
+ * or ReactionType::REACTION_TYPE_NEGATIVE for a dislike
+ * other values are not allowed and will resulted in a LogicException.
+ * If there are no legacy reaction type, the method returns null.
+ *
+ * @param integer $type
+ * @return integer|null
+ */
+ public function getLegacyReactionTypeID($type) {
+ // @TODO determine real values
+ switch ($type) {
+ case ReactionType::REACTION_TYPE_POSITIVE:
+ return 2;
+
+ case ReactionType::REACTION_TYPE_NEGATIVE:
+ return 1;
+
+ default:
+ throw new \LogicException('Invalid type given.');
+ }
+ }
}
}
img.reactionType {
- width: 24px;
- height: 24px;
+ width: 16px;
+ height: 16px;
}
.reactionPopoverContent {
}
}
+ > ul > li.reactionTypeButton > img {
+ width: 24px;
+ height: 24px;
+ }
+
> ul > li.reactionTypeButton > img:hover {
transform: scale(1.2);
}
padding: 0px;
}
}
-
-ul.buttonList.iconList {
- img.reactionType {
- width: 16px;
- height: 16px;
- }
-}
-
-li.notificationItem {
- img.reactionType {
- width: 16px;
- height: 16px;
- }
-}
\ No newline at end of file