1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 20:12:39 +01:00

Add component accessor through context

This commit is contained in:
Allan Wang 2023-06-18 13:16:40 -07:00
parent d99185ae4c
commit 521fc349db
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
7 changed files with 138 additions and 53 deletions

View File

@ -20,11 +20,16 @@ import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.google.common.flogger.FluentLogger
import com.pitchedapps.frost.hilt.FrostComponents
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import javax.inject.Provider
@HiltAndroidApp
class FrostApp : Application() {
@Inject lateinit var componentsProvider: Provider<FrostComponents>
override fun onCreate() {
super.onCreate()
@ -48,7 +53,7 @@ class FrostApp : Application() {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
logger.atFine().log("Activity %s created", activity.localClassName)
}
}
},
)
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2023 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.components
import androidx.datastore.core.DataStore
import com.pitchedapps.frost.proto.Account
import com.pitchedapps.frost.proto.settings.Appearance
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
@Singleton
class FrostDataStore
@Inject
internal constructor(
private val accountProvider: Provider<DataStore<Account>>,
private val appearanceProvider: Provider<DataStore<Appearance>>,
) {
val account: DataStore<Account>
get() = accountProvider.get()
val appearance: DataStore<Appearance>
get() = appearanceProvider.get()
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2023 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext
import com.pitchedapps.frost.ext.components
import com.pitchedapps.frost.extension.FrostCoreExtension
@Composable
fun FrostCoreExtensionEffect() {
val components = LocalContext.current.components
DisposableEffect(components.core.store) {
val feature =
FrostCoreExtension(
runtime = components.core.engine,
store = components.core.store,
converter = components.extensionModelConverter,
)
feature.start()
onDispose { feature.stop() }
}
}

View File

@ -20,6 +20,8 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.pitchedapps.frost.FrostApp
import com.pitchedapps.frost.hilt.FrostComponents
inline fun <reified T : Activity> Context.launchActivity(
clearStack: Boolean = false,
@ -38,3 +40,6 @@ inline fun <reified T : Activity> Context.launchActivity(
finish()
}
}
val Context.components: FrostComponents
get() = (applicationContext as FrostApp).componentsProvider.get()

View File

@ -17,7 +17,9 @@
package com.pitchedapps.frost.hilt
import com.pitchedapps.frost.components.Core
import com.pitchedapps.frost.components.FrostDataStore
import com.pitchedapps.frost.components.UseCases
import com.pitchedapps.frost.extension.ExtensionModelConverter
import javax.inject.Inject
import javax.inject.Singleton
@ -29,4 +31,11 @@ import javax.inject.Singleton
* but with hilt
*/
@Singleton
class FrostComponents @Inject internal constructor(val core: Core, val useCases: UseCases)
class FrostComponents
@Inject
internal constructor(
val core: Core,
val useCases: UseCases,
val extensionModelConverter: ExtensionModelConverter,
val dataStore: FrostDataStore,
)

View File

@ -24,20 +24,19 @@ import androidx.compose.material.Icon
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pitchedapps.frost.components.UseCases
import com.pitchedapps.frost.compose.FrostCoreExtensionEffect
import com.pitchedapps.frost.compose.FrostWeb
import com.pitchedapps.frost.extension.FrostCoreExtension
import com.pitchedapps.frost.ext.components
import mozilla.components.browser.state.helper.Target
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine
@Composable
fun MainScreen(modifier: Modifier = Modifier) {
@ -47,60 +46,58 @@ fun MainScreen(modifier: Modifier = Modifier) {
if (contextId.isEmpty()) return // Not ready
DisposableEffect(vm.store) {
val feature =
FrostCoreExtension(
runtime = vm.engine,
store = vm.store,
converter = vm.extensionModelConverter,
)
val tabs by vm.tabsFlow.collectAsState(initial = emptyList())
feature.start()
if (tabs.isEmpty()) return // Not ready
onDispose { feature.stop() }
FrostCoreExtensionEffect()
val onTabSelect =
remember(vm) {
{ selectedIndex: Int ->
if (selectedIndex == vm.tabIndex) {
vm.components.useCases.homeTabs.reloadTab(selectedIndex)
// context.launchFloatingUrl(FACEBOOK_M_URL)
} else {
// Change? What if previous selected tab is not home tab
vm.components.useCases.homeTabs.selectHomeTab(selectedIndex)
vm.tabIndex = selectedIndex
}
}
}
MainContainer(
modifier = modifier,
engine = vm.engine,
store = vm.store,
contextId = contextId,
useCases = vm.useCases,
tabIndex = vm.tabIndex,
tabs = vm.tabs,
onTabSelect = { selectedIndex: Int ->
if (selectedIndex == vm.tabIndex) {
vm.useCases.homeTabs.reloadTab(selectedIndex)
// context.launchFloatingUrl(FACEBOOK_M_URL)
} else {
// Change? What if previous selected tab is not home tab
vm.useCases.homeTabs.selectHomeTab(selectedIndex)
vm.tabIndex = selectedIndex
}
},
tabs = tabs,
onTabSelect = onTabSelect,
)
}
@Composable
private fun MainContainer(
engine: Engine,
store: BrowserStore,
contextId: String,
useCases: UseCases,
tabIndex: Int,
tabs: List<MainTabItem>,
onTabSelect: (Int) -> Unit,
modifier: Modifier = Modifier
) {
val components = LocalContext.current.components
LaunchedEffect(contextId) {
useCases.homeTabs.createHomeTabs(contextId, tabIndex, tabs.map { it.url })
components.useCases.homeTabs.createHomeTabs(contextId, tabIndex, tabs.map { it.url })
}
Column(modifier = modifier) {
MainTabRow(selectedIndex = tabIndex, items = tabs, onTabSelect = onTabSelect)
// For tab switching, must use SelectedTab
// https://github.com/mozilla-mobile/android-components/issues/12798
FrostWeb(engine = engine, store = store, target = Target.SelectedTab)
FrostWeb(
engine = components.core.engine,
store = components.core.store,
target = Target.SelectedTab
)
}
}

View File

@ -20,21 +20,14 @@ import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.datastore.core.DataStore
import androidx.lifecycle.ViewModel
import com.pitchedapps.frost.components.UseCases
import com.pitchedapps.frost.extension.ExtensionModelConverter
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.hilt.FrostComponents
import com.pitchedapps.frost.proto.Account
import com.pitchedapps.frost.proto.settings.Appearance
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine
@HiltViewModel
class MainScreenViewModel
@ -42,20 +35,17 @@ class MainScreenViewModel
internal constructor(
@ApplicationContext context: Context,
val components: FrostComponents,
val engine: Engine,
val store: BrowserStore,
val useCases: UseCases,
val extensionModelConverter: ExtensionModelConverter,
accountDataStore: DataStore<Account>,
appearanceDataStore: DataStore<Appearance>,
) : ViewModel() {
val tabsFlow: Flow<List<MainTabItem>> =
appearanceDataStore.data.map { appearance ->
appearance.mainTabsList.mapNotNull { FbItem.fromKey(it)?.tab(context) }
components.dataStore.appearance.data
.map { appearance ->
appearance.mainTabsList.mapNotNull { FbItem.fromKey(it) }.takeIf { it.isNotEmpty() }
?: FbItem.defaults()
}
.map { items -> items.map { it.tab(context) } }
val contextIdFlow: Flow<String> = accountDataStore.data.map { it.accountId }
val contextIdFlow: Flow<String> = components.dataStore.account.data.map { it.accountId }
var tabIndex: Int by mutableStateOf(0)
}