all: remove unused desktop code

This commit is contained in:
Harsh Shandilya 2022-08-03 00:17:50 +05:30
parent 325a4e34f6
commit 0cb4393947
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
6 changed files with 1 additions and 511 deletions

View file

@ -1,50 +0,0 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
kotlin("multiplatform")
alias(libs.plugins.compose)
id("dev.msfjarvis.claw.kotlin-common")
}
group = "dev.msfjarvis.claw"
version = "1.0"
kotlin {
jvm { compilations.all { kotlinOptions.jvmTarget = "11" } }
sourceSets["jvmMain"].apply {
dependencies {
implementation(compose.desktop.currentOs)
implementation(projects.api)
implementation(projects.common)
implementation(libs.aurora.component)
implementation(libs.aurora.theming)
implementation(libs.aurora.window)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.ktor.client.java)
implementation(libs.multiplatform.paging)
implementation(libs.retrofit.lib)
implementation(libs.retrofit.kotlinxSerializationConverter)
}
}
}
compose.desktop {
application {
mainClass = "MainKt"
jvmArgs += listOf("-Xmx1G")
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Claw"
packageVersion = "1.0.0"
description = "Desktop client for lobste.rs link aggregation site"
copyright = "© 2021 Harsh Shandilya. All rights reserved."
vendor = "Harsh Shandilya"
includeAllModules = false
}
}
}

View file

@ -1,37 +0,0 @@
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dev.msfjarvis.claw.api.LobstersApi
import io.github.aakira.napier.Napier
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.create
@OptIn(ExperimentalSerializationApi::class)
class Api {
private val json = Json { ignoreUnknownKeys = true }
private fun getOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor { chain ->
val request = chain.request()
Napier.d(tag = "LobstersApi") { "${request.method()}: ${request.url()}" }
chain.proceed(request)
}
.build()
}
private fun getRetrofit(
okHttpClient: OkHttpClient,
): Retrofit {
val contentType = MediaType.get("application/json")
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(LobstersApi.BASE_URL)
.addConverterFactory(json.asConverterFactory(contentType))
.build()
}
val api: LobstersApi = getRetrofit(getOkHttpClient()).create()
}

View file

