Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 389d3f9e52 | |||
| 69a50f8fcd | |||
| 8839015249 | |||
| 478639e427 | |||
| dcbfd43896 |
@@ -40,7 +40,7 @@ jobs:
|
||||
echo "APK_NAME=$(basename $APK_PATH)" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload APK with original name
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_NAME }}
|
||||
path: ${{ env.APK_PATH }}
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
echo "APK_NAME=$(basename $APK_PATH)" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload APK with original name
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_NAME }}
|
||||
path: ${{ env.APK_PATH }}
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
- name: Upload release APK
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: app-release.apk
|
||||
path: app/build/outputs/apk/release/app-release.apk
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
run: ./gradlew bundleRelease
|
||||
|
||||
- name: Upload release Bundle
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: app-release.aab
|
||||
path: app/build/outputs/bundle/release/app-release.aab
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
@@ -15,43 +14,20 @@ import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.conena.nanokt.android.content.pxToDp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import dev.meloda.fast.MainViewModel
|
||||
import dev.meloda.fast.MainViewModelImpl
|
||||
import dev.meloda.fast.common.AppConstants
|
||||
import dev.meloda.fast.common.LongPollController
|
||||
import dev.meloda.fast.common.model.LongPollState
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.datastore.UserSettings
|
||||
import dev.meloda.fast.model.api.domain.VkUser
|
||||
import dev.meloda.fast.service.OnlineService
|
||||
import dev.meloda.fast.service.longpolling.LongPollingService
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.common.LocalSizeConfig
|
||||
import dev.meloda.fast.ui.model.DeviceSize
|
||||
import dev.meloda.fast.ui.model.SizeConfig
|
||||
import dev.meloda.fast.ui.model.ThemeConfig
|
||||
import dev.meloda.fast.ui.theme.AppTheme
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.theme.LocalUser
|
||||
import dev.meloda.fast.ui.util.isNeedToEnableDarkMode
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@@ -88,168 +64,26 @@ class MainActivity : AppCompatActivity() {
|
||||
requestNotificationPermissions()
|
||||
|
||||
setContent {
|
||||
val resources = LocalResources.current
|
||||
|
||||
val userSettings: UserSettings = koinInject()
|
||||
val longPollController: LongPollController = koinInject()
|
||||
|
||||
val longPollCurrentState by longPollController.currentState.collectAsStateWithLifecycle()
|
||||
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
||||
|
||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||
|
||||
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||
LaunchedEffect(viewModel) {
|
||||
Log.d("VM_CREATE", "onCreate: viewModel: $viewModel")
|
||||
}
|
||||
|
||||
LifecycleResumeEffect(true) {
|
||||
viewModel.onAppResumed(intent)
|
||||
onPauseOrDispose {}
|
||||
}
|
||||
|
||||
val permissionState =
|
||||
rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)
|
||||
|
||||
val isNeedToCheckPermission by viewModel.isNeedToCheckNotificationsPermission.collectAsStateWithLifecycle()
|
||||
val isNeedToRequestPermission by viewModel.isNeedToRequestNotifications.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(isNeedToCheckPermission) {
|
||||
if (isNeedToCheckPermission) {
|
||||
viewModel.onPermissionCheckStatus(permissionState.status)
|
||||
|
||||
if (permissionState.status.isGranted) {
|
||||
if (longPollCurrentState == LongPollState.InApp) {
|
||||
toggleLongPollService(false)
|
||||
}
|
||||
|
||||
RootScreen(
|
||||
toggleLongPollService = { enable, inBackground ->
|
||||
toggleLongPollService(
|
||||
enable = true,
|
||||
inBackground = true
|
||||
enable = enable,
|
||||
inBackground = inBackground ?: AppSettings.Experimental.longPollInBackground
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isNeedToRequestPermission) {
|
||||
if (isNeedToRequestPermission) {
|
||||
viewModel.onPermissionsRequested()
|
||||
permissionState.launchPermissionRequest()
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleResumeEffect(longPollStateToApply) {
|
||||
Log.d("LongPollMainActivity", "longPollStateToApply: $longPollStateToApply")
|
||||
if (longPollStateToApply != LongPollState.Background) {
|
||||
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
||||
&& longPollCurrentState != longPollStateToApply
|
||||
) {
|
||||
toggleLongPollService(false)
|
||||
Log.d("LongPoll", "recreate()")
|
||||
}
|
||||
|
||||
toggleLongPollService(
|
||||
enable = longPollStateToApply.isLaunched(),
|
||||
inBackground = longPollStateToApply == LongPollState.Background
|
||||
},
|
||||
toggleOnlineService = ::toggleOnlineService
|
||||
)
|
||||
}
|
||||
|
||||
onPauseOrDispose {}
|
||||
}
|
||||
|
||||
val sendOnline by userSettings.sendOnlineStatus.collectAsStateWithLifecycle()
|
||||
LifecycleResumeEffect(sendOnline) {
|
||||
toggleOnlineService(sendOnline)
|
||||
|
||||
onPauseOrDispose {
|
||||
toggleOnlineService(false)
|
||||
}
|
||||
}
|
||||
|
||||
val deviceWidthDp = remember(true) {
|
||||
resources.displayMetrics.widthPixels.pxToDp()
|
||||
}
|
||||
val deviceHeightDp = remember(true) {
|
||||
resources.displayMetrics.heightPixels.pxToDp()
|
||||
}
|
||||
|
||||
val deviceWidthSize by remember(deviceWidthDp) {
|
||||
derivedStateOf {
|
||||
when {
|
||||
deviceWidthDp <= 360 -> DeviceSize.Small
|
||||
deviceWidthDp <= 600 -> DeviceSize.Compact
|
||||
deviceWidthDp <= 840 -> DeviceSize.Medium
|
||||
else -> DeviceSize.Expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val deviceHeightSize by remember(deviceHeightDp) {
|
||||
derivedStateOf {
|
||||
when {
|
||||
deviceHeightDp <= 480 -> DeviceSize.Small
|
||||
deviceHeightDp <= 700 -> DeviceSize.Compact
|
||||
deviceHeightDp <= 900 -> DeviceSize.Medium
|
||||
else -> DeviceSize.Expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sizeConfig by remember(deviceWidthSize, deviceHeightSize) {
|
||||
mutableStateOf(
|
||||
SizeConfig(
|
||||
widthSize = deviceWidthSize,
|
||||
heightSize = deviceHeightSize
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val darkMode by userSettings.darkMode.collectAsStateWithLifecycle()
|
||||
val dynamicColors by userSettings.enableDynamicColors.collectAsStateWithLifecycle()
|
||||
val amoledDark by userSettings.enableAmoledDark.collectAsStateWithLifecycle()
|
||||
val enableBlur by userSettings.useBlur.collectAsStateWithLifecycle()
|
||||
val enableMultiline by userSettings.enableMultiline.collectAsStateWithLifecycle()
|
||||
val useSystemFont by userSettings.useSystemFont.collectAsStateWithLifecycle()
|
||||
val enableAnimations by userSettings.enableAnimations.collectAsStateWithLifecycle()
|
||||
|
||||
val setDarkMode = isNeedToEnableDarkMode(darkMode = darkMode)
|
||||
|
||||
val themeConfig by remember(
|
||||
darkMode,
|
||||
dynamicColors,
|
||||
amoledDark,
|
||||
enableBlur,
|
||||
enableMultiline,
|
||||
setDarkMode,
|
||||
useSystemFont
|
||||
) {
|
||||
derivedStateOf {
|
||||
ThemeConfig(
|
||||
darkMode = setDarkMode,
|
||||
dynamicColors = dynamicColors,
|
||||
selectedColorScheme = 0,
|
||||
amoledDark = amoledDark,
|
||||
enableBlur = enableBlur,
|
||||
enableMultiline = enableMultiline,
|
||||
useSystemFont = useSystemFont,
|
||||
enableAnimations = enableAnimations
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalThemeConfig provides themeConfig,
|
||||
LocalSizeConfig provides sizeConfig,
|
||||
LocalUser provides currentUser
|
||||
) {
|
||||
AppTheme(
|
||||
useDarkTheme = themeConfig.darkMode,
|
||||
useDynamicColors = themeConfig.dynamicColors,
|
||||
selectedColorScheme = themeConfig.selectedColorScheme,
|
||||
useAmoledBackground = themeConfig.amoledDark,
|
||||
useSystemFont = themeConfig.useSystemFont
|
||||
) {
|
||||
RootScreen(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNotificationChannels() {
|
||||
@@ -279,7 +113,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
val notificationManager: NotificationManager =
|
||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
notificationManager.createNotificationChannels(
|
||||
listOf(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package dev.meloda.fast.presentation
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
@@ -15,44 +17,225 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.conena.nanokt.android.content.pxToDp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import dev.meloda.fast.MainViewModel
|
||||
import dev.meloda.fast.MainViewModelImpl
|
||||
import dev.meloda.fast.auth.authNavGraph
|
||||
import dev.meloda.fast.auth.navigateToAuth
|
||||
import dev.meloda.fast.chatmaterials.navigation.chatMaterialsScreen
|
||||
import dev.meloda.fast.chatmaterials.navigation.navigateToChatMaterials
|
||||
import dev.meloda.fast.common.LongPollController
|
||||
import dev.meloda.fast.common.model.LongPollState
|
||||
import dev.meloda.fast.conversations.navigation.createChatScreen
|
||||
import dev.meloda.fast.conversations.navigation.navigateToCreateChat
|
||||
import dev.meloda.fast.datastore.UserSettings
|
||||
import dev.meloda.fast.languagepicker.navigation.languagePickerScreen
|
||||
import dev.meloda.fast.languagepicker.navigation.navigateToLanguagePicker
|
||||
import dev.meloda.fast.messageshistory.navigation.messagesHistoryScreen
|
||||
import dev.meloda.fast.messageshistory.navigation.navigateToMessagesHistory
|
||||
import dev.meloda.fast.model.api.domain.VkUser
|
||||
import dev.meloda.fast.navigation.Main
|
||||
import dev.meloda.fast.navigation.mainScreen
|
||||
import dev.meloda.fast.photoviewer.presentation.PhotoViewDialog
|
||||
import dev.meloda.fast.settings.navigation.navigateToSettings
|
||||
import dev.meloda.fast.settings.navigation.settingsScreen
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.common.LocalSizeConfig
|
||||
import dev.meloda.fast.ui.model.DeviceSize
|
||||
import dev.meloda.fast.ui.model.SizeConfig
|
||||
import dev.meloda.fast.ui.model.ThemeConfig
|
||||
import dev.meloda.fast.ui.theme.AppTheme
|
||||
import dev.meloda.fast.ui.theme.LocalNavController
|
||||
import dev.meloda.fast.ui.theme.LocalNavRootController
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.theme.LocalUser
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList
|
||||
import dev.meloda.fast.ui.util.immutableListOf
|
||||
import dev.meloda.fast.ui.util.isNeedToEnableDarkMode
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun RootScreen(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
viewModel: MainViewModel
|
||||
toggleLongPollService: (enable: Boolean, inBackground: Boolean?) -> Unit,
|
||||
toggleOnlineService: (enable: Boolean) -> Unit
|
||||
) {
|
||||
val resources = LocalResources.current
|
||||
|
||||
val userSettings: UserSettings = koinInject()
|
||||
val longPollController: LongPollController = koinInject()
|
||||
|
||||
val longPollCurrentState by longPollController.currentState.collectAsStateWithLifecycle()
|
||||
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
||||
|
||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||
LaunchedEffect(viewModel) {
|
||||
Log.d("VM_CREATE", "RootScreen(): viewModel: $viewModel")
|
||||
}
|
||||
|
||||
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||
|
||||
val permissionState =
|
||||
rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)
|
||||
|
||||
val isNeedToCheckPermission by viewModel.isNeedToCheckNotificationsPermission.collectAsStateWithLifecycle()
|
||||
val isNeedToRequestPermission by viewModel.isNeedToRequestNotifications.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(isNeedToCheckPermission) {
|
||||
if (isNeedToCheckPermission) {
|
||||
viewModel.onPermissionCheckStatus(permissionState.status)
|
||||
|
||||
if (permissionState.status.isGranted) {
|
||||
if (longPollCurrentState == LongPollState.InApp) {
|
||||
toggleLongPollService(false, null)
|
||||
}
|
||||
|
||||
toggleLongPollService(true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isNeedToRequestPermission) {
|
||||
if (isNeedToRequestPermission) {
|
||||
viewModel.onPermissionsRequested()
|
||||
permissionState.launchPermissionRequest()
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleResumeEffect(longPollStateToApply) {
|
||||
Log.d("LongPollMainActivity", "longPollStateToApply: $longPollStateToApply")
|
||||
if (longPollStateToApply != LongPollState.Background) {
|
||||
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
||||
&& longPollCurrentState != longPollStateToApply
|
||||
) {
|
||||
toggleLongPollService(false, null)
|
||||
Log.d("LongPoll", "recreate()")
|
||||
}
|
||||
|
||||
toggleLongPollService(
|
||||
longPollStateToApply.isLaunched(),
|
||||
longPollStateToApply == LongPollState.Background
|
||||
)
|
||||
}
|
||||
|
||||
onPauseOrDispose {}
|
||||
}
|
||||
|
||||
val sendOnline by userSettings.sendOnlineStatus.collectAsStateWithLifecycle()
|
||||
LifecycleResumeEffect(sendOnline) {
|
||||
toggleOnlineService(sendOnline)
|
||||
|
||||
onPauseOrDispose {
|
||||
toggleOnlineService(false)
|
||||
}
|
||||
}
|
||||
|
||||
val deviceWidthDp = remember(true) {
|
||||
resources.displayMetrics.widthPixels.pxToDp()
|
||||
}
|
||||
val deviceHeightDp = remember(true) {
|
||||
resources.displayMetrics.heightPixels.pxToDp()
|
||||
}
|
||||
|
||||
val deviceWidthSize by remember(deviceWidthDp) {
|
||||
derivedStateOf {
|
||||
when {
|
||||
deviceWidthDp <= 360 -> DeviceSize.Small
|
||||
deviceWidthDp <= 600 -> DeviceSize.Compact
|
||||
deviceWidthDp <= 840 -> DeviceSize.Medium
|
||||
else -> DeviceSize.Expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val deviceHeightSize by remember(deviceHeightDp) {
|
||||
derivedStateOf {
|
||||
when {
|
||||
deviceHeightDp <= 480 -> DeviceSize.Small
|
||||
deviceHeightDp <= 700 -> DeviceSize.Compact
|
||||
deviceHeightDp <= 900 -> DeviceSize.Medium
|
||||
else -> DeviceSize.Expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sizeConfig by remember(deviceWidthSize, deviceHeightSize) {
|
||||
mutableStateOf(
|
||||
SizeConfig(
|
||||
widthSize = deviceWidthSize,
|
||||
heightSize = deviceHeightSize
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val darkMode by userSettings.darkMode.collectAsStateWithLifecycle()
|
||||
val dynamicColors by userSettings.enableDynamicColors.collectAsStateWithLifecycle()
|
||||
val amoledDark by userSettings.enableAmoledDark.collectAsStateWithLifecycle()
|
||||
val enableBlur by userSettings.useBlur.collectAsStateWithLifecycle()
|
||||
val enableMultiline by userSettings.enableMultiline.collectAsStateWithLifecycle()
|
||||
val useSystemFont by userSettings.useSystemFont.collectAsStateWithLifecycle()
|
||||
val enableAnimations by userSettings.enableAnimations.collectAsStateWithLifecycle()
|
||||
|
||||
val setDarkMode = isNeedToEnableDarkMode(darkMode = darkMode)
|
||||
|
||||
val themeConfig by remember(
|
||||
darkMode,
|
||||
dynamicColors,
|
||||
amoledDark,
|
||||
enableBlur,
|
||||
enableMultiline,
|
||||
setDarkMode,
|
||||
useSystemFont
|
||||
) {
|
||||
derivedStateOf {
|
||||
ThemeConfig(
|
||||
darkMode = setDarkMode,
|
||||
dynamicColors = dynamicColors,
|
||||
selectedColorScheme = 0,
|
||||
amoledDark = amoledDark,
|
||||
enableBlur = enableBlur,
|
||||
enableMultiline = enableMultiline,
|
||||
useSystemFont = useSystemFont,
|
||||
enableAnimations = enableAnimations
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalThemeConfig provides themeConfig,
|
||||
LocalSizeConfig provides sizeConfig,
|
||||
LocalUser provides currentUser
|
||||
) {
|
||||
AppTheme(
|
||||
useDarkTheme = themeConfig.darkMode,
|
||||
useDynamicColors = themeConfig.dynamicColors,
|
||||
selectedColorScheme = themeConfig.selectedColorScheme,
|
||||
useAmoledBackground = themeConfig.amoledDark,
|
||||
useSystemFont = themeConfig.useSystemFont
|
||||
) {
|
||||
val navController: NavHostController = rememberNavController()
|
||||
val activity = LocalActivity.current
|
||||
val context = LocalContext.current
|
||||
val startDestination by viewModel.startDestination.collectAsStateWithLifecycle()
|
||||
@@ -126,7 +309,7 @@ fun RootScreen(
|
||||
LocalNavController provides navController
|
||||
) {
|
||||
var photoViewerInfo by rememberSaveable {
|
||||
mutableStateOf<Pair<List<String>, Int?>?>(null)
|
||||
mutableStateOf<Pair<ImmutableList<String>, Int?>?>(null)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
@@ -149,7 +332,9 @@ fun RootScreen(
|
||||
onError = viewModel::onError,
|
||||
onSettingsButtonClicked = navController::navigateToSettings,
|
||||
onNavigateToMessagesHistory = navController::navigateToMessagesHistory,
|
||||
onPhotoClicked = { url -> photoViewerInfo = listOf(url) to null },
|
||||
onPhotoClicked = { url ->
|
||||
photoViewerInfo = immutableListOf(url) to null
|
||||
},
|
||||
onMessageClicked = navController::navigateToMessagesHistory,
|
||||
onNavigateToCreateChat = navController::navigateToCreateChat
|
||||
)
|
||||
@@ -159,12 +344,14 @@ fun RootScreen(
|
||||
onBack = navController::navigateUp,
|
||||
onNavigateToChatMaterials = navController::navigateToChatMaterials,
|
||||
onNavigateToPhotoViewer = { photos, index ->
|
||||
photoViewerInfo = photos to index
|
||||
photoViewerInfo = photos.toImmutableList() to index
|
||||
}
|
||||
)
|
||||
chatMaterialsScreen(
|
||||
onBack = navController::navigateUp,
|
||||
onPhotoClicked = { url -> photoViewerInfo = listOf(url) to null }
|
||||
onPhotoClicked = { url ->
|
||||
photoViewerInfo = immutableListOf(url) to null
|
||||
}
|
||||
)
|
||||
createChatScreen(
|
||||
onChatCreated = { conversationId ->
|
||||
@@ -197,6 +384,8 @@ fun RootScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateToMain() {
|
||||
|
||||
+14
-10
@@ -92,12 +92,14 @@ class MessagesRepositoryImpl(
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message),
|
||||
replyMessage = message.replyMessage?.copy(
|
||||
user = usersMap.messageUser(message),
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message),
|
||||
replyMessage = message.replyMessage.let { replyMessage ->
|
||||
replyMessage?.copy(
|
||||
user = usersMap.messageUser(replyMessage),
|
||||
group = groupsMap.messageGroup(replyMessage),
|
||||
actionUser = usersMap.messageActionUser(replyMessage),
|
||||
actionGroup = groupsMap.messageActionGroup(replyMessage),
|
||||
)
|
||||
}
|
||||
).also { VkMemoryCache[message.id] = it }
|
||||
}
|
||||
}
|
||||
@@ -167,12 +169,14 @@ class MessagesRepositoryImpl(
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message),
|
||||
replyMessage = message.replyMessage?.asDomain()?.copy(
|
||||
user = usersMap.messageUser(message),
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message),
|
||||
replyMessage = message.replyMessage?.asDomain().let { replyMessage ->
|
||||
replyMessage?.copy(
|
||||
user = usersMap.messageUser(replyMessage),
|
||||
group = groupsMap.messageGroup(replyMessage),
|
||||
actionUser = usersMap.messageActionUser(replyMessage),
|
||||
actionGroup = groupsMap.messageActionGroup(replyMessage),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -5,6 +5,7 @@ import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import dev.meloda.fast.friends.FriendsViewModel
|
||||
import dev.meloda.fast.friends.FriendsViewModelImpl
|
||||
import dev.meloda.fast.friends.OnlineFriendsViewModelImpl
|
||||
import dev.meloda.fast.friends.presentation.FriendsRoute
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -20,14 +21,14 @@ fun NavGraphBuilder.friendsScreen(
|
||||
onMessageClicked: (userId: Long) -> Unit,
|
||||
onScrolledToTop: () -> Unit
|
||||
) {
|
||||
val friendsViewModel: FriendsViewModel = with(activity) {
|
||||
getViewModel<FriendsViewModelImpl>()
|
||||
}
|
||||
val friendsViewModel: FriendsViewModel = activity.getViewModel<FriendsViewModelImpl>()
|
||||
val onlineFriendsViewModel =
|
||||
activity.getViewModel<OnlineFriendsViewModelImpl>()
|
||||
|
||||
composable<Friends> {
|
||||
FriendsRoute(
|
||||
activity = activity,
|
||||
friendsViewModel = friendsViewModel,
|
||||
onlineFriendsViewModel = onlineFriendsViewModel,
|
||||
onError = onError,
|
||||
onPhotoClicked = onPhotoClicked,
|
||||
onMessageClicked = onMessageClicked,
|
||||
|
||||
+3
-7
@@ -1,6 +1,5 @@
|
||||
package dev.meloda.fast.friends.presentation
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
@@ -54,17 +53,16 @@ import dev.meloda.fast.ui.theme.LocalHazeState
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
||||
@Composable
|
||||
fun FriendsRoute(
|
||||
activity: AppCompatActivity,
|
||||
friendsViewModel: FriendsViewModel,
|
||||
onlineFriendsViewModel: OnlineFriendsViewModelImpl,
|
||||
onError: (BaseError) -> Unit,
|
||||
onPhotoClicked: (url: String) -> Unit,
|
||||
onMessageClicked: (userid: Long) -> Unit,
|
||||
onScrolledToTop: () -> Unit
|
||||
onScrolledToTop: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val currentTheme = LocalThemeConfig.current
|
||||
@@ -232,9 +230,7 @@ fun FriendsRoute(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) { index ->
|
||||
FriendsScreen(
|
||||
viewModel = if (index == 0) friendsViewModel else with(activity) {
|
||||
getViewModel<OnlineFriendsViewModelImpl>()
|
||||
},
|
||||
viewModel = if (index == 0) friendsViewModel else onlineFriendsViewModel,
|
||||
orderType = orderType,
|
||||
padding = padding,
|
||||
tabIndex = index,
|
||||
|
||||
+1
-1
@@ -1231,7 +1231,7 @@ class MessagesHistoryViewModelImpl(
|
||||
val newUiMessages = messages.mapIndexed { index, message ->
|
||||
message.asPresentation(
|
||||
resourceProvider = resourceProvider,
|
||||
showName = false,
|
||||
showName = true,
|
||||
prevMessage = messages.getOrNull(index + 1),
|
||||
nextMessage = messages.getOrNull(index - 1),
|
||||
showTimeInActionMessages = AppSettings.Experimental.showTimeInActionMessages,
|
||||
|
||||
+5
-1
@@ -1,14 +1,17 @@
|
||||
package dev.meloda.fast.messageshistory.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import dev.meloda.fast.common.model.UiImage
|
||||
import dev.meloda.fast.model.api.domain.VkAttachment
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
|
||||
sealed class UiItem(
|
||||
open val id: Long,
|
||||
open val cmId: Long
|
||||
) {
|
||||
|
||||
@Stable
|
||||
data class Message(
|
||||
override val id: Long,
|
||||
override val cmId: Long,
|
||||
@@ -29,12 +32,13 @@ sealed class UiItem(
|
||||
val isSelected: Boolean,
|
||||
val isPinned: Boolean,
|
||||
val isImportant: Boolean,
|
||||
val attachments: List<VkAttachment>?,
|
||||
val attachments: ImmutableList<VkAttachment>?,
|
||||
val replyCmId: Long?,
|
||||
val replyTitle: String?,
|
||||
val replySummary: String?
|
||||
) : UiItem(id, cmId)
|
||||
|
||||
@Stable
|
||||
data class ActionMessage(
|
||||
override val id: Long,
|
||||
override val cmId: Long,
|
||||
|
||||
+67
-34
@@ -16,6 +16,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
@@ -27,6 +28,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -58,7 +60,8 @@ fun MessageBubble(
|
||||
replySummary: String? = null,
|
||||
onClick: (VkAttachment) -> Unit = {},
|
||||
onLongClick: (VkAttachment) -> Unit = {},
|
||||
onReplyClick: () -> Unit = {}
|
||||
onReplyClick: () -> Unit = {},
|
||||
onBubbleWidthChange: (Int) -> Unit = {},
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
|
||||
@@ -66,22 +69,7 @@ fun MessageBubble(
|
||||
val currentOnLongClick by rememberUpdatedState(onLongClick)
|
||||
|
||||
val theme = LocalThemeConfig.current
|
||||
val backgroundColor = if (!isOut) {
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)
|
||||
} else {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
}
|
||||
val replyBackgroundColor = if (!isOut) {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.inversePrimary
|
||||
}
|
||||
|
||||
val contentColor = if (!isOut) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onPrimaryContainer
|
||||
}
|
||||
val colors = messageBubbleColors(isOut = isOut)
|
||||
|
||||
val shouldShowBubble = !text.isNullOrEmpty()
|
||||
|
||||
@@ -115,7 +103,7 @@ fun MessageBubble(
|
||||
label = "dateContainerWidth"
|
||||
)
|
||||
|
||||
CompositionLocalProvider(LocalContentColor provides contentColor) {
|
||||
CompositionLocalProvider(LocalContentColor provides colors.content) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.wrapContentWidth()
|
||||
@@ -138,8 +126,8 @@ fun MessageBubble(
|
||||
onClick = onReplyClick,
|
||||
title = replyTitle,
|
||||
summary = replySummary,
|
||||
backgroundColor = backgroundColor,
|
||||
innerBackgroundColor = replyBackgroundColor
|
||||
backgroundColor = colors.container,
|
||||
innerBackgroundColor = colors.replyContainer
|
||||
)
|
||||
}
|
||||
|
||||
@@ -149,6 +137,9 @@ fun MessageBubble(
|
||||
.onGloballyPositioned {
|
||||
bubbleContainerWidth = it.size.width
|
||||
}
|
||||
.onSizeChanged {
|
||||
onBubbleWidthChange(it.width)
|
||||
}
|
||||
.widthIn(min = if (shouldFill) attachmentsContainerWidth.dp else 56.dp)
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
@@ -158,7 +149,7 @@ fun MessageBubble(
|
||||
bottomEnd = if (attachments != null) 0.dp else 24.dp
|
||||
)
|
||||
)
|
||||
.background(backgroundColor)
|
||||
.background(colors.container)
|
||||
.padding(
|
||||
start = 8.dp,
|
||||
end = 8.dp,
|
||||
@@ -199,6 +190,15 @@ fun MessageBubble(
|
||||
}
|
||||
|
||||
if (attachments != null) {
|
||||
val firstAttachment = attachments.firstOrNull()
|
||||
val isMediaAttachment = firstAttachment is VkStickerDomain ||
|
||||
firstAttachment is VkVideoMessageDomain
|
||||
val attachmentBackgroundColor = if (isMediaAttachment) {
|
||||
Color.Transparent
|
||||
} else {
|
||||
colors.container
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.onGloballyPositioned {
|
||||
@@ -213,18 +213,7 @@ fun MessageBubble(
|
||||
topEnd = 0.dp
|
||||
)
|
||||
)
|
||||
.background(
|
||||
backgroundColor.copy(
|
||||
alpha = if ((attachments.firstOrNull()?.javaClass
|
||||
?: Nothing::class.java)
|
||||
in listOf(
|
||||
VkStickerDomain::class.java,
|
||||
VkVideoMessageDomain::class.java
|
||||
)
|
||||
) 0f
|
||||
else 1f
|
||||
)
|
||||
)
|
||||
.background(attachmentBackgroundColor)
|
||||
) {
|
||||
Attachments(
|
||||
modifier = Modifier,
|
||||
@@ -236,7 +225,7 @@ fun MessageBubble(
|
||||
val dateStatusBackground = if (theme.darkMode) Color.Black.copy(alpha = 0.5f)
|
||||
else Color.White.copy(alpha = 0.5f)
|
||||
|
||||
CompositionLocalProvider(LocalContentColor provides contentColor) {
|
||||
CompositionLocalProvider(LocalContentColor provides colors.content) {
|
||||
DateStatus(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
@@ -261,9 +250,34 @@ fun MessageBubble(
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private data class MessageBubbleColors(
|
||||
val container: Color,
|
||||
val content: Color,
|
||||
val replyContainer: Color,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun messageBubbleColors(isOut: Boolean): MessageBubbleColors {
|
||||
return if (isOut) {
|
||||
MessageBubbleColors(
|
||||
container = MaterialTheme.colorScheme.primaryContainer,
|
||||
content = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
replyContainer = MaterialTheme.colorScheme.inversePrimary
|
||||
)
|
||||
} else {
|
||||
MessageBubbleColors(
|
||||
container = MaterialTheme.colorScheme.surfaceContainer,
|
||||
content = MaterialTheme.colorScheme.onSurface,
|
||||
replyContainer = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Bubble() {
|
||||
Column {
|
||||
MessageBubble(
|
||||
modifier = Modifier,
|
||||
text = AnnotatedString("Some cool text"),
|
||||
@@ -281,4 +295,23 @@ private fun Bubble() {
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
|
||||
MessageBubble(
|
||||
modifier = Modifier,
|
||||
text = AnnotatedString("Some cool text"),
|
||||
isOut = false,
|
||||
date = "19:01",
|
||||
isEdited = true,
|
||||
isRead = true,
|
||||
sendingStatus = SendingStatus.SENT,
|
||||
isPinned = true,
|
||||
isImportant = true,
|
||||
isSelected = false,
|
||||
attachments = emptyImmutableList(),
|
||||
replyTitle = "Danil Nikolaev",
|
||||
replySummary = "2 photos",
|
||||
onClick = {},
|
||||
onLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+20
-6
@@ -20,12 +20,16 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
@@ -36,7 +40,6 @@ import com.conena.nanokt.android.content.dpInPx
|
||||
import dev.meloda.fast.messageshistory.model.UiItem
|
||||
import dev.meloda.fast.model.api.domain.VkAttachment
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
@@ -49,10 +52,16 @@ fun IncomingMessageBubble(
|
||||
onLongClick: (VkAttachment) -> Unit = {},
|
||||
onReplyClick: () -> Unit = {}
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
|
||||
val currentOnClick by rememberUpdatedState(onClick)
|
||||
val currentOnLongClick by rememberUpdatedState(onLongClick)
|
||||
val currentOnReplyClick by rememberUpdatedState(onReplyClick)
|
||||
|
||||
var bubbleContainerWidth by remember {
|
||||
mutableStateOf(0.dp)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -103,9 +112,12 @@ fun IncomingMessageBubble(
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp)
|
||||
.widthIn(max = 140.dp),
|
||||
.widthIn(
|
||||
max = (bubbleContainerWidth.takeIf { it > 0.dp }
|
||||
?: 140.dp) - 24.dp
|
||||
),
|
||||
text = message.name,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -123,14 +135,16 @@ fun IncomingMessageBubble(
|
||||
isPinned = message.isPinned,
|
||||
isImportant = message.isImportant,
|
||||
isSelected = message.isSelected,
|
||||
attachments = message.attachments?.toImmutableList(),
|
||||
attachments = message.attachments,
|
||||
replyTitle = message.replyTitle,
|
||||
replySummary = message.replySummary,
|
||||
onClick = currentOnClick,
|
||||
onLongClick = currentOnLongClick,
|
||||
onReplyClick = currentOnReplyClick
|
||||
onReplyClick = currentOnReplyClick,
|
||||
onBubbleWidthChange = {
|
||||
bubbleContainerWidth = with(density) { it.toDp() }
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.fillMaxWidth(0.25f))
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ fun OutgoingMessageBubble(
|
||||
isPinned = message.isPinned,
|
||||
isImportant = message.isImportant,
|
||||
isSelected = message.isSelected,
|
||||
attachments = message.attachments?.toImmutableList(),
|
||||
attachments = message.attachments,
|
||||
replyTitle = message.replyTitle,
|
||||
replySummary = message.replySummary,
|
||||
onClick = currentOnClick,
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import dev.meloda.fast.model.api.domain.FormatDataType
|
||||
import dev.meloda.fast.model.api.domain.VkConversation
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@@ -156,7 +157,7 @@ fun VkMessage.asPresentation(
|
||||
isSelected = isSelected,
|
||||
isPinned = isPinned,
|
||||
isImportant = isImportant,
|
||||
attachments = attachments?.ifEmpty { null },
|
||||
attachments = attachments?.ifEmpty { null }?.toImmutableList(),
|
||||
replyCmId = replyMessage?.cmId,
|
||||
replyTitle = extractReplyTitle(),
|
||||
replySummary = extractReplySummary()
|
||||
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
package dev.meloda.fast.photoviewer.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.Animatable
|
||||
@@ -59,9 +58,9 @@ import dev.meloda.fast.photoviewer.model.PhotoViewScreenState
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.components.FullScreenDialog
|
||||
import dev.meloda.fast.ui.components.Loader
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
import dev.meloda.fast.ui.util.getImage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import java.net.URLEncoder
|
||||
@@ -69,7 +68,7 @@ import kotlin.math.abs
|
||||
|
||||
@Composable
|
||||
fun PhotoViewDialog(
|
||||
photoViewerInfo: Pair<List<String>, Int?>?,
|
||||
photoViewerInfo: Pair<ImmutableList<String>, Int?>?,
|
||||
modifier: Modifier = Modifier,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
@@ -85,7 +84,7 @@ fun PhotoViewDialog(
|
||||
arguments = PhotoViewArguments(
|
||||
imageUrls = photoViewerInfo.first.map {
|
||||
URLEncoder.encode(it, "utf-8")
|
||||
},
|
||||
}.toList(),
|
||||
selectedIndex = photoViewerInfo.second
|
||||
),
|
||||
applicationContext = applicationContext
|
||||
|
||||
Reference in New Issue
Block a user