mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #1159 from omnivore-app/feature/android-home-feed
Home Feed and WebView (Android)
This commit is contained in:
commit
7b4063822a
15 changed files with 471 additions and 54 deletions
|
|
@ -139,12 +139,6 @@ dependencies {
|
|||
implementation "androidx.security:security-crypto:1.0.0"
|
||||
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||
|
||||
|
||||
|
||||
// Activity KTX for viewModels()
|
||||
// TODO: remove this since compose-activity imports ktx?
|
||||
// implementation "androidx.activity:activity-ktx:1.3.1"
|
||||
|
||||
//Dagger - Hilt
|
||||
implementation 'com.google.dagger:hilt-android:2.42'
|
||||
kapt 'com.google.dagger:hilt-compiler:2.42'
|
||||
|
|
@ -156,6 +150,8 @@ dependencies {
|
|||
|
||||
implementation 'com.google.android.gms:play-services-auth:20.2.0'
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:0.25.1"
|
||||
|
||||
implementation("io.coil-kt:coil-compose:2.2.0")
|
||||
}
|
||||
|
||||
apollo {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mutation SaveUrl ($input: SaveUrlInput!) {
|
||||
saveUrl(input:$input){
|
||||
mutation SaveUrl($input: SaveUrlInput!) {
|
||||
saveUrl(input: $input) {
|
||||
... on SaveSuccess {
|
||||
url
|
||||
}
|
||||
|
|
@ -7,4 +7,4 @@ mutation SaveUrl ($input: SaveUrlInput!) {
|
|||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
51
android/Omnivore/app/src/main/graphql/Search.graphql
Normal file
51
android/Omnivore/app/src/main/graphql/Search.graphql
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
query Search($after: String, $first: Int, $query: String) {
|
||||
search(first: $first, after: $after, query: $query) {
|
||||
... on SearchSuccess {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
title
|
||||
slug
|
||||
url
|
||||
pageType
|
||||
contentReader
|
||||
createdAt
|
||||
isArchived
|
||||
readingProgressPercent
|
||||
readingProgressAnchorIndex
|
||||
author
|
||||
image
|
||||
description
|
||||
publishedAt
|
||||
ownedByViewer
|
||||
originalArticleUrl
|
||||
uploadFileId
|
||||
labels {
|
||||
id
|
||||
name
|
||||
color
|
||||
}
|
||||
pageId
|
||||
shortId
|
||||
quote
|
||||
annotation
|
||||
state
|
||||
siteName
|
||||
subscription
|
||||
readAt
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
... on SearchError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import androidx.core.view.ViewCompat
|
|||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.ui.home.HomeViewModel
|
||||
import app.omnivore.omnivore.ui.root.RootView
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
|
|
@ -18,11 +19,12 @@ class MainActivity : ComponentActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val viewModel: LoginViewModel by viewModels()
|
||||
val loginViewModel: LoginViewModel by viewModels()
|
||||
val homeViewModel: HomeViewModel by viewModels()
|
||||
|
||||
setContent {
|
||||
OmnivoreTheme {
|
||||
RootView(viewModel = viewModel)
|
||||
RootView(loginViewModel, homeViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package app.omnivore.omnivore
|
||||
|
||||
sealed class Routes(val route: String) {
|
||||
object Root : Routes("Root")
|
||||
object Home : Routes("Home")
|
||||
object Welcome : Routes("Welcome")
|
||||
object WebReader : Routes("WebReader")
|
||||
object Settings: Routes("Settings")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import java.util.*
|
|||
|
||||
@Composable
|
||||
fun AppleAuthButton(viewModel: LoginViewModel) {
|
||||
val showDialog = remember { mutableStateOf(false) }
|
||||
val showDialog = remember { mutableStateOf(false) }
|
||||
|
||||
LoadingButtonWithIcon(
|
||||
text = "Continue with Apple",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.google.android.gms.tasks.Task
|
|||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
|
|
@ -36,6 +37,10 @@ class LoginViewModel @Inject constructor(
|
|||
.distinctUntilChanged()
|
||||
.asLiveData()
|
||||
|
||||
fun getAuthCookieString(): String? = runBlocking {
|
||||
datastoreRepo.getString(DatastoreKeys.omnivoreAuthCookieString)
|
||||
}
|
||||
|
||||
fun login(email: String, password: String) {
|
||||
val emailLogin = RetrofitHelper.getInstance().create(EmailLoginSubmit::class.java)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
|
|
@ -19,7 +18,6 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
|
|||
|
|
@ -1,48 +1,114 @@
|
|||
package app.omnivore.omnivore.ui.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import app.omnivore.omnivore.Routes
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeView(
|
||||
homeViewModel: HomeViewModel,
|
||||
navController: NavHostController
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Home") },
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
actions = {
|
||||
IconButton(onClick = { navController.navigate(Routes.Settings.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
HomeViewContent(
|
||||
homeViewModel,
|
||||
navController,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeView(viewModel: LoginViewModel) {
|
||||
val context = LocalContext.current
|
||||
fun HomeViewContent(
|
||||
homeViewModel: HomeViewModel,
|
||||
navController: NavHostController,
|
||||
modifier: Modifier
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
val linkedItems: List<LinkedItem> by homeViewModel.itemsLiveData.observeAsState(listOf())
|
||||
|
||||
Column(
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 6.dp)
|
||||
) {
|
||||
Text("You have a valid auth token. Nice. Go save something in Chrome!")
|
||||
|
||||
Button(onClick = {
|
||||
// Sign out google users
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.build()
|
||||
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
googleSignIn.signOut()
|
||||
|
||||
viewModel.logout()
|
||||
}) {
|
||||
Text(text = "Logout")
|
||||
items(linkedItems) { item ->
|
||||
LinkedItemCard(
|
||||
item = item,
|
||||
onClickHandler = {
|
||||
navController.navigate("WebReader/${item.slug}")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InfiniteListHandler(listState = listState) {
|
||||
homeViewModel.load()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfiniteListHandler(
|
||||
listState: LazyListState,
|
||||
buffer: Int = 2,
|
||||
onLoadMore: () -> Unit
|
||||
) {
|
||||
val loadMore = remember {
|
||||
derivedStateOf {
|
||||
val layoutInfo = listState.layoutInfo
|
||||
val totalItemsNumber = layoutInfo.totalItemsCount
|
||||
val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
|
||||
|
||||
lastVisibleItemIndex > (totalItemsNumber - buffer)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(loadMore) {
|
||||
snapshotFlow { loadMore.value }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
onLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package app.omnivore.omnivore.ui.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.Uri
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.lifecycle.*
|
||||
import app.omnivore.omnivore.AppleConstants
|
||||
import app.omnivore.omnivore.Constants
|
||||
import app.omnivore.omnivore.DatastoreKeys
|
||||
import app.omnivore.omnivore.DatastoreRepository
|
||||
import app.omnivore.omnivore.graphql.generated.SearchQuery
|
||||
import app.omnivore.omnivore.ui.auth.AppleAuthDialog
|
||||
import com.apollographql.apollo3.ApolloClient
|
||||
import com.apollographql.apollo3.api.Optional
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class HomeViewModel @Inject constructor(
|
||||
private val datastoreRepo: DatastoreRepository
|
||||
): ViewModel() {
|
||||
var cursor: String? = null
|
||||
val itemsLiveData = MutableLiveData<List<LinkedItem>>(listOf())
|
||||
|
||||
private fun getAuthToken(): String? = runBlocking {
|
||||
datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken)
|
||||
}
|
||||
|
||||
fun load() {
|
||||
viewModelScope.launch {
|
||||
val authToken = getAuthToken()
|
||||
|
||||
val apolloClient = ApolloClient.Builder()
|
||||
.serverUrl("${Constants.apiURL}/api/graphql")
|
||||
.addHttpHeader("Authorization", value = authToken ?: "")
|
||||
.build()
|
||||
|
||||
val response = apolloClient.query(
|
||||
SearchQuery(
|
||||
after = Optional.presentIfNotNull(cursor),
|
||||
first = Optional.presentIfNotNull(15),
|
||||
)
|
||||
).execute()
|
||||
|
||||
cursor = response.data?.search?.onSearchSuccess?.pageInfo?.endCursor
|
||||
val itemList = response.data?.search?.onSearchSuccess?.edges ?: listOf()
|
||||
|
||||
val newItems = itemList.map {
|
||||
LinkedItem(
|
||||
id = it.node.id,
|
||||
title = it.node.title,
|
||||
createdAt = it.node.createdAt,
|
||||
readAt = it.node.readAt,
|
||||
readingProgress = it.node.readingProgressPercent,
|
||||
readingProgressAnchor = it.node.readingProgressAnchorIndex,
|
||||
imageURLString = it.node.image,
|
||||
descriptionText = it.node.description,
|
||||
slug = it.node.slug
|
||||
)
|
||||
}
|
||||
|
||||
itemsLiveData.value = (itemsLiveData.value ?: listOf()).plus(newItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public data class LinkedItem(
|
||||
public val id: String,
|
||||
public val title: String,
|
||||
public val createdAt: Any,
|
||||
// public val savedAt: Any,
|
||||
public val readAt: Any?,
|
||||
// public val updatedAt: Any,
|
||||
public val readingProgress: Double,
|
||||
public val readingProgressAnchor: Int,
|
||||
public val imageURLString: String?,
|
||||
// public val onDeviceImageURLString: String?,
|
||||
// public val documentDirectoryPath: String?,
|
||||
// public val pageURLString: String,
|
||||
public val descriptionText: String?,
|
||||
// public val publisherURLString: String?,
|
||||
// public val siteName: String?,
|
||||
// public val author: String?,
|
||||
// public val publishDate: Any?,
|
||||
public val slug: String,
|
||||
// public val isArchived: Boolean,
|
||||
// public val contentReader: String?,
|
||||
// public val originalHtml: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package app.omnivore.omnivore.ui.home
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LinkedItemCard(
|
||||
item: LinkedItem,
|
||||
onClickHandler: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Card(
|
||||
onClick = { onClickHandler() },
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(15.dp)
|
||||
) {
|
||||
Row {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(item.imageURLString),
|
||||
contentDescription = "Image associated with linked item",
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
Text(
|
||||
text = item.descriptionText ?: "",
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package app.omnivore.omnivore.ui.reader
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Composable
|
||||
fun ArticleWebView(slug: String, authCookieString: String) {
|
||||
WebView.setWebContentsDebuggingEnabled(true)
|
||||
|
||||
val url = "https://demo.omnivore.app/app/me/$slug"
|
||||
|
||||
AndroidView(factory = {
|
||||
WebView(it).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
|
||||
settings.javaScriptEnabled = true
|
||||
settings.allowContentAccess = true
|
||||
settings.allowFileAccess = true
|
||||
settings.domStorageEnabled = true
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
}
|
||||
|
||||
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
|
||||
CookieManager.getInstance().setAcceptCookie(true)
|
||||
CookieManager.getInstance().setCookie("https://api-demo.omnivore.app", authCookieString) {
|
||||
loadUrl(url)
|
||||
}
|
||||
}
|
||||
}, update = {
|
||||
it.loadUrl(url)
|
||||
})
|
||||
}
|
||||
|
|
@ -1,19 +1,51 @@
|
|||
package app.omnivore.omnivore.ui.root
|
||||
|
||||
import SettingsView
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import app.omnivore.omnivore.Routes
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.ui.auth.WelcomeScreen
|
||||
import app.omnivore.omnivore.ui.home.HomeView
|
||||
import app.omnivore.omnivore.ui.home.HomeViewModel
|
||||
import app.omnivore.omnivore.ui.reader.ArticleWebView
|
||||
|
||||
@Composable
|
||||
fun RootView(viewModel: LoginViewModel) {
|
||||
val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false)
|
||||
fun RootView(loginViewModel: LoginViewModel, homeViewModel: HomeViewModel) {
|
||||
val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false)
|
||||
|
||||
if (hasAuthToken) {
|
||||
HomeView(viewModel = viewModel)
|
||||
} else {
|
||||
WelcomeScreen(viewModel = viewModel)
|
||||
}
|
||||
if (hasAuthToken) {
|
||||
PrimaryNavigator(loginViewModel = loginViewModel, homeViewModel = homeViewModel)
|
||||
} else {
|
||||
WelcomeScreen(viewModel = loginViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PrimaryNavigator(loginViewModel: LoginViewModel, homeViewModel: HomeViewModel){
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController = navController, startDestination = Routes.Home.route) {
|
||||
composable(Routes.Home.route) {
|
||||
HomeView(
|
||||
homeViewModel = homeViewModel,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
|
||||
composable("WebReader/{slug}") {
|
||||
ArticleWebView(
|
||||
it.arguments?.getString("slug") ?: "",
|
||||
authCookieString = loginViewModel.getAuthCookieString() ?: ""
|
||||
)
|
||||
}
|
||||
|
||||
composable(Routes.Settings.route) {
|
||||
SettingsView(loginViewModel = loginViewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,6 @@ class SaveViewModel @Inject constructor(
|
|||
|
||||
val authToken = getAuthToken()
|
||||
|
||||
Log.d(ContentValues.TAG, "AuthToken: $authToken")
|
||||
|
||||
if (authToken == null) {
|
||||
message = "You are not logged in. Please login before saving."
|
||||
isLoading = false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.Routes
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.ui.home.HomeViewContent
|
||||
import app.omnivore.omnivore.ui.home.HomeViewModel
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@Composable
|
||||
fun SettingsView(
|
||||
loginViewModel: LoginViewModel,
|
||||
navController: NavHostController
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Settings") },
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
actions = {
|
||||
IconButton(onClick = { navController.navigate(Routes.Home.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Home,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
LogoutButton { loginViewModel.logout() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LogoutButton(actionHandler: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Button(onClick = {
|
||||
// Sign out google users
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.build()
|
||||
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
googleSignIn.signOut()
|
||||
|
||||
actionHandler()
|
||||
}) {
|
||||
Text(text = "Logout")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue