🎨 Add container for new posts/replies
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 1 Jul 2020 19:22:03 +0000 (21:22 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 1 Jul 2020 20:15:29 +0000 (22:15 +0200)
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/test/Guice.kt
src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt [new file with mode: 0644]

index 4650625..41ff052 100644 (file)
 package net.pterodactylus.sone.web;
 
 import static java.util.logging.Logger.getLogger;
-import static java.util.stream.Collectors.toSet;
 
 import java.util.Collection;
-import java.util.Set;
 import java.util.TimeZone;
 import java.util.logging.Logger;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import javax.inject.Named;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.core.ElementLoader;
@@ -41,10 +38,8 @@ import net.pterodactylus.sone.main.PluginHomepage;
 import net.pterodactylus.sone.main.PluginVersion;
 import net.pterodactylus.sone.main.PluginYear;
 import net.pterodactylus.sone.main.SonePlugin;
-import net.pterodactylus.sone.notify.ListNotification;
 import net.pterodactylus.sone.notify.ListNotificationFilter;
 import net.pterodactylus.sone.notify.PostVisibilityFilter;
-import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
 import net.pterodactylus.sone.template.LinkedElementRenderFilter;
 import net.pterodactylus.sone.template.ParserFilter;
 import net.pterodactylus.sone.template.RenderFilter;
@@ -89,7 +84,6 @@ import net.pterodactylus.util.web.TemplatePage;
 
 import com.codahale.metrics.*;
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 import freenet.clients.http.ToadletContext;
@@ -100,9 +94,6 @@ import freenet.clients.http.ToadletContext;
  */
 public class WebInterface implements SessionProvider {
 
-       /** The logger. */
-       private static final Logger logger = getLogger(WebInterface.class.getName());
-
        /** The loaders for templates, pages, and classpath providers. */
        private final Loaders loaders;
 
@@ -126,7 +117,6 @@ public class WebInterface implements SessionProvider {
 
        private final ListNotificationFilter listNotificationFilter;
        private final PostVisibilityFilter postVisibilityFilter;
-       private final ReplyVisibilityFilter replyVisibilityFilter;
 
        private final ElementLoader elementLoader;
        private final LinkedElementRenderFilter linkedElementRenderFilter;
@@ -138,37 +128,23 @@ public class WebInterface implements SessionProvider {
        private final Translation translation;
        private final SessionProvider sessionProvider;
 
-       /** The “new post” notification. */
-       private final ListNotification<Post> newPostNotification;
-
-       /** The “new reply” notification. */
-       private final ListNotification<PostReply> newReplyNotification;
-
-       /** The invisible “local post” notification. */
-       private final ListNotification<Post> localPostNotification;
-
-       /** The invisible “local reply” notification. */
-       private final ListNotification<PostReply> localReplyNotification;
+       private final NewElements newElements;
 
        @Inject
        public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter,
-                       PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter,
-                       ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
-                       TemplateRenderer templateRenderer,
-                       ParserFilter parserFilter, ShortenFilter shortenFilter,
-                       RenderFilter renderFilter,
-                       LinkedElementRenderFilter linkedElementRenderFilter,
-                       PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
-                       NotificationManager notificationManager, SessionProvider sessionProvider,
-                       @Named("newRemotePost") ListNotification<Post> newPostNotification,
-                       @Named("newRemotePostReply") ListNotification<PostReply> newReplyNotification,
-                       @Named("localPost") ListNotification<Post> localPostNotification,
-                       @Named("localReply") ListNotification<PostReply> localReplyNotification) {
+                                               PostVisibilityFilter postVisibilityFilter,
+                                               ElementLoader elementLoader, TemplateContextFactory templateContextFactory,
+                                               TemplateRenderer templateRenderer,
+                                               ParserFilter parserFilter, ShortenFilter shortenFilter,
+                                               RenderFilter renderFilter,
+                                               LinkedElementRenderFilter linkedElementRenderFilter,
+                                               PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter,
+                                               NotificationManager notificationManager, SessionProvider sessionProvider,
+                                               NewElements newElements) {
                this.sonePlugin = sonePlugin;
                this.loaders = loaders;
                this.listNotificationFilter = listNotificationFilter;
                this.postVisibilityFilter = postVisibilityFilter;
-               this.replyVisibilityFilter = replyVisibilityFilter;
                this.elementLoader = elementLoader;
                this.templateRenderer = templateRenderer;
                this.parserFilter = parserFilter;
@@ -181,10 +157,7 @@ public class WebInterface implements SessionProvider {
                this.translation = translation;
                this.notificationManager = notificationManager;
                this.sessionProvider = sessionProvider;
-               this.newPostNotification = newPostNotification;
-               this.newReplyNotification = newReplyNotification;
-               this.localPostNotification = localPostNotification;
-               this.localReplyNotification = localReplyNotification;
+               this.newElements = newElements;
                formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
 
                this.templateContextFactory = templateContextFactory;
@@ -260,20 +233,12 @@ public class WebInterface implements SessionProvider {
 
        @Nonnull
        public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
-               Set<Post> allNewPosts = ImmutableSet.<Post> builder()
-                               .addAll(newPostNotification.getElements())
-                               .addAll(localPostNotification.getElements())
-                               .build();
-               return allNewPosts.stream().filter(p -> postVisibilityFilter.isPostVisible(currentSone, p)).collect(toSet());
+               return newElements.getNewPosts();
        }
 
        @Nonnull
        public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
-               Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
-                               .addAll(newReplyNotification.getElements())
-                               .addAll(localReplyNotification.getElements())
-                               .build();
-               return allNewReplies.stream().filter(r -> replyVisibilityFilter.isReplyVisible(currentSone, r)).collect(toSet());
+               return newElements.getNewReplies();
        }
 
        //
diff --git a/src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt b/src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt
new file mode 100644 (file)
index 0000000..1026f36
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * Sone - NewElements.kt - Copyright © 2020 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.pterodactylus.sone.web
+
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.notify.ListNotification
+import net.pterodactylus.sone.notify.PostVisibilityFilter
+import net.pterodactylus.sone.notify.ReplyVisibilityFilter
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Container for new elements that should be shown in the web interface.
+ *
+ * This is just a wrapper around the notifications that store the new elements.
+ */
+class NewElements @Inject constructor(
+               @Named("newRemotePost") private val newPostNotification: ListNotification<Post>,
+               @Named("newRemotePostReply") private val newReplyNotification: ListNotification<PostReply>,
+               @Named("localPost") private val localPostNotification: ListNotification<Post>,
+               @Named("localReply") private val localReplyNotification: ListNotification<PostReply>,
+               private val postVisibilityFilter: PostVisibilityFilter,
+               private val replyVisibilityFilter: ReplyVisibilityFilter
+) {
+
+       val newPosts: Collection<Post>
+               get() = listOf(newPostNotification, localPostNotification)
+                               .flatMap(ListNotification<Post>::elements)
+                               .filter { postVisibilityFilter.isPostVisible(null, it) }
+
+       val newReplies: Collection<PostReply>
+               get() = listOf(newReplyNotification, localReplyNotification)
+                               .flatMap(ListNotification<PostReply>::elements)
+                               .filter { replyVisibilityFilter.isReplyVisible(null, it) }
+}
+
+private fun <R> Collection<*>.cast(): List<R> = map { it as R }
index b0bed5c..e6c0fdb 100644 (file)
@@ -1,20 +1,27 @@
 package net.pterodactylus.sone.test
 
-import com.google.inject.*
-import com.google.inject.name.*
-import org.hamcrest.MatcherAssert.*
-import org.hamcrest.Matchers.*
-import org.mockito.*
+import com.google.inject.Injector
+import com.google.inject.Key
+import com.google.inject.Module
+import com.google.inject.TypeLiteral
+import com.google.inject.name.Names
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.sameInstance
+import org.mockito.Mockito
 import javax.inject.Provider
-import kotlin.reflect.*
+import kotlin.reflect.KClass
 
 fun <T : Any> KClass<T>.isProvidedBy(instance: T) = Module { it.bind(this.java).toProvider(Provider<T> { instance }) }
 fun <T : Any> KClass<T>.withNameIsProvidedBy(instance: T, name: String) = Module { it.bind(this.java).annotatedWith(Names.named(name)).toProvider(Provider<T> { instance }) }
 fun <T : Any> KClass<T>.isProvidedBy(provider: com.google.inject.Provider<T>) = Module { it.bind(this.java).toProvider(provider) }
 fun <T : Any> KClass<T>.isProvidedBy(provider: KClass<out Provider<T>>) = Module { it.bind(this.java).toProvider(provider.java) }
+fun <T : Any> Key<T>.isProvidedBy(instance: T) = Module { it.bind(this).toProvider(Provider<T> { instance }) }
 inline fun <reified T : Any> KClass<T>.isProvidedByMock() = Module { it.bind(this.java).toProvider(Provider<T> { mock() }) }
 inline fun <reified T : Any> KClass<T>.isProvidedByDeepMock() = Module { it.bind(this.java).toProvider(Provider<T> { deepMock() }) }
 
+inline fun <reified T> key(): Key<T> = Key.get(object : TypeLiteral<T>() {})
+inline fun <reified T> key(annotation: Annotation): Key<T> = Key.get(object : TypeLiteral<T>() {}, annotation)
+
 inline fun <reified T : Any> Injector.getInstance(annotation: Annotation? = null): T = annotation
                ?.let { getInstance(Key.get(object : TypeLiteral<T>() {}, it)) }
                ?: getInstance(Key.get(object : TypeLiteral<T>() {}))
diff --git a/src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt
new file mode 100644 (file)
index 0000000..34dfe66
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Sone - NewElementsTest.kt - Copyright © 2020 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.pterodactylus.sone.web
+
+import com.google.inject.Guice
+import com.google.inject.name.Names.named
+import net.pterodactylus.sone.data.Post
+import net.pterodactylus.sone.data.PostReply
+import net.pterodactylus.sone.notify.ListNotification
+import net.pterodactylus.sone.notify.matchThisPost
+import net.pterodactylus.sone.notify.matchThisReply
+import net.pterodactylus.sone.notify.showAllPosts
+import net.pterodactylus.sone.notify.showAllReplies
+import net.pterodactylus.sone.test.createPost
+import net.pterodactylus.sone.test.createPostReply
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedBy
+import net.pterodactylus.sone.test.key
+import net.pterodactylus.util.template.Template
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.contains
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.junit.Test
+
+class NewElementsTest {
+
+       private val newPostNotification = ListNotification<Post>("", "", Template())
+       private val newReplyNotification = ListNotification<PostReply>("", "", Template())
+       private val localPostNotification = ListNotification<Post>("", "", Template())
+       private val localReplyNotification = ListNotification<PostReply>("", "", Template())
+       private val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, showAllPosts, showAllReplies)
+
+       init {
+               localPostNotification.add(post1)
+               newPostNotification.add(post2)
+               localReplyNotification.add(reply1)
+               newReplyNotification.add(reply2)
+       }
+
+       @Test
+       fun `new elements container can be created by guice`() {
+               val injector = Guice.createInjector(
+                               key<ListNotification<Post>>(named("newRemotePost")).isProvidedBy(newPostNotification),
+                               key<ListNotification<Post>>(named("localPost")).isProvidedBy(localPostNotification),
+                               key<ListNotification<PostReply>>(named("newRemotePostReply")).isProvidedBy(newReplyNotification),
+                               key<ListNotification<PostReply>>(named("localReply")).isProvidedBy(localReplyNotification)
+               )
+               injector.getInstance<NewElements>()
+       }
+
+       @Test
+       fun `new posts include new and local posts`() {
+               assertThat(newElements.newPosts, containsInAnyOrder(post1, post2))
+       }
+
+       @Test
+       fun `new posts are filtered`() {
+               val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, matchThisPost(post2), showAllReplies)
+               assertThat(newElements.newPosts, contains(post2))
+       }
+
+       @Test
+       fun `new replies include new and local replies`() {
+               assertThat(newElements.newReplies, containsInAnyOrder(reply1, reply2))
+       }
+
+       @Test
+       fun `new replies are filtered`() {
+               val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, showAllPosts, matchThisReply(reply2))
+               assertThat(newElements.newReplies, containsInAnyOrder(reply2))
+       }
+
+}
+
+private val post1 = createPost()
+private val post2 = createPost()
+private val reply1 = createPostReply()
+private val reply2 = createPostReply()