Skip to content

Commit

Permalink
[1.44.*] Pre-release merge (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
tramline-github[bot] authored Jan 26, 2024
2 parents 03856e9 + f817a6d commit 7ec7652
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2024 Sasikanth Miriyampalli
*
* 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 dev.sasikanth.rss.reader.core.network.post

import dev.sasikanth.rss.reader.di.scopes.AppScope
import dev.sasikanth.rss.reader.util.DispatchersProvider
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.withContext
import me.tatarka.inject.annotations.Inject

@Inject
@AppScope
class PostSourceFetcher(
private val httpClient: HttpClient,
private val dispatchersProvider: DispatchersProvider
) {

suspend fun fetch(link: String): String? {
return withContext(dispatchersProvider.io) {
val response = httpClient.get(link)
if (response.status == HttpStatusCode.OK) {
response.bodyAsText()
} else {
null
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2024 Sasikanth Miriyampalli
*
* 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 dev.sasikanth.rss.reader.resources.icons

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.ImageVector.Builder
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.unit.dp

val TwineIcons.ArticleShortcut: ImageVector
get() {
if (articleShortcut != null) {
return articleShortcut!!
}
articleShortcut =
Builder(
name = "ArticleShortcut",
defaultWidth = 24.0.dp,
defaultHeight = 24.0.dp,
viewportWidth = 960.0f,
viewportHeight = 960.0f
)
.apply {
path(
fill = SolidColor(Color(0xFF000000)),
stroke = null,
strokeLineWidth = 0.0f,
strokeLineCap = Butt,
strokeLineJoin = Miter,
strokeLineMiter = 4.0f,
pathFillType = NonZero
) {
moveTo(400.0f, 680.0f)
horizontalLineToRelative(160.0f)
verticalLineToRelative(-80.0f)
lineTo(400.0f, 600.0f)
verticalLineToRelative(80.0f)
close()
moveTo(400.0f, 520.0f)
horizontalLineToRelative(280.0f)
verticalLineToRelative(-80.0f)
lineTo(400.0f, 440.0f)
verticalLineToRelative(80.0f)
close()
moveTo(280.0f, 360.0f)
horizontalLineToRelative(400.0f)
verticalLineToRelative(-80.0f)
lineTo(280.0f, 280.0f)
verticalLineToRelative(80.0f)
close()
moveTo(480.0f, 480.0f)
close()
moveTo(265.0f, 880.0f)
quadToRelative(-79.0f, 0.0f, -134.5f, -55.5f)
reflectiveQuadTo(75.0f, 690.0f)
quadToRelative(0.0f, -57.0f, 29.5f, -102.0f)
reflectiveQuadToRelative(77.5f, -68.0f)
lineTo(80.0f, 520.0f)
verticalLineToRelative(-80.0f)
horizontalLineToRelative(240.0f)
verticalLineToRelative(240.0f)
horizontalLineToRelative(-80.0f)
verticalLineToRelative(-97.0f)
quadToRelative(-37.0f, 8.0f, -61.0f, 38.0f)
reflectiveQuadToRelative(-24.0f, 69.0f)
quadToRelative(0.0f, 46.0f, 32.5f, 78.0f)
reflectiveQuadToRelative(77.5f, 32.0f)
verticalLineToRelative(80.0f)
close()
moveTo(400.0f, 840.0f)
verticalLineToRelative(-80.0f)
horizontalLineToRelative(360.0f)
verticalLineToRelative(-560.0f)
lineTo(200.0f, 200.0f)
verticalLineToRelative(160.0f)
horizontalLineToRelative(-80.0f)
verticalLineToRelative(-160.0f)
quadToRelative(0.0f, -33.0f, 23.5f, -56.5f)
reflectiveQuadTo(200.0f, 120.0f)
horizontalLineToRelative(560.0f)
quadToRelative(33.0f, 0.0f, 56.5f, 23.5f)
reflectiveQuadTo(840.0f, 200.0f)
verticalLineToRelative(560.0f)
quadToRelative(0.0f, 33.0f, -23.5f, 56.5f)
reflectiveQuadTo(760.0f, 840.0f)
lineTo(400.0f, 840.0f)
close()
}
}
.build()
return articleShortcut!!
}

private var articleShortcut: ImageVector? = null
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package dev.sasikanth.rss.reader.di
import android.content.Context
import android.os.Build
import dev.sasikanth.rss.reader.app.AppInfo
import dev.sasikanth.rss.reader.core.network.post.PostSourceFetcher
import dev.sasikanth.rss.reader.di.scopes.AppScope
import dev.sasikanth.rss.reader.repository.RssRepository
import dev.sasikanth.rss.reader.repository.SettingsRepository
Expand All @@ -33,6 +34,8 @@ abstract class ApplicationComponent(@get:Provides val context: Context) :

abstract val settingsRepository: SettingsRepository

abstract val postSourceFetcher: PostSourceFetcher

@Provides
@AppScope
fun providesAppInfo(context: Context): AppInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ sealed interface ReaderEvent {
data object BackClicked : ReaderEvent

data object TogglePostBookmark : ReaderEvent

data object ArticleShortcutClicked : ReaderEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.lifecycle.doOnCreate
import dev.sasikanth.readability.Readability
import dev.sasikanth.rss.reader.core.network.post.PostSourceFetcher
import dev.sasikanth.rss.reader.repository.RssRepository
import dev.sasikanth.rss.reader.util.DispatchersProvider
import dev.sasikanth.rss.reader.util.relativeDurationString
Expand All @@ -40,6 +41,7 @@ import me.tatarka.inject.annotations.Inject
class ReaderPresenter(
dispatchersProvider: DispatchersProvider,
private val rssRepository: RssRepository,
private val postSourceFetcher: PostSourceFetcher,
@Assisted private val postLink: String,
@Assisted componentContext: ComponentContext,
@Assisted private val goBack: () -> Unit
Expand All @@ -50,7 +52,8 @@ class ReaderPresenter(
PresenterInstance(
dispatchersProvider = dispatchersProvider,
rssRepository = rssRepository,
postLink = postLink
postLink = postLink,
postSourceFetcher = postSourceFetcher
)
}

Expand All @@ -75,6 +78,7 @@ class ReaderPresenter(
private val dispatchersProvider: DispatchersProvider,
private val rssRepository: RssRepository,
private val postLink: String,
private val postSourceFetcher: PostSourceFetcher,
) : InstanceKeeper.Instance {

private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchersProvider.main)
Expand All @@ -94,6 +98,7 @@ class ReaderPresenter(
/* no-op */
}
ReaderEvent.TogglePostBookmark -> togglePostBookmark(postLink)
ReaderEvent.ArticleShortcutClicked -> articleShortcutClicked()
}
}

Expand Down Expand Up @@ -139,7 +144,18 @@ class ReaderPresenter(
}
}

private suspend fun extractArticleHtmlContent(feedLink: String, content: String) =
withContext(dispatchersProvider.io) { Readability(feedLink, content) }.parse().content
private suspend fun extractArticleHtmlContent(postLink: String, content: String) =
withContext(dispatchersProvider.io) { Readability(postLink, content) }.parse().content

private fun articleShortcutClicked() {
coroutineScope.launch {
val content = postSourceFetcher.fetch(postLink)
if (content != null) {
_state.update { it.copy(isFetchingFullArticle = true) }
val htmlContent = extractArticleHtmlContent(postLink, content)
_state.update { it.copy(content = htmlContent, isFetchingFullArticle = false) }
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ internal data class ReaderState(
val content: String?,
val publishedAt: String?,
val isBookmarked: Boolean?,
val feed: Feed? = null
val feed: Feed?,
val isFetchingFullArticle: Boolean?
) {

val hasContent: Boolean
Expand All @@ -42,7 +43,8 @@ internal data class ReaderState(
content = null,
publishedAt = null,
isBookmarked = null,
feed = null
feed = null,
isFetchingFullArticle = null
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ package dev.sasikanth.rss.reader.reader.ui

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
Expand Down Expand Up @@ -55,6 +55,7 @@ import dev.sasikanth.material.color.utilities.utils.StringUtils
import dev.sasikanth.rss.reader.platform.LocalLinkHandler
import dev.sasikanth.rss.reader.reader.ReaderEvent
import dev.sasikanth.rss.reader.reader.ReaderPresenter
import dev.sasikanth.rss.reader.resources.icons.ArticleShortcut
import dev.sasikanth.rss.reader.resources.icons.Bookmark
import dev.sasikanth.rss.reader.resources.icons.Bookmarked
import dev.sasikanth.rss.reader.resources.icons.Share
Expand Down Expand Up @@ -107,27 +108,49 @@ internal fun ReaderScreen(presenter: ReaderPresenter, modifier: Modifier = Modif
modifier =
Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.navigationBars)
.padding(vertical = 8.dp)
.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(Modifier.weight(1f))
val bookmarkIcon =
if (state.isBookmarked == true) {
TwineIcons.Bookmarked
Box(Modifier.weight(1f), contentAlignment = Alignment.Center) {
val bookmarkIcon =
if (state.isBookmarked == true) {
TwineIcons.Bookmarked
} else {
TwineIcons.Bookmark
}
IconButton(onClick = { presenter.dispatch(ReaderEvent.TogglePostBookmark) }) {
Icon(bookmarkIcon, contentDescription = null)
}
}

Box(Modifier.weight(1f), contentAlignment = Alignment.Center) {
if (state.isFetchingFullArticle == true) {
CircularProgressIndicator(
color = AppTheme.colorScheme.tintedForeground,
modifier = Modifier.requiredSize(24.dp)
)
} else {
TwineIcons.Bookmark
IconButton(
onClick = {
coroutineScope.launch { presenter.dispatch(ReaderEvent.ArticleShortcutClicked) }
}
) {
Icon(TwineIcons.ArticleShortcut, contentDescription = null)
}
}
IconButton(onClick = { presenter.dispatch(ReaderEvent.TogglePostBookmark) }) {
Icon(bookmarkIcon, contentDescription = null)
}
Spacer(Modifier.weight(1f))
IconButton(onClick = { coroutineScope.launch { linkHandler.openLink(state.link) } }) {
Icon(TwineIcons.Website, contentDescription = null)

Box(Modifier.weight(1f), contentAlignment = Alignment.Center) {
IconButton(onClick = { coroutineScope.launch { linkHandler.openLink(state.link) } }) {
Icon(TwineIcons.Website, contentDescription = null)
}
}
Spacer(Modifier.weight(1f))
IconButton(onClick = { coroutineScope.launch { sharedHandler.share(state.link) } }) {
Icon(TwineIcons.Share, contentDescription = null)

Box(Modifier.weight(1f), contentAlignment = Alignment.Center) {
IconButton(onClick = { coroutineScope.launch { sharedHandler.share(state.link) } }) {
Icon(TwineIcons.Share, contentDescription = null)
}
}
Spacer(Modifier.weight(1f))
}
}
},
Expand Down Expand Up @@ -163,11 +186,12 @@ internal fun ReaderScreen(presenter: ReaderPresenter, modifier: Modifier = Modif
val dividerColor =
StringUtils.hexFromArgb(AppTheme.colorScheme.surfaceContainerHigh.toArgb())

val htmlTemplate = remember {
// TODO: Extract out the HTML rendering and customisation to separate class
// with actual templating
// language=HTML
"""
val htmlTemplate =
remember(state.content) {
// TODO: Extract out the HTML rendering and customisation to separate class
// with actual templating
// language=HTML
"""
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
Expand Down Expand Up @@ -299,16 +323,17 @@ internal fun ReaderScreen(presenter: ReaderPresenter, modifier: Modifier = Modif
</body>
</html>
"""
.trimIndent()
}
.trimIndent()
}
val webViewState = rememberWebViewStateWithHTMLData(htmlTemplate)

Box(Modifier.fillMaxSize().padding(paddingValues).padding(horizontal = 16.dp)) {
WebView(
modifier = Modifier.fillMaxSize(),
state = webViewState,
navigator = navigator,
webViewJsBridge = jsBridge
webViewJsBridge = jsBridge,
captureBackPresses = false
)
}
}
Expand Down

0 comments on commit 7ec7652

Please sign in to comment.