fix: deep link (and hide back button if not possible)

This commit is contained in:
Diego Beraldin 2023-10-21 20:04:38 +02:00
parent e168ab2396
commit ba7c197957
20 changed files with 224 additions and 190 deletions

View File

@ -6,7 +6,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
val coreApiModule = module { val coreApiModule = module {
single<ServiceProvider> { single<ServiceProvider>(named("default")) {
DefaultServiceProvider() DefaultServiceProvider()
} }
single<ServiceProvider>(named("custom")) { single<ServiceProvider>(named("custom")) {

View File

@ -57,7 +57,17 @@ internal class DefaultServiceProvider : ServiceProvider {
private val factory = provideHttpClientEngineFactory() private val factory = provideHttpClientEngineFactory()
private val client = HttpClient(factory) { init {
reinitialize()
}
override fun changeInstance(value: String) {
currentInstance = value
reinitialize()
}
private fun reinitialize() {
val client = HttpClient(factory) {
defaultRequest { defaultRequest {
url { url {
host = currentInstance host = currentInstance
@ -76,17 +86,6 @@ internal class DefaultServiceProvider : ServiceProvider {
json(Json { isLenient = true; ignoreUnknownKeys = true }) json(Json { isLenient = true; ignoreUnknownKeys = true })
} }
} }
init {
reinitialize()
}
override fun changeInstance(value: String) {
currentInstance = value
reinitialize()
}
private fun reinitialize() {
val ktorfit = Ktorfit.Builder() val ktorfit = Ktorfit.Builder()
.baseUrl(baseUrl) .baseUrl(baseUrl)
.httpClient(client) .httpClient(client)

View File

@ -213,14 +213,16 @@ class CommunityDetailScreen(
) )
}, },
navigationIcon = { navigationIcon = {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick { modifier = Modifier.onClick {
navigator?.pop() navigator.pop()
}, },
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
) )
}
}, },
) )
}, floatingActionButton = { }, floatingActionButton = {

View File

@ -9,7 +9,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
fun getCommmunityFromUrl(url: String): CommunityModel? { fun getCommunityFromUrl(url: String?): CommunityModel? {
val (normalizedUrl, instance) = normalizeUrl(url) val (normalizedUrl, instance) = normalizeUrl(url)
val res = extractCommunity(normalizedUrl) val res = extractCommunity(normalizedUrl)
return if (res != null && res.host.isEmpty()) { return if (res != null && res.host.isEmpty()) {
@ -19,7 +19,7 @@ fun getCommmunityFromUrl(url: String): CommunityModel? {
} }
} }
fun getUserFromUrl(url: String): UserModel? { fun getUserFromUrl(url: String?): UserModel? {
val (normalizedUrl, instance) = normalizeUrl(url) val (normalizedUrl, instance) = normalizeUrl(url)
val res = extractUser(normalizedUrl) val res = extractUser(normalizedUrl)
return if (res != null && res.host.isEmpty()) { return if (res != null && res.host.isEmpty()) {
@ -29,10 +29,10 @@ fun getUserFromUrl(url: String): UserModel? {
} }
} }
fun getPostFromUrl(url: String): Pair<PostModel, String>? { fun getPostFromUrl(url: String?): Pair<PostModel, String>? {
val (normalizedUrl, instance) = normalizeUrl(url) val (normalizedUrl, instance) = normalizeUrl(url)
val post = extractPost(normalizedUrl) val post = extractPost(normalizedUrl)
return if (post != null && instance != null) { return if (post != null && instance.isNotEmpty()) {
post to instance post to instance
} else { } else {
null null
@ -45,7 +45,7 @@ fun handleUrl(
uriHandler: UriHandler, uriHandler: UriHandler,
navigator: Navigator? = null, navigator: Navigator? = null,
) { ) {
val community = getCommmunityFromUrl(url) val community = getCommunityFromUrl(url)
val user = getUserFromUrl(url) val user = getUserFromUrl(url)
when { when {
@ -79,8 +79,8 @@ fun handleUrl(
} }
} }
private fun normalizeUrl(url: String): Pair<String, String> { private fun normalizeUrl(url: String?): Pair<String, String> {
val matches = Regex("https?://(?<instance>.*?)(?<pathAndQuery>/.*)").findAll(url) val matches = Regex("https?://(?<instance>.*?)(?<pathAndQuery>/.*)").findAll(url.orEmpty())
var instance = "" var instance = ""
val res = buildString { val res = buildString {
if (matches.count() > 0) { if (matches.count() > 0) {

View File

@ -7,12 +7,13 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
internal class DefaultNavigationCoordinator : NavigationCoordinator { internal class DefaultNavigationCoordinator : NavigationCoordinator {
override val onDoubleTabSelection = MutableSharedFlow<Tab>() override val onDoubleTabSelection = MutableSharedFlow<Tab>()
override val deeplinkUrl = MutableStateFlow<String?>(null) override val deepLinkUrl = MutableStateFlow<String?>(null)
private var connection: NestedScrollConnection? = null private var connection: NestedScrollConnection? = null
private var navigator: Navigator? = null private var navigator: Navigator? = null
@ -43,8 +44,10 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
} }
} }
override fun submitDeeplink(url: String?) { override fun consumeDeeplink(): String? = deepLinkUrl.getAndUpdate { null }
deeplinkUrl.value = url
override fun submitDeeplink(url: String) {
deepLinkUrl.value = url
} }
override fun setCanGoBackCallback(value: (() -> Boolean)?) { override fun setCanGoBackCallback(value: (() -> Boolean)?) {

View File

@ -3,18 +3,20 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.Tab
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
interface NavigationCoordinator { interface NavigationCoordinator {
val deeplinkUrl: StateFlow<String?> val onDoubleTabSelection: Flow<Tab>
val onDoubleTabSelection: SharedFlow<Tab>
val inboxUnread: StateFlow<Int> val inboxUnread: StateFlow<Int>
val deepLinkUrl: StateFlow<String?>
fun setCurrentSection(tab: Tab) fun setCurrentSection(tab: Tab)
fun submitDeeplink(url: String?) fun consumeDeeplink(): String?
fun submitDeeplink(url: String)
fun setRootNavigator(value: Navigator?) fun setRootNavigator(value: Navigator?)

View File

@ -127,7 +127,8 @@ class PostDetailScreen(
model.bindToLifecycle(key) model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState() val uiState by model.uiState.collectAsState()
val isOnOtherInstance = otherInstance.isNotEmpty() val isOnOtherInstance = otherInstance.isNotEmpty()
val navigator = remember { getNavigationCoordinator().getRootNavigator() } val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState() val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState) val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
@ -207,14 +208,16 @@ class PostDetailScreen(
) )
}, },
navigationIcon = { navigationIcon = {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick { modifier = Modifier.onClick {
navigator?.pop() navigator.pop()
}, },
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
) )
}
}, },
) )
}, },

View File

@ -107,7 +107,6 @@ class PostDetailViewModel(
} }
if (highlightCommentId != null) { if (highlightCommentId != null) {
val auth = identityRepository.authToken.value
val comment = commentRepository.getBy( val comment = commentRepository.getBy(
id = highlightCommentId, id = highlightCommentId,
auth = auth, auth = auth,

View File

@ -192,14 +192,16 @@ class UserDetailScreen(
) )
}, },
navigationIcon = { navigationIcon = {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick { modifier = Modifier.onClick {
navigator?.pop() navigator.pop()
}, },
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
) )
}
}, },
) )
}, },

