}
/**
- * Returns all Sones that have liked the given reply.
- *
- * @param reply
- * The reply to get the liking Sones for
- * @return The Sones that like the given reply
- */
- public Set<Sone> getLikes(PostReply reply) {
- Set<Sone> sones = new HashSet<Sone>();
- for (Sone sone : getSones()) {
- if (sone.getLikedReplyIds().contains(reply.getId())) {
- sones.add(sone);
- }
- }
- return sones;
- }
-
- /**
* Returns whether the given post is bookmarked.
*
* @param post
package net.pterodactylus.sone.data;
import java.util.Comparator;
+import java.util.Set;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
*/
public boolean isKnown();
+ void like(Sone localSone);
+ void unlike(Sone localSone);
+
+ boolean isLiked(Sone sone);
+ Set<Sone> getLikes();
+
Modifier<T> modify();
interface Modifier<T> {
Sone setLikeReplyIds(Set<String> likedReplyIds);
/**
- * Checks whether the given reply ID is liked by this Sone.
- *
- * @param replyId
- * The ID of the reply
- * @return {@code true} if this Sone likes the given reply, {@code false}
- * otherwise
- */
- boolean isLikedReplyId(String replyId);
-
- /**
- * Adds the given reply ID to the list of replies this Sone likes.
- *
- * @param replyId
- * The ID of the reply
- * @return This Sone (for method chaining)
- */
- Sone addLikedReplyId(String replyId);
-
- /**
- * Removes the given post ID from the list of replies this Sone likes.
- *
- * @param replyId
- * The ID of the reply
- * @return This Sone (for method chaining)
- */
- Sone removeLikedReplyId(String replyId);
-
- /**
* Returns the root album that contains all visible albums of this Sone.
*
* @return The root album of this Sone
package net.pterodactylus.sone.data.impl;
+import java.util.Set;
+
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.database.Database;
import com.google.common.base.Optional;
}
@Override
+ public void like(Sone localSone) {
+ database.likePostReply(this, localSone);
+ }
+
+ @Override
+ public void unlike(Sone localSone) {
+ database.unlikePostReply(this, localSone);
+ }
+
+ @Override
+ public boolean isLiked(Sone sone) {
+ return database.isLiked(this, sone);
+ }
+
+ @Override
+ public Set<Sone> getLikes() {
+ return database.getLikes(this);
+ }
+
+ @Override
public Modifier<PostReply> modify() {
return new Modifier<PostReply>() {
private boolean known = isKnown();
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
*/
void removePostReplies(Sone sone);
+ void likePostReply(PostReply postReply, Sone localSone);
+ void unlikePostReply(PostReply postReply, Sone localSone);
+
+ boolean isLiked(PostReply postReply, Sone sone);
+ Set<Sone> getLikes(PostReply postReply);
+
}
/** All post replies by their ID. */
private final Map<String, PostReply> allPostReplies = new HashMap<String, PostReply>();
+ private final SetMultimap<String, String> likedPostRepliesBySone = HashMultimap.create();
+ private final SetMultimap<String, String> postReplyLikingSones = HashMultimap.create();
/** Replies sorted by Sone. */
private final SortedSetMultimap<String, PostReply> sonePostReplies = TreeMultimap.create(new Comparator<String>() {
}
}
+ @Override
+ public void likePostReply(PostReply postReply, Sone localSone) {
+ lock.writeLock().lock();
+ try {
+ likedPostRepliesBySone.put(localSone.getId(), postReply.getId());
+ postReplyLikingSones.put(postReply.getId(), localSone.getId());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void unlikePostReply(PostReply postReply, Sone localSone) {
+ lock.writeLock().lock();
+ try {
+ likedPostRepliesBySone.remove(localSone.getId(), postReply.getId());
+ postReplyLikingSones.remove(postReply.getId(), localSone.getId());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean isLiked(PostReply postReply, Sone sone) {
+ lock.readLock().lock();
+ try {
+ return postReplyLikingSones.containsEntry(postReply.getId(), sone.getId());
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Set<Sone> getLikes(PostReply postReply) {
+ lock.readLock().lock();
+ try {
+ return from(postReplyLikingSones.get(postReply.getId())).transform(getSone()).transformAndConcat(this.<Sone>unwrap()).toSet();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
//
// POSTREPLYSTORE METHODS
//
public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
PostReply reply = getReply(parameters, "Reply");
Sone sone = getMandatoryLocalSone(parameters, "Sone");
- sone.addLikedReplyId(reply.getId());
- return new Response("ReplyLiked", new SimpleFieldSetBuilder().put("LikeCount", getCore().getLikes(reply).size()).get());
+ reply.like(sone);
+ return new Response("ReplyLiked", new SimpleFieldSetBuilder().put("LikeCount", reply.getLikes().size()).get());
}
}
public Object get(TemplateContext templateContext, Object object, String member) {
PostReply reply = (PostReply) object;
if ("likes".equals(member)) {
- return core.getLikes(reply);
+ return reply.getLikes();
} else if (member.equals("liked")) {
Sone currentSone = (Sone) templateContext.get("currentSone");
- return (currentSone != null) && (currentSone.isLikedReplyId(reply.getId()));
+ return (currentSone != null) && reply.isLiked(currentSone);
} else if (member.equals("new")) {
return !reply.isKnown();
} else if (member.equals("loaded")) {
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
post.get().like(currentSone);
}
} else if ("reply".equals(type)) {
- currentSone.addLikedReplyId(id);
+ Optional<PostReply> postReply = webInterface.getCore().getDatabase().getPostReply(id);
+ if (postReply.isPresent()) {
+ postReply.get().like(currentSone);
+ }
}
throw new RedirectException(returnPage);
}
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
post.get().unlike(currentSone);
}
} else if ("reply".equals(type)) {
- currentSone.removeLikedReplyId(id);
+ Optional<PostReply> postReply = webInterface.getCore().getDatabase().getPostReply(id);
+ if (postReply.isPresent()) {
+ postReply.get().unlike(currentSone);
+ }
}
throw new RedirectException(returnPage);
}
if (!reply.isPresent()) {
return createErrorJsonObject("invalid-reply-id");
}
- Set<Sone> sones = webInterface.getCore().getLikes(reply.get());
+ Set<Sone> sones = reply.get().getLikes();
return createSuccessJsonObject().put("likes", sones.size()).put("sones", getSones(sones));
}
return createErrorJsonObject("invalid-type");
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
}
webInterface.getCore().touchConfiguration();
} else if ("reply".equals(type)) {
- currentSone.addLikedReplyId(id);
+ Optional<PostReply> postReply = webInterface.getCore().getDatabase().getPostReply(id);
+ if (postReply.isPresent()) {
+ postReply.get().like(currentSone);
+ }
webInterface.getCore().touchConfiguration();
} else {
return createErrorJsonObject("invalid-type");
package net.pterodactylus.sone.web.ajax;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
}
webInterface.getCore().touchConfiguration();
} else if ("reply".equals(type)) {
- currentSone.removeLikedReplyId(id);
+ Optional<PostReply> postReply = webInterface.getCore().getDatabase().getPostReply(id);
+ if (postReply.isPresent()) {
+ postReply.get().unlike(currentSone);
+ }
webInterface.getCore().touchConfiguration();
} else {
return createErrorJsonObject("invalid-type");
private final Multimap<Post, PostReply> postReplies = create();
private final Multimap<String, Post> directedPosts = create();
private final SetMultimap<Post, Sone> postLikingSones = HashMultimap.create();
+ private final SetMultimap<PostReply, Sone> postReplyLikingSones = HashMultimap.create();
public final Database database;
public final Core core;
if (text.isPresent()) {
when(postReply.getText()).thenReturn(text.get());
}
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ postReplyLikingSones.put(postReply, (Sone) invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(postReply).like(Matchers.<Sone>any());
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ postReplyLikingSones.remove(postReply, invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(postReply).unlike(Matchers.<Sone>any());
+ when(postReply.getLikes()).thenAnswer(new Answer<Set<Sone>>() {
+ @Override
+ public Set<Sone> answer(InvocationOnMock invocation) throws Throwable {
+ return postReplyLikingSones.get(postReply);
+ }
+ });
return postReply;
}
}