mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-08 15:28:42 +01:00
fix: pull to refresh indicator in filtered contents (#856)
This commit is contained in:
parent
f9ed0337fc
commit
8c167c8975
@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
var loadingFinished = false
|
||||
installSplashScreen().setKeepOnScreenCondition {
|
||||
@ -27,31 +26,33 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
// manage exit confirmation
|
||||
val navigationCoordinator = getNavigationCoordinator()
|
||||
val backPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
// if in home, ask for confirmation
|
||||
if (navigationCoordinator.currentSection.value == TabNavigationSection.Home) {
|
||||
// asks for confirmation
|
||||
if (!navigationCoordinator.exitMessageVisible.value) {
|
||||
navigationCoordinator.setExitMessageVisible(true)
|
||||
val backPressedCallback =
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
// if in home, ask for confirmation
|
||||
if (navigationCoordinator.currentSection.value == TabNavigationSection.Home) {
|
||||
// asks for confirmation
|
||||
if (!navigationCoordinator.exitMessageVisible.value) {
|
||||
navigationCoordinator.setExitMessageVisible(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// goes back to home
|
||||
with(navigationCoordinator) {
|
||||
changeTab(HomeTab)
|
||||
setCurrentSection(TabNavigationSection.Home)
|
||||
// goes back to home
|
||||
with(navigationCoordinator) {
|
||||
changeTab(HomeTab)
|
||||
setCurrentSection(TabNavigationSection.Home)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// when back is detected and the confirmation callback is not active, terminate the activity
|
||||
val finishBackPressedCallback = object : OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigationCoordinator.setExitMessageVisible(false)
|
||||
finish()
|
||||
val finishBackPressedCallback =
|
||||
object : OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigationCoordinator.setExitMessageVisible(false)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
navigationCoordinator.exitMessageVisible.onEach { exitMessageVisible ->
|
||||
backPressedCallback.isEnabled = !exitMessageVisible
|
||||
finishBackPressedCallback.isEnabled = exitMessageVisible
|
||||
@ -75,17 +76,20 @@ class MainActivity : ComponentActivity() {
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent?) = intent?.apply {
|
||||
when (action) {
|
||||
Intent.ACTION_SEND -> intent.getStringExtra(Intent.EXTRA_TEXT)?.let { content ->
|
||||
handleCreatePost(content)
|
||||
}
|
||||
private fun handleIntent(intent: Intent?) =
|
||||
intent?.apply {
|
||||
when (action) {
|
||||
Intent.ACTION_SEND ->
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { content ->
|
||||
handleCreatePost(content)
|
||||
}
|
||||
|
||||
else -> data.toString().takeUnless { it.isEmpty() }?.also { url ->
|
||||
handleDeeplink(url)
|
||||
else ->
|
||||
data.toString().takeUnless { it.isEmpty() }?.also { url ->
|
||||
handleDeeplink(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeeplink(url: String) {
|
||||
val navigationCoordinator = getNavigationCoordinator()
|
||||
@ -94,11 +98,12 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
private fun handleCreatePost(content: String) {
|
||||
val looksLikeAnUrl = Patterns.WEB_URL.matcher(content).matches()
|
||||
val event = if (looksLikeAnUrl) {
|
||||
ComposeEvent.WithUrl(content)
|
||||
} else {
|
||||
ComposeEvent.WithText(content)
|
||||
}
|
||||
val event =
|
||||
if (looksLikeAnUrl) {
|
||||
ComposeEvent.WithUrl(content)
|
||||
} else {
|
||||
ComposeEvent.WithText(content)
|
||||
}
|
||||
val navigationCoordinator = getNavigationCoordinator()
|
||||
navigationCoordinator.submitComposeEvent(event)
|
||||
}
|
||||
|
@ -24,15 +24,16 @@ class MainApplication : Application(), ImageLoaderFactory {
|
||||
sharedHelperModule,
|
||||
)
|
||||
|
||||
AppInfo.versionCode = buildString {
|
||||
append(BuildConfig.VERSION_NAME)
|
||||
append(" (")
|
||||
append(BuildConfig.VERSION_CODE)
|
||||
append(")")
|
||||
if (BuildConfig.DEBUG) {
|
||||
append(" - dev")
|
||||
AppInfo.versionCode =
|
||||
buildString {
|
||||
append(BuildConfig.VERSION_NAME)
|
||||
append(" (")
|
||||
append(BuildConfig.VERSION_CODE)
|
||||
append(")")
|
||||
if (BuildConfig.DEBUG) {
|
||||
append(" - dev")
|
||||
}
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
val crashReportWriter: CrashReportWriter by inject()
|
||||
val crashReportConfig: CrashReportConfiguration by inject()
|
||||
|
@ -5,11 +5,12 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvide
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
val coreApiModule = module {
|
||||
single<ServiceProvider>(named("default")) {
|
||||
DefaultServiceProvider()
|
||||
val coreApiModule =
|
||||
module {
|
||||
single<ServiceProvider>(named("default")) {
|
||||
DefaultServiceProvider()
|
||||
}
|
||||
single<ServiceProvider>(named("custom")) {
|
||||
DefaultServiceProvider()
|
||||
}
|
||||
}
|
||||
single<ServiceProvider>(named("custom")) {
|
||||
DefaultServiceProvider()
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
internal class DefaultServiceProvider : ServiceProvider {
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_INSTANCE = "lemmy.world"
|
||||
private const val VERSION = "v3"
|
||||
@ -74,36 +73,38 @@ internal class DefaultServiceProvider : ServiceProvider {
|
||||
}
|
||||
|
||||
private fun reinitialize() {
|
||||
val client = HttpClient(factory) {
|
||||
defaultRequest {
|
||||
url {
|
||||
host = currentInstance
|
||||
val client =
|
||||
HttpClient(factory) {
|
||||
defaultRequest {
|
||||
url {
|
||||
host = currentInstance
|
||||
}
|
||||
}
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = 600_000
|
||||
connectTimeoutMillis = 30_000
|
||||
socketTimeoutMillis = 30_000
|
||||
}
|
||||
if (ENABLE_LOGGING) {
|
||||
install(Logging) {
|
||||
logger = defaultLogger
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = 600_000
|
||||
connectTimeoutMillis = 30_000
|
||||
socketTimeoutMillis = 30_000
|
||||
}
|
||||
if (ENABLE_LOGGING) {
|
||||
install(Logging) {
|
||||
logger = defaultLogger
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
val ktorfit = Ktorfit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.httpClient(client)
|
||||
.build()
|
||||
val ktorfit =
|
||||
Ktorfit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.httpClient(client)
|
||||
.build()
|
||||
auth = ktorfit.create()
|
||||
post = ktorfit.create()
|
||||
community = ktorfit.create()
|
||||
|
@ -3,8 +3,9 @@ package com.github.diegoberaldin.raccoonforlemmy.core.api.provider
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.debug.logDebug
|
||||
import io.ktor.client.plugins.logging.Logger
|
||||
|
||||
internal val defaultLogger = object : Logger {
|
||||
override fun log(message: String) {
|
||||
logDebug(message)
|
||||
internal val defaultLogger =
|
||||
object : Logger {
|
||||
override fun log(message: String) {
|
||||
logDebug(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.service.SiteService
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.service.UserService
|
||||
|
||||
interface ServiceProvider {
|
||||
|
||||
val currentInstance: String
|
||||
val auth: AuthService
|
||||
val post: PostService
|
||||
|
@ -26,7 +26,6 @@ import de.jensklingenberg.ktorfit.http.PUT
|
||||
import de.jensklingenberg.ktorfit.http.Query
|
||||
|
||||
interface CommunityService {
|
||||
|
||||
@GET("community")
|
||||
suspend fun get(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
|
@ -25,7 +25,6 @@ import de.jensklingenberg.ktorfit.http.PUT
|
||||
import de.jensklingenberg.ktorfit.http.Query
|
||||
|
||||
interface UserService {
|
||||
|
||||
@GET("user")
|
||||
suspend fun getDetails(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
|
@ -13,19 +13,24 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
|
||||
class DefaultBarColorProvider : BarColorProvider {
|
||||
@Composable
|
||||
override fun setBarColorAccordingToTheme(theme: UiTheme, barTheme: UiBarTheme) {
|
||||
override fun setBarColorAccordingToTheme(
|
||||
theme: UiTheme,
|
||||
barTheme: UiBarTheme,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
LaunchedEffect(theme, barTheme) {
|
||||
(view.context as? Activity)?.window?.apply {
|
||||
val baseColor = when (theme) {
|
||||
UiTheme.Light -> Color.White
|
||||
else -> Color.Black
|
||||
}
|
||||
val barColor = when (barTheme) {
|
||||
UiBarTheme.Opaque -> baseColor.copy(alpha = 0.25f)
|
||||
UiBarTheme.Transparent -> baseColor.copy(alpha = 0.01f)
|
||||
else -> baseColor
|
||||
}.toArgb()
|
||||
val baseColor =
|
||||
when (theme) {
|
||||
UiTheme.Light -> Color.White
|
||||
else -> Color.Black
|
||||
}
|
||||
val barColor =
|
||||
when (barTheme) {
|
||||
UiBarTheme.Opaque -> baseColor.copy(alpha = 0.25f)
|
||||
UiBarTheme.Transparent -> baseColor.copy(alpha = 0.01f)
|
||||
else -> baseColor
|
||||
}.toArgb()
|
||||
statusBarColor = barColor
|
||||
navigationBarColor = barColor
|
||||
|
||||
@ -40,14 +45,16 @@ class DefaultBarColorProvider : BarColorProvider {
|
||||
}
|
||||
|
||||
WindowCompat.getInsetsController(this, decorView).apply {
|
||||
isAppearanceLightStatusBars = when (theme) {
|
||||
UiTheme.Light -> true
|
||||
else -> false
|
||||
}
|
||||
isAppearanceLightNavigationBars = when (theme) {
|
||||
UiTheme.Light -> true
|
||||
else -> false
|
||||
}
|
||||
isAppearanceLightStatusBars =
|
||||
when (theme) {
|
||||
UiTheme.Light -> true
|
||||
else -> false
|
||||
}
|
||||
isAppearanceLightNavigationBars =
|
||||
when (theme) {
|
||||
UiTheme.Light -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
import com.materialkolor.dynamicColorScheme
|
||||
|
||||
internal class DefaultColorSchemeProvider(private val context: Context) : ColorSchemeProvider {
|
||||
|
||||
override val supportsDynamicColors: Boolean
|
||||
@ChecksSdkIntAtLeast(31)
|
||||
get() {
|
||||
@ -24,53 +23,54 @@ internal class DefaultColorSchemeProvider(private val context: Context) : ColorS
|
||||
theme: UiTheme,
|
||||
dynamic: Boolean,
|
||||
customSeed: Color?,
|
||||
): ColorScheme = when (theme) {
|
||||
UiTheme.Dark -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicDarkColorScheme(context)
|
||||
}
|
||||
): ColorScheme =
|
||||
when (theme) {
|
||||
UiTheme.Dark -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicDarkColorScheme(context)
|
||||
}
|
||||
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, true)
|
||||
}
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
DarkColors
|
||||
else -> {
|
||||
DarkColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UiTheme.Black -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicDarkColorScheme(context).blackify()
|
||||
}
|
||||
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, true).blackify()
|
||||
}
|
||||
|
||||
else -> {
|
||||
BlackColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, false)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LightColors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UiTheme.Black -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicDarkColorScheme(context).blackify()
|
||||
}
|
||||
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, true).blackify()
|
||||
}
|
||||
|
||||
else -> {
|
||||
BlackColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
dynamic -> {
|
||||
dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
customSeed != null -> {
|
||||
dynamicColorScheme(customSeed, false)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LightColors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class DefaultThemeRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
val dispatcherTestRule = DispatcherTestRule()
|
||||
|
||||
|
@ -6,62 +6,72 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface CommentBarTheme {
|
||||
data object Green : CommentBarTheme
|
||||
|
||||
data object Blue : CommentBarTheme
|
||||
|
||||
data object Red : CommentBarTheme
|
||||
|
||||
data object Rainbow : CommentBarTheme
|
||||
}
|
||||
|
||||
fun Int?.toCommentBarTheme(): CommentBarTheme = when (this) {
|
||||
3 -> CommentBarTheme.Rainbow
|
||||
2 -> CommentBarTheme.Red
|
||||
1 -> CommentBarTheme.Green
|
||||
else -> CommentBarTheme.Blue
|
||||
}
|
||||
fun Int?.toCommentBarTheme(): CommentBarTheme =
|
||||
when (this) {
|
||||
3 -> CommentBarTheme.Rainbow
|
||||
2 -> CommentBarTheme.Red
|
||||
1 -> CommentBarTheme.Green
|
||||
else -> CommentBarTheme.Blue
|
||||
}
|
||||
|
||||
fun CommentBarTheme.toInt(): Int = when (this) {
|
||||
CommentBarTheme.Rainbow -> 3
|
||||
CommentBarTheme.Red -> 2
|
||||
CommentBarTheme.Green -> 1
|
||||
CommentBarTheme.Blue -> 0
|
||||
}
|
||||
fun CommentBarTheme.toInt(): Int =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> 3
|
||||
CommentBarTheme.Red -> 2
|
||||
CommentBarTheme.Green -> 1
|
||||
CommentBarTheme.Blue -> 0
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommentBarTheme?.toReadableName() = when (this) {
|
||||
CommentBarTheme.Rainbow -> LocalXmlStrings.current.settingsCommentBarThemeMulti
|
||||
CommentBarTheme.Red -> LocalXmlStrings.current.settingsCommentBarThemeRed
|
||||
CommentBarTheme.Green -> LocalXmlStrings.current.settingsCommentBarThemeGreen
|
||||
CommentBarTheme.Blue -> LocalXmlStrings.current.settingsCommentBarThemeBlue
|
||||
else -> LocalXmlStrings.current.settingsColorCustom
|
||||
}
|
||||
fun CommentBarTheme?.toReadableName() =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> LocalXmlStrings.current.settingsCommentBarThemeMulti
|
||||
CommentBarTheme.Red -> LocalXmlStrings.current.settingsCommentBarThemeRed
|
||||
CommentBarTheme.Green -> LocalXmlStrings.current.settingsCommentBarThemeGreen
|
||||
CommentBarTheme.Blue -> LocalXmlStrings.current.settingsCommentBarThemeBlue
|
||||
else -> LocalXmlStrings.current.settingsColorCustom
|
||||
}
|
||||
|
||||
fun CommentBarTheme?.toUpVoteColor(): Color = when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFF00FF00)
|
||||
CommentBarTheme.Red -> Color(0xFFD00000)
|
||||
CommentBarTheme.Green -> Color(0xFF52B788)
|
||||
CommentBarTheme.Blue -> Color(0xFF014F86)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
fun CommentBarTheme?.toUpVoteColor(): Color =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFF00FF00)
|
||||
CommentBarTheme.Red -> Color(0xFFD00000)
|
||||
CommentBarTheme.Green -> Color(0xFF52B788)
|
||||
CommentBarTheme.Blue -> Color(0xFF014F86)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
|
||||
fun CommentBarTheme?.toDownVoteColor(): Color = when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFFF0000)
|
||||
CommentBarTheme.Red -> Color(0xFF2FFFFF)
|
||||
CommentBarTheme.Green -> Color(0xFFAD4877)
|
||||
CommentBarTheme.Blue -> Color(0xFF9400D3)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
fun CommentBarTheme?.toDownVoteColor(): Color =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFFF0000)
|
||||
CommentBarTheme.Red -> Color(0xFF2FFFFF)
|
||||
CommentBarTheme.Green -> Color(0xFFAD4877)
|
||||
CommentBarTheme.Blue -> Color(0xFF9400D3)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
|
||||
fun CommentBarTheme?.toReplyColor(): Color = when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFFF5722)
|
||||
CommentBarTheme.Red -> Color(0xFF8BC34A)
|
||||
CommentBarTheme.Green -> Color(0xFFFF9800)
|
||||
CommentBarTheme.Blue -> Color(0xFF388E3C)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
fun CommentBarTheme?.toReplyColor(): Color =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFFF5722)
|
||||
CommentBarTheme.Red -> Color(0xFF8BC34A)
|
||||
CommentBarTheme.Green -> Color(0xFFFF9800)
|
||||
CommentBarTheme.Blue -> Color(0xFF388E3C)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
|
||||
fun CommentBarTheme?.toSaveColor(): Color = when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFE040FB)
|
||||
CommentBarTheme.Red -> Color(0xFFFFC107)
|
||||
CommentBarTheme.Green -> Color(0xFF388E3C)
|
||||
CommentBarTheme.Blue -> Color(0xFF7C4DFF)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
fun CommentBarTheme?.toSaveColor(): Color =
|
||||
when (this) {
|
||||
CommentBarTheme.Rainbow -> Color(0xFFE040FB)
|
||||
CommentBarTheme.Red -> Color(0xFFFFC107)
|
||||
CommentBarTheme.Green -> Color(0xFF388E3C)
|
||||
CommentBarTheme.Blue -> Color(0xFF7C4DFF)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
|
@ -5,45 +5,54 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface FontScale {
|
||||
data object Largest : FontScale
|
||||
|
||||
data object Larger : FontScale
|
||||
|
||||
data object Large : FontScale
|
||||
|
||||
data object Normal : FontScale
|
||||
|
||||
data object Small : FontScale
|
||||
|
||||
data object Smaller : FontScale
|
||||
|
||||
data object Smallest : FontScale
|
||||
}
|
||||
|
||||
val FontScale.scaleFactor: Float
|
||||
get() = when (this) {
|
||||
FontScale.Largest -> ReferenceValues.largest
|
||||
FontScale.Larger -> ReferenceValues.larger
|
||||
FontScale.Large -> ReferenceValues.large
|
||||
FontScale.Normal -> ReferenceValues.normal
|
||||
FontScale.Small -> ReferenceValues.small
|
||||
FontScale.Smaller -> ReferenceValues.smaller
|
||||
FontScale.Smallest -> ReferenceValues.smallest
|
||||
}
|
||||
get() =
|
||||
when (this) {
|
||||
FontScale.Largest -> ReferenceValues.largest
|
||||
FontScale.Larger -> ReferenceValues.larger
|
||||
FontScale.Large -> ReferenceValues.large
|
||||
FontScale.Normal -> ReferenceValues.normal
|
||||
FontScale.Small -> ReferenceValues.small
|
||||
FontScale.Smaller -> ReferenceValues.smaller
|
||||
FontScale.Smallest -> ReferenceValues.smallest
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FontScale.toReadableName(): String = when (this) {
|
||||
FontScale.Largest -> LocalXmlStrings.current.settingsContentFontLargest
|
||||
FontScale.Larger -> LocalXmlStrings.current.settingsContentFontLarger
|
||||
FontScale.Large -> LocalXmlStrings.current.settingsContentFontLarge
|
||||
FontScale.Normal -> LocalXmlStrings.current.settingsContentFontNormal
|
||||
FontScale.Small -> LocalXmlStrings.current.settingsContentFontSmall
|
||||
FontScale.Smaller -> LocalXmlStrings.current.settingsContentFontSmaller
|
||||
FontScale.Smallest -> LocalXmlStrings.current.settingsContentFontSmallest
|
||||
}
|
||||
fun FontScale.toReadableName(): String =
|
||||
when (this) {
|
||||
FontScale.Largest -> LocalXmlStrings.current.settingsContentFontLargest
|
||||
FontScale.Larger -> LocalXmlStrings.current.settingsContentFontLarger
|
||||
FontScale.Large -> LocalXmlStrings.current.settingsContentFontLarge
|
||||
FontScale.Normal -> LocalXmlStrings.current.settingsContentFontNormal
|
||||
FontScale.Small -> LocalXmlStrings.current.settingsContentFontSmall
|
||||
FontScale.Smaller -> LocalXmlStrings.current.settingsContentFontSmaller
|
||||
FontScale.Smallest -> LocalXmlStrings.current.settingsContentFontSmallest
|
||||
}
|
||||
|
||||
fun Float.toFontScale(): FontScale = when (this) {
|
||||
ReferenceValues.largest -> FontScale.Largest
|
||||
ReferenceValues.larger -> FontScale.Larger
|
||||
ReferenceValues.large -> FontScale.Large
|
||||
ReferenceValues.small -> FontScale.Small
|
||||
ReferenceValues.smaller -> FontScale.Smaller
|
||||
ReferenceValues.smallest -> FontScale.Smallest
|
||||
else -> FontScale.Normal
|
||||
}
|
||||
fun Float.toFontScale(): FontScale =
|
||||
when (this) {
|
||||
ReferenceValues.largest -> FontScale.Largest
|
||||
ReferenceValues.larger -> FontScale.Larger
|
||||
ReferenceValues.large -> FontScale.Large
|
||||
ReferenceValues.small -> FontScale.Small
|
||||
ReferenceValues.smaller -> FontScale.Smaller
|
||||
ReferenceValues.smallest -> FontScale.Smallest
|
||||
else -> FontScale.Normal
|
||||
}
|
||||
|
||||
// log(2 + 0.1 * n) / log(2)
|
||||
private object ReferenceValues {
|
||||
|
@ -5,25 +5,30 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface PostLayout {
|
||||
data object Card : PostLayout
|
||||
|
||||
data object Compact : PostLayout
|
||||
|
||||
data object Full : PostLayout
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostLayout.toReadableName(): String = when (this) {
|
||||
PostLayout.Full -> LocalXmlStrings.current.settingsPostLayoutFull
|
||||
PostLayout.Compact -> LocalXmlStrings.current.settingsPostLayoutCompact
|
||||
else -> LocalXmlStrings.current.settingsPostLayoutCard
|
||||
}
|
||||
fun PostLayout.toReadableName(): String =
|
||||
when (this) {
|
||||
PostLayout.Full -> LocalXmlStrings.current.settingsPostLayoutFull
|
||||
PostLayout.Compact -> LocalXmlStrings.current.settingsPostLayoutCompact
|
||||
else -> LocalXmlStrings.current.settingsPostLayoutCard
|
||||
}
|
||||
|
||||
fun Int.toPostLayout(): PostLayout = when (this) {
|
||||
1 -> PostLayout.Compact
|
||||
2 -> PostLayout.Full
|
||||
else -> PostLayout.Card
|
||||
}
|
||||
fun Int.toPostLayout(): PostLayout =
|
||||
when (this) {
|
||||
1 -> PostLayout.Compact
|
||||
2 -> PostLayout.Full
|
||||
else -> PostLayout.Card
|
||||
}
|
||||
|
||||
fun PostLayout.toInt(): Int = when (this) {
|
||||
PostLayout.Full -> 2
|
||||
PostLayout.Compact -> 1
|
||||
else -> 0
|
||||
}
|
||||
fun PostLayout.toInt(): Int =
|
||||
when (this) {
|
||||
PostLayout.Full -> 2
|
||||
PostLayout.Compact -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
@ -5,19 +5,23 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface UiBarTheme {
|
||||
data object Solid : UiBarTheme
|
||||
|
||||
data object Transparent : UiBarTheme
|
||||
|
||||
data object Opaque : UiBarTheme
|
||||
}
|
||||
|
||||
fun UiBarTheme?.toInt(): Int = when (this) {
|
||||
UiBarTheme.Transparent -> 2
|
||||
UiBarTheme.Opaque -> 1
|
||||
else -> 0
|
||||
}
|
||||
fun UiBarTheme?.toInt(): Int =
|
||||
when (this) {
|
||||
UiBarTheme.Transparent -> 2
|
||||
UiBarTheme.Opaque -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UiBarTheme?.toReadableName(): String = when (this) {
|
||||
UiBarTheme.Transparent -> LocalXmlStrings.current.barThemeTransparent
|
||||
UiBarTheme.Opaque -> LocalXmlStrings.current.barThemeOpaque
|
||||
else -> ""
|
||||
}
|
||||
fun UiBarTheme?.toReadableName(): String =
|
||||
when (this) {
|
||||
UiBarTheme.Transparent -> LocalXmlStrings.current.barThemeTransparent
|
||||
UiBarTheme.Opaque -> LocalXmlStrings.current.barThemeOpaque
|
||||
else -> ""
|
||||
}
|
||||
|
@ -10,24 +10,27 @@ enum class UiFontFamily {
|
||||
Poppins,
|
||||
}
|
||||
|
||||
fun Int.toUiFontFamily() = when (this) {
|
||||
0 -> UiFontFamily.Poppins
|
||||
3 -> UiFontFamily.NotoSans
|
||||
4 -> UiFontFamily.CharisSIL
|
||||
else -> UiFontFamily.Default
|
||||
}
|
||||
fun Int.toUiFontFamily() =
|
||||
when (this) {
|
||||
0 -> UiFontFamily.Poppins
|
||||
3 -> UiFontFamily.NotoSans
|
||||
4 -> UiFontFamily.CharisSIL
|
||||
else -> UiFontFamily.Default
|
||||
}
|
||||
|
||||
fun UiFontFamily.toInt() = when (this) {
|
||||
UiFontFamily.Poppins -> 0
|
||||
UiFontFamily.NotoSans -> 3
|
||||
UiFontFamily.CharisSIL -> 4
|
||||
UiFontFamily.Default -> 7
|
||||
}
|
||||
fun UiFontFamily.toInt() =
|
||||
when (this) {
|
||||
UiFontFamily.Poppins -> 0
|
||||
UiFontFamily.NotoSans -> 3
|
||||
UiFontFamily.CharisSIL -> 4
|
||||
UiFontFamily.Default -> 7
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UiFontFamily.toReadableName() = when (this) {
|
||||
UiFontFamily.Poppins -> "Poppins"
|
||||
UiFontFamily.NotoSans -> "Noto Sans"
|
||||
UiFontFamily.CharisSIL -> "Charis SIL"
|
||||
UiFontFamily.Default -> LocalXmlStrings.current.settingsFontFamilyDefault
|
||||
}
|
||||
fun UiFontFamily.toReadableName() =
|
||||
when (this) {
|
||||
UiFontFamily.Poppins -> "Poppins"
|
||||
UiFontFamily.NotoSans -> "Noto Sans"
|
||||
UiFontFamily.CharisSIL -> "Charis SIL"
|
||||
UiFontFamily.Default -> LocalXmlStrings.current.settingsFontFamilyDefault
|
||||
}
|
||||
|
@ -10,34 +10,40 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface UiTheme {
|
||||
data object Light : UiTheme
|
||||
|
||||
data object Dark : UiTheme
|
||||
|
||||
data object Black : UiTheme
|
||||
}
|
||||
|
||||
fun Int?.toUiTheme(): UiTheme? = when (this) {
|
||||
2 -> UiTheme.Black
|
||||
1 -> UiTheme.Dark
|
||||
0 -> UiTheme.Light
|
||||
else -> null
|
||||
}
|
||||
fun Int?.toUiTheme(): UiTheme? =
|
||||
when (this) {
|
||||
2 -> UiTheme.Black
|
||||
1 -> UiTheme.Dark
|
||||
0 -> UiTheme.Light
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun UiTheme?.toInt(): Int? = when (this) {
|
||||
UiTheme.Black -> 2
|
||||
UiTheme.Dark -> 1
|
||||
UiTheme.Light -> 0
|
||||
else -> null
|
||||
}
|
||||
fun UiTheme?.toInt(): Int? =
|
||||
when (this) {
|
||||
UiTheme.Black -> 2
|
||||
UiTheme.Dark -> 1
|
||||
UiTheme.Light -> 0
|
||||
else -> null
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UiTheme?.toReadableName(): String = when (this) {
|
||||
UiTheme.Black -> LocalXmlStrings.current.settingsThemeBlack
|
||||
UiTheme.Dark -> LocalXmlStrings.current.settingsThemeDark
|
||||
UiTheme.Light -> LocalXmlStrings.current.settingsThemeLight
|
||||
else -> LocalXmlStrings.current.settingsFontFamilyDefault
|
||||
}
|
||||
fun UiTheme?.toReadableName(): String =
|
||||
when (this) {
|
||||
UiTheme.Black -> LocalXmlStrings.current.settingsThemeBlack
|
||||
UiTheme.Dark -> LocalXmlStrings.current.settingsThemeDark
|
||||
UiTheme.Light -> LocalXmlStrings.current.settingsThemeLight
|
||||
else -> LocalXmlStrings.current.settingsFontFamilyDefault
|
||||
}
|
||||
|
||||
fun UiTheme.toIcon(): ImageVector = when (this) {
|
||||
UiTheme.Black -> Icons.Default.DarkMode
|
||||
UiTheme.Dark -> Icons.Outlined.DarkMode
|
||||
UiTheme.Light -> Icons.Default.LightMode
|
||||
}
|
||||
fun UiTheme.toIcon(): ImageVector =
|
||||
when (this) {
|
||||
UiTheme.Black -> Icons.Default.DarkMode
|
||||
UiTheme.Dark -> Icons.Outlined.DarkMode
|
||||
UiTheme.Light -> Icons.Default.LightMode
|
||||
}
|
||||
|
@ -9,32 +9,38 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface VoteFormat {
|
||||
data object Aggregated : VoteFormat
|
||||
|
||||
data object Separated : VoteFormat
|
||||
|
||||
data object Percentage : VoteFormat
|
||||
|
||||
data object Hidden : VoteFormat
|
||||
}
|
||||
|
||||
fun VoteFormat.toLong(): Long = when (this) {
|
||||
VoteFormat.Percentage -> 2L
|
||||
VoteFormat.Separated -> 1L
|
||||
VoteFormat.Aggregated -> 0L
|
||||
VoteFormat.Hidden -> -1L
|
||||
}
|
||||
fun VoteFormat.toLong(): Long =
|
||||
when (this) {
|
||||
VoteFormat.Percentage -> 2L
|
||||
VoteFormat.Separated -> 1L
|
||||
VoteFormat.Aggregated -> 0L
|
||||
VoteFormat.Hidden -> -1L
|
||||
}
|
||||
|
||||
fun Long.toVoteFormat(): VoteFormat = when (this) {
|
||||
2L -> VoteFormat.Percentage
|
||||
1L -> VoteFormat.Separated
|
||||
-1L -> VoteFormat.Hidden
|
||||
else -> VoteFormat.Aggregated
|
||||
}
|
||||
fun Long.toVoteFormat(): VoteFormat =
|
||||
when (this) {
|
||||
2L -> VoteFormat.Percentage
|
||||
1L -> VoteFormat.Separated
|
||||
-1L -> VoteFormat.Hidden
|
||||
else -> VoteFormat.Aggregated
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VoteFormat.toReadableName(): String = when (this) {
|
||||
VoteFormat.Percentage -> LocalXmlStrings.current.settingsVoteFormatPercentage
|
||||
VoteFormat.Separated -> LocalXmlStrings.current.settingsVoteFormatSeparated
|
||||
VoteFormat.Hidden -> LocalXmlStrings.current.settingsVoteFormatHidden
|
||||
else -> LocalXmlStrings.current.settingsVoteFormatAggregated
|
||||
}
|
||||
fun VoteFormat.toReadableName(): String =
|
||||
when (this) {
|
||||
VoteFormat.Percentage -> LocalXmlStrings.current.settingsVoteFormatPercentage
|
||||
VoteFormat.Separated -> LocalXmlStrings.current.settingsVoteFormatSeparated
|
||||
VoteFormat.Hidden -> LocalXmlStrings.current.settingsVoteFormatHidden
|
||||
else -> LocalXmlStrings.current.settingsVoteFormatAggregated
|
||||
}
|
||||
|
||||
fun formatToReadableValue(
|
||||
voteFormat: VoteFormat,
|
||||
@ -45,66 +51,67 @@ fun formatToReadableValue(
|
||||
downVoteColor: Color,
|
||||
upVoted: Boolean = false,
|
||||
downVoted: Boolean = false,
|
||||
): AnnotatedString = buildAnnotatedString {
|
||||
when (voteFormat) {
|
||||
VoteFormat.Percentage -> {
|
||||
val totalVotes = upVotes + downVotes
|
||||
val percVote = if (totalVotes == 0) 0.0 else upVotes.toDouble() / totalVotes
|
||||
val text = "${(percVote * 100).toInt()} %"
|
||||
append(text)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = text.length,
|
||||
)
|
||||
} else if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = 0,
|
||||
end = length,
|
||||
)
|
||||
): AnnotatedString =
|
||||
buildAnnotatedString {
|
||||
when (voteFormat) {
|
||||
VoteFormat.Percentage -> {
|
||||
val totalVotes = upVotes + downVotes
|
||||
val percVote = if (totalVotes == 0) 0.0 else upVotes.toDouble() / totalVotes
|
||||
val text = "${(percVote * 100).toInt()} %"
|
||||
append(text)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = text.length,
|
||||
)
|
||||
} else if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = 0,
|
||||
end = length,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VoteFormat.Separated -> {
|
||||
val upvoteText = upVotes.toString()
|
||||
append(upvoteText)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = upvoteText.length,
|
||||
)
|
||||
VoteFormat.Separated -> {
|
||||
val upvoteText = upVotes.toString()
|
||||
append(upvoteText)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = upvoteText.length,
|
||||
)
|
||||
}
|
||||
append(" / ")
|
||||
val downvoteText = downVotes.toString()
|
||||
append(downvoteText)
|
||||
if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = upvoteText.length + 3,
|
||||
end = upvoteText.length + 3 + downvoteText.length,
|
||||
)
|
||||
}
|
||||
}
|
||||
append(" / ")
|
||||
val downvoteText = downVotes.toString()
|
||||
append(downvoteText)
|
||||
if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = upvoteText.length + 3,
|
||||
end = upvoteText.length + 3 + downvoteText.length,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val text = score.toString()
|
||||
append(text)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = text.length,
|
||||
)
|
||||
} else if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = 0,
|
||||
end = length,
|
||||
)
|
||||
else -> {
|
||||
val text = score.toString()
|
||||
append(text)
|
||||
if (upVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = upVoteColor),
|
||||
start = 0,
|
||||
end = text.length,
|
||||
)
|
||||
} else if (downVoted) {
|
||||
addStyle(
|
||||
style = SpanStyle(color = downVoteColor),
|
||||
start = 0,
|
||||
end = length,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,9 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.repository.Theme
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val coreAppearanceModule = module {
|
||||
includes(nativeAppearanceModule)
|
||||
val coreAppearanceModule =
|
||||
module {
|
||||
includes(nativeAppearanceModule)
|
||||
|
||||
singleOf<ThemeRepository>(::DefaultThemeRepository)
|
||||
}
|
||||
singleOf<ThemeRepository>(::DefaultThemeRepository)
|
||||
}
|
||||
|
@ -6,6 +6,9 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.ColorSchem
|
||||
import org.koin.core.module.Module
|
||||
|
||||
expect val nativeAppearanceModule: Module
|
||||
|
||||
expect fun getThemeRepository(): ThemeRepository
|
||||
|
||||
expect fun getColorSchemeProvider(): ColorSchemeProvider
|
||||
|
||||
expect fun getBarColorProvider(): BarColorProvider
|
||||
|
@ -9,7 +9,10 @@ data class ContentFontScales(
|
||||
|
||||
sealed interface ContentFontClass {
|
||||
data object Title : ContentFontClass
|
||||
|
||||
data object Body : ContentFontClass
|
||||
|
||||
data object Comment : ContentFontClass
|
||||
|
||||
data object AncillaryText : ContentFontClass
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal class DefaultThemeRepository : ThemeRepository {
|
||||
|
||||
override val uiTheme = MutableStateFlow<UiTheme?>(null)
|
||||
override val uiFontFamily = MutableStateFlow(UiFontFamily.Poppins)
|
||||
override val uiFontScale = MutableStateFlow(1f)
|
||||
@ -52,7 +51,10 @@ internal class DefaultThemeRepository : ThemeRepository {
|
||||
dynamicColors.value = value
|
||||
}
|
||||
|
||||
override fun getCommentBarColor(depth: Int, commentBarTheme: CommentBarTheme): Color {
|
||||
override fun getCommentBarColor(
|
||||
depth: Int,
|
||||
commentBarTheme: CommentBarTheme,
|
||||
): Color {
|
||||
val colors = getCommentBarColors(commentBarTheme)
|
||||
if (colors.isEmpty()) {
|
||||
return Color.Transparent
|
||||
@ -91,40 +93,44 @@ internal class DefaultThemeRepository : ThemeRepository {
|
||||
|
||||
override fun getCommentBarColors(commentBarTheme: CommentBarTheme): List<Color> =
|
||||
when (commentBarTheme) {
|
||||
CommentBarTheme.Green -> buildList {
|
||||
this += Color(0xFF1B4332)
|
||||
this += Color(0xFF2D6A4F)
|
||||
this += Color(0xFF40916C)
|
||||
this += Color(0xFF52B788)
|
||||
this += Color(0xFF74C69D)
|
||||
this += Color(0xFF95D5B2)
|
||||
}
|
||||
CommentBarTheme.Green ->
|
||||
buildList {
|
||||
this += Color(0xFF1B4332)
|
||||
this += Color(0xFF2D6A4F)
|
||||
this += Color(0xFF40916C)
|
||||
this += Color(0xFF52B788)
|
||||
this += Color(0xFF74C69D)
|
||||
this += Color(0xFF95D5B2)
|
||||
}
|
||||
|
||||
CommentBarTheme.Red -> buildList {
|
||||
this += Color(0xFF6A040F)
|
||||
this += Color(0xFF9D0208)
|
||||
this += Color(0xFFD00000)
|
||||
this += Color(0xFFDC2F02)
|
||||
this += Color(0xFFE85D04)
|
||||
this += Color(0xFFF48C06)
|
||||
}
|
||||
CommentBarTheme.Red ->
|
||||
buildList {
|
||||
this += Color(0xFF6A040F)
|
||||
this += Color(0xFF9D0208)
|
||||
this += Color(0xFFD00000)
|
||||
this += Color(0xFFDC2F02)
|
||||
this += Color(0xFFE85D04)
|
||||
this += Color(0xFFF48C06)
|
||||
}
|
||||
|
||||
CommentBarTheme.Blue -> buildList {
|
||||
this += Color(0xFF012A4A)
|
||||
this += Color(0xFF013A63)
|
||||
this += Color(0xFF014F86)
|
||||
this += Color(0xFF2C7DA0)
|
||||
this += Color(0xFF61A5C2)
|
||||
this += Color(0xFFA9D6E5)
|
||||
}
|
||||
CommentBarTheme.Blue ->
|
||||
buildList {
|
||||
this += Color(0xFF012A4A)
|
||||
this += Color(0xFF013A63)
|
||||
this += Color(0xFF014F86)
|
||||
this += Color(0xFF2C7DA0)
|
||||
this += Color(0xFF61A5C2)
|
||||
this += Color(0xFFA9D6E5)
|
||||
}
|
||||
|
||||
CommentBarTheme.Rainbow -> buildList {
|
||||
this += Color(0xFF9400D3)
|
||||
this += Color(0xFF0000FF)
|
||||
this += Color(0xFF00FF00)
|
||||
this += Color(0xFFFFFF00)
|
||||
this += Color(0xFFFF7F00)
|
||||
this += Color(0xFFFF0000)
|
||||
}
|
||||
CommentBarTheme.Rainbow ->
|
||||
buildList {
|
||||
this += Color(0xFF9400D3)
|
||||
this += Color(0xFF0000FF)
|
||||
this += Color(0xFF00FF00)
|
||||
this += Color(0xFFFFFF00)
|
||||
this += Color(0xFFFF7F00)
|
||||
this += Color(0xFFFF0000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
@Stable
|
||||
interface ThemeRepository {
|
||||
|
||||
val uiTheme: StateFlow<UiTheme?>
|
||||
val uiFontFamily: StateFlow<UiFontFamily>
|
||||
val uiFontScale: StateFlow<Float>
|
||||
@ -40,7 +39,10 @@ interface ThemeRepository {
|
||||
|
||||
fun changeDynamicColors(value: Boolean)
|
||||
|
||||
fun getCommentBarColor(depth: Int, commentBarTheme: CommentBarTheme): Color
|
||||
fun getCommentBarColor(
|
||||
depth: Int,
|
||||
commentBarTheme: CommentBarTheme,
|
||||
): Color
|
||||
|
||||
fun changeCustomSeedColor(color: Color?)
|
||||
|
||||
@ -49,6 +51,7 @@ interface ThemeRepository {
|
||||
fun changeDownVoteColor(color: Color?)
|
||||
|
||||
fun changeReplyColor(color: Color?)
|
||||
|
||||
fun changeSaveColor(color: Color?)
|
||||
|
||||
fun changePostLayout(value: PostLayout)
|
||||
|
@ -18,24 +18,27 @@ fun AppTheme(
|
||||
barTheme: UiBarTheme = UiBarTheme.Solid,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val repository = remember {
|
||||
getThemeRepository()
|
||||
}
|
||||
val repository =
|
||||
remember {
|
||||
getThemeRepository()
|
||||
}
|
||||
|
||||
val themeState by repository.uiTheme.collectAsState()
|
||||
val customSeedColor by repository.customSeedColor.collectAsState()
|
||||
val defaultTheme = if (isSystemInDarkTheme()) {
|
||||
UiTheme.Dark
|
||||
} else {
|
||||
UiTheme.Light
|
||||
}
|
||||
val defaultTheme =
|
||||
if (isSystemInDarkTheme()) {
|
||||
UiTheme.Dark
|
||||
} else {
|
||||
UiTheme.Light
|
||||
}
|
||||
|
||||
val colorSchemeProvider = remember { getColorSchemeProvider() }
|
||||
val colorScheme = colorSchemeProvider.getColorScheme(
|
||||
theme = themeState ?: defaultTheme,
|
||||
dynamic = useDynamicColors,
|
||||
customSeed = customSeedColor,
|
||||
)
|
||||
val colorScheme =
|
||||
colorSchemeProvider.getColorScheme(
|
||||
theme = themeState ?: defaultTheme,
|
||||
dynamic = useDynamicColors,
|
||||
customSeed = customSeedColor,
|
||||
)
|
||||
|
||||
val fontFamily by repository.uiFontFamily.collectAsState()
|
||||
val typography = fontFamily.toTypography()
|
||||
|
@ -6,5 +6,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
|
||||
interface BarColorProvider {
|
||||
@Composable
|
||||
fun setBarColorAccordingToTheme(theme: UiTheme, barTheme: UiBarTheme)
|
||||
fun setBarColorAccordingToTheme(
|
||||
theme: UiTheme,
|
||||
barTheme: UiBarTheme,
|
||||
)
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
|
||||
@Stable
|
||||
interface ColorSchemeProvider {
|
||||
|
||||
val supportsDynamicColors: Boolean
|
||||
|
||||
fun getColorScheme(
|
||||
@ -17,17 +16,18 @@ interface ColorSchemeProvider {
|
||||
): ColorScheme
|
||||
}
|
||||
|
||||
fun ColorScheme.blackify(): ColorScheme = copy(
|
||||
background = md_theme_black_background,
|
||||
onBackground = md_theme_black_onBackground,
|
||||
surface = md_theme_black_surface,
|
||||
onSurface = md_theme_black_onSurface,
|
||||
surfaceVariant = md_theme_black_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_black_onSurfaceVariant,
|
||||
primaryContainer = md_theme_black_primaryContainer,
|
||||
onPrimaryContainer = md_theme_black_onPrimaryContainer,
|
||||
secondaryContainer = md_theme_black_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_black_onSecondaryContainer,
|
||||
tertiaryContainer = md_theme_black_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_black_onTertiaryContainer,
|
||||
)
|
||||
fun ColorScheme.blackify(): ColorScheme =
|
||||
copy(
|
||||
background = md_theme_black_background,
|
||||
onBackground = md_theme_black_onBackground,
|
||||
surface = md_theme_black_surface,
|
||||
onSurface = md_theme_black_onSurface,
|
||||
surfaceVariant = md_theme_black_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_black_onSurfaceVariant,
|
||||
primaryContainer = md_theme_black_primaryContainer,
|
||||
onPrimaryContainer = md_theme_black_onPrimaryContainer,
|
||||
secondaryContainer = md_theme_black_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_black_onSecondaryContainer,
|
||||
tertiaryContainer = md_theme_black_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_black_onTertiaryContainer,
|
||||
)
|
||||
|
@ -3,68 +3,70 @@ package com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
|
||||
internal val LightColors = lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
primaryContainer = md_theme_light_primaryContainer,
|
||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||
secondary = md_theme_light_secondary,
|
||||
onSecondary = md_theme_light_onSecondary,
|
||||
secondaryContainer = md_theme_light_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||
tertiary = md_theme_light_tertiary,
|
||||
onTertiary = md_theme_light_onTertiary,
|
||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||
error = md_theme_light_error,
|
||||
errorContainer = md_theme_light_errorContainer,
|
||||
onError = md_theme_light_onError,
|
||||
onErrorContainer = md_theme_light_onErrorContainer,
|
||||
background = md_theme_light_background,
|
||||
onBackground = md_theme_light_onBackground,
|
||||
surface = md_theme_light_surface,
|
||||
onSurface = md_theme_light_onSurface,
|
||||
surfaceVariant = md_theme_light_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||
outline = md_theme_light_outline,
|
||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
inversePrimary = md_theme_light_inversePrimary,
|
||||
surfaceTint = md_theme_light_surfaceTint,
|
||||
outlineVariant = md_theme_light_outlineVariant,
|
||||
scrim = md_theme_light_scrim,
|
||||
)
|
||||
internal val LightColors =
|
||||
lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
primaryContainer = md_theme_light_primaryContainer,
|
||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||
secondary = md_theme_light_secondary,
|
||||
onSecondary = md_theme_light_onSecondary,
|
||||
secondaryContainer = md_theme_light_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||
tertiary = md_theme_light_tertiary,
|
||||
onTertiary = md_theme_light_onTertiary,
|
||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||
error = md_theme_light_error,
|
||||
errorContainer = md_theme_light_errorContainer,
|
||||
onError = md_theme_light_onError,
|
||||
onErrorContainer = md_theme_light_onErrorContainer,
|
||||
background = md_theme_light_background,
|
||||
onBackground = md_theme_light_onBackground,
|
||||
surface = md_theme_light_surface,
|
||||
onSurface = md_theme_light_onSurface,
|
||||
surfaceVariant = md_theme_light_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||
outline = md_theme_light_outline,
|
||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
inversePrimary = md_theme_light_inversePrimary,
|
||||
surfaceTint = md_theme_light_surfaceTint,
|
||||
outlineVariant = md_theme_light_outlineVariant,
|
||||
scrim = md_theme_light_scrim,
|
||||
)
|
||||
|
||||
internal val DarkColors = darkColorScheme(
|
||||
primary = md_theme_dark_primary,
|
||||
onPrimary = md_theme_dark_onPrimary,
|
||||
primaryContainer = md_theme_dark_primaryContainer,
|
||||
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
||||
secondary = md_theme_dark_secondary,
|
||||
onSecondary = md_theme_dark_onSecondary,
|
||||
secondaryContainer = md_theme_dark_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
||||
tertiary = md_theme_dark_tertiary,
|
||||
onTertiary = md_theme_dark_onTertiary,
|
||||
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
||||
error = md_theme_dark_error,
|
||||
errorContainer = md_theme_dark_errorContainer,
|
||||
onError = md_theme_dark_onError,
|
||||
onErrorContainer = md_theme_dark_onErrorContainer,
|
||||
background = md_theme_dark_background,
|
||||
onBackground = md_theme_dark_onBackground,
|
||||
surface = md_theme_dark_surface,
|
||||
onSurface = md_theme_dark_onSurface,
|
||||
surfaceVariant = md_theme_dark_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
||||
outline = md_theme_dark_outline,
|
||||
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
inversePrimary = md_theme_dark_inversePrimary,
|
||||
surfaceTint = md_theme_dark_surfaceTint,
|
||||
outlineVariant = md_theme_dark_outlineVariant,
|
||||
scrim = md_theme_dark_scrim,
|
||||
)
|
||||
internal val DarkColors =
|
||||
darkColorScheme(
|
||||
primary = md_theme_dark_primary,
|
||||
onPrimary = md_theme_dark_onPrimary,
|
||||
primaryContainer = md_theme_dark_primaryContainer,
|
||||
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
||||
secondary = md_theme_dark_secondary,
|
||||
onSecondary = md_theme_dark_onSecondary,
|
||||
secondaryContainer = md_theme_dark_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
||||
tertiary = md_theme_dark_tertiary,
|
||||
onTertiary = md_theme_dark_onTertiary,
|
||||
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
||||
error = md_theme_dark_error,
|
||||
errorContainer = md_theme_dark_errorContainer,
|
||||
onError = md_theme_dark_onError,
|
||||
onErrorContainer = md_theme_dark_onErrorContainer,
|
||||
background = md_theme_dark_background,
|
||||
onBackground = md_theme_dark_onBackground,
|
||||
surface = md_theme_dark_surface,
|
||||
onSurface = md_theme_dark_onSurface,
|
||||
surfaceVariant = md_theme_dark_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
||||
outline = md_theme_dark_outline,
|
||||
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
inversePrimary = md_theme_dark_inversePrimary,
|
||||
surfaceTint = md_theme_dark_surfaceTint,
|
||||
outlineVariant = md_theme_dark_outlineVariant,
|
||||
scrim = md_theme_dark_scrim,
|
||||
)
|
||||
|
||||
internal val BlackColors = DarkColors.blackify()
|
||||
|
@ -17,130 +17,146 @@ import com.github.diegoberaldin.raccoonforlemmy.core.resources.di.getCoreResourc
|
||||
@Composable
|
||||
fun UiFontFamily.toTypography(): Typography {
|
||||
val coreResources = remember { getCoreResources() }
|
||||
val fontFamily = when (this) {
|
||||
UiFontFamily.NotoSans -> coreResources.notoSans
|
||||
UiFontFamily.CharisSIL -> coreResources.charisSil
|
||||
UiFontFamily.Poppins -> coreResources.poppins
|
||||
else -> FontFamily.Default
|
||||
}
|
||||
val fontFamily =
|
||||
when (this) {
|
||||
UiFontFamily.NotoSans -> coreResources.notoSans
|
||||
UiFontFamily.CharisSIL -> coreResources.charisSil
|
||||
UiFontFamily.Poppins -> coreResources.poppins
|
||||
else -> FontFamily.Default
|
||||
}
|
||||
return Typography(
|
||||
// h1
|
||||
displayLarge = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 57.sp,
|
||||
letterSpacing = (-.25).sp,
|
||||
lineHeight = 64.sp,
|
||||
),
|
||||
displayLarge =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 57.sp,
|
||||
letterSpacing = (-.25).sp,
|
||||
lineHeight = 64.sp,
|
||||
),
|
||||
// h2
|
||||
displayMedium = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 45.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 52.sp,
|
||||
),
|
||||
displayMedium =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 45.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 52.sp,
|
||||
),
|
||||
// h3
|
||||
displaySmall = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 36.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 44.sp,
|
||||
),
|
||||
headlineLarge = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 32.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 40.sp,
|
||||
),
|
||||
displaySmall =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 36.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 44.sp,
|
||||
),
|
||||
headlineLarge =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 32.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 40.sp,
|
||||
),
|
||||
// h4
|
||||
headlineMedium = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 28.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 36.sp,
|
||||
),
|
||||
headlineMedium =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 28.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 36.sp,
|
||||
),
|
||||
// h5
|
||||
headlineSmall = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 24.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 23.sp,
|
||||
),
|
||||
headlineSmall =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 24.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 23.sp,
|
||||
),
|
||||
// h6
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 28.sp,
|
||||
),
|
||||
titleLarge =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
letterSpacing = 0.sp,
|
||||
lineHeight = 28.sp,
|
||||
),
|
||||
// subtitle1
|
||||
titleMedium = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 16.sp,
|
||||
letterSpacing = (0.15).sp,
|
||||
lineHeight = 24.sp,
|
||||
),
|
||||
titleMedium =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 16.sp,
|
||||
letterSpacing = (0.15).sp,
|
||||
lineHeight = 24.sp,
|
||||
),
|
||||
// subtitle2
|
||||
titleSmall = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.1).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
titleSmall =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.1).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
// body1
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 24.sp,
|
||||
),
|
||||
bodyLarge =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 24.sp,
|
||||
),
|
||||
// body2
|
||||
bodyMedium = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.25).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
bodyMedium =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.25).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
// caption
|
||||
bodySmall = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 12.sp,
|
||||
letterSpacing = (0.4).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
bodySmall =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 12.sp,
|
||||
letterSpacing = (0.4).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
// button
|
||||
labelLarge = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.1).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
labelMedium = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 12.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
labelLarge =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = (0.1).sp,
|
||||
lineHeight = 20.sp,
|
||||
),
|
||||
labelMedium =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 12.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
// overline
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
labelSmall =
|
||||
TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
letterSpacing = (0.5).sp,
|
||||
lineHeight = 16.sp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -4,8 +4,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
internal val shapes = Shapes(
|
||||
small = RoundedCornerShape(4.dp),
|
||||
medium = RoundedCornerShape(4.dp),
|
||||
large = RoundedCornerShape(0.dp),
|
||||
)
|
||||
internal val shapes =
|
||||
Shapes(
|
||||
small = RoundedCornerShape(4.dp),
|
||||
medium = RoundedCornerShape(4.dp),
|
||||
large = RoundedCornerShape(0.dp),
|
||||
)
|
||||
|
@ -11,19 +11,19 @@ import org.koin.dsl.module
|
||||
|
||||
actual fun getThemeRepository(): ThemeRepository = CoreAppearanceHelper.repository
|
||||
|
||||
actual val nativeAppearanceModule = module {
|
||||
single<ColorSchemeProvider> {
|
||||
DefaultColorSchemeProvider()
|
||||
actual val nativeAppearanceModule =
|
||||
module {
|
||||
single<ColorSchemeProvider> {
|
||||
DefaultColorSchemeProvider()
|
||||
}
|
||||
single<BarColorProvider> {
|
||||
DefaultBarColorProvider()
|
||||
}
|
||||
}
|
||||
single<BarColorProvider> {
|
||||
DefaultBarColorProvider()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun getColorSchemeProvider(): ColorSchemeProvider = CoreAppearanceHelper.colorSchemeProvider
|
||||
|
||||
actual fun getBarColorProvider(): BarColorProvider =
|
||||
CoreAppearanceHelper.barColorProvider
|
||||
actual fun getBarColorProvider(): BarColorProvider = CoreAppearanceHelper.barColorProvider
|
||||
|
||||
object CoreAppearanceHelper : KoinComponent {
|
||||
internal val repository: ThemeRepository by inject()
|
||||
|
@ -11,12 +11,16 @@ import platform.UIKit.setStatusBarStyle
|
||||
|
||||
class DefaultBarColorProvider : BarColorProvider {
|
||||
@Composable
|
||||
override fun setBarColorAccordingToTheme(theme: UiTheme, transparent: UiBarTheme) {
|
||||
override fun setBarColorAccordingToTheme(
|
||||
theme: UiTheme,
|
||||
transparent: UiBarTheme,
|
||||
) {
|
||||
LaunchedEffect(theme) {
|
||||
val style = when {
|
||||
theme == UiTheme.Light -> UIStatusBarStyleLightContent
|
||||
else -> UIStatusBarStyleDarkContent
|
||||
}
|
||||
val style =
|
||||
when {
|
||||
theme == UiTheme.Light -> UIStatusBarStyleLightContent
|
||||
else -> UIStatusBarStyleDarkContent
|
||||
}
|
||||
UIApplication.sharedApplication().setStatusBarStyle(style)
|
||||
}
|
||||
}
|
||||
|
@ -6,36 +6,36 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
import com.materialkolor.dynamicColorScheme
|
||||
|
||||
internal class DefaultColorSchemeProvider : ColorSchemeProvider {
|
||||
|
||||
override val supportsDynamicColors = false
|
||||
|
||||
override fun getColorScheme(
|
||||
theme: UiTheme,
|
||||
dynamic: Boolean,
|
||||
customSeed: Color?,
|
||||
): ColorScheme = when (theme) {
|
||||
UiTheme.Dark -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, true)
|
||||
} else {
|
||||
DarkColors
|
||||
): ColorScheme =
|
||||
when (theme) {
|
||||
UiTheme.Dark -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, true)
|
||||
} else {
|
||||
DarkColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UiTheme.Black -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, true).blackify()
|
||||
} else {
|
||||
BlackColors
|
||||
UiTheme.Black -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, true).blackify()
|
||||
} else {
|
||||
BlackColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, false)
|
||||
} else {
|
||||
LightColors
|
||||
else -> {
|
||||
if (customSeed != null) {
|
||||
dynamicColorScheme(customSeed, false)
|
||||
} else {
|
||||
LightColors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.update
|
||||
abstract class DefaultMviModel<Intent, State, Effect>(
|
||||
initialState: State,
|
||||
) : MviModel<Intent, State, Effect> {
|
||||
|
||||
override val uiState = MutableStateFlow(initialState)
|
||||
override val effects = MutableSharedFlow<Effect>()
|
||||
|
||||
|
@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
* Model contract for Model-View-Intent architecture.
|
||||
*/
|
||||
interface MviModel<Intent, State, Effect> {
|
||||
|
||||
/**
|
||||
* Representation of the state holder's state for the view to consume.
|
||||
*/
|
||||
|
@ -39,23 +39,30 @@ actual fun CustomWebView(
|
||||
modifier = modifier,
|
||||
factory = { context ->
|
||||
WebView(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
|
||||
navigator.canGoBack = view.canGoBack()
|
||||
layoutParams =
|
||||
ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
webViewClient =
|
||||
object : WebViewClient() {
|
||||
override fun onPageStarted(
|
||||
view: WebView,
|
||||
url: String?,
|
||||
favicon: Bitmap?,
|
||||
) {
|
||||
navigator.canGoBack = view.canGoBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
settings.javaScriptEnabled = true
|
||||
|
||||
setOnScrollChangeListener { _, scrollX, scrollY, oldScrollX, oldScrollY ->
|
||||
scrollConnection?.onPreScroll(
|
||||
available = Offset(
|
||||
x = (oldScrollX - scrollX) / density,
|
||||
y = (oldScrollY - scrollY) / density,
|
||||
),
|
||||
available =
|
||||
Offset(
|
||||
x = (oldScrollX - scrollX) / density,
|
||||
y = (oldScrollY - scrollY) / density,
|
||||
),
|
||||
source = NestedScrollSource.Drag,
|
||||
)
|
||||
}
|
||||
|
@ -29,37 +29,40 @@ actual fun VideoPlayer(
|
||||
onPlaybackStarted: (() -> Unit)?,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val exoPlayer = remember {
|
||||
ExoPlayer.Builder(context)
|
||||
.build()
|
||||
.apply {
|
||||
val defaultDataSourceFactory = DefaultDataSource.Factory(context)
|
||||
val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(
|
||||
context,
|
||||
defaultDataSourceFactory,
|
||||
)
|
||||
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(MediaItem.fromUri(url))
|
||||
setMediaSource(source)
|
||||
val exoPlayer =
|
||||
remember {
|
||||
ExoPlayer.Builder(context)
|
||||
.build()
|
||||
.apply {
|
||||
val defaultDataSourceFactory = DefaultDataSource.Factory(context)
|
||||
val dataSourceFactory: DataSource.Factory =
|
||||
DefaultDataSource.Factory(
|
||||
context,
|
||||
defaultDataSourceFactory,
|
||||
)
|
||||
val source =
|
||||
ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(MediaItem.fromUri(url))
|
||||
setMediaSource(source)
|
||||
|
||||
addListener(
|
||||
object : Player.Listener {
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
if (playbackState == PlaybackState.STATE_PLAYING) {
|
||||
onPlaybackStarted?.invoke()
|
||||
addListener(
|
||||
object : Player.Listener {
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
if (playbackState == PlaybackState.STATE_PLAYING) {
|
||||
onPlaybackStarted?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
prepare()
|
||||
}.apply {
|
||||
playWhenReady = true
|
||||
videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT
|
||||
repeatMode = Player.REPEAT_MODE_ONE
|
||||
volume = 0f
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
prepare()
|
||||
}.apply {
|
||||
playWhenReady = true
|
||||
videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT
|
||||
repeatMode = Player.REPEAT_MODE_ONE
|
||||
volume = 0f
|
||||
}
|
||||
}
|
||||
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
|
@ -31,10 +31,11 @@ fun BottomSheetHeader(
|
||||
modifier = Modifier.padding(bottom = Spacing.xs),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.xs,
|
||||
bottom = Spacing.xs,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = Spacing.xs,
|
||||
bottom = Spacing.xs,
|
||||
),
|
||||
text = title,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
@ -44,19 +45,18 @@ fun BottomSheetHeader(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetHandle(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
private fun BottomSheetHandle(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.width(60.dp)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
bottom = Spacing.xxxs,
|
||||
)
|
||||
.height(3.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha),
|
||||
shape = RoundedCornerShape(1.5.dp),
|
||||
),
|
||||
modifier =
|
||||
modifier.width(60.dp)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
bottom = Spacing.xxxs,
|
||||
)
|
||||
.height(3.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha),
|
||||
shape = RoundedCornerShape(1.5.dp),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -18,18 +18,20 @@ fun CustomizedContent(
|
||||
val themeRepository = remember { getThemeRepository() }
|
||||
val fontScale by themeRepository.contentFontScale.collectAsState()
|
||||
val uiFontScale by themeRepository.uiFontScale.collectAsState()
|
||||
val scaleFactor = when (contentClass) {
|
||||
ContentFontClass.Title -> fontScale.title
|
||||
ContentFontClass.Body -> fontScale.body
|
||||
ContentFontClass.Comment -> fontScale.comment
|
||||
ContentFontClass.AncillaryText -> fontScale.ancillary
|
||||
} * uiFontScale
|
||||
val scaleFactor =
|
||||
when (contentClass) {
|
||||
ContentFontClass.Title -> fontScale.title
|
||||
ContentFontClass.Body -> fontScale.body
|
||||
ContentFontClass.Comment -> fontScale.comment
|
||||
ContentFontClass.AncillaryText -> fontScale.ancillary
|
||||
} * uiFontScale
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides Density(
|
||||
density = LocalDensity.current.density,
|
||||
fontScale = scaleFactor,
|
||||
),
|
||||
LocalDensity provides
|
||||
Density(
|
||||
density = LocalDensity.current.density,
|
||||
fontScale = scaleFactor,
|
||||
),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import kotlin.math.roundToInt
|
||||
|
||||
private sealed interface SlideAnchorPosition {
|
||||
data object Opened : SlideAnchorPosition
|
||||
|
||||
data object Closed : SlideAnchorPosition
|
||||
}
|
||||
|
||||
@ -46,25 +47,28 @@ fun DraggableSideMenu(
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val maxWidth = if (availableWidth.isSpecified) availableWidth * 0.85f else 500.dp
|
||||
val draggableState = remember(availableWidth) {
|
||||
AnchoredDraggableState(
|
||||
initialValue = SlideAnchorPosition.Closed,
|
||||
anchors = DraggableAnchors<SlideAnchorPosition> {
|
||||
SlideAnchorPosition.Closed at with(density) { availableWidth.toPx() }
|
||||
SlideAnchorPosition.Opened at with(density) { (availableWidth - maxWidth).toPx() }
|
||||
},
|
||||
positionalThreshold = { distance: Float -> distance * 0.5f },
|
||||
velocityThreshold = { with(density) { 100.dp.toPx() } },
|
||||
animationSpec = tween(),
|
||||
)
|
||||
}
|
||||
val draggableState =
|
||||
remember(availableWidth) {
|
||||
AnchoredDraggableState(
|
||||
initialValue = SlideAnchorPosition.Closed,
|
||||
anchors =
|
||||
DraggableAnchors<SlideAnchorPosition> {
|
||||
SlideAnchorPosition.Closed at with(density) { availableWidth.toPx() }
|
||||
SlideAnchorPosition.Opened at with(density) { (availableWidth - maxWidth).toPx() }
|
||||
},
|
||||
positionalThreshold = { distance: Float -> distance * 0.5f },
|
||||
velocityThreshold = { with(density) { 100.dp.toPx() } },
|
||||
animationSpec = tween(),
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(opened) {
|
||||
val target = if (opened) {
|
||||
SlideAnchorPosition.Opened
|
||||
} else {
|
||||
SlideAnchorPosition.Closed
|
||||
}
|
||||
val target =
|
||||
if (opened) {
|
||||
SlideAnchorPosition.Opened
|
||||
} else {
|
||||
SlideAnchorPosition.Closed
|
||||
}
|
||||
draggableState.animateTo(target)
|
||||
}
|
||||
|
||||
@ -77,26 +81,27 @@ fun DraggableSideMenu(
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.width(maxWidth)
|
||||
.fillMaxHeight()
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = draggableState.requireOffset().roundToInt(),
|
||||
y = 0,
|
||||
modifier =
|
||||
modifier
|
||||
.width(maxWidth)
|
||||
.fillMaxHeight()
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = draggableState.requireOffset().roundToInt(),
|
||||
y = 0,
|
||||
)
|
||||
}
|
||||
.anchoredDraggable(
|
||||
state = draggableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
)
|
||||
}
|
||||
.anchoredDraggable(
|
||||
state = draggableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
.padding(
|
||||
top = Spacing.xxl,
|
||||
bottom = Spacing.m,
|
||||
end = Spacing.s,
|
||||
start = Spacing.s,
|
||||
),
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
.padding(
|
||||
top = Spacing.xxl,
|
||||
bottom = Spacing.m,
|
||||
end = Spacing.s,
|
||||
start = Spacing.s,
|
||||
),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
@ -32,24 +32,26 @@ fun FeedbackButton(
|
||||
animationSpec = spring(stiffness = StiffnessMediumLow),
|
||||
)
|
||||
Image(
|
||||
modifier = modifier
|
||||
.scale(scale)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onPress = {
|
||||
zoomed = true
|
||||
tryAwaitRelease()
|
||||
zoomed = false
|
||||
},
|
||||
onTap = {
|
||||
onClick()
|
||||
},
|
||||
)
|
||||
},
|
||||
modifier =
|
||||
modifier
|
||||
.scale(scale)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onPress = {
|
||||
zoomed = true
|
||||
tryAwaitRelease()
|
||||
zoomed = false
|
||||
},
|
||||
onTap = {
|
||||
onClick()
|
||||
},
|
||||
)
|
||||
},
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(
|
||||
color = tintColor,
|
||||
),
|
||||
colorFilter =
|
||||
ColorFilter.tint(
|
||||
color = tintColor,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -63,33 +63,37 @@ fun FloatingActionButtonMenu(
|
||||
val dynamicColors by themeRepository.dynamicColors.collectAsState()
|
||||
var fabExpanded by remember { mutableStateOf(false) }
|
||||
val fabRotation by animateFloatAsState(if (fabExpanded) 45f else 0f)
|
||||
val enterTransition = remember {
|
||||
fadeIn(
|
||||
initialAlpha = 0.3f,
|
||||
animationSpec = tween(ANIMATION_DURATION, easing = FastOutSlowInEasing),
|
||||
)
|
||||
}
|
||||
val exitTransition = remember {
|
||||
fadeOut(
|
||||
animationSpec = tween(ANIMATION_DURATION, easing = FastOutSlowInEasing),
|
||||
)
|
||||
}
|
||||
val enterTransition =
|
||||
remember {
|
||||
fadeIn(
|
||||
initialAlpha = 0.3f,
|
||||
animationSpec = tween(ANIMATION_DURATION, easing = FastOutSlowInEasing),
|
||||
)
|
||||
}
|
||||
val exitTransition =
|
||||
remember {
|
||||
fadeOut(
|
||||
animationSpec = tween(ANIMATION_DURATION, easing = FastOutSlowInEasing),
|
||||
)
|
||||
}
|
||||
val numberOfItems by animateIntAsState(
|
||||
targetValue = if (fabExpanded) items.size else 0,
|
||||
animationSpec = tween(
|
||||
durationMillis = ANIMATION_DURATION * items.size,
|
||||
easing = LinearEasing,
|
||||
),
|
||||
animationSpec =
|
||||
tween(
|
||||
durationMillis = ANIMATION_DURATION * items.size,
|
||||
easing = LinearEasing,
|
||||
),
|
||||
)
|
||||
val indices: List<Int> = if (numberOfItems == 0) {
|
||||
emptyList()
|
||||
} else {
|
||||
buildList {
|
||||
for (i in 0 until numberOfItems) {
|
||||
add(items.size - i - 1)
|
||||
val indices: List<Int> =
|
||||
if (numberOfItems == 0) {
|
||||
emptyList()
|
||||
} else {
|
||||
buildList {
|
||||
for (i in 0 until numberOfItems) {
|
||||
add(items.size - i - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
@ -107,36 +111,39 @@ fun FloatingActionButtonMenu(
|
||||
exit = exitTransition,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
fabExpanded = false
|
||||
item.onSelected?.invoke()
|
||||
},
|
||||
).padding(end = 15.dp),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = {
|
||||
fabExpanded = false
|
||||
item.onSelected?.invoke()
|
||||
},
|
||||
).padding(end = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
shape = RoundedCornerShape(CornerSize.s),
|
||||
).padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
shape = RoundedCornerShape(CornerSize.s),
|
||||
).padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
text = item.text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(IconSize.m)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = CircleShape,
|
||||
).padding(6.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(IconSize.m)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = CircleShape,
|
||||
).padding(6.dp),
|
||||
imageVector = item.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
@ -147,15 +154,17 @@ fun FloatingActionButtonMenu(
|
||||
Spacer(modifier = Modifier.height(Spacing.xxs))
|
||||
}
|
||||
|
||||
val fabContainerColor = when (theme) {
|
||||
UiTheme.Black -> schemeProvider.getColorScheme(
|
||||
theme = UiTheme.Dark,
|
||||
dynamic = dynamicColors,
|
||||
customSeed = seedColor,
|
||||
).primaryContainer
|
||||
val fabContainerColor =
|
||||
when (theme) {
|
||||
UiTheme.Black ->
|
||||
schemeProvider.getColorScheme(
|
||||
theme = UiTheme.Dark,
|
||||
dynamic = dynamicColors,
|
||||
customSeed = seedColor,
|
||||
).primaryContainer
|
||||
|
||||
else -> MaterialTheme.colorScheme.primaryContainer
|
||||
}
|
||||
else -> MaterialTheme.colorScheme.primaryContainer
|
||||
}
|
||||
FloatingActionButton(
|
||||
containerColor = fabContainerColor,
|
||||
shape = CircleShape,
|
||||
|
@ -23,20 +23,22 @@ fun PlaceholderImage(
|
||||
title: String,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(size)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(size / 2),
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(size)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(size / 2),
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val translationAmount = with(LocalDensity.current) { 2.dp.toPx() }
|
||||
Text(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
translationY = -translationAmount
|
||||
},
|
||||
modifier =
|
||||
Modifier.graphicsLayer {
|
||||
translationY = -translationAmount
|
||||
},
|
||||
text = title.firstOrNull()?.toString().orEmpty().uppercase(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
|
@ -16,9 +16,10 @@ fun ProgressHud(
|
||||
color: Color = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(overlayColor),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(overlayColor),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
|
@ -23,34 +23,36 @@ fun SectionSelector(
|
||||
onSectionSelected: (Int) -> Unit,
|
||||
) {
|
||||
var isTowardsStart by remember { mutableStateOf(false) }
|
||||
val draggableState = remember {
|
||||
DraggableState { delta ->
|
||||
isTowardsStart = delta > 0
|
||||
val draggableState =
|
||||
remember {
|
||||
DraggableState { delta ->
|
||||
isTowardsStart = delta > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
TabRow(
|
||||
modifier = modifier,
|
||||
selectedTabIndex = currentSection,
|
||||
tabs = {
|
||||
titles.forEachIndexed { i, title ->
|
||||
Tab(
|
||||
modifier = Modifier.then(
|
||||
if (draggable) {
|
||||
Modifier.draggable(
|
||||
state = draggableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
onDragStopped = {
|
||||
if (isTowardsStart) {
|
||||
onSectionSelected((currentSection - 1).coerceAtLeast(0))
|
||||
} else {
|
||||
onSectionSelected((currentSection + 1).coerceAtMost(titles.lastIndex))
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.then(
|
||||
if (draggable) {
|
||||
Modifier.draggable(
|
||||
state = draggableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
onDragStopped = {
|
||||
if (isTowardsStart) {
|
||||
onSectionSelected((currentSection - 1).coerceAtLeast(0))
|
||||
} else {
|
||||
onSectionSelected((currentSection + 1).coerceAtMost(titles.lastIndex))
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
selected = i == currentSection,
|
||||
text = {
|
||||
Text(
|
||||
|
@ -52,42 +52,45 @@ fun SwipeActionCard(
|
||||
var secondNotified by remember { mutableStateOf(false) }
|
||||
val gestureBeginCallback by rememberUpdatedState(onGestureBegin)
|
||||
var lastProgress by remember { mutableStateOf(0.0f) }
|
||||
val dismissState = rememberNoFlingSwipeToDismissBoxState(
|
||||
confirmValueChange = rememberCallbackArgs { value ->
|
||||
when (value) {
|
||||
SwipeToDismissBoxValue.StartToEnd -> {
|
||||
val enableSecondAction = swipeToEndActions.size > 1
|
||||
if (lastProgress >= SECOND_ACTION_THRESHOLD && enableSecondAction && secondNotified) {
|
||||
swipeToEndActions.getOrNull(1)?.onTriggered?.invoke()
|
||||
} else {
|
||||
swipeToEndActions.firstOrNull()?.onTriggered?.invoke()
|
||||
}
|
||||
}
|
||||
val dismissState =
|
||||
rememberNoFlingSwipeToDismissBoxState(
|
||||
confirmValueChange =
|
||||
rememberCallbackArgs { value ->
|
||||
when (value) {
|
||||
SwipeToDismissBoxValue.StartToEnd -> {
|
||||
val enableSecondAction = swipeToEndActions.size > 1
|
||||
if (lastProgress >= SECOND_ACTION_THRESHOLD && enableSecondAction && secondNotified) {
|
||||
swipeToEndActions.getOrNull(1)?.onTriggered?.invoke()
|
||||
} else {
|
||||
swipeToEndActions.firstOrNull()?.onTriggered?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
SwipeToDismissBoxValue.EndToStart -> {
|
||||
val enableSecondAction = swipeToStartActions.size > 1
|
||||
if (lastProgress >= SECOND_ACTION_THRESHOLD && enableSecondAction && secondNotified) {
|
||||
swipeToStartActions.getOrNull(1)?.onTriggered?.invoke()
|
||||
} else {
|
||||
swipeToStartActions.firstOrNull()?.onTriggered?.invoke()
|
||||
}
|
||||
}
|
||||
SwipeToDismissBoxValue.EndToStart -> {
|
||||
val enableSecondAction = swipeToStartActions.size > 1
|
||||
if (lastProgress >= SECOND_ACTION_THRESHOLD && enableSecondAction && secondNotified) {
|
||||
swipeToStartActions.getOrNull(1)?.onTriggered?.invoke()
|
||||
} else {
|
||||
swipeToStartActions.firstOrNull()?.onTriggered?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
notified = false
|
||||
secondNotified = false
|
||||
// return false to stay dismissed
|
||||
false
|
||||
},
|
||||
)
|
||||
else -> Unit
|
||||
}
|
||||
notified = false
|
||||
secondNotified = false
|
||||
// return false to stay dismissed
|
||||
false
|
||||
},
|
||||
)
|
||||
LaunchedEffect(dismissState, swipeToEndActions, swipeToEndActions) {
|
||||
snapshotFlow { dismissState.progress }.onEach { progress ->
|
||||
val enableSecondAction = when (dismissState.targetValue) {
|
||||
SwipeToDismissBoxValue.Settled -> false
|
||||
SwipeToDismissBoxValue.StartToEnd -> swipeToEndActions.size > 1
|
||||
SwipeToDismissBoxValue.EndToStart -> swipeToStartActions.size > 1
|
||||
}
|
||||
val enableSecondAction =
|
||||
when (dismissState.targetValue) {
|
||||
SwipeToDismissBoxValue.Settled -> false
|
||||
SwipeToDismissBoxValue.StartToEnd -> swipeToEndActions.size > 1
|
||||
SwipeToDismissBoxValue.EndToStart -> swipeToStartActions.size > 1
|
||||
}
|
||||
|
||||
if (!enableSecondAction) {
|
||||
when {
|
||||
@ -120,27 +123,30 @@ fun SwipeActionCard(
|
||||
enableDismissFromEndToStart = swipeToStartActions.isNotEmpty(),
|
||||
backgroundContent = {
|
||||
val direction = dismissState.dismissDirection
|
||||
val actions = when (dismissState.targetValue) {
|
||||
SwipeToDismissBoxValue.Settled -> listOf()
|
||||
SwipeToDismissBoxValue.StartToEnd -> swipeToEndActions
|
||||
SwipeToDismissBoxValue.EndToStart -> swipeToStartActions
|
||||
}
|
||||
val actions =
|
||||
when (dismissState.targetValue) {
|
||||
SwipeToDismissBoxValue.Settled -> listOf()
|
||||
SwipeToDismissBoxValue.StartToEnd -> swipeToEndActions
|
||||
SwipeToDismissBoxValue.EndToStart -> swipeToStartActions
|
||||
}
|
||||
val enableSecondAction = actions.size > 1
|
||||
val bgColor by animateColorAsState(
|
||||
targetValue = if (
|
||||
dismissState.progress < SECOND_ACTION_THRESHOLD ||
|
||||
dismissState.targetValue == SwipeToDismissBoxValue.Settled ||
|
||||
!enableSecondAction
|
||||
) {
|
||||
actions.firstOrNull()?.backgroundColor ?: Color.Transparent
|
||||
} else {
|
||||
actions.getOrNull(1)?.backgroundColor ?: Color.Transparent
|
||||
},
|
||||
targetValue =
|
||||
if (
|
||||
dismissState.progress < SECOND_ACTION_THRESHOLD ||
|
||||
dismissState.targetValue == SwipeToDismissBoxValue.Settled ||
|
||||
!enableSecondAction
|
||||
) {
|
||||
actions.firstOrNull()?.backgroundColor ?: Color.Transparent
|
||||
} else {
|
||||
actions.getOrNull(1)?.backgroundColor ?: Color.Transparent
|
||||
},
|
||||
)
|
||||
val alignment = when (direction) {
|
||||
SwipeToDismissBoxValue.StartToEnd -> Alignment.CenterStart
|
||||
else -> Alignment.CenterEnd
|
||||
}
|
||||
val alignment =
|
||||
when (direction) {
|
||||
SwipeToDismissBoxValue.StartToEnd -> Alignment.CenterStart
|
||||
else -> Alignment.CenterEnd
|
||||
}
|
||||
Box(
|
||||
Modifier.fillMaxSize()
|
||||
.background(bgColor)
|
||||
@ -180,11 +186,12 @@ private fun rememberNoFlingSwipeToDismissBoxState(
|
||||
// instead of LocalDensity.current we use a value that makes velocityThreshold to skyrocket
|
||||
val density = Density(Float.POSITIVE_INFINITY)
|
||||
return rememberSaveable(
|
||||
saver = SwipeToDismissBoxState.Saver(
|
||||
confirmValueChange = confirmValueChange,
|
||||
density = density,
|
||||
positionalThreshold = positionalThreshold,
|
||||
),
|
||||
saver =
|
||||
SwipeToDismissBoxState.Saver(
|
||||
confirmValueChange = confirmValueChange,
|
||||
density = density,
|
||||
positionalThreshold = positionalThreshold,
|
||||
),
|
||||
) {
|
||||
SwipeToDismissBoxState(
|
||||
initialValue = initialValue,
|
||||
|
@ -58,51 +58,54 @@ fun ZoomableImage(
|
||||
}
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AnimatedVisibility(visible = visible) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.onClick(
|
||||
onDoubleClick = {
|
||||
if (scale > 1f) {
|
||||
scale = 1f
|
||||
offset = Offset.Zero
|
||||
} else {
|
||||
scale *= 2.5f
|
||||
}
|
||||
},
|
||||
)
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures(
|
||||
onGesture = { _, pan, gestureZoom, _ ->
|
||||
val extraWidth = (scale - 1) * constraints.maxWidth
|
||||
val extraHeight = (scale - 1) * constraints.maxHeight
|
||||
val maxX = extraWidth / 2
|
||||
val maxY = extraHeight / 2
|
||||
|
||||
scale = (scale * gestureZoom).coerceIn(1f, 16f)
|
||||
|
||||
offset = if (scale > 1) {
|
||||
Offset(
|
||||
x = (offset.x + pan.x * scale).coerceIn(-maxX, maxX),
|
||||
y = (offset.y + pan.y * scale).coerceIn(-maxY, maxY),
|
||||
)
|
||||
modifier =
|
||||
Modifier
|
||||
.onClick(
|
||||
onDoubleClick = {
|
||||
if (scale > 1f) {
|
||||
scale = 1f
|
||||
offset = Offset.Zero
|
||||
} else {
|
||||
Offset.Zero
|
||||
scale *= 2.5f
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = offset.x,
|
||||
translationY = offset.y,
|
||||
),
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures(
|
||||
onGesture = { _, pan, gestureZoom, _ ->
|
||||
val extraWidth = (scale - 1) * constraints.maxWidth
|
||||
val extraHeight = (scale - 1) * constraints.maxHeight
|
||||
val maxX = extraWidth / 2
|
||||
val maxY = extraHeight / 2
|
||||
|
||||
scale = (scale * gestureZoom).coerceIn(1f, 16f)
|
||||
|
||||
offset =
|
||||
if (scale > 1) {
|
||||
Offset(
|
||||
x = (offset.x + pan.x * scale).coerceIn(-maxX, maxX),
|
||||
y = (offset.y + pan.y * scale).coerceIn(-maxY, maxY),
|
||||
)
|
||||
} else {
|
||||
Offset.Zero
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = offset.x,
|
||||
translationY = offset.y,
|
||||
),
|
||||
url = url,
|
||||
contentScale = contentScale,
|
||||
quality = FilterQuality.High,
|
||||
@ -116,19 +119,21 @@ fun ZoomableImage(
|
||||
)
|
||||
},
|
||||
onLoading = { progress ->
|
||||
val prog = if (progress != null) {
|
||||
progress
|
||||
} else {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val res by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec = InfiniteRepeatableSpec(
|
||||
animation = tween(LOADING_ANIMATION_DURATION),
|
||||
),
|
||||
)
|
||||
res
|
||||
}
|
||||
val prog =
|
||||
if (progress != null) {
|
||||
progress
|
||||
} else {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val res by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec =
|
||||
InfiniteRepeatableSpec(
|
||||
animation = tween(LOADING_ANIMATION_DURATION),
|
||||
),
|
||||
)
|
||||
res
|
||||
}
|
||||
CircularProgressIndicator(
|
||||
progress = { prog },
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
|
@ -42,10 +42,11 @@ actual fun CustomImage(
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (shouldBeRendered) {
|
||||
val painterResource = asyncPainterResource(
|
||||
data = url,
|
||||
filterQuality = quality,
|
||||
)
|
||||
val painterResource =
|
||||
asyncPainterResource(
|
||||
data = url,
|
||||
filterQuality = quality,
|
||||
)
|
||||
KamelImage(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
resource = painterResource,
|
||||
|
@ -50,38 +50,41 @@ actual fun CustomWebView(
|
||||
|
||||
UIKitView(
|
||||
factory = {
|
||||
val config = WKWebViewConfiguration().apply {
|
||||
allowsInlineMediaPlayback = true
|
||||
}
|
||||
val config =
|
||||
WKWebViewConfiguration().apply {
|
||||
allowsInlineMediaPlayback = true
|
||||
}
|
||||
WKWebView(
|
||||
frame = CGRectZero.readValue(),
|
||||
configuration = config,
|
||||
).apply {
|
||||
userInteractionEnabled = true
|
||||
allowsBackForwardNavigationGestures = true
|
||||
val navigationDelegate = object : NSObject(), WKNavigationDelegateProtocol {
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didFinishNavigation: WKNavigation?,
|
||||
) {
|
||||
navigator.canGoBack = webView.canGoBack
|
||||
}
|
||||
}
|
||||
this.navigationDelegate = navigationDelegate
|
||||
this.scrollView.delegate = object : NSObject(), UIScrollViewDelegateProtocol {
|
||||
override fun scrollViewDidScroll(scrollView: UIScrollView) {
|
||||
scrollView.contentOffset.useContents {
|
||||
val offsetX = (lastOffsetX - x).toFloat() / density
|
||||
val offsetY = (lastOffsetY - y).toFloat() / density
|
||||
scrollConnection?.onPreScroll(
|
||||
available = Offset(offsetX, offsetY),
|
||||
source = NestedScrollSource.Drag,
|
||||
)
|
||||
lastOffsetX = x.toFloat()
|
||||
lastOffsetY = y.toFloat()
|
||||
val navigationDelegate =
|
||||
object : NSObject(), WKNavigationDelegateProtocol {
|
||||
override fun webView(
|
||||
webView: WKWebView,
|
||||
didFinishNavigation: WKNavigation?,
|
||||
) {
|
||||
navigator.canGoBack = webView.canGoBack
|
||||
}
|
||||
}
|
||||
this.navigationDelegate = navigationDelegate
|
||||
this.scrollView.delegate =
|
||||
object : NSObject(), UIScrollViewDelegateProtocol {
|
||||
override fun scrollViewDidScroll(scrollView: UIScrollView) {
|
||||
scrollView.contentOffset.useContents {
|
||||
val offsetX = (lastOffsetX - x).toFloat() / density
|
||||
val offsetY = (lastOffsetY - y).toFloat() / density
|
||||
scrollConnection?.onPreScroll(
|
||||
available = Offset(offsetX, offsetY),
|
||||
source = NestedScrollSource.Drag,
|
||||
)
|
||||
lastOffsetX = x.toFloat()
|
||||
lastOffsetY = y.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
webView = it
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ class DefaultDetailOpener(
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val communityRepository: CommunityRepository,
|
||||
) : DetailOpener {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
|
||||
override fun openCommunityDetail(
|
||||
@ -39,22 +38,21 @@ class DefaultDetailOpener(
|
||||
otherInstance: String,
|
||||
) {
|
||||
scope.launch {
|
||||
val (actualCommunity, actualInstance) = withContext(Dispatchers.IO) {
|
||||
val defaultResult = community to otherInstance
|
||||
if (otherInstance.isNotEmpty()) {
|
||||
val found = searchCommunity(name = community.name, host = otherInstance)
|
||||
if (found != null) {
|
||||
found to ""
|
||||
val (actualCommunity, actualInstance) =
|
||||
run {
|
||||
val defaultResult = community to otherInstance
|
||||
if (otherInstance.isNotEmpty()) {
|
||||
val found = searchCommunity(name = community.name, host = otherInstance)
|
||||
if (found != null) {
|
||||
found to ""
|
||||
} else {
|
||||
defaultResult
|
||||
}
|
||||
} else {
|
||||
defaultResult
|
||||
}
|
||||
} else {
|
||||
defaultResult
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
itemCache.putCommunity(actualCommunity)
|
||||
}
|
||||
itemCache.putCommunity(actualCommunity)
|
||||
navigationCoordinator.pushScreen(
|
||||
CommunityDetailScreen(
|
||||
communityId = actualCommunity.id,
|
||||
@ -64,7 +62,10 @@ class DefaultDetailOpener(
|
||||
}
|
||||
}
|
||||
|
||||
override fun openUserDetail(user: UserModel, otherInstance: String) {
|
||||
override fun openUserDetail(
|
||||
user: UserModel,
|
||||
otherInstance: String,
|
||||
) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
itemCache.putUser(user)
|
||||
@ -118,13 +119,14 @@ class DefaultDetailOpener(
|
||||
itemCache.putComment(editedComment)
|
||||
}
|
||||
}
|
||||
val screen = CreateCommentScreen(
|
||||
draftId = draftId,
|
||||
originalPostId = originalPost?.id,
|
||||
originalCommentId = originalComment?.id,
|
||||
editedCommentId = editedComment?.id,
|
||||
initialText = initialText,
|
||||
)
|
||||
val screen =
|
||||
CreateCommentScreen(
|
||||
draftId = draftId,
|
||||
originalPostId = originalPost?.id,
|
||||
originalCommentId = originalComment?.id,
|
||||
editedCommentId = editedComment?.id,
|
||||
initialText = initialText,
|
||||
)
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
}
|
||||
@ -149,36 +151,42 @@ class DefaultDetailOpener(
|
||||
itemCache.putPost(crossPost)
|
||||
}
|
||||
}
|
||||
val screen = CreatePostScreen(
|
||||
draftId = draftId,
|
||||
editedPostId = editedPost?.id,
|
||||
crossPostId = crossPost?.id,
|
||||
communityId = communityId,
|
||||
initialText = initialText,
|
||||
initialTitle = initialTitle,
|
||||
initialUrl = initialUrl,
|
||||
initialNsfw = initialNsfw,
|
||||
forceCommunitySelection = forceCommunitySelection,
|
||||
)
|
||||
val screen =
|
||||
CreatePostScreen(
|
||||
draftId = draftId,
|
||||
editedPostId = editedPost?.id,
|
||||
crossPostId = crossPost?.id,
|
||||
communityId = communityId,
|
||||
initialText = initialText,
|
||||
initialTitle = initialTitle,
|
||||
initialUrl = initialUrl,
|
||||
initialNsfw = initialNsfw,
|
||||
forceCommunitySelection = forceCommunitySelection,
|
||||
)
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun searchCommunity(name: String, host: String): CommunityModel? {
|
||||
private suspend fun searchCommunity(
|
||||
name: String,
|
||||
host: String,
|
||||
): CommunityModel? {
|
||||
val auth = identityRepository.authToken.value
|
||||
|
||||
tailrec suspend fun searchRec(page: Int = 0): CommunityModel? {
|
||||
val results = communityRepository.search(
|
||||
auth = auth,
|
||||
query = name,
|
||||
resultType = SearchResultType.Communities,
|
||||
page = page,
|
||||
limit = 50,
|
||||
).filterIsInstance<SearchResult.Community>()
|
||||
val results =
|
||||
communityRepository.search(
|
||||
auth = auth,
|
||||
query = name,
|
||||
resultType = SearchResultType.Communities,
|
||||
page = page,
|
||||
limit = 50,
|
||||
).filterIsInstance<SearchResult.Community>()
|
||||
|
||||
val found = results.firstOrNull {
|
||||
it.model.name == name && it.model.host == host
|
||||
}?.model
|
||||
val found =
|
||||
results.firstOrNull {
|
||||
it.model.name == name && it.model.host == host
|
||||
}?.model
|
||||
// iterates for no more than a number of pages before giving up
|
||||
if (found != null || page >= MAX_PAGE_NUMBER_IN_COMMUNITY_REC_SEARCH) {
|
||||
return found
|
||||
|
@ -5,19 +5,23 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface BlockActionType {
|
||||
data object User : BlockActionType
|
||||
|
||||
data object Community : BlockActionType
|
||||
|
||||
data object Instance : BlockActionType
|
||||
}
|
||||
|
||||
fun Int.toBlockActionType(): BlockActionType = when (this) {
|
||||
2 -> BlockActionType.Instance
|
||||
1 -> BlockActionType.Community
|
||||
else -> BlockActionType.User
|
||||
}
|
||||
fun Int.toBlockActionType(): BlockActionType =
|
||||
when (this) {
|
||||
2 -> BlockActionType.Instance
|
||||
1 -> BlockActionType.Community
|
||||
else -> BlockActionType.User
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BlockActionType.toReadableName(): String = when (this) {
|
||||
BlockActionType.Community -> LocalXmlStrings.current.blockActionCommunity
|
||||
BlockActionType.Instance -> LocalXmlStrings.current.communityDetailBlockInstance
|
||||
BlockActionType.User -> LocalXmlStrings.current.blockActionUser
|
||||
}
|
||||
fun BlockActionType.toReadableName(): String =
|
||||
when (this) {
|
||||
BlockActionType.Community -> LocalXmlStrings.current.blockActionCommunity
|
||||
BlockActionType.Instance -> LocalXmlStrings.current.communityDetailBlockInstance
|
||||
BlockActionType.User -> LocalXmlStrings.current.blockActionUser
|
||||
}
|
||||
|
@ -53,30 +53,34 @@ fun CollapsedCommentCard(
|
||||
val commentBarTheme by themeRepository.commentBarTheme.collectAsState()
|
||||
var commentHeight by remember { mutableStateOf(0f) }
|
||||
val barWidth = 2.dp
|
||||
val barColor = themeRepository.getCommentBarColor(
|
||||
depth = comment.depth,
|
||||
commentBarTheme = commentBarTheme,
|
||||
)
|
||||
val barColor =
|
||||
themeRepository.getCommentBarColor(
|
||||
depth = comment.depth,
|
||||
commentBarTheme = commentBarTheme,
|
||||
)
|
||||
Column(
|
||||
modifier = modifier.onClick(
|
||||
onClick = onClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
modifier.onClick(
|
||||
onClick = onClick ?: {},
|
||||
),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.padding(
|
||||
start = (INDENT_AMOUNT * comment.depth).dp,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
start = (INDENT_AMOUNT * comment.depth).dp,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = barWidth)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
).onGloballyPositioned {
|
||||
commentHeight = it.size.toSize().height
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = barWidth)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
).onGloballyPositioned {
|
||||
commentHeight = it.size.toSize().height
|
||||
},
|
||||
) {
|
||||
CommunityAndCreatorInfo(
|
||||
iconSize = IconSize.s,
|
||||
@ -116,11 +120,12 @@ fun CollapsedCommentCard(
|
||||
}
|
||||
if (comment.depth > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = Spacing.xxs)
|
||||
.width(barWidth)
|
||||
.height(commentHeight.toLocalDp())
|
||||
.background(color = barColor),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = Spacing.xxs)
|
||||
.width(barWidth)
|
||||
.height(commentHeight.toLocalDp())
|
||||
.background(color = barColor),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -77,35 +77,39 @@ fun CommentCard(
|
||||
val themeRepository = remember { getThemeRepository() }
|
||||
var commentHeight by remember { mutableStateOf(0f) }
|
||||
val commentBarTheme by themeRepository.commentBarTheme.collectAsState()
|
||||
val barColor = themeRepository.getCommentBarColor(
|
||||
depth = comment.depth,
|
||||
commentBarTheme = commentBarTheme,
|
||||
)
|
||||
val barColor =
|
||||
themeRepository.getCommentBarColor(
|
||||
depth = comment.depth,
|
||||
commentBarTheme = commentBarTheme,
|
||||
)
|
||||
val barWidth = BAR_BASE_WIDTH_UNIT * barThickness
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = onClick ?: {},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
).padding(
|
||||
start = indentAmount.takeIf { it > 0 }?.let {
|
||||
(it * comment.depth).dp + Spacing.xxxs
|
||||
} ?: 0.dp,
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = onClick ?: {},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
).padding(
|
||||
start =
|
||||
indentAmount.takeIf { it > 0 }?.let {
|
||||
(it * comment.depth).dp + Spacing.xxxs
|
||||
} ?: 0.dp,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = barWidth + Spacing.xxs)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
).onGloballyPositioned {
|
||||
commentHeight = it.size.toSize().height
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = barWidth + Spacing.xxs)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
).onGloballyPositioned {
|
||||
commentHeight = it.size.toSize().height
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
CommunityAndCreatorInfo(
|
||||
@ -119,12 +123,14 @@ fun CommentCard(
|
||||
distinguished = comment.distinguished,
|
||||
isOp = isOp,
|
||||
isBot = comment.creator?.bot.takeIf { showBot } ?: false,
|
||||
onOpenCreator = rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
onOpenCommunity = rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onOpenCreator =
|
||||
rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
onOpenCommunity =
|
||||
rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onToggleExpanded = onToggleExpanded,
|
||||
)
|
||||
if (comment.removed) {
|
||||
@ -136,11 +142,12 @@ fun CommentCard(
|
||||
} else {
|
||||
CustomizedContent(ContentFontClass.Body) {
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides Density(
|
||||
density = LocalDensity.current.density,
|
||||
// additional downscale for font in comments
|
||||
fontScale = LocalDensity.current.fontScale * COMMENT_TEXT_SCALE_FACTOR,
|
||||
),
|
||||
LocalDensity provides
|
||||
Density(
|
||||
density = LocalDensity.current.density,
|
||||
// additional downscale for font in comments
|
||||
fontScale = LocalDensity.current.fontScale * COMMENT_TEXT_SCALE_FACTOR,
|
||||
),
|
||||
) {
|
||||
PostCardBody(
|
||||
text = comment.text,
|
||||
@ -180,11 +187,12 @@ fun CommentCard(
|
||||
}
|
||||
if (indentAmount > 0 && comment.depth > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = Spacing.xxs)
|
||||
.width(barWidth)
|
||||
.height(commentHeight.toLocalDp())
|
||||
.background(color = barColor, shape = RoundedCornerShape(barWidth / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = Spacing.xxs)
|
||||
.width(barWidth)
|
||||
.height(commentHeight.toLocalDp())
|
||||
.background(color = barColor, shape = RoundedCornerShape(barWidth / 2)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -37,42 +37,47 @@ fun CommentCardPlaceholder(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(IconSize.s)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.size(IconSize.s)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(80.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -81,32 +81,34 @@ fun CommunityAndCreatorInfo(
|
||||
if (communityIcon.isNotEmpty()) {
|
||||
if (autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
)
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
)
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = communityIcon,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
} else {
|
||||
PlaceholderImage(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
size = IconSize.l,
|
||||
title = communityName,
|
||||
)
|
||||
@ -114,32 +116,34 @@ fun CommunityAndCreatorInfo(
|
||||
} else if (creatorAvatar.isNotEmpty()) {
|
||||
if (autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
)
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
)
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = creatorAvatar,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
} else {
|
||||
PlaceholderImage(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
size = iconSize,
|
||||
title = creatorName,
|
||||
)
|
||||
@ -151,14 +155,15 @@ fun CommunityAndCreatorInfo(
|
||||
if (community != null) {
|
||||
CustomizedContent(ContentFontClass.AncillaryText) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenCommunity?.invoke(community)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenCommunity?.invoke(community)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
text = communityName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = if (creator == null) ancillaryColor else fullColor,
|
||||
@ -168,14 +173,15 @@ fun CommunityAndCreatorInfo(
|
||||
if (creator != null) {
|
||||
CustomizedContent(ContentFontClass.AncillaryText) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenCreator?.invoke(creator)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenCreator?.invoke(creator)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
text = creatorName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = ancillaryColor,
|
||||
@ -234,13 +240,14 @@ fun CommunityAndCreatorInfo(
|
||||
)
|
||||
}
|
||||
if (indicatorExpanded != null) {
|
||||
val expandedModifier = Modifier
|
||||
.padding(end = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onToggleExpanded?.invoke()
|
||||
},
|
||||
)
|
||||
val expandedModifier =
|
||||
Modifier
|
||||
.padding(end = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onToggleExpanded?.invoke()
|
||||
},
|
||||
)
|
||||
if (indicatorExpanded) {
|
||||
Icon(
|
||||
modifier = expandedModifier,
|
||||
|
@ -67,17 +67,20 @@ fun CommunityHeader(
|
||||
contentDescription = null,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.95f),
|
||||
Color.Transparent,
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.75f),
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush =
|
||||
Brush.horizontalGradient(
|
||||
colors =
|
||||
listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.95f),
|
||||
Color.Transparent,
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.75f),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -92,15 +95,16 @@ fun CommunityHeader(
|
||||
// avatar
|
||||
if (communityIcon.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(IconSize.xxl)
|
||||
.clip(RoundedCornerShape(IconSize.xxl / 2))
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenImage?.invoke(communityIcon)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(IconSize.xxl)
|
||||
.clip(RoundedCornerShape(IconSize.xxl / 2))
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenImage?.invoke(communityIcon)
|
||||
},
|
||||
),
|
||||
url = communityIcon,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
@ -144,10 +148,11 @@ fun CommunityHeader(
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = community.subscribers.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
text =
|
||||
community.subscribers.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -159,10 +164,11 @@ fun CommunityHeader(
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = community.monthlyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
text =
|
||||
community.monthlyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -172,10 +178,11 @@ fun CommunityHeader(
|
||||
|
||||
if (onInfo != null) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(iconSize)
|
||||
.onClick(onClick = onInfo),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(iconSize)
|
||||
.onClick(onClick = onInfo),
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
@ -69,22 +69,24 @@ fun CommunityItem(
|
||||
var optionsMenuOpen by remember { mutableStateOf(false) }
|
||||
|
||||
Row(
|
||||
modifier = modifier.then(
|
||||
if (noPadding) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.padding(Spacing.s)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
modifier.then(
|
||||
if (noPadding) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.padding(Spacing.s)
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
if (communityIcon.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = communityIcon,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
@ -98,19 +100,21 @@ fun CommunityItem(
|
||||
modifier = Modifier.weight(1f).padding(start = Spacing.xs),
|
||||
) {
|
||||
Text(
|
||||
text = buildString {
|
||||
append(title)
|
||||
},
|
||||
text =
|
||||
buildString {
|
||||
append(title)
|
||||
},
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = fullColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = buildString {
|
||||
append("!")
|
||||
append(communityHandle)
|
||||
},
|
||||
text =
|
||||
buildString {
|
||||
append("!")
|
||||
append(communityHandle)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = ancillaryColor,
|
||||
)
|
||||
@ -147,24 +151,26 @@ fun CommunityItem(
|
||||
|
||||
showSubscribeButton -> {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(IconSize.m)
|
||||
.onClick(
|
||||
onClick = { onSubscribe?.invoke() },
|
||||
),
|
||||
imageVector = when (community.subscribed) {
|
||||
true -> {
|
||||
Icons.Outlined.RemoveCircleOutline
|
||||
}
|
||||
modifier =
|
||||
Modifier
|
||||
.size(IconSize.m)
|
||||
.onClick(
|
||||
onClick = { onSubscribe?.invoke() },
|
||||
),
|
||||
imageVector =
|
||||
when (community.subscribed) {
|
||||
true -> {
|
||||
Icons.Outlined.RemoveCircleOutline
|
||||
}
|
||||
|
||||
false -> {
|
||||
Icons.Outlined.AddCircleOutline
|
||||
}
|
||||
false -> {
|
||||
Icons.Outlined.AddCircleOutline
|
||||
}
|
||||
|
||||
else -> {
|
||||
Icons.Outlined.Pending
|
||||
}
|
||||
},
|
||||
else -> {
|
||||
Icons.Outlined.Pending
|
||||
}
|
||||
},
|
||||
contentDescription = "",
|
||||
tint = ancillaryColor,
|
||||
)
|
||||
@ -174,16 +180,17 @@ fun CommunityItem(
|
||||
if (options.isNotEmpty()) {
|
||||
Box {
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -194,10 +201,11 @@ fun CommunityItem(
|
||||
onDismiss = {
|
||||
optionsMenuOpen = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
offset =
|
||||
DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
|
@ -23,37 +23,41 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.shimmerEffect
|
||||
@Composable
|
||||
fun CommunityItemPlaceholder() {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(start = Spacing.xs),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(40.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(20.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(20.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,5 +2,6 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui
|
||||
|
||||
sealed interface CreatePostSection {
|
||||
data object Edit : CreatePostSection
|
||||
|
||||
data object Preview : CreatePostSection
|
||||
}
|
||||
|
@ -31,18 +31,19 @@ fun DetailInfoItem(
|
||||
Icon(imageVector = icon, contentDescription = null)
|
||||
}
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
append(value)
|
||||
}
|
||||
append(" ")
|
||||
append(title)
|
||||
},
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
append(value)
|
||||
}
|
||||
append(" ")
|
||||
append(title)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,18 @@ internal class DefaultFabNestedScrollConnection : FabNestedScrollConnection {
|
||||
private val fabVisible = MutableStateFlow(true)
|
||||
private val scope = CoroutineScope(SupervisorJob())
|
||||
override val isFabVisible: StateFlow<Boolean>
|
||||
get() = fabVisible
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = true,
|
||||
)
|
||||
get() =
|
||||
fabVisible
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = true,
|
||||
)
|
||||
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||
override fun onPreScroll(
|
||||
available: Offset,
|
||||
source: NestedScrollSource,
|
||||
): Offset {
|
||||
if (available.y < -THRESHOLD) {
|
||||
fabVisible.value = false
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||
|
||||
sealed interface InboxCardType {
|
||||
data object Mention : InboxCardType
|
||||
|
||||
data object Reply : InboxCardType
|
||||
}
|
||||
|
||||
@ -54,27 +55,28 @@ fun InboxCard(
|
||||
onReply: (() -> Unit)? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(
|
||||
elevation = 5.dp,
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
)
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(vertical = Spacing.s)
|
||||
} else {
|
||||
Modifier.background(MaterialTheme.colorScheme.background)
|
||||
},
|
||||
).onClick(
|
||||
onClick = {
|
||||
onOpenPost(mention.post)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(
|
||||
elevation = 5.dp,
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
)
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(vertical = Spacing.s)
|
||||
} else {
|
||||
Modifier.background(MaterialTheme.colorScheme.background)
|
||||
},
|
||||
).onClick(
|
||||
onClick = {
|
||||
onOpenPost(mention.post)
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
@ -94,9 +96,10 @@ fun InboxCard(
|
||||
} else {
|
||||
CustomizedContent(ContentFontClass.Body) {
|
||||
PostCardBody(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
// takes just the first line
|
||||
text = mention.comment.text.substringBefore("\n"),
|
||||
autoLoadImages = autoLoadImages,
|
||||
@ -108,11 +111,12 @@ fun InboxCard(
|
||||
}
|
||||
}
|
||||
InboxReplySubtitle(
|
||||
modifier = Modifier.padding(
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
top = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
top = Spacing.s,
|
||||
),
|
||||
creator = mention.creator,
|
||||
community = mention.community,
|
||||
autoLoadImages = autoLoadImages,
|
||||
@ -127,9 +131,10 @@ fun InboxCard(
|
||||
downVoted = mention.myVote < 0,
|
||||
options = options,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenCreator = rememberCallbackArgs { user ->
|
||||
onOpenCreator(user)
|
||||
},
|
||||
onOpenCreator =
|
||||
rememberCallbackArgs { user ->
|
||||
onOpenCreator(user)
|
||||
},
|
||||
onUpVote = onUpVote,
|
||||
onDownVote = onDownVote,
|
||||
onOptionSelected = onOptionSelected,
|
||||
|
@ -22,47 +22,49 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.shimmerEffect
|
||||
|
||||
@Composable
|
||||
fun InboxCardPlaceholder(
|
||||
postLayout: PostLayout = PostLayout.Card,
|
||||
) {
|
||||
fun InboxCardPlaceholder(postLayout: PostLayout = PostLayout.Card) {
|
||||
Column(
|
||||
modifier = Modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(elevation = 5.dp, shape = RoundedCornerShape(CornerSize.l))
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(Spacing.s)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(elevation = 5.dp, shape = RoundedCornerShape(CornerSize.l))
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(Spacing.s)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = Spacing.xxxs)
|
||||
.height(50.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(vertical = Spacing.xxxs)
|
||||
.height(50.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -29,52 +29,55 @@ fun InboxCardHeader(
|
||||
) {
|
||||
val fullColor = MaterialTheme.colorScheme.onBackground
|
||||
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||
val header = buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold, color = fullColor)) {
|
||||
append(mention.creator.name)
|
||||
}
|
||||
append(" ")
|
||||
withStyle(SpanStyle(color = ancillaryColor)) {
|
||||
when (type) {
|
||||
InboxCardType.Mention -> {
|
||||
append(LocalXmlStrings.current.inboxItemMention)
|
||||
}
|
||||
val header =
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold, color = fullColor)) {
|
||||
append(mention.creator.name)
|
||||
}
|
||||
append(" ")
|
||||
withStyle(SpanStyle(color = ancillaryColor)) {
|
||||
when (type) {
|
||||
InboxCardType.Mention -> {
|
||||
append(LocalXmlStrings.current.inboxItemMention)
|
||||
}
|
||||
|
||||
InboxCardType.Reply -> {
|
||||
if (mention.isCommentReply) {
|
||||
append(LocalXmlStrings.current.inboxItemReplyComment)
|
||||
} else {
|
||||
append(LocalXmlStrings.current.inboxItemReplyPost)
|
||||
InboxCardType.Reply -> {
|
||||
if (mention.isCommentReply) {
|
||||
append(LocalXmlStrings.current.inboxItemReplyComment)
|
||||
} else {
|
||||
append(LocalXmlStrings.current.inboxItemReplyPost)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
append(" ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold, color = fullColor)) {
|
||||
append(mention.post.title)
|
||||
append(" ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold, color = fullColor)) {
|
||||
append(mention.post.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
text = header,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = fullColor,
|
||||
)
|
||||
if (!mention.read) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(IconSize.xs),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(IconSize.xs),
|
||||
imageVector = Icons.Filled.FiberManualRecord,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
|
@ -104,24 +104,26 @@ fun InboxReplySubtitle(
|
||||
) {
|
||||
if (creatorName.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
if (creatorAvatar.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = creatorAvatar,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
@ -141,25 +143,27 @@ fun InboxReplySubtitle(
|
||||
}
|
||||
if (communityName.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (community != null) {
|
||||
onOpenCommunity?.invoke(community)
|
||||
}
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (communityIcon.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = communityIcon,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
@ -185,15 +189,16 @@ fun InboxReplySubtitle(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Icon(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 3.5.dp,
|
||||
bottom = 3.5.dp,
|
||||
end = 3.5.dp,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onReply?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 3.5.dp,
|
||||
bottom = 3.5.dp,
|
||||
end = 3.5.dp,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onReply?.invoke()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.AutoMirrored.Default.Chat,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -216,16 +221,17 @@ fun InboxReplySubtitle(
|
||||
}
|
||||
if (options.isNotEmpty()) {
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsExpanded = true
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsExpanded = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreHoriz,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -233,49 +239,54 @@ fun InboxReplySubtitle(
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FeedbackButton(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
imageVector = Icons.Default.ArrowCircleUp,
|
||||
tintColor = if (upVoted) {
|
||||
upVoteColor ?: defaultUpvoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
tintColor =
|
||||
if (upVoted) {
|
||||
upVoteColor ?: defaultUpvoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
onClick = {
|
||||
onUpVote?.invoke()
|
||||
},
|
||||
)
|
||||
if (showScores) {
|
||||
Text(
|
||||
text = formatToReadableValue(
|
||||
voteFormat = voteFormat,
|
||||
score = score,
|
||||
upVotes = upVotes,
|
||||
downVotes = downVotes,
|
||||
upVoteColor = upVoteColor ?: defaultUpvoteColor,
|
||||
downVoteColor = downVoteColor ?: defaultDownVoteColor,
|
||||
upVoted = upVoted,
|
||||
downVoted = downVoted,
|
||||
),
|
||||
text =
|
||||
formatToReadableValue(
|
||||
voteFormat = voteFormat,
|
||||
score = score,
|
||||
upVotes = upVotes,
|
||||
downVotes = downVotes,
|
||||
upVoteColor = upVoteColor ?: defaultUpvoteColor,
|
||||
downVoteColor = downVoteColor ?: defaultDownVoteColor,
|
||||
upVoted = upVoted,
|
||||
downVoted = downVoted,
|
||||
),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = ancillaryColor,
|
||||
)
|
||||
}
|
||||
FeedbackButton(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
start = 2.5.dp,
|
||||
),
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
start = 2.5.dp,
|
||||
),
|
||||
imageVector = Icons.Default.ArrowCircleDown,
|
||||
tintColor = if (downVoted) {
|
||||
downVoteColor ?: defaultDownVoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
tintColor =
|
||||
if (downVoted) {
|
||||
downVoteColor ?: defaultDownVoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
onClick = {
|
||||
onDownVote?.invoke()
|
||||
},
|
||||
@ -286,10 +297,11 @@ fun InboxReplySubtitle(
|
||||
onDismiss = {
|
||||
optionsExpanded = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
offset =
|
||||
DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
|
@ -22,16 +22,17 @@ fun IndicatorChip(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.border(
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
width = Dp.Hairline,
|
||||
shape = RoundedCornerShape(CornerSize.m),
|
||||
)
|
||||
.padding(
|
||||
vertical = Spacing.xxxs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.border(
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
width = Dp.Hairline,
|
||||
shape = RoundedCornerShape(CornerSize.m),
|
||||
)
|
||||
.padding(
|
||||
vertical = Spacing.xxxs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
|
@ -10,33 +10,38 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
|
||||
sealed interface ModeratorZoneAction {
|
||||
data object GlobalModLog : ModeratorZoneAction
|
||||
|
||||
data object GlobalReports : ModeratorZoneAction
|
||||
|
||||
data object ModeratedContents : ModeratorZoneAction
|
||||
}
|
||||
|
||||
fun Int.toModeratorZoneAction(): ModeratorZoneAction = when (this) {
|
||||
2 -> ModeratorZoneAction.ModeratedContents
|
||||
1 -> ModeratorZoneAction.GlobalReports
|
||||
else -> ModeratorZoneAction.GlobalModLog
|
||||
}
|
||||
fun Int.toModeratorZoneAction(): ModeratorZoneAction =
|
||||
when (this) {
|
||||
2 -> ModeratorZoneAction.ModeratedContents
|
||||
1 -> ModeratorZoneAction.GlobalReports
|
||||
else -> ModeratorZoneAction.GlobalModLog
|
||||
}
|
||||
|
||||
fun ModeratorZoneAction.toInt(): Int = when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> 0
|
||||
ModeratorZoneAction.GlobalReports -> 1
|
||||
ModeratorZoneAction.ModeratedContents -> 2
|
||||
}
|
||||
fun ModeratorZoneAction.toInt(): Int =
|
||||
when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> 0
|
||||
ModeratorZoneAction.GlobalReports -> 1
|
||||
ModeratorZoneAction.ModeratedContents -> 2
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ModeratorZoneAction.toReadableName(): String = when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> LocalXmlStrings.current.modlogTitle
|
||||
ModeratorZoneAction.GlobalReports -> LocalXmlStrings.current.reportListTitle
|
||||
ModeratorZoneAction.ModeratedContents -> LocalXmlStrings.current.moderatorZoneActionContents
|
||||
}
|
||||
fun ModeratorZoneAction.toReadableName(): String =
|
||||
when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> LocalXmlStrings.current.modlogTitle
|
||||
ModeratorZoneAction.GlobalReports -> LocalXmlStrings.current.reportListTitle
|
||||
ModeratorZoneAction.ModeratedContents -> LocalXmlStrings.current.moderatorZoneActionContents
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ModeratorZoneAction.toIcon(): ImageVector = when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> Icons.AutoMirrored.Default.ListAlt
|
||||
ModeratorZoneAction.GlobalReports -> Icons.Default.Report
|
||||
ModeratorZoneAction.ModeratedContents -> Icons.Default.Shield
|
||||
}
|
||||
fun ModeratorZoneAction.toIcon(): ImageVector =
|
||||
when (this) {
|
||||
ModeratorZoneAction.GlobalModLog -> Icons.AutoMirrored.Default.ListAlt
|
||||
ModeratorZoneAction.GlobalReports -> Icons.Default.Report
|
||||
ModeratorZoneAction.ModeratedContents -> Icons.Default.Shield
|
||||
}
|
||||
|
@ -60,8 +60,9 @@ fun MultiCommunityItem(
|
||||
) {
|
||||
if (communityIcon.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier.padding(Spacing.xxxs).size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier.padding(Spacing.xxxs).size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = communityIcon,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
@ -77,9 +78,10 @@ fun MultiCommunityItem(
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = Spacing.s),
|
||||
text = buildString {
|
||||
append(title)
|
||||
},
|
||||
text =
|
||||
buildString {
|
||||
append(title)
|
||||
},
|
||||
color = fullColor,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
@ -88,16 +90,17 @@ fun MultiCommunityItem(
|
||||
if (options.isNotEmpty()) {
|
||||
Box {
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -108,10 +111,11 @@ fun MultiCommunityItem(
|
||||
onDismiss = {
|
||||
optionsMenuOpen = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
offset =
|
||||
DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
|
@ -7,32 +7,60 @@ data class Option(
|
||||
|
||||
sealed class OptionId(val value: Int) {
|
||||
data object Share : OptionId(0)
|
||||
|
||||
data object Hide : OptionId(1)
|
||||
|
||||
data object SeeRaw : OptionId(2)
|
||||
|
||||
data object CrossPost : OptionId(3)
|
||||
|
||||
data object Report : OptionId(4)
|
||||
|
||||
data object Edit : OptionId(5)
|
||||
|
||||
data object Delete : OptionId(6)
|
||||
|
||||
data object Purge : OptionId(7)
|
||||
|
||||
data object InfoInstance : OptionId(8)
|
||||
|
||||
data object Block : OptionId(9)
|
||||
|
||||
data object BlockInstance : OptionId(10)
|
||||
|
||||
data object ToggleRead : OptionId(11)
|
||||
|
||||
data object FeaturePost : OptionId(13)
|
||||
|
||||
data object LockPost : OptionId(14)
|
||||
|
||||
data object Remove : OptionId(15)
|
||||
|
||||
data object DistinguishComment : OptionId(16)
|
||||
|
||||
data object OpenReports : OptionId(17)
|
||||
|
||||
data object ResolveReport : OptionId(18)
|
||||
|
||||
data object BanUser : OptionId(19)
|
||||
|
||||
data object AddMod : OptionId(20)
|
||||
|
||||
data object Favorite : OptionId(21)
|
||||
|
||||
data object ViewModlog : OptionId(22)
|
||||
|
||||
data object Unban : OptionId(23)
|
||||
|
||||
data object SetCustomSort : OptionId(24)
|
||||
|
||||
data object Search : OptionId(25)
|
||||
|
||||
data object Copy : OptionId(26)
|
||||
|
||||
data object ExploreInstance : OptionId(27)
|
||||
|
||||
data object Unsubscribe : OptionId(28)
|
||||
|
||||
data object PurgeCreator : OptionId(29)
|
||||
}
|
||||
|
@ -90,36 +90,38 @@ fun PostCard(
|
||||
) {
|
||||
val markRead = post.read && fadeRead
|
||||
Box(
|
||||
modifier = modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(
|
||||
elevation = 5.dp,
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
)
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(vertical = Spacing.s)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
).onClick(
|
||||
onClick = onClick ?: {},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
modifier.then(
|
||||
if (postLayout == PostLayout.Card) {
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.shadow(
|
||||
elevation = 5.dp,
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
)
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(vertical = Spacing.s)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
).onClick(
|
||||
onClick = onClick ?: {},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
) {
|
||||
if (postLayout != PostLayout.Compact) {
|
||||
ExtendedPost(
|
||||
post = post,
|
||||
isFromModerator = isFromModerator,
|
||||
hideAuthor = hideAuthor,
|
||||
backgroundColor = when (postLayout) {
|
||||
PostLayout.Card -> MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp)
|
||||
else -> MaterialTheme.colorScheme.background
|
||||
},
|
||||
backgroundColor =
|
||||
when (postLayout) {
|
||||
PostLayout.Card -> MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp)
|
||||
else -> MaterialTheme.colorScheme.background
|
||||
},
|
||||
showBody = includeFullBody || postLayout == PostLayout.Full,
|
||||
limitBodyHeight = limitBodyHeight,
|
||||
voteFormat = voteFormat,
|
||||
@ -218,19 +220,20 @@ private fun CompactPost(
|
||||
post.url.orEmpty().takeIf { !it.looksLikeAnImage && !it.looksLikeAVideo }.orEmpty()
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onTap = {
|
||||
onClick?.invoke()
|
||||
},
|
||||
)
|
||||
},
|
||||
modifier =
|
||||
modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onTap = {
|
||||
onClick?.invoke()
|
||||
},
|
||||
)
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
CommunityAndCreatorInfo(
|
||||
@ -242,18 +245,21 @@ private fun CompactPost(
|
||||
locked = post.locked,
|
||||
markRead = markRead,
|
||||
isFromModerator = isFromModerator,
|
||||
onOpenCommunity = rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onOpenCreator = rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
onOpenCommunity =
|
||||
rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onOpenCreator =
|
||||
rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
autoLoadImages = autoLoadImages,
|
||||
preferNicknames = preferNicknames,
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onLongClick =
|
||||
rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = Spacing.xs),
|
||||
@ -281,42 +287,45 @@ private fun CompactPost(
|
||||
|
||||
if (post.videoUrl.isNotEmpty()) {
|
||||
PostCardVideo(
|
||||
modifier = Modifier
|
||||
.weight(0.25f)
|
||||
.padding(vertical = Spacing.xxs),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(0.25f)
|
||||
.padding(vertical = Spacing.xxs),
|
||||
url = post.videoUrl,
|
||||
blurred = blurNsfw && post.nsfw,
|
||||
autoLoadImages = autoLoadImages,
|
||||
onOpen = rememberCallback {
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onClick?.invoke()
|
||||
}
|
||||
},
|
||||
onOpen =
|
||||
rememberCallback {
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onClick?.invoke()
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
PostCardImage(
|
||||
modifier = Modifier
|
||||
.weight(0.25f)
|
||||
.then(
|
||||
if (fullHeightImage) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.aspectRatio(1f)
|
||||
},
|
||||
)
|
||||
.padding(vertical = Spacing.xs)
|
||||
.clip(RoundedCornerShape(CornerSize.s)),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(0.25f)
|
||||
.then(
|
||||
if (fullHeightImage) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.aspectRatio(1f)
|
||||
},
|
||||
)
|
||||
.padding(vertical = Spacing.xs)
|
||||
.clip(RoundedCornerShape(CornerSize.s)),
|
||||
minHeight = Dp.Unspecified,
|
||||
maxHeight = Dp.Unspecified,
|
||||
imageUrl = post.imageUrl,
|
||||
@ -325,43 +334,47 @@ private fun CompactPost(
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
},
|
||||
blurred = blurNsfw && post.nsfw,
|
||||
onImageClick = rememberCallbackArgs { url ->
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onOpenImage?.invoke(url)
|
||||
}
|
||||
},
|
||||
onImageClick =
|
||||
rememberCallbackArgs { url ->
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onOpenImage?.invoke(url)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onLongClick =
|
||||
rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
PostCardFooter(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.xxs,
|
||||
start = Spacing.xs,
|
||||
end = Spacing.xs,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = Spacing.xxs,
|
||||
start = Spacing.xs,
|
||||
end = Spacing.xs,
|
||||
),
|
||||
markRead = markRead,
|
||||
comments = post.comments,
|
||||
voteFormat = voteFormat,
|
||||
score = post.score,
|
||||
showScores = showScores,
|
||||
unreadComments = post.unreadComments.takeIf {
|
||||
it != null && it > 0 && showUnreadComments && it != post.comments
|
||||
},
|
||||
unreadComments =
|
||||
post.unreadComments.takeIf {
|
||||
it != null && it > 0 && showUnreadComments && it != post.comments
|
||||
},
|
||||
upVotes = post.upvotes,
|
||||
downVotes = post.downvotes,
|
||||
upVoted = post.myVote > 0,
|
||||
@ -421,27 +434,29 @@ private fun ExtendedPost(
|
||||
val customTabsHelper = remember { getCustomTabsHelper() }
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val optionsMenuOpen = remember { mutableStateOf(false) }
|
||||
val postLinkUrl = post.url.orEmpty().takeIf {
|
||||
it != post.imageUrl &&
|
||||
it != post.videoUrl &&
|
||||
!it.looksLikeAnImage &&
|
||||
!it.looksLikeAVideo &&
|
||||
!it.showInEmbeddedWebView
|
||||
}.orEmpty()
|
||||
val postLinkUrl =
|
||||
post.url.orEmpty().takeIf {
|
||||
it != post.imageUrl &&
|
||||
it != post.videoUrl &&
|
||||
!it.looksLikeAnImage &&
|
||||
!it.looksLikeAVideo &&
|
||||
!it.showInEmbeddedWebView
|
||||
}.orEmpty()
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(backgroundColor)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onTap = {
|
||||
onClick?.invoke()
|
||||
},
|
||||
)
|
||||
},
|
||||
modifier =
|
||||
modifier
|
||||
.background(backgroundColor)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onTap = {
|
||||
onClick?.invoke()
|
||||
},
|
||||
)
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
CommunityAndCreatorInfo(
|
||||
@ -453,25 +468,29 @@ private fun ExtendedPost(
|
||||
locked = post.locked,
|
||||
markRead = markRead,
|
||||
isFromModerator = isFromModerator,
|
||||
onOpenCommunity = rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onOpenCreator = rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
onOpenCommunity =
|
||||
rememberCallbackArgs { community ->
|
||||
onOpenCommunity?.invoke(community, "")
|
||||
},
|
||||
onOpenCreator =
|
||||
rememberCallbackArgs { user ->
|
||||
onOpenCreator?.invoke(user, "")
|
||||
},
|
||||
autoLoadImages = autoLoadImages,
|
||||
preferNicknames = preferNicknames,
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onLongClick =
|
||||
rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
)
|
||||
CustomizedContent(ContentFontClass.Title) {
|
||||
PostCardTitle(
|
||||
modifier = Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
text = post.title,
|
||||
markRead = markRead,
|
||||
bolder = showBody,
|
||||
@ -483,19 +502,21 @@ private fun ExtendedPost(
|
||||
onClick = onClick,
|
||||
onOpenImage = onOpenImage,
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
onLongClick =
|
||||
rememberCallback {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (post.embeddedUrl.isNotEmpty()) {
|
||||
PostCardEmbeddedWebView(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
),
|
||||
blurred = blurNsfw && post.nsfw,
|
||||
autoLoadImages = autoLoadImages,
|
||||
url = post.embeddedUrl,
|
||||
@ -518,11 +539,12 @@ private fun ExtendedPost(
|
||||
)
|
||||
} else if (post.videoUrl.isNotEmpty()) {
|
||||
PostCardVideo(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
),
|
||||
url = post.videoUrl,
|
||||
blurred = blurNsfw && post.nsfw,
|
||||
autoLoadImages = autoLoadImages,
|
||||
@ -546,42 +568,44 @@ private fun ExtendedPost(
|
||||
)
|
||||
} else {
|
||||
PostCardImage(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
)
|
||||
.then(
|
||||
if (roundedCornerImage && !fullWidthImage) {
|
||||
Modifier.clip(RoundedCornerShape(CornerSize.xl))
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
).then(
|
||||
if (fullHeightImage) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.heightIn(max = 200.dp)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = if (fullWidthImage) 0.dp else Spacing.s,
|
||||
)
|
||||
.then(
|
||||
if (roundedCornerImage && !fullWidthImage) {
|
||||
Modifier.clip(RoundedCornerShape(CornerSize.xl))
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
).then(
|
||||
if (fullHeightImage) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.heightIn(max = 200.dp)
|
||||
},
|
||||
),
|
||||
imageUrl = post.imageUrl,
|
||||
blurred = blurNsfw && post.nsfw,
|
||||
onImageClick = rememberCallbackArgs { url ->
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onOpenImage?.invoke(url)
|
||||
}
|
||||
},
|
||||
onImageClick =
|
||||
rememberCallbackArgs { url ->
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
} else {
|
||||
onOpenImage?.invoke(url)
|
||||
}
|
||||
},
|
||||
onDoubleClick = onDoubleClick,
|
||||
autoLoadImages = autoLoadImages,
|
||||
onLongClick = {
|
||||
@ -601,17 +625,19 @@ private fun ExtendedPost(
|
||||
} else {
|
||||
CustomizedContent(ContentFontClass.Body) {
|
||||
PostCardBody(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.xxs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = Spacing.xxs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
text = post.text,
|
||||
maxLines = if (limitBodyHeight) {
|
||||
settings.postBodyMaxLines
|
||||
} else {
|
||||
null
|
||||
},
|
||||
maxLines =
|
||||
if (limitBodyHeight) {
|
||||
settings.postBodyMaxLines
|
||||
} else {
|
||||
null
|
||||
},
|
||||
autoLoadImages = autoLoadImages,
|
||||
markRead = markRead,
|
||||
onClick = onClick,
|
||||
@ -630,45 +656,48 @@ private fun ExtendedPost(
|
||||
}
|
||||
if (postLinkUrl.isNotEmpty()) {
|
||||
PostLinkBanner(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
bottom = Spacing.xxs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
bottom = Spacing.xxs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
navigationCoordinator.handleUrl(
|
||||
url = postLinkUrl,
|
||||
openingMode = settings.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenWeb = onOpenWeb,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenUser = onOpenCreator,
|
||||
)
|
||||
},
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
),
|
||||
url = postLinkUrl,
|
||||
)
|
||||
}
|
||||
PostCardFooter(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.xs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = Spacing.xs,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
markRead = markRead,
|
||||
comments = post.comments,
|
||||
voteFormat = voteFormat,
|
||||
score = post.score,
|
||||
showScores = showScores,
|
||||
unreadComments = post.unreadComments.takeIf {
|
||||
it != null && it > 0 && showUnreadComments && it != post.comments
|
||||
},
|
||||
unreadComments =
|
||||
post.unreadComments.takeIf {
|
||||
it != null && it > 0 && showUnreadComments && it != post.comments
|
||||
},
|
||||
upVotes = post.upvotes,
|
||||
downVotes = post.downvotes,
|
||||
upVoted = post.myVote > 0,
|
||||
|
@ -57,43 +57,47 @@ fun PostCardBody(
|
||||
content = text,
|
||||
maxLines = maxLines,
|
||||
autoLoadImages = autoLoadImages,
|
||||
typography = markdownTypography(
|
||||
h1 = typography.titleLarge,
|
||||
h2 = typography.titleLarge,
|
||||
h3 = typography.titleMedium,
|
||||
h4 = typography.titleMedium,
|
||||
h5 = typography.titleSmall,
|
||||
h6 = typography.titleSmall,
|
||||
text = typography.bodyMedium,
|
||||
paragraph = typography.bodyMedium,
|
||||
quote = typography.bodyMedium.copy(fontStyle = FontStyle.Italic),
|
||||
bullet = typography.bodyMedium,
|
||||
list = typography.bodyMedium,
|
||||
ordered = typography.bodyMedium,
|
||||
code = typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||
),
|
||||
colors = markdownColor(
|
||||
text = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
linkText = MaterialTheme.colorScheme.primary.copy(alpha = additionalAlphaFactor),
|
||||
codeText = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeBackground = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f),
|
||||
dividerColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
),
|
||||
onOpenUrl = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.handleUrl(
|
||||
url = url,
|
||||
openingMode = settingsRepository.currentSettings.value.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenUser = onOpenUser,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenWeb = onOpenWeb,
|
||||
)
|
||||
},
|
||||
onOpenImage = rememberCallbackArgs { url ->
|
||||
onOpenImage?.invoke(url)
|
||||
},
|
||||
typography =
|
||||
markdownTypography(
|
||||
h1 = typography.titleLarge,
|
||||
h2 = typography.titleLarge,
|
||||
h3 = typography.titleMedium,
|
||||
h4 = typography.titleMedium,
|
||||
h5 = typography.titleSmall,
|
||||
h6 = typography.titleSmall,
|
||||
text = typography.bodyMedium,
|
||||
paragraph = typography.bodyMedium,
|
||||
quote = typography.bodyMedium.copy(fontStyle = FontStyle.Italic),
|
||||
bullet = typography.bodyMedium,
|
||||
list = typography.bodyMedium,
|
||||
ordered = typography.bodyMedium,
|
||||
code = typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||
),
|
||||
colors =
|
||||
markdownColor(
|
||||
text = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
linkText = MaterialTheme.colorScheme.primary.copy(alpha = additionalAlphaFactor),
|
||||
codeText = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeBackground = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f),
|
||||
dividerColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
),
|
||||
onOpenUrl =
|
||||
rememberCallbackArgs { url ->
|
||||
navigationCoordinator.handleUrl(
|
||||
url = url,
|
||||
openingMode = settingsRepository.currentSettings.value.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenUser = onOpenUser,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenWeb = onOpenWeb,
|
||||
)
|
||||
},
|
||||
onOpenImage =
|
||||
rememberCallbackArgs { url ->
|
||||
onOpenImage?.invoke(url)
|
||||
},
|
||||
onClick = onClick,
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = onLongClick,
|
||||
|
@ -97,16 +97,17 @@ fun PostCardFooter(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 3.5.dp,
|
||||
end = 3.5.dp,
|
||||
bottom = 3.5.dp,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onReply?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 3.5.dp,
|
||||
end = 3.5.dp,
|
||||
bottom = 3.5.dp,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onReply?.invoke()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.AutoMirrored.Default.Chat,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -120,13 +121,14 @@ fun PostCardFooter(
|
||||
}
|
||||
if (unreadComments != null) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = Spacing.xxs)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
shape = RoundedCornerShape(CornerSize.s),
|
||||
)
|
||||
.padding(horizontal = Spacing.xxs),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = Spacing.xxs)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
shape = RoundedCornerShape(CornerSize.s),
|
||||
)
|
||||
.padding(horizontal = Spacing.xxs),
|
||||
text = "+$unreadComments",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSecondary,
|
||||
@ -144,18 +146,20 @@ fun PostCardFooter(
|
||||
) {
|
||||
val isShowingUpdateDate = !updateDate.isNullOrBlank()
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.s).then(
|
||||
if (!isShowingUpdateDate) {
|
||||
Modifier.padding(0.5.dp)
|
||||
modifier =
|
||||
Modifier.size(IconSize.s).then(
|
||||
if (!isShowingUpdateDate) {
|
||||
Modifier.padding(0.5.dp)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
imageVector =
|
||||
if (isShowingUpdateDate) {
|
||||
Icons.Default.Update
|
||||
} else {
|
||||
Modifier
|
||||
Icons.Default.Schedule
|
||||
},
|
||||
),
|
||||
imageVector = if (isShowingUpdateDate) {
|
||||
Icons.Default.Update
|
||||
} else {
|
||||
Icons.Default.Schedule
|
||||
},
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
)
|
||||
@ -169,16 +173,17 @@ fun PostCardFooter(
|
||||
}
|
||||
if (options.isNotEmpty()) {
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen.value = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreHoriz,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -187,21 +192,24 @@ fun PostCardFooter(
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (actionButtonsActive) {
|
||||
FeedbackButton(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
imageVector = if (!saved) {
|
||||
Icons.Default.BookmarkBorder
|
||||
} else {
|
||||
Icons.Default.Bookmark
|
||||
},
|
||||
tintColor = if (saved) {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
imageVector =
|
||||
if (!saved) {
|
||||
Icons.Default.BookmarkBorder
|
||||
} else {
|
||||
Icons.Default.Bookmark
|
||||
},
|
||||
tintColor =
|
||||
if (saved) {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
onClick = {
|
||||
onSave?.invoke()
|
||||
},
|
||||
@ -209,52 +217,58 @@ fun PostCardFooter(
|
||||
}
|
||||
FeedbackButton(
|
||||
modifier = buttonModifier.padding(all = 2.5.dp),
|
||||
imageVector = if (actionButtonsActive) {
|
||||
Icons.Default.ArrowCircleUp
|
||||
} else {
|
||||
Icons.Default.ArrowUpward
|
||||
},
|
||||
tintColor = if (upVoted) {
|
||||
upVoteColor ?: defaultUpvoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
imageVector =
|
||||
if (actionButtonsActive) {
|
||||
Icons.Default.ArrowCircleUp
|
||||
} else {
|
||||
Icons.Default.ArrowUpward
|
||||
},
|
||||
tintColor =
|
||||
if (upVoted) {
|
||||
upVoteColor ?: defaultUpvoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
onClick = {
|
||||
onUpVote?.invoke()
|
||||
},
|
||||
)
|
||||
if (showScores) {
|
||||
Text(
|
||||
text = formatToReadableValue(
|
||||
voteFormat = voteFormat,
|
||||
score = score,
|
||||
upVotes = upVotes,
|
||||
downVotes = downVotes,
|
||||
upVoteColor = upVoteColor ?: defaultUpvoteColor,
|
||||
downVoteColor = downVoteColor ?: defaultDownVoteColor,
|
||||
upVoted = upVoted,
|
||||
downVoted = downVoted,
|
||||
),
|
||||
text =
|
||||
formatToReadableValue(
|
||||
voteFormat = voteFormat,
|
||||
score = score,
|
||||
upVotes = upVotes,
|
||||
downVotes = downVotes,
|
||||
upVoteColor = upVoteColor ?: defaultUpvoteColor,
|
||||
downVoteColor = downVoteColor ?: defaultDownVoteColor,
|
||||
upVoted = upVoted,
|
||||
downVoted = downVoted,
|
||||
),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = ancillaryColor,
|
||||
)
|
||||
}
|
||||
FeedbackButton(
|
||||
modifier = buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
imageVector = if (actionButtonsActive) {
|
||||
Icons.Default.ArrowCircleDown
|
||||
} else {
|
||||
Icons.Default.ArrowDownward
|
||||
},
|
||||
tintColor = if (downVoted) {
|
||||
downVoteColor ?: defaultDownVoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
modifier =
|
||||
buttonModifier.padding(
|
||||
top = 2.5.dp,
|
||||
bottom = 2.5.dp,
|
||||
end = 2.5.dp,
|
||||
),
|
||||
imageVector =
|
||||
if (actionButtonsActive) {
|
||||
Icons.Default.ArrowCircleDown
|
||||
} else {
|
||||
Icons.Default.ArrowDownward
|
||||
},
|
||||
tintColor =
|
||||
if (downVoted) {
|
||||
downVoteColor ?: defaultDownVoteColor
|
||||
} else {
|
||||
ancillaryColor
|
||||
},
|
||||
onClick = {
|
||||
onDownVote?.invoke()
|
||||
},
|
||||
@ -266,10 +280,11 @@ fun PostCardFooter(
|
||||
onDismiss = {
|
||||
optionsMenuOpen.value = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
offset =
|
||||
DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
|
@ -37,14 +37,15 @@ fun PostCardImage(
|
||||
) {
|
||||
if (imageUrl.isNotEmpty()) {
|
||||
CustomImage(
|
||||
modifier = modifier.fillMaxWidth()
|
||||
.heightIn(min = minHeight, max = maxHeight)
|
||||
.blur(radius = if (blurred) 60.dp else 0.dp)
|
||||
.onClick(
|
||||
onClick = { onImageClick?.invoke(imageUrl) },
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
modifier =
|
||||
modifier.fillMaxWidth()
|
||||
.heightIn(min = minHeight, max = maxHeight)
|
||||
.blur(radius = if (blurred) 60.dp else 0.dp)
|
||||
.onClick(
|
||||
onClick = { onImageClick?.invoke(imageUrl) },
|
||||
onDoubleClick = onDoubleClick ?: {},
|
||||
onLongClick = onLongClick ?: {},
|
||||
),
|
||||
url = imageUrl,
|
||||
quality = FilterQuality.Low,
|
||||
autoload = autoLoadImages,
|
||||
@ -59,19 +60,21 @@ fun PostCardImage(
|
||||
)
|
||||
},
|
||||
onLoading = { progress ->
|
||||
val prog = if (progress != null) {
|
||||
progress
|
||||
} else {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val res by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec = InfiniteRepeatableSpec(
|
||||
animation = tween(1000),
|
||||
),
|
||||
)
|
||||
res
|
||||
}
|
||||
val prog =
|
||||
if (progress != null) {
|
||||
progress
|
||||
} else {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val res by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec =
|
||||
InfiniteRepeatableSpec(
|
||||
animation = tween(1000),
|
||||
),
|
||||
)
|
||||
res
|
||||
}
|
||||
CircularProgressIndicator(
|
||||
progress = { prog },
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
|
@ -42,25 +42,28 @@ fun PostCardPlaceholder(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(IconSize.s)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.size(IconSize.s)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -69,39 +72,43 @@ fun PostCardPlaceholder(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(0.2f)
|
||||
.aspectRatio(1.33f)
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(0.2f)
|
||||
.aspectRatio(1.33f)
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.weight(1f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(40.dp)
|
||||
.weight(1f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
PostLayout.Card -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.shadow(elevation = 5.dp, shape = RoundedCornerShape(CornerSize.l))
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(Spacing.s),
|
||||
modifier =
|
||||
Modifier
|
||||
.shadow(elevation = 5.dp, shape = RoundedCornerShape(CornerSize.l))
|
||||
.clip(RoundedCornerShape(CornerSize.l))
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
)
|
||||
.padding(Spacing.s),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Row(
|
||||
@ -109,48 +116,54 @@ fun PostCardPlaceholder(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(200.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(200.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -165,49 +178,55 @@ fun PostCardPlaceholder(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(IconSize.l)
|
||||
.clip(CircleShape)
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier.height(IconSize.s)
|
||||
.fillMaxWidth(0.5f)
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(200.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(200.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.s))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,58 +50,64 @@ fun PostCardTitle(
|
||||
val themeRepository = remember { getThemeRepository() }
|
||||
val fontFamily by themeRepository.contentFontFamily.collectAsState()
|
||||
val typography = fontFamily.toTypography()
|
||||
val weightNormalOrLight = if (bolder) {
|
||||
FontWeight.Normal
|
||||
} else {
|
||||
FontWeight.Light
|
||||
}
|
||||
val weightMediumOrNormal = if (bolder) {
|
||||
FontWeight.Medium
|
||||
} else {
|
||||
FontWeight.Normal
|
||||
}
|
||||
val weightNormalOrLight =
|
||||
if (bolder) {
|
||||
FontWeight.Normal
|
||||
} else {
|
||||
FontWeight.Light
|
||||
}
|
||||
val weightMediumOrNormal =
|
||||
if (bolder) {
|
||||
FontWeight.Medium
|
||||
} else {
|
||||
FontWeight.Normal
|
||||
}
|
||||
val additionalAlphaFactor = if (markRead) readContentAlpha else 1f
|
||||
|
||||
CustomMarkdownWrapper(
|
||||
modifier = modifier.padding(horizontal = Spacing.xxs),
|
||||
content = text,
|
||||
autoLoadImages = autoLoadImages,
|
||||
typography = markdownTypography(
|
||||
h1 = typography.titleLarge.copy(fontWeight = weightNormalOrLight),
|
||||
h2 = typography.titleLarge.copy(fontWeight = weightNormalOrLight),
|
||||
h3 = typography.titleMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
h4 = typography.titleMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
h5 = typography.titleSmall.copy(fontWeight = weightMediumOrNormal),
|
||||
h6 = typography.titleSmall,
|
||||
text = typography.bodyMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
paragraph = typography.bodyMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
quote = typography.bodyMedium.copy(fontStyle = FontStyle.Italic),
|
||||
bullet = typography.bodyMedium,
|
||||
list = typography.bodyMedium,
|
||||
ordered = typography.bodyMedium,
|
||||
code = typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||
),
|
||||
colors = markdownColor(
|
||||
text = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeText = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeBackground = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f),
|
||||
dividerColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
),
|
||||
onOpenUrl = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.handleUrl(
|
||||
url = url,
|
||||
openingMode = settingsRepository.currentSettings.value.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenUser = onOpenUser,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenWeb = onOpenWeb,
|
||||
)
|
||||
},
|
||||
onOpenImage = rememberCallbackArgs { url ->
|
||||
onOpenImage?.invoke(url)
|
||||
},
|
||||
typography =
|
||||
markdownTypography(
|
||||
h1 = typography.titleLarge.copy(fontWeight = weightNormalOrLight),
|
||||
h2 = typography.titleLarge.copy(fontWeight = weightNormalOrLight),
|
||||
h3 = typography.titleMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
h4 = typography.titleMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
h5 = typography.titleSmall.copy(fontWeight = weightMediumOrNormal),
|
||||
h6 = typography.titleSmall,
|
||||
text = typography.bodyMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
paragraph = typography.bodyMedium.copy(fontWeight = weightMediumOrNormal),
|
||||
quote = typography.bodyMedium.copy(fontStyle = FontStyle.Italic),
|
||||
bullet = typography.bodyMedium,
|
||||
list = typography.bodyMedium,
|
||||
ordered = typography.bodyMedium,
|
||||
code = typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||
),
|
||||
colors =
|
||||
markdownColor(
|
||||
text = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeText = MaterialTheme.colorScheme.onBackground.copy(alpha = additionalAlphaFactor),
|
||||
codeBackground = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f),
|
||||
dividerColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
),
|
||||
onOpenUrl =
|
||||
rememberCallbackArgs { url ->
|
||||
navigationCoordinator.handleUrl(
|
||||
url = url,
|
||||
openingMode = settingsRepository.currentSettings.value.urlOpeningMode.toUrlOpeningMode(),
|
||||
uriHandler = uriHandler,
|
||||
customTabsHelper = customTabsHelper,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenUser = onOpenUser,
|
||||
onOpenPost = onOpenPost,
|
||||
onOpenWeb = onOpenWeb,
|
||||
)
|
||||
},
|
||||
onOpenImage =
|
||||
rememberCallbackArgs { url ->
|
||||
onOpenImage?.invoke(url)
|
||||
},
|
||||
onClick = onClick,
|
||||
onDoubleClick = onDoubleClick,
|
||||
onLongClick = onLongClick,
|
||||
|
@ -24,14 +24,15 @@ fun PostLinkBanner(
|
||||
) {
|
||||
if (url.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
).padding(
|
||||
horizontal = Spacing.m,
|
||||
vertical = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
).padding(
|
||||
horizontal = Spacing.m,
|
||||
vertical = Spacing.s,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.m),
|
||||
) {
|
||||
|
@ -2,5 +2,6 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui
|
||||
|
||||
sealed interface ProfileLoggedSection {
|
||||
data object Posts : ProfileLoggedSection
|
||||
|
||||
data object Comments : ProfileLoggedSection
|
||||
}
|
||||
|
@ -24,16 +24,17 @@ fun SettingsFormattedInfo(
|
||||
) {
|
||||
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
|
@ -24,10 +24,11 @@ fun SettingsHeader(
|
||||
) {
|
||||
val fullColor = MaterialTheme.colorScheme.onBackground
|
||||
Row(
|
||||
modifier = modifier.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
modifier.padding(
|
||||
vertical = Spacing.xxs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
|
@ -31,16 +31,17 @@ fun SettingsImageInfo(
|
||||
) {
|
||||
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
|
@ -33,9 +33,10 @@ fun SettingsIntValueRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = Spacing.s),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = Spacing.s),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
@ -59,9 +60,10 @@ fun SettingsIntValueRow(
|
||||
onClick = onDecrement,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.sizeIn(minWidth = 40.dp)
|
||||
.padding(horizontal = Spacing.s),
|
||||
modifier =
|
||||
Modifier
|
||||
.sizeIn(minWidth = 40.dp)
|
||||
.padding(horizontal = Spacing.s),
|
||||
textAlign = TextAlign.Center,
|
||||
text = value.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
|
@ -38,16 +38,17 @@ fun SettingsRow(
|
||||
val fullColor = MaterialTheme.colorScheme.onBackground
|
||||
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(
|
||||
vertical = Spacing.s,
|
||||
horizontal = Spacing.m,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onTap?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.padding(
|
||||
vertical = Spacing.s,
|
||||
horizontal = Spacing.m,
|
||||
)
|
||||
.onClick(
|
||||
onClick = {
|
||||
onTap?.invoke()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
|
@ -27,16 +27,17 @@ fun SettingsTextualInfo(
|
||||
val fullColor = MaterialTheme.colorScheme.onBackground
|
||||
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.m,
|
||||
).onClick(
|
||||
onClick = {
|
||||
onEdit?.invoke()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
|
@ -57,45 +57,49 @@ fun TextFormattingBar(
|
||||
// bold
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue = textFieldValue.let {
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("**")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("**")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
}
|
||||
append("**")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection =
|
||||
if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 2)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 2,
|
||||
end = it.selection.end + 2,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
append("**")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 2)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 2,
|
||||
end = it.selection.end + 2,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.FormatBold,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -105,45 +109,49 @@ fun TextFormattingBar(
|
||||
// italic
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue = textFieldValue.let {
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("*")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("*")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
}
|
||||
append("*")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection =
|
||||
if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
append("*")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.FormatItalic,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -153,45 +161,49 @@ fun TextFormattingBar(
|
||||
// strikethrough
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue = textFieldValue.let {
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("~~")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val selection = textFieldValue.selection
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("~~")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
}
|
||||
append("~~")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection =
|
||||
if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 2)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 2,
|
||||
end = it.selection.end + 2,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
append("~~")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 2)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 2,
|
||||
end = it.selection.end + 2,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.FormatStrikethrough,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
contentDescription = null,
|
||||
@ -202,11 +214,12 @@ fun TextFormattingBar(
|
||||
if (onSelectImage != null) {
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
onSelectImage.invoke()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = {
|
||||
onSelectImage.invoke()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.Image,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -217,47 +230,51 @@ fun TextFormattingBar(
|
||||
// hyperlink
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue = textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("[")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("[")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
}
|
||||
append("](")
|
||||
append(urlPlaceholder)
|
||||
append(")")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection =
|
||||
if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
append("](")
|
||||
append(urlPlaceholder)
|
||||
append(")")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.InsertLink,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -267,45 +284,49 @@ fun TextFormattingBar(
|
||||
// inline code
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue = textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("`")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("`")
|
||||
if (selection.collapsed) {
|
||||
append(textPlaceholder)
|
||||
} else {
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.start,
|
||||
selection.end,
|
||||
),
|
||||
)
|
||||
}
|
||||
append("`")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection =
|
||||
if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
append("`")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = if (selection.collapsed) {
|
||||
TextRange(index = selection.start + textPlaceholder.length + 1)
|
||||
} else {
|
||||
TextRange(
|
||||
start = it.selection.start + 1,
|
||||
end = it.selection.end + 1,
|
||||
)
|
||||
}
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.Code,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -315,27 +336,30 @@ fun TextFormattingBar(
|
||||
// block quote
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue = textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n> ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 3)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n> ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 3)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.FormatQuote,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -345,27 +369,30 @@ fun TextFormattingBar(
|
||||
// bulleted list
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue = textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n- ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 3)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n- ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 3)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.AutoMirrored.Default.FormatListBulleted,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -375,27 +402,30 @@ fun TextFormattingBar(
|
||||
// numbered list
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue = textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText = buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n1. ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 4)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
key = textFieldValue,
|
||||
onClick = {
|
||||
val newValue =
|
||||
textFieldValue.let {
|
||||
val selection = it.selection
|
||||
val newText =
|
||||
buildString {
|
||||
append(it.text.substring(0, selection.start))
|
||||
append("\n1. ")
|
||||
append(
|
||||
it.text.substring(
|
||||
selection.end,
|
||||
it.text.length,
|
||||
),
|
||||
)
|
||||
}
|
||||
val newSelection = TextRange(index = selection.start + 4)
|
||||
it.copy(text = newText, selection = newSelection)
|
||||
}
|
||||
onTextFieldValueChanged(newValue)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.FormatListNumbered,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@ -406,22 +436,24 @@ fun TextFormattingBar(
|
||||
if (onSelectLanguage != null) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(IconSize.m)
|
||||
.border(
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
width = Dp.Hairline,
|
||||
shape = RoundedCornerShape(CornerSize.m),
|
||||
)
|
||||
.clickable(onClick = onSelectLanguage)
|
||||
.padding(Spacing.xxxs),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(IconSize.m)
|
||||
.border(
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
width = Dp.Hairline,
|
||||
shape = RoundedCornerShape(CornerSize.m),
|
||||
)
|
||||
.clickable(onClick = onSelectLanguage)
|
||||
.padding(Spacing.xxxs),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val languageCode = availableLanguages.firstOrNull { l ->
|
||||
l.id == currentLanguageId
|
||||
}?.takeIf { l ->
|
||||
l.id > 0 // undetermined language
|
||||
}?.code ?: "λ"
|
||||
val languageCode =
|
||||
availableLanguages.firstOrNull { l ->
|
||||
l.id == currentLanguageId
|
||||
}?.takeIf { l ->
|
||||
l.id > 0 // undetermined language
|
||||
}?.code ?: "λ"
|
||||
Text(
|
||||
text = languageCode,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
@ -436,11 +468,12 @@ fun TextFormattingBar(
|
||||
if (lastActionIcon != null && onLastAction != null) {
|
||||
item {
|
||||
Icon(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
onLastAction()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.onClick(
|
||||
onClick = {
|
||||
onLastAction()
|
||||
},
|
||||
),
|
||||
imageVector = lastActionIcon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
|
@ -93,96 +93,100 @@ fun NavigationCoordinator.handleUrl(
|
||||
private fun normalizeUrl(url: String?): Pair<String, String> {
|
||||
val matches = Regex("https?://(?<instance>.*?)(?<pathAndQuery>/.*)").findAll(url.orEmpty())
|
||||
var instance = ""
|
||||
val res = buildString {
|
||||
if (matches.count() > 0) {
|
||||
val match = matches.iterator().next()
|
||||
val value = match.groups["pathAndQuery"]?.value.orEmpty()
|
||||
instance = match.groups["instance"]?.value.orEmpty()
|
||||
if (value.isNotEmpty()) {
|
||||
append(value)
|
||||
val res =
|
||||
buildString {
|
||||
if (matches.count() > 0) {
|
||||
val match = matches.iterator().next()
|
||||
val value = match.groups["pathAndQuery"]?.value.orEmpty()
|
||||
instance = match.groups["instance"]?.value.orEmpty()
|
||||
if (value.isNotEmpty()) {
|
||||
append(value)
|
||||
} else {
|
||||
append(url)
|
||||
}
|
||||
} else {
|
||||
append(url)
|
||||
}
|
||||
} else {
|
||||
append(url)
|
||||
}
|
||||
}
|
||||
return res to instance
|
||||
}
|
||||
|
||||
private fun extractCommunity(url: String): CommunityModel? = when {
|
||||
url.startsWith("/c/") -> {
|
||||
val cleanString = url.substring(3)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
CommunityModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
CommunityModel(
|
||||
name = cleanString,
|
||||
)
|
||||
private fun extractCommunity(url: String): CommunityModel? =
|
||||
when {
|
||||
url.startsWith("/c/") -> {
|
||||
val cleanString = url.substring(3)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
CommunityModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
CommunityModel(
|
||||
name = cleanString,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
url.startsWith("!") -> {
|
||||
val cleanString = url.substring(1)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
CommunityModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
CommunityModel(
|
||||
name = cleanString,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
url.startsWith("!") -> {
|
||||
val cleanString = url.substring(1)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
CommunityModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
CommunityModel(
|
||||
name = cleanString,
|
||||
)
|
||||
private fun extractUser(url: String): UserModel? =
|
||||
when {
|
||||
url.startsWith("@") -> {
|
||||
val cleanString = url.substring(1)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
UserModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
UserModel(
|
||||
name = cleanString,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun extractUser(url: String): UserModel? = when {
|
||||
url.startsWith("@") -> {
|
||||
val cleanString = url.substring(1)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
UserModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
UserModel(
|
||||
name = cleanString,
|
||||
)
|
||||
url.startsWith("/u/") -> {
|
||||
val cleanString = url.substring(3)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
UserModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
UserModel(
|
||||
name = cleanString,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
url.startsWith("/u/") -> {
|
||||
val cleanString = url.substring(3)
|
||||
if (cleanString.count { it == '@' } == 1) {
|
||||
val (name, host) = cleanString.split("@", limit = 2).let { l -> l[0] to l[1] }
|
||||
UserModel(
|
||||
name = name,
|
||||
host = host,
|
||||
)
|
||||
} else {
|
||||
UserModel(
|
||||
name = cleanString,
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun extractPost(url: String): PostModel? {
|
||||
val regex = Regex("/post/(?<postId>.*$)")
|
||||
val match = regex.find(url)
|
||||
val id = match
|
||||
?.let { it.groups["postId"]?.value.orEmpty() }?.toLongOrNull()
|
||||
val id =
|
||||
match
|
||||
?.let { it.groups["postId"]?.value.orEmpty() }?.toLongOrNull()
|
||||
return if (id != null) {
|
||||
PostModel(id = id)
|
||||
} else {
|
||||
|
@ -2,5 +2,6 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui
|
||||
|
||||
sealed interface UserDetailSection {
|
||||
data object Posts : UserDetailSection
|
||||
|
||||
data object Comments : UserDetailSection
|
||||
}
|
||||
|
@ -70,15 +70,18 @@ fun UserHeader(
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize().background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.95f),
|
||||
Color.Transparent,
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.75f),
|
||||
),
|
||||
modifier =
|
||||
Modifier.fillMaxSize().background(
|
||||
brush =
|
||||
Brush.horizontalGradient(
|
||||
colors =
|
||||
listOf(
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.95f),
|
||||
Color.Transparent,
|
||||
MaterialTheme.colorScheme.background.copy(alpha = 0.75f),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -92,14 +95,15 @@ fun UserHeader(
|
||||
val userAvatar = user.avatar.orEmpty()
|
||||
if (userAvatar.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier.padding(Spacing.xxxs)
|
||||
.size(IconSize.xxl)
|
||||
.clip(RoundedCornerShape(IconSize.xxl / 2))
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenImage?.invoke(userAvatar)
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(Spacing.xxxs)
|
||||
.size(IconSize.xxl)
|
||||
.clip(RoundedCornerShape(IconSize.xxl / 2))
|
||||
.onClick(
|
||||
onClick = {
|
||||
onOpenImage?.invoke(userAvatar)
|
||||
},
|
||||
),
|
||||
url = userAvatar,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
@ -145,10 +149,11 @@ fun UserHeader(
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = postScore.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
text =
|
||||
postScore.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -163,10 +168,11 @@ fun UserHeader(
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = commentScore.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
text =
|
||||
commentScore.getPrettyNumber(
|
||||
thousandLabel = LocalXmlStrings.current.profileThousandShort,
|
||||
millionLabel = LocalXmlStrings.current.profileMillionShort,
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -199,10 +205,11 @@ fun UserHeader(
|
||||
|
||||
if (onInfo != null) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(iconSize)
|
||||
.onClick(onClick = onInfo),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(end = Spacing.s)
|
||||
.size(iconSize)
|
||||
.onClick(onClick = onInfo),
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
@ -60,17 +60,19 @@ fun UserItem(
|
||||
var optionsMenuOpen by remember { mutableStateOf(false) }
|
||||
|
||||
Row(
|
||||
modifier = modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.s,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
if (avatar.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier.padding(Spacing.xxxs).size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
modifier =
|
||||
Modifier.padding(Spacing.xxxs).size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = avatar,
|
||||
quality = FilterQuality.Low,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
@ -86,19 +88,21 @@ fun UserItem(
|
||||
modifier = Modifier.weight(1f).padding(start = Spacing.xs),
|
||||
) {
|
||||
Text(
|
||||
text = buildString {
|
||||
append(displayName)
|
||||
},
|
||||
text =
|
||||
buildString {
|
||||
append(displayName)
|
||||
},
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = fullColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = buildString {
|
||||
append("!")
|
||||
append(userHandle)
|
||||
},
|
||||
text =
|
||||
buildString {
|
||||
append("!")
|
||||
append(userHandle)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = ancillaryColor,
|
||||
)
|
||||
@ -107,16 +111,17 @@ fun UserItem(
|
||||
if (options.isNotEmpty()) {
|
||||
Box {
|
||||
Icon(
|
||||
modifier = Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.size(IconSize.m)
|
||||
.padding(Spacing.xs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = {
|
||||
optionsMenuOpen = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = null,
|
||||
tint = ancillaryColor,
|
||||
@ -127,10 +132,11 @@ fun UserItem(
|
||||
onDismiss = {
|
||||
optionsMenuOpen = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
offset =
|
||||
DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
|
@ -4,8 +4,9 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.DefaultFab
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.FabNestedScrollConnection
|
||||
import org.koin.dsl.module
|
||||
|
||||
val lemmyUiModule = module {
|
||||
factory<FabNestedScrollConnection> {
|
||||
DefaultFabNestedScrollConnection()
|
||||
val lemmyUiModule =
|
||||
module {
|
||||
factory<FabNestedScrollConnection> {
|
||||
DefaultFabNestedScrollConnection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.FabNestedS
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
actual fun getFabNestedScrollConnection(): FabNestedScrollConnection =
|
||||
LemmyUiDiHelper.fabNestedScrollConnection
|
||||
actual fun getFabNestedScrollConnection(): FabNestedScrollConnection = LemmyUiDiHelper.fabNestedScrollConnection
|
||||
|
||||
object LemmyUiDiHelper : KoinComponent {
|
||||
val fabNestedScrollConnection: FabNestedScrollConnection by inject()
|
||||
|
@ -33,21 +33,23 @@ class AppIconBottomSheet : Screen {
|
||||
val coreResources = remember { getCoreResources() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.settingsAppIcon)
|
||||
val values = listOf(
|
||||
AppIconVariant.Default,
|
||||
AppIconVariant.Alt1,
|
||||
)
|
||||
val values =
|
||||
listOf(
|
||||
AppIconVariant.Default,
|
||||
AppIconVariant.Alt1,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
@ -56,10 +58,11 @@ class AppIconBottomSheet : Screen {
|
||||
SettingsRow(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxs),
|
||||
title = value.toReadableName(),
|
||||
painter = when (value) {
|
||||
AppIconVariant.Alt1 -> coreResources.appIconAlt1
|
||||
else -> coreResources.appIconDefault
|
||||
},
|
||||
painter =
|
||||
when (value) {
|
||||
AppIconVariant.Alt1 -> coreResources.appIconAlt1
|
||||
else -> coreResources.appIconDefault
|
||||
},
|
||||
onTap = {
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
notificationCenter.send(
|
||||
|
@ -27,46 +27,48 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotific
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
|
||||
class BarThemeBottomSheet : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.settingsBarTheme)
|
||||
val values = listOf(
|
||||
UiBarTheme.Transparent,
|
||||
UiBarTheme.Opaque,
|
||||
)
|
||||
val values =
|
||||
listOf(
|
||||
UiBarTheme.Transparent,
|
||||
UiBarTheme.Opaque,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeSystemBarTheme(value),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeSystemBarTheme(value),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = value.toReadableName(),
|
||||
|
@ -37,21 +37,21 @@ class BlockBottomSheet(
|
||||
private val userInstanceName: String? = null,
|
||||
private val userInstanceId: Long? = null,
|
||||
) : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.communityDetailBlock)
|
||||
@ -59,76 +59,86 @@ class BlockBottomSheet(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
val values: List<Triple<BlockActionType, Long, String>> = buildList {
|
||||
if (userName != null && userId != null) {
|
||||
this += Triple(
|
||||
BlockActionType.User,
|
||||
userId,
|
||||
userName,
|
||||
)
|
||||
val values: List<Triple<BlockActionType, Long, String>> =
|
||||
buildList {
|
||||
if (userName != null && userId != null) {
|
||||
this +=
|
||||
Triple(
|
||||
BlockActionType.User,
|
||||
userId,
|
||||
userName,
|
||||
)
|
||||
}
|
||||
if (communityName != null && communityId != null) {
|
||||
this +=
|
||||
Triple(
|
||||
BlockActionType.Community,
|
||||
communityId,
|
||||
communityName,
|
||||
)
|
||||
}
|
||||
if (instanceName != null && instanceId != null) {
|
||||
this +=
|
||||
Triple(
|
||||
BlockActionType.Instance,
|
||||
instanceId,
|
||||
instanceName,
|
||||
)
|
||||
}
|
||||
if (userInstanceName != null && userInstanceId != null && userInstanceName != instanceName) {
|
||||
this +=
|
||||
Triple(
|
||||
BlockActionType.Instance,
|
||||
userInstanceId,
|
||||
userInstanceName,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (communityName != null && communityId != null) {
|
||||
this += Triple(
|
||||
BlockActionType.Community,
|
||||
communityId,
|
||||
communityName,
|
||||
)
|
||||
}
|
||||
if (instanceName != null && instanceId != null) {
|
||||
this += Triple(
|
||||
BlockActionType.Instance,
|
||||
instanceId,
|
||||
instanceName,
|
||||
)
|
||||
}
|
||||
if (userInstanceName != null && userInstanceId != null && userInstanceName != instanceName) {
|
||||
this += Triple(
|
||||
BlockActionType.Instance,
|
||||
userInstanceId,
|
||||
userInstanceName,
|
||||
)
|
||||
}
|
||||
}
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = when (value.first) {
|
||||
BlockActionType.Community -> NotificationCenterEvent.BlockActionSelected(
|
||||
communityId = value.second,
|
||||
)
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event =
|
||||
when (value.first) {
|
||||
BlockActionType.Community ->
|
||||
NotificationCenterEvent.BlockActionSelected(
|
||||
communityId = value.second,
|
||||
)
|
||||
|
||||
BlockActionType.Instance ->
|
||||
NotificationCenterEvent.BlockActionSelected(
|
||||
instanceId = value.second,
|
||||
)
|
||||
BlockActionType.Instance ->
|
||||
NotificationCenterEvent.BlockActionSelected(
|
||||
instanceId = value.second,
|
||||
)
|
||||
|
||||
BlockActionType.User -> NotificationCenterEvent.BlockActionSelected(
|
||||
userId = value.second,
|
||||
)
|
||||
}
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
BlockActionType.User ->
|
||||
NotificationCenterEvent.BlockActionSelected(
|
||||
userId = value.second,
|
||||
)
|
||||
}
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val valueText = buildString {
|
||||
append(value.first.toReadableName())
|
||||
val additionalText = value.third
|
||||
if (additionalText.isNotEmpty()) {
|
||||
append("\n")
|
||||
append("(")
|
||||
append(additionalText)
|
||||
append(")")
|
||||
val valueText =
|
||||
buildString {
|
||||
append(value.first.toReadableName())
|
||||
val additionalText = value.third
|
||||
if (additionalText.isNotEmpty()) {
|
||||
append("\n")
|
||||
append("(")
|
||||
append(additionalText)
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = valueText,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
|
@ -29,21 +29,21 @@ class CopyPostBottomSheet(
|
||||
private val title: String? = null,
|
||||
private val text: String? = null,
|
||||
) : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.actionCopyClipboard)
|
||||
@ -55,19 +55,20 @@ class CopyPostBottomSheet(
|
||||
val textCanBeCopied = !text.isNullOrBlank()
|
||||
if (titleCanBeCopied) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(title.orEmpty())
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(title.orEmpty())
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
@ -79,19 +80,20 @@ class CopyPostBottomSheet(
|
||||
}
|
||||
if (textCanBeCopied) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(text.orEmpty())
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(text.orEmpty())
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
@ -101,25 +103,27 @@ class CopyPostBottomSheet(
|
||||
)
|
||||
}
|
||||
if (titleCanBeCopied) {
|
||||
val textToCopy = buildString {
|
||||
append(title)
|
||||
append("\n")
|
||||
append(text)
|
||||
}
|
||||
val textToCopy =
|
||||
buildString {
|
||||
append(title)
|
||||
append("\n")
|
||||
append(text)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(textToCopy)
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = NotificationCenterEvent.CopyText(textToCopy)
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
@ -34,35 +34,37 @@ enum class DurationBottomSheetType {
|
||||
}
|
||||
|
||||
class DurationBottomSheet(
|
||||
private val values: List<Duration> = listOf(
|
||||
1.seconds,
|
||||
2.seconds,
|
||||
3.seconds,
|
||||
5.seconds,
|
||||
10.seconds,
|
||||
),
|
||||
private val values: List<Duration> =
|
||||
listOf(
|
||||
1.seconds,
|
||||
2.seconds,
|
||||
3.seconds,
|
||||
5.seconds,
|
||||
10.seconds,
|
||||
),
|
||||
private val type: DurationBottomSheetType = DurationBottomSheetType.ZOMBIE_MODE_INTERVAL,
|
||||
) : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
val title = when (type) {
|
||||
DurationBottomSheetType.ZOMBIE_MODE_INTERVAL -> LocalXmlStrings.current.settingsZombieModeInterval
|
||||
DurationBottomSheetType.INBOX_CHECK_PERIOD -> LocalXmlStrings.current.settingsInboxBackgroundCheckPeriod
|
||||
}
|
||||
val title =
|
||||
when (type) {
|
||||
DurationBottomSheetType.ZOMBIE_MODE_INTERVAL -> LocalXmlStrings.current.settingsZombieModeInterval
|
||||
DurationBottomSheetType.INBOX_CHECK_PERIOD -> LocalXmlStrings.current.settingsInboxBackgroundCheckPeriod
|
||||
}
|
||||
BottomSheetHeader(title)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
@ -70,33 +72,36 @@ class DurationBottomSheet(
|
||||
) {
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event = when (type) {
|
||||
DurationBottomSheetType.ZOMBIE_MODE_INTERVAL ->
|
||||
NotificationCenterEvent.ChangeZombieInterval(value)
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
val event =
|
||||
when (type) {
|
||||
DurationBottomSheetType.ZOMBIE_MODE_INTERVAL ->
|
||||
NotificationCenterEvent.ChangeZombieInterval(value)
|
||||
|
||||
DurationBottomSheetType.INBOX_CHECK_PERIOD ->
|
||||
NotificationCenterEvent.ChangeInboxBackgroundCheckPeriod(value)
|
||||
}
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
DurationBottomSheetType.INBOX_CHECK_PERIOD ->
|
||||
NotificationCenterEvent.ChangeInboxBackgroundCheckPeriod(value)
|
||||
}
|
||||
notificationCenter.send(event)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = value.getPrettyDuration(
|
||||
secondsLabel = LocalXmlStrings.current.postSecondShort,
|
||||
minutesLabel = LocalXmlStrings.current.postMinuteShort,
|
||||
hoursLabel = LocalXmlStrings.current.postHourShort,
|
||||
),
|
||||
text =
|
||||
value.getPrettyDuration(
|
||||
secondsLabel = LocalXmlStrings.current.postSecondShort,
|
||||
minutesLabel = LocalXmlStrings.current.postMinuteShort,
|
||||
hoursLabel = LocalXmlStrings.current.postHourShort,
|
||||
),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
@ -64,10 +64,11 @@ fun EditFormattedInfoDialog(
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.padding(Spacing.s),
|
||||
modifier =
|
||||
Modifier
|
||||
.imePadding()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.padding(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
@ -82,44 +83,50 @@ fun EditFormattedInfoDialog(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
SectionSelector(
|
||||
titles = listOf(
|
||||
LocalXmlStrings.current.createPostTabEditor,
|
||||
LocalXmlStrings.current.createPostTabPreview,
|
||||
),
|
||||
currentSection = when (currentSection) {
|
||||
CreatePostSection.Preview -> 1
|
||||
else -> 0
|
||||
},
|
||||
titles =
|
||||
listOf(
|
||||
LocalXmlStrings.current.createPostTabEditor,
|
||||
LocalXmlStrings.current.createPostTabPreview,
|
||||
),
|
||||
currentSection =
|
||||
when (currentSection) {
|
||||
CreatePostSection.Preview -> 1
|
||||
else -> 0
|
||||
},
|
||||
onSectionSelected = {
|
||||
val section = when (it) {
|
||||
1 -> CreatePostSection.Preview
|
||||
else -> CreatePostSection.Edit
|
||||
}
|
||||
val section =
|
||||
when (it) {
|
||||
1 -> CreatePostSection.Preview
|
||||
else -> CreatePostSection.Edit
|
||||
}
|
||||
currentSection = section
|
||||
},
|
||||
)
|
||||
|
||||
if (currentSection == CreatePostSection.Edit) {
|
||||
TextFormattingBar(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
),
|
||||
textFieldValue = textFieldValue,
|
||||
onTextFieldValueChanged = {
|
||||
textFieldValue = it
|
||||
},
|
||||
)
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.height(400.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(400.dp)
|
||||
.fillMaxWidth(),
|
||||
colors =
|
||||
TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
label = {
|
||||
Text(
|
||||
text = title,
|
||||
@ -128,25 +135,28 @@ fun EditFormattedInfoDialog(
|
||||
},
|
||||
textStyle = typography.bodyMedium,
|
||||
value = textFieldValue,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
autoCorrect = true,
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
autoCorrect = true,
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
textFieldValue = value
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(400.dp)
|
||||
.fillMaxWidth(),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(400.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
PostCardBody(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.s)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(Spacing.s)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
text = textFieldValue.text,
|
||||
)
|
||||
}
|
||||
|
@ -52,10 +52,11 @@ fun EditTextualInfoDialog(
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.padding(Spacing.s),
|
||||
modifier =
|
||||
Modifier
|
||||
.imePadding()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.padding(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
@ -68,11 +69,12 @@ fun EditTextualInfoDialog(
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
colors =
|
||||
TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
label = {
|
||||
Text(
|
||||
text = title,
|
||||
@ -81,10 +83,11 @@ fun EditTextualInfoDialog(
|
||||
},
|
||||
textStyle = typography.bodyMedium,
|
||||
value = textFieldValue,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
autoCorrect = true,
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
autoCorrect = true,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
textFieldValue = value
|
||||
},
|
||||
|
@ -32,14 +32,15 @@ class InboxTypeSheet : Screen {
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.inboxListingTypeTitle)
|
||||
@ -48,20 +49,21 @@ class InboxTypeSheet : Screen {
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeInboxType(true),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeInboxType(true),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
@ -71,19 +73,20 @@ class InboxTypeSheet : Screen {
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
).onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeInboxType(false),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
).onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeInboxType(false),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
@ -30,91 +30,95 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLanguageFlag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLanguageName
|
||||
|
||||
class LanguageBottomSheet : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.safeContent)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.safeContent)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.settingsLanguage)
|
||||
val values = listOf(
|
||||
Locales.Ar,
|
||||
Locales.Bg,
|
||||
Locales.Cs,
|
||||
Locales.Da,
|
||||
Locales.De,
|
||||
Locales.Et,
|
||||
Locales.El,
|
||||
Locales.En,
|
||||
Locales.Es,
|
||||
Locales.Eo,
|
||||
Locales.Fr,
|
||||
Locales.Ga,
|
||||
Locales.Hr,
|
||||
Locales.It,
|
||||
Locales.Lv,
|
||||
Locales.Lt,
|
||||
Locales.Hu,
|
||||
Locales.Mt,
|
||||
Locales.Nl,
|
||||
Locales.No,
|
||||
Locales.Pl,
|
||||
Locales.Pt,
|
||||
Locales.PtBr,
|
||||
Locales.Ro,
|
||||
Locales.Ru,
|
||||
Locales.Sk,
|
||||
Locales.Sl,
|
||||
Locales.Sq,
|
||||
Locales.Sr,
|
||||
Locales.Fi,
|
||||
Locales.Se,
|
||||
Locales.Tok,
|
||||
Locales.Tr,
|
||||
Locales.Uk,
|
||||
)
|
||||
val values =
|
||||
listOf(
|
||||
Locales.Ar,
|
||||
Locales.Bg,
|
||||
Locales.Cs,
|
||||
Locales.Da,
|
||||
Locales.De,
|
||||
Locales.Et,
|
||||
Locales.El,
|
||||
Locales.En,
|
||||
Locales.Es,
|
||||
Locales.Eo,
|
||||
Locales.Fr,
|
||||
Locales.Ga,
|
||||
Locales.Hr,
|
||||
Locales.It,
|
||||
Locales.Lv,
|
||||
Locales.Lt,
|
||||
Locales.Hu,
|
||||
Locales.Mt,
|
||||
Locales.Nl,
|
||||
Locales.No,
|
||||
Locales.Pl,
|
||||
Locales.Pt,
|
||||
Locales.PtBr,
|
||||
Locales.Ro,
|
||||
Locales.Ru,
|
||||
Locales.Sk,
|
||||
Locales.Sl,
|
||||
Locales.Sq,
|
||||
Locales.Sr,
|
||||
Locales.Fi,
|
||||
Locales.Se,
|
||||
Locales.Tok,
|
||||
Locales.Tr,
|
||||
Locales.Uk,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeLanguage(value),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeLanguage(value),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
with(value) {
|
||||
append(toLanguageFlag())
|
||||
append(" ")
|
||||
append(toLanguageName())
|
||||
}
|
||||
},
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
with(value) {
|
||||
append(toLanguageFlag())
|
||||
append(" ")
|
||||
append(toLanguageName())
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
@ -32,14 +32,15 @@ class LikedTypeSheet : Screen {
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.filteredContentsType)
|
||||
@ -48,20 +49,21 @@ class LikedTypeSheet : Screen {
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangedLikedType(true),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangedLikedType(true),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
@ -71,19 +73,20 @@ class LikedTypeSheet : Screen {
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
).onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangedLikedType(false),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
).onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangedLikedType(false),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
@ -41,46 +41,49 @@ class ListingTypeBottomSheet(
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
BottomSheetHeader(LocalXmlStrings.current.homeListingTitle)
|
||||
val values = buildList {
|
||||
if (isLogged) {
|
||||
this += ListingType.Subscribed
|
||||
val values =
|
||||
buildList {
|
||||
if (isLogged) {
|
||||
this += ListingType.Subscribed
|
||||
}
|
||||
this += ListingType.All
|
||||
this += ListingType.Local
|
||||
}
|
||||
this += ListingType.All
|
||||
this += ListingType.Local
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||
) {
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeFeedType(
|
||||
value = value,
|
||||
screenKey = screenKey,
|
||||
),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeFeedType(
|
||||
value = value,
|
||||
screenKey = screenKey,
|
||||
),
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user