@ -1,280 +0,0 @@
/*
* Copyright 2020 The Android Open Source Project
*
* 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.
*/
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.paging.CombinedLoadStates
import androidx.paging.DifferCallback
import androidx.paging.ItemSnapshotList
import androidx.paging.LoadState
import androidx.paging.LoadStates
import androidx.paging.NullPaddedList
import androidx.paging.PagingData
import androidx.paging.PagingDataDiffer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
/**
* The class responsible for accessing the data from a [Flow] of [PagingData]. In order to obtain an
* instance of [LazyPagingItems] use the [collectAsLazyPagingItems] extension method of [Flow] with
* [PagingData]. This instance can be used by the [items] and [itemsIndexed] methods inside
* [LazyListScope] to display data received from the [Flow] of [PagingData].
*
* @param T the type of value used by [PagingData].
*/
class LazyPagingItems<T : Any>
internal constructor(
/** the [Flow] object which contains a stream of [PagingData] elements. */
private val flow: Flow<PagingData<T>>
) {
private val mainDispatcher = Dispatchers.Default
/**
* Contains the immutable [ItemSnapshotList] of currently presented items, including any
* placeholders if they are enabled. Note that similarly to [peek] accessing the items in a list
* will not trigger any loads. Use [get] to achieve such behavior.
*/
var itemSnapshotList by mutableStateOf(ItemSnapshotList<T>(0, 0, emptyList()))
private set
/** The number of items which can be accessed. */
val itemCount: Int
get() = itemSnapshotList.size
private val differCallback: DifferCallback =
object : DifferCallback {
override fun onChanged(position: Int, count: Int) {
if (count > 0) {
updateItemSnapshotList()
}
}
override fun onInserted(position: Int, count: Int) {
if (count > 0) {
updateItemSnapshotList()
}
}
override fun onRemoved(position: Int, count: Int) {
if (count > 0) {
updateItemSnapshotList()
}
}
}
private val pagingDataDiffer =
object : PagingDataDiffer<T>(differCallback = differCallback, mainDispatcher = mainDispatcher) {
override suspend fun presentNewList(
previousList: NullPaddedList<T>,
newList: NullPaddedList<T>,
lastAccessedIndex: Int,
onListPresentable: () -> Unit
): Int? {
onListPresentable()
updateItemSnapshotList()
return null
}
}
private fun updateItemSnapshotList() {
itemSnapshotList = pagingDataDiffer.snapshot()
}
/**
* Returns the presented item at the specified position, notifying Paging of the item access to
* trigger any loads necessary to fulfill prefetchDistance.
*
* @see peek
*/
operator fun get(index: Int): T? {
pagingDataDiffer[index] // this registers the value load
return itemSnapshotList[index]
}
/**
* Returns the presented item at the specified position, without notifying Paging of the item
* access that would normally trigger page loads.
*
* @param index Index of the presented item to return, including placeholders.
* @return The presented item at position [index], `null` if it is a placeholder
*/
fun peek(index: Int): T? {
return itemSnapshotList[index]
}
/**
* Retry any failed load requests that would result in a [LoadState.Error] update to this
* [LazyPagingItems].
*
* Unlike [refresh], this does not invalidate [PagingSource], it only retries failed loads within
* the same generation of [PagingData].
*
* [LoadState.Error] can be generated from two types of load requests:
* * [PagingSource.load] returning [PagingSource.LoadResult.Error]
* * [RemoteMediator.load] returning [RemoteMediator.MediatorResult.Error]
*/
fun retry() {
pagingDataDiffer.retry()
}
/**
* Refresh the data presented by this [LazyPagingItems].
*
* [refresh] triggers the creation of a new [PagingData] with a new instance of [PagingSource] to
* represent an updated snapshot of the backing dataset. If a [RemoteMediator] is set, calling
* [refresh] will also trigger a call to [RemoteMediator.load] with [LoadType] [REFRESH] to allow
* [RemoteMediator] to check for updates to the dataset backing [PagingSource].
*
* Note: This API is intended for UI-driven refresh signals, such as swipe-to-refresh.
* Invalidation due repository-layer signals, such as DB-updates, should instead use
* [PagingSource.invalidate].
*
* @see PagingSource.invalidate
*/
fun refresh() {
pagingDataDiffer.refresh()
}
/** A [CombinedLoadStates] object which represents the current loading state. */
var loadState: CombinedLoadStates by
mutableStateOf(
CombinedLoadStates(
refresh = InitialLoadStates.refresh,
prepend = InitialLoadStates.prepend,
append = InitialLoadStates.append,
source = InitialLoadStates
)
)
private set
internal suspend fun collectLoadState() {
pagingDataDiffer.loadStateFlow.collect { loadState = it }
}
internal suspend fun collectPagingData() {
flow.collectLatest { pagingDataDiffer.collectFrom(it) }
}
}
private val IncompleteLoadState = LoadState.NotLoading(false)
private val InitialLoadStates =
LoadStates(IncompleteLoadState, IncompleteLoadState, IncompleteLoadState)
/**
* Collects values from this [Flow] of [PagingData] and represents them inside a [LazyPagingItems]
* instance. The [LazyPagingItems] instance can be used by the [items] and [itemsIndexed] methods
* from [LazyListScope] in order to display the data obtained from a [Flow] of [PagingData].
*
* @sample androidx.paging.compose.samples.PagingBackendSample
*/
@Composable
fun <T : Any> Flow<PagingData<T>>.collectAsLazyPagingItems(): LazyPagingItems<T> {
val lazyPagingItems = remember(this) { LazyPagingItems(this) }
LaunchedEffect(lazyPagingItems) { lazyPagingItems.collectPagingData() }
LaunchedEffect(lazyPagingItems) { lazyPagingItems.collectLoadState() }
return lazyPagingItems
}
/**
* Adds the [LazyPagingItems] and their content to the scope. The range from 0 (inclusive) to
* [LazyPagingItems.itemCount] (exclusive) always represents the full range of presentable items,
* because every event from [PagingDataDiffer] will trigger a recomposition.
*
* @sample androidx.paging.compose.samples.ItemsDemo
*
* @param items the items received from a [Flow] of [PagingData].
* @param key a factory of stable and unique keys representing the item. Using the same key for
* multiple items in the list is not allowed. Type of the key should be saveable via Bundle on
* Android. If null is passed the position in the list will represent the key. When you specify the
* key the scroll position will be maintained based on the key, which means if you add/remove items
* before the current visible item the item with the given key will be kept as the first visible
* one.
* @param itemContent the content displayed by a single item. In case the item is `null`, the
* [itemContent] method should handle the logic of displaying a placeholder instead of the main
* content displayed by an item which is not `null`.
*/
fun <T : Any> LazyListScope.items(
items: LazyPagingItems<T>,
key: ((item: T) -> Any)? = null,
itemContent: @Composable LazyItemScope.(value: T?) -> Unit
) {
items(
count = items.itemCount,
key =
if (key == null) null
else
{ index ->
val item = items.peek(index)
if (item == null) {
PagingPlaceholderKey(index)
} else {
key(item)
}
}
) { index -> itemContent(items[index]) }
}
/**
* Adds the [LazyPagingItems] and their content to the scope where the content of an item is aware
* of its local index. The range from 0 (inclusive) to [LazyPagingItems.itemCount] (exclusive)
* always represents the full range of presentable items, because every event from
* [PagingDataDiffer] will trigger a recomposition.
*
* @sample androidx.paging.compose.samples.ItemsIndexedDemo
*
* @param items the items received from a [Flow] of [PagingData].
* @param key a factory of stable and unique keys representing the item. Using the same key for
* multiple items in the list is not allowed. Type of the key should be saveable via Bundle on
* Android. If null is passed the position in the list will represent the key. When you specify the
* key the scroll position will be maintained based on the key, which means if you add/remove items
* before the current visible item the item with the given key will be kept as the first visible
* one.
* @param itemContent the content displayed by a single item. In case the item is `null`, the
* [itemContent] method should handle the logic of displaying a placeholder instead of the main
* content displayed by an item which is not `null`.
*/
fun <T : Any> LazyListScope.itemsIndexed(
items: LazyPagingItems<T>,
key: ((index: Int, item: T) -> Any)? = null,
itemContent: @Composable LazyItemScope.(index: Int, value: T?) -> Unit
) {
items(
count = items.itemCount,
key =
if (key == null) null
else
{ index ->
val item = items.peek(index)
if (item == null) {
PagingPlaceholderKey(index)
} else {
key(index, item)
}
}
) { index -> itemContent(index, items[index]) }
}
private data class PagingPlaceholderKey(private val index: Int)

