aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--juick-core/src/main/java/com/juick/Message.java9
-rw-r--r--juick-server-core/src/main/java/com/juick/service/MessagesService.java4
-rw-r--r--juick-server-jdbc/src/main/java/com/juick/service/MessagesServiceImpl.java40
-rw-r--r--juick-server-jdbc/src/test/java/com/juick/service/MessageServiceTest.java4
-rw-r--r--juick-www/src/main/java/com/juick/www/controllers/Messages.java8
-rw-r--r--juick-www/src/test/java/com/juick/www/WebAppTests.java61
6 files changed, 101 insertions, 25 deletions
diff --git a/juick-core/src/main/java/com/juick/Message.java b/juick-core/src/main/java/com/juick/Message.java
index 4a693558..0d91cc63 100644
--- a/juick-core/src/main/java/com/juick/Message.java
+++ b/juick-core/src/main/java/com/juick/Message.java
@@ -42,6 +42,7 @@ public class Message implements Comparable {
private User user = null;
private final List<Tag> tags;
private Instant ts;
+ private Instant updated;
@XmlTransient
@JsonIgnore
public int TimeAgo = 0;
@@ -339,4 +340,12 @@ public class Message implements Comparable {
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
+
+ public Instant getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(Instant updated) {
+ this.updated = updated;
+ }
}
diff --git a/juick-server-core/src/main/java/com/juick/service/MessagesService.java b/juick-server-core/src/main/java/com/juick/service/MessagesService.java
index ee679ca4..b90cca79 100644
--- a/juick-server-core/src/main/java/com/juick/service/MessagesService.java
+++ b/juick-server-core/src/main/java/com/juick/service/MessagesService.java
@@ -64,7 +64,7 @@ public interface MessagesService {
List<Integer> getPrivate(int uid, int before);
- List<Integer> getDiscussions(int uid, int before);
+ List<Integer> getDiscussions(int uid, Long to);
List<Integer> getRecommended(int uid, int before);
@@ -88,7 +88,7 @@ public interface MessagesService {
List<Integer> getUserSearch(int UID, String search, int privacy, int before);
- List<com.juick.Message> getMessages(Collection<Integer> mids);
+ List<com.juick.Message> getMessages(List<Integer> mids);
List<com.juick.Message> getReplies(int mid);
diff --git a/juick-server-jdbc/src/main/java/com/juick/service/MessagesServiceImpl.java b/juick-server-jdbc/src/main/java/com/juick/service/MessagesServiceImpl.java
index de8b256e..d7067901 100644
--- a/juick-server-jdbc/src/main/java/com/juick/service/MessagesServiceImpl.java
+++ b/juick-server-jdbc/src/main/java/com/juick/service/MessagesServiceImpl.java
@@ -97,6 +97,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
msg.setRepliesBy(rs.getString(19));
msg.setText(rs.getString(20));
msg.setReplyQuote(MessageUtils.formatQuote(rs.getString(21)));
+ msg.setUpdated(rs.getTimestamp(22).toInstant());
try {
imagesService.setAttachmentMetadata(imgDir, baseImagesUrl, msg);
} catch (Exception e) {
@@ -305,7 +306,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
+ "messages.readonly, messages.privacy, messages.replies,"
+ "messages.attach, messages.place_id, messages.lat,"
+ "messages.lon, COUNT(favorites.user_id) as likes, messages.hidden,"
- + "txt.tags, txt.repliesby, txt.txt, '' as q FROM messages "
+ + "txt.tags, txt.repliesby, txt.txt, '' as q, messages.updated FROM messages "
+ "INNER JOIN users ON messages.user_id = users.id "
+ "INNER JOIN messages_txt AS txt "
+ "ON messages.message_id = txt.message_id "
@@ -511,18 +512,20 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
@Transactional(readOnly = true)
@Override
- public List<Integer> getDiscussions(final int uid, final int before) {
+ public List<Integer> getDiscussions(final int uid, final Long to) {
SqlParameterSource sqlParameterSource = new MapSqlParameterSource()
.addValue("uid", uid)
- .addValue("before", before);
+ .addValue("to", new Timestamp(to));
- return getNamedParameterJdbcTemplate().queryForList(
- "SELECT message_id FROM subscr_messages WHERE suser_id = :uid" +
- (before > 0 ?
- " AND message_id < :before " : " ") +
- "ORDER BY message_id DESC LIMIT 20",
+ return getNamedParameterJdbcTemplate().query(
+ "SELECT messages.message_id, messages.updated FROM subscr_messages " +
+ "INNER JOIN messages ON messages.message_id=subscr_messages.message_id " +
+ "WHERE suser_id = :uid " +
+ (to != 0 ?
+ "AND updated < :to " : StringUtils.EMPTY) +
+ "ORDER BY updated DESC, message_id DESC LIMIT 20",
sqlParameterSource,
- Integer.class);
+ (rs, rowNum) -> rs.getInt(1));
}
@Transactional(readOnly = true)
@@ -722,9 +725,9 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
@Transactional(readOnly = true)
@Override
- public List<com.juick.Message> getMessages(final Collection<Integer> mids) {
+ public List<com.juick.Message> getMessages(final List<Integer> mids) {
if (CollectionUtils.isNotEmpty(mids)) {
- return getNamedParameterJdbcTemplate().query(
+ List<com.juick.Message> msgs = getNamedParameterJdbcTemplate().query(
"SELECT messages.message_id, 0 as rid, 0 as replyto, "
+ "messages.user_id,users.nick, 0 as banned, "
+ "TIMESTAMPDIFF(MINUTE,messages.ts,NOW()),"
@@ -732,15 +735,18 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
+ "messages.readonly,messages.privacy,messages.replies,"
+ "messages.attach,messages.place_id,messages.lat,"
+ "messages.lon,COUNT(favorites.user_id) AS likes,messages.hidden,"
- + "messages_txt.tags,messages_txt.repliesby, messages_txt.txt, '' as q "
+ + "messages_txt.tags,messages_txt.repliesby, messages_txt.txt, '' as q, "
+ + "messages.updated "
+ "FROM (messages INNER JOIN messages_txt "
+ "ON messages.message_id=messages_txt.message_id) "
+ "INNER JOIN users ON messages.user_id=users.id "
+ "LEFT JOIN favorites "
+ "ON messages.message_id = favorites.message_id "
- + "WHERE messages.message_id IN (:ids) GROUP BY messages.message_id ORDER BY messages.message_id DESC",
+ + "WHERE messages.message_id IN (:ids) GROUP BY messages.message_id",
new MapSqlParameterSource("ids", mids),
new MessageMapper());
+ msgs.sort(Comparator.comparing(item -> mids.indexOf(item.getMid())));
+ return msgs;
}
return Collections.emptyList();
}
@@ -756,7 +762,8 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
"replies.attach, 0 as place_id, 0 as lat, " +
"0 as lon, 0 as likes, 0 as hidden, " +
"NULL as tags, NULL as repliesby, replies.txt, " +
- "IFNULL(qw.txt, t.txt) as q " +
+ "IFNULL(qw.txt, t.txt) as q, " +
+ "NOW() " +
"FROM replies INNER JOIN users " +
"ON replies.user_id = users.id " +
"LEFT JOIN replies qw ON replies.message_id = qw.message_id and replies.replyto = qw.reply_id " +
@@ -880,10 +887,11 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
@Transactional(readOnly = true)
@Override
public List<com.juick.Message> getNotifications(User user, LocalDateTime before) {
- return getNamedParameterJdbcTemplate().query("SELECT n.message_id as mid, n.reply_id, n.replyto, " +
+ return getNamedParameterJdbcTemplate().query("SELECT n.message_id as mid, n.reply_id, " +
+ "n.replyto, " +
"n.user_id, users.nick, users.banned, 0 as ago, n.ts, 0 as readonly, 0 as privacy, " +
"0 as replies, n.attach, 0 as place_id, 0 as lat, 0 as lon, 0 as likes, 0 as hidden, " +
- "NULL as tags, NULL as repliesby, n.txt, IFNULL(qw.txt, t.txt) as q " +
+ "NULL as tags, NULL as repliesby, n.txt, IFNULL(qw.txt, t.txt) as q, NOW() " +
"FROM (SELECT * FROM replies WHERE EXISTS (SELECT 1 FROM subscr_messages WHERE suser_id=:uid " +
"AND replies.user_id!=:uid AND replies.message_id=message_id " +
(before != null ? "AND replies.ts < :before " : StringUtils.EMPTY) +
diff --git a/juick-server-jdbc/src/test/java/com/juick/service/MessageServiceTest.java b/juick-server-jdbc/src/test/java/com/juick/service/MessageServiceTest.java
index bdb9cdcb..4ebfe056 100644
--- a/juick-server-jdbc/src/test/java/com/juick/service/MessageServiceTest.java
+++ b/juick-server-jdbc/src/test/java/com/juick/service/MessageServiceTest.java
@@ -35,6 +35,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.inject.Inject;
import java.sql.Timestamp;
import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -164,6 +166,8 @@ public class MessageServiceTest extends AbstractJUnit4SpringContextTests {
assertEquals(-1, userService.checkPassword(ugnich.getName(), "xy"));
subscriptionService.subscribeMessage(msg.getMid(), ugnich.getUid());
assertEquals(1, subscriptionService.getUsersSubscribedToComments(msg.getMid(), user.getUid()).size());
+ assertThat(messagesService.getDiscussions(ugnich.getUid(), Instant.now().toEpochMilli()).get(0),
+ equalTo(msg.getMid()));
messagesService.deleteMessage(user_id, mid);
messagesService.deleteMessage(user_id, mid2);
String htmlTagName = ">_<";
diff --git a/juick-www/src/main/java/com/juick/www/controllers/Messages.java b/juick-www/src/main/java/com/juick/www/controllers/Messages.java
index e74f7486..c83bb356 100644
--- a/juick-www/src/main/java/com/juick/www/controllers/Messages.java
+++ b/juick-www/src/main/java/com/juick/www/controllers/Messages.java
@@ -41,6 +41,9 @@ import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLEncoder;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
@@ -117,6 +120,7 @@ public class Messages {
@RequestParam(name = "show", required = false) String paramShow,
@RequestParam(name = "search", required = false) String paramSearch,
@RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore,
+ @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo,
@CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie,
ModelMap model) throws IOException {
if (tag != null) {
@@ -156,7 +160,7 @@ public class Messages {
mids = messagesService.getPrivate(visitor.getUid(), paramBefore);
} else if (paramShow.equals("discuss") && !visitor.isAnonymous()) {
title = "Обсуждения";
- mids = messagesService.getDiscussions(visitor.getUid(), paramBefore);
+ mids = messagesService.getDiscussions(visitor.getUid(), paramTo);
} else if (paramShow.equals("recommended") && !visitor.isAnonymous()) {
title = "Рекомендации";
mids = messagesService.getRecommended(visitor.getUid(), paramBefore);
@@ -194,7 +198,7 @@ public class Messages {
model.addAttribute("showAdv",
paramShow == null && paramBefore == 0 && paramSearch == null && visitor.getUid() == 0);
if (mids.size() >= 20) {
- String nextpage = "?before=" + mids.get(mids.size() - 1);
+ String nextpage = (paramShow != null && paramShow.equals("discuss")) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : "?before=" + mids.get(mids.size() - 1);
if (paramShow != null) {
nextpage += "&amp;show=" + paramShow;
}
diff --git a/juick-www/src/test/java/com/juick/www/WebAppTests.java b/juick-www/src/test/java/com/juick/www/WebAppTests.java
index af6b3f89..9d822d77 100644
--- a/juick-www/src/test/java/com/juick/www/WebAppTests.java
+++ b/juick-www/src/test/java/com/juick/www/WebAppTests.java
@@ -28,11 +28,7 @@ import com.juick.Tag;
import com.juick.User;
import com.juick.configuration.DataConfiguration;
import com.juick.configuration.RepositoryConfiguration;
-import com.juick.service.ImagesService;
-import com.juick.service.MessagesService;
-import com.juick.service.MockImagesService;
-import com.juick.service.PrivacyQueriesService;
-import com.juick.service.UserService;
+import com.juick.service.*;
import com.juick.util.MessageUtils;
import com.juick.www.configuration.SapeConfiguration;
import com.juick.www.configuration.WebSecurityConfig;
@@ -42,6 +38,7 @@ import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.error.PebbleException;
import com.mitchellbosecke.pebble.template.PebbleTemplate;
import org.apache.commons.text.StringEscapeUtils;
+import org.eclipse.jetty.websocket.common.message.MessageAppender;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +69,8 @@ import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
@@ -119,6 +118,8 @@ public class WebAppTests {
private PrivacyQueriesService privacyQueriesService;
@Inject
private JdbcTemplate jdbcTemplate;
+ @Inject
+ private SubscriptionService subscriptionService;
@Inject
private PebbleEngine pebbleEngine;
@@ -333,4 +334,54 @@ public class WebAppTests {
public void nonExistentBlogShouldReturn404() throws Exception {
mockMvc.perform(get("/ololoe/")).andExpect(status().isNotFound());
}
+ @Test
+ public void discussionsShouldBePageableByTimestamp() throws Exception {
+ String msgText = "Привет, я снова Угнич";
+ int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null);
+ int midNew = messagesService.createMessage(ugnich.getUid(), "Я более новый Угнич", null, null);
+ MvcResult loginResult = mockMvc.perform(post("/login")
+ .param("username", freefdName)
+ .param("password", freefdPassword)).andReturn();
+ Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me");
+ webClient.setCookieManager(new CookieManager());
+ webClient.getCookieManager().addCookie(
+ new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(),
+ loginCookie.getName(),
+ loginCookie.getValue()));
+ String discussionsUrl = "http://localhost:8080/?show=discuss";
+ HtmlPage discussions = webClient.getPage(discussionsUrl);
+ assertThat(discussions.querySelectorAll("article").size(), is(0));
+ subscriptionService.subscribeMessage(mid, freefd.getUid());
+ discussions = (HtmlPage) discussions.refresh();
+ assertThat(discussions.querySelectorAll("article").size(), is(1));
+ subscriptionService.subscribeMessage(midNew, freefd.getUid());
+ discussions = (HtmlPage) discussions.refresh();
+ assertThat(discussions.querySelectorAll("article").size(), is(2));
+ assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
+ messagesService.createReply(mid, 0, freefd.getUid(), "I'm replied", null);
+ discussions = (HtmlPage) discussions.refresh();
+ assertThat(discussions.querySelectorAll("article").size(), is(2));
+ assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
+ Message msg = messagesService.getMessage(mid);
+ HtmlPage discussionsOld = webClient.getPage(discussionsUrl + "&to=" + msg.getUpdated().toEpochMilli());
+ assertThat(discussionsOld.querySelectorAll("article").size(), is(1));
+ assertThat(discussionsOld.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
+ List<Integer> newMids = IntStream.rangeClosed(1, 19).map(i -> messagesService.createMessage(ugnich.getUid(), String.valueOf(i), null, null)).boxed().collect(Collectors.toList());
+ for (Integer m : newMids) {
+ subscriptionService.subscribeMessage(m, freefd.getUid());
+ }
+ discussions = (HtmlPage) discussions.refresh();
+ assertThat(discussions.querySelectorAll("article").size(), is(20));
+ assertThat(discussions.querySelectorAll("article")
+ .get(19).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
+ messagesService.createReply(midNew, 0, freefd.getUid(), "I'm replied", null);
+ discussions = (HtmlPage) discussions.refresh();
+ assertThat(discussions.querySelectorAll("article")
+ .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew)));
+ Message old = messagesService.getMessage(newMids.get(0));
+ discussionsOld = webClient.getPage(discussionsUrl + "&to=" + old.getUpdated().toEpochMilli());
+ assertThat(discussionsOld.querySelectorAll("article").size(), is(1));
+ assertThat(discussionsOld.querySelectorAll("article")
+ .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid)));
+ }
}