View File

@ -12,12 +12,13 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.DefaultS
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.LoginUseCase import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.LoginUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.LogoutUseCase import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.LogoutUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.SwitchAccountUseCase import com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase.SwitchAccountUseCase
import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
val coreIdentityModule = module { val coreIdentityModule = module {
single<ApiConfigurationRepository> { single<ApiConfigurationRepository> {
DefaultApiConfigurationRepository( DefaultApiConfigurationRepository(
serviceProvider = get(), serviceProvider = get(named("default")),
) )
} }
single<IdentityRepository> { single<IdentityRepository> {
@ -27,7 +28,7 @@ val coreIdentityModule = module {
} }
single<AuthRepository> { single<AuthRepository> {
DefaultAuthRepository( DefaultAuthRepository(
services = get(), services = get(named("default")),
) )
} }
single<LoginUseCase> { single<LoginUseCase> {
@ -52,7 +53,7 @@ val coreIdentityModule = module {
identityRepository = get(), identityRepository = get(),
accountRepository = get(), accountRepository = get(),
settingsRepository = get(), settingsRepository = get(),
serviceProvider = get(), serviceProvider = get(named("default")),
notificationCenter = get(), notificationCenter = get(),
) )
} }

View File

@ -1,12 +1,10 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository package com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow
interface ApiConfigurationRepository { interface ApiConfigurationRepository {
val instance: Flow<String> val instance: StateFlow<String>
fun getInstance(): String
fun changeInstance(value: String) fun changeInstance(value: String)
} }

View File

@ -1,25 +1,32 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository package com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
internal class DefaultApiConfigurationRepository( internal class DefaultApiConfigurationRepository(
private val serviceProvider: ServiceProvider, private val serviceProvider: ServiceProvider,
) : ApiConfigurationRepository { ) : ApiConfigurationRepository {
override val instance: Flow<String> = channelFlow { private val scope = CoroutineScope(SupervisorJob())
override val instance = channelFlow {
while (isActive) { while (isActive) {
val value = getInstance() val value = serviceProvider.currentInstance
trySend(value) trySend(value)
delay(1000) delay(1000)
} }
}.distinctUntilChanged() }.distinctUntilChanged().stateIn(
scope = scope,
override fun getInstance() = serviceProvider.currentInstance started = SharingStarted.Lazily,
initialValue = "",
)
override fun changeInstance(value: String) { override fun changeInstance(value: String) {
serviceProvider.changeInstance(value) serviceProvider.changeInstance(value)

View File

@ -23,7 +23,7 @@ internal class DefaultLoginUseCase(
password: String, password: String,
totp2faToken: String?, totp2faToken: String?,
): Result<Unit> { ): Result<Unit> {
val oldInstance = apiConfigurationRepository.getInstance() val oldInstance = apiConfigurationRepository.instance.value
apiConfigurationRepository.changeInstance(instance) apiConfigurationRepository.changeInstance(instance)
val response = authRepository.login( val response = authRepository.login(

View File

@ -8,7 +8,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.to
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel
class PrivateMessageRepository( class PrivateMessageRepository(
private val serviceProvider: ServiceProvider, private val services: ServiceProvider,
) { ) {
suspend fun getAll( suspend fun getAll(
auth: String? = null, auth: String? = null,
@ -16,7 +16,7 @@ class PrivateMessageRepository(
limit: Int = PostRepository.DEFAULT_PAGE_SIZE, limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
unreadOnly: Boolean = true, unreadOnly: Boolean = true,
): List<PrivateMessageModel>? = runCatching { ): List<PrivateMessageModel>? = runCatching {
val response = serviceProvider.privateMessages.getPrivateMessages( val response = services.privateMessages.getPrivateMessages(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
limit = limit, limit = limit,
@ -37,7 +37,7 @@ class PrivateMessageRepository(
auth = auth.orEmpty(), auth = auth.orEmpty(),
recipientId = recipiendId, recipientId = recipiendId,
) )
serviceProvider.privateMessages.createPrivateMessage( services.privateMessages.createPrivateMessage(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )
@ -53,7 +53,7 @@ class PrivateMessageRepository(
auth = auth.orEmpty(), auth = auth.orEmpty(),
read = read, read = read,
) )
serviceProvider.privateMessages.markPrivateMessageAsRead( services.privateMessages.markPrivateMessageAsRead(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )

View File

@ -9,10 +9,10 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.to
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel
class SiteRepository( class SiteRepository(
private val serviceProvider: ServiceProvider, private val services: ServiceProvider,
) { ) {
suspend fun getCurrentUser(auth: String): UserModel? = runCatching { suspend fun getCurrentUser(auth: String): UserModel? = runCatching {
val response = serviceProvider.site.get( val response = services.site.get(
auth = auth, auth = auth,
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
) )
@ -28,14 +28,14 @@ class SiteRepository(
instanceId = id, instanceId = id,
block = blocked, block = blocked,
) )
serviceProvider.site.block( services.site.block(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )
} }
suspend fun getMetadata(url: String): MetadataModel? = runCatching { suspend fun getMetadata(url: String): MetadataModel? = runCatching {
val response = serviceProvider.site.getSiteMetadata( val response = services.site.getSiteMetadata(
url = url, url = url,
) )
response.body()?.metadata?.toModel() response.body()?.metadata?.toModel()

View File

@ -16,8 +16,8 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.to
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel
class UserRepository( class UserRepository(
private val serviceProvider: ServiceProvider, private val services: ServiceProvider,
private val customServiceProvider: ServiceProvider, private val customServices: ServiceProvider,
) { ) {
suspend fun get( suspend fun get(
@ -25,7 +25,7 @@ class UserRepository(
auth: String? = null, auth: String? = null,
username: String? = null, username: String? = null,
): UserModel? = runCatching { ): UserModel? = runCatching {
val response = serviceProvider.user.getDetails( val response = services.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
personId = id, personId = id,
@ -48,8 +48,8 @@ class UserRepository(
username: String? = null, username: String? = null,
auth: String? = null, auth: String? = null,
): UserModel? = runCatching { ): UserModel? = runCatching {
customServiceProvider.changeInstance(instance) customServices.changeInstance(instance)
val response = customServiceProvider.user.getDetails( val response = customServices.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
username = username, username = username,
@ -73,7 +73,7 @@ class UserRepository(
limit: Int = PostRepository.DEFAULT_PAGE_SIZE, limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
sort: SortType = SortType.Active, sort: SortType = SortType.Active,
): List<PostModel>? = runCatching { ): List<PostModel>? = runCatching {
val response = serviceProvider.user.getDetails( val response = services.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
personId = id, personId = id,
@ -92,7 +92,7 @@ class UserRepository(
limit: Int = PostRepository.DEFAULT_PAGE_SIZE, limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
sort: SortType = SortType.Active, sort: SortType = SortType.Active,
): List<PostModel>? = runCatching { ): List<PostModel>? = runCatching {
val response = serviceProvider.user.getDetails( val response = services.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
personId = id, personId = id,
@ -112,7 +112,7 @@ class UserRepository(
limit: Int = PostRepository.DEFAULT_PAGE_SIZE, limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
sort: SortType = SortType.Active, sort: SortType = SortType.Active,
): List<CommentModel>? = runCatching { ): List<CommentModel>? = runCatching {
val response = serviceProvider.user.getDetails( val response = services.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
personId = id, personId = id,
@ -131,7 +131,7 @@ class UserRepository(
limit: Int = PostRepository.DEFAULT_PAGE_SIZE, limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
sort: SortType = SortType.Active, sort: SortType = SortType.Active,
): List<CommentModel>? = runCatching { ): List<CommentModel>? = runCatching {
val response = serviceProvider.user.getDetails( val response = services.user.getDetails(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
personId = id, personId = id,
@ -151,7 +151,7 @@ class UserRepository(
sort: SortType = SortType.New, sort: SortType = SortType.New,
unreadOnly: Boolean = true, unreadOnly: Boolean = true,
): List<PersonMentionModel>? = runCatching { ): List<PersonMentionModel>? = runCatching {
val response = serviceProvider.user.getMentions( val response = services.user.getMentions(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
limit = limit, limit = limit,
@ -170,7 +170,7 @@ class UserRepository(
sort: SortType = SortType.New, sort: SortType = SortType.New,
unreadOnly: Boolean = true, unreadOnly: Boolean = true,
): List<PersonMentionModel>? = runCatching { ): List<PersonMentionModel>? = runCatching {
val response = serviceProvider.user.getReplies( val response = services.user.getReplies(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
auth = auth, auth = auth,
limit = limit, limit = limit,
@ -186,7 +186,7 @@ class UserRepository(
auth: String? = null, auth: String? = null,
) { ) {
val data = MarkAllAsReadForm(auth.orEmpty()) val data = MarkAllAsReadForm(auth.orEmpty())
serviceProvider.user.markAllAsRead( services.user.markAllAsRead(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )
@ -198,7 +198,7 @@ class UserRepository(
read = read, read = read,
auth = auth.orEmpty(), auth = auth.orEmpty(),
) )
serviceProvider.user.markPersonMentionAsRead( services.user.markPersonMentionAsRead(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )
@ -210,7 +210,7 @@ class UserRepository(
read = read, read = read,
auth = auth.orEmpty(), auth = auth.orEmpty(),
) )
serviceProvider.user.markCommentReplyAsRead( services.user.markCommentReplyAsRead(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )
@ -222,7 +222,7 @@ class UserRepository(
block = blocked, block = blocked,
auth = auth.orEmpty(), auth = auth.orEmpty(),
) )
serviceProvider.user.block( services.user.block(
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
form = data, form = data,
) )

View File

@ -6,25 +6,42 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepo
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PrivateMessageRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PrivateMessageRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository
import org.koin.core.module.dsl.singleOf
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
val repositoryModule = module { val repositoryModule = module {
single { single {
PostRepository( PostRepository(
services = get(), services = get(named("default")),
customServices = get(named("custom")), customServices = get(named("custom")),
) )
} }
single { single {
CommunityRepository( CommunityRepository(
services = get(), services = get(named("default")),
customServices = get(named("custom")), customServices = get(named("custom")),
) )
} }
singleOf(::UserRepository) single {
singleOf(::SiteRepository) UserRepository(
singleOf(::CommentRepository) services = get(named("default")),
singleOf(::PrivateMessageRepository) customServices = get(named("custom")),
)
}
single {
SiteRepository(
services = get(named("default")),
)
}
single {
CommentRepository(
services = get(named("default")),
customServices = get(named("custom")),
)
}
single {
PrivateMessageRepository(
services = get(named("default")),
)
}
} }

View File

@ -66,7 +66,7 @@ class ExploreViewModel(
mvi.onStarted() mvi.onStarted()
mvi.updateState { mvi.updateState {
it.copy( it.copy(
instance = apiConfigRepository.getInstance(), instance = apiConfigRepository.instance.value,
) )
} }
mvi.scope?.launch(Dispatchers.Main) { mvi.scope?.launch(Dispatchers.Main) {

View File

@ -31,9 +31,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.CurrentScreen import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.TabNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator
@ -47,7 +45,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.AppTheme
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getCommmunityFromUrl import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getCommunityFromUrl
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getPostFromUrl import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getPostFromUrl
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getUserFromUrl import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.getUserFromUrl
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator
@ -77,12 +75,20 @@ fun App() {
val settingsRepository = remember { getSettingsRepository() } val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState() val settings by settingsRepository.currentSettings.collectAsState()
var hasBeenInitialized by remember { mutableStateOf(false) } var hasBeenInitialized by remember { mutableStateOf(false) }
LaunchedEffect(settingsRepository) { val apiConfigurationRepository = remember { getApiConfigurationRepository() }
LaunchedEffect(accountRepository) {
val accountId = accountRepository.getActive()?.id val accountId = accountRepository.getActive()?.id
val currentSettings = settingsRepository.getSettings(accountId) val currentSettings = settingsRepository.getSettings(accountId)
settingsRepository.changeCurrentSettings(currentSettings) settingsRepository.changeCurrentSettings(currentSettings)
val lastActiveAccount = accountRepository.getActive()
val lastInstance = lastActiveAccount?.instance
if (lastInstance != null) {
apiConfigurationRepository.changeInstance(lastInstance)
}
hasBeenInitialized = true hasBeenInitialized = true
} }
val defaultTheme = if (isSystemInDarkTheme()) { val defaultTheme = if (isSystemInDarkTheme()) {
UiTheme.Dark.toInt() UiTheme.Dark.toInt()
} else { } else {
@ -100,15 +106,6 @@ fun App() {
StringDesc.localeType = StringDesc.LocaleType.Custom(lang) StringDesc.localeType = StringDesc.LocaleType.Custom(lang)
}.launchIn(scope) }.launchIn(scope)
val apiConfigurationRepository = remember { getApiConfigurationRepository() }
LaunchedEffect(Unit) {
val lastActiveAccount = accountRepository.getActive()
val lastInstance = lastActiveAccount?.instance
if (lastInstance != null) {
apiConfigurationRepository.changeInstance(lastInstance)
}
}
val themeRepository = remember { getThemeRepository() } val themeRepository = remember { getThemeRepository() }
LaunchedEffect(settings) { LaunchedEffect(settings) {
with(themeRepository) { with(themeRepository) {
@ -136,49 +133,41 @@ fun App() {
val lang by languageRepository.currentLanguage.collectAsState() val lang by languageRepository.currentLanguage.collectAsState()
LaunchedEffect(lang) {} LaunchedEffect(lang) {}
CompositionLocalProvider( val url = navigationCoordinator.consumeDeeplink()
LocalDensity provides Density( LaunchedEffect(navigationCoordinator, url) {
density = LocalDensity.current.density, val community = getCommunityFromUrl(url)
fontScale = uiFontScale,
),
) {
BottomSheetNavigator(
sheetShape = RoundedCornerShape(topStart = CornerSize.xl, topEnd = CornerSize.xl),
sheetBackgroundColor = MaterialTheme.colorScheme.background,
) {
val screens: List<Screen> = remember(navigationCoordinator.deeplinkUrl) {
val url = navigationCoordinator.deeplinkUrl.value.orEmpty()
val community = getCommmunityFromUrl(url)
val user = getUserFromUrl(url) val user = getUserFromUrl(url)
val postAndInstance = getPostFromUrl(url) val postAndInstance = getPostFromUrl(url)
buildList { val newScreen = when {
if (community != null) { community != null -> {
add(
CommunityDetailScreen( CommunityDetailScreen(
community = community, community = community,
otherInstance = community.host, otherInstance = community.host,
) )
) }
} else if (user != null) {
add( user != null -> {
UserDetailScreen( UserDetailScreen(
user = user, user = user,
otherInstance = user.host, otherInstance = user.host,
) )
) }
} else if (postAndInstance != null) {
postAndInstance != null -> {
val (post, otherInstance) = postAndInstance val (post, otherInstance) = postAndInstance
add(
PostDetailScreen( PostDetailScreen(
post = post, post = post,
otherInstance = otherInstance, otherInstance = otherInstance,
) )
) }
} else {
add(MainScreen()) else -> null
} }
if (newScreen != null) {
navigationCoordinator.getRootNavigator()?.push(newScreen)
} }
} }
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val drawerCoordinator = remember { getDrawerCoordinator() } val drawerCoordinator = remember { getDrawerCoordinator() }
LaunchedEffect(drawerCoordinator) { LaunchedEffect(drawerCoordinator) {
@ -210,6 +199,20 @@ fun App() {
}.launchIn(this) }.launchIn(this)
} }
val drawerGestureEnabled by drawerCoordinator.gesturesEnabled.collectAsState() val drawerGestureEnabled by drawerCoordinator.gesturesEnabled.collectAsState()
CompositionLocalProvider(
LocalDensity provides Density(
density = LocalDensity.current.density,
fontScale = uiFontScale,
),
) {
BottomSheetNavigator(
sheetShape = RoundedCornerShape(
topStart = CornerSize.xl,
topEnd = CornerSize.xl
),
sheetBackgroundColor = MaterialTheme.colorScheme.background,
) {
ModalNavigationDrawer( ModalNavigationDrawer(
drawerState = drawerState, drawerState = drawerState,
gesturesEnabled = drawerGestureEnabled, gesturesEnabled = drawerGestureEnabled,
@ -217,21 +220,19 @@ fun App() {
ModalDrawerSheet { ModalDrawerSheet {
TabNavigator(ModalDrawerContent) TabNavigator(ModalDrawerContent)
} }
} },
) { ) {
Navigator( Navigator(
screens = screens, screen = MainScreen,
onBackPressed = { onBackPressed = {
val callback = navigationCoordinator.getCanGoBackCallback() val callback = navigationCoordinator.getCanGoBackCallback()
callback?.let { it() } ?: true callback?.let { it() } ?: true
} }
) { ) { navigator ->
val navigator = LocalNavigator.current
navigationCoordinator.setRootNavigator(navigator) navigationCoordinator.setRootNavigator(navigator)
if (hasBeenInitialized) { if (hasBeenInitialized) {
CurrentScreen() CurrentScreen()
} else { } else {
// loading screen
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,

View File

@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlin.math.roundToInt import kotlin.math.roundToInt
internal class MainScreen : Screen { internal object MainScreen : Screen {
@Composable @Composable
override fun Content() { override fun Content() {