app: implement first draft of lobste.rs UI

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-09-23 03:29:21 +05:30
parent 6e4a9c6a73
commit 63f5bea155
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
6 changed files with 147 additions and 20 deletions

View file

@ -38,6 +38,7 @@ dependencies {
kapt "com.google.dagger:hilt-android-compiler:$hilt_version" kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation(project(":data")) implementation(project(":data"))
implementation(project(":lobsters-api")) implementation(project(":lobsters-api"))
implementation(project(":model"))
implementation 'androidx.core:core-ktx:1.5.0-alpha03' implementation 'androidx.core:core-ktx:1.5.0-alpha03'
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation "androidx.compose.foundation:foundation:$compose_version" implementation "androidx.compose.foundation:foundation:$compose_version"

View file

@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.msfjarvis.lobsters"> package="dev.msfjarvis.lobsters">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:name=".Application" android:name=".Application"
android:allowBackup="true" android:allowBackup="true"

View file

@ -5,6 +5,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Text import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumnFor
import androidx.compose.material.AlertDialog import androidx.compose.material.AlertDialog
import androidx.compose.material.Button import androidx.compose.material.Button
import androidx.compose.material.FloatingActionButton import androidx.compose.material.FloatingActionButton
@ -16,8 +17,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Providers import androidx.compose.runtime.Providers
import androidx.compose.runtime.ambientOf import androidx.compose.runtime.ambientOf
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -28,15 +29,20 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.ui.tooling.preview.Preview
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.msfjarvis.lobsters.api.LobstersApi
import dev.msfjarvis.lobsters.compose.utils.IconResource import dev.msfjarvis.lobsters.compose.utils.IconResource
import dev.msfjarvis.lobsters.data.model.TodoItem import dev.msfjarvis.lobsters.data.model.TodoItem
import dev.msfjarvis.lobsters.data.source.TodoDatabase import dev.msfjarvis.lobsters.data.source.TodoDatabase
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.ui.ListContent import dev.msfjarvis.lobsters.ui.ListContent
import dev.msfjarvis.lobsters.ui.LobstersItem
import dev.msfjarvis.lobsters.ui.LobstersTheme import dev.msfjarvis.lobsters.ui.LobstersTheme
import dev.msfjarvis.lobsters.urllauncher.UrlLauncher import dev.msfjarvis.lobsters.urllauncher.UrlLauncher
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject import javax.inject.Inject
val UrlLauncherAmbient = ambientOf<UrlLauncher> { error("Needs to be provided") } val UrlLauncherAmbient = ambientOf<UrlLauncher> { error("Needs to be provided") }
@ -45,6 +51,7 @@ val UrlLauncherAmbient = ambientOf<UrlLauncher> { error("Needs to be provided")
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@Inject lateinit var database: TodoDatabase @Inject lateinit var database: TodoDatabase
@Inject lateinit var urlLauncher: UrlLauncher @Inject lateinit var urlLauncher: UrlLauncher
@Inject lateinit var apiClient: LobstersApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -52,6 +59,25 @@ class MainActivity : AppCompatActivity() {
Providers(UrlLauncherAmbient provides urlLauncher) { Providers(UrlLauncherAmbient provides urlLauncher) {
LobstersTheme { LobstersTheme {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val posts = mutableStateListOf<LobstersPost>()
coroutineScope.launch {
apiClient.getHottestPosts().enqueue(object : Callback<List<LobstersPost>> {
override fun onResponse(
call: Call<List<LobstersPost>>,
response: Response<List<LobstersPost>>
) {
if (response.isSuccessful) {
response.body()?.let { posts.addAll(it) }
}
}
override fun onFailure(call: Call<List<LobstersPost>>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
LobstersApp(posts)
/*
val itemsDao = database.todoItemsDao() val itemsDao = database.todoItemsDao()
val items by itemsDao.getAllItems().collectAsState(initial = emptyList()) val items by itemsDao.getAllItems().collectAsState(initial = emptyList())
TodoApp( TodoApp(
@ -59,12 +85,31 @@ class MainActivity : AppCompatActivity() {
{ item -> coroutineScope.launch { itemsDao.insert(item) } }, { item -> coroutineScope.launch { itemsDao.insert(item) } },
{ item -> coroutineScope.launch { itemsDao.delete(item) } }, { item -> coroutineScope.launch { itemsDao.delete(item) } },
) )
*/
} }
} }
} }
} }
} }
@Composable
fun LobstersApp(
items: List<LobstersPost>,
) {
val urlLauncher = UrlLauncherAmbient.current
Scaffold(
topBar = { TopAppBar({ Text(text = stringResource(R.string.app_name)) }) },
bodyContent = {
LazyColumnFor(items) { item ->
LobstersItem(item) { post ->
urlLauncher.launch(post.url)
}
}
}
)
}
@Composable @Composable
fun TodoApp( fun TodoApp(
items: List<TodoItem>, items: List<TodoItem>,
@ -145,6 +190,7 @@ fun ItemAddDialog(
) )
} }
/*
@Preview @Preview
@Composable @Composable
fun PreviewApp() { fun PreviewApp() {
@ -157,3 +203,4 @@ fun PreviewApp() {
) )
} }
} }
*/