View file

@ -1,31 +0,0 @@
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import com.kuuurt.paging.multiplatform.Pager
import com.kuuurt.paging.multiplatform.PagingResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
class Paging(
private val coroutineScope: CoroutineScope,
) {
private val api = Api()
private val pager =
Pager(
clientScope = coroutineScope,
config = PagingConfig(20),
initialKey = 1,
getItems = { currentKey, _ ->
val items = api.api.getHottestPosts(currentKey)
PagingResult(
items = items,
currentKey = currentKey,
prevKey = { if (currentKey == 1) null else currentKey - 1 },
nextKey = { currentKey + 1 },
)
}
)
val pagingData
get() = pager.pagingData.cachedIn(coroutineScope)
}

View file

@ -1,104 +0,0 @@
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.rememberWindowState
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.common.posts.LobstersCard
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.toDbModel
import dev.msfjarvis.claw.common.theme.DarkThemeColors
import dev.msfjarvis.claw.common.theme.LightThemeColors
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import dev.msfjarvis.claw.database.local.SavedPost
import org.pushingpixels.aurora.component.AuroraVerticalScrollbar
import org.pushingpixels.aurora.theming.ceruleanSkin
import org.pushingpixels.aurora.window.AuroraWindow
import org.pushingpixels.aurora.window.auroraApplication
fun main() = auroraApplication {
val paging = Paging(rememberCoroutineScope())
val items = paging.pagingData.collectAsLazyPagingItems()
val urlLauncher = UrlLauncher()
val state =
rememberWindowState(
placement = WindowPlacement.Floating,
position = WindowPosition.Aligned(Alignment.Center),
)
val postActions = remember {
object : PostActions {
override fun viewPost(postUrl: String, commentsUrl: String) {
urlLauncher.openUri(postUrl.ifEmpty { commentsUrl })
}
override fun viewComments(postId: String) {
urlLauncher.openUri("${LobstersApi.BASE_URL}/s/${postId}")
}
override fun viewCommentsPage(commentsUrl: String) {
urlLauncher.openUri(commentsUrl)
}
override fun toggleSave(post: SavedPost) {}
}
}
AuroraWindow(
skin = ceruleanSkin(),
title = "Claw",
state = state,
undecorated = true,
onCloseRequest = ::exitApplication,
) {
val colorScheme =
if (isSystemInDarkTheme()) {
DarkThemeColors
} else {
LightThemeColors
}
LobstersTheme(
colorScheme = colorScheme,
providedValues = arrayOf(LocalUriHandler provides urlLauncher),
) {
Box(
modifier = Modifier.fillMaxSize(),
) {
val listState = rememberLazyListState()
if (items.itemCount == 0) {
Box(modifier = Modifier.fillMaxSize())
} else {
LazyColumn(
state = listState,
) {
items(items) { item ->
if (item != null) {
LobstersCard(
post = item.toDbModel(),
isSaved = false,
postActions = postActions,
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
)
}
}
}
}
AuroraVerticalScrollbar(
adapter = rememberScrollbarAdapter(listState),
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
)
}
}
}
}

