Skip to content

Commit

Permalink
Release v1.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
wb14123 committed Jan 31, 2025
1 parent e049d2a commit 559f7f2
Show file tree
Hide file tree
Showing 21 changed files with 203 additions and 76 deletions.
6 changes: 4 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ initialize := {
}


lazy val doobieVersion = "1.0.0-RC4"
// I fixed a bug based in doobie but it's not released yet: https://github.com/typelevel/doobie/issues/2132
// TODO: Update to new version after it's released
lazy val doobieVersion = "1.0.0-RC5"

Test / parallelExecution := false
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oF")
Expand Down Expand Up @@ -148,7 +150,7 @@ libraryDependencies ++= Seq(
"io.prometheus" % "simpleclient" % prometheusVersion,
"io.prometheus" % "simpleclient_hotspot" % prometheusVersion,
"io.prometheus" % "simpleclient_httpserver" % prometheusVersion,
"me.binwang.archmage" %% "core" % "0.1.0-SNAPSHOT",
"me.binwang.archmage" %% "core" % "0.1.0",

// throttling
"com.google.guava" % "guava" % "31.1-jre",
Expand Down
33 changes: 33 additions & 0 deletions js/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,39 @@ progress {
font-size: 14px !important;
}

.article-info-social-media {
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
}

.article-info-social-media .source-avatar {
height: 48px !important;
width: 48px !important;
border-radius: 50%;
}

.social-media-info-details {
display: flex;
flex-direction: column
}

.social-media-info-details .source-title {
color: var(--pico-color-force);;
font-size: 22px;
font-weight: bold;
}

.social-media-info-details .source-title:hover {
color: var(--pico-color);
}

.social-media-info-details .article-time {
font-size: 16px;
color: var(--pico-code-color);
}

.article-desc {
font-size: 18px;
max-height: 260px;
Expand Down
14 changes: 10 additions & 4 deletions js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
"license": "ISC",
"dependencies": {
"@floating-ui/dom": "^1.6.10",
"@ungap/custom-elements": "^1.3.0",
"alpinejs": "^3.14.1",
"dompurify": "^3.1.6",
"htmx.org": "^1.9.12",
"imgs-html": "^0.0.4",
"lite-youtube-embed": "^0.3.3",
"somment": "^0.0.2",
"somment": "^0.0.3",
"toastify-js": "^1.12.0",
"viewerjs": "^1.11.6"
},
Expand Down
2 changes: 1 addition & 1 deletion js/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import '../css/google-fonts.css';
import '../css/main.css';

// js
import '@ungap/custom-elements';
import './boolean-checkbox.js';

import 'htmx.org';
Expand Down Expand Up @@ -39,6 +40,5 @@ import './popover-menu.js';
import './match-id.js';
import './set-theme.js';
import './source-images.js';
import './register-service-worker.js';

Alpine.start();
2 changes: 1 addition & 1 deletion project/plugin.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")

addSbtPlugin("org.typelevel" % "sbt-fs2-grpc" % "2.7.5")
addSbtPlugin("me.binwang.scala2grpc" % "plugin" % "1.0.1-SNAPSHOT")
addSbtPlugin("me.binwang.scala2grpc" % "plugin" % "1.0.1")

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0")

Expand Down
17 changes: 17 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ RSS Brain is a modern RSS Reader. Check [the official website](https://www.rssbr

## Build

### Build Custom Doobie Version

This version uses a custom branch of doobie to resolve [this issue](https://github.com/typelevel/doobie/issues/2132).

Build the [this branch](https://github.com/wb14123/doobie/tree/stream-leak-patch) locally with:

```
git clone https://github.com/wb14123/doobie
git checkout stream-leak-patch
sbt +publishLocal
```

Then find the built version at `~/.ivy2/local/org.tpolecat/doobie-postgres_2.13` and assign the version to
`doobieVersion` in RSS Brain's `build.sbt`.

### Build RSS Brain

```
sbt clean generateGRPCCode
sbt compile
Expand Down
2 changes: 2 additions & 0 deletions src/main/protobuf/grpc-api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ enum ArticleOrder {
enum ArticleListLayout {
LIST = 0;
GRID = 1;
SOCIAL_MEDIA = 2;
COMPACT = 3;
}

// Define Folder
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ redis {
}

api {
version = "1.5.0"
version = "1.7.0"
}

grpc {
Expand Down
File renamed without changes.
4 changes: 3 additions & 1 deletion src/main/scala/me/binwang/rss/model/ArticleListLayout.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ object ArticleListLayout extends Enumeration {
type ArticleListLayout = Value
val
LIST,
GRID
GRID,
SOCIAL_MEDIA,
COMPACT
= Value
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ object ArticleParserHelper {
.charset("UTF-8")
.outline(true)
.indentAmount(2)
val dom = Jsoup.parse(html).outputSettings(settings)
val dom = Jsoup.parse(html.replaceAll("\u0000", "")).outputSettings(settings)
removeTags.foreach(dom.select(_).remove())
val cleaner = new Cleaner(safeList)
val cleaned = cleaner.clean(dom).body()
Expand Down
56 changes: 26 additions & 30 deletions src/main/scala/me/binwang/rss/webview/routes/ArticleListView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import me.binwang.rss.webview.basic.ErrorHandler.RedirectDefaultFolderException
import me.binwang.rss.webview.basic.HtmlCleaner.str2cleaner
import me.binwang.rss.webview.basic.ScalaTagAttributes._
import me.binwang.rss.webview.routes.ArticleList._
import me.binwang.rss.webview.widgets.ArticleRender.{ArticleRenderLayout, GridLayout, HorizontalLayout, VerticalLayout}
import me.binwang.rss.webview.widgets.ArticleRender.{ArticleListDirection, Horizontal, Vertical}
import me.binwang.rss.webview.widgets.{ArticleRender, EditFolderButton, EditSourceButton, PageHeader, SearchBox}
import org.http4s.dsl.io._
import org.http4s.headers._
Expand All @@ -30,11 +30,6 @@ object ArticleList {
case ArticleOrder.SCORE => "by_score"
}

def layout(layout: ArticleListLayout): String = layout match {
case ArticleListLayout.LIST => "list"
case ArticleListLayout.GRID => "grid"
}

def mapToQueryStr(m: Map[String, String]): String = {
m.map {
case (k, "") => s"${k.encodeUrl}"
Expand All @@ -52,9 +47,9 @@ object ArticleList {
if (folder.isUserDefault) {
s"/hx/articles?title=All Articles"
} else if (folder.searchEnabled && folder.searchTerm.isDefined) {
s"/folders/$folderID/articles/search/${layout(folder.articleListLayout)}?title=$folderName&by_time&q=${folder.searchTerm.get}"
s"/folders/$folderID/articles/search/${folder.articleListLayout}?title=$folderName&by_time&q=${folder.searchTerm.get}"
} else {
s"/hx/folders/$folderID/articles/${sortBy(folder.articleOrder)}/${layout(folder.articleListLayout)}?title=$folderName"
s"/hx/folders/$folderID/articles/${sortBy(folder.articleOrder)}/${folder.articleListLayout}?title=$folderName"
}
}

Expand All @@ -64,7 +59,7 @@ object ArticleList {
"in_folder" -> fs.folderMapping.folderID,
)
s"/hx/sources/${fs.folderMapping.sourceID}/articles/${sortBy(fs.folderMapping.articleOrder)}" +
s"/${layout(fs.folderMapping.articleListLayout)}?${mapToQueryStr(newParams)}"
s"/${fs.folderMapping.articleListLayout}?${mapToQueryStr(newParams)}"
}

}
Expand Down Expand Up @@ -92,15 +87,6 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
}
}

private def layoutFromStr(str: String): ArticleRenderLayout = str match {
case "list" => VerticalLayout()
case "grid" => GridLayout()
case "horizontal" => HorizontalLayout()
case _ => VerticalLayout()
}



private def articlesWithHeader(req: Request[IO], showFilters: Boolean, editButton: Option[Frag],
searchLink: Option[String]): Frag = {
val headerTitle = req.params.getOrElse("title", "")
Expand Down Expand Up @@ -194,6 +180,14 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
s"/search?sourceID=$sourceID&layout=$layout&${mapToQueryStr(req.params)}"
}

private def direction(req: Request[IO]): ArticleListDirection = {
if (req.params.get("direction").exists(_.equals("horizontal"))) {
Horizontal()
} else {
Vertical()
}
}

val routes: HttpRoutes[IO] = HttpRoutes.of[IO] {

case req @ GET -> Root / "hx" / "articles" => articleWrapperOr(req, searchLink = Some(allSearchLink(req))) {
Expand All @@ -205,7 +199,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
req.params.get("read").map(_ => false),
req.params.get("bookmarked").map(_ => true),
)
ArticleRender.renderList(articlesStream, VerticalLayout(), Some(article => {
ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.LIST, Some(article => {
val postedBefore = ModelTranslator.dateTimeToLong(article.postedAt)
val newParams = req.params ++ Map(
"postedBefore" -> postedBefore.toString,
Expand All @@ -228,7 +222,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
val baseLink = baseAllSearchLink()
val searchBox: fs2.Stream[IO, Frag] = if (showSearchBox)
fs2.Stream.emit(SearchBox(baseLink, req, Some(searchOptions))) else fs2.Stream.empty
searchBox ++ ArticleRender.renderList(articlesStream, VerticalLayout(), Some(_ =>
searchBox ++ ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.LIST, Some(_ =>
s"$baseLink?${searchOptionsToUrlParams(nextSearchOption)}"
))
}
Expand Down Expand Up @@ -261,7 +255,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
req.params.get("read").map(_ => false),
req.params.get("bookmarked").map(_ => true),
)
ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(article => {
ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.withName(layout.toUpperCase), Some(article => {
val postedBefore = ModelTranslator.dateTimeToLong(article.postedAt)
val newParams = req.params ++ Map(
"postedBefore" -> postedBefore.toString,
Expand All @@ -284,7 +278,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
req.params.get("read").map(_ => false),
req.params.get("bookmarked").map(_ => true),
)
ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(article => {
ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.withName(layout.toUpperCase), Some(article => {
val newParams = req.params ++ Map(
"maxScore" -> article.score.toString,
"articleID" -> article.id,
Expand All @@ -306,9 +300,10 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
val baseLink = baseSourceSearchLink(sourceID, layout)
val searchBox: fs2.Stream[IO, Frag] = if (showSearchBox)
fs2.Stream.emit(SearchBox(baseLink, req, Some(searchOptions))) else fs2.Stream.empty
searchBox ++ ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(_ =>
s"$baseLink?${searchOptionsToUrlParams(nextSearchOption)}"
))
searchBox ++ ArticleRender.renderList(articlesStream, direction(req),
ArticleListLayout.withName(layout.toUpperCase),
Some(_ => s"$baseLink?${searchOptionsToUrlParams(nextSearchOption)}"),
)
}
}
}
Expand All @@ -334,7 +329,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
req.params.get("read").map(_ => false),
req.params.get("bookmarked").map(_ => true),
)
ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(article => {
ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.withName(layout.toUpperCase), Some(article => {
val postedBefore = ModelTranslator.dateTimeToLong(article.postedAt)
val newParams = req.params ++ Map(
"postedBefore" -> postedBefore.toString,
Expand All @@ -357,7 +352,7 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
req.params.get("read").map(_ => false),
req.params.get("bookmarked").map(_ => true),
)
ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(article => {
ArticleRender.renderList(articlesStream, direction(req), ArticleListLayout.withName(layout.toUpperCase), Some(article => {
val newParams = req.params ++ Map(
"maxScore" -> article.score.toString,
"articleID" -> article.id,
Expand All @@ -379,9 +374,10 @@ class ArticleListView(articleService: ArticleService, userService: UserService,
val baseLink = baseFolderSearchLink(folderID, layout)
val searchBox: fs2.Stream[IO, Frag] = if (showSearchBox)
fs2.Stream.emit(SearchBox(baseLink, req, Some(searchOptions))) else fs2.Stream.empty
searchBox ++ ArticleRender.renderList(articlesStream, layoutFromStr(layout), Some(_ =>
s"$baseLink?${searchOptionsToUrlParams(nextSearchOption)}"
))
searchBox ++ ArticleRender.renderList(articlesStream, direction(req),
ArticleListLayout.withName(layout.toUpperCase),
Some(_ => s"$baseLink?${searchOptionsToUrlParams(nextSearchOption)}")
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object FolderListView {
span(cls := "material-icons-outlined warning", title := fs.source.fetchFailedMsg.get)("error_outline")

val sortBy = ArticleList.sortBy(fs.folderMapping.articleOrder)
val layout = ArticleList.layout(fs.folderMapping.articleListLayout)
val layout = fs.folderMapping.articleListLayout.toString
val proxyImageUrl = if (fs.source.iconUrl.isEmpty) Seq() else {
val iconUrl = fs.source.iconUrl.get.escapeHtml
val httpsUrl = iconUrl.toHttps
Expand Down Expand Up @@ -277,9 +277,9 @@ class FolderListView(folderService: FolderService, sourceService: SourceService,
input(`type` := "text", name := "description", value := folder.description.getOrElse(""))),
label(cls := "form-row",
input(is := "boolean-checkbox", `type` := "checkbox", name := "searchEnabled",
if (folder.searchEnabled) checked else ""), "Enable Search",
if (folder.searchEnabled) checked else ""), "Enable Filter",
),
label(div("Search Term"),
label(div("Filter Search Term"),
input(`type` := "text", name := "searchTerm", value := folder.searchTerm.getOrElse(""))),
label(div("Order By"), select(name := "articleOrder",
option(value := ArticleOrder.TIME.toString,
Expand All @@ -290,9 +290,13 @@ class FolderListView(folderService: FolderService, sourceService: SourceService,
label(div("Article List Layout"), select(
name := "articleListLayout",
option(value := ArticleListLayout.LIST.toString,
if (folder.articleListLayout == ArticleListLayout.LIST) selected else "")("Vertical List"),
if (folder.articleListLayout == ArticleListLayout.LIST) selected else "")("List"),
option(value := ArticleListLayout.GRID.toString,
if (folder.articleListLayout == ArticleListLayout.GRID) selected else "")("Grid"),
option(value := ArticleListLayout.SOCIAL_MEDIA.toString,
if (folder.articleListLayout == ArticleListLayout.SOCIAL_MEDIA) selected else "")("Social Media"),
option(value := ArticleListLayout.COMPACT.toString,
if (folder.articleListLayout == ArticleListLayout.COMPACT) selected else "")("Compact"),
)),
div(cls := "button-row",
button(cls := "secondary", ContentRender.hxSwapContentAttrs,
Expand Down
Loading

0 comments on commit 559f7f2

Please sign in to comment.