Forum Message UI Improvement (Thesis Board #3470) #61

Merged
tozh4728 merged 20 commits from 3470-forum-msg-ui-improvement into develop 2024-12-19 15:28:23 +01:00
9 changed files with 164 additions and 107 deletions
Showing only changes of commit 08f40959fd - Show all commits

View File

@ -5,10 +5,10 @@ import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.system.User;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.util.Set;
public interface BasicForumService extends Serializable {
ForumPost createReply(ForumThread forumThread, User poster, String content, Set<Attachment> attachments);
boolean setRead(User user, ForumPost post, boolean read);

View File

@ -1,20 +1,18 @@
package se.su.dsv.scipro.forum;
import jakarta.transaction.Transactional;
import se.su.dsv.scipro.system.JpaRepository;
import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
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.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;
import java.util.List;
@Transactional
public interface ForumPostRepository extends JpaRepository<ForumPost, Long>, QueryDslPredicateExecutor<ForumPost> {
List<ForumPost> findByThread(ForumThread forumThread);
List<Pair<ProjectThread, ForumPost>> latestPost(Project project, int amount);

View File

@ -1,10 +1,13 @@
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.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;
@ -17,11 +20,8 @@ import se.su.dsv.scipro.system.GenericRepo;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.util.Pair;
import java.util.List;
import static com.querydsl.core.types.dsl.Expressions.allOf;
public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implements ForumPostRepository {
@Inject
public ForumPostRepositoryImpl(Provider<EntityManager> em) {
super(em, ForumPost.class, QForumPost.forumPost);
@ -44,9 +44,7 @@ public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implem
.limit(amount)
.fetch()
.stream()
.map(tuple -> new Pair<>(
tuple.get(QProjectThread.projectThread),
tuple.get(QForumPost.forumPost)))
.map(tuple -> new Pair<>(tuple.get(QProjectThread.projectThread), tuple.get(QForumPost.forumPost)))
.toList();
}
@ -56,12 +54,17 @@ public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implem
.select(QForumThread.forumThread.id.countDistinct())
.from(QForumThread.forumThread)
.leftJoin(QForumThread.forumThread.posts, QForumPost.forumPost)
.where(QForumPost.forumPost.notIn(
.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();
.where(
QForumPostReadState.forumPostReadState.read.isTrue(),
QForumPostReadState.forumPostReadState.id.user.eq(user)
)
),
QForumThread.forumThread.in(forumThreadList)
)
.fetchOne();
}
}

View File

@ -127,9 +127,7 @@ public class ProjectForumServiceImpl implements ProjectForumService {
@Override
public long getUnreadThreadsCount(Project project, User user) {
List<ProjectThread> threads = getThreads(project);
List<ForumThread> list = threads.stream()
.map(ProjectThread::getForumThread)
.toList();
List<ForumThread> list = threads.stream().map(ProjectThread::getForumThread).toList();
return basicForumService.countUnreadThreads(list, user);
}

View File

@ -1,16 +1,14 @@
package se.su.dsv.scipro.forum;
import jakarta.transaction.Transactional;
import java.util.List;
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 java.util.List;
@Transactional
public interface ProjectThreadRepository extends JpaRepository<ProjectThread, Long>, QueryDslPredicateExecutor<ProjectThread> {
public interface ProjectThreadRepository
extends JpaRepository<ProjectThread, Long>, QueryDslPredicateExecutor<ProjectThread> {
List<ProjectThread> findByProject(Project project);
}

View File

@ -3,14 +3,14 @@ package se.su.dsv.scipro.forum;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
import java.util.List;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.GenericRepo;
import java.util.List;
public class ProjectThreadRepositoryImpl extends GenericRepo<ProjectThread, Long> implements ProjectThreadRepository {
@Inject
public ProjectThreadRepositoryImpl(Provider<EntityManager> em) {
super(em, ProjectThread.class, QProjectThread.projectThread);

View File

@ -1,5 +1,6 @@
package se.su.dsv.scipro.forum.panels;
import java.util.Optional;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
@ -7,8 +8,6 @@ import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.panel.Panel;
import java.util.Optional;
public abstract class AbstractReadStatePanel extends Panel {
public static final String TOGGLE = "toggle";
@ -16,7 +15,6 @@ public abstract class AbstractReadStatePanel extends Panel {
public AbstractReadStatePanel(final String id) {
super(id);
Component icon = new UpdatingImage(ICON);
icon.setOutputMarkupId(true);
@ -35,9 +33,11 @@ public abstract class AbstractReadStatePanel extends Panel {
}
protected abstract boolean isRead();
protected abstract void onFlagClick(final AjaxRequestTarget target);
private class UpdatingImage extends WebComponent {
public UpdatingImage(String id) {
super(id);
}

View File

@ -11,9 +11,7 @@ public abstract class NumberOfMessagesPanel extends Panel {
public NumberOfMessagesPanel(final String id) {
super(id);
add(new Label("msgCount", model));
}
@Override

View File

@ -1,10 +1,14 @@
package se.su.dsv.scipro.supervisor.panels;
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;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
@ -47,11 +51,6 @@ import se.su.dsv.scipro.system.ProjectType;
import se.su.dsv.scipro.system.ProjectTypeService;
import se.su.dsv.scipro.system.User;
import java.util.ArrayList;
import java.util.List;
import static java.util.Arrays.asList;
public class SupervisorMyProjectsPanel extends Panel {
public static final String FILTER_FORM = "filterForm";
@ -63,10 +62,13 @@ public class SupervisorMyProjectsPanel extends Panel {
@Inject
private ProjectTypeService projectTypeService;
@Inject
private ProjectService projectService;
@Inject
private UserProfileService profileService;
@Inject
private ProjectForumService projectForumService;
@ -82,7 +84,10 @@ public class SupervisorMyProjectsPanel extends Panel {
}
private void dataViewSetup() {
SortableDataProvider<Project, String> provider = new FilteredDataProvider<>(projectService, Model.of(this.filter));
SortableDataProvider<Project, String> provider = new FilteredDataProvider<>(
projectService,
Model.of(this.filter)
);
provider.setSort("dateCreated", SortOrder.DESCENDING);
dataPanel = new ExportableDataPanel<>(DATA_PANEL, createColumns(), provider);
add(dataPanel);
@ -95,14 +100,24 @@ public class SupervisorMyProjectsPanel extends Panel {
columns.add(new TemporalColumn<>(Model.of("Started"), "startDate", Project::getStartDate));
columns.add(new LambdaColumn<>(Model.of("Type"), "projectType.name", Project::getProjectTypeName));
columns.add(new ProjectTitleColumn(Model.of("Title"), "title"));
columns.add(new MultipleUsersColumn<>(Model.of("Authors")) {
columns.add(
new MultipleUsersColumn<>(Model.of("Authors")) {
@Override
public ListAdapterModel<User> getUsers(final IModel<Project> rowModel) {
return new ListAdapterModel<>(rowModel.map(Project::getProjectParticipants));
}
});
columns.add(new ProjectNoteColumn(Model.of("Note"), LoadableDetachableModel.of(this::currentUser), supervisorProjectNoteDisplayModel));
columns.add(new UserColumn<>(Model.of("Head supervisor"), "headSupervisor.fullName", Project::getHeadSupervisor));
}
);
columns.add(
new ProjectNoteColumn(
Model.of("Note"),
LoadableDetachableModel.of(this::currentUser),
supervisorProjectNoteDisplayModel
)
);
columns.add(
new UserColumn<>(Model.of("Head supervisor"), "headSupervisor.fullName", Project::getHeadSupervisor)
);
return columns;
}
@ -139,47 +154,87 @@ public class SupervisorMyProjectsPanel extends Panel {
public FilterForm(String id, IModel<ProjectService.Filter> filterParams) {
super(id, filterParams);
add(new AjaxCheckBox(SUPERVISOR, LambdaModel.of(filterParams, ProjectService.Filter::isFilterSupervisor, ProjectService.Filter::setFilterSupervisor)) {
add(
new AjaxCheckBox(
SUPERVISOR,
LambdaModel.of(
filterParams,
ProjectService.Filter::isFilterSupervisor,
ProjectService.Filter::setFilterSupervisor
)
) {
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(dataPanel);
updateProfileWithCurrentFilter();
}
});
add(new AjaxCheckBoxMultipleChoice<>(ROLE_FILTER, LambdaModel.of(filterParams, ProjectService.Filter::getRoles, ProjectService.Filter::setRoles), List.of(ProjectTeamMemberRoles.CO_SUPERVISOR), new EnumChoiceRenderer<>(this)) {
}
);
add(
new AjaxCheckBoxMultipleChoice<>(
ROLE_FILTER,
LambdaModel.of(filterParams, ProjectService.Filter::getRoles, ProjectService.Filter::setRoles),
List.of(ProjectTeamMemberRoles.CO_SUPERVISOR),
new EnumChoiceRenderer<>(this)
) {
@Override
public void onUpdate(AjaxRequestTarget target) {
target.add(dataPanel);
updateProfileWithCurrentFilter();
}
});
add(new AjaxCheckBoxMultipleChoice<>(STATUS_FILTER, LambdaModel.of(filterParams, ProjectService.Filter::getStatuses, ProjectService.Filter::setStatuses), asList(ProjectStatus.values()), new EnumChoiceRenderer<>(this)) {
}
);
add(
new AjaxCheckBoxMultipleChoice<>(
STATUS_FILTER,
LambdaModel.of(
filterParams,
ProjectService.Filter::getStatuses,
ProjectService.Filter::setStatuses
),
asList(ProjectStatus.values()),
new EnumChoiceRenderer<>(this)
) {
@Override
public void onUpdate(AjaxRequestTarget target) {
target.add(dataPanel);
updateProfileWithCurrentFilter();
}
});
add(new AjaxCheckBoxMultipleChoice<>(TYPE_FILTER, LambdaModel.of(filterParams, ProjectService.Filter::getProjectTypes, ProjectService.Filter::setProjectTypes), projectTypeService.findBySupervisorProjects(SciProSession.get().getUser()), new LambdaChoiceRenderer<>(ProjectType::getName, ProjectType::getId)) {
}
);
add(
new AjaxCheckBoxMultipleChoice<>(
TYPE_FILTER,
LambdaModel.of(
filterParams,
ProjectService.Filter::getProjectTypes,
ProjectService.Filter::setProjectTypes
),
projectTypeService.findBySupervisorProjects(SciProSession.get().getUser()),
new LambdaChoiceRenderer<>(ProjectType::getName, ProjectType::getId)
) {
@Override
public void onUpdate(AjaxRequestTarget target) {
target.add(dataPanel);
updateProfileWithCurrentFilter();
}
});
}
);
BootstrapRadioChoice<SupervisorProjectNoteDisplay> noteDisplay = new BootstrapRadioChoice<>(
"note_display",
supervisorProjectNoteDisplayModel,
List.of(SupervisorProjectNoteDisplay.values()),
new EnumChoiceRenderer<>(this));
noteDisplay.add(new AjaxFormChoiceComponentUpdatingBehavior() {
new EnumChoiceRenderer<>(this)
);
noteDisplay.add(
new AjaxFormChoiceComponentUpdatingBehavior() {
@Override
public void onUpdate(AjaxRequestTarget target) {
target.add(dataPanel);
updateProfileWithCurrentFilter();
}
});
}
);
add(noteDisplay);
}
@ -195,6 +250,7 @@ public class SupervisorMyProjectsPanel extends Panel {
}
private class ProjectForumStateColumn extends AbstractColumn<Project, String> {
public ProjectForumStateColumn(IModel<String> label) {
super(label);
}
@ -206,8 +262,10 @@ public class SupervisorMyProjectsPanel extends Panel {
Fragment fragment = new Fragment(id, "readStateColumnMarkupId", SupervisorMyProjectsPanel.this);
long msgCount = projectForumService.getUnreadThreadsCount(projectModel.getObject(),
SciProSession.get().getUser());
long msgCount = projectForumService.getUnreadThreadsCount(
projectModel.getObject(),
SciProSession.get().getUser()
);
boolean isRead = msgCount == 0;
AbstractReadStatePanel readStatePanel = new AbstractReadStatePanel("flag") {
@ -218,8 +276,10 @@ public class SupervisorMyProjectsPanel extends Panel {
@Override
protected void onFlagClick(AjaxRequestTarget target) {
setResponsePage(SupervisorThreadedForumPage.class,
SupervisorThreadedForumPage.getPageParameters(projectModel.getObject()));
setResponsePage(
SupervisorThreadedForumPage.class,
SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())
);
}
};
@ -229,12 +289,14 @@ public class SupervisorMyProjectsPanel extends Panel {
fragment.add(readStatePanel);
fragment.add(new NumberOfMessagesPanel("counter") {
fragment.add(
new NumberOfMessagesPanel("counter") {
@Override
public long getMessageCount() {
return msgCount;
}
});
}
);
item.add(fragment);
}