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:
parent
d99185ae4c
commit
521fc349db
@ -20,11 +20,16 @@ import android.app.Activity
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.common.flogger.FluentLogger
|
import com.google.common.flogger.FluentLogger
|
||||||
|
import com.pitchedapps.frost.hilt.FrostComponents
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class FrostApp : Application() {
|
class FrostApp : Application() {
|
||||||
|
|
||||||
|
@Inject lateinit var componentsProvider: Provider<FrostComponents>
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
@ -48,7 +53,7 @@ class FrostApp : Application() {
|
|||||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
logger.atFine().log("Activity %s created", activity.localClassName)
|
logger.atFine().log("Activity %s created", activity.localClassName)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
@ -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() }
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,8 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import com.pitchedapps.frost.FrostApp
|
||||||
|
import com.pitchedapps.frost.hilt.FrostComponents
|
||||||
|
|
||||||
inline fun <reified T : Activity> Context.launchActivity(
|
inline fun <reified T : Activity> Context.launchActivity(
|
||||||
clearStack: Boolean = false,
|
clearStack: Boolean = false,
|
||||||
@ -38,3 +40,6 @@ inline fun <reified T : Activity> Context.launchActivity(
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Context.components: FrostComponents
|
||||||
|
get() = (applicationContext as FrostApp).componentsProvider.get()
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
package com.pitchedapps.frost.hilt
|
package com.pitchedapps.frost.hilt
|
||||||
|
|
||||||
import com.pitchedapps.frost.components.Core
|
import com.pitchedapps.frost.components.Core
|
||||||
|
import com.pitchedapps.frost.components.FrostDataStore
|
||||||
import com.pitchedapps.frost.components.UseCases
|
import com.pitchedapps.frost.components.UseCases
|
||||||
|
import com.pitchedapps.frost.extension.ExtensionModelConverter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -29,4 +31,11 @@ import javax.inject.Singleton
|
|||||||
* but with hilt
|
* but with hilt
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@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,
|
||||||
|
)
|
||||||
|
@ -24,20 +24,19 @@ import androidx.compose.material.Icon
|
|||||||
import androidx.compose.material.Tab
|
import androidx.compose.material.Tab
|
||||||
import androidx.compose.material.TabRow
|
import androidx.compose.material.TabRow
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
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.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.helper.Target
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.concept.engine.Engine
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(modifier: Modifier = Modifier) {
|
fun MainScreen(modifier: Modifier = Modifier) {
|
||||||
@ -47,60 +46,58 @@ fun MainScreen(modifier: Modifier = Modifier) {
|
|||||||
|
|
||||||
if (contextId.isEmpty()) return // Not ready
|
if (contextId.isEmpty()) return // Not ready
|
||||||
|
|
||||||
DisposableEffect(vm.store) {
|
val tabs by vm.tabsFlow.collectAsState(initial = emptyList())
|
||||||
val feature =
|
|
||||||
FrostCoreExtension(
|
|
||||||
runtime = vm.engine,
|
|
||||||
store = vm.store,
|
|
||||||
converter = vm.extensionModelConverter,
|
|
||||||
)
|
|
||||||
|
|
||||||
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(
|
MainContainer(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
engine = vm.engine,
|
|
||||||
store = vm.store,
|
|
||||||
contextId = contextId,
|
contextId = contextId,
|
||||||
useCases = vm.useCases,
|
|
||||||
tabIndex = vm.tabIndex,
|
tabIndex = vm.tabIndex,
|
||||||
tabs = vm.tabs,
|
tabs = tabs,
|
||||||
onTabSelect = { selectedIndex: Int ->
|
onTabSelect = onTabSelect,
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainContainer(
|
private fun MainContainer(
|
||||||
engine: Engine,
|
|
||||||
store: BrowserStore,
|
|
||||||
contextId: String,
|
contextId: String,
|
||||||
useCases: UseCases,
|
|
||||||
tabIndex: Int,
|
tabIndex: Int,
|
||||||
tabs: List<MainTabItem>,
|
tabs: List<MainTabItem>,
|
||||||
onTabSelect: (Int) -> Unit,
|
onTabSelect: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val components = LocalContext.current.components
|
||||||
|
|
||||||
LaunchedEffect(contextId) {
|
LaunchedEffect(contextId) {
|
||||||
useCases.homeTabs.createHomeTabs(contextId, tabIndex, tabs.map { it.url })
|
components.useCases.homeTabs.createHomeTabs(contextId, tabIndex, tabs.map { it.url })
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
MainTabRow(selectedIndex = tabIndex, items = tabs, onTabSelect = onTabSelect)
|
MainTabRow(selectedIndex = tabIndex, items = tabs, onTabSelect = onTabSelect)
|
||||||
// For tab switching, must use SelectedTab
|
// For tab switching, must use SelectedTab
|
||||||
// https://github.com/mozilla-mobile/android-components/issues/12798
|
// 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,21 +20,14 @@ import android.content.Context
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.datastore.core.DataStore
|
|
||||||
import androidx.lifecycle.ViewModel
|
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.facebook.FbItem
|
||||||
import com.pitchedapps.frost.hilt.FrostComponents
|
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.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import mozilla.components.concept.engine.Engine
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MainScreenViewModel
|
class MainScreenViewModel
|
||||||
@ -42,20 +35,17 @@ class MainScreenViewModel
|
|||||||
internal constructor(
|
internal constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
val components: FrostComponents,
|
val components: FrostComponents,
|
||||||
val engine: Engine,
|
|
||||||
val store: BrowserStore,
|
|
||||||
val useCases: UseCases,
|
|
||||||
val extensionModelConverter: ExtensionModelConverter,
|
|
||||||
accountDataStore: DataStore<Account>,
|
|
||||||
appearanceDataStore: DataStore<Appearance>,
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val tabsFlow: Flow<List<MainTabItem>> =
|
val tabsFlow: Flow<List<MainTabItem>> =
|
||||||
appearanceDataStore.data.map { appearance ->
|
components.dataStore.appearance.data
|
||||||
appearance.mainTabsList.mapNotNull { FbItem.fromKey(it)?.tab(context) }
|
.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)
|
var tabIndex: Int by mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user