View file

@ -0,0 +1,90 @@
package dev.msfjarvis.lobsters.ui
import androidx.compose.foundation.Text
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumnFor
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ListItem
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.model.Submitter
@Composable
fun LazyItemScope.LobstersItem(
post: LobstersPost,
modifier: Modifier = Modifier,
onClick: (LobstersPost) -> Unit,
) {
ListItem(
modifier = modifier.padding(horizontal = 8.dp)
.fillParentMaxWidth()
.clickable(onClick = { onClick.invoke(post) }),
text = {
Column {
Text(
text = post.title,
color = Color(0xFF7395D9),
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(top = 8.dp)
)
Row {
post.tags.forEach { tag ->
Text(
text = tag,
modifier = Modifier
.background(Color(0xFFE2E0C5), RoundedCornerShape(4.dp)),
color = Color.DarkGray,
)
}
}
Text(
text = "authored by ${post.submitterUser.username}",
)
}
}
)
}
@Composable
fun PreviewLobstersItem() {
val post = LobstersPost(
"zqyydb",
"https://lobste.rs/s/zqyydb",
"2020-09-21T07:11:14.000-05:00",
"k2k20 hackathon report: Bob Beck on LibreSSL progress",
"https://undeadly.org/cgi?action=article;sid=20200921105847",
4,
0,
0,
"",
"https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
Submitter(
"Vigdis",
"2017-02-27T21:08:14.000-06:00",
false,
"Alleycat for the fun, sys/net admin for a living and OpenBSD contributions for the pleasure. (Not so) French dude in Montreal\r\n\r\nhttps://chown.me",
false,
76,
"/avatars/Vigdis-100.png",
"sevan",
null,
null,
null,
),
listOf("openbsd")
)
LobstersTheme {
LazyColumnFor(items = listOf(post)) { item ->
LobstersItem(post = item, onClick = {})
}
}
}

View file

@ -1,38 +1,25 @@
package dev.msfjarvis.lobsters.ui package dev.msfjarvis.lobsters.ui
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
val lightColors = lightColors( val lightColors = lightColors(
primary = Color.White, primary = Color.White,
secondary = Color(0xFFAC130D), secondary = Color(0xFF6C0000),
background = Color(0xFFFEFEFE), background = Color.White,
surface = Color.White, surface = Color.White,
onPrimary = Color(0x7395D9), onPrimary = Color.DarkGray,
onSecondary = Color.White, onSecondary = Color.White,
onBackground = Color.Black, onBackground = Color.Black,
onSurface = Color.Black, onSurface = Color.Black,
) )
val darkColors = darkColors(
primary = Color(0xFF121212),
secondary = Color(0xFFAC130D),
background = Color.Black,
surface = Color(0xFF121212),
onPrimary = Color(0x7395D9),
onSecondary = Color.White,
onBackground = Color.White,
onSurface = Color.White,
)
@Composable @Composable
fun LobstersTheme(children: @Composable () -> Unit) { fun LobstersTheme(children: @Composable () -> Unit) {
MaterialTheme( MaterialTheme(
colors = if (isSystemInDarkTheme()) darkColors else lightColors, colors = lightColors,
content = children, content = children,
) )
} }

View file

@ -7,8 +7,8 @@ dependencies {
def moshi_version = "1.9.3" def moshi_version = "1.9.3"
def retrofit_version = "2.9.0" def retrofit_version = "2.9.0"
implementation project(":model") implementation project(":model")
api "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
kaptTest "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" kaptTest "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
// retrofit uses 3.14.9, so shall we. // retrofit uses 3.14.9, so shall we.