View file

@ -2,7 +2,6 @@
accompanist = "0.26.0-alpha" accompanist = "0.26.0-alpha"
agp = "7.2.1" agp = "7.2.1"
androidXTest = "1.4.0" androidXTest = "1.4.0"
aurora = "1.2-SNAPSHOT"
coil = "2.1.0" coil = "2.1.0"
# @keep This is used by paparazzi-tests and Renovate # @keep This is used by paparazzi-tests and Renovate
composeCompiler = "1.3.0-rc01" composeCompiler = "1.3.0-rc01"
@ -47,9 +46,6 @@ androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidXT
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidXTest" } androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidXTest" }
androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0" androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0"
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workmanager" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workmanager" }
aurora-component = { module = "org.pushing-pixels:aurora-component", version.ref = "aurora" }
aurora-theming = { module = "org.pushing-pixels:aurora-theming", version.ref = "aurora" }
aurora-window = { module = "org.pushing-pixels:aurora-window", version.ref = "aurora" }
build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
build-cachefix = "org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin:2.5.5" build-cachefix = "org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin:2.5.5"
build-kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } build-kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
@ -66,15 +62,12 @@ copydown = "io.github.furstenheim:copy_down:1.0"
dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "dagger" } dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "dagger" }
dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" } dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
javapoet = "com.squareup:javapoet:1.13.0" javapoet = "com.squareup:javapoet:1.13.0"
kamel-image = "com.alialbaali.kamel:kamel-image:0.4.1"
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0" kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0"
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" } kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
ktor-client-java = "io.ktor:ktor-client-java:2.0.3"
material_motion-core = { module = "io.github.fornewid:material-motion-compose-core", version.ref = "material_motion" } material_motion-core = { module = "io.github.fornewid:material-motion-compose-core", version.ref = "material_motion" }
material_motion-navigation = { module = "io.github.fornewid:material-motion-compose-navigation", version.ref = "material_motion" } material_motion-navigation = { module = "io.github.fornewid:material-motion-compose-navigation", version.ref = "material_motion" }
multiplatform-paging = "io.github.kuuuurt:multiplatform-paging:0.4.7"
napier = "io.github.aakira:napier:2.6.1" napier = "io.github.aakira:napier:2.6.1"
okhttp-loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:3.14.9" okhttp-loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:3.14.9"
retrofit-kotlinxSerializationConverter = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" retrofit-kotlinxSerializationConverter = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
@ -88,8 +81,7 @@ testparameterinjector = "com.google.testparameterinjector:test-parameter-injecto
[plugins] [plugins]
android-test = { id = "com.android.test", version.ref = "agp" } android-test = { id = "com.android.test", version.ref = "agp" }
aurora-svg-transcoder = { id = "org.pushing-pixels.aurora.tools.svgtranscoder.gradle", version.ref = "aurora" } aurora-svg-transcoder = "org.pushing-pixels.aurora.tools.svgtranscoder.gradle:1.2-SNAPSHOT"
compose = "org.jetbrains.compose:1.2.0-alpha01-dev753"
hilt = { id = "com.google.dagger.hilt.android", version.ref = "dagger" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "dagger" }
paparazzi = "app.cash.paparazzi:1.0.0" paparazzi = "app.cash.paparazzi:1.0.0"
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }