From 3ebb1b270129cf17cc80337352a226f8668d131a Mon Sep 17 00:00:00 2001 From: Maxim Valyanskiy Date: Thu, 2 Jan 2025 10:35:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B2?= =?UTF-8?q?=20=D0=BB=D0=B5=D0=BD=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru/org/linux/tag/TagPageController.scala | 18 +++-- .../ru/org/linux/topic/PreparedImage.java | 65 ------------------- .../edithistory/EditHistoryService.scala | 6 +- .../ru/org/linux/gallery/ImageService.scala | 8 ++- .../org/linux/spring/MainPageController.scala | 4 +- .../ru/org/linux/topic/PreparedImage.scala | 34 ++++++++++ .../linux/topic/TagTopicListController.scala | 4 +- .../org/linux/topic/TopicListController.scala | 6 +- .../org/linux/topic/TopicPrepareService.scala | 29 ++++----- .../topic/UncommitedTopicsController.scala | 4 +- .../linux/topic/UserTopicListController.scala | 8 +-- src/main/webapp/WEB-INF/tags/image.tag | 3 +- 12 files changed, 79 insertions(+), 110 deletions(-) delete mode 100644 src/main/java/ru/org/linux/topic/PreparedImage.java create mode 100644 src/main/scala/ru/org/linux/topic/PreparedImage.scala diff --git a/src/main/java/ru/org/linux/tag/TagPageController.scala b/src/main/java/ru/org/linux/tag/TagPageController.scala index c5fb35fa15..e82e2203c8 100644 --- a/src/main/java/ru/org/linux/tag/TagPageController.scala +++ b/src/main/java/ru/org/linux/tag/TagPageController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -104,11 +104,11 @@ class TagPageController(tagService: TagService, prepareService: TopicPrepareServ val synonyms = tagService.getSynonymsFor(tagInfo.id) - val model = Map( + val model = Map[String, AnyRef]( "tag" -> tag, "title" -> WordUtils.capitalize(tag), - "favsCount" -> userTagService.countFavs(tagInfo.id), - "ignoreCount" -> userTagService.countIgnore(tagInfo.id), + "favsCount" -> Int.box(userTagService.countFavs(tagInfo.id)), + "ignoreCount" -> Int.box(userTagService.countIgnore(tagInfo.id)), "showAdsense" -> Boolean.box(!currentUser.authorized || !currentUser.profile.hideAdsense), "showDelete" -> Boolean.box(currentUser.moderator), "synonyms" -> synonyms.asJava, @@ -146,7 +146,7 @@ class TagPageController(tagService: TagService, prepareService: TopicPrepareServ (Seq.empty, newsTopics.take(TagPageController.TotalNewsCount-1)) } - val fullNews = prepareService.prepareTopicsForUser(fullNewsTopics, loadUserpics = false) + val fullNews = prepareService.prepareTopics(fullNewsTopics, loadUserpics = false) val briefNewsByDate = TopicListTools.datePartition(briefNewsTopics) @@ -164,7 +164,7 @@ class TagPageController(tagService: TagService, prepareService: TopicPrepareServ None } - (Map( + (Map[String, AnyRef]( "fullNews" -> fullNews.asJava, "briefNews" -> TopicListTools.split(briefNewsByDate.map(p => p._1 -> prepareService.prepareBrief(p._2, groupInTitle = false))) ) ++ more ++ addNews, newestDate) @@ -186,9 +186,7 @@ class TagPageController(tagService: TagService, prepareService: TopicPrepareServ None } - Map( - "gallery" -> list - ) ++ add ++ more + Map[String, AnyRef]("gallery" -> list) ++ add ++ more } private def getTopicList(tag: String, tagId: Int, section: Int, mode: CommitMode)(implicit currentUser: AnySession) = { @@ -217,7 +215,7 @@ class TagPageController(tagService: TagService, prepareService: TopicPrepareServ val newestDate = forumTopics.headOption.map(t => Instant.ofEpochMilli(t.getEffectiveDate.getMillis)) - (Map( + (Map[String, AnyRef]( forumSection.getUrlName -> TopicListTools.split( topicByDate.map(p => p._1 -> prepareService.prepareBrief(p._2, groupInTitle = true))) ) ++ more ++ add, newestDate) diff --git a/src/main/java/ru/org/linux/topic/PreparedImage.java b/src/main/java/ru/org/linux/topic/PreparedImage.java deleted file mode 100644 index 62032afb99..0000000000 --- a/src/main/java/ru/org/linux/topic/PreparedImage.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 1998-2018 Linux.org.ru - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ru.org.linux.topic; - -import ru.org.linux.gallery.Image; -import ru.org.linux.util.image.ImageInfo; - -public class PreparedImage { - private final String mediumName; - private final String fullName; - - private final ImageInfo mediumInfo; - private final ImageInfo fullInfo; - - private final Image image; - - public PreparedImage(String mediumName, ImageInfo mediumInfo, String fullName, ImageInfo fullInfo, Image image) { - this.mediumName = mediumName; - this.mediumInfo = mediumInfo; - this.fullName = fullName; - this.fullInfo = fullInfo; - this.image = image; - } - - public String getMediumName() { - return mediumName; - } - - public ImageInfo getMediumInfo() { - return mediumInfo; - } - - public String getFullName() { - return fullName; - } - - public ImageInfo getFullInfo() { - return fullInfo; - } - - public Image getImage() { - return image; - } - - public String getSrcset() { - if (getFullInfo().getWidth() < Image.MaxScaledSize()) { - return image.getSrcsetUpTo(getFullInfo().getWidth()) + ", " + getFullName() + " " + getFullInfo().getWidth() + "w"; - } else { - return image.getSrcset(); - } - } -} diff --git a/src/main/scala/ru/org/linux/edithistory/EditHistoryService.scala b/src/main/scala/ru/org/linux/edithistory/EditHistoryService.scala index 4f1bcb7f6f..80ea0b20ce 100644 --- a/src/main/scala/ru/org/linux/edithistory/EditHistoryService.scala +++ b/src/main/scala/ru/org/linux/edithistory/EditHistoryService.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -70,13 +70,13 @@ class EditHistoryService(topicTagService: TopicTagService, userService: UserServ val imageDeleted = this.image == null && dto.oldimage.isDefined val addedImages = if (dto.oldaddimages.isDefined) { - this.additionalImages.filterNot(img => dto.oldaddimages.get.contains(img.getImage.id)).asJava + this.additionalImages.filterNot(img => dto.oldaddimages.get.contains(img.image.id)).asJava } else { null } val removedImages = if (dto.oldaddimages.isDefined) { - dto.oldaddimages.get.filterNot(this.additionalImages.map(_.getImage.id).contains).map(imageDao.getImage).flatMap(imageService.prepareImage).asJava + dto.oldaddimages.get.filterNot(this.additionalImages.map(_.image.id).contains).map(imageDao.getImage).flatMap(imageService.prepareImage).asJava } else { null } diff --git a/src/main/scala/ru/org/linux/gallery/ImageService.scala b/src/main/scala/ru/org/linux/gallery/ImageService.scala index 30c97b0019..acb1fef59f 100644 --- a/src/main/scala/ru/org/linux/gallery/ImageService.scala +++ b/src/main/scala/ru/org/linux/gallery/ImageService.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -74,7 +74,9 @@ class ImageService(imageDao: ImageDao, editHistoryDao: EditHistoryDao, PreparedGalleryItem(item, userService.getUserCached(item.getUserid)) } - def prepareImage(image: Image): Option[PreparedImage] = { + def prepareImage(image: Image): Option[PreparedImage] = prepareImage(image, lazyLoad = false) + + def prepareImage(image: Image, lazyLoad: Boolean): Option[PreparedImage] = { Preconditions.checkNotNull(image) val htmlPath = siteConfig.getUploadPath @@ -87,7 +89,7 @@ class ImageService(imageDao: ImageDao, editHistoryDao: EditHistoryDao, val medURI = siteConfig.getSecureUrl + mediumName val fullURI = siteConfig.getSecureUrl + image.original - Some(new PreparedImage(medURI, mediumImageInfo, fullURI, fullInfo, image)) + Some(PreparedImage(medURI, mediumImageInfo, fullURI, fullInfo, image, lazyLoad)) } catch { case e: FileNotFoundException => logger.error(s"Image not found! id=${image.id}: ${e.getMessage}") diff --git a/src/main/scala/ru/org/linux/spring/MainPageController.scala b/src/main/scala/ru/org/linux/spring/MainPageController.scala index ff95b2eb07..de52df53fd 100644 --- a/src/main/scala/ru/org/linux/spring/MainPageController.scala +++ b/src/main/scala/ru/org/linux/spring/MainPageController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -48,7 +48,7 @@ class MainPageController(prepareService: TopicPrepareService, topicListService: val mv = new ModelAndView("index") - mv.getModel.put("news", prepareService.prepareTopicsForUser(messages, loadUserpics = false).asJava) + mv.getModel.put("news", prepareService.prepareTopics(messages, loadUserpics = false).asJava) val briefNewsByDate = TopicListTools.datePartition(titles) diff --git a/src/main/scala/ru/org/linux/topic/PreparedImage.scala b/src/main/scala/ru/org/linux/topic/PreparedImage.scala new file mode 100644 index 0000000000..a3ca22e29a --- /dev/null +++ b/src/main/scala/ru/org/linux/topic/PreparedImage.scala @@ -0,0 +1,34 @@ +/* + * Copyright 1998-2025 Linux.org.ru + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ru.org.linux.topic + +import ru.org.linux.gallery.Image +import ru.org.linux.util.image.ImageInfo + +import scala.beans.BeanProperty + +case class PreparedImage(@BeanProperty mediumName: String, @BeanProperty mediumInfo: ImageInfo, + @BeanProperty fullName: String, @BeanProperty fullInfo: ImageInfo, @BeanProperty image: Image, + @BeanProperty lazyLoad: Boolean) { + def getSrcset: String = { + if (fullInfo.getWidth < Image.MaxScaledSize) { + image.getSrcsetUpTo(fullInfo.getWidth) + ", " + fullName + " " + fullInfo.getWidth + "w" + } else { + image.getSrcset + } + } + + def getLoadingCode: String = if (lazyLoad) "loading=\"lazy\"" else "" +} \ No newline at end of file diff --git a/src/main/scala/ru/org/linux/topic/TagTopicListController.scala b/src/main/scala/ru/org/linux/topic/TagTopicListController.scala index b30c15abca..efa1134e54 100644 --- a/src/main/scala/ru/org/linux/topic/TagTopicListController.scala +++ b/src/main/scala/ru/org/linux/topic/TagTopicListController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -126,7 +126,7 @@ class TagTopicListController(userTagService: UserTagService, sectionService: Sec } else { val topics = topicListService.getTopicsFeed(section, None, Some(tag), offset, None, 20, noTalks = false, tech = false) - (prepareService.prepareTopicsForUser(topics, loadUserpics = false), 20) + (prepareService.prepareTopics(topics, loadUserpics = false), 20) } modelAndView.addObject("messages", preparedTopics.asJava) diff --git a/src/main/scala/ru/org/linux/topic/TopicListController.scala b/src/main/scala/ru/org/linux/topic/TopicListController.scala index d953578dda..a8616ccf18 100644 --- a/src/main/scala/ru/org/linux/topic/TopicListController.scala +++ b/src/main/scala/ru/org/linux/topic/TopicListController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -139,7 +139,7 @@ class TopicListController(sectionService: SectionService, topicListService: Topi modelAndView.addObject( "messages", - prepareService.prepareTopicsForUser(messages, loadUserpics = false).asJava) + prepareService.prepareTopics(messages, loadUserpics = false).asJava) modelAndView.addObject("offsetNavigation", topicListForm.yearMonth.isEmpty) @@ -282,7 +282,7 @@ class TopicListController(sectionService: SectionService, topicListService: Topi if (lastModified.exists(webRequest.checkNotModified)) { null } else { - modelAndView.addObject("messages", prepareService.prepareTopics(messages.toSeq).asJava) + modelAndView.addObject("messages", prepareService.prepareTopicForRSS(messages.toSeq).asJava) modelAndView } diff --git a/src/main/scala/ru/org/linux/topic/TopicPrepareService.scala b/src/main/scala/ru/org/linux/topic/TopicPrepareService.scala index 6d43c78131..00014f63bc 100644 --- a/src/main/scala/ru/org/linux/topic/TopicPrepareService.scala +++ b/src/main/scala/ru/org/linux/topic/TopicPrepareService.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -44,11 +44,11 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de warningService: WarningService) { def prepareTopic(message: Topic)(implicit session: AnySession): PreparedTopic = prepareTopic(message, topicTagService.getTagRefs(message).asScala, minimizeCut = false, None, - msgbaseDao.getMessageText(message.id), image = None) + msgbaseDao.getMessageText(message.id), image = None, imageLazyLoad = false) def prepareTopic(message: Topic, tags: collection.Seq[TagRef], text: MessageText, warnings: Seq[Warning])(implicit session: AnySession): PreparedTopic = - prepareTopic(message, tags, minimizeCut = false, None, text, None, Seq.empty, warnings) + prepareTopic(message, tags, minimizeCut = false, None, text, None, Seq.empty, warnings, imageLazyLoad = false) def prepareTopicPreview(message: Topic, tags: Seq[TagRef], newPoll: Option[Poll], text: MessageText, image: Option[UploadedImagePreview], additionalImages: Seq[UploadedImagePreview]): PreparedTopic = { @@ -56,7 +56,7 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de val additionalImageObjects = additionalImages.map(_.toImage(main = false)) prepareTopic(message, tags, minimizeCut = false, newPoll.map(pollPrepareService.preparePollPreview), - text, imageObject, additionalImageObjects)(NonAuthorizedSession) + text, imageObject, additionalImageObjects, imageLazyLoad = false)(NonAuthorizedSession) } def prepareEditInfo(editInfo: EditInfoSummary, topic: Topic)(implicit session: AnySession): PreparedEditInfoSummary = { @@ -78,7 +78,8 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de * @return подготовленный топик */ private def prepareTopic(topic: Topic, tags: collection.Seq[TagRef], minimizeCut: Boolean, poll: Option[PreparedPoll], - text: MessageText, image: Option[Image], additionalImages: Seq[Image] = Seq.empty, warnings: Seq[Warning] = Seq.empty) + text: MessageText, image: Option[Image], additionalImages: Seq[Image] = Seq.empty, warnings: Seq[Warning] = Seq.empty, + imageLazyLoad: Boolean) (implicit session: AnySession): PreparedTopic = { val group = groupDao.getGroup(topic.groupId) val author = userService.getUserCached(topic.authorUserId) @@ -118,12 +119,11 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de val loadedAdditionalImage = currentImages.filterNot(_.main) ++ additionalImages - (loadedImage.flatMap(imageService.prepareImage), loadedAdditionalImage.flatMap(imageService.prepareImage)) + (loadedImage.flatMap(imageService.prepareImage(_, imageLazyLoad)), loadedAdditionalImage.flatMap(imageService.prepareImage)) } else { (None, Seq.empty) } - val remark = session.userOpt.flatMap { user => remarkDao.getRemark(user, author) } @@ -161,14 +161,14 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de * @param loadUserpics флаг загрузки аватар * @return список подготовленных топиков */ - def prepareTopicsForUser(messages: collection.Seq[Topic], loadUserpics: Boolean) - (implicit user: AnySession): collection.Seq[PersonalizedPreparedTopic] = { + def prepareTopics(messages: collection.Seq[Topic], loadUserpics: Boolean) + (implicit user: AnySession): collection.Seq[PersonalizedPreparedTopic] = { val textMap = loadTexts(messages) val tags = topicTagService.tagRefs(messages.map(_.id)) - messages.map { message => + messages.zipWithIndex.map { case (message, idx) => val preparedMessage = prepareTopic(message, tags.getOrElse(message.id, Seq.empty), minimizeCut = true, None, - textMap(message.id), image = None) + textMap(message.id), image = None, imageLazyLoad = idx >= 3) val topicMenu = getTopicMenu(preparedMessage, loadUserpics) new PersonalizedPreparedTopic(preparedMessage, topicMenu) @@ -179,19 +179,18 @@ class TopicPrepareService(sectionService: SectionService, groupDao: GroupDao, de msgbaseDao.getMessageText(messages.map(_.id)) /** - * Подготовка ленты топиков, используется в TopicListController например - * сообщения рендерятся со свернутым cut + * Подготовка ленты топиков для RSS * * @param messages список топиков * @return список подготовленных топиков */ - def prepareTopics(messages: collection.Seq[Topic]): Seq[PreparedTopic] = { + def prepareTopicForRSS(messages: collection.Seq[Topic]): Seq[PreparedTopic] = { val textMap = loadTexts(messages) val tags = topicTagService.tagRefs(messages.map(_.id)) messages.view.map { message => prepareTopic(message, tags.getOrElse(message.id, Seq.empty), minimizeCut = true, None, textMap(message.id), - image = None)(NonAuthorizedSession) + image = None, imageLazyLoad = false)(NonAuthorizedSession) }.toSeq } diff --git a/src/main/scala/ru/org/linux/topic/UncommitedTopicsController.scala b/src/main/scala/ru/org/linux/topic/UncommitedTopicsController.scala index 3cd0481f42..f6415e25a6 100644 --- a/src/main/scala/ru/org/linux/topic/UncommitedTopicsController.scala +++ b/src/main/scala/ru/org/linux/topic/UncommitedTopicsController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -66,7 +66,7 @@ class UncommitedTopicsController(sectionService: SectionService, topicListServic val messages = topicListService.getUncommitedTopic(section, calendar.getTime, includeAnonymous) - val topics = prepareService.prepareTopicsForUser(messages, loadUserpics = false) + val topics = prepareService.prepareTopics(messages, loadUserpics = false) modelAndView.addObject("messages", topics.asJava) diff --git a/src/main/scala/ru/org/linux/topic/UserTopicListController.scala b/src/main/scala/ru/org/linux/topic/UserTopicListController.scala index e28a63f614..81ac27c511 100644 --- a/src/main/scala/ru/org/linux/topic/UserTopicListController.scala +++ b/src/main/scala/ru/org/linux/topic/UserTopicListController.scala @@ -1,5 +1,5 @@ /* - * Copyright 1998-2024 Linux.org.ru + * Copyright 1998-2025 Linux.org.ru * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -152,7 +152,7 @@ class UserTopicListController(topicListService: TopicListService, userDao: UserD val topics = topicListService.getDeletedUserTopics(user, currentUser.profile.topics) - val params = Map( + val params = Map[String, AnyRef]( "topics" -> topics.asJava, "user" -> user ) @@ -190,10 +190,10 @@ class UserTopicListController(topicListService: TopicListService, userDao: UserD private def prepareTopicsForPlainOrRss(modelAndView: ModelAndView, rss: Boolean, messages: collection.Seq[Topic]) (implicit currentUser: AnySession): Unit = { if (rss) { - modelAndView.addObject("messages", prepareService.prepareTopics(messages).asJava) + modelAndView.addObject("messages", prepareService.prepareTopicForRSS(messages).asJava) modelAndView.setViewName("section-rss") } else { - modelAndView.addObject("messages", prepareService.prepareTopicsForUser(messages, loadUserpics = false).asJava) + modelAndView.addObject("messages", prepareService.prepareTopics(messages, loadUserpics = false).asJava) } } diff --git a/src/main/webapp/WEB-INF/tags/image.tag b/src/main/webapp/WEB-INF/tags/image.tag index ef89e2b4f5..658a397258 100644 --- a/src/main/webapp/WEB-INF/tags/image.tag +++ b/src/main/webapp/WEB-INF/tags/image.tag @@ -3,7 +3,7 @@ <%@ taglib prefix="l" uri="http://www.linux.org.ru" %> <%@ tag pageEncoding="UTF-8"%> <%-- - ~ Copyright 1998-2024 Linux.org.ru + ~ Copyright 1998-2025 Linux.org.ru ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at @@ -38,6 +38,7 @@ alt="${title}" srcset="${image.srcset}" sizes="100vw" style="position: absolute" + "${image.loadingCode}" ${image.mediumInfo.code}>