diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java index 4bccf27413..6a680ad26f 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java @@ -1,7 +1,8 @@ package se.su.dsv.scipro.forum; import java.io.Serializable; -import java.util.*; +import java.util.List; +import java.util.Set; import se.su.dsv.scipro.forum.dataobjects.ForumPost; import se.su.dsv.scipro.forum.dataobjects.ForumThread; import se.su.dsv.scipro.system.User; @@ -20,4 +21,6 @@ public interface BasicForumService extends Serializable { List getPosts(ForumThread forumThread); ForumThread createThread(String subject); + + long countUnreadThreads(List forumThreadList, User user); } diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java index 8da3bb70d6..c48e3cab91 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java @@ -87,6 +87,11 @@ public class BasicForumServiceImpl implements BasicForumService { return threadRepository.save(forumThread); } + @Override + public long countUnreadThreads(List forumThreadList, User user) { + return postRepository.countUnreadThreads(forumThreadList, user); + } + private ForumPostReadState getReadState(final User user, final ForumPost post) { ForumPostReadState state = readStateRepository.find(user, post); if (state == null) { diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java index f557fd70a6..e5d975add4 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java @@ -8,6 +8,7 @@ import se.su.dsv.scipro.forum.dataobjects.ProjectThread; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.system.JpaRepository; import se.su.dsv.scipro.system.QueryDslPredicateExecutor; +import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.Pair; @Transactional @@ -15,4 +16,6 @@ public interface ForumPostRepository extends JpaRepository, Que List findByThread(ForumThread forumThread); List> latestPost(Project project, int amount); + + long countUnreadThreads(List forumThreadList, User user); } diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java index 207d02c9d5..f43a0147bb 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java @@ -2,19 +2,22 @@ package se.su.dsv.scipro.forum; import static com.querydsl.core.types.dsl.Expressions.allOf; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQuery; import jakarta.inject.Inject; import jakarta.inject.Provider; import jakarta.persistence.EntityManager; -import java.util.*; +import java.util.List; import se.su.dsv.scipro.forum.dataobjects.ForumPost; import se.su.dsv.scipro.forum.dataobjects.ForumThread; import se.su.dsv.scipro.forum.dataobjects.ProjectThread; import se.su.dsv.scipro.forum.dataobjects.QForumPost; +import se.su.dsv.scipro.forum.dataobjects.QForumPostReadState; import se.su.dsv.scipro.forum.dataobjects.QForumThread; import se.su.dsv.scipro.forum.dataobjects.QProjectThread; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.system.GenericRepo; +import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.Pair; public class ForumPostRepositoryImpl extends GenericRepo implements ForumPostRepository { @@ -44,4 +47,24 @@ public class ForumPostRepositoryImpl extends GenericRepo implem .map(tuple -> new Pair<>(tuple.get(QProjectThread.projectThread), tuple.get(QForumPost.forumPost))) .toList(); } + + @Override + public long countUnreadThreads(List forumThreadList, User user) { + return new JPAQuery<>(em()) + .select(QForumThread.forumThread.id.countDistinct()) + .from(QForumThread.forumThread) + .leftJoin(QForumThread.forumThread.posts, QForumPost.forumPost) + .where( + QForumPost.forumPost.notIn( + JPAExpressions.select(QForumPostReadState.forumPostReadState.id.post) + .from(QForumPostReadState.forumPostReadState) + .where( + QForumPostReadState.forumPostReadState.read.isTrue(), + QForumPostReadState.forumPostReadState.id.user.eq(user) + ) + ), + QForumThread.forumThread.in(forumThreadList) + ) + .fetchOne(); + } } diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java index c8f9c3e020..a6ac11ebcd 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java @@ -23,5 +23,5 @@ public interface ProjectForumService { // TODO: Get these away from here List> latestPost(Project a, int amount); - boolean hasUnreadThreads(Project project, User user); + long getUnreadThreadsCount(Project project, User user); } diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java index 5de733222a..0c6b65227f 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java @@ -3,7 +3,8 @@ package se.su.dsv.scipro.forum; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import java.util.*; +import java.util.List; +import java.util.Set; import se.su.dsv.scipro.file.FileSource; import se.su.dsv.scipro.file.ProjectFileService; import se.su.dsv.scipro.forum.dataobjects.ForumPost; @@ -114,14 +115,10 @@ public class ProjectForumServiceImpl implements ProjectForumService { } @Override - public boolean hasUnreadThreads(Project project, User user) { + public long getUnreadThreadsCount(Project project, User user) { List threads = getThreads(project); - for (ProjectThread thread : threads) { - if (!basicForumService.isThreadRead(user, thread.getForumThread())) { - return true; - } - } - return false; + List list = threads.stream().map(ProjectThread::getForumThread).toList(); + return basicForumService.countUnreadThreads(list, user); } @Override diff --git a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java index 3f6a99dba8..321bc5b940 100644 --- a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java +++ b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java @@ -105,9 +105,9 @@ public class ProjectForumServiceImplTest extends ForumModuleTest { final ProjectThread thread = service.createThread(project, supervisor, "subject", "content", Set.of()); service.createReply(thread, author, "reply", Set.of()); - boolean hasUnreadThreads = service.hasUnreadThreads(project, supervisor); + long count = service.getUnreadThreadsCount(project, supervisor); - assertTrue(hasUnreadThreads); + assertEquals(1, count); } private void assertNewForumThread( diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html index 7e4d838121..0fb10bd9e4 100644 --- a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html +++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.html @@ -6,9 +6,7 @@ - - - + \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java index 92a80a83ef..54072c9532 100644 --- a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java +++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java @@ -10,10 +10,14 @@ import org.apache.wicket.markup.html.panel.Panel; public abstract class AbstractReadStatePanel extends Panel { - private final Component icon; + public static final String TOGGLE = "toggle"; + static final String ICON = "icon"; public AbstractReadStatePanel(final String id) { super(id); + Component icon = new UpdatingImage(ICON); + icon.setOutputMarkupId(true); + AjaxFallbackLink link = new AjaxFallbackLink<>(TOGGLE) { @Override public void onClick(final Optional target) { @@ -23,20 +27,15 @@ public abstract class AbstractReadStatePanel extends Panel { }); } }; - add(link); - - icon = new UpdatingImage(ICON); - icon.setOutputMarkupId(true); link.add(icon); + + add(link); } protected abstract boolean isRead(); protected abstract void onFlagClick(final AjaxRequestTarget target); - public static final String TOGGLE = "toggle"; - static final String ICON = "icon"; - private class UpdatingImage extends WebComponent { public UpdatingImage(String id) { diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html index 73ab571f7f..d171f8e3ba 100755 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html @@ -41,6 +41,13 @@
+ + + + + () + + \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java index 2b3c9dce94..cb76132906 100755 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java @@ -5,6 +5,7 @@ import static java.util.Arrays.asList; import jakarta.inject.Inject; import java.util.ArrayList; import java.util.List; +import org.apache.wicket.AttributeModifier; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; @@ -14,16 +15,22 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColu import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn; import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; +import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.EnumChoiceRenderer; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.LambdaChoiceRenderer; +import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LambdaModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; -import se.su.dsv.scipro.components.*; +import se.su.dsv.scipro.components.AjaxCheckBoxMultipleChoice; +import se.su.dsv.scipro.components.BootstrapRadioChoice; +import se.su.dsv.scipro.components.ExportableDataPanel; +import se.su.dsv.scipro.components.ListAdapterModel; +import se.su.dsv.scipro.components.TemporalColumn; import se.su.dsv.scipro.components.datatables.MultipleUsersColumn; import se.su.dsv.scipro.components.datatables.UserColumn; import se.su.dsv.scipro.dataproviders.FilteredDataProvider; @@ -250,25 +257,49 @@ public class SupervisorMyProjectsPanel extends Panel { @Override public void populateItem(Item> item, String id, IModel projectModel) { - item.add( - new AbstractReadStatePanel(id) { - @Override - protected boolean isRead() { - return !projectForumService.hasUnreadThreads( - projectModel.getObject(), - SciProSession.get().getUser() - ); - } + // Since table cell only can contain one item, we use Wicket Fragment here. It will contain two components, + // one for flag, one for unread messages counter. - @Override - protected void onFlagClick(AjaxRequestTarget target) { - setResponsePage( - SupervisorThreadedForumPage.class, - SupervisorThreadedForumPage.getPageParameters(projectModel.getObject()) - ); - } - } + Fragment fragment = new Fragment(id, "readStateColumnMarkupId", SupervisorMyProjectsPanel.this); + + long msgCount = projectForumService.getUnreadThreadsCount( + projectModel.getObject(), + SciProSession.get().getUser() ); + boolean isRead = msgCount == 0; + + AbstractReadStatePanel readStatePanel = new AbstractReadStatePanel("flag") { + @Override + protected boolean isRead() { + return isRead; + } + + @Override + protected void onFlagClick(AjaxRequestTarget target) { + setResponsePage( + SupervisorThreadedForumPage.class, + SupervisorThreadedForumPage.getPageParameters(projectModel.getObject()) + ); + } + }; + + if (!isRead) { + readStatePanel.add(new AttributeModifier("title", getString("unread.msg"))); + } + + fragment.add(readStatePanel); + + Label counterLabel = new Label("counter", msgCount) { + @Override + protected void onConfigure() { + super.onConfigure(); + setVisible(msgCount > 0); + } + }; + + fragment.add(counterLabel); + + item.add(fragment); } } } diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties index 17f454ad8c..e279982933 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties @@ -12,3 +12,5 @@ ProjectStatus.COMPLETED= Completed SupervisorProjectNoteDisplay.COMPACT=Compact SupervisorProjectNoteDisplay.FULL=Full + +unread.msg=There are unread messages. \ No newline at end of file